diff --git a/assets/Config/settings.json b/assets/Config/settings.json
index bc1b2916..284fa0a4 100644
--- a/assets/Config/settings.json
+++ b/assets/Config/settings.json
@@ -13,7 +13,7 @@
"graphicsPerformanceEnableVSync" : false,
"graphicsPerformanceDrawShadows" : true,
"graphicsPerformanceOIT" : true,
- "graphicsPerformanceEnableFoliageManager" : true,
+ "graphicsPerformanceEnableFoliageManager" : false,
"graphicsViewRange" : 20000.0,
"renderResolutionX": 1920,
diff --git a/assets/Data/game/recipes.json b/assets/Data/game/recipes.json
index 0ba9a125..c16d6037 100644
--- a/assets/Data/game/recipes.json
+++ b/assets/Data/game/recipes.json
@@ -43,7 +43,7 @@
],
"products": [
{
- "itemType": "SPAWN_Workbench",
+ "itemType": "Workbench",
"count": 1
}
]
diff --git a/buildNumber.properties b/buildNumber.properties
index 2e5e90dc..88000257 100644
--- a/buildNumber.properties
+++ b/buildNumber.properties
@@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file
-#Fri Nov 15 15:38:56 EST 2024
-buildNumber=385
+#Sat Nov 16 12:45:38 EST 2024
+buildNumber=387
diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md
index 24ecaacf..d17e5a06 100644
--- a/docs/src/progress/renderertodo.md
+++ b/docs/src/progress/renderertodo.md
@@ -1052,6 +1052,10 @@ File watching scripts source dir
Fix STBImage flipping bug (set flag statically)
Update visuals on pine tree
+(11/16/2024)
+Mountain generation work
+Implement crafting
+
# TODO
@@ -1077,10 +1081,6 @@ Implement gadgets
- Decoy (creates a decoy)
- Torch
- Throwable potions
- Crafting
- - Crafting Menu
- - Recipe definitions
- - Reagent items
Ability to fully reload game engine state without exiting client
- Back out to main menu and load a new level without any values persisting
diff --git a/net/inventory.json b/net/inventory.json
index 913bbf7d..eaf537ac 100644
--- a/net/inventory.json
+++ b/net/inventory.json
@@ -48,6 +48,14 @@
{
"name" : "viewTargetZ",
"type" : "FIXED_DOUBLE"
+ },
+ {
+ "name" : "stationId",
+ "type" : "FIXED_INT"
+ },
+ {
+ "name" : "recipeId",
+ "type" : "FIXED_INT"
}
],
"messageTypes" : [
@@ -142,6 +150,15 @@
"viewTargetY",
"viewTargetZ"
]
+ },
+ {
+ "messageName" : "clientRequestCraft",
+ "description" : "Requests that the server craft an item",
+ "data" : [
+ "entityId",
+ "stationId",
+ "recipeId"
+ ]
}
]
}
diff --git a/pom.xml b/pom.xml
index ff55397a..d5d223f6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -305,7 +305,7 @@
io.github.studiorailgun
MathUtils
- 1.3.0
+ 1.4.0
diff --git a/src/main/java/electrosphere/client/ui/components/CraftingPanel.java b/src/main/java/electrosphere/client/ui/components/CraftingPanel.java
index dc4388c8..cb279f51 100644
--- a/src/main/java/electrosphere/client/ui/components/CraftingPanel.java
+++ b/src/main/java/electrosphere/client/ui/components/CraftingPanel.java
@@ -1,5 +1,7 @@
package electrosphere.client.ui.components;
+import java.util.function.Consumer;
+
import electrosphere.client.ui.menu.WindowStrings;
import electrosphere.engine.Globals;
import electrosphere.engine.signal.Signal.SignalType;
@@ -51,9 +53,10 @@ public class CraftingPanel {
/**
* Creates the crafting panel component
+ * @param onCraft Called when an item is crafted
* @return The component
*/
- public static Element createCraftingPanelComponent(){
+ public static Element createCraftingPanelComponent(Consumer onCraft){
//top level element
Div rVal = Div.createCol();
@@ -83,6 +86,7 @@ public class CraftingPanel {
Globals.gameConfigCurrent.getRecipeMap().getTypes().forEach((RecipeData recipe) -> {
Button recipeButton = Button.createButton(recipe.getDisplayName(), () -> {
CraftingPanel.setDetails(rVal, recipeDetailsSection, recipe);
+ selectedRecipe = recipe;
});
recipeScrollable.addChild(recipeButton);
});
@@ -91,7 +95,7 @@ public class CraftingPanel {
//the button to actually craft
Button craftButton = Button.createButton("Craft", () -> {
- System.out.println("Craft an item here");
+ onCraft.accept(selectedRecipe);
});
Div buttonRow = Div.createRow(
craftButton
diff --git a/src/main/java/electrosphere/client/ui/components/NaturalInventoryPanel.java b/src/main/java/electrosphere/client/ui/components/NaturalInventoryPanel.java
index b42a0a8f..f7f44577 100644
--- a/src/main/java/electrosphere/client/ui/components/NaturalInventoryPanel.java
+++ b/src/main/java/electrosphere/client/ui/components/NaturalInventoryPanel.java
@@ -17,6 +17,7 @@ import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.ui.elements.Div;
import electrosphere.renderer.ui.elements.ImagePanel;
import electrosphere.renderer.ui.elements.Label;
+import electrosphere.renderer.ui.elements.Tooltip;
import electrosphere.renderer.ui.elementtypes.ClickableElement.ClickEventCallback;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment;
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.DraggableElement.DragEventCallback;
import electrosphere.renderer.ui.elementtypes.Element;
+import electrosphere.renderer.ui.elementtypes.HoverableElement.HoverEventCallback;
import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.events.DragEvent;
+import electrosphere.renderer.ui.events.HoverEvent;
/**
* An inventory panel showing a natural inventory
*/
public class NaturalInventoryPanel {
+ /**
+ * The tooltip for the currently hovered item
+ */
+ static Tooltip itemTooltip;
+
/**
* Creates the natural inventory panel
* @param entity The entity who has the inventory
@@ -179,6 +187,27 @@ public class NaturalInventoryPanel {
}
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 {
panel.setOnDragRelease(new DragEventCallback(){public boolean execute(DragEvent event){
if(Globals.dragSourceInventory instanceof RelationalInventoryState){
diff --git a/src/main/java/electrosphere/client/ui/menu/ingame/CraftingWindow.java b/src/main/java/electrosphere/client/ui/menu/ingame/CraftingWindow.java
index 157a48b8..d4460d0b 100644
--- a/src/main/java/electrosphere/client/ui/menu/ingame/CraftingWindow.java
+++ b/src/main/java/electrosphere/client/ui/menu/ingame/CraftingWindow.java
@@ -8,6 +8,8 @@ import electrosphere.controls.ControlHandler.ControlsState;
import electrosphere.engine.Globals;
import electrosphere.engine.assetmanager.AssetDataStrings;
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.elementtypes.ContainerElement.YogaAlignment;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification;
@@ -61,7 +63,13 @@ public class CraftingWindow {
//
//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
diff --git a/src/main/java/electrosphere/client/ui/menu/mainmenu/MenuGeneratorsUITesting.java b/src/main/java/electrosphere/client/ui/menu/mainmenu/MenuGeneratorsUITesting.java
index e651945f..8d8d2fa2 100644
--- a/src/main/java/electrosphere/client/ui/menu/mainmenu/MenuGeneratorsUITesting.java
+++ b/src/main/java/electrosphere/client/ui/menu/mainmenu/MenuGeneratorsUITesting.java
@@ -24,6 +24,7 @@ import electrosphere.entity.state.inventory.RelationalInventoryState;
import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.entity.types.creature.CreatureTemplate;
import electrosphere.game.data.common.CommonEntityType;
+import electrosphere.game.data.crafting.RecipeData;
import electrosphere.game.data.creature.type.equip.EquipPoint;
import electrosphere.game.data.voxel.VoxelType;
import electrosphere.renderer.actor.ActorUtils;
@@ -155,7 +156,9 @@ public class MenuGeneratorsUITesting {
}));
} break;
case "CraftingPanel": {
- formEl.addChild(CraftingPanel.createCraftingPanelComponent());
+ formEl.addChild(CraftingPanel.createCraftingPanelComponent((RecipeData recipe) -> {
+ System.out.println("Craft " + recipe.getDisplayName());
+ }));
} break;
}
}
diff --git a/src/main/java/electrosphere/controls/categories/ControlCategoryMainGame.java b/src/main/java/electrosphere/controls/categories/ControlCategoryMainGame.java
index ac612fad..0034c537 100644
--- a/src/main/java/electrosphere/controls/categories/ControlCategoryMainGame.java
+++ b/src/main/java/electrosphere/controls/categories/ControlCategoryMainGame.java
@@ -618,6 +618,7 @@ public class ControlCategoryMainGame {
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
Entity target = collisionEngine.rayCast(centerPos, eyePos, CollisionEngine.DEFAULT_INTERACT_DISTANCE, Collidable.MASK_NO_TERRAIN);
if(target != null && CommonEntityFlags.isInteractable(target)){
+ Globals.interactionTarget = target;
InteractionData interactionData = CommonEntityUtils.getCommonData(target).getInteraction();
switch(interactionData.getOnInteract()){
case InteractionData.ON_INTERACT_TARGET: {
diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java
index b3bde10a..51820d67 100644
--- a/src/main/java/electrosphere/engine/Globals.java
+++ b/src/main/java/electrosphere/engine/Globals.java
@@ -395,6 +395,11 @@ public class Globals {
//the entity for the first person modal (view model)
public static Entity firstPersonEntity;
+ /**
+ * The target of the interaction
+ */
+ public static Entity interactionTarget = null;
+
//client current selected voxel type
public static VoxelType clientSelectedVoxelType = null;
//the selected type of entity to spawn
diff --git a/src/main/java/electrosphere/entity/EntityCreationUtils.java b/src/main/java/electrosphere/entity/EntityCreationUtils.java
index ac2f5144..b1397e8c 100644
--- a/src/main/java/electrosphere/entity/EntityCreationUtils.java
+++ b/src/main/java/electrosphere/entity/EntityCreationUtils.java
@@ -61,6 +61,35 @@ public class EntityCreationUtils {
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)
* @return The entity
diff --git a/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java b/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java
index 42e13867..68c4e46e 100644
--- a/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java
+++ b/src/main/java/electrosphere/entity/state/inventory/ClientInventoryState.java
@@ -181,6 +181,7 @@ public class ClientInventoryState implements BehaviorTree {
// throw new UnsupportedOperationException("TODO: in world item is null");
}
} break;
+ case CLIENTREQUESTCRAFT:
case CLIENTUPDATETOOLBAR:
case CLIENTREQUESTADDNATURAL:
case CLIENTREQUESTADDTOOLBAR:
diff --git a/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java b/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java
index c0a6c62a..4aa05080 100644
--- a/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java
+++ b/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java
@@ -401,7 +401,7 @@ public class InventoryUtils {
//need creature so we can figure out where to drop the item
public static void serverAttemptEjectItem(Entity creature, Entity item){
//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
diff --git a/src/main/java/electrosphere/entity/state/inventory/RelationalInventoryState.java b/src/main/java/electrosphere/entity/state/inventory/RelationalInventoryState.java
index d2bee4f1..ff9a2e83 100644
--- a/src/main/java/electrosphere/entity/state/inventory/RelationalInventoryState.java
+++ b/src/main/java/electrosphere/entity/state/inventory/RelationalInventoryState.java
@@ -1,5 +1,6 @@
package electrosphere.entity.state.inventory;
+import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -86,6 +87,14 @@ public class RelationalInventoryState {
return items.get(slot);
}
+ /**
+ * Gets the collection of items in the inventory
+ * @return The entities in the inventory
+ */
+ public Collection getItems(){
+ return items.values();
+ }
+
/**
* Gets the item slot for a given item
* @param item The item
diff --git a/src/main/java/electrosphere/entity/state/inventory/ServerInventoryState.java b/src/main/java/electrosphere/entity/state/inventory/ServerInventoryState.java
index aca51814..566ef171 100644
--- a/src/main/java/electrosphere/entity/state/inventory/ServerInventoryState.java
+++ b/src/main/java/electrosphere/entity/state/inventory/ServerInventoryState.java
@@ -16,14 +16,26 @@ import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils;
*/
public class ServerInventoryState implements BehaviorTree {
+ /**
+ * The queue of messages to handle
+ */
CopyOnWriteArrayList networkMessageQueue = new CopyOnWriteArrayList();
+ /**
+ * The parent of the state
+ */
Entity parent;
- ServerInventoryState() {
-
- }
+ /**
+ * Constructor
+ */
+ ServerInventoryState() {}
+ /**
+ * Creates an inventory state
+ * @param parent The parent entity
+ * @return The inventory state
+ */
public static ServerInventoryState serverCreateInventoryState(Entity parent){
ServerInventoryState rVal = new ServerInventoryState();
rVal.parent = parent;
@@ -57,7 +69,7 @@ public class ServerInventoryState implements BehaviorTree {
//make sure can unequip
if(InventoryUtils.hasEquipInventory(parent) && InventoryUtils.hasNaturalInventory(parent) && ServerEquipState.hasEquipState(parent)){
ServerEquipState equipState = ServerEquipState.getEquipState(parent);
- EquipPoint point = equipState.getEquipPoint(message.getequipPointId());
+ // EquipPoint point = equipState.getEquipPoint(message.getequipPointId());
if(equipState.hasEquippedAtPoint(message.getequipPointId())){
equipState.commandAttemptUnequip(message.getequipPointId());
//tell player
@@ -76,6 +88,7 @@ public class ServerInventoryState implements BehaviorTree {
ServerToolbarState serverToolbarState = ServerToolbarState.getServerToolbarState(parent);
serverToolbarState.attemptChangeSelection(message.gettoolbarId());
} break;
+ case CLIENTREQUESTCRAFT:
case CLIENTREQUESTPERFORMITEMACTION:
case SERVERCOMMANDUNEQUIPITEM:
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) {
networkMessageQueue.add(networkMessage);
}
diff --git a/src/main/java/electrosphere/entity/types/common/CommonEntityFlags.java b/src/main/java/electrosphere/entity/types/common/CommonEntityFlags.java
index 4146b23c..8e1327c0 100644
--- a/src/main/java/electrosphere/entity/types/common/CommonEntityFlags.java
+++ b/src/main/java/electrosphere/entity/types/common/CommonEntityFlags.java
@@ -23,7 +23,16 @@ public class CommonEntityFlags {
* @return true if should be synchronized, false otherwise
*/
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);
}
/**
diff --git a/src/main/java/electrosphere/entity/types/item/ItemUtils.java b/src/main/java/electrosphere/entity/types/item/ItemUtils.java
index dc5a1fc3..2c1f2cf2 100644
--- a/src/main/java/electrosphere/entity/types/item/ItemUtils.java
+++ b/src/main/java/electrosphere/entity/types/item/ItemUtils.java
@@ -20,8 +20,12 @@ import electrosphere.entity.state.AnimationPriorities;
import electrosphere.entity.state.attach.AttachUtils;
import electrosphere.entity.state.gravity.ServerGravityTree;
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.common.CommonEntityUtils;
+import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.game.data.item.EquipData;
import electrosphere.game.data.item.EquipWhitelist;
import electrosphere.game.data.item.Item;
@@ -29,6 +33,7 @@ import electrosphere.game.data.item.ItemAudio;
import electrosphere.game.data.item.WeaponData;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.EntityMessage;
+import electrosphere.net.parser.net.message.InventoryMessage;
import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.net.server.player.Player;
import electrosphere.renderer.actor.Actor;
@@ -108,19 +113,20 @@ public class ItemUtils {
}
-
/**
- * 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
+ * Applies transforms to an entity to make it an item entity
+ * @param realm The realm of the entity
+ * @param position The position of the entity
+ * @param rVal The entity itself
+ * @param item The item's type
*/
- 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);
+ public static void serverApplyItemEntityTransforms(Realm realm, Vector3d position, Entity rVal, Item item){
+
+ //error check inputs
+ if(realm == null || position == null || rVal == null || item == null){
+ throw new Error("Provided bad data to item transform! " + realm + " " + position + " " + rVal + " " + item);
+ }
+
//
//
//Common entity transforms
@@ -184,7 +190,23 @@ public class ItemUtils {
}
}
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
@@ -428,9 +450,9 @@ public class ItemUtils {
* @param containingParent The parent that contains the item
*/
public static Entity serverRecreateContainerItem(Entity item, Entity containingParent){
- if(isItem(item)){
+ if(ItemUtils.isItem(item)){
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_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){
- if(isItem(item)){
+ if(ItemUtils.isItem(item)){
//destroy physics
if(PhysicsEntityUtils.containsDBody(item) && item.containsKey(EntityDataStrings.PHYSICS_COLLIDABLE)){
//destroy physics
diff --git a/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java b/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java
index 77d31d8e..33c5a338 100644
--- a/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java
+++ b/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java
@@ -66,6 +66,7 @@ public class InventoryProtocol implements ClientProtocolTemplate= TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTCRAFT_SIZE){
+ return true;
+ } else {
+ return false;
+ }
}
return false;
}
@@ -726,6 +763,30 @@ public class InventoryMessage extends NetworkMessage {
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
void serialize(){
byte[] intValues = new byte[8];
@@ -948,6 +1009,25 @@ public class InventoryMessage extends NetworkMessage {
rawBytes[30+equipPointId.length()+i] = intValues[i];
}
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;
}
diff --git a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java
index 40a96433..85380e8a 100644
--- a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java
+++ b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java
@@ -364,6 +364,11 @@ public abstract class NetworkMessage {
rVal = InventoryMessage.parseclientRequestPerformItemActionMessage(byteBuffer);
}
break;
+ case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTCRAFT:
+ if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
+ rVal = InventoryMessage.parseclientRequestCraftMessage(byteBuffer);
+ }
+ break;
}
break;
case TypeBytes.MESSAGE_TYPE_SYNCHRONIZATION:
diff --git a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java
index b103a71a..58831df0 100644
--- a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java
+++ b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java
@@ -151,6 +151,7 @@ public class TypeBytes {
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_CLIENTREQUESTPERFORMITEMACTION = 10;
+ public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTCRAFT = 11;
/*
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_CLIENTREQUESTADDNATURAL_SIZE = 6;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTUPDATETOOLBAR_SIZE = 6;
+ public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTCRAFT_SIZE = 14;
/*
Synchronization subcategories
diff --git a/src/main/java/electrosphere/net/server/protocol/InventoryProtocol.java b/src/main/java/electrosphere/net/server/protocol/InventoryProtocol.java
index 102deddb..dc30c825 100644
--- a/src/main/java/electrosphere/net/server/protocol/InventoryProtocol.java
+++ b/src/main/java/electrosphere/net/server/protocol/InventoryProtocol.java
@@ -1,11 +1,24 @@
package electrosphere.net.server.protocol;
+import java.util.LinkedList;
+import java.util.List;
+
+import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
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.net.parser.net.message.InventoryMessage;
import electrosphere.net.server.ServerConnectionHandler;
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.player.PlayerActions;
@@ -80,6 +93,16 @@ public class InventoryProtocol implements ServerProtocolTemplate reagentList = new LinkedList();
+ 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);
+ }
+ }
+
}
diff --git a/src/main/java/electrosphere/server/datacell/Realm.java b/src/main/java/electrosphere/server/datacell/Realm.java
index ec338a89..3ce2f265 100644
--- a/src/main/java/electrosphere/server/datacell/Realm.java
+++ b/src/main/java/electrosphere/server/datacell/Realm.java
@@ -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
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
EntityDataCellMapper entityDataCellMapper;
@@ -261,6 +266,14 @@ public class Realm {
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
* @param sceneInstanceId The instance id
diff --git a/src/main/java/electrosphere/server/datacell/ServerDataCell.java b/src/main/java/electrosphere/server/datacell/ServerDataCell.java
index 16ef3066..d04efad1 100644
--- a/src/main/java/electrosphere/server/datacell/ServerDataCell.java
+++ b/src/main/java/electrosphere/server/datacell/ServerDataCell.java
@@ -132,7 +132,7 @@ public class ServerDataCell {
* Sends the current state of the datacell to the player
* 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()){
this.serializeEntityToPlayer(entity,player);
}
diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java
index 87957b7e..5f4182e8 100644
--- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java
+++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java
@@ -14,6 +14,7 @@ import electrosphere.game.server.world.ServerWorldData;
import electrosphere.server.terrain.generation.heightmap.EmptySkyGen;
import electrosphere.server.terrain.generation.heightmap.HeightmapGenerator;
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.interfaces.ChunkGenerator;
import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel;
@@ -84,11 +85,12 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
*/
public TestGenerationChunkGenerator(ServerWorldData serverWorldData, boolean useJavascript){
this.serverWorldData = serverWorldData;
- registerHeightmapGenerator(new EmptySkyGen());
- registerHeightmapGenerator(new HillsGen());
- registerHeightmapGenerator(new PlainsGen());
- registerVoxelGenerator(new HillsVoxelGen());
- registerVoxelGenerator(new AnimeMountainsGen());
+ this.registerHeightmapGenerator(new EmptySkyGen());
+ this.registerHeightmapGenerator(new HillsGen());
+ this.registerHeightmapGenerator(new PlainsGen());
+ this.registerHeightmapGenerator(new MountainGen());
+ this.registerVoxelGenerator(new HillsVoxelGen());
+ this.registerVoxelGenerator(new AnimeMountainsGen());
this.useJavascript = useJavascript;
}
@@ -121,6 +123,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
HeightmapGenerator heightmapGen = this.tagHeightmapMap.get(surfaceParams.getSurfaceGenTag());
+ heightmapGen = this.tagHeightmapMap.get("mountains");
if(heightmapGen == null){
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
}
diff --git a/src/main/java/electrosphere/server/terrain/generation/heightmap/MountainGen.java b/src/main/java/electrosphere/server/terrain/generation/heightmap/MountainGen.java
new file mode 100644
index 00000000..667c58cd
--- /dev/null
+++ b/src/main/java/electrosphere/server/terrain/generation/heightmap/MountainGen.java
@@ -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";
+ }
+}