gridded data cell improvements
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2025-05-25 12:14:10 -04:00
parent d812393d4c
commit da3ada73d0
3 changed files with 166 additions and 100 deletions

View File

@ -1988,6 +1988,8 @@ Performance improvements
- Reduce bones on LOD human model - Reduce bones on LOD human model
Increase human move speed Increase human move speed
LOD components re-attach physics LOD components re-attach physics
Memory improvements
- Gridded data cell physics cell pooling

View File

@ -72,112 +72,117 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
/** /**
* The number of frames without players that must pass before a server data cell is unloaded * The number of frames without players that must pass before a server data cell is unloaded
*/ */
static final int UNLOAD_FRAME_THRESHOLD = 100; private static final int UNLOAD_FRAME_THRESHOLD = 100;
/** /**
* The distance at which simulation is queued * The distance at which simulation is queued
*/ */
static final double SIMULATION_DISTANCE_CUTOFF = 5; private static final double SIMULATION_DISTANCE_CUTOFF = 5;
/** /**
* Big number used when scanning for a data cell to spawn a macro object within * Big number used when scanning for a data cell to spawn a macro object within
*/ */
static final double MACRO_SCANNING_BIG_NUMBER = 10000; private static final double MACRO_SCANNING_BIG_NUMBER = 10000;
/** /**
* Used for generating physics chunks * Used for generating physics chunks
*/ */
ExecutorService generationService = null; private ExecutorService generationService = null;
/** /**
* The service for loading data cells from disk * The service for loading data cells from disk
*/ */
GriddedDataCellLoaderService loaderService; private GriddedDataCellLoaderService loaderService;
/** /**
* Tracks whether this manager has been flagged to unload cells or not * Tracks whether this manager has been flagged to unload cells or not
*/ */
boolean unloadCells = true; private boolean unloadCells = true;
/** /**
* These are going to be the natural ground grid of data cells, but we're going to have more than this * These are going to be the natural ground grid of data cells, but we're going to have more than this
*/ */
Map<Long,ServerDataCell> groundDataCells = new HashMap<Long,ServerDataCell>(); private Map<Long,ServerDataCell> groundDataCells = new HashMap<Long,ServerDataCell>();
/** /**
* Map of server cell to its world position * Map of server cell to its world position
*/ */
Map<ServerDataCell,Vector3i> cellPositionMap = new HashMap<ServerDataCell,Vector3i>(); private Map<ServerDataCell,Vector3i> cellPositionMap = new HashMap<ServerDataCell,Vector3i>();
/** /**
* Map of server data cell to the number of frames said cell has had no players * Map of server data cell to the number of frames said cell has had no players
*/ */
Map<ServerDataCell,Integer> cellPlayerlessFrameMap = new HashMap<ServerDataCell,Integer>(); private Map<ServerDataCell,Integer> cellPlayerlessFrameMap = new HashMap<ServerDataCell,Integer>();
/** /**
* A map of ServerDataCell->GriddedDataCellTrackingData * A map of ServerDataCell->GriddedDataCellTrackingData
*/ */
Map<ServerDataCell,GriddedDataCellTrackingData> cellTrackingMap = new HashMap<ServerDataCell,GriddedDataCellTrackingData>(); private Map<ServerDataCell,GriddedDataCellTrackingData> cellTrackingMap = new HashMap<ServerDataCell,GriddedDataCellTrackingData>();
/** /**
* Loaded cells * Loaded cells
*/ */
ReentrantLock loadedCellsLock = new ReentrantLock(); private ReentrantLock loadedCellsLock = new ReentrantLock();
/** /**
* Parent realm * Parent realm
*/ */
Realm parent; private Realm parent;
/** /**
* The world data of the parent * The world data of the parent
*/ */
ServerWorldData serverWorldData; private ServerWorldData serverWorldData;
/** /**
* Manager for terrain for this particular cell manager * Manager for terrain for this particular cell manager
*/ */
ServerTerrainManager serverTerrainManager; private ServerTerrainManager serverTerrainManager;
/** /**
* Manager for fluids for this particular cell manager * Manager for fluids for this particular cell manager
*/ */
ServerFluidManager serverFluidManager; private ServerFluidManager serverFluidManager;
/** /**
* Lock for terrain editing * Lock for terrain editing
*/ */
Semaphore terrainEditLock = new Semaphore(1); private Semaphore terrainEditLock = new Semaphore(1);
/** /**
* Manager for getting entities to fill in a cell * Manager for getting entities to fill in a cell
*/ */
ServerContentManager serverContentManager; private ServerContentManager serverContentManager;
/** /**
* Used for cleaning server data cells no longer in use from the realm * Used for cleaning server data cells no longer in use from the realm
*/ */
Set<ServerDataCell> toCleanQueue = new HashSet<ServerDataCell>(); private Set<ServerDataCell> toCleanQueue = new HashSet<ServerDataCell>();
/** /**
* Map of world position key -> physics cell * Map of world position key -> physics cell
*/ */
Map<Long,PhysicsDataCell> posPhysicsMap = new HashMap<Long,PhysicsDataCell>(); private Map<Long,PhysicsDataCell> posPhysicsMap = new HashMap<Long,PhysicsDataCell>();
/**
* Pooling for physics data cells
*/
private LinkedList<PhysicsDataCell> cellPool = new LinkedList<PhysicsDataCell>();
/** /**
* Number of data cells cleaned up in the most recent frame * Number of data cells cleaned up in the most recent frame
*/ */
int numCleaned = 0; private int numCleaned = 0;
/** /**
* Queue of cells that need to have their physics regenerated (ie on block edits) * Queue of cells that need to have their physics regenerated (ie on block edits)
*/ */
Map<Long,Vector3i> physicsQueue = new HashMap<Long,Vector3i>(); private Map<Long,Vector3i> physicsQueue = new HashMap<Long,Vector3i>();
/** /**
* The pathfinder for the manager * The pathfinder for the manager
*/ */
VoxelPathfinder pathfinder; private VoxelPathfinder pathfinder;
/** /**
* Caches lookups for nearby entities between simulate() calls * Caches lookups for nearby entities between simulate() calls
@ -359,7 +364,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
loadedCellsLock.lock(); loadedCellsLock.lock();
if(posPhysicsMap.containsKey(key)){ if(posPhysicsMap.containsKey(key)){
PhysicsDataCell cell = posPhysicsMap.get(key); PhysicsDataCell cell = posPhysicsMap.get(key);
cell.retireCell(); cell.destroyEntities();
} }
loadedCellsLock.unlock(); loadedCellsLock.unlock();
//get data to generate with //get data to generate with
@ -384,7 +389,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//the server will not be able to synchronize it properly. //the server will not be able to synchronize it properly.
ServerEntityUtils.initiallyPositionEntity(parent,blockEntity,realPos); ServerEntityUtils.initiallyPositionEntity(parent,blockEntity,realPos);
ServerEntityUtils.initiallyPositionEntity(parent,terrainEntity,realPos); ServerEntityUtils.initiallyPositionEntity(parent,terrainEntity,realPos);
PhysicsDataCell cell = PhysicsDataCell.createPhysicsCell(worldPos, terrainEntity, blockEntity); PhysicsDataCell cell = this.getPhysicsDataCell(worldPos, terrainEntity, blockEntity);
cell.setTerrainChunk(terrainChunk); cell.setTerrainChunk(terrainChunk);
cell.setBlockChunk(blockChunkData); cell.setBlockChunk(blockChunkData);
cell.generatePhysics(); cell.generatePhysics();
@ -545,39 +550,8 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
continue; continue;
} }
this.unloadCell(cell);
Vector3i worldPos = this.getCellWorldPosition(cell);
Long key = this.getServerDataCellKey(worldPos);
//entities are serialized before tracking is removed. This makes sure that any side effects from calling destroyEntity (ie if it looks up the chunk that we're deleting)
//don't trigger the chunk to be re-created
Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Serialize entities");
ContentSerialization serializedEntities = ContentSerialization.constructContentSerialization(cell.getScene().getEntityList());
Globals.profiler.endCpuSample();
Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Destroy entities");
for(Entity entity : cell.getScene().getEntityList()){
ServerEntityUtils.destroyEntity(entity);
}
Globals.profiler.endCpuSample();
//save terrain to disk
//terrain is saved before tracking is removed. This makes sure that any side effects from calling savePositionToDisk (ie if it looks up the chunk that we're deleting)
//don't trigger the chunk to be re-created
Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Store data");
this.loaderService.queueLocationBasedOperation(key, () -> {
serverContentManager.saveSerializationToDisk(key, serializedEntities);
serverTerrainManager.savePositionToDisk(worldPos);
});
Globals.profiler.endCpuSample();
//deregister from all tracking structures
parent.deregisterCell(cell);
groundDataCells.remove(key);
this.posPhysicsMap.remove(key);
this.cellPositionMap.remove(cell);
this.cellTrackingMap.remove(cell);
this.cellPlayerlessFrameMap.remove(cell);
} }
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
loadedCellsLock.unlock(); loadedCellsLock.unlock();
@ -598,26 +572,73 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
toCleanQueue.add(cell); toCleanQueue.add(cell);
} }
for(ServerDataCell cell : toCleanQueue){ for(ServerDataCell cell : toCleanQueue){
parent.deregisterCell(cell); this.unloadCell(cell);
Vector3i worldPos = this.getCellWorldPosition(cell);
Long key = getServerDataCellKey(worldPos);
groundDataCells.remove(key);
this.posPhysicsMap.remove(key);
this.cellPositionMap.remove(cell);
this.cellPlayerlessFrameMap.remove(cell);
this.cellTrackingMap.remove(cell);
//offload all entities in cell to chunk file
serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList());
//clear all entities in cell
for(Entity entity : cell.getScene().getEntityList()){
ServerEntityUtils.destroyEntity(entity);
}
} }
loadedCellsLock.unlock(); loadedCellsLock.unlock();
this.serverTerrainManager.evictAll(); this.serverTerrainManager.evictAll();
toCleanQueue.clear(); toCleanQueue.clear();
} }
/**
* Unloads a server data cell
* @param cell The cell
*/
public void unloadCell(ServerDataCell cell){
Vector3i worldPos = this.getCellWorldPosition(cell);
Long key = this.getServerDataCellKey(worldPos);
//entities are serialized before tracking is removed. This makes sure that any side effects from calling destroyEntity (ie if it looks up the chunk that we're deleting)
//don't trigger the chunk to be re-created
Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Serialize entities");
ContentSerialization serializedEntities = ContentSerialization.constructContentSerialization(cell.getScene().getEntityList());
Globals.profiler.endCpuSample();
Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Destroy entities");
for(Entity entity : cell.getScene().getEntityList()){
ServerEntityUtils.destroyEntity(entity);
}
Globals.profiler.endCpuSample();
//save terrain to disk
//terrain is saved before tracking is removed. This makes sure that any side effects from calling savePositionToDisk (ie if it looks up the chunk that we're deleting)
//don't trigger the chunk to be re-created
Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Store data");
this.loaderService.queueLocationBasedOperation(key, () -> {
serverContentManager.saveSerializationToDisk(key, serializedEntities);
serverTerrainManager.savePositionToDisk(worldPos);
});
Globals.profiler.endCpuSample();
//deregister from all tracking structures
this.parent.deregisterCell(cell);
this.groundDataCells.remove(key);
PhysicsDataCell releasedCell = this.posPhysicsMap.remove(key);
if(releasedCell != null){
this.cellPool.add(releasedCell);
releasedCell.destroyEntities();
}
this.cellPositionMap.remove(cell);
this.cellTrackingMap.remove(cell);
this.cellPlayerlessFrameMap.remove(cell);
}
/**
* Gets a physics data cell from the pool
* @return The physics data cell
*/
private PhysicsDataCell getPhysicsDataCell(Vector3i worldPos, Entity physicsEntity, Entity blockPhysicsEntity){
PhysicsDataCell rVal = null;
loadedCellsLock.lock();
if(cellPool.size() > 0){
rVal = this.cellPool.poll();
rVal.reset(worldPos, physicsEntity, blockPhysicsEntity);
} else {
rVal = PhysicsDataCell.createPhysicsCell(physicsEntity, blockPhysicsEntity);
}
loadedCellsLock.unlock();
return rVal;
}
/** /**
* Get data cell at a given real point in this realm * Get data cell at a given real point in this realm
* @param point The real point * @param point The real point
@ -819,7 +840,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
ServerEntityUtils.initiallyPositionEntity(realm,blockEntity,realPos); ServerEntityUtils.initiallyPositionEntity(realm,blockEntity,realPos);
ServerEntityUtils.initiallyPositionEntity(realm,terrainEntity,realPos); ServerEntityUtils.initiallyPositionEntity(realm,terrainEntity,realPos);
PhysicsDataCell targetCell = PhysicsDataCell.createPhysicsCell(worldPos, terrainEntity, blockEntity); PhysicsDataCell targetCell = this.getPhysicsDataCell(worldPos, terrainEntity, blockEntity);
if(cell == null){ if(cell == null){
posPhysicsMap.put(key, targetCell); posPhysicsMap.put(key, targetCell);
} else { } else {
@ -847,7 +868,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//create physics entities //create physics entities
if(cell != null){ if(cell != null){
cell.retireCell(); cell.destroyEntities();
cell.generatePhysics(); cell.generatePhysics();
} else { } else {
targetCell.generatePhysics(); targetCell.generatePhysics();
@ -882,6 +903,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
Long cellKey = this.getServerDataCellKey(localWorldPos); Long cellKey = this.getServerDataCellKey(localWorldPos);
loadedCellsLock.lock(); loadedCellsLock.lock();
if(groundDataCells.containsKey(cellKey)){
throw new Error("Creating server data cell at position that already has a cell! " + localWorldPos);
}
groundDataCells.put(cellKey,rVal); groundDataCells.put(cellKey,rVal);
cellPlayerlessFrameMap.put(rVal,0); cellPlayerlessFrameMap.put(rVal,0);
LoggerInterface.loggerEngine.DEBUG("Create server data cell with key " + cellKey); LoggerInterface.loggerEngine.DEBUG("Create server data cell with key " + cellKey);
@ -1154,6 +1178,21 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
return cellPlayerlessFrameMap; return cellPlayerlessFrameMap;
} }
/**
* Gets the tracking data for a cell
* @param cell The cell
* @return The tracking data as a string
*/
public String getCellData(ServerDataCell cell){
String message = "Failed to find position of cell!\n" +
"groundDataCells: " + this.groundDataCells.values().contains(cell) + "\n" +
"cellPositionMap: " + this.cellPositionMap.keySet().contains(cell) + "\n" +
"cellPlayerlessFrameMap: " + this.cellPositionMap.keySet().contains(cell) + "\n" +
"cellTrackingMap: " + this.cellTrackingMap.keySet().contains(cell) + "\n" +
"";
return message;
}
@Override @Override
public List<Vector3d> findPath(Vector3d start, Vector3d end){ public List<Vector3d> findPath(Vector3d start, Vector3d end){
Vector3i startChunkPos = ServerWorldData.convertRealToChunkSpace(start); Vector3i startChunkPos = ServerWorldData.convertRealToChunkSpace(start);

View File

@ -3,7 +3,6 @@ package electrosphere.server.datacell.physics;
import electrosphere.client.block.BlockChunkData; import electrosphere.client.block.BlockChunkData;
import electrosphere.client.terrain.cache.ChunkData; import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.client.terrain.data.TerrainChunkData; import electrosphere.client.terrain.data.TerrainChunkData;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.ServerEntityUtils; import electrosphere.entity.ServerEntityUtils;
@ -11,39 +10,54 @@ import electrosphere.entity.types.terrain.BlockChunkEntity;
import electrosphere.entity.types.terrain.TerrainChunk; import electrosphere.entity.types.terrain.TerrainChunk;
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;
import electrosphere.server.physics.terrain.manager.ServerTerrainChunk; import electrosphere.server.physics.terrain.manager.ServerTerrainChunk;
import org.joml.Vector3i; import org.joml.Vector3i;
import org.ode4j.ode.DBody;
/** /**
* An entity which contains physics for terrain for a given chunk on the server * An entity which contains physics for terrain for a given chunk on the server
*/ */
public class PhysicsDataCell { public class PhysicsDataCell {
Vector3i worldPos;
Entity physicsEntity; /**
Entity blockPhysicsEntity; * The terrain physics entity
*/
private Entity physicsEntity;
ServerTerrainChunk terrainChunk; /**
BlockChunkData blockChunk; * The block physics entity
*/
DBody physicsObject; private Entity blockPhysicsEntity;
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]; * The terrain chunk data
*/
private ServerTerrainChunk terrainChunk;
/**
* The block chunk data
*/
private BlockChunkData blockChunk;
/**
* The weight data
*/
private float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
/**
* The type data
*/
private int[][][] types = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
/** /**
* The terrain vertex data * The terrain vertex data
*/ */
TerrainChunkData terrainChunkData; private TerrainChunkData terrainChunkData;
/** /**
* The block vertex data * The block vertex data
*/ */
BlockMeshData blockData; private BlockMeshData blockData;
/** /**
* Creates a physics cell * Creates a physics cell
@ -52,7 +66,6 @@ public class PhysicsDataCell {
* @return The cell * @return The cell
*/ */
public static PhysicsDataCell createPhysicsCell( public static PhysicsDataCell createPhysicsCell(
Vector3i worldPos,
Entity physicsEntity, Entity physicsEntity,
Entity blockPhysicsEntity Entity blockPhysicsEntity
@ -60,19 +73,40 @@ public class PhysicsDataCell {
PhysicsDataCell rVal = new PhysicsDataCell(); PhysicsDataCell rVal = new PhysicsDataCell();
rVal.physicsEntity = physicsEntity; rVal.physicsEntity = physicsEntity;
rVal.blockPhysicsEntity = blockPhysicsEntity; rVal.blockPhysicsEntity = blockPhysicsEntity;
rVal.worldPos = worldPos;
return rVal; return rVal;
} }
/** /**
* Retires a physics data cell * Retires a physics data cell
*/ */
public void retireCell(){ public void destroyEntities(){
ServerEntityUtils.destroyEntity(physicsEntity); ServerEntityUtils.destroyEntity(physicsEntity);
this.physicsEntity = null; this.physicsEntity = null;
ServerEntityUtils.destroyEntity(blockPhysicsEntity); ServerEntityUtils.destroyEntity(blockPhysicsEntity);
this.blockPhysicsEntity = null; this.blockPhysicsEntity = null;
} }
/**
* Resets the state of the physics data cell
*/
public void reset(
Vector3i worldPos,
Entity physicsEntity,
Entity blockPhysicsEntity
){
if(this.physicsEntity != null){
ServerEntityUtils.destroyEntity(this.physicsEntity);
}
if(this.blockPhysicsEntity != null){
ServerEntityUtils.destroyEntity(this.blockPhysicsEntity);
}
this.physicsEntity = physicsEntity;
this.blockPhysicsEntity = blockPhysicsEntity;
this.terrainChunk = null;
this.blockChunk = null;
this.terrainChunkData = null;
this.blockData = null;
}
/** /**
* Generates the physics entity for this chunk * Generates the physics entity for this chunk
@ -105,15 +139,6 @@ public class PhysicsDataCell {
localBlockPhysicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); localBlockPhysicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
localBlockPhysicsEntity.putData(EntityDataStrings.BLOCK_ENTITY, true); localBlockPhysicsEntity.putData(EntityDataStrings.BLOCK_ENTITY, true);
} }
/**
* Destroys the physics for this data cell
*/
public void destroyPhysics(){
Realm realm = Globals.serverState.realmManager.getEntityRealm(physicsEntity);
realm.getCollisionEngine().destroyPhysics(physicsEntity);
realm.getCollisionEngine().destroyPhysics(blockPhysicsEntity);
}
/** /**
* Fills in the internal arrays of data for generate terrain models * Fills in the internal arrays of data for generate terrain models