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
Bug Fixes
- Server not regenerating physics entities correctly after block edit
- 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
- Fix light cluster mapping for foliage shader
- Fix flickering when applying yoga signal (may need to rethink arch here)
- Fix virtual scrollables not working
- Fix single blades of grass generating in bad locations
Startup Performance
- 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;
/**
*
* Debug menu for gridded managers
*/
public class ImGuiGriddedManager {
@ -19,7 +19,7 @@ public class ImGuiGriddedManager {
* Creates the windows in this file
*/
protected static void createGriddedManagerWindows(){
createGriddedManagerWindow();
ImGuiGriddedManager.createGriddedManagerWindow();
}
/**
@ -38,8 +38,14 @@ public class ImGuiGriddedManager {
}
}
if(manager != null && manager.getLoadedCells() != null){
ImGui.text("Loaded Cells: " + manager.getLoadedCells().size());
if(manager != null){
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
*/
public void generateContentForDataCell(Realm realm, Vector3i worldPos, ServerDataCell cell, Long cellKey){
Globals.profiler.beginCpuSample("ServerContentManager.generateContentForDataCell");
String fullPath = "/content/" + cellKey + ".dat";
if(generateContent){ //in other words, if not arena mode
if(FileUtils.checkSavePathExists(Globals.currentSave.getName(), fullPath)){
@ -68,6 +69,7 @@ public class ServerContentManager {
// ),
// Globals.navMeshManager.getBlockerCache().getBlocker(worldPos.x, worldPos.z))
// );
Globals.profiler.endCpuSample();
}
/**

View File

@ -130,6 +130,11 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* Map of world position key -> physics cell
*/
Map<Long,PhysicsDataCell> posPhysicsMap = new HashMap<Long,PhysicsDataCell>();
/**
* Number of data cells cleaned up in the most recent frame
*/
int numCleaned = 0;
/**
* Constructor
@ -183,8 +188,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
LoggerInterface.loggerEngine.DEBUG("Creating new cell @ " + x + " " + y + " " + z);
//create data cell
this.createServerDataCell(targetPos);
//add to loaded cells
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0);
//add 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++){
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){
if(groundDataCells.get(this.getServerDataCellKey(targetPos)) != null){
loadedCellsLock.lock();
groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player);
groundDataCells.get(this.getServerDataCellKey(targetPos)).addPlayer(player);
loadedCellsLock.unlock();
} else {
loadedCellsLock.lock();
//create data cell
createServerDataCell(targetPos);
//add to loaded cells
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0);
this.createServerDataCell(targetPos);
//add player
groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player);
groundDataCells.get(this.getServerDataCellKey(targetPos)).addPlayer(player);
loadedCellsLock.unlock();
}
}
@ -327,6 +328,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* @return True if the player changed cell, false otherwise
*/
public boolean updatePlayerPositions(){
Globals.profiler.beginCpuSample("GriddedDataCellManager.updatePlayerPositions");
boolean playerChangedChunk = false;
for(Player player : Globals.playerManager.getPlayers()){
Entity playerEntity = player.getPlayerEntity();
@ -342,6 +344,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//remove from cells that are out of range
loadedCellsLock.lock();
Globals.profiler.beginCpuSample("GriddedDataCellManager.updatePlayerPositions - Remove from old cells");
for(ServerDataCell cell : this.groundDataCells.values()){
Vector3i cellWorldPos = this.getCellWorldPosition(cell);
if(cell.containsPlayer(player) && !this.shouldContainPlayer(cellWorldPos, newPosition, playerSimulationRadius)){
@ -352,9 +355,11 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
this.broadcastDestructionToPlayer(player, cell);
}
}
Globals.profiler.endCpuSample();
loadedCellsLock.unlock();
//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 y = newPosition.y - playerSimulationRadius + 1; y < newPosition.y + playerSimulationRadius; y++){
for(int z = newPosition.z - playerSimulationRadius + 1; z < newPosition.z + playerSimulationRadius; z++){
@ -371,8 +376,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
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.unlock();
@ -381,8 +384,10 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
}
}
}
Globals.profiler.endCpuSample();
}
}
Globals.profiler.endCpuSample();
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
*/
public void unloadPlayerlessChunks(){
Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks");
if(this.unloadCells){
//TODO: improve to make have less performance impact
loadedCellsLock.lock();
Globals.profiler.beginCpuSample("GriddedDataCellManager.unloadPlayerlessChunks - Increment cleaning time");
for(ServerDataCell cell : this.groundDataCells.values()){
if(cellPlayerlessFrameMap.containsKey(cell)){
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){
boolean containsPlayerEntity = false;
//clear all entities in cell
@ -433,24 +443,38 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
if(containsPlayerEntity){
continue;
}
parent.deregisterCell(cell);
Vector3i worldPos = getCellWorldPosition(cell);
Long key = getServerDataCellKey(worldPos);
groundDataCells.remove(key);
this.posPhysicsMap.remove(key);
this.cellPositionMap.remove(cell);
this.cellPlayerlessFrameMap.remove(cell);
Vector3i worldPos = this.getCellWorldPosition(cell);
Long key = this.getServerDataCellKey(worldPos);
//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());
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 terrain");
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();
toCleanQueue.clear();
}
Globals.profiler.endCpuSample();
}
/**
@ -534,8 +558,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
loadedCellsLock.lock();
//create data cell
this.createServerDataCell(worldPos);
//add to loaded cells
cellPlayerlessFrameMap.put(groundDataCells.get(this.getServerDataCellKey(worldPos)),0);
loadedCellsLock.unlock();
} else if(groundDataCells.get(this.getServerDataCellKey(worldPos)) == null) {
LoggerInterface.loggerEngine.ERROR(
@ -574,20 +596,24 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
}
//queue fluid simulation
Vector3i cellPos = this.getCellWorldPosition(cell);
if(cellPos != null){
this.serverFluidManager.queue(cellPos.x, cellPos.y, cellPos.z);
if(Globals.RUN_FLUIDS){
Vector3i cellPos = this.getCellWorldPosition(cell);
if(cellPos != null){
this.serverFluidManager.queue(cellPos.x, cellPos.y, cellPos.z);
}
}
}
//simulate fluids
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())
);
});
if(Globals.RUN_FLUIDS){
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())
);
});
}
loadedCellsLock.unlock();
this.unloadPlayerlessChunks();
@ -683,9 +709,11 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* @param cell The cell itself
*/
private ServerDataCell createServerDataCell(Vector3i worldPos){
Globals.profiler.beginCpuSample("GriddedDataCellManager.createServerDataCell");
ServerDataCell rVal = parent.createNewCell();
Long cellKey = this.getServerDataCellKey(worldPos);
groundDataCells.put(cellKey,rVal);
cellPlayerlessFrameMap.put(rVal,0);
LoggerInterface.loggerEngine.DEBUG("Create server data cell with key " + cellKey);
cellPositionMap.put(rVal,new Vector3i(worldPos));
serverContentManager.generateContentForDataCell(parent, worldPos, rVal, cellKey);
@ -693,6 +721,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
Long key = this.getServerDataCellKey(worldPos);
PhysicsDataCell cell = posPhysicsMap.get(key);
GriddedDataCellManager.runPhysicsGenerationThread(worldPos,key,cell,this.posPhysicsMap,this.groundDataCells,this.parent);
Globals.profiler.endCpuSample();
return rVal;
}
@ -858,14 +887,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
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
*/
@ -918,4 +939,28 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
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
*/
public void savePositionToDisk(Vector3i position){
if(chunkDiskMap != null){
chunkDiskMap.saveToDisk(getChunk(position.x, position.y, position.z));
if(chunkDiskMap != null && chunkCache.containsChunk(position.x, position.y, position.z, ChunkData.NO_STRIDE)){
chunkDiskMap.saveToDisk(this.getChunk(position.x, position.y, position.z));
}
}