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"; + } +}