implement crafting
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2024-11-16 18:38:43 -05:00
parent 851875b62f
commit 9bd81242fc
28 changed files with 526 additions and 39 deletions

View File

@ -13,7 +13,7 @@
"graphicsPerformanceEnableVSync" : false, "graphicsPerformanceEnableVSync" : false,
"graphicsPerformanceDrawShadows" : true, "graphicsPerformanceDrawShadows" : true,
"graphicsPerformanceOIT" : true, "graphicsPerformanceOIT" : true,
"graphicsPerformanceEnableFoliageManager" : true, "graphicsPerformanceEnableFoliageManager" : false,
"graphicsViewRange" : 20000.0, "graphicsViewRange" : 20000.0,
"renderResolutionX": 1920, "renderResolutionX": 1920,

View File

@ -43,7 +43,7 @@
], ],
"products": [ "products": [
{ {
"itemType": "SPAWN_Workbench", "itemType": "Workbench",
"count": 1 "count": 1
} }
] ]

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file #maven.buildNumber.plugin properties file
#Fri Nov 15 15:38:56 EST 2024 #Sat Nov 16 12:45:38 EST 2024
buildNumber=385 buildNumber=387

View File

@ -1052,6 +1052,10 @@ File watching scripts source dir
Fix STBImage flipping bug (set flag statically) Fix STBImage flipping bug (set flag statically)
Update visuals on pine tree Update visuals on pine tree
(11/16/2024)
Mountain generation work
Implement crafting
# TODO # TODO
@ -1077,10 +1081,6 @@ Implement gadgets
- Decoy (creates a decoy) - Decoy (creates a decoy)
- Torch - Torch
- Throwable potions - Throwable potions
Crafting
- Crafting Menu
- Recipe definitions
- Reagent items
Ability to fully reload game engine state without exiting client Ability to fully reload game engine state without exiting client
- Back out to main menu and load a new level without any values persisting - Back out to main menu and load a new level without any values persisting

View File

@ -48,6 +48,14 @@
{ {
"name" : "viewTargetZ", "name" : "viewTargetZ",
"type" : "FIXED_DOUBLE" "type" : "FIXED_DOUBLE"
},
{
"name" : "stationId",
"type" : "FIXED_INT"
},
{
"name" : "recipeId",
"type" : "FIXED_INT"
} }
], ],
"messageTypes" : [ "messageTypes" : [
@ -142,6 +150,15 @@
"viewTargetY", "viewTargetY",
"viewTargetZ" "viewTargetZ"
] ]
},
{
"messageName" : "clientRequestCraft",
"description" : "Requests that the server craft an item",
"data" : [
"entityId",
"stationId",
"recipeId"
]
} }
] ]
} }

View File

@ -305,7 +305,7 @@
<dependency> <dependency>
<groupId>io.github.studiorailgun</groupId> <groupId>io.github.studiorailgun</groupId>
<artifactId>MathUtils</artifactId> <artifactId>MathUtils</artifactId>
<version>1.3.0</version> <version>1.4.0</version>
</dependency> </dependency>
<!--DataStructures--> <!--DataStructures-->

View File

@ -1,5 +1,7 @@
package electrosphere.client.ui.components; package electrosphere.client.ui.components;
import java.util.function.Consumer;
import electrosphere.client.ui.menu.WindowStrings; import electrosphere.client.ui.menu.WindowStrings;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.engine.signal.Signal.SignalType; import electrosphere.engine.signal.Signal.SignalType;
@ -51,9 +53,10 @@ public class CraftingPanel {
/** /**
* Creates the crafting panel component * Creates the crafting panel component
* @param onCraft Called when an item is crafted
* @return The component * @return The component
*/ */
public static Element createCraftingPanelComponent(){ public static Element createCraftingPanelComponent(Consumer<RecipeData> onCraft){
//top level element //top level element
Div rVal = Div.createCol(); Div rVal = Div.createCol();
@ -83,6 +86,7 @@ public class CraftingPanel {
Globals.gameConfigCurrent.getRecipeMap().getTypes().forEach((RecipeData recipe) -> { Globals.gameConfigCurrent.getRecipeMap().getTypes().forEach((RecipeData recipe) -> {
Button recipeButton = Button.createButton(recipe.getDisplayName(), () -> { Button recipeButton = Button.createButton(recipe.getDisplayName(), () -> {
CraftingPanel.setDetails(rVal, recipeDetailsSection, recipe); CraftingPanel.setDetails(rVal, recipeDetailsSection, recipe);
selectedRecipe = recipe;
}); });
recipeScrollable.addChild(recipeButton); recipeScrollable.addChild(recipeButton);
}); });
@ -91,7 +95,7 @@ public class CraftingPanel {
//the button to actually craft //the button to actually craft
Button craftButton = Button.createButton("Craft", () -> { Button craftButton = Button.createButton("Craft", () -> {
System.out.println("Craft an item here"); onCraft.accept(selectedRecipe);
}); });
Div buttonRow = Div.createRow( Div buttonRow = Div.createRow(
craftButton craftButton

View File

@ -17,6 +17,7 @@ import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.ui.elements.Div; import electrosphere.renderer.ui.elements.Div;
import electrosphere.renderer.ui.elements.ImagePanel; import electrosphere.renderer.ui.elements.ImagePanel;
import electrosphere.renderer.ui.elements.Label; import electrosphere.renderer.ui.elements.Label;
import electrosphere.renderer.ui.elements.Tooltip;
import electrosphere.renderer.ui.elementtypes.ClickableElement.ClickEventCallback; import electrosphere.renderer.ui.elementtypes.ClickableElement.ClickEventCallback;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment; import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaFlexDirection; import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaFlexDirection;
@ -24,14 +25,21 @@ import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification
import electrosphere.renderer.ui.elementtypes.ContainerElement; import electrosphere.renderer.ui.elementtypes.ContainerElement;
import electrosphere.renderer.ui.elementtypes.DraggableElement.DragEventCallback; import electrosphere.renderer.ui.elementtypes.DraggableElement.DragEventCallback;
import electrosphere.renderer.ui.elementtypes.Element; import electrosphere.renderer.ui.elementtypes.Element;
import electrosphere.renderer.ui.elementtypes.HoverableElement.HoverEventCallback;
import electrosphere.renderer.ui.events.ClickEvent; import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.events.DragEvent; import electrosphere.renderer.ui.events.DragEvent;
import electrosphere.renderer.ui.events.HoverEvent;
/** /**
* An inventory panel showing a natural inventory * An inventory panel showing a natural inventory
*/ */
public class NaturalInventoryPanel { public class NaturalInventoryPanel {
/**
* The tooltip for the currently hovered item
*/
static Tooltip itemTooltip;
/** /**
* Creates the natural inventory panel * Creates the natural inventory panel
* @param entity The entity who has the inventory * @param entity The entity who has the inventory
@ -179,6 +187,27 @@ public class NaturalInventoryPanel {
} }
return false; return false;
}}); }});
panel.setOnHoverCallback(new HoverEventCallback() {public boolean execute(HoverEvent event){
if(event.isHovered() && Globals.draggedItem == null){
if(itemTooltip != null){
Tooltip.destroy(itemTooltip);
}
Entity itemEntity = finalEnt;
Item itemData = Globals.gameConfigCurrent.getItemMap().getItem(itemEntity);
Globals.signalSystem.post(SignalType.UI_MODIFICATION,()->{
itemTooltip = Tooltip.create(null,
Div.createCol(Label.createLabel(itemData.getId()))
);
itemTooltip.setPositionX(panel.getAbsoluteX() + panelWidth);
itemTooltip.setPositionY(panel.getAbsoluteY());
});
} else {
if(itemTooltip != null){
Tooltip.destroy(itemTooltip);
}
}
return false;
}});
} else { } else {
panel.setOnDragRelease(new DragEventCallback(){public boolean execute(DragEvent event){ panel.setOnDragRelease(new DragEventCallback(){public boolean execute(DragEvent event){
if(Globals.dragSourceInventory instanceof RelationalInventoryState){ if(Globals.dragSourceInventory instanceof RelationalInventoryState){

View File

@ -8,6 +8,8 @@ import electrosphere.controls.ControlHandler.ControlsState;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.engine.assetmanager.AssetDataStrings;
import electrosphere.engine.signal.Signal.SignalType; import electrosphere.engine.signal.Signal.SignalType;
import electrosphere.game.data.crafting.RecipeData;
import electrosphere.net.parser.net.message.InventoryMessage;
import electrosphere.renderer.ui.elements.Window; import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment; import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification; import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification;
@ -61,7 +63,13 @@ public class CraftingWindow {
// //
//contents //contents
// //
rVal.addChild(CraftingPanel.createCraftingPanelComponent()); rVal.addChild(CraftingPanel.createCraftingPanelComponent((RecipeData recipe) -> {
Globals.clientConnection.queueOutgoingMessage(InventoryMessage.constructclientRequestCraftMessage(
Globals.clientSceneWrapper.mapClientToServerId(Globals.playerEntity.getId()),
Globals.clientSceneWrapper.mapClientToServerId(Globals.interactionTarget.getId()),
recipe.getId()
));
}));
// //
//Final setup //Final setup

View File

@ -24,6 +24,7 @@ import electrosphere.entity.state.inventory.RelationalInventoryState;
import electrosphere.entity.state.inventory.UnrelationalInventoryState; import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.entity.types.creature.CreatureTemplate; import electrosphere.entity.types.creature.CreatureTemplate;
import electrosphere.game.data.common.CommonEntityType; import electrosphere.game.data.common.CommonEntityType;
import electrosphere.game.data.crafting.RecipeData;
import electrosphere.game.data.creature.type.equip.EquipPoint; import electrosphere.game.data.creature.type.equip.EquipPoint;
import electrosphere.game.data.voxel.VoxelType; import electrosphere.game.data.voxel.VoxelType;
import electrosphere.renderer.actor.ActorUtils; import electrosphere.renderer.actor.ActorUtils;
@ -155,7 +156,9 @@ public class MenuGeneratorsUITesting {
})); }));
} break; } break;
case "CraftingPanel": { case "CraftingPanel": {
formEl.addChild(CraftingPanel.createCraftingPanelComponent()); formEl.addChild(CraftingPanel.createCraftingPanelComponent((RecipeData recipe) -> {
System.out.println("Craft " + recipe.getDisplayName());
}));
} break; } break;
} }
} }

View File

@ -618,6 +618,7 @@ public class ControlCategoryMainGame {
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera)); Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
Entity target = collisionEngine.rayCast(centerPos, eyePos, CollisionEngine.DEFAULT_INTERACT_DISTANCE, Collidable.MASK_NO_TERRAIN); Entity target = collisionEngine.rayCast(centerPos, eyePos, CollisionEngine.DEFAULT_INTERACT_DISTANCE, Collidable.MASK_NO_TERRAIN);
if(target != null && CommonEntityFlags.isInteractable(target)){ if(target != null && CommonEntityFlags.isInteractable(target)){
Globals.interactionTarget = target;
InteractionData interactionData = CommonEntityUtils.getCommonData(target).getInteraction(); InteractionData interactionData = CommonEntityUtils.getCommonData(target).getInteraction();
switch(interactionData.getOnInteract()){ switch(interactionData.getOnInteract()){
case InteractionData.ON_INTERACT_TARGET: { case InteractionData.ON_INTERACT_TARGET: {

View File

@ -395,6 +395,11 @@ public class Globals {
//the entity for the first person modal (view model) //the entity for the first person modal (view model)
public static Entity firstPersonEntity; public static Entity firstPersonEntity;
/**
* The target of the interaction
*/
public static Entity interactionTarget = null;
//client current selected voxel type //client current selected voxel type
public static VoxelType clientSelectedVoxelType = null; public static VoxelType clientSelectedVoxelType = null;
//the selected type of entity to spawn //the selected type of entity to spawn

View File

@ -61,6 +61,35 @@ public class EntityCreationUtils {
return rVal; return rVal;
} }
/**
* Creates a server entity in the given realm and position. This uses spatial entity as a server entity can't (currently) exist outside of a realm.
* @param realm The realm to attach the entity to
* @return The entity
*/
public static Entity createServerInventoryEntity(Realm realm){
Entity rVal = EntityCreationUtils.spawnSpatialEntity();
//register to global entity id lookup table
EntityLookupUtils.registerServerEntity(rVal);
//assign to realm
Globals.realmManager.mapEntityToRealm(rVal, realm);
//init data cell if it doesn't exist
ServerDataCell cell = realm.getInventoryCell();
//If a server data cell was not created, this is considered illegal state
if(cell == null){
throw new IllegalStateException("Realm inventory data cell undefined!");
}
//register to entity data cell mapper
Globals.entityDataCellMapper.registerEntity(rVal, cell);
//enable behavior tree tracking
ServerBehaviorTreeUtils.registerEntity(rVal);
if(Globals.entityDataCellMapper.getEntityDataCell(rVal) == null){
throw new Error("Failed to map entity to cell!");
}
return rVal;
}
/** /**
* Spawns an entity that is not attached to a realm (for instance an item in an inventory) * Spawns an entity that is not attached to a realm (for instance an item in an inventory)
* @return The entity * @return The entity

View File

@ -181,6 +181,7 @@ public class ClientInventoryState implements BehaviorTree {
// throw new UnsupportedOperationException("TODO: in world item is null"); // throw new UnsupportedOperationException("TODO: in world item is null");
} }
} break; } break;
case CLIENTREQUESTCRAFT:
case CLIENTUPDATETOOLBAR: case CLIENTUPDATETOOLBAR:
case CLIENTREQUESTADDNATURAL: case CLIENTREQUESTADDNATURAL:
case CLIENTREQUESTADDTOOLBAR: case CLIENTREQUESTADDTOOLBAR:

View File

@ -401,7 +401,7 @@ public class InventoryUtils {
//need creature so we can figure out where to drop the item //need creature so we can figure out where to drop the item
public static void serverAttemptEjectItem(Entity creature, Entity item){ public static void serverAttemptEjectItem(Entity creature, Entity item){
//if we're the server, immediately attempt the transform //if we're the server, immediately attempt the transform
serverAttemptEjectItemTransform(creature,item); InventoryUtils.serverAttemptEjectItemTransform(creature,item);
} }
//need creature so we can figure out where to drop the item //need creature so we can figure out where to drop the item

View File

@ -1,5 +1,6 @@
package electrosphere.entity.state.inventory; package electrosphere.entity.state.inventory;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -86,6 +87,14 @@ public class RelationalInventoryState {
return items.get(slot); return items.get(slot);
} }
/**
* Gets the collection of items in the inventory
* @return The entities in the inventory
*/
public Collection<Entity> getItems(){
return items.values();
}
/** /**
* Gets the item slot for a given item * Gets the item slot for a given item
* @param item The item * @param item The item

View File

@ -16,14 +16,26 @@ import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils;
*/ */
public class ServerInventoryState implements BehaviorTree { public class ServerInventoryState implements BehaviorTree {
/**
* The queue of messages to handle
*/
CopyOnWriteArrayList<InventoryMessage> networkMessageQueue = new CopyOnWriteArrayList<InventoryMessage>(); CopyOnWriteArrayList<InventoryMessage> networkMessageQueue = new CopyOnWriteArrayList<InventoryMessage>();
/**
* The parent of the state
*/
Entity parent; Entity parent;
ServerInventoryState() { /**
* Constructor
} */
ServerInventoryState() {}
/**
* Creates an inventory state
* @param parent The parent entity
* @return The inventory state
*/
public static ServerInventoryState serverCreateInventoryState(Entity parent){ public static ServerInventoryState serverCreateInventoryState(Entity parent){
ServerInventoryState rVal = new ServerInventoryState(); ServerInventoryState rVal = new ServerInventoryState();
rVal.parent = parent; rVal.parent = parent;
@ -57,7 +69,7 @@ public class ServerInventoryState implements BehaviorTree {
//make sure can unequip //make sure can unequip
if(InventoryUtils.hasEquipInventory(parent) && InventoryUtils.hasNaturalInventory(parent) && ServerEquipState.hasEquipState(parent)){ if(InventoryUtils.hasEquipInventory(parent) && InventoryUtils.hasNaturalInventory(parent) && ServerEquipState.hasEquipState(parent)){
ServerEquipState equipState = ServerEquipState.getEquipState(parent); ServerEquipState equipState = ServerEquipState.getEquipState(parent);
EquipPoint point = equipState.getEquipPoint(message.getequipPointId()); // EquipPoint point = equipState.getEquipPoint(message.getequipPointId());
if(equipState.hasEquippedAtPoint(message.getequipPointId())){ if(equipState.hasEquippedAtPoint(message.getequipPointId())){
equipState.commandAttemptUnequip(message.getequipPointId()); equipState.commandAttemptUnequip(message.getequipPointId());
//tell player //tell player
@ -76,6 +88,7 @@ public class ServerInventoryState implements BehaviorTree {
ServerToolbarState serverToolbarState = ServerToolbarState.getServerToolbarState(parent); ServerToolbarState serverToolbarState = ServerToolbarState.getServerToolbarState(parent);
serverToolbarState.attemptChangeSelection(message.gettoolbarId()); serverToolbarState.attemptChangeSelection(message.gettoolbarId());
} break; } break;
case CLIENTREQUESTCRAFT:
case CLIENTREQUESTPERFORMITEMACTION: case CLIENTREQUESTPERFORMITEMACTION:
case SERVERCOMMANDUNEQUIPITEM: case SERVERCOMMANDUNEQUIPITEM:
case SERVERCOMMANDEQUIPITEM: case SERVERCOMMANDEQUIPITEM:
@ -85,6 +98,10 @@ public class ServerInventoryState implements BehaviorTree {
} }
} }
/**
* Add a network message to be handled on the server
* @param networkMessage The message
*/
public void addNetworkMessage(InventoryMessage networkMessage) { public void addNetworkMessage(InventoryMessage networkMessage) {
networkMessageQueue.add(networkMessage); networkMessageQueue.add(networkMessage);
} }

View File

@ -23,7 +23,16 @@ public class CommonEntityFlags {
* @return true if should be synchronized, false otherwise * @return true if should be synchronized, false otherwise
*/ */
public static boolean shouldBeSynchronized(Entity entity){ public static boolean shouldBeSynchronized(Entity entity){
return entity.containsKey(EntityDataStrings.SHOULD_SYNCHRONIZE); return !entity.containsKey(EntityDataStrings.SHOULD_SYNCHRONIZE) || (boolean)entity.getData(EntityDataStrings.SHOULD_SYNCHRONIZE);
}
/**
* Sets the synchronization status of the entity
* @param entity The entity
* @param shouldSynchronize true if it should be synchronized, false otherwise
*/
public static void setSynchronization(Entity entity, boolean shouldSynchronize){
entity.putData(EntityDataStrings.SHOULD_SYNCHRONIZE, shouldSynchronize);
} }
/** /**

View File

@ -20,8 +20,12 @@ import electrosphere.entity.state.AnimationPriorities;
import electrosphere.entity.state.attach.AttachUtils; import electrosphere.entity.state.attach.AttachUtils;
import electrosphere.entity.state.gravity.ServerGravityTree; import electrosphere.entity.state.gravity.ServerGravityTree;
import electrosphere.entity.state.hitbox.HitboxCollectionState; import electrosphere.entity.state.hitbox.HitboxCollectionState;
import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.state.inventory.RelationalInventoryState;
import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.entity.types.EntityTypes.EntityType; import electrosphere.entity.types.EntityTypes.EntityType;
import electrosphere.entity.types.common.CommonEntityUtils; import electrosphere.entity.types.common.CommonEntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.game.data.item.EquipData; import electrosphere.game.data.item.EquipData;
import electrosphere.game.data.item.EquipWhitelist; import electrosphere.game.data.item.EquipWhitelist;
import electrosphere.game.data.item.Item; import electrosphere.game.data.item.Item;
@ -29,6 +33,7 @@ import electrosphere.game.data.item.ItemAudio;
import electrosphere.game.data.item.WeaponData; import electrosphere.game.data.item.WeaponData;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.net.parser.net.message.InventoryMessage;
import electrosphere.net.parser.net.message.NetworkMessage; import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.net.server.player.Player; import electrosphere.net.server.player.Player;
import electrosphere.renderer.actor.Actor; import electrosphere.renderer.actor.Actor;
@ -108,19 +113,20 @@ public class ItemUtils {
} }
/** /**
* Spawns an item on the server * Applies transforms to an entity to make it an item entity
* @param realm the realm to spawn in * @param realm The realm of the entity
* @param position the position to spawn at * @param position The position of the entity
* @param name the name of the item to spawn * @param rVal The entity itself
* @return The item entity * @param item The item's type
*/ */
public static Entity serverSpawnBasicItem(Realm realm, Vector3d position, String name){ public static void serverApplyItemEntityTransforms(Realm realm, Vector3d position, Entity rVal, Item item){
Item item = Globals.gameConfigCurrent.getItemMap().getItem(name);
//must correct the position such that it spawns inside the realm //error check inputs
Vector3d correctedPosition = ServerEntityUtils.guaranteePositionIsInBounds(realm, position); if(realm == null || position == null || rVal == null || item == null){
Entity rVal = EntityCreationUtils.createServerEntity(realm, correctedPosition); throw new Error("Provided bad data to item transform! " + realm + " " + position + " " + rVal + " " + item);
}
// //
// //
//Common entity transforms //Common entity transforms
@ -184,7 +190,23 @@ public class ItemUtils {
} }
} }
rVal.putData(EntityDataStrings.ITEM_IS_IN_INVENTORY, false); rVal.putData(EntityDataStrings.ITEM_IS_IN_INVENTORY, false);
}
/**
* Spawns an item on the server
* @param realm the realm to spawn in
* @param position the position to spawn at
* @param name the name of the item to spawn
* @return The item entity
*/
public static Entity serverSpawnBasicItem(Realm realm, Vector3d position, String name){
Item item = Globals.gameConfigCurrent.getItemMap().getItem(name);
//must correct the position such that it spawns inside the realm
Vector3d correctedPosition = ServerEntityUtils.guaranteePositionIsInBounds(realm, position);
Entity rVal = EntityCreationUtils.createServerEntity(realm, correctedPosition);
//apply item transforms to an entity
ItemUtils.serverApplyItemEntityTransforms(realm, correctedPosition, rVal, item);
//position entity //position entity
@ -428,9 +450,9 @@ public class ItemUtils {
* @param containingParent The parent that contains the item * @param containingParent The parent that contains the item
*/ */
public static Entity serverRecreateContainerItem(Entity item, Entity containingParent){ public static Entity serverRecreateContainerItem(Entity item, Entity containingParent){
if(isItem(item)){ if(ItemUtils.isItem(item)){
Entity rVal = EntityCreationUtils.createRealmlessServerEntity(); Entity rVal = EntityCreationUtils.createRealmlessServerEntity();
if(getEquipWhitelist(item) != null){ if(ItemUtils.getEquipWhitelist(item) != null){
rVal.putData(EntityDataStrings.ITEM_EQUIP_WHITELIST, getEquipWhitelist(item)); rVal.putData(EntityDataStrings.ITEM_EQUIP_WHITELIST, getEquipWhitelist(item));
} }
rVal.putData(EntityDataStrings.ITEM_ICON,ItemUtils.getItemIcon(item)); rVal.putData(EntityDataStrings.ITEM_ICON,ItemUtils.getItemIcon(item));
@ -445,8 +467,51 @@ public class ItemUtils {
} }
} }
/**
* Creates a new item in the parent's inventory
* @param parent The parent that contains the item
* @param itemData The item data
*/
public static void serverCreateContainerItem(Entity parent, Item itemData){
if(parent == null || itemData == null){
throw new Error("Provided bad data! " + parent + " " + itemData);
}
//make sure there's an item to store the item
UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(parent);
RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(parent);
if(naturalInventory == null && toolbarInventory == null){
return;
}
Realm realm = Globals.realmManager.getEntityRealm(parent);
Vector3d parentPos = EntityUtils.getPosition(parent);
//must correct the position such that it spawns inside the realm
Entity rVal = EntityCreationUtils.createServerInventoryEntity(realm);
//apply item transforms to an entity
ItemUtils.serverApplyItemEntityTransforms(realm, parentPos, rVal, itemData);
//error checking
if(Globals.realmManager.getEntityRealm(rVal) == null){
LoggerInterface.loggerEngine.ERROR(new IllegalStateException("Created item without it being assigned to a realm!"));
}
//send entity to client
if(CreatureUtils.hasControllerPlayerId(parent)){
int playerId = CreatureUtils.getControllerPlayerId(parent);
Player player = Globals.playerManager.getPlayerFromId(playerId);
player.addMessage(InventoryMessage.constructaddItemToInventoryMessage(rVal.getId(), itemData.getId()));
}
}
/**
* Try destroying a client item in the world
* @param item The item to destroy
*/
public static void clientDestroyInWorldItem(Entity item){ public static void clientDestroyInWorldItem(Entity item){
if(isItem(item)){ if(ItemUtils.isItem(item)){
//destroy physics //destroy physics
if(PhysicsEntityUtils.containsDBody(item) && item.containsKey(EntityDataStrings.PHYSICS_COLLIDABLE)){ if(PhysicsEntityUtils.containsDBody(item) && item.containsKey(EntityDataStrings.PHYSICS_COLLIDABLE)){
//destroy physics //destroy physics

View File

@ -66,6 +66,7 @@ public class InventoryProtocol implements ClientProtocolTemplate<InventoryMessag
} }
} }
break; break;
case CLIENTREQUESTCRAFT:
case CLIENTUPDATETOOLBAR: case CLIENTUPDATETOOLBAR:
case CLIENTREQUESTADDNATURAL: case CLIENTREQUESTADDNATURAL:
case CLIENTREQUESTADDTOOLBAR: case CLIENTREQUESTADDTOOLBAR:

View File

@ -22,6 +22,7 @@ public class InventoryMessage extends NetworkMessage {
CLIENTREQUESTADDNATURAL, CLIENTREQUESTADDNATURAL,
CLIENTUPDATETOOLBAR, CLIENTUPDATETOOLBAR,
CLIENTREQUESTPERFORMITEMACTION, CLIENTREQUESTPERFORMITEMACTION,
CLIENTREQUESTCRAFT,
} }
/** /**
@ -39,6 +40,8 @@ public class InventoryMessage extends NetworkMessage {
double viewTargetX; double viewTargetX;
double viewTargetY; double viewTargetY;
double viewTargetZ; double viewTargetZ;
int stationId;
int recipeId;
/** /**
* Constructor * Constructor
@ -207,6 +210,34 @@ public class InventoryMessage extends NetworkMessage {
this.viewTargetZ = viewTargetZ; this.viewTargetZ = viewTargetZ;
} }
/**
* Gets stationId
*/
public int getstationId() {
return stationId;
}
/**
* Sets stationId
*/
public void setstationId(int stationId) {
this.stationId = stationId;
}
/**
* Gets recipeId
*/
public int getrecipeId() {
return recipeId;
}
/**
* Sets recipeId
*/
public void setrecipeId(int recipeId) {
this.recipeId = recipeId;
}
/** /**
* Removes the packet header from the buffer * Removes the packet header from the buffer
* @param byteBuffer The buffer * @param byteBuffer The buffer
@ -261,6 +292,12 @@ public class InventoryMessage extends NetworkMessage {
} }
case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION: case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION:
return InventoryMessage.canParseclientRequestPerformItemActionMessage(byteBuffer); return InventoryMessage.canParseclientRequestPerformItemActionMessage(byteBuffer);
case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTCRAFT:
if(byteBuffer.getRemaining() >= TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTCRAFT_SIZE){
return true;
} else {
return false;
}
} }
return false; return false;
} }
@ -726,6 +763,30 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type clientRequestCraft
*/
public static InventoryMessage parseclientRequestCraftMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTCRAFT);
stripPacketHeader(byteBuffer);
rVal.setentityId(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setstationId(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setrecipeId(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
return rVal;
}
/**
* Constructs a message of type clientRequestCraft
*/
public static InventoryMessage constructclientRequestCraftMessage(int entityId,int stationId,int recipeId){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTCRAFT);
rVal.setentityId(entityId);
rVal.setstationId(stationId);
rVal.setrecipeId(recipeId);
rVal.serialize();
return rVal;
}
@Override @Override
void serialize(){ void serialize(){
byte[] intValues = new byte[8]; byte[] intValues = new byte[8];
@ -948,6 +1009,25 @@ public class InventoryMessage extends NetworkMessage {
rawBytes[30+equipPointId.length()+i] = intValues[i]; rawBytes[30+equipPointId.length()+i] = intValues[i];
} }
break; break;
case CLIENTREQUESTCRAFT:
rawBytes = new byte[2+4+4+4];
//message header
rawBytes[0] = TypeBytes.MESSAGE_TYPE_INVENTORY;
//entity messaage header
rawBytes[1] = TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTCRAFT;
intValues = ByteStreamUtils.serializeIntToBytes(entityId);
for(int i = 0; i < 4; i++){
rawBytes[2+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeIntToBytes(stationId);
for(int i = 0; i < 4; i++){
rawBytes[6+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeIntToBytes(recipeId);
for(int i = 0; i < 4; i++){
rawBytes[10+i] = intValues[i];
}
break;
} }
serialized = true; serialized = true;
} }

View File

@ -364,6 +364,11 @@ public abstract class NetworkMessage {
rVal = InventoryMessage.parseclientRequestPerformItemActionMessage(byteBuffer); rVal = InventoryMessage.parseclientRequestPerformItemActionMessage(byteBuffer);
} }
break; break;
case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTCRAFT:
if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
rVal = InventoryMessage.parseclientRequestCraftMessage(byteBuffer);
}
break;
} }
break; break;
case TypeBytes.MESSAGE_TYPE_SYNCHRONIZATION: case TypeBytes.MESSAGE_TYPE_SYNCHRONIZATION:

View File

@ -151,6 +151,7 @@ public class TypeBytes {
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL = 8; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL = 8;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTUPDATETOOLBAR = 9; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTUPDATETOOLBAR = 9;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION = 10; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION = 10;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTCRAFT = 11;
/* /*
Inventory packet sizes Inventory packet sizes
*/ */
@ -158,6 +159,7 @@ public class TypeBytes {
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDTOOLBAR_SIZE = 10; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDTOOLBAR_SIZE = 10;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL_SIZE = 6; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL_SIZE = 6;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTUPDATETOOLBAR_SIZE = 6; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTUPDATETOOLBAR_SIZE = 6;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTCRAFT_SIZE = 14;
/* /*
Synchronization subcategories Synchronization subcategories

View File

@ -1,11 +1,24 @@
package electrosphere.net.server.protocol; package electrosphere.net.server.protocol;
import java.util.LinkedList;
import java.util.List;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.state.inventory.RelationalInventoryState;
import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.game.data.crafting.RecipeData;
import electrosphere.game.data.crafting.RecipeIngredientData;
import electrosphere.game.data.item.Item;
import electrosphere.game.data.item.ItemDataMap;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.InventoryMessage; import electrosphere.net.parser.net.message.InventoryMessage;
import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.net.template.ServerProtocolTemplate; import electrosphere.net.template.ServerProtocolTemplate;
import electrosphere.server.datacell.ServerDataCell;
import electrosphere.server.datacell.utils.DataCellSearchUtils;
import electrosphere.server.datacell.utils.EntityLookupUtils; import electrosphere.server.datacell.utils.EntityLookupUtils;
import electrosphere.server.player.PlayerActions; import electrosphere.server.player.PlayerActions;
@ -80,6 +93,16 @@ public class InventoryProtocol implements ServerProtocolTemplate<InventoryMessag
InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message); InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
} }
} break; } break;
case CLIENTREQUESTCRAFT: {
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
Entity workshopEntity = EntityLookupUtils.getEntityById(message.getstationId());
RecipeData recipe = Globals.gameConfigCurrent.getRecipeMap().getType(message.getrecipeId());
if(target != null && recipe != null){
this.attemptCraft(target,workshopEntity,recipe);
// System.out.println(message.getentityId() + " " + message.getstationId() + " " + message.getrecipeId());
// InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
}
} break;
case SERVERCOMMANDUNEQUIPITEM: case SERVERCOMMANDUNEQUIPITEM:
case SERVERCOMMANDMOVEITEMCONTAINER: case SERVERCOMMANDMOVEITEMCONTAINER:
case SERVERCOMMANDEQUIPITEM: case SERVERCOMMANDEQUIPITEM:
@ -88,4 +111,98 @@ public class InventoryProtocol implements ServerProtocolTemplate<InventoryMessag
} }
} }
/**
* Attempts to craft an item
* @param crafter The entity performing the crafting
* @param station The (optional) station for crafting
* @param recipe The recipe to craft
*/
private void attemptCraft(Entity crafter, Entity station, RecipeData recipe){
if(InventoryUtils.serverGetInventoryState(crafter) == null){
return;
}
UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(crafter);
RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(crafter);
//get data obj
ItemDataMap itemMap = Globals.gameConfigCurrent.getItemMap();
//get reagents
List<Entity> reagentList = new LinkedList<Entity>();
boolean hasReagents = true;
//find the reagents we're going to use to craft
for(RecipeIngredientData ingredient : recipe.getIngredients()){
int found = 0;
if(naturalInventory != null){
for(Entity itemEnt : naturalInventory.getItems()){
if(itemMap.getItem(itemEnt).getId().matches(ingredient.getItemType())){
found++;
reagentList.add(itemEnt);
}
if(found >= ingredient.getCount()){
break;
}
}
}
if(toolbarInventory != null){
for(Entity itemEnt : toolbarInventory.getItems()){
if(itemMap.getItem(itemEnt).getId().matches(ingredient.getItemType())){
found++;
reagentList.add(itemEnt);
}
if(found >= ingredient.getCount()){
break;
}
}
}
if(found < ingredient.getCount()){
hasReagents = false;
break;
}
}
if(hasReagents){
for(Entity reagentEnt : reagentList){
if(naturalInventory != null){
naturalInventory.removeItem(reagentEnt);
}
if(toolbarInventory != null){
toolbarInventory.tryRemoveItem(reagentEnt);
}
this.deleteItemInInventory(crafter, reagentEnt);
}
for(RecipeIngredientData product : recipe.getProducts()){
Item productType = itemMap.getItem(product.getItemType());
if(productType == null){
throw new Error("Could not locate product definition! " + productType + " " + product.getItemType());
}
for(int i = 0; i < product.getCount(); i++){
ItemUtils.serverCreateContainerItem(crafter, productType);
}
}
}
}
/**
* Delete an item that is in an inventory
* @param parent The parent entity that contains the in-inventory item
* @param itemEnt The item that is in an inventory
*/
private void deleteItemInInventory(Entity parent, Entity inInventory){
//the parent entity's data cell
ServerDataCell dataCell = DataCellSearchUtils.getEntityDataCell(parent);
//broadcast destroy entity
dataCell.broadcastNetworkMessage(InventoryMessage.constructremoveItemFromInventoryMessage(inInventory.getId()));
//remove the inventories
UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(parent);
RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(parent);
if(naturalInventory != null){
naturalInventory.removeItem(inInventory);
}
if(toolbarInventory != null){
toolbarInventory.tryRemoveItem(inInventory);
}
}
} }

View File

@ -36,6 +36,11 @@ public class Realm {
//this is the cell that all players loading into the game (via connection startup, death, etc) reside in //this is the cell that all players loading into the game (via connection startup, death, etc) reside in
ServerDataCell loadingCell = new ServerDataCell(new Scene()); ServerDataCell loadingCell = new ServerDataCell(new Scene());
/**
* The data cell that will contain in-inventory items
*/
ServerDataCell inventoryCell = new ServerDataCell(new Scene());
//resolver for entity -> data cell within this realm //resolver for entity -> data cell within this realm
EntityDataCellMapper entityDataCellMapper; EntityDataCellMapper entityDataCellMapper;
@ -261,6 +266,14 @@ public class Realm {
this.spawnPoints.add(point); this.spawnPoints.add(point);
} }
/**
* Get the inventory data cell
* @return The inventory data cell
*/
public ServerDataCell getInventoryCell(){
return inventoryCell;
}
/** /**
* Sets the script-engine side instance id for the scene that was loaded with this realm * Sets the script-engine side instance id for the scene that was loaded with this realm
* @param sceneInstanceId The instance id * @param sceneInstanceId The instance id

View File

@ -132,7 +132,7 @@ public class ServerDataCell {
* Sends the current state of the datacell to the player * Sends the current state of the datacell to the player
* Commonly, this should be called when a player is added to the cell * Commonly, this should be called when a player is added to the cell
*/ */
void serializeStateToPlayer(Player player){ private void serializeStateToPlayer(Player player){
for(Entity entity : scene.getEntityList()){ for(Entity entity : scene.getEntityList()){
this.serializeEntityToPlayer(entity,player); this.serializeEntityToPlayer(entity,player);
} }

View File

@ -14,6 +14,7 @@ import electrosphere.game.server.world.ServerWorldData;
import electrosphere.server.terrain.generation.heightmap.EmptySkyGen; import electrosphere.server.terrain.generation.heightmap.EmptySkyGen;
import electrosphere.server.terrain.generation.heightmap.HeightmapGenerator; import electrosphere.server.terrain.generation.heightmap.HeightmapGenerator;
import electrosphere.server.terrain.generation.heightmap.HillsGen; import electrosphere.server.terrain.generation.heightmap.HillsGen;
import electrosphere.server.terrain.generation.heightmap.MountainGen;
import electrosphere.server.terrain.generation.heightmap.PlainsGen; import electrosphere.server.terrain.generation.heightmap.PlainsGen;
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel; import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel;
@ -84,11 +85,12 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
*/ */
public TestGenerationChunkGenerator(ServerWorldData serverWorldData, boolean useJavascript){ public TestGenerationChunkGenerator(ServerWorldData serverWorldData, boolean useJavascript){
this.serverWorldData = serverWorldData; this.serverWorldData = serverWorldData;
registerHeightmapGenerator(new EmptySkyGen()); this.registerHeightmapGenerator(new EmptySkyGen());
registerHeightmapGenerator(new HillsGen()); this.registerHeightmapGenerator(new HillsGen());
registerHeightmapGenerator(new PlainsGen()); this.registerHeightmapGenerator(new PlainsGen());
registerVoxelGenerator(new HillsVoxelGen()); this.registerHeightmapGenerator(new MountainGen());
registerVoxelGenerator(new AnimeMountainsGen()); this.registerVoxelGenerator(new HillsVoxelGen());
this.registerVoxelGenerator(new AnimeMountainsGen());
this.useJavascript = useJavascript; this.useJavascript = useJavascript;
} }
@ -121,6 +123,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
HeightmapGenerator heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag()); HeightmapGenerator heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());
heightmapGen = this.tagHeightmapMap.get("mountains");
if(heightmapGen == null){ if(heightmapGen == null){
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
} }

View File

@ -0,0 +1,69 @@
package electrosphere.server.terrain.generation.heightmap;
import io.github.studiorailgun.NoiseUtils;
public class MountainGen implements HeightmapGenerator {
/**
* Offset from baseline to place the noisemap at
*/
static final float HEIGHT_OFFSET = 10;
/**
* The falloff factor
*/
static final double FALLOFF_FACTOR = 10.0f;
/**
* Vertical scale of the noise
*/
static final float VERTICAL_SCALE = 1024.0f;
/**
* Horizontal scale of the noise
*/
static final float HORIZONTAL_SCALE = 1024.0f;
/**
* The power applied to the noise
*/
static final float POWER_SCALE = 2;
/**
* The scale to apply to the coordinates
*/
static final float GEN_SCALE = 1.0f / HORIZONTAL_SCALE;
//the different scales of noise to sample from
static final double[][] NOISE_SCALES = new double[][]{
{0.01, 3.0},
{0.02, 2.0},
{0.05, 0.8},
{0.1, 0.3},
{0.3, 0.2},
};
/**
* Gets the height at a given position for this generation approach
* @param SEED The seed
* @param x The x position
* @param y The y position
* @return The height
*/
public float getHeight(long SEED, double x, double y){
float rVal = 0.0f;
double smoothVoronoiSample = NoiseUtils.smoothVoronoi(x * GEN_SCALE, y * GEN_SCALE, (double)SEED, FALLOFF_FACTOR);
double inverted = 1.0 - smoothVoronoiSample;
double minClamped = Math.max(inverted,0.0f);
double powered = Math.pow(minClamped,POWER_SCALE);
rVal = (float)powered * VERTICAL_SCALE;
return rVal;
}
@Override
public String getTag() {
return "mountains";
}
}