server terrain chunk stride fix

This commit is contained in:
austin 2025-04-29 16:55:38 -04:00
parent a977168ad9
commit 4d9759c6ec
12 changed files with 166 additions and 19 deletions

View File

@ -1592,6 +1592,8 @@ ServerBlockManager places macro data structures when generating chunks that cont
Fix life trees creating state items when trees undefined in data
Unhashing ivec hashed keys in chunk caches
Unit tests for unhash func
Filter client entity list to terrain
Fix server loading full res chunks from disk as strided chunks

View File

@ -10,6 +10,7 @@ import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.types.terrain.TerrainChunk;
import electrosphere.renderer.meshgen.TransvoxelModelGeneration;
@ -593,6 +594,26 @@ public class DrawCell {
return cachedMinDistance;
}
}
@Override
public String toString(){
Vector3d entityPos = null;
if(modelEntity != null){
entityPos = EntityUtils.getPosition(modelEntity);
}
String rVal = "" +
"worldPos: " + worldPos + "\n" +
"lod: " + lod + "\n" +
"modelEntity: " + modelEntity + "\n" +
"entityPos: " + entityPos + "\n" +
"hasRequested: " + hasRequested + "\n" +
"hasGenerated: " + hasGenerated + "\n" +
"homogenous: " + homogenous + "\n" +
"cachedMinDistance: " + cachedMinDistance + "\n" +
""
;
return rVal;
}
}

View File

@ -0,0 +1,75 @@
package electrosphere.client.ui.menu.debug;
import org.joml.Vector3i;
import electrosphere.client.entity.camera.CameraEntityUtils;
import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.client.terrain.cells.DrawCell;
import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.ui.imgui.ImGuiWindow;
import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback;
import imgui.ImGui;
/**
* Debug ui for draw cells
*/
public class ImGuiDrawCell {
/**
* Window for viewing chunk status on server and client
*/
protected static ImGuiWindow drawCellWindow;
/**
* Creates the windows in this file
*/
protected static void createDrawCellWindows(){
ImGuiDrawCell.createDrawCellWindow();
}
/**
* Client scene entity view
*/
protected static void createDrawCellWindow(){
drawCellWindow = new ImGuiWindow("Draw Cells");
drawCellWindow.setCallback(new ImGuiWindowCallback() {
@Override
public void exec() {
if(ImGui.button("Debug camera position")){
Vector3i cameraWorldPos = Globals.clientWorldData.convertRealToWorldSpace(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
DrawCell cell = Globals.clientDrawCellManager.getDrawCell(cameraWorldPos.x, cameraWorldPos.y, cameraWorldPos.z);
LoggerInterface.loggerEngine.WARNING("" + cell);
LoggerInterface.loggerEngine.WARNING("Chunk topology:");
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(cameraWorldPos, 1);
if(data != null){
for(int x = 0; x < ChunkData.CHUNK_DATA_SIZE; x++){
String line = "";
for(int z = 0; z < ChunkData.CHUNK_DATA_SIZE; z++){
int height = 0;
for(int y = 0; y < ChunkData.CHUNK_DATA_SIZE; y++){
if(data.getType(x, y, z) != 0){
height = y;
}
}
line = line + String.format("%2d ",height);
}
LoggerInterface.loggerEngine.WARNING(line);
}
LoggerInterface.loggerEngine.WARNING("\n");
} else {
LoggerInterface.loggerEngine.WARNING("Chunk not in cache! " + cameraWorldPos);
}
}
if(ImGui.button("Request terrain at camera position")){
Vector3i cameraWorldPos = Globals.clientWorldData.convertRealToWorldSpace(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Globals.clientTerrainManager.requestChunk(cameraWorldPos.x, cameraWorldPos.y, cameraWorldPos.z, 1);
}
}
});
drawCellWindow.setOpen(false);
Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(drawCellWindow);
}
}

View File

@ -53,6 +53,7 @@ public class ImGuiWindowMacros {
ImGuiGriddedManager.createGriddedManagerWindows();
ImGuiMemory.createMemoryWindows();
ImGuiEditorWindows.initEditorWindows();
ImGuiDrawCell.createDrawCellWindows();
}
/**
@ -180,6 +181,9 @@ public class ImGuiWindowMacros {
if(ImGui.button("Network Monitor")){
ImGuiNetworkMonitor.netMonitorWindow.setOpen(true);
}
if(ImGui.button("Client Draw Cell Utils")){
ImGuiDrawCell.drawCellWindow.setOpen(true);
}
//close button
if(ImGui.button("Close")){
mainDebugWindow.setOpen(false);

View File

@ -26,6 +26,7 @@ import electrosphere.entity.types.common.CommonEntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.types.foliage.FoliageUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.entity.types.terrain.TerrainChunk;
import electrosphere.game.data.creature.type.equip.EquipPoint;
import electrosphere.game.data.foliage.type.FoliageType;
import electrosphere.logger.LoggerInterface;
@ -52,6 +53,7 @@ public class ImGuiEntityMacros {
private static boolean filterBasic = true; //filters out entities we probably wouldn't want to see (particles, terrain meshes, foliage cells, etc)
private static boolean filterToCreatures = false; //filters the entity list to just creatures
private static boolean filterToFoliage = false; //filters the entity list to just foliage
private static boolean filterToTerrain = false; //filters the entity list to just terrain
//window for viewing details about an entity
protected static ImGuiWindow clientEntityDetailWindow;
@ -99,6 +101,9 @@ public class ImGuiEntityMacros {
if(ImGui.checkbox("Filter to Foliage", filterToFoliage)){
filterToFoliage = !filterToFoliage;
}
if(ImGui.checkbox("Filter to Terrain", filterToTerrain)){
filterToTerrain = !filterToTerrain;
}
for(Entity entity : Globals.clientSceneWrapper.getScene().getEntityList()){
//filters
if(filterToCreatures && !CreatureUtils.isCreature(entity)){
@ -107,6 +112,9 @@ public class ImGuiEntityMacros {
if(filterToFoliage && !FoliageUtils.isFoliage(entity)){
continue;
}
if(filterToTerrain && !TerrainChunk.isTerrainEntity(entity)){
continue;
}
if(filterBasic &&
(
AmbientFoliage.getAmbientFoliageTree(entity) != null ||

View File

@ -65,7 +65,7 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
LoggerInterface.loggerNetworking.DEBUG("(Server) Received request for terrain " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
// System.out.println("Received request for terrain " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
TerrainProtocol.sendWorldSubChunk(connectionHandler,
message.getworldX(), message.getworldY(), message.getworldZ()
message.getworldX(), message.getworldY(), message.getworldZ(), message.getchunkResolution()
);
} break;
case REQUESTEDITVOXEL: {
@ -123,7 +123,7 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
* @param worldY the world y
* @param worldZ the world z
*/
static void sendWorldSubChunk(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ){
static void sendWorldSubChunk(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ, int stride){
Globals.profiler.beginAggregateCpuSample("TerrainProtocol(server).sendWorldSubChunk");
// System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY());
@ -133,7 +133,7 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
}
//request chunk
ServerTerrainChunk chunk = realm.getServerWorldData().getServerTerrainManager().getChunk(worldX, worldY, worldZ);
ServerTerrainChunk chunk = realm.getServerWorldData().getServerTerrainManager().getChunk(worldX, worldY, worldZ, stride);
//The length along each access of the chunk data. Typically, should be at least 17.
//Because CHUNK_SIZE is 16, 17 adds the necessary extra value. Each chunk needs the value of the immediately following position to generate

View File

@ -42,6 +42,7 @@ import electrosphere.server.pathfinding.NavMeshConstructor;
import electrosphere.server.physics.block.manager.ServerBlockManager;
import electrosphere.server.physics.fluid.manager.ServerFluidChunk;
import electrosphere.server.physics.fluid.manager.ServerFluidManager;
import electrosphere.server.physics.terrain.manager.ServerChunkCache;
import electrosphere.server.physics.terrain.manager.ServerTerrainChunk;
import electrosphere.server.physics.terrain.manager.ServerTerrainManager;
import electrosphere.server.physics.terrain.models.TerrainModel;
@ -340,7 +341,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
);
BlockChunkData blockChunkData = parent.getServerWorldData().getServerBlockManager().getChunk(worldPos.x, worldPos.y, worldPos.z);
ServerTerrainChunk terrainChunk = parent.getServerWorldData().getServerTerrainManager().getChunk(worldPos.x, worldPos.y, worldPos.z);
ServerTerrainChunk terrainChunk = parent.getServerWorldData().getServerTerrainManager().getChunk(worldPos.x, worldPos.y, worldPos.z, ServerChunkCache.STRIDE_FULL_RES);
//create entities
Entity blockEntity = EntityCreationUtils.createServerEntity(parent, realPos);
@ -784,7 +785,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
generationService.submit(() -> {
try {
BlockChunkData blockChunkData = realm.getServerWorldData().getServerBlockManager().getChunk(worldPos.x, worldPos.y, worldPos.z);
ServerTerrainChunk terrainChunk = realm.getServerWorldData().getServerTerrainManager().getChunk(worldPos.x, worldPos.y, worldPos.z);
ServerTerrainChunk terrainChunk = realm.getServerWorldData().getServerTerrainManager().getChunk(worldPos.x, worldPos.y, worldPos.z, ServerChunkCache.STRIDE_FULL_RES);
targetCell.setTerrainChunk(terrainChunk);
targetCell.setBlockChunk(blockChunkData);
@ -867,7 +868,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* @return The weight of the described voxel
*/
public float getVoxelWeightAtLocalPosition(Vector3i worldPosition, Vector3i voxelPosition) {
return serverTerrainManager.getChunk(worldPosition.x, worldPosition.y, worldPosition.z).getWeights()[voxelPosition.x][voxelPosition.y][voxelPosition.z];
return serverTerrainManager.getChunk(worldPosition.x, worldPosition.y, worldPosition.z, ServerChunkCache.STRIDE_FULL_RES).getWeights()[voxelPosition.x][voxelPosition.y][voxelPosition.z];
}
@Override
@ -878,7 +879,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* @return The type of the described voxel
*/
public int getVoxelTypeAtLocalPosition(Vector3i worldPosition, Vector3i voxelPosition) {
return serverTerrainManager.getChunk(worldPosition.x, worldPosition.y, worldPosition.z).getValues()[voxelPosition.x][voxelPosition.y][voxelPosition.z];
return serverTerrainManager.getChunk(worldPosition.x, worldPosition.y, worldPosition.z, ServerChunkCache.STRIDE_FULL_RES).getValues()[voxelPosition.x][voxelPosition.y][voxelPosition.z];
}
@Override
@ -888,7 +889,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* @return The ServerTerrainChunk of data at that position, or null if it is out of bounds or otherwise doesn't exist
*/
public ServerTerrainChunk getChunkAtPosition(Vector3i worldPosition) {
return serverTerrainManager.getChunk(worldPosition.x, worldPosition.y, worldPosition.z);
return serverTerrainManager.getChunk(worldPosition.x, worldPosition.y, worldPosition.z, ServerChunkCache.STRIDE_FULL_RES);
}
@Override

View File

@ -8,6 +8,7 @@ import electrosphere.server.physics.fluid.generation.FluidGenerator;
import electrosphere.server.physics.fluid.models.FluidModel;
import electrosphere.server.physics.fluid.simulator.FluidAcceleratedSimulator;
import electrosphere.server.physics.fluid.simulator.ServerFluidSimulator;
import electrosphere.server.physics.terrain.manager.ServerChunkCache;
import electrosphere.server.physics.terrain.manager.ServerTerrainChunk;
import electrosphere.server.physics.terrain.manager.ServerTerrainManager;
import electrosphere.util.FileUtils;
@ -359,7 +360,7 @@ public class ServerFluidManager {
lock.lock();
ServerFluidChunk rVal = chunkGenerator.generateChunk(worldX, worldY, worldZ);
ServerWorldData serverWorldData = this.parent;
ServerTerrainChunk terrainChunk = serverWorldData.getServerTerrainManager().getChunk(worldX, worldY, worldZ);
ServerTerrainChunk terrainChunk = serverWorldData.getServerTerrainManager().getChunk(worldX, worldY, worldZ, ServerChunkCache.STRIDE_FULL_RES);
for(int x = ServerFluidChunk.TRUE_DATA_OFFSET; x < ServerFluidChunk.TRUE_DATA_DIM + ServerFluidChunk.TRUE_DATA_OFFSET; x++){
for(int y = ServerFluidChunk.TRUE_DATA_OFFSET; y < ServerFluidChunk.TRUE_DATA_DIM + ServerFluidChunk.TRUE_DATA_OFFSET; y++){
for(int z = ServerFluidChunk.TRUE_DATA_OFFSET; z < ServerFluidChunk.TRUE_DATA_DIM + ServerFluidChunk.TRUE_DATA_OFFSET; z++){

View File

@ -16,6 +16,7 @@ import java.util.zip.InflaterOutputStream;
import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.physics.terrain.manager.ServerChunkCache;
import electrosphere.server.physics.terrain.manager.ServerTerrainChunk;
import electrosphere.server.saves.SaveUtils;
import electrosphere.util.FileUtils;
@ -122,9 +123,13 @@ public class ChunkDiskMap {
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @param stride The stride of the chunk
* @return True if the map contains the chunk, false otherwise
*/
public boolean containsTerrainAtPosition(int worldX, int worldY, int worldZ){
public boolean containsTerrainAtPosition(int worldX, int worldY, int worldZ, int stride){
if(stride != ServerChunkCache.STRIDE_FULL_RES){
throw new Error("Server chunk diskmap does not currently support non-full-res chunks! " + stride);
}
lock.lock();
boolean rVal = worldPosFileMap.containsKey(getTerrainChunkKey(worldX, worldY, worldZ));
lock.unlock();
@ -152,11 +157,14 @@ public class ChunkDiskMap {
* @param worldZ The z coordinate
* @return The server terrain chunk if it exists, null otherwise
*/
public ServerTerrainChunk getTerrainChunk(int worldX, int worldY, int worldZ){
public ServerTerrainChunk getTerrainChunk(int worldX, int worldY, int worldZ, int stride){
if(stride != ServerChunkCache.STRIDE_FULL_RES){
throw new Error("Server chunk diskmap does not currently support non-full-res chunks! " + stride);
}
lock.lock();
LoggerInterface.loggerEngine.INFO("Load chunk " + worldX + " " + worldY + " " + worldZ);
ServerTerrainChunk rVal = null;
if(this.containsTerrainAtPosition(worldX, worldY, worldZ)){
if(this.containsTerrainAtPosition(worldX, worldY, worldZ, stride)){
//read file
String fileName = worldPosFileMap.get(ChunkDiskMap.getTerrainChunkKey(worldX, worldY, worldZ));
byte[] rawDataCompressed = FileUtils.loadBinaryFromSavePath(Globals.currentSave.getName(), fileName);

View File

@ -102,9 +102,9 @@ public class ChunkGenerationThread implements Runnable {
chunk = chunkCache.get(worldX, worldY, worldZ, stride);
} else {
//pull from disk if it exists
if(chunkDiskMap != null){
if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){
chunk = chunkDiskMap.getTerrainChunk(worldX, worldY, worldZ);
if(chunkDiskMap != null && stride == ServerChunkCache.STRIDE_FULL_RES){
if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ, stride)){
chunk = chunkDiskMap.getTerrainChunk(worldX, worldY, worldZ, stride);
}
}
//generate if it does not exist

View File

@ -8,6 +8,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import electrosphere.client.terrain.cells.ClientDrawCellManager;
import electrosphere.server.physics.terrain.diskmap.ChunkDiskMap;
import electrosphere.util.math.HashUtils;
@ -21,6 +22,31 @@ public class ServerChunkCache {
*/
static final int CACHE_SIZE = 1500;
/**
* Stride for a full res chunk
*/
public static final int STRIDE_FULL_RES = ClientDrawCellManager.FULL_RES_LOD;
/**
* Stride for a half res chunk
*/
public static final int STRIDE_HALF_RES = ClientDrawCellManager.HALF_RES_LOD;
/**
* Stride for a quarter res chunk
*/
public static final int STRIDE_QUARTER_RES = ClientDrawCellManager.QUARTER_RES_LOD;
/**
* Stride for a eighth res chunk
*/
public static final int STRIDE_EIGHTH_RES = ClientDrawCellManager.EIGHTH_RES_LOD;
/**
* Stride for a sixteenth res chunk
*/
public static final int STRIDE_SIXTEENTH_RES = ClientDrawCellManager.SIXTEENTH_RES_LOD;
/**
* The size of the cache
*/

View File

@ -275,9 +275,10 @@ public class ServerTerrainManager {
* @param worldX The world x position
* @param worldY The world y position
* @param worldZ The world z position
* @param stride The stride of the data
* @return The ServerTerrainChunk
*/
public ServerTerrainChunk getChunk(int worldX, int worldY, int worldZ){
public ServerTerrainChunk getChunk(int worldX, int worldY, int worldZ, int stride){
Globals.profiler.beginAggregateCpuSample("ServerTerrainManager.getChunk");
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
ServerTerrainChunk returnedChunk = null;
@ -286,8 +287,8 @@ public class ServerTerrainManager {
} else {
//pull from disk if it exists
if(chunkDiskMap != null){
if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){
returnedChunk = chunkDiskMap.getTerrainChunk(worldX, worldY, worldZ);
if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ, stride)){
returnedChunk = chunkDiskMap.getTerrainChunk(worldX, worldY, worldZ, stride);
}
}
//generate if it does not exist
@ -336,7 +337,7 @@ public class ServerTerrainManager {
*/
public void savePositionToDisk(Vector3i position){
if(chunkDiskMap != null && chunkCache.containsChunk(position.x, position.y, position.z, ChunkData.NO_STRIDE)){
chunkDiskMap.saveToDisk(this.getChunk(position.x, position.y, position.z));
chunkDiskMap.saveToDisk(this.getChunk(position.x, position.y, position.z, ServerChunkCache.STRIDE_FULL_RES));
}
}