more visibility into gridded cell manager

This commit is contained in:
austin 2025-03-28 15:10:14 -04:00
parent 72e329d855
commit 7df05d611d
5 changed files with 96 additions and 45 deletions

View File

@ -1389,13 +1389,11 @@ Floating world origin
- Separately simulated regions of physics that dynamically merge/unmerge based on chunk loading - Separately simulated regions of physics that dynamically merge/unmerge based on chunk loading
Bug Fixes Bug Fixes
- Server not regenerating physics entities correctly after block edit
- Fix hitbox placement does not scale with entity scale on server - Fix hitbox placement does not scale with entity scale on server
- Calculate bounding sphere for meshes by deforming vertices with bone default pose instead of no bone deform - Calculate bounding sphere for meshes by deforming vertices with bone default pose instead of no bone deform
- Fix light cluster mapping for foliage shader - Fix light cluster mapping for foliage shader
- Fix flickering when applying yoga signal (may need to rethink arch here) - Fix flickering when applying yoga signal (may need to rethink arch here)
- Fix virtual scrollables not working - Fix virtual scrollables not working
- Fix single blades of grass generating in bad locations
Startup Performance Startup Performance
- Allow texture map to bind multiple model paths to a single set of mesh->textures - Allow texture map to bind multiple model paths to a single set of mesh->textures

View File

@ -8,7 +8,7 @@ import electrosphere.server.datacell.Realm;
import imgui.ImGui; import imgui.ImGui;
/** /**
* * Debug menu for gridded managers
*/ */
public class ImGuiGriddedManager { public class ImGuiGriddedManager {
@ -19,7 +19,7 @@ public class ImGuiGriddedManager {
* Creates the windows in this file * Creates the windows in this file
*/ */
protected static void createGriddedManagerWindows(){ protected static void createGriddedManagerWindows(){
createGriddedManagerWindow(); ImGuiGriddedManager.createGriddedManagerWindow();
} }
/** /**
@ -38,8 +38,14 @@ public class ImGuiGriddedManager {
} }
} }
if(manager != null && manager.getLoadedCells() != null){ if(manager != null){
ImGui.text("Loaded Cells: " + manager.getLoadedCells().size()); if(manager.getLoadedCells() != null){
ImGui.text("Loaded Cells: " + manager.getLoadedCells().size());
}
if(manager.getCellPlayerlessFrameMap() != null){
ImGui.text("Playerless tracking map size: " + manager.getCellPlayerlessFrameMap().keySet().size());
}
ImGui.text("Cells cleaned last frame: " + manager.getNumCleaned());
} }
} }

View File

@ -41,6 +41,7 @@ public class ServerContentManager {
* @param cell The cell * @param cell The cell
*/ */
public void generateContentForDataCell(Realm realm, Vector3i worldPos, ServerDataCell cell, Long cellKey){ public void generateContentForDataCell(Realm realm, Vector3i worldPos, ServerDataCell cell, Long cellKey){
Globals.profiler.beginCpuSample("ServerContentManager.generateContentForDataCell");
String fullPath = "/content/" + cellKey + ".dat"; String fullPath = "/content/" + cellKey + ".dat";
if(generateContent){ //in other words, if not arena mode if(generateContent){ //in other words, if not arena mode
if(FileUtils.checkSavePathExists(Globals.currentSave.getName(), fullPath)){ if(FileUtils.checkSavePathExists(Globals.currentSave.getName(), fullPath)){
@ -68,6 +69,7 @@ public class ServerContentManager {
// ), // ),
// Globals.navMeshManager.getBlockerCache().getBlocker(worldPos.x, worldPos.z)) // Globals.navMeshManager.getBlockerCache().getBlocker(worldPos.x, worldPos.z))
// ); // );
Globals.profiler.endCpuSample();
} }
/** /**

View File

@ -131,6 +131,11 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
*/ */
Map<Long,PhysicsDataCell> posPhysicsMap = new HashMap<Long,PhysicsDataCell>(); Map<Long,PhysicsDataCell> posPhysicsMap = new HashMap<Long,PhysicsDataCell>();
/**
* Number of data cells cleaned up in the most recent frame
*/
int numCleaned = 0;
/** /**
* Constructor * Constructor
* @param parent The gridded data cell manager's parent realm * @param parent The gridded data cell manager's parent realm
@ -183,8 +188,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
LoggerInterface.loggerEngine.DEBUG("Creating new cell @ " + x + " " + y + " " + z); LoggerInterface.loggerEngine.DEBUG("Creating new cell @ " + x + " " + y + " " + z);
//create data cell //create data cell
this.createServerDataCell(targetPos); this.createServerDataCell(targetPos);
//add to loaded cells
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0);
//add player //add player
groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player);
} }
@ -224,18 +227,16 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
for(int z = newPosition.x - playerSimulationRadius; z < newPosition.z + playerSimulationRadius + 1; z++){ for(int z = newPosition.x - playerSimulationRadius; z < newPosition.z + playerSimulationRadius + 1; z++){
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(this.getServerDataCellKey(targetPos)) != null){
loadedCellsLock.lock(); loadedCellsLock.lock();
groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); groundDataCells.get(this.getServerDataCellKey(targetPos)).addPlayer(player);
loadedCellsLock.unlock(); loadedCellsLock.unlock();
} else { } else {
loadedCellsLock.lock(); loadedCellsLock.lock();
//create data cell //create data cell
createServerDataCell(targetPos); this.createServerDataCell(targetPos);
//add to loaded cells
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0);
//add player //add player
groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); groundDataCells.get(this.getServerDataCellKey(targetPos)).addPlayer(player);
loadedCellsLock.unlock(); loadedCellsLock.unlock();
} }
} }
@ -327,6 +328,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* @return True if the player changed cell, false otherwise * @return True if the player changed cell, false otherwise
*/ */
public boolean updatePlayerPositions(){ public boolean updatePlayerPositions(){
Globals.profiler.beginCpuSample("GriddedDataCellManager.updatePlayerPositions");
boolean playerChangedChunk = false; boolean playerChangedChunk = false;
for(Player player : Globals.playerManager.getPlayers()){ for(Player player : Globals.playerManager.getPlayers()){
Entity playerEntity = player.getPlayerEntity(); Entity playerEntity = player.getPlayerEntity();
@ -342,6 +344,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//remove from cells that are out of range //remove from cells that are out of range
loadedCellsLock.lock(); loadedCellsLock.lock();
Globals.profiler.beginCpuSample("GriddedDataCellManager.updatePlayerPositions - Remove from old cells");
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)){
@ -352,9 +355,11 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
this.broadcastDestructionToPlayer(player, cell); this.broadcastDestructionToPlayer(player, cell);
} }
} }
Globals.profiler.endCpuSample();
loadedCellsLock.unlock(); loadedCellsLock.unlock();
//Add to cells that are in range //Add to cells that are in range
Globals.profiler.beginCpuSample("GriddedDataCellManager.updatePlayerPositions - Create new cells");
for(int x = newPosition.x - playerSimulationRadius + 1; x < newPosition.x + playerSimulationRadius; x++){ for(int x = newPosition.x - playerSimulationRadius + 1; x < newPosition.x + playerSimulationRadius; x++){
for(int y = newPosition.y - playerSimulationRadius + 1; y < newPosition.y + playerSimulationRadius; y++){ for(int y = newPosition.y - playerSimulationRadius + 1; y < newPosition.y + playerSimulationRadius; y++){
for(int z = newPosition.z - playerSimulationRadius + 1; z < newPosition.z + playerSimulationRadius; z++){ for(int z = newPosition.z - playerSimulationRadius + 1; z < newPosition.z + playerSimulationRadius; z++){
@ -371,8 +376,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
loadedCellsLock.lock(); loadedCellsLock.lock();
//create data cell //create data cell
this.createServerDataCell(targetPos); this.createServerDataCell(targetPos);
//add to loaded cells
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.unlock(); loadedCellsLock.unlock();
@ -381,8 +384,10 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
} }
} }
} }
Globals.profiler.endCpuSample();
} }
} }
Globals.profiler.endCpuSample();
return playerChangedChunk; return playerChangedChunk;
} }
@ -391,9 +396,11 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* Unloads all chunks that haven't had players in them for a set amount of time * Unloads all chunks that haven't had players in them for a set amount of time
*/ */
public void unloadPlayerlessChunks(){ public void unloadPlayerlessChunks(){
Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks");
if(this.unloadCells){ if(this.unloadCells){
//TODO: improve to make have less performance impact //TODO: improve to make have less performance impact
loadedCellsLock.lock(); loadedCellsLock.lock();
Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Increment cleaning time");
for(ServerDataCell cell : this.groundDataCells.values()){ for(ServerDataCell cell : this.groundDataCells.values()){
if(cellPlayerlessFrameMap.containsKey(cell)){ if(cellPlayerlessFrameMap.containsKey(cell)){
if(cell.isReady() && cell.getPlayers().size() < 1){ if(cell.isReady() && cell.getPlayers().size() < 1){
@ -409,6 +416,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
} }
} }
} }
Globals.profiler.endCpuSample();
Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Deconstruct timed out cells");
this.numCleaned = toCleanQueue.size();
for(ServerDataCell cell : toCleanQueue){ for(ServerDataCell cell : toCleanQueue){
boolean containsPlayerEntity = false; boolean containsPlayerEntity = false;
//clear all entities in cell //clear all entities in cell
@ -433,24 +443,38 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
if(containsPlayerEntity){ if(containsPlayerEntity){
continue; continue;
} }
parent.deregisterCell(cell); Vector3i worldPos = this.getCellWorldPosition(cell);
Vector3i worldPos = getCellWorldPosition(cell); Long key = this.getServerDataCellKey(worldPos);
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 //offload all entities in cell to chunk file
//entities are saved 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 - Destroy entities");
serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList()); serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList());
for(Entity entity : cell.getScene().getEntityList()){ for(Entity entity : cell.getScene().getEntityList()){
ServerEntityUtils.destroyEntity(entity); ServerEntityUtils.destroyEntity(entity);
} }
Globals.profiler.endCpuSample();
//save terrain to disk //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 terrain");
serverTerrainManager.savePositionToDisk(worldPos); 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.cellPlayerlessFrameMap.remove(cell);
} }
Globals.profiler.endCpuSample();
loadedCellsLock.unlock(); loadedCellsLock.unlock();
toCleanQueue.clear(); toCleanQueue.clear();
} }
Globals.profiler.endCpuSample();
} }
/** /**
@ -534,8 +558,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
loadedCellsLock.lock(); loadedCellsLock.lock();
//create data cell //create data cell
this.createServerDataCell(worldPos); this.createServerDataCell(worldPos);
//add to loaded cells
cellPlayerlessFrameMap.put(groundDataCells.get(this.getServerDataCellKey(worldPos)),0);
loadedCellsLock.unlock(); loadedCellsLock.unlock();
} else if(groundDataCells.get(this.getServerDataCellKey(worldPos)) == null) { } else if(groundDataCells.get(this.getServerDataCellKey(worldPos)) == null) {
LoggerInterface.loggerEngine.ERROR( LoggerInterface.loggerEngine.ERROR(
@ -574,20 +596,24 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
} }
//queue fluid simulation //queue fluid simulation
Vector3i cellPos = this.getCellWorldPosition(cell); if(Globals.RUN_FLUIDS){
if(cellPos != null){ 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((ServerFluidChunk fluidChunk) -> { if(Globals.RUN_FLUIDS){
ServerDataCell cell = getCellAtWorldPosition(fluidChunk.getWorldPosition()); this.serverFluidManager.simulate((ServerFluidChunk fluidChunk) -> {
ServerFluidChunk chunk = getFluidChunkAtPosition(fluidChunk.getWorldPosition()); ServerDataCell cell = getCellAtWorldPosition(fluidChunk.getWorldPosition());
cell.broadcastNetworkMessage( ServerFluidChunk chunk = getFluidChunkAtPosition(fluidChunk.getWorldPosition());
TerrainMessage.constructupdateFluidDataMessage(fluidChunk.getWorldX(), fluidChunk.getWorldY(), fluidChunk.getWorldZ(), TerrainProtocol.constructFluidByteBuffer(chunk).array()) cell.broadcastNetworkMessage(
); TerrainMessage.constructupdateFluidDataMessage(fluidChunk.getWorldX(), fluidChunk.getWorldY(), fluidChunk.getWorldZ(), TerrainProtocol.constructFluidByteBuffer(chunk).array())
}); );
});
}
loadedCellsLock.unlock(); loadedCellsLock.unlock();
this.unloadPlayerlessChunks(); this.unloadPlayerlessChunks();
@ -683,9 +709,11 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* @param cell The cell itself * @param cell The cell itself
*/ */
private ServerDataCell createServerDataCell(Vector3i worldPos){ private ServerDataCell createServerDataCell(Vector3i worldPos){
Globals.profiler.beginCpuSample("GriddedDataCellManager.createServerDataCell");
ServerDataCell rVal = parent.createNewCell(); ServerDataCell rVal = parent.createNewCell();
Long cellKey = this.getServerDataCellKey(worldPos); Long cellKey = this.getServerDataCellKey(worldPos);
groundDataCells.put(cellKey,rVal); groundDataCells.put(cellKey,rVal);
cellPlayerlessFrameMap.put(rVal,0);
LoggerInterface.loggerEngine.DEBUG("Create server data cell with key " + cellKey); LoggerInterface.loggerEngine.DEBUG("Create server data cell with key " + cellKey);
cellPositionMap.put(rVal,new Vector3i(worldPos)); cellPositionMap.put(rVal,new Vector3i(worldPos));
serverContentManager.generateContentForDataCell(parent, worldPos, rVal, cellKey); serverContentManager.generateContentForDataCell(parent, worldPos, rVal, cellKey);
@ -693,6 +721,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
Long key = this.getServerDataCellKey(worldPos); Long key = this.getServerDataCellKey(worldPos);
PhysicsDataCell cell = posPhysicsMap.get(key); PhysicsDataCell cell = posPhysicsMap.get(key);
GriddedDataCellManager.runPhysicsGenerationThread(worldPos,key,cell,this.posPhysicsMap,this.groundDataCells,this.parent); GriddedDataCellManager.runPhysicsGenerationThread(worldPos,key,cell,this.posPhysicsMap,this.groundDataCells,this.parent);
Globals.profiler.endCpuSample();
return rVal; return rVal;
} }
@ -858,14 +887,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
return returnPos; return returnPos;
} }
/**
* Gets the set of loaded cells
* @return The set of loaded cells
*/
public Collection<ServerDataCell> getLoadedCells(){
return Collections.unmodifiableCollection(this.groundDataCells.values());
}
/** /**
* Stops the executor service * Stops the executor service
*/ */
@ -918,4 +939,28 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
terrainEditLock.release(); terrainEditLock.release();
} }
/**
* Gets the set of loaded cells
* @return The set of loaded cells
*/
public Collection<ServerDataCell> getLoadedCells(){
return Collections.unmodifiableCollection(this.groundDataCells.values());
}
/**
* Gets the number of cells cleaned in the most recent frame
* @return The number of cells cleaned
*/
public int getNumCleaned(){
return this.numCleaned;
}
/**
* Gets the playerless cell->frame count map
* @return The playerless cell->frame count map
*/
public Map<ServerDataCell,Integer> getCellPlayerlessFrameMap(){
return cellPlayerlessFrameMap;
}
} }

View File

@ -308,8 +308,8 @@ public class ServerTerrainManager {
* @param position The position to save * @param position The position to save
*/ */
public void savePositionToDisk(Vector3i position){ public void savePositionToDisk(Vector3i position){
if(chunkDiskMap != null){ if(chunkDiskMap != null && chunkCache.containsChunk(position.x, position.y, position.z, ChunkData.NO_STRIDE)){
chunkDiskMap.saveToDisk(getChunk(position.x, position.y, position.z)); chunkDiskMap.saveToDisk(this.getChunk(position.x, position.y, position.z));
} }
} }