From c7dc9ef07d5fdde77379b0761e302ee2c0d40a7b Mon Sep 17 00:00:00 2001 From: austin Date: Sat, 29 Mar 2025 16:08:37 -0400 Subject: [PATCH] netparser message pooling + multithread chunk io --- docs/src/ideas/optimizationideas.md | 26 +++ docs/src/mainpage.md | 1 + docs/src/progress/renderertodo.md | 8 + .../terrain/cells/VoxelTextureAtlas.java | 26 ++- .../collision/RayCastCallback.java | 45 ++-- .../entity/state/attack/ServerAttackTree.java | 3 + .../net/parser/net/message/AuthMessage.java | 9 +- .../parser/net/message/CharacterMessage.java | 9 +- .../net/parser/net/message/CombatMessage.java | 9 +- .../net/parser/net/message/EntityMessage.java | 59 +++-- .../parser/net/message/InventoryMessage.java | 9 +- .../net/parser/net/message/LoreMessage.java | 9 +- .../net/parser/net/message/MessagePool.java | 176 +++++++++++++++ .../parser/net/message/NetworkMessage.java | 205 +++++++++--------- .../net/parser/net/message/PlayerMessage.java | 9 +- .../net/parser/net/message/ServerMessage.java | 9 +- .../net/message/SynchronizationMessage.java | 9 +- .../parser/net/message/TerrainMessage.java | 89 +++++--- .../net/parser/net/raw/NetworkParser.java | 23 +- .../server/content/ServerContentManager.java | 11 + .../server/datacell/EntityDataCellMapper.java | 21 +- .../gridded/GriddedDataCellLoaderService.java | 60 ++++- .../gridded/GriddedDataCellManager.java | 37 +++- .../datacell/physics/PhysicsDataCell.java | 24 +- 24 files changed, 665 insertions(+), 221 deletions(-) create mode 100644 docs/src/ideas/optimizationideas.md create mode 100644 src/main/java/electrosphere/net/parser/net/message/MessagePool.java diff --git a/docs/src/ideas/optimizationideas.md b/docs/src/ideas/optimizationideas.md new file mode 100644 index 00000000..f787a417 --- /dev/null +++ b/docs/src/ideas/optimizationideas.md @@ -0,0 +1,26 @@ +@page optimizationideas Optimization Ideas + +Automatically transition between instanced actors and non-instanced actors for non-animated models + - Need to keep track of number of entities drawing a model, once that total passes a threshold convert them all to instanced actors + - name it something fun like "HybridActor" or "ShapeshiftActor" + +Merge all kinematic bodies in a scene into one + - Need to keep track of individual entities' shapes after the merge + - When an individual entity dies, remove its shape from the main body + +Second frustum full on shadow map pipeline + - only draw what is both in front of camera AND visible from sky (currently ignores sky's view) + +Move hitbox position updates to behavior tree + - If an entity is not moving (ie a tree), don't have to update the hitbox's position + +On client, only colide hitboxes closest to the player + - Currently collide all hitboxes and it's costly + +Simplify collidable logic + - IE trees don't need to worry about gravity + +In GriddedDataCellManager, skip ServerDataCells if they aren't doing anything interesting + +In GriddedDataCellManager, run unload logic at the same time we're iterating through all cells to simulate the + diff --git a/docs/src/mainpage.md b/docs/src/mainpage.md index ec0a2a87..6b499822 100644 --- a/docs/src/mainpage.md +++ b/docs/src/mainpage.md @@ -5,3 +5,4 @@ - @subpage toolsindex - @subpage highleveldesignindex - @subpage progress + - @subpage optimizationideas diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index b7eb1d3e..4d98c402 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1375,6 +1375,14 @@ Disable kinematic colliders on creation MainContentPipeline reduce allocations Move AI simulation to server level from chunk level +(03/29/2025) +Optimize ray casting near callback +Chunk file operation threadsafing +Multithread entity creation on server cell creation +Network message object pooling +Threadsafe EntityDataCellMapper +Code cleanup +Small ServerAttackTree fix (for when not holding an item) diff --git a/src/main/java/electrosphere/client/terrain/cells/VoxelTextureAtlas.java b/src/main/java/electrosphere/client/terrain/cells/VoxelTextureAtlas.java index fe2f0e42..5261279c 100644 --- a/src/main/java/electrosphere/client/terrain/cells/VoxelTextureAtlas.java +++ b/src/main/java/electrosphere/client/terrain/cells/VoxelTextureAtlas.java @@ -10,21 +10,35 @@ import electrosphere.renderer.texture.Texture; */ public class VoxelTextureAtlas { - //A map of voxel id -> coordinates in the atlas texture for its texture + /** + * A map of voxel id -> coordinates in the atlas texture for its texture + */ Map typeCoordMap = new HashMap(); - //the actual texture + /** + * The actual texture + */ Texture specular; - //the normal texture + /** + * The normal texture + */ Texture normal; - //the width in pixels of a single texture in the atlas + /** + * The width in pixels of a single texture in the atlas + */ public static final int ATLAS_ELEMENT_DIM = 256; - //the width in pixels of the whole atlas texture + + /** + * The width in pixels of the whole atlas texture + */ public static final int ATLAS_DIM = 8192; - //number of textures per row in the atlas + + /** + * Number of textures per row in the atlas + */ public static final int ELEMENTS_PER_ROW = ATLAS_DIM / ATLAS_ELEMENT_DIM; /** diff --git a/src/main/java/electrosphere/collision/RayCastCallback.java b/src/main/java/electrosphere/collision/RayCastCallback.java index 1b12d3d9..654b09ea 100644 --- a/src/main/java/electrosphere/collision/RayCastCallback.java +++ b/src/main/java/electrosphere/collision/RayCastCallback.java @@ -1,8 +1,5 @@ package electrosphere.collision; -import static org.ode4j.ode.OdeConstants.dContactBounce; -import static org.ode4j.ode.OdeConstants.dContactSoftCFM; -import static org.ode4j.ode.OdeConstants.dInfinity; import static org.ode4j.ode.OdeHelper.areConnectedExcluding; import java.util.List; @@ -28,7 +25,7 @@ public class RayCastCallback implements DNearCallback { /** * Maximum number of contacts allowed */ - static final int MAX_CONTACTS = 5; + static final int MAX_CONTACTS = 2; /** * Really far away from the ray origin point @@ -63,6 +60,7 @@ void RayCallback(void *Data, dGeomID Geometry1, dGeomID Geometry2) { public void call(Object data, DGeom o1, DGeom o2) { // if (o1->body && o2->body) return; RayCastCallbackData rayCastData = (RayCastCallbackData)data; + //null out potentially previous results // rayCastData.collisionPosition = null; // rayCastData.collidedEntity = null; @@ -71,33 +69,25 @@ void RayCallback(void *Data, dGeomID Geometry1, dGeomID Geometry2) { DBody b2 = o2.getBody(); if (b1!=null && b2!=null && areConnectedExcluding (b1,b2,DContactJoint.class)) return; - //creates a buffer to store potential collisions - DContactBuffer contacts = new DContactBuffer(MAX_CONTACTS); // up to MAX_CONTACTS contacts per box-box - for (int i=0; i authMessagePool = new LinkedList(); + + /** + * Pools character messages + */ + List characterMessagePool = new LinkedList(); + + /** + * Pools combat messages + */ + List combatMessagePool = new LinkedList(); + + /** + * Pools entity messages + */ + List entityMessagePool = new LinkedList(); + + /** + * Pools inventory messages + */ + List inventoryMessagePool = new LinkedList(); + + /** + * Pools lore messages + */ + List loreMessagePool = new LinkedList(); + + /** + * Pools player messages + */ + List playerMessagePool = new LinkedList(); + + /** + * Pools server messages + */ + List serverMessagePool = new LinkedList(); + + /** + * Pools synchronization messages + */ + List synchronizationMessagePool = new LinkedList(); + + /** + * Pools terrain messages + */ + List terrainMessagePool = new LinkedList(); + + /** + * Lock for thread-safeing operations + */ + ReentrantLock lock = new ReentrantLock(); + + /** + * Gets a network message from the pool. Allocates if no free one is available + * @param type The type of the message + * @return + */ + public NetworkMessage get(MessageType type){ + NetworkMessage rVal = null; + lock.lock(); + if(type == MessageType.AUTH_MESSAGE){ + if(authMessagePool.size() > 0){ + rVal = authMessagePool.remove(0); + } else { + rVal = new AuthMessage(); + } + } else if(type == MessageType.CHARACTER_MESSAGE){ + if(characterMessagePool.size() > 0){ + rVal = characterMessagePool.remove(0); + } else { + rVal = new CharacterMessage(); + } + } else if(type == MessageType.COMBAT_MESSAGE){ + if(combatMessagePool.size() > 0){ + rVal = combatMessagePool.remove(0); + } else { + rVal = new CombatMessage(); + } + } else if(type == MessageType.ENTITY_MESSAGE){ + if(entityMessagePool.size() > 0){ + rVal = entityMessagePool.remove(0); + } else { + rVal = new EntityMessage(); + } + } else if(type == MessageType.INVENTORY_MESSAGE){ + if(inventoryMessagePool.size() > 0){ + rVal = inventoryMessagePool.remove(0); + } else { + rVal = new InventoryMessage(); + } + } else if(type == MessageType.LORE_MESSAGE){ + if(loreMessagePool.size() > 0){ + rVal = loreMessagePool.remove(0); + } else { + rVal = new LoreMessage(); + } + } else if(type == MessageType.PLAYER_MESSAGE){ + if(playerMessagePool.size() > 0){ + rVal = playerMessagePool.remove(0); + } else { + rVal = new PlayerMessage(); + } + } else if(type == MessageType.SERVER_MESSAGE){ + if(serverMessagePool.size() > 0){ + rVal = serverMessagePool.remove(0); + } else { + rVal = new ServerMessage(); + } + } else if(type == MessageType.SYNCHRONIZATION_MESSAGE){ + if(synchronizationMessagePool.size() > 0){ + rVal = synchronizationMessagePool.remove(0); + } else { + rVal = new SynchronizationMessage(); + } + } else if(type == MessageType.TERRAIN_MESSAGE){ + if(terrainMessagePool.size() > 0){ + rVal = terrainMessagePool.remove(0); + } else { + rVal = new TerrainMessage(); + } + } else { + throw new Error("Unsupported message type! " + type); + } + lock.unlock(); + return rVal; + } + + /** + * Releases a message back into the pool + * @param message The message + */ + public void release(NetworkMessage message){ + lock.lock(); + if(message instanceof AuthMessage){ + authMessagePool.add(message); + } else if(message instanceof CharacterMessage){ + characterMessagePool.add(message); + } else if(message instanceof CombatMessage){ + combatMessagePool.add(message); + } else if(message instanceof EntityMessage){ + entityMessagePool.add(message); + } else if(message instanceof InventoryMessage){ + inventoryMessagePool.add(message); + } else if(message instanceof LoreMessage){ + loreMessagePool.add(message); + } else if(message instanceof PlayerMessage){ + playerMessagePool.add(message); + } else if(message instanceof ServerMessage){ + serverMessagePool.add(message); + } else if(message instanceof SynchronizationMessage){ + synchronizationMessagePool.add(message); + } else if(message instanceof TerrainMessage){ + terrainMessagePool.add(message); + } else { + throw new Error("Unsupported message type! " + message.getClass()); + } + lock.unlock(); + } + +} 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 5b7b9c06..4856a261 100644 --- a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java @@ -1,16 +1,16 @@ package electrosphere.net.parser.net.message; -import io.github.studiorailgun.CircularByteBuffer; - -/** - * A network message - */ -public abstract class NetworkMessage { - - /** - * The different categories of network messages - */ - public enum MessageType { +import io.github.studiorailgun.CircularByteBuffer; + +/** + * A network message + */ +public abstract class NetworkMessage { + + /** + * The different categories of network messages + */ + public enum MessageType { ENTITY_MESSAGE, LORE_MESSAGE, PLAYER_MESSAGE, @@ -21,102 +21,103 @@ public abstract class NetworkMessage { INVENTORY_MESSAGE, SYNCHRONIZATION_MESSAGE, COMBAT_MESSAGE, - } - - /** - * The type of this message - */ - MessageType type; - - /** - * Tracks whether the message has been serialized to bytes or not - */ - boolean serialized; - - /** - * The raw bytes contained in the message - */ - byte[] rawBytes; - - /** - * Gets the type of the message - * @return The type of the message - */ - public MessageType getType() { - return type; - } - - /** - * Gets the raw bytes of the message - * @return The raw bytes - */ - public byte[] getRawBytes() { - return rawBytes; - } - - /** - * Parses the byte stream for the next message - * @param byteBuffer The byte buffer - * @return The message if one is at the front of the byte stream, null otherwise - */ - public static NetworkMessage parseBytestreamForMessage(CircularByteBuffer byteBuffer){ - NetworkMessage rVal = null; - byte firstByte; - byte secondByte; - if(byteBuffer.getRemaining() > 1){ - firstByte = byteBuffer.peek(); - switch(firstByte){ + } + + /** + * The type of this message + */ + MessageType type; + + /** + * Tracks whether the message has been serialized to bytes or not + */ + boolean serialized; + + /** + * The raw bytes contained in the message + */ + byte[] rawBytes; + + /** + * Gets the type of the message + * @return The type of the message + */ + public MessageType getType() { + return type; + } + + /** + * Gets the raw bytes of the message + * @return The raw bytes + */ + public byte[] getRawBytes() { + return rawBytes; + } + + /** + * Parses the byte stream for the next message + * @param byteBuffer The byte buffer + * @param pool The message pool + * @return The message if one is at the front of the byte stream, null otherwise + */ + public static NetworkMessage parseBytestreamForMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + NetworkMessage rVal = null; + byte firstByte; + byte secondByte; + if(byteBuffer.getRemaining() > 1){ + firstByte = byteBuffer.peek(); + switch(firstByte){ case TypeBytes.MESSAGE_TYPE_ENTITY: secondByte = byteBuffer.peek(1); switch(secondByte){ case TypeBytes.ENTITY_MESSAGE_TYPE_CREATE: if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = EntityMessage.parseCreateMessage(byteBuffer); + rVal = EntityMessage.parseCreateMessage(byteBuffer,pool); } break; case TypeBytes.ENTITY_MESSAGE_TYPE_MOVEUPDATE: if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = EntityMessage.parsemoveUpdateMessage(byteBuffer); + rVal = EntityMessage.parsemoveUpdateMessage(byteBuffer,pool); } break; case TypeBytes.ENTITY_MESSAGE_TYPE_ATTACKUPDATE: if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = EntityMessage.parseattackUpdateMessage(byteBuffer); + rVal = EntityMessage.parseattackUpdateMessage(byteBuffer,pool); } break; case TypeBytes.ENTITY_MESSAGE_TYPE_STARTATTACK: if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = EntityMessage.parsestartAttackMessage(byteBuffer); + rVal = EntityMessage.parsestartAttackMessage(byteBuffer,pool); } break; case TypeBytes.ENTITY_MESSAGE_TYPE_KILL: if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = EntityMessage.parseKillMessage(byteBuffer); + rVal = EntityMessage.parseKillMessage(byteBuffer,pool); } break; case TypeBytes.ENTITY_MESSAGE_TYPE_DESTROY: if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = EntityMessage.parseDestroyMessage(byteBuffer); + rVal = EntityMessage.parseDestroyMessage(byteBuffer,pool); } break; case TypeBytes.ENTITY_MESSAGE_TYPE_SETPROPERTY: if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = EntityMessage.parsesetPropertyMessage(byteBuffer); + rVal = EntityMessage.parsesetPropertyMessage(byteBuffer,pool); } break; case TypeBytes.ENTITY_MESSAGE_TYPE_ATTACHENTITYTOENTITY: if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = EntityMessage.parseattachEntityToEntityMessage(byteBuffer); + rVal = EntityMessage.parseattachEntityToEntityMessage(byteBuffer,pool); } break; case TypeBytes.ENTITY_MESSAGE_TYPE_UPDATEENTITYVIEWDIR: if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = EntityMessage.parseupdateEntityViewDirMessage(byteBuffer); + rVal = EntityMessage.parseupdateEntityViewDirMessage(byteBuffer,pool); } break; case TypeBytes.ENTITY_MESSAGE_TYPE_SYNCPHYSICS: if(EntityMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = EntityMessage.parsesyncPhysicsMessage(byteBuffer); + rVal = EntityMessage.parsesyncPhysicsMessage(byteBuffer,pool); } break; } @@ -156,82 +157,82 @@ public abstract class NetworkMessage { switch(secondByte){ case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTMETADATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseRequestMetadataMessage(byteBuffer); + rVal = TerrainMessage.parseRequestMetadataMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseResponseMetadataMessage(byteBuffer); + rVal = TerrainMessage.parseResponseMetadataMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTEDITVOXEL: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseRequestEditVoxelMessage(byteBuffer); + rVal = TerrainMessage.parseRequestEditVoxelMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEVOXEL: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseUpdateVoxelMessage(byteBuffer); + rVal = TerrainMessage.parseUpdateVoxelMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseRequestUseTerrainPaletteMessage(byteBuffer); + rVal = TerrainMessage.parseRequestUseTerrainPaletteMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_SPAWNPOSITION: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseSpawnPositionMessage(byteBuffer); + rVal = TerrainMessage.parseSpawnPositionMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseRequestChunkDataMessage(byteBuffer); + rVal = TerrainMessage.parseRequestChunkDataMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parsesendChunkDataMessage(byteBuffer); + rVal = TerrainMessage.parsesendChunkDataMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseRequestReducedChunkDataMessage(byteBuffer); + rVal = TerrainMessage.parseRequestReducedChunkDataMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDCHUNKDATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseSendReducedChunkDataMessage(byteBuffer); + rVal = TerrainMessage.parseSendReducedChunkDataMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDBLOCKDATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseRequestReducedBlockDataMessage(byteBuffer); + rVal = TerrainMessage.parseRequestReducedBlockDataMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDBLOCKDATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseSendReducedBlockDataMessage(byteBuffer); + rVal = TerrainMessage.parseSendReducedBlockDataMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEBLOCK: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseUpdateBlockMessage(byteBuffer); + rVal = TerrainMessage.parseUpdateBlockMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseRequestFluidDataMessage(byteBuffer); + rVal = TerrainMessage.parseRequestFluidDataMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parsesendFluidDataMessage(byteBuffer); + rVal = TerrainMessage.parsesendFluidDataMessage(byteBuffer,pool); } break; case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ - rVal = TerrainMessage.parseupdateFluidDataMessage(byteBuffer); + rVal = TerrainMessage.parseupdateFluidDataMessage(byteBuffer,pool); } break; } @@ -456,23 +457,23 @@ public abstract class NetworkMessage { break; } break; - } - } - return rVal; - } - - /** - * Checks if this message is serialized or not - * @return true if it is serialized, false otherwise - */ - public boolean isSerialized(){ - return serialized; - } - - /** - * Serializes the message - */ - abstract void serialize(); - -} - + } + } + return rVal; + } + + /** + * Checks if this message is serialized or not + * @return true if it is serialized, false otherwise + */ + public boolean isSerialized(){ + return serialized; + } + + /** + * Serializes the message + */ + abstract void serialize(); + +} + diff --git a/src/main/java/electrosphere/net/parser/net/message/PlayerMessage.java b/src/main/java/electrosphere/net/parser/net/message/PlayerMessage.java index 5f6def56..ee21b00a 100644 --- a/src/main/java/electrosphere/net/parser/net/message/PlayerMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/PlayerMessage.java @@ -25,11 +25,18 @@ public class PlayerMessage extends NetworkMessage { * Constructor * @param messageType The type of this message */ - PlayerMessage(PlayerMessageType messageType){ + private PlayerMessage(PlayerMessageType messageType){ this.type = MessageType.PLAYER_MESSAGE; this.messageType = messageType; } + /** + * Constructor + */ + protected PlayerMessage(){ + this.type = MessageType.PLAYER_MESSAGE; + } + public PlayerMessageType getMessageSubtype(){ return this.messageType; } diff --git a/src/main/java/electrosphere/net/parser/net/message/ServerMessage.java b/src/main/java/electrosphere/net/parser/net/message/ServerMessage.java index baf96284..0c96f123 100644 --- a/src/main/java/electrosphere/net/parser/net/message/ServerMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/ServerMessage.java @@ -20,11 +20,18 @@ public class ServerMessage extends NetworkMessage { * Constructor * @param messageType The type of this message */ - ServerMessage(ServerMessageType messageType){ + private ServerMessage(ServerMessageType messageType){ this.type = MessageType.SERVER_MESSAGE; this.messageType = messageType; } + /** + * Constructor + */ + protected ServerMessage(){ + this.type = MessageType.SERVER_MESSAGE; + } + public ServerMessageType getMessageSubtype(){ return this.messageType; } diff --git a/src/main/java/electrosphere/net/parser/net/message/SynchronizationMessage.java b/src/main/java/electrosphere/net/parser/net/message/SynchronizationMessage.java index 2d557d3e..083cde00 100644 --- a/src/main/java/electrosphere/net/parser/net/message/SynchronizationMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/SynchronizationMessage.java @@ -42,11 +42,18 @@ public class SynchronizationMessage extends NetworkMessage { * Constructor * @param messageType The type of this message */ - SynchronizationMessage(SynchronizationMessageType messageType){ + private SynchronizationMessage(SynchronizationMessageType messageType){ this.type = MessageType.SYNCHRONIZATION_MESSAGE; this.messageType = messageType; } + /** + * Constructor + */ + protected SynchronizationMessage(){ + this.type = MessageType.SYNCHRONIZATION_MESSAGE; + } + public SynchronizationMessageType getMessageSubtype(){ return this.messageType; } diff --git a/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java b/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java index d16e07c2..ed1aa14c 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java @@ -64,11 +64,18 @@ public class TerrainMessage extends NetworkMessage { * Constructor * @param messageType The type of this message */ - TerrainMessage(TerrainMessageType messageType){ + private TerrainMessage(TerrainMessageType messageType){ this.type = MessageType.TERRAIN_MESSAGE; this.messageType = messageType; } + /** + * Constructor + */ + protected TerrainMessage(){ + this.type = MessageType.TERRAIN_MESSAGE; + } + public TerrainMessageType getMessageSubtype(){ return this.messageType; } @@ -536,8 +543,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type RequestMetadata */ - public static TerrainMessage parseRequestMetadataMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTMETADATA); + public static TerrainMessage parseRequestMetadataMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.REQUESTMETADATA; stripPacketHeader(byteBuffer); return rVal; } @@ -554,8 +562,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type ResponseMetadata */ - public static TerrainMessage parseResponseMetadataMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA); + public static TerrainMessage parseResponseMetadataMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.RESPONSEMETADATA; stripPacketHeader(byteBuffer); rVal.setworldSizeDiscrete(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldMinX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -586,8 +595,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type RequestEditVoxel */ - public static TerrainMessage parseRequestEditVoxelMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTEDITVOXEL); + public static TerrainMessage parseRequestEditVoxelMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.REQUESTEDITVOXEL; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -620,8 +630,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type UpdateVoxel */ - public static TerrainMessage parseUpdateVoxelMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEVOXEL); + public static TerrainMessage parseUpdateVoxelMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.UPDATEVOXEL; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -654,8 +665,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type RequestUseTerrainPalette */ - public static TerrainMessage parseRequestUseTerrainPaletteMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTUSETERRAINPALETTE); + public static TerrainMessage parseRequestUseTerrainPaletteMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.REQUESTUSETERRAINPALETTE; stripPacketHeader(byteBuffer); rVal.setrealLocationX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer)); rVal.setrealLocationY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer)); @@ -684,8 +696,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type SpawnPosition */ - public static TerrainMessage parseSpawnPositionMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SPAWNPOSITION); + public static TerrainMessage parseSpawnPositionMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.SPAWNPOSITION; stripPacketHeader(byteBuffer); rVal.setrealLocationX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer)); rVal.setrealLocationY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer)); @@ -708,8 +721,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type RequestChunkData */ - public static TerrainMessage parseRequestChunkDataMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTCHUNKDATA); + public static TerrainMessage parseRequestChunkDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.REQUESTCHUNKDATA; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -763,8 +777,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type sendChunkData */ - public static TerrainMessage parsesendChunkDataMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDCHUNKDATA); + public static TerrainMessage parsesendChunkDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.SENDCHUNKDATA; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -789,8 +804,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type RequestReducedChunkData */ - public static TerrainMessage parseRequestReducedChunkDataMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA); + public static TerrainMessage parseRequestReducedChunkDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.REQUESTREDUCEDCHUNKDATA; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -852,8 +868,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type SendReducedChunkData */ - public static TerrainMessage parseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA); + public static TerrainMessage parseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.SENDREDUCEDCHUNKDATA; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -882,8 +899,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type RequestReducedBlockData */ - public static TerrainMessage parseRequestReducedBlockDataMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDBLOCKDATA); + public static TerrainMessage parseRequestReducedBlockDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.REQUESTREDUCEDBLOCKDATA; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -945,8 +963,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type SendReducedBlockData */ - public static TerrainMessage parseSendReducedBlockDataMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDBLOCKDATA); + public static TerrainMessage parseSendReducedBlockDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.SENDREDUCEDBLOCKDATA; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -975,8 +994,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type UpdateBlock */ - public static TerrainMessage parseUpdateBlockMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEBLOCK); + public static TerrainMessage parseUpdateBlockMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.UPDATEBLOCK; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -1009,8 +1029,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type RequestFluidData */ - public static TerrainMessage parseRequestFluidDataMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA); + public static TerrainMessage parseRequestFluidDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.REQUESTFLUIDDATA; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -1064,8 +1085,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type sendFluidData */ - public static TerrainMessage parsesendFluidDataMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA); + public static TerrainMessage parsesendFluidDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.SENDFLUIDDATA; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); @@ -1121,8 +1143,9 @@ public class TerrainMessage extends NetworkMessage { /** * Parses a message of type updateFluidData */ - public static TerrainMessage parseupdateFluidDataMessage(CircularByteBuffer byteBuffer){ - TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA); + public static TerrainMessage parseupdateFluidDataMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.UPDATEFLUIDDATA; stripPacketHeader(byteBuffer); rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); diff --git a/src/main/java/electrosphere/net/parser/net/raw/NetworkParser.java b/src/main/java/electrosphere/net/parser/net/raw/NetworkParser.java index 1422ab13..790a1ceb 100644 --- a/src/main/java/electrosphere/net/parser/net/raw/NetworkParser.java +++ b/src/main/java/electrosphere/net/parser/net/raw/NetworkParser.java @@ -1,6 +1,7 @@ -package electrosphere.net.parser.net.raw; - -import electrosphere.net.parser.net.message.NetworkMessage; +package electrosphere.net.parser.net.raw; + +import electrosphere.net.parser.net.message.MessagePool; +import electrosphere.net.parser.net.message.NetworkMessage; import io.github.studiorailgun.CircularByteBuffer; import java.io.IOException; import java.io.InputStream; @@ -48,6 +49,11 @@ public class NetworkParser { */ CircularByteBuffer incomingByteBuffer = new CircularByteBuffer(CIRCULAR_BUFFER_SIZE); + /** + * Message object pool + */ + MessagePool pool = new MessagePool(); + /** * The block array used to read blocks of bytes in */ @@ -91,7 +97,7 @@ public class NetworkParser { //parse byte queue for messages //for each message, append to clientIncomingMessageQueue NetworkMessage newMessage; - while((newMessage = NetworkMessage.parseBytestreamForMessage(incomingByteBuffer))!=null){ + while((newMessage = NetworkMessage.parseBytestreamForMessage(incomingByteBuffer,this.pool))!=null){ incomingMessageQueue.add(newMessage); } } @@ -104,6 +110,7 @@ public class NetworkParser { for(NetworkMessage message : outgoingMessageQueue){ outgoingMessageQueue.remove(message); outgoingStream.write(message.getRawBytes()); + pool.release(message); } } @@ -154,5 +161,13 @@ public class NetworkParser { public long getNumberOfBytesRead(){ return totalBytesRead; } + + /** + * Releases a network message object back into the pool + * @param message The message + */ + public void release(NetworkMessage message){ + this.pool.release(message); + } } diff --git a/src/main/java/electrosphere/server/content/ServerContentManager.java b/src/main/java/electrosphere/server/content/ServerContentManager.java index 9c079fbf..0fd32bc2 100644 --- a/src/main/java/electrosphere/server/content/ServerContentManager.java +++ b/src/main/java/electrosphere/server/content/ServerContentManager.java @@ -84,5 +84,16 @@ public class ServerContentManager { FileUtils.serializeObjectToFilePath(fullPath, serialization); } + /** + * Saves a collection of serialized entities to disk + * @param locationKey The location key to save under + * @param serialization The collection of entities to save + */ + public void saveSerializationToDisk(Long locationKey, ContentSerialization serialization){ + String dirPath = SaveUtils.deriveSaveDirectoryPath(Globals.currentSave.getName()); + String fullPath = dirPath + "/content/" + locationKey + ".dat"; + FileUtils.serializeObjectToFilePath(fullPath, serialization); + } + } diff --git a/src/main/java/electrosphere/server/datacell/EntityDataCellMapper.java b/src/main/java/electrosphere/server/datacell/EntityDataCellMapper.java index 7f4da3db..f6f5786e 100644 --- a/src/main/java/electrosphere/server/datacell/EntityDataCellMapper.java +++ b/src/main/java/electrosphere/server/datacell/EntityDataCellMapper.java @@ -2,6 +2,7 @@ package electrosphere.server.datacell; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; import electrosphere.entity.Entity; @@ -10,19 +11,28 @@ import electrosphere.entity.Entity; */ public class EntityDataCellMapper { - // The map of entity -> server data cell + /** + * The map of entity -> server data cell + */ Map entityDataCellMap = new HashMap(); + /** + * Lock for thread safe-ing the mapper + */ + ReentrantLock lock = new ReentrantLock(); + /** * Registers an entity into the map. Should be called every time any entity is created on the server. * @param entity The entity that was just created * @param serverDataCell The server data cell to register this entity to */ public void registerEntity(Entity entity, ServerDataCell serverDataCell){ + lock.lock(); if(serverDataCell == null){ throw new Error("Mapping entity to null!"); } entityDataCellMap.put(entity, serverDataCell); + lock.unlock(); } /** @@ -31,7 +41,10 @@ public class EntityDataCellMapper { * @return The server data cell that the entity is inside of */ public ServerDataCell getEntityDataCell(Entity entity){ - return entityDataCellMap.get(entity); + lock.lock(); + ServerDataCell rVal = entityDataCellMap.get(entity); + lock.unlock(); + return rVal; } /** @@ -40,10 +53,12 @@ public class EntityDataCellMapper { * @param serverDataCell The new server data cell for the entity */ public void updateEntityCell(Entity entity, ServerDataCell serverDataCell){ + lock.lock(); if(serverDataCell == null){ throw new Error("Passing null to cell mapper update! " + entity + " " + serverDataCell); } entityDataCellMap.put(entity, serverDataCell); + lock.unlock(); } /** @@ -51,7 +66,9 @@ public class EntityDataCellMapper { * @param entity The entity to eject */ public void ejectEntity(Entity entity){ + lock.lock(); entityDataCellMap.remove(entity); + lock.unlock(); } } diff --git a/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellLoaderService.java b/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellLoaderService.java index 22180456..17f4b3cc 100644 --- a/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellLoaderService.java +++ b/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellLoaderService.java @@ -2,6 +2,7 @@ package electrosphere.server.datacell.gridded; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -30,6 +31,62 @@ public class GriddedDataCellLoaderService { */ private static final Map> queuedWorkLock = new HashMap>(); + /** + * Map of a job to its corresponding work + */ + private static final Map jobOperationMap = new HashMap(); + + /** + * Queues an operation that requires a read or write of a location's file data. + * Guarantees that all operations will be run in order without losing any work. + * @param key The key for the cell + * @param operation The operation to perform + */ + protected static void queueLocationBasedOperation(long key, Runnable operation){ + lock.lock(); + //if there is a job queued and we couldn't cancel it, wait + Future job = queuedWorkLock.get(key); + Runnable opCallback = () -> { + //work here + operation.run(); + //let the service know we've finished this job + lock.lock(); + queuedWorkLock.remove(key); + lock.unlock(); + }; + if(job != null){ + if(!job.cancel(false)){ + try { + Globals.profiler.beginCpuSample("Waiting for cell io job to finish"); + job.get(); + Globals.profiler.endCpuSample(); + } catch (InterruptedException e) { + LoggerInterface.loggerEngine.ERROR("Failed to wait for previous job for cell!", e); + } catch (ExecutionException e) { + LoggerInterface.loggerEngine.ERROR("Previous job for cell threw an error!", e); + } + } + if(job.isCancelled()){ + Runnable oldOp = jobOperationMap.remove(key); + //queue job to run the old operation first, then the new one + opCallback = () -> { + oldOp.run(); + //load here + operation.run(); + //let the service know we've finished this job + lock.lock(); + queuedWorkLock.remove(key); + lock.unlock(); + }; + } + } + //queue job to do the operation + Future newJob = ioThreadService.submit(opCallback); + queuedWorkLock.put(key, newJob); + jobOperationMap.put(key,operation); + lock.unlock(); + } + /** * Loads a cell * @param key The key for the cell @@ -48,7 +105,7 @@ public class GriddedDataCellLoaderService { } } //queue job to load the cell - ioThreadService.submit(() -> { + Future newJob = ioThreadService.submit(() -> { //load here loadLogic.run(); //let the service know we've finished this job @@ -56,6 +113,7 @@ public class GriddedDataCellLoaderService { queuedWorkLock.remove(key); lock.unlock(); }); + queuedWorkLock.put(key, newJob); lock.unlock(); } diff --git a/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java b/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java index d200e13a..95339352 100644 --- a/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java +++ b/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java @@ -32,6 +32,7 @@ import electrosphere.net.server.player.Player; import electrosphere.net.server.protocol.TerrainProtocol; import electrosphere.server.block.manager.ServerBlockManager; import electrosphere.server.content.ServerContentManager; +import electrosphere.server.content.serialization.ContentSerialization; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.ServerDataCell; import electrosphere.server.datacell.interfaces.DataCellManager; @@ -320,7 +321,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager //the server will not be able to synchronize it properly. ServerEntityUtils.initiallyPositionEntity(parent,blockEntity,realPos); ServerEntityUtils.initiallyPositionEntity(parent,terrainEntity,realPos); - PhysicsDataCell cell = PhysicsDataCell.createPhysicsCell(worldPos, terrainEntity, blockEntity, terrainChunk, blockChunkData); + PhysicsDataCell cell = PhysicsDataCell.createPhysicsCell(worldPos, terrainEntity, blockEntity); + cell.setTerrainChunk(terrainChunk); + cell.setBlockChunk(blockChunkData); cell.generatePhysics(); posPhysicsMap.put(key, cell); } @@ -449,11 +452,14 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager Vector3i worldPos = this.getCellWorldPosition(cell); Long key = this.getServerDataCellKey(worldPos); - //offload all entities in cell to chunk file - //entities are saved before tracking is removed. This makes sure that any side effects from calling destroyEntity (ie if it looks up the chunk that we're deleting) + //entities are serialized before tracking is removed. This makes sure that any side effects from calling destroyEntity (ie if it looks up the chunk that we're deleting) //don't trigger the chunk to be re-created + Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Serialize entities"); + ContentSerialization serializedEntities = ContentSerialization.constructContentSerialization(cell.getScene().getEntityList()); + Globals.profiler.endCpuSample(); + + Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Destroy entities"); - serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList()); for(Entity entity : cell.getScene().getEntityList()){ ServerEntityUtils.destroyEntity(entity); } @@ -462,8 +468,11 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager //save terrain to disk //terrain is saved before tracking is removed. This makes sure that any side effects from calling savePositionToDisk (ie if it looks up the chunk that we're deleting) //don't trigger the chunk to be re-created - Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Store terrain"); - serverTerrainManager.savePositionToDisk(worldPos); + Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Store data"); + GriddedDataCellLoaderService.queueLocationBasedOperation(key, () -> { + serverContentManager.saveSerializationToDisk(key, serializedEntities); + serverTerrainManager.savePositionToDisk(worldPos); + }); Globals.profiler.endCpuSample(); //deregister from all tracking structures @@ -662,8 +671,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager worldPos.y * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET, worldPos.z * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET ); - BlockChunkData blockChunkData = realm.getServerWorldData().getServerBlockManager().getChunk(worldPos.x, worldPos.y, worldPos.z); - ServerTerrainChunk terrainChunk = realm.getServerWorldData().getServerTerrainManager().getChunk(worldPos.x, worldPos.y, worldPos.z); ServerDataCell dataCell = groundDataCells.get(key); //create entities @@ -678,7 +685,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager ServerEntityUtils.initiallyPositionEntity(realm,blockEntity,realPos); ServerEntityUtils.initiallyPositionEntity(realm,terrainEntity,realPos); - PhysicsDataCell targetCell = PhysicsDataCell.createPhysicsCell(worldPos, terrainEntity, blockEntity, terrainChunk, blockChunkData); + PhysicsDataCell targetCell = PhysicsDataCell.createPhysicsCell(worldPos, terrainEntity, blockEntity); if(cell == null){ posPhysicsMap.put(key, targetCell); } else { @@ -687,6 +694,10 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager } generationService.submit(() -> { + BlockChunkData blockChunkData = realm.getServerWorldData().getServerBlockManager().getChunk(worldPos.x, worldPos.y, worldPos.z); + ServerTerrainChunk terrainChunk = realm.getServerWorldData().getServerTerrainManager().getChunk(worldPos.x, worldPos.y, worldPos.z); + targetCell.setTerrainChunk(terrainChunk); + targetCell.setBlockChunk(blockChunkData); //create physics entities if(cell != null){ cell.retireCell(); @@ -725,10 +736,12 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager cellPositionMap.put(rVal,new Vector3i(worldPos)); loadedCellsLock.unlock(); - //generate content - serverContentManager.generateContentForDataCell(parent, worldPos, rVal, cellKey); - //generates physics for the cell in a dedicated thread then finally registers Long key = this.getServerDataCellKey(worldPos); + //generate content + GriddedDataCellLoaderService.queueLocationBasedOperation(key, () -> { + serverContentManager.generateContentForDataCell(parent, worldPos, rVal, cellKey); + }); + //generates physics for the cell in a dedicated thread then finally registers PhysicsDataCell cell = posPhysicsMap.get(key); GriddedDataCellManager.runPhysicsGenerationThread(worldPos,key,cell,this.posPhysicsMap,this.groundDataCells,this.parent); diff --git a/src/main/java/electrosphere/server/datacell/physics/PhysicsDataCell.java b/src/main/java/electrosphere/server/datacell/physics/PhysicsDataCell.java index cc8c0ea3..9372f707 100644 --- a/src/main/java/electrosphere/server/datacell/physics/PhysicsDataCell.java +++ b/src/main/java/electrosphere/server/datacell/physics/PhysicsDataCell.java @@ -48,17 +48,13 @@ public class PhysicsDataCell { public static PhysicsDataCell createPhysicsCell( Vector3i worldPos, Entity physicsEntity, - Entity blockPhysicsEntity, - ServerTerrainChunk currentChunk, - BlockChunkData blockChunk + Entity blockPhysicsEntity ){ PhysicsDataCell rVal = new PhysicsDataCell(); rVal.physicsEntity = physicsEntity; rVal.blockPhysicsEntity = blockPhysicsEntity; rVal.worldPos = worldPos; - rVal.blockChunk = blockChunk; - rVal.terrainChunk = currentChunk; return rVal; } @@ -231,5 +227,23 @@ public class PhysicsDataCell { // types[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0; // } } + + /** + * Sets the terrain chunk data for the physics cell + * @param terrainChunk The terrain chunk data + */ + public void setTerrainChunk(ServerTerrainChunk terrainChunk) { + this.terrainChunk = terrainChunk; + } + + /** + * Sets the block chunk data for the physics cell + * @param blockChunk The block chunk data + */ + public void setBlockChunk(BlockChunkData blockChunk) { + this.blockChunk = blockChunk; + } + + }