Fix memory leak
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				studiorailgun/Renderer/pipeline/head This commit looks good
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	studiorailgun/Renderer/pipeline/head This commit looks good
				
			This commit is contained in:
		
							parent
							
								
									0e2c29d8a1
								
							
						
					
					
						commit
						569311a23e
					
				
							
								
								
									
										8
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							| @ -9,14 +9,14 @@ | |||||||
|             "name": "Launch Current File", |             "name": "Launch Current File", | ||||||
|             "request": "launch", |             "request": "launch", | ||||||
|             "mainClass": "${file}", |             "mainClass": "${file}", | ||||||
|             "vmArgs": "-Xmx4G -Xms1024m -Djava.library.path=./shared-folder -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.dump\"" |             "vmArgs": "-Xmx4G -Xms1024m -Djava.library.path=./shared-folder -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.hprof\"" | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             "type": "java", |             "type": "java", | ||||||
|             "name": "Launch Main", |             "name": "Launch Main", | ||||||
|             "request": "launch", |             "request": "launch", | ||||||
|             "mainClass": "electrosphere.engine.Main", |             "mainClass": "electrosphere.engine.Main", | ||||||
|             "vmArgs": "-Xmx4G -Xms1024m -Djava.library.path=./shared-folder -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.dump\"", |             "vmArgs": "-Xmx4G -Xms1024m -Djava.library.path=./shared-folder -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.hprof\"", | ||||||
|             "projectName": "Renderer" |             "projectName": "Renderer" | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @ -24,7 +24,7 @@ | |||||||
|             "name": "Launch Main (Debug Memory)", |             "name": "Launch Main (Debug Memory)", | ||||||
|             "request": "launch", |             "request": "launch", | ||||||
|             "mainClass": "electrosphere.engine.Main", |             "mainClass": "electrosphere.engine.Main", | ||||||
|             "vmArgs": "-Xmx4G -Xms1024m -Djava.library.path=./shared-folder -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.dump\" -javaagent:./lwjglx-debug-1.0.0.jar=t;o=trace.log", |             "vmArgs": "-Xmx4G -Xms1024m -Djava.library.path=./shared-folder -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.hprof\" -javaagent:./lwjglx-debug-1.0.0.jar=t;o=trace.log", | ||||||
|             "projectName": "Renderer" |             "projectName": "Renderer" | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @ -35,7 +35,7 @@ | |||||||
|             "env": { |             "env": { | ||||||
|                 "ALSOFT_LOGLEVEL": 4, |                 "ALSOFT_LOGLEVEL": 4, | ||||||
|             }, |             }, | ||||||
|             "vmArgs": "-Xmx4G -Xms1024m -Djava.library.path=./shared-folder -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.dump\"", |             "vmArgs": "-Xmx4G -Xms1024m -Djava.library.path=./shared-folder -XX:+UseZGC -XX:SoftMaxHeapSize=3G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\"./tmp/heap.hprof\"", | ||||||
|             "projectName": "Renderer" |             "projectName": "Renderer" | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|  | |||||||
| @ -1206,6 +1206,7 @@ Move header file generation location | |||||||
| Add more debugging tools for fluids | Add more debugging tools for fluids | ||||||
| Remove conditional update check in fluid sim | Remove conditional update check in fluid sim | ||||||
| Explicit memory management of fluid chunk cache buffers | Explicit memory management of fluid chunk cache buffers | ||||||
|  | Fix GriddedDataCellManager memory leak caused by physics and ConcurrentHashMap | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								docs/src/tools/profiling/eclipsememoryanalyzer.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/src/tools/profiling/eclipsememoryanalyzer.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | @page eclipsememoryanalyzer Eclipse Memory Analyzer | ||||||
|  | 
 | ||||||
|  | Useful for viewing memory usage (particularly gives good reports of memory leaks) | ||||||
							
								
								
									
										3
									
								
								docs/src/tools/profiling/visualvm.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/src/tools/profiling/visualvm.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | @page visualvm VisualVM | ||||||
|  | 
 | ||||||
|  | Useful for profiling CPU usage | ||||||
| @ -6,3 +6,5 @@ | |||||||
|  - @subpage indexdocumentation |  - @subpage indexdocumentation | ||||||
|  - @subpage jenkins |  - @subpage jenkins | ||||||
|  - @subpage gimp |  - @subpage gimp | ||||||
|  |  - @subpage visualvm | ||||||
|  |  - @subpage eclipsememoryanalyzer | ||||||
| @ -18,7 +18,7 @@ public class BlockChunkCache { | |||||||
|     /** |     /** | ||||||
|      * Number of chunks to cache |      * Number of chunks to cache | ||||||
|      */ |      */ | ||||||
|     static final int CACHE_SIZE = 1500; |     static final int CACHE_SIZE = 500; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * The size of the cache |      * The size of the cache | ||||||
|  | |||||||
| @ -17,12 +17,10 @@ import electrosphere.entity.Entity; | |||||||
| import electrosphere.entity.EntityCreationUtils; | import electrosphere.entity.EntityCreationUtils; | ||||||
| import electrosphere.entity.EntityDataStrings; | import electrosphere.entity.EntityDataStrings; | ||||||
| import electrosphere.entity.EntityUtils; | import electrosphere.entity.EntityUtils; | ||||||
| import electrosphere.entity.ServerEntityUtils; |  | ||||||
| import electrosphere.entity.types.collision.CollisionObjUtils; | import electrosphere.entity.types.collision.CollisionObjUtils; | ||||||
| import electrosphere.logger.LoggerInterface; | import electrosphere.logger.LoggerInterface; | ||||||
| import electrosphere.renderer.meshgen.BlockMeshgen; | import electrosphere.renderer.meshgen.BlockMeshgen; | ||||||
| import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData; | import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData; | ||||||
| import electrosphere.server.datacell.Realm; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Generates block chunk entities |  * Generates block chunk entities | ||||||
| @ -104,30 +102,16 @@ public class BlockChunkEntity { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Creates a block chunk entity on the server |      * Creates a block chunk entity on the server | ||||||
|      * @param realm The realm |      * @param entity The entity to populate | ||||||
|      * @param position The position of the chunk |  | ||||||
|      * @param weights The weights for the block chunk |      * @param weights The weights for the block chunk | ||||||
|      * @param values The values of each voxel in the chunk |      * @param values The values of each voxel in the chunk | ||||||
|      * @return The block entity |      * @return The block entity | ||||||
|      */ |      */ | ||||||
|     public static Entity serverCreateBlockChunkEntity(Realm realm, Vector3d position, BlockMeshData blockChunkData){ |     public static void serverCreateBlockChunkEntity(Entity entity, BlockMeshData blockChunkData){ | ||||||
|          |  | ||||||
|         Entity rVal = EntityCreationUtils.createServerEntity(realm, position); |  | ||||||
|         if(blockChunkData.getVertices().length > 0){ |         if(blockChunkData.getVertices().length > 0){ | ||||||
|             PhysicsEntityUtils.serverAttachTriGeomRigidBody(rVal, blockChunkData); |             PhysicsEntityUtils.serverAttachTriGeomRigidBody(entity, blockChunkData); | ||||||
|             rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); |             entity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         //position entity |  | ||||||
|         //this needs to be called at the end of this function. |  | ||||||
|         //Burried underneath this is function call to initialize a server side entity. |  | ||||||
|         //The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored |  | ||||||
|         //the server will not be able to synchronize it properly. |  | ||||||
|         ServerEntityUtils.initiallyPositionEntity(realm,rVal,position); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         return rVal; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -18,12 +18,10 @@ import electrosphere.entity.Entity; | |||||||
| import electrosphere.entity.EntityCreationUtils; | import electrosphere.entity.EntityCreationUtils; | ||||||
| import electrosphere.entity.EntityDataStrings; | import electrosphere.entity.EntityDataStrings; | ||||||
| import electrosphere.entity.EntityUtils; | import electrosphere.entity.EntityUtils; | ||||||
| import electrosphere.entity.ServerEntityUtils; |  | ||||||
| import electrosphere.entity.types.collision.CollisionObjUtils; | import electrosphere.entity.types.collision.CollisionObjUtils; | ||||||
| import electrosphere.logger.LoggerInterface; | import electrosphere.logger.LoggerInterface; | ||||||
| import electrosphere.renderer.meshgen.TransvoxelModelGeneration; | import electrosphere.renderer.meshgen.TransvoxelModelGeneration; | ||||||
| import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; | import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; | ||||||
| import electrosphere.server.datacell.Realm; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Utilities for creating terrain chunk entities |  * Utilities for creating terrain chunk entities | ||||||
| @ -104,38 +102,24 @@ public class TerrainChunk { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Creates a terrain chunk entity on the server |      * Creates a terrain chunk entity on the server | ||||||
|      * @param realm The realm |      * @param entity The entity to populate | ||||||
|      * @param position The position of the chunk |  | ||||||
|      * @param weights The weights for the terrain chunk |      * @param weights The weights for the terrain chunk | ||||||
|      * @param values The values of each voxel in the chunk |      * @param values The values of each voxel in the chunk | ||||||
|      * @return The terrain entity |  | ||||||
|      */ |      */ | ||||||
|     public static Entity serverCreateTerrainChunkEntity(Realm realm, Vector3d position, float[][][] weights, int[][][] values){ |     public static void serverCreateTerrainChunkEntity(Entity entity, float[][][] weights, int[][][] values){ | ||||||
|          |          | ||||||
|         TransvoxelChunkData chunkData = new TransvoxelChunkData(weights, values, ClientDrawCellManager.FULL_RES_LOD); |         TransvoxelChunkData chunkData = new TransvoxelChunkData(weights, values, ClientDrawCellManager.FULL_RES_LOD); | ||||||
|         TerrainChunkData data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData); |         TerrainChunkData data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData); | ||||||
| 
 | 
 | ||||||
|         Entity rVal = EntityCreationUtils.createServerEntity(realm, position); |  | ||||||
|         if(data.vertices.length > 0){ |         if(data.vertices.length > 0){ | ||||||
|             PhysicsEntityUtils.serverAttachTriGeomRigidBody(rVal, data); |             PhysicsEntityUtils.serverAttachTriGeomRigidBody(entity, data); | ||||||
|             rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); |             entity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); | ||||||
|             // ServerEntityUtils.initiallyPositionEntity(realm, rVal, position); |             // ServerEntityUtils.initiallyPositionEntity(realm, rVal, position); | ||||||
|                     // physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true); |                     // physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true); | ||||||
|         // Realm realm = Globals.realmManager.getEntityRealm(physicsEntity); |         // Realm realm = Globals.realmManager.getEntityRealm(physicsEntity); | ||||||
|         // realm.getCollisionEngine().registerPhysicsEntity(physicsEntity); |         // realm.getCollisionEngine().registerPhysicsEntity(physicsEntity); | ||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         //position entity |  | ||||||
|         //this needs to be called at the end of this function. |  | ||||||
|         //Burried underneath this is function call to initialize a server side entity. |  | ||||||
|         //The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored |  | ||||||
|         //the server will not be able to synchronize it properly. |  | ||||||
|         ServerEntityUtils.initiallyPositionEntity(realm,rVal,position); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         return rVal; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -11,12 +11,15 @@ import java.util.Set; | |||||||
| import java.util.concurrent.ExecutorService; | import java.util.concurrent.ExecutorService; | ||||||
| import java.util.concurrent.Executors; | import java.util.concurrent.Executors; | ||||||
| import java.util.concurrent.Semaphore; | import java.util.concurrent.Semaphore; | ||||||
|  | import java.util.concurrent.locks.ReentrantLock; | ||||||
| 
 | 
 | ||||||
| import org.joml.Vector3d; | import org.joml.Vector3d; | ||||||
| import org.joml.Vector3i; | import org.joml.Vector3i; | ||||||
| 
 | 
 | ||||||
|  | import electrosphere.client.block.BlockChunkData; | ||||||
| import electrosphere.engine.Globals; | import electrosphere.engine.Globals; | ||||||
| import electrosphere.entity.Entity; | import electrosphere.entity.Entity; | ||||||
|  | import electrosphere.entity.EntityCreationUtils; | ||||||
| import electrosphere.entity.EntityUtils; | import electrosphere.entity.EntityUtils; | ||||||
| import electrosphere.entity.ServerEntityUtils; | import electrosphere.entity.ServerEntityUtils; | ||||||
| import electrosphere.entity.state.server.ServerPlayerViewDirTree; | import electrosphere.entity.state.server.ServerPlayerViewDirTree; | ||||||
| @ -85,7 +88,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|     /** |     /** | ||||||
|      * Loaded cells |      * Loaded cells | ||||||
|      */ |      */ | ||||||
|     Semaphore loadedCellsLock = new Semaphore(1); |     ReentrantLock loadedCellsLock = new ReentrantLock(); | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Parent realm |      * Parent realm | ||||||
| @ -172,7 +175,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|                     if(this.canCreateCell(x, y, z) && this.shouldContainPlayer(new Vector3i(x,y,z), worldPos, playerSimulationRadius)){ |                     if(this.canCreateCell(x, y, z) && this.shouldContainPlayer(new Vector3i(x,y,z), worldPos, playerSimulationRadius)){ | ||||||
|                         Vector3i targetPos = new Vector3i(x,y,z); |                         Vector3i targetPos = new Vector3i(x,y,z); | ||||||
|                         LoggerInterface.loggerEngine.DEBUG("GriddedDataCellManager: Add player to " + x + " " + y + " " + z); |                         LoggerInterface.loggerEngine.DEBUG("GriddedDataCellManager: Add player to " + x + " " + y + " " + z); | ||||||
|                         loadedCellsLock.acquireUninterruptibly(); |                         loadedCellsLock.lock(); | ||||||
|                         if(groundDataCells.get(getServerDataCellKey(targetPos)) != null){ |                         if(groundDataCells.get(getServerDataCellKey(targetPos)) != null){ | ||||||
|                             groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); |                             groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); | ||||||
|                         } else { |                         } else { | ||||||
| @ -184,7 +187,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|                             //add player |                             //add player | ||||||
|                             groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); |                             groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); | ||||||
|                         } |                         } | ||||||
|                         loadedCellsLock.release(); |                         loadedCellsLock.unlock(); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @ -221,18 +224,18 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|                     if(this.canCreateCell(x, y, z) && this.shouldContainPlayer(new Vector3i(x, y, z), newPosition, playerSimulationRadius)){ |                     if(this.canCreateCell(x, y, z) && this.shouldContainPlayer(new Vector3i(x, y, z), newPosition, playerSimulationRadius)){ | ||||||
|                         Vector3i targetPos = new Vector3i(x,y,z); |                         Vector3i targetPos = new Vector3i(x,y,z); | ||||||
|                         if(groundDataCells.get(getServerDataCellKey(targetPos)) != null){ |                         if(groundDataCells.get(getServerDataCellKey(targetPos)) != null){ | ||||||
|                             loadedCellsLock.acquireUninterruptibly(); |                             loadedCellsLock.lock(); | ||||||
|                             groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); |                             groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); | ||||||
|                             loadedCellsLock.release(); |                             loadedCellsLock.unlock(); | ||||||
|                         } else { |                         } else { | ||||||
|                             loadedCellsLock.acquireUninterruptibly(); |                             loadedCellsLock.lock(); | ||||||
|                             //create data cell |                             //create data cell | ||||||
|                             createServerDataCell(targetPos); |                             createServerDataCell(targetPos); | ||||||
|                             //add to loaded cells |                             //add to loaded cells | ||||||
|                             cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0); |                             cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0); | ||||||
|                             //add player |                             //add player | ||||||
|                             groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); |                             groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); | ||||||
|                             loadedCellsLock.release(); |                             loadedCellsLock.unlock(); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| @ -290,13 +293,32 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|         Long key = this.getServerDataCellKey(worldPos); |         Long key = this.getServerDataCellKey(worldPos); | ||||||
|         if(posPhysicsMap.containsKey(key)){ |         if(posPhysicsMap.containsKey(key)){ | ||||||
|             PhysicsDataCell cell = posPhysicsMap.get(key); |             PhysicsDataCell cell = posPhysicsMap.get(key); | ||||||
|             cell.retireCell(); |  | ||||||
|             cell.generatePhysics(); |             cell.generatePhysics(); | ||||||
|         } else { |  | ||||||
|             PhysicsDataCell cell = PhysicsDataCell.createPhysicsCell(parent, worldPos); |  | ||||||
|             cell.generatePhysics(); |  | ||||||
|             posPhysicsMap.put(key, cell); |  | ||||||
|         } |         } | ||||||
|  |         //get data to generate with | ||||||
|  |         Vector3d realPos = new Vector3d( | ||||||
|  |             worldPos.x * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET, | ||||||
|  |             worldPos.y * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET, | ||||||
|  |             worldPos.z * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         BlockChunkData blockChunkData = parent.getServerWorldData().getServerBlockManager().getChunk(worldPos.x, worldPos.y, worldPos.z); | ||||||
|  |         ServerTerrainChunk terrainChunk = parent.getServerWorldData().getServerTerrainManager().getChunk(worldPos.x, worldPos.y, worldPos.z); | ||||||
|  | 
 | ||||||
|  |         //create entities | ||||||
|  |         Entity blockEntity = EntityCreationUtils.createServerEntity(parent, realPos); | ||||||
|  |         Entity terrainEntity = EntityCreationUtils.createServerEntity(parent, realPos); | ||||||
|  |          | ||||||
|  |         //position entity | ||||||
|  |         //this needs to be called at the end of this function. | ||||||
|  |         //Burried underneath this is function call to initialize a server side entity. | ||||||
|  |         //The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored | ||||||
|  |         //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); | ||||||
|  |         cell.generatePhysics(); | ||||||
|  |         posPhysicsMap.put(key, cell); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     /** |     /** | ||||||
| @ -318,6 +340,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|                 int playerSimulationRadius = player.getSimulationRadius(); |                 int playerSimulationRadius = player.getSimulationRadius(); | ||||||
| 
 | 
 | ||||||
|                 //remove from cells that are out of range |                 //remove from cells that are out of range | ||||||
|  |                 loadedCellsLock.lock(); | ||||||
|                 for(ServerDataCell cell : this.groundDataCells.values()){ |                 for(ServerDataCell cell : this.groundDataCells.values()){ | ||||||
|                     Vector3i cellWorldPos = this.getCellWorldPosition(cell); |                     Vector3i cellWorldPos = this.getCellWorldPosition(cell); | ||||||
|                     if(cell.containsPlayer(player) && !this.shouldContainPlayer(cellWorldPos, newPosition, playerSimulationRadius)){ |                     if(cell.containsPlayer(player) && !this.shouldContainPlayer(cellWorldPos, newPosition, playerSimulationRadius)){ | ||||||
| @ -328,6 +351,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|                         this.broadcastDestructionToPlayer(player, cell); |                         this.broadcastDestructionToPlayer(player, cell); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |                 loadedCellsLock.unlock(); | ||||||
| 
 | 
 | ||||||
|                 //Add to cells that are in range |                 //Add to cells that are in range | ||||||
|                 for(int x = newPosition.x - playerSimulationRadius + 1; x < newPosition.x + playerSimulationRadius; x++){ |                 for(int x = newPosition.x - playerSimulationRadius + 1; x < newPosition.x + playerSimulationRadius; x++){ | ||||||
| @ -336,21 +360,21 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|                             if(this.canCreateCell(x,y,z) && this.shouldContainPlayer(new Vector3i(x,y,z), newPosition, playerSimulationRadius)){ |                             if(this.canCreateCell(x,y,z) && this.shouldContainPlayer(new Vector3i(x,y,z), newPosition, playerSimulationRadius)){ | ||||||
|                                 Vector3i targetPos = new Vector3i(x,y,z); |                                 Vector3i targetPos = new Vector3i(x,y,z); | ||||||
|                                 if(groundDataCells.get(this.getServerDataCellKey(targetPos)) != null){ |                                 if(groundDataCells.get(this.getServerDataCellKey(targetPos)) != null){ | ||||||
|                                     loadedCellsLock.acquireUninterruptibly(); |                                     loadedCellsLock.lock(); | ||||||
|                                     ServerDataCell cell = groundDataCells.get(this.getServerDataCellKey(targetPos)); |                                     ServerDataCell cell = groundDataCells.get(this.getServerDataCellKey(targetPos)); | ||||||
|                                     if(!cell.containsPlayer(player)){ |                                     if(!cell.containsPlayer(player)){ | ||||||
|                                         cell.addPlayer(player); |                                         cell.addPlayer(player); | ||||||
|                                     } |                                     } | ||||||
|                                     loadedCellsLock.release(); |                                     loadedCellsLock.unlock(); | ||||||
|                                 } else { |                                 } else { | ||||||
|                                     loadedCellsLock.acquireUninterruptibly(); |                                     loadedCellsLock.lock(); | ||||||
|                                     //create data cell |                                     //create data cell | ||||||
|                                     this.createServerDataCell(targetPos); |                                     this.createServerDataCell(targetPos); | ||||||
|                                     //add to loaded cells |                                     //add to loaded cells | ||||||
|                                     cellPlayerlessFrameMap.put(groundDataCells.get(this.getServerDataCellKey(targetPos)),0); |                                     cellPlayerlessFrameMap.put(groundDataCells.get(this.getServerDataCellKey(targetPos)),0); | ||||||
|                                     //add player |                                     //add player | ||||||
|                                     groundDataCells.get(this.getServerDataCellKey(targetPos)).addPlayer(player); |                                     groundDataCells.get(this.getServerDataCellKey(targetPos)).addPlayer(player); | ||||||
|                                     loadedCellsLock.release(); |                                     loadedCellsLock.unlock(); | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
| @ -368,17 +392,19 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|     public void unloadPlayerlessChunks(){ |     public void unloadPlayerlessChunks(){ | ||||||
|         if(this.unloadCells){ |         if(this.unloadCells){ | ||||||
|             //TODO: improve to make have less performance impact |             //TODO: improve to make have less performance impact | ||||||
|             loadedCellsLock.acquireUninterruptibly(); |             loadedCellsLock.lock(); | ||||||
|             for(ServerDataCell cell : this.groundDataCells.values()){ |             for(ServerDataCell cell : this.groundDataCells.values()){ | ||||||
|                 if(cell.isReady() && cell.getPlayers().size() < 1){ |                 if(cellPlayerlessFrameMap.containsKey(cell)){ | ||||||
|                     int frameCount = cellPlayerlessFrameMap.get(cell) + 1; |                     if(cell.isReady() && cell.getPlayers().size() < 1){ | ||||||
|                     cellPlayerlessFrameMap.put(cell,frameCount); |                         int frameCount = cellPlayerlessFrameMap.get(cell) + 1; | ||||||
|                     if(frameCount > UNLOAD_FRAME_THRESHOLD){ |                         cellPlayerlessFrameMap.put(cell,frameCount); | ||||||
|                         toCleanQueue.add(cell); |                         if(frameCount > UNLOAD_FRAME_THRESHOLD){ | ||||||
|                     } |                             toCleanQueue.add(cell); | ||||||
|                 } else { |                         } | ||||||
|                     if(cellPlayerlessFrameMap.get(cell) > 0){ |                     } else { | ||||||
|                         cellPlayerlessFrameMap.put(cell, 0); |                         if(cellPlayerlessFrameMap.get(cell) > 0){ | ||||||
|  |                             cellPlayerlessFrameMap.put(cell, 0); | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @ -410,6 +436,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|                 Vector3i worldPos = getCellWorldPosition(cell); |                 Vector3i worldPos = getCellWorldPosition(cell); | ||||||
|                 Long key = getServerDataCellKey(worldPos); |                 Long key = getServerDataCellKey(worldPos); | ||||||
|                 groundDataCells.remove(key); |                 groundDataCells.remove(key); | ||||||
|  |                 this.posPhysicsMap.remove(key); | ||||||
|  |                 this.cellPositionMap.remove(cell); | ||||||
|  |                 this.cellPlayerlessFrameMap.remove(cell); | ||||||
|                 //offload all entities in cell to chunk file |                 //offload all entities in cell to chunk file | ||||||
|                 serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList()); |                 serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList()); | ||||||
|                 for(Entity entity : cell.getScene().getEntityList()){ |                 for(Entity entity : cell.getScene().getEntityList()){ | ||||||
| @ -418,7 +447,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|                 //save terrain to disk |                 //save terrain to disk | ||||||
|                 serverTerrainManager.savePositionToDisk(worldPos); |                 serverTerrainManager.savePositionToDisk(worldPos); | ||||||
|             } |             } | ||||||
|             loadedCellsLock.release(); |             loadedCellsLock.unlock(); | ||||||
|             toCleanQueue.clear(); |             toCleanQueue.clear(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -428,7 +457,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|      */ |      */ | ||||||
|     public void evictAll(){ |     public void evictAll(){ | ||||||
|         //TODO: improve to make have less performance impact |         //TODO: improve to make have less performance impact | ||||||
|         loadedCellsLock.acquireUninterruptibly(); |         loadedCellsLock.lock(); | ||||||
|         for(ServerDataCell cell : this.groundDataCells.values()){ |         for(ServerDataCell cell : this.groundDataCells.values()){ | ||||||
|             int frameCount = cellPlayerlessFrameMap.get(cell) + 1; |             int frameCount = cellPlayerlessFrameMap.get(cell) + 1; | ||||||
|             cellPlayerlessFrameMap.put(cell,frameCount); |             cellPlayerlessFrameMap.put(cell,frameCount); | ||||||
| @ -439,6 +468,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|             Vector3i worldPos = getCellWorldPosition(cell); |             Vector3i worldPos = getCellWorldPosition(cell); | ||||||
|             Long key = getServerDataCellKey(worldPos); |             Long key = getServerDataCellKey(worldPos); | ||||||
|             groundDataCells.remove(key); |             groundDataCells.remove(key); | ||||||
|  |             this.posPhysicsMap.remove(key); | ||||||
|  |             this.cellPositionMap.remove(cell); | ||||||
|  |             this.cellPlayerlessFrameMap.remove(cell); | ||||||
|             //offload all entities in cell to chunk file |             //offload all entities in cell to chunk file | ||||||
|             serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList()); |             serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList()); | ||||||
|             //clear all entities in cell |             //clear all entities in cell | ||||||
| @ -446,7 +478,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|                 ServerEntityUtils.destroyEntity(entity); |                 ServerEntityUtils.destroyEntity(entity); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         loadedCellsLock.release(); |         loadedCellsLock.unlock(); | ||||||
|         this.serverTerrainManager.evictAll(); |         this.serverTerrainManager.evictAll(); | ||||||
|         toCleanQueue.clear(); |         toCleanQueue.clear(); | ||||||
|     } |     } | ||||||
| @ -498,12 +530,12 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|      */ |      */ | ||||||
|     public ServerDataCell tryCreateCellAtPoint(Vector3i worldPos){ |     public ServerDataCell tryCreateCellAtPoint(Vector3i worldPos){ | ||||||
|         if(this.canCreateCell(worldPos) && groundDataCells.get(this.getServerDataCellKey(worldPos)) == null){ |         if(this.canCreateCell(worldPos) && groundDataCells.get(this.getServerDataCellKey(worldPos)) == null){ | ||||||
|             loadedCellsLock.acquireUninterruptibly(); |             loadedCellsLock.lock(); | ||||||
|             //create data cell |             //create data cell | ||||||
|             this.createServerDataCell(worldPos); |             this.createServerDataCell(worldPos); | ||||||
|             //add to loaded cells |             //add to loaded cells | ||||||
|             cellPlayerlessFrameMap.put(groundDataCells.get(this.getServerDataCellKey(worldPos)),0); |             cellPlayerlessFrameMap.put(groundDataCells.get(this.getServerDataCellKey(worldPos)),0); | ||||||
|             loadedCellsLock.release(); |             loadedCellsLock.unlock(); | ||||||
|         } else if(groundDataCells.get(this.getServerDataCellKey(worldPos)) == null) { |         } else if(groundDataCells.get(this.getServerDataCellKey(worldPos)) == null) { | ||||||
|             LoggerInterface.loggerEngine.WARNING( |             LoggerInterface.loggerEngine.WARNING( | ||||||
|                 "Trying to create data cell outside world bounds!\n" + |                 "Trying to create data cell outside world bounds!\n" + | ||||||
| @ -532,7 +564,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|      */ |      */ | ||||||
|     public void simulate(){ |     public void simulate(){ | ||||||
|         Globals.profiler.beginCpuSample("GriddedDataCellManager.simulate"); |         Globals.profiler.beginCpuSample("GriddedDataCellManager.simulate"); | ||||||
|         loadedCellsLock.acquireUninterruptibly(); |         loadedCellsLock.lock(); | ||||||
|         for(ServerDataCell cell : this.groundDataCells.values()){ |         for(ServerDataCell cell : this.groundDataCells.values()){ | ||||||
|             if(Globals.microSimulation != null && Globals.microSimulation.isReady()){ |             if(Globals.microSimulation != null && Globals.microSimulation.isReady()){ | ||||||
|                 Globals.microSimulation.simulate(cell); |                 Globals.microSimulation.simulate(cell); | ||||||
| @ -540,19 +572,21 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
| 
 | 
 | ||||||
|             //queue fluid simulation |             //queue fluid simulation | ||||||
|             Vector3i cellPos = this.getCellWorldPosition(cell); |             Vector3i cellPos = this.getCellWorldPosition(cell); | ||||||
|             this.serverFluidManager.queue(cellPos.x, cellPos.y, cellPos.z); |             if(cellPos != null){ | ||||||
|  |                 this.serverFluidManager.queue(cellPos.x, cellPos.y, cellPos.z); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         //simulate fluids |         //simulate fluids | ||||||
|         this.serverFluidManager.simulate(); |         this.serverFluidManager.simulate((ServerFluidChunk fluidChunk) -> { | ||||||
|  |             ServerDataCell cell = getCellAtWorldPosition(fluidChunk.getWorldPosition()); | ||||||
|  |             ServerFluidChunk chunk = getFluidChunkAtPosition(fluidChunk.getWorldPosition()); | ||||||
|  |             cell.broadcastNetworkMessage( | ||||||
|  |                 TerrainMessage.constructupdateFluidDataMessage(fluidChunk.getWorldX(), fluidChunk.getWorldY(), fluidChunk.getWorldZ(), TerrainProtocol.constructFluidByteBuffer(chunk).array()) | ||||||
|  |             ); | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|         //rebroadcast updated chunks |         loadedCellsLock.unlock(); | ||||||
|         for(ServerFluidChunk chunk : this.serverFluidManager.getBroadcastQueue()){ |  | ||||||
|             this.rebroadcastFluidChunk(chunk.getWorldPosition()); |  | ||||||
|         } |  | ||||||
|         this.serverFluidManager.getBroadcastQueue().clear(); |  | ||||||
| 
 |  | ||||||
|         loadedCellsLock.release(); |  | ||||||
|         this.unloadPlayerlessChunks(); |         this.unloadPlayerlessChunks(); | ||||||
|         this.updatePlayerPositions(); |         this.updatePlayerPositions(); | ||||||
|         Globals.profiler.endCpuSample(); |         Globals.profiler.endCpuSample(); | ||||||
| @ -580,18 +614,55 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|      * Because cell hasn't been registered yet, no simulation is performed until the physics is created. |      * Because cell hasn't been registered yet, no simulation is performed until the physics is created. | ||||||
|      * @param worldPos |      * @param worldPos | ||||||
|      */ |      */ | ||||||
|     private void runPhysicsGenerationThread(Vector3i worldPos){ |     private static void runPhysicsGenerationThread( | ||||||
|  |         Vector3i worldPos, | ||||||
|  |         Long key, | ||||||
|  |         PhysicsDataCell cell, | ||||||
|  |         Map<Long, PhysicsDataCell> posPhysicsMap, | ||||||
|  |         Map<Long, ServerDataCell> groundDataCells, | ||||||
|  |         Realm realm | ||||||
|  |     ){ | ||||||
|  |         //get data to generate with | ||||||
|  |         Vector3d realPos = new Vector3d( | ||||||
|  |             worldPos.x * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET, | ||||||
|  |             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 | ||||||
|  |         Entity blockEntity = EntityCreationUtils.createServerEntity(realm, realPos); | ||||||
|  |         Entity terrainEntity = EntityCreationUtils.createServerEntity(realm, realPos); | ||||||
|  |          | ||||||
|  |         //position entity | ||||||
|  |         //this needs to be called at the end of this function. | ||||||
|  |         //Burried underneath this is function call to initialize a server side entity. | ||||||
|  |         //The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored | ||||||
|  |         //the server will not be able to synchronize it properly. | ||||||
|  |         ServerEntityUtils.initiallyPositionEntity(realm,blockEntity,realPos); | ||||||
|  |         ServerEntityUtils.initiallyPositionEntity(realm,terrainEntity,realPos); | ||||||
|  | 
 | ||||||
|  |         PhysicsDataCell targetCell = PhysicsDataCell.createPhysicsCell(worldPos, terrainEntity, blockEntity, terrainChunk, blockChunkData); | ||||||
|  |         if(cell == null){ | ||||||
|  |             posPhysicsMap.put(key, targetCell); | ||||||
|  |         } else { | ||||||
|  |             ServerEntityUtils.destroyEntity(terrainEntity); | ||||||
|  |             ServerEntityUtils.destroyEntity(blockEntity); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         generationService.submit(() -> { |         generationService.submit(() -> { | ||||||
|             //create physics entities |             //create physics entities | ||||||
|             createTerrainPhysicsEntities(worldPos); |             if(cell != null){ | ||||||
|             //set ready |                 cell.retireCell(); | ||||||
|             if(groundDataCells.get(getServerDataCellKey(worldPos)) != null){ |                 cell.generatePhysics(); | ||||||
|                 groundDataCells.get(getServerDataCellKey(worldPos)).setReady(true); |  | ||||||
|             } else { |             } else { | ||||||
|                 LoggerInterface.loggerEngine.WARNING("Finished generating physics for server cell, but cell is null!"); |                 targetCell.generatePhysics(); | ||||||
|             } |             } | ||||||
|  |             //set ready | ||||||
|  |             dataCell.setReady(true); | ||||||
|         }); |         }); | ||||||
|         groundDataCells.get(getServerDataCellKey(worldPos)).setReady(false); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -616,7 +687,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|         cellPositionMap.put(rVal,new Vector3i(worldPos)); |         cellPositionMap.put(rVal,new Vector3i(worldPos)); | ||||||
|         serverContentManager.generateContentForDataCell(parent, worldPos, rVal, cellKey); |         serverContentManager.generateContentForDataCell(parent, worldPos, rVal, cellKey); | ||||||
|         //generates physics for the cell in a dedicated thread then finally registers |         //generates physics for the cell in a dedicated thread then finally registers | ||||||
|         this.runPhysicsGenerationThread(worldPos); |         Long key = this.getServerDataCellKey(worldPos); | ||||||
|  |         PhysicsDataCell cell = posPhysicsMap.get(key); | ||||||
|  |         GriddedDataCellManager.runPhysicsGenerationThread(worldPos,key,cell,this.posPhysicsMap,this.groundDataCells,this.parent); | ||||||
|         return rVal; |         return rVal; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -699,6 +772,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|                 serverTerrainManager.deformTerrainAtLocationToValue(toUpdate, new Vector3i(localVoxelX, localVoxelY, localVoxelZ), weight, type); |                 serverTerrainManager.deformTerrainAtLocationToValue(toUpdate, new Vector3i(localVoxelX, localVoxelY, localVoxelZ), weight, type); | ||||||
| 
 | 
 | ||||||
|                 //update anything loaded |                 //update anything loaded | ||||||
|  |                 this.loadedCellsLock.lock(); | ||||||
|                 ServerDataCell cell = groundDataCells.get(this.getServerDataCellKey(toUpdate)); |                 ServerDataCell cell = groundDataCells.get(this.getServerDataCellKey(toUpdate)); | ||||||
|                 if(cell != null){ |                 if(cell != null){ | ||||||
|                     //update physics |                     //update physics | ||||||
| @ -710,6 +784,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|                         localVoxelX, localVoxelY, localVoxelZ,  |                         localVoxelX, localVoxelY, localVoxelZ,  | ||||||
|                         weight, type)); |                         weight, type)); | ||||||
|                 } |                 } | ||||||
|  |                 this.loadedCellsLock.unlock(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         terrainEditLock.release(); |         terrainEditLock.release(); | ||||||
| @ -747,20 +822,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Rebroadcasts the fluid cell at a given position |  | ||||||
|      * @param worldPosition the world position |  | ||||||
|      */ |  | ||||||
|     private void rebroadcastFluidChunk(Vector3i worldPosition){ |  | ||||||
|         Globals.profiler.beginAggregateCpuSample("GriddedDataCellManager.rebroadcastFluidChunk"); |  | ||||||
|         ServerDataCell cell = getCellAtWorldPosition(worldPosition); |  | ||||||
|         ServerFluidChunk chunk = getFluidChunkAtPosition(worldPosition); |  | ||||||
|         cell.broadcastNetworkMessage( |  | ||||||
|             TerrainMessage.constructupdateFluidDataMessage(worldPosition.x, worldPosition.y, worldPosition.z, TerrainProtocol.constructFluidByteBuffer(chunk).array()) |  | ||||||
|         ); |  | ||||||
|         Globals.profiler.endCpuSample(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |     @Override | ||||||
|     public void save(String saveName) { |     public void save(String saveName) { | ||||||
|         for(ServerDataCell cell : this.groundDataCells.values()){ |         for(ServerDataCell cell : this.groundDataCells.values()){ | ||||||
|  | |||||||
| @ -1,9 +1,10 @@ | |||||||
| package electrosphere.server.datacell; | package electrosphere.server.datacell; | ||||||
| 
 | 
 | ||||||
|  | import java.util.HashMap; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| import java.util.concurrent.ConcurrentHashMap; |  | ||||||
| import java.util.concurrent.CopyOnWriteArraySet; | import java.util.concurrent.CopyOnWriteArraySet; | ||||||
|  | import java.util.concurrent.locks.ReentrantLock; | ||||||
| 
 | 
 | ||||||
| import org.joml.Vector3d; | import org.joml.Vector3d; | ||||||
| 
 | 
 | ||||||
| @ -27,9 +28,14 @@ public class RealmManager { | |||||||
|     //All realms in this manager |     //All realms in this manager | ||||||
|     Set<Realm> realms = new CopyOnWriteArraySet<Realm>(); |     Set<Realm> realms = new CopyOnWriteArraySet<Realm>(); | ||||||
|     //Map of entities to the realm the entity is in |     //Map of entities to the realm the entity is in | ||||||
|     Map<Entity,Realm> entityToRealmMap = new ConcurrentHashMap<Entity,Realm>(); |     Map<Entity,Realm> entityToRealmMap = new HashMap<Entity,Realm>(); | ||||||
|     //Map of player to the realm the player is in |     //Map of player to the realm the player is in | ||||||
|     Map<Player,Realm> playerToRealmMap = new ConcurrentHashMap<Player,Realm>(); |     Map<Player,Realm> playerToRealmMap = new HashMap<Player,Realm>(); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Lock for thread-safing the manager | ||||||
|  |      */ | ||||||
|  |     ReentrantLock lock = new ReentrantLock(); | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Constructor |      * Constructor | ||||||
| @ -126,7 +132,9 @@ public class RealmManager { | |||||||
|      * @param realm The realm |      * @param realm The realm | ||||||
|      */ |      */ | ||||||
|     public void mapEntityToRealm(Entity entity, Realm realm){ |     public void mapEntityToRealm(Entity entity, Realm realm){ | ||||||
|  |         lock.lock(); | ||||||
|         entityToRealmMap.put(entity, realm); |         entityToRealmMap.put(entity, realm); | ||||||
|  |         lock.unlock(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -134,7 +142,9 @@ public class RealmManager { | |||||||
|      * @param entity The entity to remove |      * @param entity The entity to remove | ||||||
|      */ |      */ | ||||||
|     public void removeEntity(Entity entity){ |     public void removeEntity(Entity entity){ | ||||||
|  |         lock.lock(); | ||||||
|         entityToRealmMap.remove(entity); |         entityToRealmMap.remove(entity); | ||||||
|  |         lock.unlock(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -143,7 +153,10 @@ public class RealmManager { | |||||||
|      * @return The realm, or null if the entity is not inside a realm |      * @return The realm, or null if the entity is not inside a realm | ||||||
|      */ |      */ | ||||||
|     public Realm getEntityRealm(Entity entity){ |     public Realm getEntityRealm(Entity entity){ | ||||||
|         return entityToRealmMap.get(entity); |         lock.lock(); | ||||||
|  |         Realm rVal = entityToRealmMap.get(entity); | ||||||
|  |         lock.unlock(); | ||||||
|  |         return rVal; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -173,7 +186,9 @@ public class RealmManager { | |||||||
|      * @param realm The realm |      * @param realm The realm | ||||||
|      */ |      */ | ||||||
|     public void setPlayerRealm(Player player, Realm realm){ |     public void setPlayerRealm(Player player, Realm realm){ | ||||||
|  |         lock.lock(); | ||||||
|         playerToRealmMap.put(player, realm); |         playerToRealmMap.put(player, realm); | ||||||
|  |         lock.unlock(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -182,7 +197,10 @@ public class RealmManager { | |||||||
|      * @return The realm |      * @return The realm | ||||||
|      */ |      */ | ||||||
|     public Realm getPlayerRealm(Player player){ |     public Realm getPlayerRealm(Player player){ | ||||||
|         return playerToRealmMap.get(player); |         lock.lock(); | ||||||
|  |         Realm rVal = playerToRealmMap.get(player); | ||||||
|  |         lock.unlock(); | ||||||
|  |         return rVal; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -221,8 +239,10 @@ public class RealmManager { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         this.realms.clear(); |         this.realms.clear(); | ||||||
|  |         lock.lock(); | ||||||
|         this.entityToRealmMap.clear(); |         this.entityToRealmMap.clear(); | ||||||
|         this.playerToRealmMap.clear(); |         this.playerToRealmMap.clear(); | ||||||
|  |         lock.unlock(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| package electrosphere.server.datacell.physics; | package electrosphere.server.datacell.physics; | ||||||
| 
 | 
 | ||||||
|  | import electrosphere.client.block.BlockChunkData; | ||||||
| import electrosphere.client.terrain.cache.ChunkData; | import electrosphere.client.terrain.cache.ChunkData; | ||||||
| import electrosphere.engine.Globals; | import electrosphere.engine.Globals; | ||||||
| import electrosphere.entity.Entity; | import electrosphere.entity.Entity; | ||||||
| @ -11,9 +12,7 @@ import electrosphere.renderer.meshgen.BlockMeshgen; | |||||||
| import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData; | import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData; | ||||||
| import electrosphere.server.datacell.Realm; | import electrosphere.server.datacell.Realm; | ||||||
| import electrosphere.server.terrain.manager.ServerTerrainChunk; | import electrosphere.server.terrain.manager.ServerTerrainChunk; | ||||||
| import electrosphere.server.terrain.manager.ServerTerrainManager; |  | ||||||
| 
 | 
 | ||||||
| import org.joml.Vector3d; |  | ||||||
| import org.joml.Vector3i; | import org.joml.Vector3i; | ||||||
| import org.ode4j.ode.DBody; | import org.ode4j.ode.DBody; | ||||||
| 
 | 
 | ||||||
| @ -26,12 +25,12 @@ public class PhysicsDataCell { | |||||||
|      |      | ||||||
|     Entity physicsEntity; |     Entity physicsEntity; | ||||||
|     Entity blockPhysicsEntity; |     Entity blockPhysicsEntity; | ||||||
|  | 
 | ||||||
|  |     ServerTerrainChunk terrainChunk; | ||||||
|  |     BlockChunkData blockChunk; | ||||||
|      |      | ||||||
|     DBody physicsObject; |     DBody physicsObject; | ||||||
| 
 | 
 | ||||||
|     Realm realm; |  | ||||||
|     ServerTerrainManager serverTerrainManager; |  | ||||||
| 
 |  | ||||||
|     float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; |     float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; | ||||||
|     int[][][] types = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; |     int[][][] types = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; | ||||||
|      |      | ||||||
| @ -47,13 +46,19 @@ public class PhysicsDataCell { | |||||||
|      * @return The cell |      * @return The cell | ||||||
|      */ |      */ | ||||||
|     public static PhysicsDataCell createPhysicsCell( |     public static PhysicsDataCell createPhysicsCell( | ||||||
|         Realm realm, |         Vector3i worldPos, | ||||||
|         Vector3i worldPos |         Entity physicsEntity, | ||||||
|  |         Entity blockPhysicsEntity, | ||||||
|  |         ServerTerrainChunk currentChunk, | ||||||
|  |         BlockChunkData blockChunk | ||||||
|  | 
 | ||||||
|     ){ |     ){ | ||||||
|         PhysicsDataCell rVal = new PhysicsDataCell(); |         PhysicsDataCell rVal = new PhysicsDataCell(); | ||||||
|         rVal.realm = realm; |         rVal.physicsEntity = physicsEntity; | ||||||
|         rVal.serverTerrainManager = realm.getServerWorldData().getServerTerrainManager(); |         rVal.blockPhysicsEntity = blockPhysicsEntity; | ||||||
|         rVal.worldPos = worldPos; |         rVal.worldPos = worldPos; | ||||||
|  |         rVal.blockChunk = blockChunk; | ||||||
|  |         rVal.terrainChunk = currentChunk; | ||||||
|         return rVal; |         return rVal; | ||||||
|     } |     } | ||||||
|      |      | ||||||
| @ -72,31 +77,17 @@ public class PhysicsDataCell { | |||||||
|      */ |      */ | ||||||
|     public void generatePhysics(){ |     public void generatePhysics(){ | ||||||
|         //if the entity hasn't already been created for some reason, need to create it |         //if the entity hasn't already been created for some reason, need to create it | ||||||
|         if(physicsEntity == null){ |  | ||||||
|              |              | ||||||
|             // |         // | ||||||
|             //fill in weights and types maps |         //fill in weights and types maps | ||||||
|             // |         // | ||||||
|             this.fillInData(); |         this.fillInData(); | ||||||
| 
 | 
 | ||||||
|             Vector3d realPos = new Vector3d( |         TerrainChunk.serverCreateTerrainChunkEntity(physicsEntity, weights, types); | ||||||
|                 worldPos.x * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET, |         physicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); | ||||||
|                 worldPos.y * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET, |         BlockMeshData meshData = BlockMeshgen.rasterize(blockChunk); | ||||||
|                 worldPos.z * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET |         BlockChunkEntity.serverCreateBlockChunkEntity(blockPhysicsEntity, meshData); | ||||||
|             ); |         blockPhysicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); | ||||||
|             physicsEntity = TerrainChunk.serverCreateTerrainChunkEntity(realm, realPos, weights, types); |  | ||||||
|             physicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); |  | ||||||
|         } |  | ||||||
|         if(blockPhysicsEntity == null){ |  | ||||||
|             Vector3d realPos = new Vector3d( |  | ||||||
|                 worldPos.x * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET, |  | ||||||
|                 worldPos.y * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET, |  | ||||||
|                 worldPos.z * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET |  | ||||||
|             ); |  | ||||||
|             BlockMeshData meshData = BlockMeshgen.rasterize(realm.getServerWorldData().getServerBlockManager().getChunk(worldPos.x, worldPos.y, worldPos.z)); |  | ||||||
|             blockPhysicsEntity = BlockChunkEntity.serverCreateBlockChunkEntity(realm, realPos, meshData); |  | ||||||
|             blockPhysicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); |  | ||||||
|         } |  | ||||||
|         // //then actually perform the attach |         // //then actually perform the attach | ||||||
|         // physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true); |         // physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true); | ||||||
|         // Realm realm = Globals.realmManager.getEntityRealm(physicsEntity); |         // Realm realm = Globals.realmManager.getEntityRealm(physicsEntity); | ||||||
| @ -120,12 +111,11 @@ public class PhysicsDataCell { | |||||||
|         //fill in data |         //fill in data | ||||||
|         // |         // | ||||||
|         //main chunk |         //main chunk | ||||||
|         ServerTerrainChunk currentChunk = serverTerrainManager.getChunk(worldPos.x, worldPos.y, worldPos.z); |  | ||||||
|         for(int x = 0; x < ChunkData.CHUNK_DATA_SIZE; x++){ |         for(int x = 0; x < ChunkData.CHUNK_DATA_SIZE; x++){ | ||||||
|             for(int y = 0; y < ChunkData.CHUNK_DATA_SIZE; y++){ |             for(int y = 0; y < ChunkData.CHUNK_DATA_SIZE; y++){ | ||||||
|                 for(int z = 0; z < ChunkData.CHUNK_DATA_SIZE; z++){ |                 for(int z = 0; z < ChunkData.CHUNK_DATA_SIZE; z++){ | ||||||
|                     weights[x][y][z] = currentChunk.getWeight(x,y,z); |                     weights[x][y][z] = terrainChunk.getWeight(x,y,z); | ||||||
|                     types[x][y][z] = currentChunk.getType(x,y,z); |                     types[x][y][z] = terrainChunk.getType(x,y,z); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -14,11 +14,13 @@ import electrosphere.util.annotation.Exclude; | |||||||
| 
 | 
 | ||||||
| import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||||
| import java.nio.FloatBuffer; | import java.nio.FloatBuffer; | ||||||
|  | import java.util.ArrayList; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.LinkedList; | import java.util.LinkedList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.concurrent.locks.ReentrantLock; | import java.util.concurrent.locks.ReentrantLock; | ||||||
|  | import java.util.function.Consumer; | ||||||
| 
 | 
 | ||||||
| import org.joml.Vector3i; | import org.joml.Vector3i; | ||||||
| 
 | 
 | ||||||
| @ -30,7 +32,7 @@ public class ServerFluidManager { | |||||||
|     /** |     /** | ||||||
|      * DEfault size of the cache |      * DEfault size of the cache | ||||||
|      */ |      */ | ||||||
|     static final int DEFAULT_CACHE_SIZE = 500; |     static final int DEFAULT_CACHE_SIZE = 50; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * The number of frames to wait between updates |      * The number of frames to wait between updates | ||||||
| @ -88,13 +90,13 @@ public class ServerFluidManager { | |||||||
|     /** |     /** | ||||||
|      * The queued of chunks to simulate |      * The queued of chunks to simulate | ||||||
|      */ |      */ | ||||||
|     List<ServerFluidChunk> simulationQueue = new LinkedList<ServerFluidChunk>(); |     List<ServerFluidChunk> simulationQueue = new ArrayList<ServerFluidChunk>(); | ||||||
| 
 | 
 | ||||||
|     @Exclude |     @Exclude | ||||||
|     /** |     /** | ||||||
|      * The queue of chunks to broadcast |      * The queue of chunks to broadcast | ||||||
|      */ |      */ | ||||||
|     List<ServerFluidChunk> broadcastQueue = new LinkedList<ServerFluidChunk>(); |     List<ServerFluidChunk> broadcastQueue = new ArrayList<ServerFluidChunk>(); | ||||||
| 
 | 
 | ||||||
|     @Exclude |     @Exclude | ||||||
|     /** |     /** | ||||||
| @ -287,14 +289,16 @@ public class ServerFluidManager { | |||||||
|      * @param worldZ The world z coordinate of the chunk |      * @param worldZ The world z coordinate of the chunk | ||||||
|      */ |      */ | ||||||
|     public void queue(int worldX, int worldY, int worldZ){ |     public void queue(int worldX, int worldY, int worldZ){ | ||||||
|         ServerFluidChunk fluidChunk = this.getChunk(worldX, worldY, worldZ); |         if(simulate){ | ||||||
|         this.simulationQueue.add(fluidChunk); |             ServerFluidChunk fluidChunk = this.getChunk(worldX, worldY, worldZ); | ||||||
|  |             this.simulationQueue.add(fluidChunk); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Simulates a chunk |      * Simulates a chunk | ||||||
|      */ |      */ | ||||||
|     public void simulate(){ |     public void simulate(Consumer<ServerFluidChunk> onUpdate){ | ||||||
|         Globals.profiler.beginAggregateCpuSample("ServerFluidManager.simulate"); |         Globals.profiler.beginAggregateCpuSample("ServerFluidManager.simulate"); | ||||||
|         lock.lock(); |         lock.lock(); | ||||||
|         if(simulate){ |         if(simulate){ | ||||||
| @ -304,8 +308,15 @@ public class ServerFluidManager { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|              |              | ||||||
|             this.simulationQueue.clear(); |             //clear both queues | ||||||
|  |             while(this.simulationQueue.size() > 0){ | ||||||
|  |                 this.simulationQueue.remove(0); | ||||||
|  |             } | ||||||
|             this.broadcastSize = this.broadcastQueue.size(); |             this.broadcastSize = this.broadcastQueue.size(); | ||||||
|  |             while(this.broadcastQueue.size() > 0){ | ||||||
|  |                 ServerFluidChunk toBroadcast = this.broadcastQueue.remove(0); | ||||||
|  |                 onUpdate.accept(toBroadcast); | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             updatePhase++; |             updatePhase++; | ||||||
|             if(updatePhase > UPDATE_RATE){ |             if(updatePhase > UPDATE_RATE){ | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user