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,
"graphicsPerformanceDrawShadows" : true,
"graphicsPerformanceOIT" : true,
"graphicsPerformanceEnableFoliageManager" : true,
"graphicsPerformanceEnableFoliageManager" : false,
"graphicsViewRange" : 20000.0,
"renderResolutionX": 1920,

View File

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

View File

@ -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

View File

@ -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

View File

@ -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"
]
}
]
}

View File

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

View File

@ -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<RecipeData> 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

View File

@ -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){

View File

@ -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

View File

@ -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;
}
}

View File

@ -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: {

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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<Entity> getItems(){
return items.values();
}
/**
* Gets the item slot for a given item
* @param item The item

View File

@ -16,14 +16,26 @@ import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils;
*/
public class ServerInventoryState implements BehaviorTree {
/**
* The queue of messages to handle
*/
CopyOnWriteArrayList<InventoryMessage> networkMessageQueue = new CopyOnWriteArrayList<InventoryMessage>();
/**
* 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);
}

View File

@ -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);
}
/**

View File

@ -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

View File

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

View File

@ -22,6 +22,7 @@ public class InventoryMessage extends NetworkMessage {
CLIENTREQUESTADDNATURAL,
CLIENTUPDATETOOLBAR,
CLIENTREQUESTPERFORMITEMACTION,
CLIENTREQUESTCRAFT,
}
/**
@ -39,6 +40,8 @@ public class InventoryMessage extends NetworkMessage {
double viewTargetX;
double viewTargetY;
double viewTargetZ;
int stationId;
int recipeId;
/**
* Constructor
@ -207,6 +210,34 @@ public class InventoryMessage extends NetworkMessage {
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
* @param byteBuffer The buffer
@ -261,6 +292,12 @@ public class InventoryMessage extends NetworkMessage {
}
case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION:
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;
}
@ -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;
}

View File

@ -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:

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_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

View File

@ -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<InventoryMessag
InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
}
} 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 SERVERCOMMANDMOVEITEMCONTAINER:
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
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

View File

@ -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);
}

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.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());
}

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