From 12e95dc2b8cbfd1b9a9fda46d71894c5048590f9 Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 29 Apr 2025 15:41:18 -0400 Subject: [PATCH] ServerBlockManager places structures --- docs/src/progress/renderertodo.md | 1 + .../entity/scene/SceneLoader.java | 4 + .../game/data/block/BlockFab.java | 6 ++ .../server/datacell/ServerWorldData.java | 39 ++++++++ .../server/macro/MacroDataLoader.java | 16 ++++ .../server/macro/structure/Structure.java | 33 ++++++- .../ServerBlockChunkGenerationThread.java | 89 ++++++++++++++++--- .../block/manager/ServerBlockManager.java | 31 ++++--- 8 files changed, 193 insertions(+), 26 deletions(-) diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 724a9357..65687f63 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1588,6 +1588,7 @@ Fab selection doesn't overflow anymore Cleaning up parts of Main class Spawn test structure in macro data on creation Macro data structures block regular foliage generation +ServerBlockManager places macro data structures when generating chunks that contain them diff --git a/src/main/java/electrosphere/entity/scene/SceneLoader.java b/src/main/java/electrosphere/entity/scene/SceneLoader.java index af15fdec..5a0a268f 100644 --- a/src/main/java/electrosphere/entity/scene/SceneLoader.java +++ b/src/main/java/electrosphere/entity/scene/SceneLoader.java @@ -149,6 +149,10 @@ public class SceneLoader { throw new UnsupportedOperationException(); } } + //hook up macro data if relevant + if(macroData != null){ + realm.getServerWorldData().getServerBlockManager().setMacroData(macroData); + } //load scripts if(!isLevelEditor && file.getInitScriptPath() != null){ Realm finalRealm = realm; diff --git a/src/main/java/electrosphere/game/data/block/BlockFab.java b/src/main/java/electrosphere/game/data/block/BlockFab.java index 6618cd07..9f80874a 100644 --- a/src/main/java/electrosphere/game/data/block/BlockFab.java +++ b/src/main/java/electrosphere/game/data/block/BlockFab.java @@ -211,6 +211,12 @@ public class BlockFab implements BlockMeshgenData { @Override public short getType(int x, int y, int z) { + if(x < 0 || y < 0 || z < 0){ + throw new Error("Negative bounds! " + x + " " + y + " " + z); + } + if(x >= dimensions.x || y >= dimensions.y || z >= dimensions.z){ + throw new Error("Out of bounds! " + x + " " + y + " " + z); + } return this.types[x * dimensions.y * dimensions.z + y * dimensions.z + z]; } diff --git a/src/main/java/electrosphere/server/datacell/ServerWorldData.java b/src/main/java/electrosphere/server/datacell/ServerWorldData.java index 8c3e92d2..f79af2ef 100644 --- a/src/main/java/electrosphere/server/datacell/ServerWorldData.java +++ b/src/main/java/electrosphere/server/datacell/ServerWorldData.java @@ -228,6 +228,21 @@ public class ServerWorldData { ); } + /** + * Converts a chunk space position to a real space position + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return The real space position + */ + public static Vector3d convertChunkToRealSpace(int x, int y, int z){ + return new Vector3d( + ServerWorldData.convertChunkToRealSpace(x), + ServerWorldData.convertChunkToRealSpace(y), + ServerWorldData.convertChunkToRealSpace(z) + ); + } + /** * Converts a real position to a local block grid position * @param real The real position @@ -237,6 +252,30 @@ public class ServerWorldData { return (int)Math.floor(real * BlockChunkData.BLOCKS_PER_UNIT_DISTANCE % BlockChunkData.CHUNK_DATA_WIDTH); } + /** + * Converts a local block grid position to a real position + * @param chunk The chunk pos + * @param blockPos The block's local pos + * @return The real position + */ + public static double convertLocalBlockToRealSpace(int chunk, int blockPos){ + return ServerWorldData.convertChunkToRealSpace(chunk + blockPos / BlockChunkData.CHUNK_DATA_WIDTH) + (blockPos % BlockChunkData.CHUNK_DATA_WIDTH) * BlockChunkData.BLOCK_SIZE_MULTIPLIER; + } + + /** + * Converts a local block grid position to a real position + * @param chunk The chunk pos + * @param blockPos The block's local pos + * @return The real position + */ + public static Vector3d convertLocalBlockToRealSpace(Vector3i chunk, Vector3i blockPos){ + return new Vector3d( + ServerWorldData.convertLocalBlockToRealSpace(chunk.x, blockPos.x), + ServerWorldData.convertLocalBlockToRealSpace(chunk.y, blockPos.y), + ServerWorldData.convertLocalBlockToRealSpace(chunk.z, blockPos.z) + ); + } + /** * Converts a chunk space coordinate to a real space coordinate * @param chunk The position within the chunk diff --git a/src/main/java/electrosphere/server/macro/MacroDataLoader.java b/src/main/java/electrosphere/server/macro/MacroDataLoader.java index ad40b5c6..0704af83 100644 --- a/src/main/java/electrosphere/server/macro/MacroDataLoader.java +++ b/src/main/java/electrosphere/server/macro/MacroDataLoader.java @@ -1,5 +1,9 @@ package electrosphere.server.macro; +import java.io.File; + +import electrosphere.game.data.block.BlockFab; +import electrosphere.server.macro.structure.Structure; import electrosphere.util.FileUtils; /** @@ -14,6 +18,18 @@ public class MacroDataLoader { */ public static MacroData loadFromSave(String saveName){ MacroData rVal = FileUtils.loadObjectFromSavePath(saveName, "macro.json", MacroData.class); + //preload and assign structure fabs + for(Structure structure : rVal.getStructures()){ + File fabFile = FileUtils.getAssetFile(structure.getFabPath()); + if(!fabFile.exists()){ + throw new Error("Failed to locate structure that does not exist! " + fabFile.getAbsolutePath()); + } + BlockFab fab = BlockFab.read(fabFile); + if(fab == null){ + throw new Error("Failed to read fab!"); + } + structure.setFab(fab); + } return rVal; } diff --git a/src/main/java/electrosphere/server/macro/structure/Structure.java b/src/main/java/electrosphere/server/macro/structure/Structure.java index 988a49a8..b872122d 100644 --- a/src/main/java/electrosphere/server/macro/structure/Structure.java +++ b/src/main/java/electrosphere/server/macro/structure/Structure.java @@ -7,10 +7,12 @@ import java.util.List; import org.joml.AABBd; import org.joml.Vector3d; +import electrosphere.game.data.block.BlockFab; import electrosphere.game.data.struct.StructureData; import electrosphere.server.macro.character.CharacterData; import electrosphere.server.macro.character.CharacterDataStrings; import electrosphere.server.macro.spatial.MacroAreaObject; +import electrosphere.util.annotation.Exclude; /** * Server representation of a structure @@ -31,6 +33,12 @@ public class Structure extends CharacterData implements MacroAreaObject { * The path to the block fab for the structure */ String fabPath; + + /** + * The actual fab + */ + @Exclude + BlockFab fab; /** * The type of the structure @@ -134,7 +142,30 @@ public class Structure extends CharacterData implements MacroAreaObject { public AABBd getAABB() { return this.aabb; } - + + /** + * Gets the path to the corresponding fab + * @return The path + */ + public String getFabPath(){ + return fabPath; + } + + /** + * Gets the fab object + * @return The fab object + */ + public BlockFab getFab() { + return fab; + } + + /** + * Sets the fab object + * @param fab The fab object + */ + public void setFab(BlockFab fab) { + this.fab = fab; + } } diff --git a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java index 308d5238..87026646 100644 --- a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java +++ b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockChunkGenerationThread.java @@ -1,12 +1,21 @@ package electrosphere.server.physics.block.manager; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.joml.AABBd; +import org.joml.Vector3d; +import org.joml.Vector3i; import electrosphere.client.block.BlockChunkCache; import electrosphere.client.block.BlockChunkData; import electrosphere.engine.Globals; import electrosphere.logger.LoggerInterface; +import electrosphere.server.datacell.ServerWorldData; +import electrosphere.server.macro.MacroData; +import electrosphere.server.macro.structure.Structure; import electrosphere.server.physics.block.diskmap.ServerBlockChunkDiskMap; /** @@ -34,6 +43,11 @@ public class ServerBlockChunkGenerationThread implements Runnable { */ BlockChunkCache chunkCache; + /** + * The macro data + */ + MacroData macroData; + /** * The world x coordinate */ @@ -61,6 +75,7 @@ public class ServerBlockChunkGenerationThread implements Runnable { /** * Creates the chunk generation job + * @param macroData The macro data * @param chunkDiskMap The chunk disk map * @param chunkCache The chunk cache on the server * @param worldX The world x coordinate @@ -70,6 +85,7 @@ public class ServerBlockChunkGenerationThread implements Runnable { * @param onLoad The work to do once the chunk is available */ public ServerBlockChunkGenerationThread( + MacroData macroData, ServerBlockChunkDiskMap chunkDiskMap, BlockChunkCache chunkCache, int worldX, int worldY, int worldZ, @@ -83,6 +99,7 @@ public class ServerBlockChunkGenerationThread implements Runnable { this.worldZ = worldZ; this.stride = stride; this.onLoad = onLoad; + this.macroData = macroData; } @Override @@ -102,19 +119,12 @@ public class ServerBlockChunkGenerationThread implements Runnable { } //generate if it does not exist if(chunk == null){ - //TODO: generate from macro-level data chunk = BlockChunkData.allocate(); - chunk.setHomogenousValue(0); - // if(worldX == 0 && worldY == 0 && worldZ == 0){ - // chunk.setHomogenousValue(BlockChunkData.NOT_HOMOGENOUS); - // for(int x = 0; x < 16; x++){ - // for(int y = 0; y < 16; y++){ - // for(int z = 0; z < 16; z++){ - // chunk.setType(x, y, z, 1); - // } - // } - // } - // } + chunk.setWorldX(worldX); + chunk.setWorldY(worldY); + chunk.setWorldZ(worldZ); + + ServerBlockChunkGenerationThread.generate(chunk, macroData, worldX, worldY, worldZ); } if(chunk != null){ chunkCache.add(worldX, worldY, worldZ, stride, chunk); @@ -139,5 +149,60 @@ public class ServerBlockChunkGenerationThread implements Runnable { LoggerInterface.loggerEngine.ERROR(e); } } + + /** + * Generates the actual values of the chunk + * @param chunk THe chunk + * @param macroData The macro data + * @param worldX THe world x coordinate + * @param worldY The world y coordinate + * @param worldZ The world z coordinate + */ + protected static void generate(BlockChunkData chunk, MacroData macroData, int worldX, int worldY, int worldZ){ + //check if this chunk intersects any macro data + AABBd localAABB = new AABBd(ServerWorldData.convertChunkToRealSpace(worldX,worldY,worldZ),ServerWorldData.convertChunkToRealSpace(worldX+1,worldY+1,worldZ+1)); + List filtered = macroData.getStructures().stream().filter((Structure struct) -> {return struct.getAABB().testAABB(localAABB);}).collect(Collectors.toList()); + if(filtered.size() > 0){ + Vector3i chunkPos = new Vector3i(worldX, worldY, worldZ); + Vector3i blockPos = new Vector3i(0,0,0); + Vector3d chunkRealPos = ServerWorldData.convertChunkToRealSpace(chunkPos); + Vector3i localBlockPos = new Vector3i(); + //contains at least one structure + for(int x = 0; x < BlockChunkData.CHUNK_DATA_WIDTH; x++){ + for(int y = 0; y < BlockChunkData.CHUNK_DATA_WIDTH; y++){ + for(int z = 0; z < BlockChunkData.CHUNK_DATA_WIDTH; z++){ + boolean placedBlock = false; + + //try placing a structure block + blockPos.set(x,y,z); + Vector3d currRealPoint = ServerWorldData.convertLocalBlockToRealSpace(chunkPos, blockPos); + for(Structure struct : filtered){ + if(struct.getAABB().testPoint(currRealPoint.x, currRealPoint.y, currRealPoint.z)){ + localBlockPos.set( + (int)((chunkRealPos.x + (x * BlockChunkData.BLOCK_SIZE_MULTIPLIER) - struct.getStartPos().x) / BlockChunkData.BLOCK_SIZE_MULTIPLIER), + (int)((chunkRealPos.y + (y * BlockChunkData.BLOCK_SIZE_MULTIPLIER) - struct.getStartPos().y) / BlockChunkData.BLOCK_SIZE_MULTIPLIER), + (int)((chunkRealPos.z + (z * BlockChunkData.BLOCK_SIZE_MULTIPLIER) - struct.getStartPos().z) / BlockChunkData.BLOCK_SIZE_MULTIPLIER) + ); + //structure file might have dimensions larger than fab, so need to make sure we're inbounds on fab file to draw data from fab file + if(localBlockPos.x < struct.getFab().getDimensions().x && localBlockPos.y < struct.getFab().getDimensions().y && localBlockPos.z < struct.getFab().getDimensions().z){ + short blocktype = struct.getFab().getType(localBlockPos.x,localBlockPos.y,localBlockPos.z); + chunk.setType(x, y, z, blocktype); + placedBlock = true; + } + } + } + + //if failed to place structure block, place an empty block + if(!placedBlock){ + chunk.setType(x, y, z, BlockChunkData.BLOCK_TYPE_EMPTY); + } + } + } + } + } else { + //an empty chunk (no structures inside) + chunk.setHomogenousValue(0); + } + } } diff --git a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java index 25ff1b3a..a7c582db 100644 --- a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java +++ b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java @@ -4,6 +4,7 @@ import electrosphere.client.block.BlockChunkCache; import electrosphere.client.block.BlockChunkData; import electrosphere.engine.Globals; import electrosphere.server.datacell.ServerWorldData; +import electrosphere.server.macro.MacroData; import electrosphere.server.physics.block.diskmap.ServerBlockChunkDiskMap; import electrosphere.util.annotation.Exclude; @@ -39,11 +40,17 @@ public class ServerBlockManager { */ ServerBlockChunkDiskMap chunkDiskMap = null; + /** + * The macro data for this world + */ + @Exclude + MacroData macroData; + /** * The threadpool for chunk generation */ @Exclude - static final ExecutorService chunkExecutorService = Executors.newFixedThreadPool(GENERATION_THREAD_POOL_SIZE); + static final ExecutorService chunkExecutorService = Executors.newFixedThreadPool(GENERATION_THREAD_POOL_SIZE); /** * Constructor @@ -120,17 +127,7 @@ public class ServerBlockManager { returnedChunk.setWorldX(worldX); returnedChunk.setWorldY(worldY); returnedChunk.setWorldZ(worldZ); - returnedChunk.setHomogenousValue(0); - // if(worldX == 0 && worldY == 0 && worldZ == 0){ - // returnedChunk.setHomogenousValue(BlockChunkData.NOT_HOMOGENOUS); - // for(int x = 3; x < 16; x++){ - // for(int y = 8; y < 16; y++){ - // for(int z = 3; z < 16; z++){ - // returnedChunk.setType(x, y, z, 1); - // } - // } - // } - // } + ServerBlockChunkGenerationThread.generate(returnedChunk, macroData, worldX, worldY, worldZ); } this.chunkCache.add(worldX, worldY, worldZ, BlockChunkData.LOD_FULL_RES, returnedChunk); } @@ -148,7 +145,7 @@ public class ServerBlockManager { */ public void getChunkAsync(int worldX, int worldY, int worldZ, int stride, Consumer onLoad){ Globals.profiler.beginAggregateCpuSample("ServerBlockManager.getChunkAsync"); - chunkExecutorService.submit(new ServerBlockChunkGenerationThread(chunkDiskMap, chunkCache, worldX, worldY, worldZ, stride, onLoad)); + chunkExecutorService.submit(new ServerBlockChunkGenerationThread(this.macroData, chunkDiskMap, chunkCache, worldX, worldY, worldZ, stride, onLoad)); Globals.profiler.endCpuSample(); } @@ -194,4 +191,12 @@ public class ServerBlockManager { chunkExecutorService.shutdownNow(); } + /** + * Sets the macro data for the block manager + * @param macroData The macro data + */ + public void setMacroData(MacroData macroData){ + this.macroData = macroData; + } + }