Fix memory leak
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-12-01 15:21:54 -05:00
parent 0e2c29d8a1
commit 569311a23e
12 changed files with 214 additions and 155 deletions

8
.vscode/launch.json vendored
View File

@ -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"
},
{

View File

@ -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

View File

@ -0,0 +1,3 @@
@page eclipsememoryanalyzer Eclipse Memory Analyzer
Useful for viewing memory usage (particularly gives good reports of memory leaks)

View File

@ -0,0 +1,3 @@
@page visualvm VisualVM
Useful for profiling CPU usage

View File

@ -6,3 +6,5 @@
- @subpage indexdocumentation
- @subpage jenkins
- @subpage gimp
- @subpage visualvm
- @subpage eclipsememoryanalyzer

View File

@ -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

View File

@ -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;
}
/**

View File

@ -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;
}
/**

View File

@ -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()){

View File

@ -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();
}
/**

View File

@ -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;
@ -27,10 +26,10 @@ public class PhysicsDataCell {
Entity physicsEntity;
Entity blockPhysicsEntity;
DBody physicsObject;
ServerTerrainChunk terrainChunk;
BlockChunkData blockChunk;
Realm realm;
ServerTerrainManager serverTerrainManager;
DBody physicsObject;
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);
}
}
}

View File

@ -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){