From 795fbb6a1c5213fc96aa22ad19927ae24a9d023c Mon Sep 17 00:00:00 2001 From: austin Date: Fri, 8 Nov 2024 12:11:40 -0500 Subject: [PATCH] grid manager entity and player tracking overhaul --- assets/Config/settings.json | 2 +- .../architecture/generation/voxelgenideas.md | 5 + .../ui/menu/debug/ImGuiGriddedManager.java | 51 ++++ .../ui/menu/debug/ImGuiWindowMacros.java | 5 + .../entity/EntityCreationUtils.java | 7 +- .../electrosphere/entity/EntityUtils.java | 4 +- .../entity/ServerEntityUtils.java | 37 ++- .../electrosphere/entity/scene/Scene.java | 36 ++- .../state/inventory/InventoryUtils.java | 5 +- .../editor/ClientEditorMovementTree.java | 50 ---- .../types/collision/CollisionObjUtils.java | 11 + .../types/common/CommonEntityUtils.java | 4 + .../entity/types/creature/CreatureUtils.java | 7 + .../net/server/player/Player.java | 23 ++ .../content/ServerContentGenerator.java | 8 +- .../server/content/ServerContentManager.java | 5 +- .../serialization/ContentSerialization.java | 3 +- .../server/datacell/EntityDataCellMapper.java | 3 + .../datacell/GriddedDataCellManager.java | 247 ++++++++++++------ .../electrosphere/server/datacell/Realm.java | 16 -- .../server/datacell/RealmManager.java | 4 +- .../server/datacell/ServerDataCell.java | 80 +++--- .../datacell/utils/DataCellSearchUtils.java | 2 +- .../datacell/utils/ServerEntityTagUtils.java | 5 +- 24 files changed, 400 insertions(+), 220 deletions(-) create mode 100644 src/main/java/electrosphere/client/ui/menu/debug/ImGuiGriddedManager.java diff --git a/assets/Config/settings.json b/assets/Config/settings.json index 74162155..730eba93 100644 --- a/assets/Config/settings.json +++ b/assets/Config/settings.json @@ -1,6 +1,6 @@ { "gameplayGenerateWorld" : false, - "gameplayPhysicsCellRadius" : 2, + "gameplayPhysicsCellRadius" : 3, "displayWidth" : 1920, "displayHeight" : 1080, diff --git a/docs/src/architecture/generation/voxelgenideas.md b/docs/src/architecture/generation/voxelgenideas.md index 476b878e..0a04496c 100644 --- a/docs/src/architecture/generation/voxelgenideas.md +++ b/docs/src/architecture/generation/voxelgenideas.md @@ -9,3 +9,8 @@ sines pulling from sines noise functions pulling from sines applying 2d values to the surface (top) of a 3d noise map subtracting true 3d noise from a 2d heightmap to account for cave entrances + + +spatially partitioning 3d shapes using the same trick as the voronoi noise but in 3d +IE, have a torus defined in 3d that is bounded in cells + diff --git a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiGriddedManager.java b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiGriddedManager.java new file mode 100644 index 00000000..d9f15b93 --- /dev/null +++ b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiGriddedManager.java @@ -0,0 +1,51 @@ +package electrosphere.client.ui.menu.debug; + +import electrosphere.engine.Globals; +import electrosphere.renderer.ui.imgui.ImGuiWindow; +import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback; +import electrosphere.server.datacell.GriddedDataCellManager; +import electrosphere.server.datacell.Realm; +import imgui.ImGui; + +/** + * + */ +public class ImGuiGriddedManager { + + //window for viewing information about the ai state + protected static ImGuiWindow griddedManagerWindow; + + /** + * Creates the windows in this file + */ + protected static void createGriddedManagerWindows(){ + createGriddedManagerWindow(); + } + + /** + * Client scene entity view + */ + protected static void createGriddedManagerWindow(){ + griddedManagerWindow = new ImGuiWindow("Gridded Manager"); + griddedManagerWindow.setCallback(new ImGuiWindowCallback() { + @Override + public void exec() { + GriddedDataCellManager manager = null; + if(Globals.realmManager != null && Globals.realmManager.first() != null){ + Realm realm = Globals.realmManager.first(); + if(realm.getDataCellManager() instanceof GriddedDataCellManager){ + manager = (GriddedDataCellManager)realm.getDataCellManager(); + } + } + + if(manager != null && manager.getLoadedCells() != null){ + ImGui.text("Loaded Cells: " + manager.getLoadedCells().size()); + } + + } + }); + griddedManagerWindow.setOpen(false); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(griddedManagerWindow); + } + +} diff --git a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java index 1a019e8d..7cb463b5 100644 --- a/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java +++ b/src/main/java/electrosphere/client/ui/menu/debug/ImGuiWindowMacros.java @@ -52,6 +52,7 @@ public class ImGuiWindowMacros { ImGuiRenderer.createRendererWindows(); ImGuiTestGen.createTestGenWindows(); ImGuiChunkMonitor.createChunkMonitorWindows(); + ImGuiGriddedManager.createGriddedManagerWindows(); } /** @@ -188,6 +189,10 @@ public class ImGuiWindowMacros { if(ImGui.button("Chunk Monitor")){ ImGuiChunkMonitor.chunkMonitorWindow.setOpen(true); } + //gridded data cell monitor + if(ImGui.button("Gridded Data Cell Monitor")){ + ImGuiGriddedManager.griddedManagerWindow.setOpen(true); + } //close button if(ImGui.button("Close")){ mainDebugWindow.setOpen(false); diff --git a/src/main/java/electrosphere/entity/EntityCreationUtils.java b/src/main/java/electrosphere/entity/EntityCreationUtils.java index 06d19d65..739eee00 100644 --- a/src/main/java/electrosphere/entity/EntityCreationUtils.java +++ b/src/main/java/electrosphere/entity/EntityCreationUtils.java @@ -50,10 +50,15 @@ public class EntityCreationUtils { throw new IllegalStateException("Failed to create a server data cell"); } //register to entity data cell mapper - realm.getEntityDataCellMapper().registerEntity(rVal, cell); + Globals.entityDataCellMapper.registerEntity(rVal, cell); //enable behavior tree tracking ServerBehaviorTreeUtils.registerEntity(rVal); + if(Globals.entityDataCellMapper.getEntityDataCell(rVal) == null){ + Globals.entityDataCellMapper.registerEntity(rVal, cell); + throw new Error("Failed to map entity to cell!"); + } + return rVal; } diff --git a/src/main/java/electrosphere/entity/EntityUtils.java b/src/main/java/electrosphere/entity/EntityUtils.java index e7e112ab..a40785b9 100644 --- a/src/main/java/electrosphere/entity/EntityUtils.java +++ b/src/main/java/electrosphere/entity/EntityUtils.java @@ -43,11 +43,11 @@ public class EntityUtils { Realm realm = Globals.realmManager.getEntityRealm(e); if(realm != null){ //get data cell - ServerDataCell dataCell = realm.getEntityDataCellMapper().getEntityDataCell(e); + ServerDataCell dataCell = Globals.entityDataCellMapper.getEntityDataCell(e); if(dataCell != null){ dataCell.getScene().deregisterEntity(e); } - realm.getEntityDataCellMapper().ejectEntity(e); + Globals.entityDataCellMapper.ejectEntity(e); } Globals.realmManager.removeEntity(e); } diff --git a/src/main/java/electrosphere/entity/ServerEntityUtils.java b/src/main/java/electrosphere/entity/ServerEntityUtils.java index b3a6aa5a..5c962005 100644 --- a/src/main/java/electrosphere/entity/ServerEntityUtils.java +++ b/src/main/java/electrosphere/entity/ServerEntityUtils.java @@ -33,6 +33,9 @@ public class ServerEntityUtils { * @param position */ public static void initiallyPositionEntity(Realm realm, Entity entity, Vector3d position){ + if(position == null){ + throw new Error("Trying to set server entity position to null!"); + } //reposition entity, if the position isn't correct then it will spawn at 0,0,0 when the synchronization part is called CollisionObjUtils.serverPositionCharacter(entity, position); //get current server data cell @@ -43,6 +46,9 @@ public class ServerEntityUtils { } else { //if it doesn't already exist, try creating it and if successfull move creature cell = realm.getDataCellManager().tryCreateCellAtPoint(position); + if(cell == null){ + throw new Error("Trying to initially position entity to position that cannot generate a data cell! " + position); + } //initialize server datacell tracking of this entity realm.initializeServerSideEntity(entity, cell); } @@ -54,25 +60,34 @@ public class ServerEntityUtils { * @param position */ public static void repositionEntity(Entity entity, Vector3d position){ + if(position == null){ + throw new Error("Trying to set server entity position to null!"); + } Realm realm = Globals.realmManager.getEntityRealm(entity); //if server, get current server data cell - ServerDataCell oldDataCell = realm.getDataCellManager().getDataCellAtPoint(EntityUtils.getPosition(entity)); + ServerDataCell oldDataCell = Globals.entityDataCellMapper.getEntityDataCell(entity); ServerDataCell newDataCell = realm.getDataCellManager().getDataCellAtPoint(position); if(oldDataCell == null){ - LoggerInterface.loggerEngine.WARNING("Trying to reposition entity on server when it's former position is null!"); + LoggerInterface.loggerEngine.WARNING( + "Trying to reposition entity on server when it's former cell is null!\n" + + "Entity original position: " + EntityUtils.getPosition(entity) + "\n" + ); } if(oldDataCell != newDataCell){ - if(newDataCell != null){ - ServerDataCell.moveEntityFromCellToCell(entity, oldDataCell, newDataCell); - ServerBehaviorTreeUtils.updateCell(entity, oldDataCell); - } else { - //if it doesn't already exist, try creating it and if successfull move creature - newDataCell = realm.getDataCellManager().tryCreateCellAtPoint(EntityUtils.getPosition(entity)); - if(newDataCell != null){ - ServerDataCell.moveEntityFromCellToCell(entity, oldDataCell, newDataCell); - ServerBehaviorTreeUtils.updateCell(entity, oldDataCell); + if(newDataCell == null){ + newDataCell = realm.getDataCellManager().tryCreateCellAtPoint(position); + if(newDataCell == null){ + LoggerInterface.loggerEngine.WARNING( + "Trying to reposition entity on server when it's new cell is null!\n" + + "Entity new position: " + position + "\n" + ); } } + ServerDataCell.moveEntityFromCellToCell(entity, oldDataCell, newDataCell); + ServerBehaviorTreeUtils.updateCell(entity, oldDataCell); + if(oldDataCell.getScene().containsEntity(entity)){ + throw new Error("Entity not removed from scene!"); + } } //reposition entity CollisionObjUtils.serverPositionCharacter(entity, position); diff --git a/src/main/java/electrosphere/entity/scene/Scene.java b/src/main/java/electrosphere/entity/scene/Scene.java index 77f40a00..b2455aa3 100644 --- a/src/main/java/electrosphere/entity/scene/Scene.java +++ b/src/main/java/electrosphere/entity/scene/Scene.java @@ -7,6 +7,7 @@ import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.state.attach.AttachUtils; import electrosphere.logger.LoggerInterface; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -19,13 +20,24 @@ import java.util.concurrent.CopyOnWriteArraySet; */ public class Scene { - + /** + * The map of id -> entity + */ Map entityIdMap; - Map> tagEntityMap; - List entityList = new CopyOnWriteArrayList(); + /** + * The map of tag -> set of entities corresponding to that tag + */ + Map> tagEntityMap; + + /** + * The list of behavior trees + */ List behaviorTreeList; + /** + * Constructor + */ public Scene(){ entityIdMap = new ConcurrentHashMap(); tagEntityMap = new ConcurrentHashMap>(); @@ -53,9 +65,6 @@ public class Scene { * @param e The entity to register */ public void registerEntity(Entity e){ - if(!entityIdMap.containsKey(e.getId())){ - entityList.add(e); - } entityIdMap.put(e.getId(), e); } @@ -101,7 +110,6 @@ public class Scene { tagEntityMap.get(key).remove(e); } entityIdMap.remove(e.getId()); - entityList.remove(e); } /** @@ -112,10 +120,10 @@ public class Scene { if(AttachUtils.hasChildren(target)){ List childrenList = AttachUtils.getChildrenList(target); for(Entity currentChild : childrenList){ - recursiveDeregister(currentChild); + this.recursiveDeregister(currentChild); } } - deregisterEntity(target); + this.deregisterEntity(target); } /** @@ -166,11 +174,11 @@ public class Scene { } /** - * Gets the list of all entities in the scene - * @return The list of all entities in the scene + * Gets the collection of all entities in the scene + * @return The collection of all entities in the scene */ - public List getEntityList(){ - return entityList; + public Collection getEntityList(){ + return this.entityIdMap.values(); } /** @@ -178,7 +186,7 @@ public class Scene { */ public void describeScene(){ LoggerInterface.loggerEngine.WARNING("Entities present in scene:"); - for(Entity entity : this.entityList){ + for(Entity entity : this.entityIdMap.values()){ LoggerInterface.loggerEngine.WARNING(entity.getId() + ""); } } diff --git a/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java b/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java index 3e09cd8e..eb0e9fc4 100644 --- a/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java +++ b/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java @@ -166,6 +166,9 @@ public class InventoryUtils { * @return The in-inventory item */ public static Entity serverAttemptStoreItemTransform(Entity creature, Entity item){ + if(item == null){ + throw new Error("Null item provided! " + item); + } boolean creatureIsCreature = CreatureUtils.isCreature(creature); boolean itemIsItem = ItemUtils.isItem(item); boolean hasInventory = hasNaturalInventory(creature); @@ -360,7 +363,7 @@ public class InventoryUtils { Realm realm = Globals.realmManager.getEntityRealm(realWorldItem); if(realm != null){ //get closest chunk - ServerDataCell dataCell = realm.getEntityDataCellMapper().getEntityDataCell(realWorldItem); + ServerDataCell dataCell = Globals.entityDataCellMapper.getEntityDataCell(realWorldItem); //broadcast destroy item NetworkMessage unequipMessage = InventoryMessage.constructserverCommandUnequipItemMessage(creature.getId(), InventoryProtocol.INVENTORY_TYPE_EQUIP, inventorySlot); dataCell.broadcastNetworkMessage(unequipMessage); diff --git a/src/main/java/electrosphere/entity/state/movement/editor/ClientEditorMovementTree.java b/src/main/java/electrosphere/entity/state/movement/editor/ClientEditorMovementTree.java index 8b1b6cc9..8276cc6f 100644 --- a/src/main/java/electrosphere/entity/state/movement/editor/ClientEditorMovementTree.java +++ b/src/main/java/electrosphere/entity/state/movement/editor/ClientEditorMovementTree.java @@ -240,56 +240,6 @@ public class ClientEditorMovementTree implements BehaviorTree { Quaterniond movementQuaternion = new Quaterniond().rotationTo(SpatialMathUtils.getOriginVector(), new Vector3d(facingVector.x,0,facingVector.z)).normalize(); Quaterniond rotation = EntityUtils.getRotation(parent); - //parse attached network messages - for(EntityMessage message : networkMessageQueue){ - networkMessageQueue.remove(message); - long updateTime = message.gettime(); -// System.out.println("MOVE to " + message.getX() + " " + message.getY() + " " + message.getZ()); - switch(message.getMessageSubtype()){ - case MOVEUPDATE: - if(updateTime >= lastUpdateTime){ - lastUpdateTime = updateTime; - switch(message.gettreeState()){ - case 0: - state = MovementTreeState.STARTUP; - // System.out.println("Set state STARTUP"); - GravityUtils.clientAttemptActivateGravity(parent); - break; - case 1: - state = MovementTreeState.MOVE; - // System.out.println("Set state MOVE"); - GravityUtils.clientAttemptActivateGravity(parent); - break; - case 2: - state = MovementTreeState.SLOWDOWN; - // System.out.println("Set state SLOWDOWN"); - GravityUtils.clientAttemptActivateGravity(parent); - break; - case 3: - state = MovementTreeState.IDLE; - // System.out.println("Set state IDLE"); - break; - } - //this should only fire on the client, we don't want the server snap updating due to client position reporting - lastServerPosition = new Vector3d(message.getpositionX(),message.getpositionY(),message.getpositionZ()); - if(position.distance(lastServerPosition) > STATE_DIFFERENCE_HARD_UPDATE_THRESHOLD){ - EntityUtils.getPosition(parent).set(lastServerPosition); - } else if(position.distance(lastServerPosition) > STATE_DIFFERENCE_SOFT_UPDATE_THRESHOLD){ - EntityUtils.getPosition(parent).lerp(lastServerPosition,SOFT_UPDATE_MULTIPLIER); - } - //we want to always update the server facing vector with where the client says they're facing - EntityUtils.getRotation(parent).set(message.getrotationX(),message.getrotationY(),message.getrotationZ(),message.getrotationW()); - CollisionObjUtils.clientPositionCharacter(parent, position, rotation); - // CreatureUtils.setFacingVector(parent, new Vector3d(message.getrotationX(),message.getrotationY(),message.getrotationZ())); - break; - } - break; - default: - break; - } - } - - // System.out.println(movementVector + " " + velocity * Main.deltaTime); //state machine switch(state){ diff --git a/src/main/java/electrosphere/entity/types/collision/CollisionObjUtils.java b/src/main/java/electrosphere/entity/types/collision/CollisionObjUtils.java index aeab90a0..629355f4 100644 --- a/src/main/java/electrosphere/entity/types/collision/CollisionObjUtils.java +++ b/src/main/java/electrosphere/entity/types/collision/CollisionObjUtils.java @@ -65,6 +65,11 @@ public class CollisionObjUtils { entity.putData(EntityDataStrings.PHYSICS_MASS, mass); } + /** + * Repositions an entity on the server + * @param e The entity + * @param position The server + */ public static void serverPositionCharacter(Entity e, Vector3d position){ EntityUtils.getPosition(e).set(position); Quaterniond rotation = EntityUtils.getRotation(e); @@ -75,6 +80,12 @@ public class CollisionObjUtils { } } + /** + * Positions an entity on the client + * @param e The entity + * @param position The position + * @param rotation The rotation + */ public static void clientPositionCharacter(Entity e, Vector3d position, Quaterniond rotation){ EntityUtils.getPosition(e).set(position); DBody body = PhysicsEntityUtils.getDBody(e); diff --git a/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java b/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java index 97cdb0dc..f2d3dd90 100644 --- a/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java +++ b/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java @@ -689,6 +689,10 @@ public class CommonEntityUtils { ServerEntityTagUtils.attachTagToEntity(entity, EntityTags.LIFE_STATE); } + if(Globals.entityDataCellMapper.getEntityDataCell(entity) == null){ + throw new Error("Failed to map entity to cell!"); + } + return entity; } diff --git a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java index 0d4a74d0..8bc40bbb 100644 --- a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java +++ b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java @@ -402,6 +402,13 @@ public class CreatureUtils { if(CreatureUtils.hasControllerPlayerId(creature)){ LoggerInterface.loggerNetworking.INFO("Sending controller packets"); player.addMessage(NetUtils.createSetCreatureControllerIdEntityMessage(creature)); + Player entityOwner = Globals.playerManager.getPlayerFromId(CreatureUtils.getControllerPlayerId(creature)); + if(entityOwner == player){ + player.setHasSentPlayerEntity(true); + } + if(Globals.playerEntity != null && player.getId() == Globals.clientPlayer.getId() && player.getPlayerEntity() == creature){ + throw new Error("Re-sending player entity to player!"); + } } } diff --git a/src/main/java/electrosphere/net/server/player/Player.java b/src/main/java/electrosphere/net/server/player/Player.java index 3f9e31e0..7167bc34 100644 --- a/src/main/java/electrosphere/net/server/player/Player.java +++ b/src/main/java/electrosphere/net/server/player/Player.java @@ -53,6 +53,11 @@ public class Player { * The player's primary entity */ Entity playerEntity; + + /** + * Tracks whether the player's entity has been sent or not + */ + boolean hasSentPlayerEntity = false; /** * Constructor @@ -151,6 +156,24 @@ public class Player { idIncrementerLock.release(); return rVal; } + + /** + * Gets whether the player has been sent their entity or not + * @return true if has been sent, false otherwise + */ + public boolean hasSentPlayerEntity() { + return hasSentPlayerEntity; + } + + /** + * Sets whether the player has been sent their entity or not + * @param hasSentPlayerEntity true if has been sent, false otherwise + */ + public void setHasSentPlayerEntity(boolean hasSentPlayerEntity) { + this.hasSentPlayerEntity = hasSentPlayerEntity; + } + + } diff --git a/src/main/java/electrosphere/server/content/ServerContentGenerator.java b/src/main/java/electrosphere/server/content/ServerContentGenerator.java index c9014aca..58806514 100644 --- a/src/main/java/electrosphere/server/content/ServerContentGenerator.java +++ b/src/main/java/electrosphere/server/content/ServerContentGenerator.java @@ -64,14 +64,16 @@ public class ServerContentGenerator { double scale = foliageDescription.getScale(); double regularity = foliageDescription.getRegularity(); double threshold = foliageDescription.getThreshold(); - if(NoiseUtils.relaxedPointGen(x * scale, z * scale, regularity, threshold) > 0){ + double realX = realm.getServerWorldData().convertVoxelToRealSpace(x, worldPos.x); + double realZ = realm.getServerWorldData().convertVoxelToRealSpace(z, worldPos.z); + if(NoiseUtils.relaxedPointGen(realX * scale, realZ * scale, regularity, threshold) > 0){ String type = foliageDescription.getEntityIDs().get(random.nextInt(0,foliageDescription.getEntityIDs().size())); FoliageUtils.serverSpawnTreeFoliage( realm, new Vector3d( - realm.getServerWorldData().convertVoxelToRealSpace(x, worldPos.x), + realX, height, - realm.getServerWorldData().convertVoxelToRealSpace(z, worldPos.z) + realZ ), type, random.nextLong() diff --git a/src/main/java/electrosphere/server/content/ServerContentManager.java b/src/main/java/electrosphere/server/content/ServerContentManager.java index 089d9d48..f1822b37 100644 --- a/src/main/java/electrosphere/server/content/ServerContentManager.java +++ b/src/main/java/electrosphere/server/content/ServerContentManager.java @@ -1,5 +1,6 @@ package electrosphere.server.content; +import java.util.Collection; import java.util.List; import org.joml.Vector3i; @@ -73,9 +74,9 @@ public class ServerContentManager { /** * Saves entity content to disk * @param locationKey the location key to save under - * @param entities the list of entities to save + * @param entities the collection of entities to save */ - public void saveContentToDisk(Long locationKey, List entities){ + public void saveContentToDisk(Long locationKey, Collection entities){ ContentSerialization serialization = ContentSerialization.constructContentSerialization(entities); String dirPath = SaveUtils.deriveSaveDirectoryPath(Globals.currentSave.getName()); String fullPath = dirPath + "/content/" + locationKey + ".dat"; diff --git a/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java b/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java index 68925d00..56b89031 100644 --- a/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java +++ b/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java @@ -1,5 +1,6 @@ package electrosphere.server.content.serialization; +import java.util.Collection; import java.util.LinkedList; import java.util.List; @@ -31,7 +32,7 @@ public class ContentSerialization { * @param entities The entities * @return The content serialization */ - public static ContentSerialization constructContentSerialization(List entities){ + public static ContentSerialization constructContentSerialization(Collection entities){ ContentSerialization rVal = new ContentSerialization(); for(Entity entity : entities){ if(!CreatureUtils.hasControllerPlayerId(entity)){ diff --git a/src/main/java/electrosphere/server/datacell/EntityDataCellMapper.java b/src/main/java/electrosphere/server/datacell/EntityDataCellMapper.java index 1661de8e..e0488f0d 100644 --- a/src/main/java/electrosphere/server/datacell/EntityDataCellMapper.java +++ b/src/main/java/electrosphere/server/datacell/EntityDataCellMapper.java @@ -19,6 +19,9 @@ public class EntityDataCellMapper { * @param serverDataCell The server data cell to register this entity to */ public void registerEntity(Entity entity, ServerDataCell serverDataCell){ + if(serverDataCell == null){ + throw new Error("Mapping entity to null!"); + } entityDataCellMap.put(entity, serverDataCell); } diff --git a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java index b7abb191..0e0a5f40 100644 --- a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java +++ b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java @@ -1,12 +1,13 @@ package electrosphere.server.datacell; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Semaphore; import org.joml.Vector3d; @@ -50,34 +51,71 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager */ public static final int MAX_GRID_SIZE = TerrainModel.MAX_MACRO_DATA_SIZE * TerrainModel.DEFAULT_MACRO_DATA_SCALE * ServerTerrainChunk.CHUNK_DIMENSION; + /** + * The number of frames without players that must pass before a server data cell is unloaded + */ + static final int UNLOAD_FRAME_THRESHOLD = 100; + /** * Tracks whether this manager has been flagged to unload cells or not */ boolean unloadCells = true; - //these are going to be the natural ground grid of data cells, but we're going to have more than this + /** + * These are going to be the natural ground grid of data cells, but we're going to have more than this + */ Map groundDataCells = new HashMap(); + + /** + * Map of server cell to its world position + */ Map cellPositionMap = new HashMap(); - //Map of server data cell to the number of frames said cell has had no players + + /** + * Map of server data cell to the number of frames said cell has had no players + */ Map cellPlayerlessFrameMap = new HashMap(); - //The number of frames without players that must pass before a server data cell is unloaded - static final int UNLOAD_FRAME_THRESHOLD = 100; - //loaded cells + + /** + * Loaded cells + */ Semaphore loadedCellsLock = new Semaphore(1); - Set loadedCells = new CopyOnWriteArraySet(); - //parent realm + + /** + * Parent realm + */ Realm parent; - //the world data of the parent + + /** + * The world data of the parent + */ ServerWorldData serverWorldData; - //Manager for terrain for this particular cell manager + + /** + * Manager for terrain for this particular cell manager + */ ServerTerrainManager serverTerrainManager; - //manager for fluids for this particular cell manager + + /** + * Manager for fluids for this particular cell manager + */ ServerFluidManager serverFluidManager; - //lock for terrain editing + + /** + * Lock for terrain editing + */ Semaphore terrainEditLock = new Semaphore(1); - //manager for getting entities to fill in a cell + + /** + * Manager for getting entities to fill in a cell + */ ServerContentManager serverContentManager; + /** + * Used for cleaning server data cells no longer in use from the realm + */ + Set toCleanQueue = new HashSet(); + /** * Map of world position key -> physics cell */ @@ -132,22 +170,21 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager ){ Vector3i targetPos = new Vector3i(x,y,z); LoggerInterface.loggerEngine.DEBUG("GriddedDataCellManager: Add player to " + x + " " + y + " " + z); + loadedCellsLock.acquireUninterruptibly(); if(groundDataCells.get(getServerDataCellKey(targetPos)) != null){ groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); } else { LoggerInterface.loggerEngine.DEBUG("Creating new cell @ " + x + " " + y + " " + z); //create data cell - createServerDataCell(targetPos); + this.createServerDataCell(targetPos); ///generates physics for the cell in a dedicated thread then finally registers - runPhysicsGenerationThread(targetPos); + this.runPhysicsGenerationThread(targetPos); //add to loaded cells - loadedCellsLock.acquireUninterruptibly(); - loadedCells.add(groundDataCells.get(getServerDataCellKey(targetPos))); - cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0); - loadedCellsLock.release(); + cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0); //add player groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); } + loadedCellsLock.release(); } } } @@ -163,36 +200,29 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager int playerSimulationRadius = player.getSimulationRadius(); Vector3i oldPosition = player.getWorldPos(); player.setWorldPos(newPosition); - // System.out.println("=======" + "SET" + newX + " " + newY + " FROM " + oldX + " " + oldY + "========"); - // int removals = 0; - // int additions = 0; - for(int x = oldPosition.x - playerSimulationRadius; x < oldPosition.x + playerSimulationRadius + 1; x++){ - for(int y = oldPosition.y - playerSimulationRadius; y < oldPosition.y + playerSimulationRadius + 1; y++){ - for(int z = oldPosition.z - playerSimulationRadius; z < oldPosition.z + playerSimulationRadius + 1; z++){ - if( - x >= 0 && x < this.serverWorldData.getWorldSizeDiscrete() && - y >= 0 && y < this.serverWorldData.getWorldSizeDiscrete() && - z >= 0 && z < this.serverWorldData.getWorldSizeDiscrete() && - ( - x < newPosition.x - playerSimulationRadius || - x > newPosition.x + playerSimulationRadius || - y < newPosition.y - playerSimulationRadius || - y > newPosition.y + playerSimulationRadius || - z < newPosition.z - playerSimulationRadius || - z > newPosition.z + playerSimulationRadius - ) - ){ - Vector3i targetPos = new Vector3i(x,y,z); - Long key = this.getServerDataCellKey(targetPos); - if( - groundDataCells.get(key) != null && - groundDataCells.get(key).containsPlayer(player) - ){ - // removals++; - groundDataCells.get(key).removePlayer(player); - this.broadcastDestructionToPlayer(player, groundDataCells.get(key)); - } - } + for(ServerDataCell cell : this.groundDataCells.values()){ + Vector3i worldPos = this.getCellWorldPosition(cell); + if( + cell.containsPlayer(player) && + ( + worldPos.x < newPosition.x - playerSimulationRadius || + worldPos.x > newPosition.x + playerSimulationRadius || + worldPos.y < newPosition.y - playerSimulationRadius || + worldPos.y > newPosition.y + playerSimulationRadius || + worldPos.z < newPosition.z - playerSimulationRadius || + worldPos.z > newPosition.z + playerSimulationRadius + ) + ){ + cell.removePlayer(player); + this.broadcastDestructionToPlayer(player, cell); + if(cell.getScene().containsEntity(player.getPlayerEntity())){ + throw new Error( + "Unregistering player from cell that contains player's entity!\n " + + player + "\n " + + worldPos + "\n " + + player.getPlayerEntity() + "\n " + + EntityUtils.getPosition(player.getPlayerEntity()) + ); } } } @@ -213,31 +243,26 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager ) ){ Vector3i targetPos = new Vector3i(x,y,z); - // System.out.println("Add player to " + x + " " + y); if(groundDataCells.get(getServerDataCellKey(targetPos)) != null){ + loadedCellsLock.acquireUninterruptibly(); groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); + loadedCellsLock.release(); } else { + loadedCellsLock.acquireUninterruptibly(); //create data cell createServerDataCell(targetPos); //generates physics for the cell in a dedicated thread then finally registers runPhysicsGenerationThread(targetPos); //add to loaded cells - loadedCellsLock.acquireUninterruptibly(); - loadedCells.add(groundDataCells.get(getServerDataCellKey(targetPos))); cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0); - loadedCellsLock.release(); //add player groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); + loadedCellsLock.release(); } - // additions++; - } else { - // System.out.println(x + "\t" + (oldX - playerSimulationRadius) + "\t" + (oldX + playerSimulationRadius)); - // System.out.println(y + "\t" + (oldY - playerSimulationRadius) + "\t" + (oldY + playerSimulationRadius)); } } } } - // System.out.println("removals: " + removals + "\tadditions: " + additions); } /** @@ -280,25 +305,80 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager int currentWorldX = parent.getServerWorldData().convertRealToChunkSpace(position.x); int currentWorldY = parent.getServerWorldData().convertRealToChunkSpace(position.y); int currentWorldZ = parent.getServerWorldData().convertRealToChunkSpace(position.z); - if(currentWorldX != player.getWorldPos().x || currentWorldY != player.getWorldPos().y || currentWorldZ != player.getWorldPos().z){ - movePlayer(player,new Vector3i(currentWorldX,currentWorldY,currentWorldZ)); - playerChangedChunk = true; + Vector3i newPosition = new Vector3i(currentWorldX,currentWorldY,currentWorldZ); + player.setWorldPos(newPosition); + + int playerSimulationRadius = player.getSimulationRadius(); + + //remove from cells that are out of range + for(ServerDataCell cell : this.groundDataCells.values()){ + Vector3i cellWorldPos = this.getCellWorldPosition(cell); + if( + cell.containsPlayer(player) && + ( + cellWorldPos.x < newPosition.x - playerSimulationRadius || + cellWorldPos.x > newPosition.x + playerSimulationRadius || + cellWorldPos.y < newPosition.y - playerSimulationRadius || + cellWorldPos.y > newPosition.y + playerSimulationRadius || + cellWorldPos.z < newPosition.z - playerSimulationRadius || + cellWorldPos.z > newPosition.z + playerSimulationRadius + ) + ){ + if(cell.getScene().containsEntity(player.getPlayerEntity())){ + throw new Error("Trying to remove player from a cell that contains its entity!"); + } + cell.removePlayer(player); + this.broadcastDestructionToPlayer(player, cell); + } + } + + //Add to cells that are in range + for(int x = newPosition.x - playerSimulationRadius + 1; x < newPosition.x + playerSimulationRadius; x++){ + for(int y = newPosition.y - playerSimulationRadius + 1; y < newPosition.y + playerSimulationRadius; y++){ + for(int z = newPosition.x - playerSimulationRadius + 1; z < newPosition.z + playerSimulationRadius; z++){ + if( + x >= 0 && x < this.serverWorldData.getWorldSizeDiscrete() && + y >= 0 && y < this.serverWorldData.getWorldSizeDiscrete() && + z >= 0 && z < this.serverWorldData.getWorldSizeDiscrete() + ){ + Vector3i targetPos = new Vector3i(x,y,z); + if(groundDataCells.get(this.getServerDataCellKey(targetPos)) != null){ + loadedCellsLock.acquireUninterruptibly(); + ServerDataCell cell = groundDataCells.get(this.getServerDataCellKey(targetPos)); + if(!cell.containsPlayer(player)){ + cell.addPlayer(player); + } + loadedCellsLock.release(); + } else { + loadedCellsLock.acquireUninterruptibly(); + //create data cell + this.createServerDataCell(targetPos); + //generates physics for the cell in a dedicated thread then finally registers + this.runPhysicsGenerationThread(targetPos); + //add to loaded cells + cellPlayerlessFrameMap.put(groundDataCells.get(this.getServerDataCellKey(targetPos)),0); + //add player + groundDataCells.get(this.getServerDataCellKey(targetPos)).addPlayer(player); + loadedCellsLock.release(); + } + } + } + } } } } return playerChangedChunk; } - //Used for cleaning server data cells no longer in use from the realm - Set toCleanQueue = new HashSet(); + /** * Unloads all chunks that haven't had players in them for a set amount of time */ public void unloadPlayerlessChunks(){ if(this.unloadCells){ //TODO: improve to make have less performance impact - for(ServerDataCell cell : loadedCells){ - loadedCellsLock.acquireUninterruptibly(); + loadedCellsLock.acquireUninterruptibly(); + for(ServerDataCell cell : this.groundDataCells.values()){ if(cell.getPlayers().size() < 1){ int frameCount = cellPlayerlessFrameMap.get(cell) + 1; cellPlayerlessFrameMap.put(cell,frameCount); @@ -310,11 +390,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager cellPlayerlessFrameMap.put(cell, 0); } } - loadedCellsLock.release(); } for(ServerDataCell cell : toCleanQueue){ parent.deregisterCell(cell); - loadedCells.remove(cell); Vector3i worldPos = getCellWorldPosition(cell); Long key = getServerDataCellKey(worldPos); groundDataCells.remove(key); @@ -323,13 +401,20 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager //clear all entities in cell for(Entity entity : cell.getScene().getEntityList()){ if(ServerPlayerViewDirTree.hasTree(entity)){ - throw new Error("Trying to unload a player's entity! " + entity + " " + worldPos); + throw new Error( + "Trying to unload a player's entity! " + + entity + "\n" + + EntityUtils.getPosition(entity) + "\n" + + serverWorldData.convertRealToWorldSpace(EntityUtils.getPosition(entity)) + "\n" + + worldPos + ); } ServerEntityUtils.destroyEntity(entity); } //save terrain to disk serverTerrainManager.savePositionToDisk(worldPos); } + loadedCellsLock.release(); toCleanQueue.clear(); } } @@ -342,16 +427,14 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager */ public void evictAll(){ //TODO: improve to make have less performance impact - for(ServerDataCell cell : loadedCells){ - loadedCellsLock.acquireUninterruptibly(); + loadedCellsLock.acquireUninterruptibly(); + for(ServerDataCell cell : this.groundDataCells.values()){ int frameCount = cellPlayerlessFrameMap.get(cell) + 1; cellPlayerlessFrameMap.put(cell,frameCount); toCleanQueue.add(cell); - loadedCellsLock.release(); } for(ServerDataCell cell : toCleanQueue){ parent.deregisterCell(cell); - loadedCells.remove(cell); Vector3i worldPos = getCellWorldPosition(cell); Long key = getServerDataCellKey(worldPos); groundDataCells.remove(key); @@ -362,6 +445,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager ServerEntityUtils.destroyEntity(entity); } } + loadedCellsLock.release(); this.serverTerrainManager.evictAll(); toCleanQueue.clear(); } @@ -420,13 +504,12 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager //isn't null groundDataCells.get(this.getServerDataCellKey(worldPos)) == null ){ + loadedCellsLock.acquireUninterruptibly(); //create data cell this.createServerDataCell(worldPos); //generates physics for the cell in a dedicated thread then finally registers this.runPhysicsGenerationThread(worldPos); //add to loaded cells - loadedCellsLock.acquireUninterruptibly(); - loadedCells.add(groundDataCells.get(this.getServerDataCellKey(worldPos))); cellPlayerlessFrameMap.put(groundDataCells.get(this.getServerDataCellKey(worldPos)),0); loadedCellsLock.release(); } else if(groundDataCells.get(this.getServerDataCellKey(worldPos)) == null) { @@ -464,7 +547,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager */ public void simulate(){ loadedCellsLock.acquireUninterruptibly(); - for(ServerDataCell cell : loadedCells){ + for(ServerDataCell cell : this.groundDataCells.values()){ if(Globals.microSimulation != null && Globals.microSimulation.isReady()){ Globals.microSimulation.simulate(cell); } @@ -477,7 +560,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager } } loadedCellsLock.release(); - updatePlayerPositions(); + this.updatePlayerPositions(); } @@ -536,7 +619,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager */ private ServerDataCell createServerDataCell(Vector3i worldPos){ ServerDataCell rVal = parent.createNewCell(); - Long cellKey = getServerDataCellKey(worldPos); + Long cellKey = this.getServerDataCellKey(worldPos); groundDataCells.put(cellKey,rVal); LoggerInterface.loggerEngine.DEBUG("Create server data cell with key " + cellKey); cellPositionMap.put(rVal,new Vector3i(worldPos)); @@ -674,7 +757,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager @Override public void save(String saveName) { - for(ServerDataCell cell : loadedCells){ + for(ServerDataCell cell : this.groundDataCells.values()){ Long key = this.getServerDataCellKey(this.getCellWorldPosition(cell)); //offload all entities in cell to chunk file serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList()); @@ -705,4 +788,12 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager return returnPos; } + /** + * Gets the set of loaded cells + * @return The set of loaded cells + */ + public Collection getLoadedCells(){ + return Collections.unmodifiableCollection(this.groundDataCells.values()); + } + } diff --git a/src/main/java/electrosphere/server/datacell/Realm.java b/src/main/java/electrosphere/server/datacell/Realm.java index be573256..a1af66dd 100644 --- a/src/main/java/electrosphere/server/datacell/Realm.java +++ b/src/main/java/electrosphere/server/datacell/Realm.java @@ -150,14 +150,6 @@ public class Realm { Globals.entityDataCellMapper.registerEntity(entity, cell); } - /** - * Gets the entity data cell mapper for this realm - * @return The entity data cell mapper for this realm - */ - public EntityDataCellMapper getEntityDataCellMapper(){ - return this.entityDataCellMapper; - } - /** * Gets the data cell manager for this realm @@ -167,14 +159,6 @@ public class Realm { return this.dataCellManager; } - /** - * Sets the entity data cell mapper for this realm - * @param entityDataCellMapper The entity data cell mapper for this realm - */ - protected void setEntityDataCellMapper(EntityDataCellMapper entityDataCellMapper){ - this.entityDataCellMapper = entityDataCellMapper; - } - /** * Sets the data cell manager for this realm * @param dataCellManager The data cell manager for this realm diff --git a/src/main/java/electrosphere/server/datacell/RealmManager.java b/src/main/java/electrosphere/server/datacell/RealmManager.java index 9542353a..82b85346 100644 --- a/src/main/java/electrosphere/server/datacell/RealmManager.java +++ b/src/main/java/electrosphere/server/datacell/RealmManager.java @@ -10,6 +10,7 @@ import org.joml.Vector3d; import electrosphere.collision.CollisionEngine; import electrosphere.collision.CollisionWorldData; import electrosphere.collision.hitbox.HitboxManager; +import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.game.server.world.ServerWorldData; import electrosphere.net.server.player.Player; @@ -76,10 +77,8 @@ public class RealmManager { ); //create function classes GriddedDataCellManager griddedDataCellManager = new GriddedDataCellManager(realm); - EntityDataCellMapper entityDataCellMapper = new EntityDataCellMapper(); //add function classes to realm realm.setDataCellManager(griddedDataCellManager); - realm.setEntityDataCellMapper(entityDataCellMapper); //register within the manager realms.add(realm); return realm; @@ -113,7 +112,6 @@ public class RealmManager { //add function classes to realm realm.setDataCellManager(ViewportDataCellManager.create(realm)); - realm.setEntityDataCellMapper(new EntityDataCellMapper()); //register realms.add(realm); diff --git a/src/main/java/electrosphere/server/datacell/ServerDataCell.java b/src/main/java/electrosphere/server/datacell/ServerDataCell.java index 68135b6b..16ef3066 100644 --- a/src/main/java/electrosphere/server/datacell/ServerDataCell.java +++ b/src/main/java/electrosphere/server/datacell/ServerDataCell.java @@ -22,7 +22,7 @@ import java.util.Set; * of simulation. It just acts as an object to relate players and entities by location. * This SHOULD be used for networking purposes. This is the mechanism to scope * network messages by location. If you are looking for something closer to a scene from - * a traditional game engine, EntityManager is effectively a scene for all intents and + * a traditional game engine, Realm is effectively a scene for all intents and * purposes. * */ @@ -62,9 +62,9 @@ public class ServerDataCell { * @param p */ public void addPlayer(Player p){ - if(!activePlayers.contains(p)){ + if(!this.containsPlayer(p)){ activePlayers.add(p); - serializeStateToPlayer(p); + this.serializeStateToPlayer(p); } } @@ -134,7 +134,7 @@ public class ServerDataCell { */ void serializeStateToPlayer(Player player){ for(Entity entity : scene.getEntityList()){ - serializeEntityToPlayer(entity,player); + this.serializeEntityToPlayer(entity,player); } } @@ -144,21 +144,23 @@ public class ServerDataCell { * @param player The player to send the entity to */ void serializeEntityToPlayer(Entity entity, Player player){ - EntityType type = CommonEntityUtils.getEntityType(entity); - if(type != null){ - switch(type){ - case CREATURE: { - CreatureUtils.sendEntityToPlayer(player, entity); - } break; - case ITEM: { - ItemUtils.sendEntityToPlayer(player, entity); - } break; - case FOLIAGE: { - FoliageUtils.sendFoliageToPlayer(player, entity); - } break; - case COMMON: { - CommonEntityUtils.sendEntityToPlayer(player, entity); - } break; + if(!player.hasSentPlayerEntity() || player.getPlayerEntity() == null || player.getPlayerEntity() != entity){ + EntityType type = CommonEntityUtils.getEntityType(entity); + if(type != null){ + switch(type){ + case CREATURE: { + CreatureUtils.sendEntityToPlayer(player, entity); + } break; + case ITEM: { + ItemUtils.sendEntityToPlayer(player, entity); + } break; + case FOLIAGE: { + FoliageUtils.sendFoliageToPlayer(player, entity); + } break; + case COMMON: { + CommonEntityUtils.sendEntityToPlayer(player, entity); + } break; + } } } } @@ -181,32 +183,42 @@ public class ServerDataCell { * @param newCell The new datacell it's moving to */ public static void moveEntityFromCellToCell(Entity entity, ServerDataCell oldCell, ServerDataCell newCell){ - //swap which holds the entity - if(oldCell != null){ - oldCell.getScene().deregisterEntity(entity); + if(entity == null){ + throw new Error("Passed null entity! " + entity); } + if(oldCell == null){ + throw new Error("Passed null oldCell! " + oldCell); + } + if(newCell == null){ + throw new Error("Passed null newCell! " + newCell); + } + //swap which holds the entity + oldCell.getScene().deregisterEntity(entity); newCell.getScene().registerEntity(entity); //update entity data cell mapper Globals.entityDataCellMapper.updateEntityCell(entity, newCell); //send the entity to new players that should care about it for(Player player : newCell.activePlayers){ - if(oldCell != null){ - //if the player hasn't already seen the entity, serialize it - if(!oldCell.containsPlayer(player)){ + if(player.getPlayerEntity() == null || player.getPlayerEntity() != entity){ + if(oldCell != null){ + //if the player hasn't already seen the entity, serialize it + if(!oldCell.containsPlayer(player)){ + newCell.serializeEntityToPlayer(entity, player); + } + } else { + //if the entity wasn't in a previous cell, send it to all players newCell.serializeEntityToPlayer(entity, player); } - } else { - //if the entity wasn't in a previous cell, send it to all players - newCell.serializeEntityToPlayer(entity, player); } } //delete the entity for players that dont care about it - if(oldCell != null){ - for(Player player : oldCell.activePlayers){ - if(!newCell.containsPlayer(player)){ - //if the player isn't also in the new cell, delete the entity - player.addMessage(EntityMessage.constructDestroyMessage(entity.getId())); - } + for(Player player : oldCell.activePlayers){ + if( + !newCell.containsPlayer(player) && + (player.getPlayerEntity() == null || player.getPlayerEntity() != entity) + ){ + //if the player isn't also in the new cell, delete the entity + player.addMessage(EntityMessage.constructDestroyMessage(entity.getId())); } } } diff --git a/src/main/java/electrosphere/server/datacell/utils/DataCellSearchUtils.java b/src/main/java/electrosphere/server/datacell/utils/DataCellSearchUtils.java index 90dc498f..3ae22022 100644 --- a/src/main/java/electrosphere/server/datacell/utils/DataCellSearchUtils.java +++ b/src/main/java/electrosphere/server/datacell/utils/DataCellSearchUtils.java @@ -29,7 +29,7 @@ public class DataCellSearchUtils { LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to get entity data cell of an entity that is not assigned to a realm!")); return null; } - return realm.getEntityDataCellMapper().getEntityDataCell(entity); + return Globals.entityDataCellMapper.getEntityDataCell(entity); } /** diff --git a/src/main/java/electrosphere/server/datacell/utils/ServerEntityTagUtils.java b/src/main/java/electrosphere/server/datacell/utils/ServerEntityTagUtils.java index d6696a39..297d3b42 100644 --- a/src/main/java/electrosphere/server/datacell/utils/ServerEntityTagUtils.java +++ b/src/main/java/electrosphere/server/datacell/utils/ServerEntityTagUtils.java @@ -1,5 +1,6 @@ package electrosphere.server.datacell.utils; +import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.server.datacell.ServerDataCell; @@ -14,7 +15,7 @@ public class ServerEntityTagUtils { * @param tag The tag */ public static void attachTagToEntity(Entity entity, String tag){ - ServerDataCell cell = DataCellSearchUtils.getEntityDataCell(entity); + ServerDataCell cell = Globals.entityDataCellMapper.getEntityDataCell(entity); cell.getScene().registerEntityToTag(entity, tag); } @@ -24,7 +25,7 @@ public class ServerEntityTagUtils { * @param tag The tag */ public static void removeTagFromEntity(Entity entity, String tag){ - ServerDataCell cell = DataCellSearchUtils.getEntityDataCell(entity); + ServerDataCell cell = Globals.entityDataCellMapper.getEntityDataCell(entity); cell.getScene().removeEntityFromTag(entity, tag); }