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",
|
||||
"request": "launch",
|
||||
"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",
|
||||
"name": "Launch Main",
|
||||
"request": "launch",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
@ -24,7 +24,7 @@
|
||||
"name": "Launch Main (Debug Memory)",
|
||||
"request": "launch",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
@ -35,7 +35,7 @@
|
||||
"env": {
|
||||
"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"
|
||||
},
|
||||
{
|
||||
|
||||
@ -1206,6 +1206,7 @@ Move header file generation location
|
||||
Add more debugging tools for fluids
|
||||
Remove conditional update check in fluid sim
|
||||
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 jenkins
|
||||
- @subpage gimp
|
||||
- @subpage visualvm
|
||||
- @subpage eclipsememoryanalyzer
|
||||
@ -18,7 +18,7 @@ public class BlockChunkCache {
|
||||
/**
|
||||
* Number of chunks to cache
|
||||
*/
|
||||
static final int CACHE_SIZE = 1500;
|
||||
static final int CACHE_SIZE = 500;
|
||||
|
||||
/**
|
||||
* The size of the cache
|
||||
|
||||
@ -17,12 +17,10 @@ import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityCreationUtils;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.ServerEntityUtils;
|
||||
import electrosphere.entity.types.collision.CollisionObjUtils;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.renderer.meshgen.BlockMeshgen;
|
||||
import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
|
||||
/**
|
||||
* Generates block chunk entities
|
||||
@ -104,30 +102,16 @@ public class BlockChunkEntity {
|
||||
|
||||
/**
|
||||
* Creates a block chunk entity on the server
|
||||
* @param realm The realm
|
||||
* @param position The position of the chunk
|
||||
* @param entity The entity to populate
|
||||
* @param weights The weights for the block chunk
|
||||
* @param values The values of each voxel in the chunk
|
||||
* @return The block entity
|
||||
*/
|
||||
public static Entity serverCreateBlockChunkEntity(Realm realm, Vector3d position, BlockMeshData blockChunkData){
|
||||
|
||||
Entity rVal = EntityCreationUtils.createServerEntity(realm, position);
|
||||
public static void serverCreateBlockChunkEntity(Entity entity, BlockMeshData blockChunkData){
|
||||
if(blockChunkData.getVertices().length > 0){
|
||||
PhysicsEntityUtils.serverAttachTriGeomRigidBody(rVal, blockChunkData);
|
||||
rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
|
||||
PhysicsEntityUtils.serverAttachTriGeomRigidBody(entity, blockChunkData);
|
||||
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.EntityDataStrings;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.ServerEntityUtils;
|
||||
import electrosphere.entity.types.collision.CollisionObjUtils;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.renderer.meshgen.TransvoxelModelGeneration;
|
||||
import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
|
||||
/**
|
||||
* Utilities for creating terrain chunk entities
|
||||
@ -104,38 +102,24 @@ public class TerrainChunk {
|
||||
|
||||
/**
|
||||
* Creates a terrain chunk entity on the server
|
||||
* @param realm The realm
|
||||
* @param position The position of the chunk
|
||||
* @param entity The entity to populate
|
||||
* @param weights The weights for the terrain 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);
|
||||
TerrainChunkData data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData);
|
||||
|
||||
Entity rVal = EntityCreationUtils.createServerEntity(realm, position);
|
||||
if(data.vertices.length > 0){
|
||||
PhysicsEntityUtils.serverAttachTriGeomRigidBody(rVal, data);
|
||||
rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
|
||||
PhysicsEntityUtils.serverAttachTriGeomRigidBody(entity, data);
|
||||
entity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
|
||||
// ServerEntityUtils.initiallyPositionEntity(realm, rVal, position);
|
||||
// physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true);
|
||||
// Realm realm = Globals.realmManager.getEntityRealm(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.Executors;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.block.BlockChunkData;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityCreationUtils;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.ServerEntityUtils;
|
||||
import electrosphere.entity.state.server.ServerPlayerViewDirTree;
|
||||
@ -85,7 +88,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
/**
|
||||
* Loaded cells
|
||||
*/
|
||||
Semaphore loadedCellsLock = new Semaphore(1);
|
||||
ReentrantLock loadedCellsLock = new ReentrantLock();
|
||||
|
||||
/**
|
||||
* 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)){
|
||||
Vector3i targetPos = new Vector3i(x,y,z);
|
||||
LoggerInterface.loggerEngine.DEBUG("GriddedDataCellManager: Add player to " + x + " " + y + " " + z);
|
||||
loadedCellsLock.acquireUninterruptibly();
|
||||
loadedCellsLock.lock();
|
||||
if(groundDataCells.get(getServerDataCellKey(targetPos)) != null){
|
||||
groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player);
|
||||
} else {
|
||||
@ -184,7 +187,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
//add 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)){
|
||||
Vector3i targetPos = new Vector3i(x,y,z);
|
||||
if(groundDataCells.get(getServerDataCellKey(targetPos)) != null){
|
||||
loadedCellsLock.acquireUninterruptibly();
|
||||
loadedCellsLock.lock();
|
||||
groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player);
|
||||
loadedCellsLock.release();
|
||||
loadedCellsLock.unlock();
|
||||
} else {
|
||||
loadedCellsLock.acquireUninterruptibly();
|
||||
loadedCellsLock.lock();
|
||||
//create data cell
|
||||
createServerDataCell(targetPos);
|
||||
//add to loaded cells
|
||||
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0);
|
||||
//add 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);
|
||||
if(posPhysicsMap.containsKey(key)){
|
||||
PhysicsDataCell cell = posPhysicsMap.get(key);
|
||||
cell.retireCell();
|
||||
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();
|
||||
|
||||
//remove from cells that are out of range
|
||||
loadedCellsLock.lock();
|
||||
for(ServerDataCell cell : this.groundDataCells.values()){
|
||||
Vector3i cellWorldPos = this.getCellWorldPosition(cell);
|
||||
if(cell.containsPlayer(player) && !this.shouldContainPlayer(cellWorldPos, newPosition, playerSimulationRadius)){
|
||||
@ -328,6 +351,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
this.broadcastDestructionToPlayer(player, cell);
|
||||
}
|
||||
}
|
||||
loadedCellsLock.unlock();
|
||||
|
||||
//Add to cells that are in range
|
||||
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)){
|
||||
Vector3i targetPos = new Vector3i(x,y,z);
|
||||
if(groundDataCells.get(this.getServerDataCellKey(targetPos)) != null){
|
||||
loadedCellsLock.acquireUninterruptibly();
|
||||
loadedCellsLock.lock();
|
||||
ServerDataCell cell = groundDataCells.get(this.getServerDataCellKey(targetPos));
|
||||
if(!cell.containsPlayer(player)){
|
||||
cell.addPlayer(player);
|
||||
}
|
||||
loadedCellsLock.release();
|
||||
loadedCellsLock.unlock();
|
||||
} else {
|
||||
loadedCellsLock.acquireUninterruptibly();
|
||||
loadedCellsLock.lock();
|
||||
//create data cell
|
||||
this.createServerDataCell(targetPos);
|
||||
//add to loaded cells
|
||||
cellPlayerlessFrameMap.put(groundDataCells.get(this.getServerDataCellKey(targetPos)),0);
|
||||
//add 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(){
|
||||
if(this.unloadCells){
|
||||
//TODO: improve to make have less performance impact
|
||||
loadedCellsLock.acquireUninterruptibly();
|
||||
loadedCellsLock.lock();
|
||||
for(ServerDataCell cell : this.groundDataCells.values()){
|
||||
if(cell.isReady() && cell.getPlayers().size() < 1){
|
||||
int frameCount = cellPlayerlessFrameMap.get(cell) + 1;
|
||||
cellPlayerlessFrameMap.put(cell,frameCount);
|
||||
if(frameCount > UNLOAD_FRAME_THRESHOLD){
|
||||
toCleanQueue.add(cell);
|
||||
}
|
||||
} else {
|
||||
if(cellPlayerlessFrameMap.get(cell) > 0){
|
||||
cellPlayerlessFrameMap.put(cell, 0);
|
||||
if(cellPlayerlessFrameMap.containsKey(cell)){
|
||||
if(cell.isReady() && cell.getPlayers().size() < 1){
|
||||
int frameCount = cellPlayerlessFrameMap.get(cell) + 1;
|
||||
cellPlayerlessFrameMap.put(cell,frameCount);
|
||||
if(frameCount > UNLOAD_FRAME_THRESHOLD){
|
||||
toCleanQueue.add(cell);
|
||||
}
|
||||
} else {
|
||||
if(cellPlayerlessFrameMap.get(cell) > 0){
|
||||
cellPlayerlessFrameMap.put(cell, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -410,6 +436,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
Vector3i worldPos = getCellWorldPosition(cell);
|
||||
Long key = getServerDataCellKey(worldPos);
|
||||
groundDataCells.remove(key);
|
||||
this.posPhysicsMap.remove(key);
|
||||
this.cellPositionMap.remove(cell);
|
||||
this.cellPlayerlessFrameMap.remove(cell);
|
||||
//offload all entities in cell to chunk file
|
||||
serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList());
|
||||
for(Entity entity : cell.getScene().getEntityList()){
|
||||
@ -418,7 +447,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
//save terrain to disk
|
||||
serverTerrainManager.savePositionToDisk(worldPos);
|
||||
}
|
||||
loadedCellsLock.release();
|
||||
loadedCellsLock.unlock();
|
||||
toCleanQueue.clear();
|
||||
}
|
||||
}
|
||||
@ -428,7 +457,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
*/
|
||||
public void evictAll(){
|
||||
//TODO: improve to make have less performance impact
|
||||
loadedCellsLock.acquireUninterruptibly();
|
||||
loadedCellsLock.lock();
|
||||
for(ServerDataCell cell : this.groundDataCells.values()){
|
||||
int frameCount = cellPlayerlessFrameMap.get(cell) + 1;
|
||||
cellPlayerlessFrameMap.put(cell,frameCount);
|
||||
@ -439,6 +468,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
Vector3i worldPos = getCellWorldPosition(cell);
|
||||
Long key = getServerDataCellKey(worldPos);
|
||||
groundDataCells.remove(key);
|
||||
this.posPhysicsMap.remove(key);
|
||||
this.cellPositionMap.remove(cell);
|
||||
this.cellPlayerlessFrameMap.remove(cell);
|
||||
//offload all entities in cell to chunk file
|
||||
serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList());
|
||||
//clear all entities in cell
|
||||
@ -446,7 +478,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
ServerEntityUtils.destroyEntity(entity);
|
||||
}
|
||||
}
|
||||
loadedCellsLock.release();
|
||||
loadedCellsLock.unlock();
|
||||
this.serverTerrainManager.evictAll();
|
||||
toCleanQueue.clear();
|
||||
}
|
||||
@ -498,12 +530,12 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
*/
|
||||
public ServerDataCell tryCreateCellAtPoint(Vector3i worldPos){
|
||||
if(this.canCreateCell(worldPos) && groundDataCells.get(this.getServerDataCellKey(worldPos)) == null){
|
||||
loadedCellsLock.acquireUninterruptibly();
|
||||
loadedCellsLock.lock();
|
||||
//create data cell
|
||||
this.createServerDataCell(worldPos);
|
||||
//add to loaded cells
|
||||
cellPlayerlessFrameMap.put(groundDataCells.get(this.getServerDataCellKey(worldPos)),0);
|
||||
loadedCellsLock.release();
|
||||
loadedCellsLock.unlock();
|
||||
} else if(groundDataCells.get(this.getServerDataCellKey(worldPos)) == null) {
|
||||
LoggerInterface.loggerEngine.WARNING(
|
||||
"Trying to create data cell outside world bounds!\n" +
|
||||
@ -532,7 +564,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
*/
|
||||
public void simulate(){
|
||||
Globals.profiler.beginCpuSample("GriddedDataCellManager.simulate");
|
||||
loadedCellsLock.acquireUninterruptibly();
|
||||
loadedCellsLock.lock();
|
||||
for(ServerDataCell cell : this.groundDataCells.values()){
|
||||
if(Globals.microSimulation != null && Globals.microSimulation.isReady()){
|
||||
Globals.microSimulation.simulate(cell);
|
||||
@ -540,19 +572,21 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
|
||||
//queue fluid simulation
|
||||
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
|
||||
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
|
||||
for(ServerFluidChunk chunk : this.serverFluidManager.getBroadcastQueue()){
|
||||
this.rebroadcastFluidChunk(chunk.getWorldPosition());
|
||||
}
|
||||
this.serverFluidManager.getBroadcastQueue().clear();
|
||||
|
||||
loadedCellsLock.release();
|
||||
loadedCellsLock.unlock();
|
||||
this.unloadPlayerlessChunks();
|
||||
this.updatePlayerPositions();
|
||||
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.
|
||||
* @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(() -> {
|
||||
//create physics entities
|
||||
createTerrainPhysicsEntities(worldPos);
|
||||
//set ready
|
||||
if(groundDataCells.get(getServerDataCellKey(worldPos)) != null){
|
||||
groundDataCells.get(getServerDataCellKey(worldPos)).setReady(true);
|
||||
if(cell != null){
|
||||
cell.retireCell();
|
||||
cell.generatePhysics();
|
||||
} 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));
|
||||
serverContentManager.generateContentForDataCell(parent, worldPos, rVal, cellKey);
|
||||
//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;
|
||||
}
|
||||
|
||||
@ -699,6 +772,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
serverTerrainManager.deformTerrainAtLocationToValue(toUpdate, new Vector3i(localVoxelX, localVoxelY, localVoxelZ), weight, type);
|
||||
|
||||
//update anything loaded
|
||||
this.loadedCellsLock.lock();
|
||||
ServerDataCell cell = groundDataCells.get(this.getServerDataCellKey(toUpdate));
|
||||
if(cell != null){
|
||||
//update physics
|
||||
@ -710,6 +784,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
localVoxelX, localVoxelY, localVoxelZ,
|
||||
weight, type));
|
||||
}
|
||||
this.loadedCellsLock.unlock();
|
||||
}
|
||||
}
|
||||
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
|
||||
public void save(String saveName) {
|
||||
for(ServerDataCell cell : this.groundDataCells.values()){
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
package electrosphere.server.datacell;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
|
||||
@ -27,9 +28,14 @@ public class RealmManager {
|
||||
//All realms in this manager
|
||||
Set<Realm> realms = new CopyOnWriteArraySet<Realm>();
|
||||
//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<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
|
||||
@ -126,7 +132,9 @@ public class RealmManager {
|
||||
* @param realm The realm
|
||||
*/
|
||||
public void mapEntityToRealm(Entity entity, Realm realm){
|
||||
lock.lock();
|
||||
entityToRealmMap.put(entity, realm);
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -134,7 +142,9 @@ public class RealmManager {
|
||||
* @param entity The entity to remove
|
||||
*/
|
||||
public void removeEntity(Entity entity){
|
||||
lock.lock();
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public void setPlayerRealm(Player player, Realm realm){
|
||||
lock.lock();
|
||||
playerToRealmMap.put(player, realm);
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,7 +197,10 @@ public class RealmManager {
|
||||
* @return The realm
|
||||
*/
|
||||
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();
|
||||
lock.lock();
|
||||
this.entityToRealmMap.clear();
|
||||
this.playerToRealmMap.clear();
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package electrosphere.server.datacell.physics;
|
||||
|
||||
import electrosphere.client.block.BlockChunkData;
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.Entity;
|
||||
@ -11,9 +12,7 @@ import electrosphere.renderer.meshgen.BlockMeshgen;
|
||||
import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
import org.ode4j.ode.DBody;
|
||||
|
||||
@ -26,12 +25,12 @@ public class PhysicsDataCell {
|
||||
|
||||
Entity physicsEntity;
|
||||
Entity blockPhysicsEntity;
|
||||
|
||||
ServerTerrainChunk terrainChunk;
|
||||
BlockChunkData blockChunk;
|
||||
|
||||
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];
|
||||
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
|
||||
*/
|
||||
public static PhysicsDataCell createPhysicsCell(
|
||||
Realm realm,
|
||||
Vector3i worldPos
|
||||
Vector3i worldPos,
|
||||
Entity physicsEntity,
|
||||
Entity blockPhysicsEntity,
|
||||
ServerTerrainChunk currentChunk,
|
||||
BlockChunkData blockChunk
|
||||
|
||||
){
|
||||
PhysicsDataCell rVal = new PhysicsDataCell();
|
||||
rVal.realm = realm;
|
||||
rVal.serverTerrainManager = realm.getServerWorldData().getServerTerrainManager();
|
||||
rVal.physicsEntity = physicsEntity;
|
||||
rVal.blockPhysicsEntity = blockPhysicsEntity;
|
||||
rVal.worldPos = worldPos;
|
||||
rVal.blockChunk = blockChunk;
|
||||
rVal.terrainChunk = currentChunk;
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@ -72,31 +77,17 @@ public class PhysicsDataCell {
|
||||
*/
|
||||
public void generatePhysics(){
|
||||
//if the entity hasn't already been created for some reason, need to create it
|
||||
if(physicsEntity == null){
|
||||
|
||||
//
|
||||
//fill in weights and types maps
|
||||
//
|
||||
this.fillInData();
|
||||
//
|
||||
//fill in weights and types maps
|
||||
//
|
||||
this.fillInData();
|
||||
|
||||
Vector3d realPos = new Vector3d(
|
||||
worldPos.x * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET,
|
||||
worldPos.y * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET,
|
||||
worldPos.z * ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET
|
||||
);
|
||||
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);
|
||||
}
|
||||
TerrainChunk.serverCreateTerrainChunkEntity(physicsEntity, weights, types);
|
||||
physicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
|
||||
BlockMeshData meshData = BlockMeshgen.rasterize(blockChunk);
|
||||
BlockChunkEntity.serverCreateBlockChunkEntity(blockPhysicsEntity, meshData);
|
||||
blockPhysicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
|
||||
// //then actually perform the attach
|
||||
// physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true);
|
||||
// Realm realm = Globals.realmManager.getEntityRealm(physicsEntity);
|
||||
@ -120,12 +111,11 @@ public class PhysicsDataCell {
|
||||
//fill in data
|
||||
//
|
||||
//main chunk
|
||||
ServerTerrainChunk currentChunk = serverTerrainManager.getChunk(worldPos.x, worldPos.y, worldPos.z);
|
||||
for(int x = 0; x < ChunkData.CHUNK_DATA_SIZE; x++){
|
||||
for(int y = 0; y < ChunkData.CHUNK_DATA_SIZE; y++){
|
||||
for(int z = 0; z < ChunkData.CHUNK_DATA_SIZE; z++){
|
||||
weights[x][y][z] = currentChunk.getWeight(x,y,z);
|
||||
types[x][y][z] = currentChunk.getType(x,y,z);
|
||||
weights[x][y][z] = terrainChunk.getWeight(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.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
@ -30,7 +32,7 @@ public class ServerFluidManager {
|
||||
/**
|
||||
* 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
|
||||
@ -88,13 +90,13 @@ public class ServerFluidManager {
|
||||
/**
|
||||
* The queued of chunks to simulate
|
||||
*/
|
||||
List<ServerFluidChunk> simulationQueue = new LinkedList<ServerFluidChunk>();
|
||||
List<ServerFluidChunk> simulationQueue = new ArrayList<ServerFluidChunk>();
|
||||
|
||||
@Exclude
|
||||
/**
|
||||
* The queue of chunks to broadcast
|
||||
*/
|
||||
List<ServerFluidChunk> broadcastQueue = new LinkedList<ServerFluidChunk>();
|
||||
List<ServerFluidChunk> broadcastQueue = new ArrayList<ServerFluidChunk>();
|
||||
|
||||
@Exclude
|
||||
/**
|
||||
@ -287,14 +289,16 @@ public class ServerFluidManager {
|
||||
* @param worldZ The world z coordinate of the chunk
|
||||
*/
|
||||
public void queue(int worldX, int worldY, int worldZ){
|
||||
ServerFluidChunk fluidChunk = this.getChunk(worldX, worldY, worldZ);
|
||||
this.simulationQueue.add(fluidChunk);
|
||||
if(simulate){
|
||||
ServerFluidChunk fluidChunk = this.getChunk(worldX, worldY, worldZ);
|
||||
this.simulationQueue.add(fluidChunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a chunk
|
||||
*/
|
||||
public void simulate(){
|
||||
public void simulate(Consumer<ServerFluidChunk> onUpdate){
|
||||
Globals.profiler.beginAggregateCpuSample("ServerFluidManager.simulate");
|
||||
lock.lock();
|
||||
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();
|
||||
while(this.broadcastQueue.size() > 0){
|
||||
ServerFluidChunk toBroadcast = this.broadcastQueue.remove(0);
|
||||
onUpdate.accept(toBroadcast);
|
||||
}
|
||||
|
||||
updatePhase++;
|
||||
if(updatePhase > UPDATE_RATE){
|
||||
|
||||
Loading…
Reference in New Issue
Block a user