From 161f40ee04b81453412f95d4baafdcd6ab6489a9 Mon Sep 17 00:00:00 2001 From: austin Date: Sat, 27 Jul 2024 00:15:50 -0400 Subject: [PATCH] synchronizing object entities --- assets/Data/objects.json | 3 +- assets/Data/objects/game_objects.json | 14 +++ docs/src/progress/renderertodo.md | 4 +- net/entity.json | 11 +++ .../client/scene/ClientSceneWrapper.java | 8 +- .../java/electrosphere/engine/Globals.java | 4 - .../engine/loadingthreads/LoadingUtils.java | 8 +- .../java/electrosphere/entity/EntityTags.java | 1 + .../entity/types/creature/CreatureUtils.java | 5 +- .../entity/types/item/ItemUtils.java | 8 +- .../entity/types/object/ObjectUtils.java | 50 +++++++---- .../net/client/protocol/EntityProtocol.java | 11 ++- .../net/client/protocol/TerrainProtocol.java | 2 +- .../net/parser/net/message/EntityMessage.java | 87 +++++++++++++++++++ .../parser/net/message/NetworkMessage.java | 5 ++ .../net/parser/net/message/TypeBytes.java | 19 ++-- .../server/protocol/CharacterProtocol.java | 11 ++- .../net/server/protocol/EntityProtocol.java | 2 + .../ClientSynchronizationManager.java | 34 ++++++-- .../character/PlayerCharacterCreation.java | 9 +- .../server/content/ServerContentManager.java | 27 +----- .../serialization/ContentSerialization.java | 33 ++++++- .../electrosphere/server/datacell/Realm.java | 29 +++++++ .../server/datacell/ServerDataCell.java | 4 + .../ServerHitboxResolutionCallback.java | 7 +- 25 files changed, 311 insertions(+), 85 deletions(-) create mode 100644 assets/Data/objects/game_objects.json diff --git a/assets/Data/objects.json b/assets/Data/objects.json index 27eccda6..3a1e21fc 100644 --- a/assets/Data/objects.json +++ b/assets/Data/objects.json @@ -27,6 +27,7 @@ "files" : [ "Data/objects/floatingisland.json", "Data/objects/testscene1objects.json", - "Data/objects/debug_objects.json" + "Data/objects/debug_objects.json", + "Data/objects/game_objects.json" ] } \ No newline at end of file diff --git a/assets/Data/objects/game_objects.json b/assets/Data/objects/game_objects.json new file mode 100644 index 00000000..8d4ecbdb --- /dev/null +++ b/assets/Data/objects/game_objects.json @@ -0,0 +1,14 @@ +{ + "objects" : [ + + { + "objectId" : "spawnPoint", + "modelPath" : "Models/basic/geometry/unitcylinder.fbx", + "tokens": [ + "SPAWNPOINT" + ] + } + + ], + "files" : [] +} \ No newline at end of file diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index e2dd7d61..2a753982 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -454,11 +454,11 @@ Devtools for updating first person attachment rotations (07/26/2024) Viewmodel equipped item rotates inverted to bone rotation -Visually block Utility object for reducing boilerplate when writing btree transitions that just play an animation then transition First animations flickering in first person (enforce animation priority requirement) Debug third person animations flickering (attachments not reflecting animations that were played that frame) - +Small data fix +Refactor spawn point to not be global # TODO diff --git a/net/entity.json b/net/entity.json index eaecbf97..e2dec0ac 100644 --- a/net/entity.json +++ b/net/entity.json @@ -149,6 +149,17 @@ "positionZ" ] }, + { + "messageName" : "SpawnObject", + "description" : "Spawns a generic object", + "data" : [ + "entityID", + "creatureTemplate", + "positionX", + "positionY", + "positionZ" + ] + }, { "messageName" : "moveUpdate", "description" : "Updates a client on the move state of an entity", diff --git a/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java b/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java index 4fd508bf..dbaada87 100644 --- a/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java +++ b/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java @@ -101,7 +101,12 @@ public class ClientSceneWrapper { * @return The entity in question */ public Entity getEntityFromServerId(int id){ - return scene.getEntityFromId(mapServerToClientId(id)); + if(serverToClientIdMap.containsKey(id)){ + int clientId = mapServerToClientId(id); + return scene.getEntityFromId(clientId); + } else { + return null; + } } /** @@ -119,6 +124,7 @@ public class ClientSceneWrapper { * @param id The id */ public void dumpIdData(int id){ + LoggerInterface.loggerNetworking.WARNING("Offending ID " + id); LoggerInterface.loggerNetworking.WARNING("Client->Server Map contains? " + clientToServerIdMap.containsKey(id)); LoggerInterface.loggerNetworking.WARNING("Server->Client Map contains? " + serverToClientIdMap.containsKey(id)); if(clientToServerIdMap.containsKey(id)){ diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index 8645f882..0c0f0cfc 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -5,7 +5,6 @@ import java.util.LinkedList; import java.util.List; import org.joml.Matrix4f; -import org.joml.Vector3d; import org.joml.Vector3f; import electrosphere.audio.AudioEngine; @@ -273,9 +272,6 @@ public class Globals { //Engine - Main managers/variables // - //spawn point - public static Vector3d spawnPoint = new Vector3d(0,0,0); - //manages all models loaded into memory public static AssetManager assetManager; diff --git a/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java b/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java index 6380e4f9..949615b4 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java +++ b/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; +import org.joml.Vector3d; import org.joml.Vector3f; import org.joml.Vector3i; @@ -171,10 +172,11 @@ public class LoadingUtils { //set player world-space coordinates Player playerObject = Globals.playerManager.getPlayerFromId(0); Realm realm = Globals.realmManager.getRealms().iterator().next(); + Vector3d spawnPoint = realm.getSpawnPoint(); playerObject.setWorldPos(new Vector3i( - realm.getServerWorldData().convertRealToChunkSpace(Globals.spawnPoint.x), - realm.getServerWorldData().convertRealToChunkSpace(Globals.spawnPoint.y), - realm.getServerWorldData().convertRealToChunkSpace(Globals.spawnPoint.z) + realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.x), + realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.y), + realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.z) )); } diff --git a/src/main/java/electrosphere/entity/EntityTags.java b/src/main/java/electrosphere/entity/EntityTags.java index 8548a9fc..afd9a915 100644 --- a/src/main/java/electrosphere/entity/EntityTags.java +++ b/src/main/java/electrosphere/entity/EntityTags.java @@ -22,6 +22,7 @@ public class EntityTags { public static final String POSEABLE = "poseable"; //is it poseable on server public static final String LIGHT = "light"; public static final String ITEM = "item"; + public static final String OBJECT = "object"; public static final String GRAVITY = "gravity"; public static final String PARTICLE = "particle"; diff --git a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java index 42bba6d8..f0c4e614 100644 --- a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java +++ b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java @@ -665,12 +665,13 @@ public class CreatureUtils { ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.LIFE_STATE); //idle tree & generic stuff all creatures have ServerIdleTree.attachTree(rVal); + CreatureUtils.setFacingVector(rVal, MathUtils.getOriginVector()); + rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true); + //required for synchronization manager ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.CREATURE); EntityUtils.setEntityType(rVal, ENTITY_TYPE_CREATURE); EntityUtils.setEntitySubtype(rVal, type); - CreatureUtils.setFacingVector(rVal, MathUtils.getOriginVector()); - rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true); //position entity //this needs to be called at the end of this function. diff --git a/src/main/java/electrosphere/entity/types/item/ItemUtils.java b/src/main/java/electrosphere/entity/types/item/ItemUtils.java index f9759a08..94843e96 100644 --- a/src/main/java/electrosphere/entity/types/item/ItemUtils.java +++ b/src/main/java/electrosphere/entity/types/item/ItemUtils.java @@ -186,14 +186,16 @@ public class ItemUtils { } } rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true); - EntityUtils.setEntityType(rVal, ENTITY_TYPE_ITEM); rVal.putData(EntityDataStrings.ITEM_IS_IN_INVENTORY, false); - EntityUtils.setEntitySubtype(rVal, name); // rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(0.005f,0.005f,0.005f)); // rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().identity().rotateY((float)(-Math.PI/2)).rotateZ(-(float)(Math.PI/2))); - ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.ITEM); + //required for synchronization manager + ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.ITEM); + EntityUtils.setEntityType(rVal, ENTITY_TYPE_ITEM); + EntityUtils.setEntitySubtype(rVal, name); + //position entity //this needs to be called at the end of this function. diff --git a/src/main/java/electrosphere/entity/types/object/ObjectUtils.java b/src/main/java/electrosphere/entity/types/object/ObjectUtils.java index 13072c9e..c4974e59 100644 --- a/src/main/java/electrosphere/entity/types/object/ObjectUtils.java +++ b/src/main/java/electrosphere/entity/types/object/ObjectUtils.java @@ -26,7 +26,9 @@ import electrosphere.entity.state.inventory.UnrelationalInventoryState; import electrosphere.entity.types.collision.CollisionObjUtils; import electrosphere.game.data.collidable.CollidableTemplate; import electrosphere.game.data.object.type.ObjectData; -import electrosphere.renderer.actor.ActorUtils; +import electrosphere.net.parser.net.message.EntityMessage; +import electrosphere.net.parser.net.message.NetworkMessage; +import electrosphere.net.server.player.Player; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils; import electrosphere.server.datacell.utils.ServerEntityTagUtils; @@ -48,12 +50,8 @@ public class ObjectUtils { EntityCreationUtils.makeEntityDrawable(rVal, rawType.getModelPath()); } //forward-searching tokens - boolean collisionMakeDynamic = true; for(String token : rawType.getTokens()){ switch(token){ - case "DISABLE_COLLISION_REACTION": { - collisionMakeDynamic = false; - } break; case "GENERATE_COLLISION_OBJECT": { Globals.assetManager.addCollisionMeshToQueue(new PhysicsMeshQueueItem(Globals.clientSceneWrapper.getCollisionEngine(),rawType.getModelPath())); Globals.clientSceneWrapper.getScene().registerBehaviorTree(new BehaviorTree() {public void simulate(float deltaTime) { @@ -86,9 +84,6 @@ public class ObjectUtils { //tokens for(String token : rawType.getTokens()){ switch(token){ - case "BLENDER_TRANSFORM": - ActorUtils.applyBlenderTransformer(rVal); - break; case "GRAVITY": Collidable collidable = (Collidable)rVal.getData(EntityDataStrings.PHYSICS_COLLIDABLE); DBody collisionObject = (DBody)rVal.getData(EntityDataStrings.PHYSICS_COLLISION_BODY); @@ -108,6 +103,9 @@ public class ObjectUtils { case "TERRAIN_COLLISION": { CollisionObjUtils.getCollidable(rVal).overrideType(Collidable.TYPE_TERRAIN); } break; + case "SPAWNPOINT": { + //ignore on client + } break; } } if(rawType.getHitboxData() != null){ @@ -139,12 +137,8 @@ public class ObjectUtils { EntityCreationUtils.makeEntityPoseable(rVal, rawType.getModelPath()); } //forward-searching tokens - boolean collisionMakeDynamic = true; for(String token : rawType.getTokens()){ switch(token){ - case "DISABLE_COLLISION_REACTION": { - collisionMakeDynamic = false; - } break; case "GENERATE_COLLISION_OBJECT": { Globals.assetManager.addCollisionMeshToQueue(new PhysicsMeshQueueItem(realm.getCollisionEngine(),rawType.getModelPath())); ServerBehaviorTreeUtils.attachBTreeToEntity(rVal, new BehaviorTree() {public void simulate(float deltaTime) { @@ -177,9 +171,6 @@ public class ObjectUtils { //tokens for(String token : rawType.getTokens()){ switch(token){ - case "BLENDER_TRANSFORM": - ActorUtils.applyBlenderTransformer(rVal); - break; case "GRAVITY": Collidable collidable = (Collidable)rVal.getData(EntityDataStrings.PHYSICS_COLLIDABLE); DBody collisionObject = (DBody)rVal.getData(EntityDataStrings.PHYSICS_COLLISION_BODY); @@ -199,6 +190,9 @@ public class ObjectUtils { case "TERRAIN_COLLISION": { CollisionObjUtils.getCollidable(rVal).overrideType(Collidable.TYPE_TERRAIN); } break; + case "SPAWNPOINT": { + realm.registerSpawnPoint(position); + } break; } } if(rawType.getHitboxData() != null){ @@ -208,6 +202,12 @@ public class ObjectUtils { ServerIdleTree.attachTree(rVal); + //required for synchronization manager + ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.OBJECT); + EntityUtils.setEntityType(rVal, ENTITY_TYPE_OBJECT); + EntityUtils.setEntitySubtype(rVal, type); + + //position entity //this needs to be called at the end of this function. //Burried underneath this is function call to initialize a server side entity. @@ -239,4 +239,24 @@ public class ObjectUtils { return (String)EntityUtils.getEntitySubtype(e); } + /** + * Sets the object to a given player + * @param player The player + * @param item The object entity + */ + public static void sendEntityToPlayer(Player player, Entity object){ + int id = object.getId(); + String type = ObjectUtils.getType(object); + Vector3d position = EntityUtils.getPosition(object); + //construct the spawn message and attach to player + NetworkMessage message = EntityMessage.constructSpawnObjectMessage( + id, + type, + position.x, + position.y, + position.z + ); + player.addMessage(message); + } + } diff --git a/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java b/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java index 6efa3ca8..8463c765 100644 --- a/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java @@ -7,7 +7,6 @@ import electrosphere.engine.Globals; import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.Entity; import electrosphere.entity.EntityCreationUtils; -import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.attack.ClientAttackTree; import electrosphere.entity.state.client.firstPerson.FirstPersonTree; @@ -16,6 +15,7 @@ import electrosphere.entity.types.creature.CreatureTemplate; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.foliage.FoliageUtils; import electrosphere.entity.types.item.ItemUtils; +import electrosphere.entity.types.object.ObjectUtils; import electrosphere.game.data.creature.type.CreatureType; import electrosphere.game.data.creature.type.ViewModelData; import electrosphere.logger.LoggerInterface; @@ -86,6 +86,15 @@ public class EntityProtocol { ClientEntityUtils.initiallyPositionEntity(newlySpawnedEntity, new Vector3d(message.getpositionX(),message.getpositionY(),message.getpositionZ())); Globals.clientSceneWrapper.mapIdToId(newlySpawnedEntity.getId(), message.getentityID()); } break; + case SPAWNOBJECT: { + LoggerInterface.loggerNetworking.DEBUG("Spawn object " + message.getentityID() + " at " + message.getpositionX() + " " + message.getpositionY() + " " + message.getpositionZ()); + //spawn item + String objectType = message.getcreatureTemplate(); + newlySpawnedEntity = ObjectUtils.clientSpawnBasicObject(objectType); + //position + ClientEntityUtils.initiallyPositionEntity(newlySpawnedEntity, new Vector3d(message.getpositionX(),message.getpositionY(),message.getpositionZ())); + Globals.clientSceneWrapper.mapIdToId(newlySpawnedEntity.getId(), message.getentityID()); + } break; diff --git a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java index 77b94d4a..328285b7 100644 --- a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java @@ -28,7 +28,7 @@ public class TerrainProtocol { Globals.clientConnection.getClientProtocol().setHasReceivedWorld(true); break; case SPAWNPOSITION: - Globals.spawnPoint.set(message.getrealLocationX(),0.25,message.getrealLocationZ()); + LoggerInterface.loggerNetworking.WARNING("Received spawnPosition packet on client. This is deprecated!"); break; case SENDCHUNKDATA: Globals.clientTerrainManager.attachTerrainMessage(message); diff --git a/src/main/java/electrosphere/net/parser/net/message/EntityMessage.java b/src/main/java/electrosphere/net/parser/net/message/EntityMessage.java index 9f9a58d9..feaa5022 100644 --- a/src/main/java/electrosphere/net/parser/net/message/EntityMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/EntityMessage.java @@ -11,6 +11,7 @@ public class EntityMessage extends NetworkMessage { CREATE, SPAWNCREATURE, SPAWNITEM, + SPAWNOBJECT, MOVEUPDATE, ATTACKUPDATE, STARTATTACK, @@ -288,6 +289,8 @@ public class EntityMessage extends NetworkMessage { return EntityMessage.canParseSpawnCreatureMessage(byteBuffer); case TypeBytes.ENTITY_MESSAGE_TYPE_SPAWNITEM: return EntityMessage.canParseSpawnItemMessage(byteBuffer); + case TypeBytes.ENTITY_MESSAGE_TYPE_SPAWNOBJECT: + return EntityMessage.canParseSpawnObjectMessage(byteBuffer); case TypeBytes.ENTITY_MESSAGE_TYPE_MOVEUPDATE: if(byteBuffer.getRemaining() >= TypeBytes.ENTITY_MESSAGE_TYPE_MOVEUPDATE_SIZE){ return true; @@ -502,6 +505,59 @@ public class EntityMessage extends NetworkMessage { return rVal; } + public static boolean canParseSpawnObjectMessage(CircularByteBuffer byteBuffer){ + int currentStreamLength = byteBuffer.getRemaining(); + List temporaryByteQueue = new LinkedList(); + if(currentStreamLength < 6){ + return false; + } + int creatureTemplateSize = 0; + if(currentStreamLength < 10){ + return false; + } else { + temporaryByteQueue.add(byteBuffer.peek(6 + 0)); + temporaryByteQueue.add(byteBuffer.peek(6 + 1)); + temporaryByteQueue.add(byteBuffer.peek(6 + 2)); + temporaryByteQueue.add(byteBuffer.peek(6 + 3)); + creatureTemplateSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue); + } + if(currentStreamLength < 10 + creatureTemplateSize){ + return false; + } + if(currentStreamLength < 18 + creatureTemplateSize){ + return false; + } + if(currentStreamLength < 26 + creatureTemplateSize){ + return false; + } + if(currentStreamLength < 34 + creatureTemplateSize){ + return false; + } + return true; + } + + public static EntityMessage parseSpawnObjectMessage(CircularByteBuffer byteBuffer){ + EntityMessage rVal = new EntityMessage(EntityMessageType.SPAWNOBJECT); + stripPacketHeader(byteBuffer); + rVal.setentityID(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setcreatureTemplate(ByteStreamUtils.popStringFromByteQueue(byteBuffer)); + rVal.setpositionX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer)); + rVal.setpositionY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer)); + rVal.setpositionZ(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer)); + return rVal; + } + + public static EntityMessage constructSpawnObjectMessage(int entityID,String creatureTemplate,double positionX,double positionY,double positionZ){ + EntityMessage rVal = new EntityMessage(EntityMessageType.SPAWNOBJECT); + rVal.setentityID(entityID); + rVal.setcreatureTemplate(creatureTemplate); + rVal.setpositionX(positionX); + rVal.setpositionY(positionY); + rVal.setpositionZ(positionZ); + rVal.serialize(); + return rVal; + } + public static EntityMessage parsemoveUpdateMessage(CircularByteBuffer byteBuffer){ EntityMessage rVal = new EntityMessage(EntityMessageType.MOVEUPDATE); stripPacketHeader(byteBuffer); @@ -857,6 +913,37 @@ public class EntityMessage extends NetworkMessage { rawBytes[26+creatureTemplate.length()+i] = intValues[i]; } break; + case SPAWNOBJECT: + rawBytes = new byte[2+4+4+creatureTemplate.length()+8+8+8]; + //message header + rawBytes[0] = TypeBytes.MESSAGE_TYPE_ENTITY; + //entity messaage header + rawBytes[1] = TypeBytes.ENTITY_MESSAGE_TYPE_SPAWNOBJECT; + intValues = ByteStreamUtils.serializeIntToBytes(entityID); + for(int i = 0; i < 4; i++){ + rawBytes[2+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(creatureTemplate.length()); + for(int i = 0; i < 4; i++){ + rawBytes[6+i] = intValues[i]; + } + stringBytes = creatureTemplate.getBytes(); + for(int i = 0; i < creatureTemplate.length(); i++){ + rawBytes[10+i] = stringBytes[i]; + } + intValues = ByteStreamUtils.serializeDoubleToBytes(positionX); + for(int i = 0; i < 8; i++){ + rawBytes[10+creatureTemplate.length()+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeDoubleToBytes(positionY); + for(int i = 0; i < 8; i++){ + rawBytes[18+creatureTemplate.length()+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeDoubleToBytes(positionZ); + for(int i = 0; i < 8; i++){ + rawBytes[26+creatureTemplate.length()+i] = intValues[i]; + } + break; case MOVEUPDATE: rawBytes = new byte[2+4+8+8+8+8+8+8+8+8+8+4+4]; //message header 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 466abce3..f6ae012e 100644 --- a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java @@ -56,6 +56,11 @@ SYNCHRONIZATION_MESSAGE, rVal = EntityMessage.parseSpawnItemMessage(byteBuffer); } break; + case TypeBytes.ENTITY_MESSAGE_TYPE_SPAWNOBJECT: + if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ + rVal = EntityMessage.parseSpawnObjectMessage(byteBuffer); + } + break; case TypeBytes.ENTITY_MESSAGE_TYPE_MOVEUPDATE: if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ rVal = EntityMessage.parsemoveUpdateMessage(byteBuffer); 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 e521450e..96eefe57 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java +++ b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java @@ -20,15 +20,16 @@ Message categories public static final byte ENTITY_MESSAGE_TYPE_CREATE = 0; public static final byte ENTITY_MESSAGE_TYPE_SPAWNCREATURE = 1; public static final byte ENTITY_MESSAGE_TYPE_SPAWNITEM = 2; - public static final byte ENTITY_MESSAGE_TYPE_MOVEUPDATE = 3; - public static final byte ENTITY_MESSAGE_TYPE_ATTACKUPDATE = 4; - public static final byte ENTITY_MESSAGE_TYPE_STARTATTACK = 5; - public static final byte ENTITY_MESSAGE_TYPE_KILL = 6; - public static final byte ENTITY_MESSAGE_TYPE_DESTROY = 7; - public static final byte ENTITY_MESSAGE_TYPE_SETPROPERTY = 8; - public static final byte ENTITY_MESSAGE_TYPE_ATTACHENTITYTOENTITY = 9; - public static final byte ENTITY_MESSAGE_TYPE_SPAWNFOLIAGESEED = 10; - public static final byte ENTITY_MESSAGE_TYPE_UPDATEENTITYVIEWDIR = 11; + public static final byte ENTITY_MESSAGE_TYPE_SPAWNOBJECT = 3; + public static final byte ENTITY_MESSAGE_TYPE_MOVEUPDATE = 4; + public static final byte ENTITY_MESSAGE_TYPE_ATTACKUPDATE = 5; + public static final byte ENTITY_MESSAGE_TYPE_STARTATTACK = 6; + public static final byte ENTITY_MESSAGE_TYPE_KILL = 7; + public static final byte ENTITY_MESSAGE_TYPE_DESTROY = 8; + public static final byte ENTITY_MESSAGE_TYPE_SETPROPERTY = 9; + public static final byte ENTITY_MESSAGE_TYPE_ATTACHENTITYTOENTITY = 10; + public static final byte ENTITY_MESSAGE_TYPE_SPAWNFOLIAGESEED = 11; + public static final byte ENTITY_MESSAGE_TYPE_UPDATEENTITYVIEWDIR = 12; /* Entity packet sizes */ diff --git a/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java b/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java index 4e255f9c..75abca93 100644 --- a/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java @@ -1,5 +1,7 @@ package electrosphere.net.server.protocol; +import org.joml.Vector3d; + import electrosphere.engine.Globals; import electrosphere.entity.types.creature.CreatureTemplate; import electrosphere.net.parser.net.message.CharacterMessage; @@ -64,17 +66,18 @@ public class CharacterProtocol { static void spawnEntityForClient(ServerConnectionHandler connectionHandler){ PlayerCharacterCreation.spawnPlayerCharacter(connectionHandler); Realm realm = Globals.playerManager.getPlayerRealm(connectionHandler.getPlayer()); + Vector3d spawnPoint = realm.getSpawnPoint(); //set client initial discrete position connectionHandler.addMessagetoOutgoingQueue( PlayerMessage.constructSetInitialDiscretePositionMessage( - realm.getServerWorldData().convertRealToChunkSpace(Globals.spawnPoint.x), - realm.getServerWorldData().convertRealToChunkSpace(Globals.spawnPoint.y), - realm.getServerWorldData().convertRealToChunkSpace(Globals.spawnPoint.z) + realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.x), + realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.y), + realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.z) ) ); //send spawn point connectionHandler.addMessagetoOutgoingQueue( - TerrainMessage.constructSpawnPositionMessage(Globals.spawnPoint.x, Globals.spawnPoint.y, Globals.spawnPoint.z) + TerrainMessage.constructSpawnPositionMessage(spawnPoint.x, spawnPoint.y, spawnPoint.z) ); } diff --git a/src/main/java/electrosphere/net/server/protocol/EntityProtocol.java b/src/main/java/electrosphere/net/server/protocol/EntityProtocol.java index f96288e1..bd91a43c 100644 --- a/src/main/java/electrosphere/net/server/protocol/EntityProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/EntityProtocol.java @@ -48,6 +48,7 @@ public class EntityProtocol { case SETPROPERTY: case SPAWNFOLIAGESEED: case SPAWNITEM: + case SPAWNOBJECT: //silently ignore break; } @@ -72,6 +73,7 @@ public class EntityProtocol { case SETPROPERTY: case SPAWNFOLIAGESEED: case SPAWNITEM: + case SPAWNOBJECT: //silently ignore break; } diff --git a/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java b/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java index 69554c7c..bcf9f1e9 100644 --- a/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java +++ b/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java @@ -3,21 +3,17 @@ package electrosphere.net.synchronization; import electrosphere.net.synchronization.FieldIdEnums; import electrosphere.entity.state.block.ClientBlockTree; -import electrosphere.entity.state.block.ServerBlockTree; import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree; -import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree; import electrosphere.logger.LoggerInterface; import electrosphere.entity.state.equip.ClientEquipState; import electrosphere.entity.state.attack.ClientAttackTree; -import electrosphere.entity.state.attack.ServerAttackTree; -import electrosphere.entity.state.equip.ServerEquipState; import electrosphere.entity.state.gravity.ClientGravityTree; -import electrosphere.entity.state.gravity.ServerGravityTree; -import electrosphere.entity.state.idle.ServerIdleTree; import electrosphere.entity.state.idle.ClientIdleTree; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import electrosphere.engine.Globals; @@ -32,6 +28,12 @@ public class ClientSynchronizationManager { //The list of messages to loop through List messages = new CopyOnWriteArrayList(); + //Map that tracks the number of times a network message bounces + Map messageBounceCount = new HashMap(); + + //the count at which to warn about a message bouncing + static final int MESSAGE_BOUNCE_WARNING_COUNT = 10; + /** * Pushes a message into the queue to be processed * @param message The message @@ -46,8 +48,18 @@ public class ClientSynchronizationManager { public void processMessages(){ List messagesToClear = new LinkedList(); for(SynchronizationMessage message : messages){ + + //track number of times this message has bounced + if(messageBounceCount.containsKey(message)){ + messageBounceCount.put(message, messageBounceCount.get(message) + 1); + } else { + messageBounceCount.put(message, 0); + } + + //attempt to handle the message if(Globals.clientSceneWrapper.containsServerId(message.getentityId()) && Globals.clientSceneWrapper.getEntityFromServerId(message.getentityId()) != null){ messagesToClear.add(message); + messageBounceCount.remove(message); switch(message.getMessageSubtype()){ case UPDATECLIENTSTATE:{ int bTreeId = message.getbTreeId(); @@ -71,15 +83,23 @@ public class ClientSynchronizationManager { int entityId = message.getentityId(); } break; } - } else if(Globals.clientSceneWrapper.getEntityFromServerId(message.getentityId()) == null){ + } else if(Globals.clientSceneWrapper.containsServerId(message.getentityId()) && Globals.clientSceneWrapper.getEntityFromServerId(message.getentityId()) == null){ String errorMessage = "Client received synchronization packet for entity that does not exists on client!\n" + + "Specifically, the synchronization manager thinks this id is registered, but there is no entity at that key\n" + "Entity id in network message: " + message.getentityId() ; Globals.clientSceneWrapper.dumpTranslationLayerStatus(); Globals.clientSceneWrapper.dumpIdData(message.getentityId()); throw new IllegalStateException(errorMessage); } + + //warn if a message has bounced a certain number of times + if(messageBounceCount.containsKey(message) && messageBounceCount.get(message) > MESSAGE_BOUNCE_WARNING_COUNT){ + String warningMessage = + "A synchronization message has bounced at least " + MESSAGE_BOUNCE_WARNING_COUNT + "times!"; + LoggerInterface.loggerNetworking.WARNING(warningMessage); + } } for(SynchronizationMessage message : messagesToClear){ messages.remove(message); diff --git a/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java b/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java index 532ce92b..1021522a 100644 --- a/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java +++ b/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java @@ -28,7 +28,8 @@ public class PlayerCharacterCreation { String raceName = template.getCreatureType(); //spawn entity in world Realm realm = Globals.realmManager.getRealms().iterator().next(); - Entity newPlayerEntity = CreatureUtils.serverSpawnBasicCreature(realm,new Vector3d(Globals.spawnPoint.x,Globals.spawnPoint.y,Globals.spawnPoint.z),raceName,template); + Vector3d spawnPoint = realm.getSpawnPoint(); + Entity newPlayerEntity = CreatureUtils.serverSpawnBasicCreature(realm,new Vector3d(spawnPoint.x,spawnPoint.y,spawnPoint.z),raceName,template); int playerCharacterId = newPlayerEntity.getId(); connectionHandler.setPlayerEntityId(playerCharacterId); CreatureUtils.setControllerPlayerId(newPlayerEntity, connectionHandler.getPlayerId()); @@ -37,9 +38,9 @@ public class PlayerCharacterCreation { //attach player object to player character playerObject.setPlayerEntity(newPlayerEntity); playerObject.setWorldPos(new Vector3i( - realm.getServerWorldData().convertRealToChunkSpace(Globals.spawnPoint.x), - realm.getServerWorldData().convertRealToChunkSpace(Globals.spawnPoint.y), - realm.getServerWorldData().convertRealToChunkSpace(Globals.spawnPoint.z) + realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.x), + realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.y), + realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.z) )); realm.getDataCellManager().addPlayerToRealm(playerObject); //parse network messages on client if running diff --git a/src/main/java/electrosphere/server/content/ServerContentManager.java b/src/main/java/electrosphere/server/content/ServerContentManager.java index 8b99b788..d37169e8 100644 --- a/src/main/java/electrosphere/server/content/ServerContentManager.java +++ b/src/main/java/electrosphere/server/content/ServerContentManager.java @@ -6,11 +6,7 @@ import org.joml.Vector3i; import electrosphere.engine.Globals; import electrosphere.entity.Entity; -import electrosphere.entity.EntityUtils; -import electrosphere.entity.types.creature.CreatureUtils; -import electrosphere.entity.types.item.ItemUtils; import electrosphere.server.content.serialization.ContentSerialization; -import electrosphere.server.content.serialization.EntitySerialization; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.ServerDataCell; import electrosphere.server.saves.SaveUtils; @@ -49,7 +45,7 @@ public class ServerContentManager { if(FileUtils.checkSavePathExists(Globals.currentSave.getName(), fullPath)){ //if on disk (has already been generated) ContentSerialization contentRaw = FileUtils.loadObjectFromSavePath(Globals.currentSave.getName(), fullPath, ContentSerialization.class); - hydrateRawContent(realm,cell,contentRaw); + contentRaw.hydrateRawContent(realm,cell); } else { //else create from scratch //UNCOMMENT THIS WHEN YOU WANT CONTENT GENERATED FOR WORLDS AGAIN @@ -60,7 +56,7 @@ public class ServerContentManager { if(FileUtils.checkSavePathExists(Globals.currentSave.getName(), fullPath)){ //if on disk (has already been generated) ContentSerialization contentRaw = FileUtils.loadObjectFromSavePath(Globals.currentSave.getName(), fullPath, ContentSerialization.class); - hydrateRawContent(realm,cell,contentRaw); + contentRaw.hydrateRawContent(realm,cell); } } //TODO: generate navmesh @@ -86,24 +82,5 @@ public class ServerContentManager { FileUtils.serializeObjectToFilePath(fullPath, serialization); } - /** - * Actually creates the entities from a content serialization - * @param contentRaw The content serialization - */ - public void hydrateRawContent(Realm realm, ServerDataCell serverDataCell, ContentSerialization contentRaw){ - List serializedEntities = contentRaw.getSerializedEntities(); - for(EntitySerialization serializedEntity : serializedEntities){ - switch(serializedEntity.getType()){ - case CreatureUtils.ENTITY_TYPE_CREATURE: { - Entity creature = CreatureUtils.serverSpawnBasicCreature(realm, serializedEntity.getPosition(), serializedEntity.getSubtype(), null); - EntityUtils.getRotation(creature).set(serializedEntity.getRotation()); - } break; - case ItemUtils.ENTITY_TYPE_ITEM: { - Entity item = ItemUtils.serverSpawnBasicItem(realm, serializedEntity.getPosition(), serializedEntity.getSubtype()); - } break; - } - } - } - } diff --git a/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java b/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java index a4f7c711..7083caee 100644 --- a/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java +++ b/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java @@ -10,6 +10,8 @@ import electrosphere.entity.types.foliage.FoliageUtils; import electrosphere.entity.types.item.ItemUtils; import electrosphere.entity.types.object.ObjectUtils; import electrosphere.entity.types.structure.StructureUtils; +import electrosphere.server.datacell.Realm; +import electrosphere.server.datacell.ServerDataCell; /** * Contains all content for a given cell @@ -46,7 +48,12 @@ public class ContentSerialization { rVal.serializedEntities.add(serializedEntity); } if(ObjectUtils.isObject(entity)){ - throw new UnsupportedOperationException(); + EntitySerialization serializedEntity = new EntitySerialization(); + serializedEntity.setPosition(EntityUtils.getPosition(entity)); + serializedEntity.setRotation(EntityUtils.getRotation(entity)); + serializedEntity.setType(ObjectUtils.ENTITY_TYPE_OBJECT); + serializedEntity.setSubtype(ObjectUtils.getType(entity)); + rVal.serializedEntities.add(serializedEntity); } if(StructureUtils.isStructure(entity)){ throw new UnsupportedOperationException(); @@ -59,6 +66,30 @@ public class ContentSerialization { return rVal; } + /** + * Actually creates the entities from a content serialization + * @param contentRaw The content serialization + */ + public void hydrateRawContent(Realm realm, ServerDataCell serverDataCell){ + List serializedEntities = this.getSerializedEntities(); + for(EntitySerialization serializedEntity : serializedEntities){ + switch(serializedEntity.getType()){ + case CreatureUtils.ENTITY_TYPE_CREATURE: { + Entity creature = CreatureUtils.serverSpawnBasicCreature(realm, serializedEntity.getPosition(), serializedEntity.getSubtype(), null); + EntityUtils.getRotation(creature).set(serializedEntity.getRotation()); + } break; + case ItemUtils.ENTITY_TYPE_ITEM: { + Entity item = ItemUtils.serverSpawnBasicItem(realm, serializedEntity.getPosition(), serializedEntity.getSubtype()); + EntityUtils.getRotation(item).set(serializedEntity.getRotation()); + } break; + case ObjectUtils.ENTITY_TYPE_OBJECT: { + Entity object = ObjectUtils.serverSpawnBasicObject(realm, serializedEntity.getPosition(), serializedEntity.getSubtype()); + EntityUtils.getRotation(object).set(serializedEntity.getRotation()); + } break; + } + } + } + /** * Gets all serialized entities * @return the list of serialized entities diff --git a/src/main/java/electrosphere/server/datacell/Realm.java b/src/main/java/electrosphere/server/datacell/Realm.java index 2ff732dc..4eee8ff4 100644 --- a/src/main/java/electrosphere/server/datacell/Realm.java +++ b/src/main/java/electrosphere/server/datacell/Realm.java @@ -12,8 +12,12 @@ import electrosphere.server.content.ServerContentManager; import electrosphere.server.datacell.interfaces.DataCellManager; import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; +import org.joml.Vector3d; + /** * Manages data cells on the server side @@ -57,6 +61,11 @@ public class Realm { * The instanceId of the scene that was loaded with this realm */ int sceneInstanceId = NO_SCENE_INSTANCE; + + /** + * The list of available spawnpoints + */ + List spawnPoints = new LinkedList(); /** * Realm constructor @@ -229,6 +238,26 @@ public class Realm { return this.serverContentManager; } + /** + * Gets the spawn point for the realm + * @return The spawn point + */ + public Vector3d getSpawnPoint(){ + if(this.spawnPoints.size() > 0){ + return this.spawnPoints.get(0); + } else { + return new Vector3d(0,0,0); + } + } + + /** + * Registers a spawn point + * @param point The spawn point location + */ + public void registerSpawnPoint(Vector3d point){ + this.spawnPoints.add(point); + } + /** * 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 3103b163..78c362ec 100644 --- a/src/main/java/electrosphere/server/datacell/ServerDataCell.java +++ b/src/main/java/electrosphere/server/datacell/ServerDataCell.java @@ -6,6 +6,7 @@ import electrosphere.entity.Scene; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.foliage.FoliageUtils; import electrosphere.entity.types.item.ItemUtils; +import electrosphere.entity.types.object.ObjectUtils; import electrosphere.entity.types.structure.StructureUtils; import electrosphere.game.server.character.Character; import electrosphere.net.parser.net.message.EntityMessage; @@ -149,6 +150,9 @@ public class ServerDataCell { if(ItemUtils.isItem(entity)){ ItemUtils.sendEntityToPlayer(player, entity); } + if(ObjectUtils.isObject(entity)){ + ObjectUtils.sendEntityToPlayer(player,entity); + } if(StructureUtils.isStructure(entity)){ StructureUtils.sendStructureToPlayer(player, entity); } diff --git a/src/main/java/electrosphere/server/datacell/physics/ServerHitboxResolutionCallback.java b/src/main/java/electrosphere/server/datacell/physics/ServerHitboxResolutionCallback.java index 4b81e511..e0eddad6 100644 --- a/src/main/java/electrosphere/server/datacell/physics/ServerHitboxResolutionCallback.java +++ b/src/main/java/electrosphere/server/datacell/physics/ServerHitboxResolutionCallback.java @@ -17,6 +17,7 @@ import electrosphere.entity.state.movement.ProjectileTree; import electrosphere.entity.types.attach.AttachUtils; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.item.ItemUtils; +import electrosphere.server.datacell.Realm; /** * Callback for managing collisions on the server @@ -64,7 +65,8 @@ public class ServerHitboxResolutionCallback implements CollisionResolutionCallba LifeUtils.getLifeState(receiverParent).damage(damage); if(!LifeUtils.getLifeState(receiverParent).isIsAlive()){ System.out.println("ServerHitboxResolutionCallback - Unimplemented!!"); - EntityUtils.getPosition(receiverParent).set(Globals.spawnPoint); + Realm entityRealm = Globals.realmManager.getEntityRealm(receiverParent); + EntityUtils.getPosition(receiverParent).set(entityRealm.getSpawnPoint()); LifeUtils.getLifeState(receiverParent).revive(); } } @@ -82,7 +84,8 @@ public class ServerHitboxResolutionCallback implements CollisionResolutionCallba LifeUtils.getLifeState(receiverParent).damage(damage); if(!LifeUtils.getLifeState(receiverParent).isIsAlive()){ System.out.println("ServerHitboxResolutionCallback - Unimplemented!!"); - EntityUtils.getPosition(receiverParent).set(Globals.spawnPoint); + Realm entityRealm = Globals.realmManager.getEntityRealm(receiverParent); + EntityUtils.getPosition(receiverParent).set(entityRealm.getSpawnPoint()); LifeUtils.getLifeState(receiverParent).revive(); } }