From a977168ad96c5cc4ad0039c96f40326f6afa6924 Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 29 Apr 2025 16:14:06 -0400 Subject: [PATCH] various work --- docs/src/progress/renderertodo.md | 3 ++ .../client/terrain/cache/ChunkData.java | 27 ++++++---- .../entity/state/life/ClientLifeTree.java | 20 ++++--- .../entity/state/life/ServerLifeTree.java | 24 +++++---- .../electrosphere/server/macro/MacroData.java | 10 ++-- .../server/macro/MacroDataLoader.java | 3 ++ .../generation/heightmap/PlainsGen.java | 2 +- .../terrain/manager/ServerChunkCache.java | 4 +- .../electrosphere/server/saves/SaveUtils.java | 5 +- .../electrosphere/util/math/HashUtils.java | 54 ++++++++++++++++++- .../util/math/HashUtilsTests.java | 28 ++++++++++ 11 files changed, 142 insertions(+), 38 deletions(-) create mode 100644 src/test/java/electrosphere/util/math/HashUtilsTests.java diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 65687f63..608ff056 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1589,6 +1589,9 @@ 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 +Fix life trees creating state items when trees undefined in data +Unhashing ivec hashed keys in chunk caches +Unit tests for unhash func diff --git a/src/main/java/electrosphere/client/terrain/cache/ChunkData.java b/src/main/java/electrosphere/client/terrain/cache/ChunkData.java index 89031960..74373287 100644 --- a/src/main/java/electrosphere/client/terrain/cache/ChunkData.java +++ b/src/main/java/electrosphere/client/terrain/cache/ChunkData.java @@ -6,6 +6,7 @@ import java.util.Set; import org.joml.Vector3i; import electrosphere.server.physics.terrain.manager.ServerTerrainChunk; +import electrosphere.util.math.HashUtils; /** * A container of data about a chunk of terrain @@ -42,9 +43,11 @@ public class ChunkData { */ float[][][] voxelWeight; - //the list of positions modified since the last call to resetModifiedPositions - //Used in DrawCell to keep track of which positions to invalidate - Set modifiedSinceLastGeneration = new HashSet(); + /** + * the list of positions modified since the last call to resetModifiedPositions + * Used in DrawCell to keep track of which positions to invalidate + */ + Set modifiedSinceLastGeneration = new HashSet(); /** * The word x coordinate @@ -107,7 +110,7 @@ public class ChunkData { for(int y = 0; y < CHUNK_DATA_SIZE; y++){ for(int z = 0; z < CHUNK_DATA_SIZE; z++){ if(voxelType[x][y][z] != this.voxelType[x][y][z]){ - String key = getVoxelPositionKey(new Vector3i(x,y,z)); + Long key = getVoxelPositionKey(new Vector3i(x,y,z)); if(!modifiedSinceLastGeneration.contains(key)){ modifiedSinceLastGeneration.add(key); } @@ -139,7 +142,7 @@ public class ChunkData { for(int y = 0; y < CHUNK_DATA_SIZE; y++){ for(int z = 0; z < CHUNK_DATA_SIZE; z++){ if(voxelWeight[x][y][z] != this.voxelWeight[x][y][z]){ - String key = getVoxelPositionKey(new Vector3i(x,y,z)); + Long key = getVoxelPositionKey(new Vector3i(x,y,z)); if(!modifiedSinceLastGeneration.contains(key)){ modifiedSinceLastGeneration.add(key); } @@ -164,7 +167,7 @@ public class ChunkData { voxelWeight[localX][localY][localZ] = weight; voxelType[localX][localY][localZ] = type; //store as modified in cache - String key = this.getVoxelPositionKey(new Vector3i(localX,localY,localZ)); + Long key = this.getVoxelPositionKey(new Vector3i(localX,localY,localZ)); if(!modifiedSinceLastGeneration.contains(key)){ modifiedSinceLastGeneration.add(key); } @@ -226,9 +229,11 @@ public class ChunkData { */ public Set getModifiedPositions(){ Set rVal = new HashSet(); - for(String key : modifiedSinceLastGeneration){ - String[] split = key.split("_"); - rVal.add(new Vector3i(Integer.parseInt(split[0]),Integer.parseInt(split[1]),Integer.parseInt(split[2]))); + for(Long key : modifiedSinceLastGeneration){ + int x = HashUtils.unhashIVec(key, HashUtils.UNHASH_COMPONENT_X); + int y = HashUtils.unhashIVec(key, HashUtils.UNHASH_COMPONENT_Y); + int z = HashUtils.unhashIVec(key, HashUtils.UNHASH_COMPONENT_Z); + rVal.add(new Vector3i(x,y,z)); } return rVal; } @@ -238,8 +243,8 @@ public class ChunkData { * @param position The voxel position * @return The key */ - private String getVoxelPositionKey(Vector3i position){ - return position.x + "_" + position.y + "_" + position.z; + private Long getVoxelPositionKey(Vector3i position){ + return HashUtils.hashIVec(worldX, worldY, worldZ); } /** diff --git a/src/main/java/electrosphere/entity/state/life/ClientLifeTree.java b/src/main/java/electrosphere/entity/state/life/ClientLifeTree.java index 3c672995..5c09859c 100644 --- a/src/main/java/electrosphere/entity/state/life/ClientLifeTree.java +++ b/src/main/java/electrosphere/entity/state/life/ClientLifeTree.java @@ -113,13 +113,19 @@ public class ClientLifeTree implements BehaviorTree { public ClientLifeTree(Entity parent, Object ... params){ this.parent = parent; this.healthSystem = (HealthSystem)params[0]; - stateTransitionUtil = StateTransitionUtil.create(parent, false, new StateTransitionUtilItem[]{ - StateTransitionUtilItem.create( - LifeStateEnum.DYING, - healthSystem.getDyingState(), - () -> {} - ) - }); + StateTransitionUtilItem[] stateData = null; + if(this.healthSystem.getDyingState() != null){ + stateData = new StateTransitionUtilItem[]{ + StateTransitionUtilItem.create( + LifeStateEnum.DYING, + this.healthSystem.getDyingState(), + () -> {} + ) + }; + } else { + stateData = new StateTransitionUtilItem[0]; + } + this.stateTransitionUtil = StateTransitionUtil.create(parent, false, stateData); } /** diff --git a/src/main/java/electrosphere/entity/state/life/ServerLifeTree.java b/src/main/java/electrosphere/entity/state/life/ServerLifeTree.java index ec16ef3d..bf472f87 100644 --- a/src/main/java/electrosphere/entity/state/life/ServerLifeTree.java +++ b/src/main/java/electrosphere/entity/state/life/ServerLifeTree.java @@ -313,15 +313,21 @@ public class ServerLifeTree implements BehaviorTree { this.lifeCurrent = this.lifeMax; this.iFrameMaxCount = this.healthSystem.getOnDamageIFrames(); this.iFrameCurrent = 0; - this.stateTransitionUtil = StateTransitionUtil.create(parent, true, new StateTransitionUtilItem[]{ - StateTransitionUtilItem.create( - LifeStateEnum.DYING, - this.healthSystem.getDyingState(), - () -> { - this.setState(LifeStateEnum.DEAD); - } - ) - }); + StateTransitionUtilItem[] stateData = null; + if(this.healthSystem.getDyingState() != null){ + stateData = new StateTransitionUtilItem[]{ + StateTransitionUtilItem.create( + LifeStateEnum.DYING, + this.healthSystem.getDyingState(), + () -> { + this.setState(LifeStateEnum.DEAD); + } + ) + }; + } else { + stateData = new StateTransitionUtilItem[0]; + } + this.stateTransitionUtil = StateTransitionUtil.create(parent, true, stateData); } /** diff --git a/src/main/java/electrosphere/server/macro/MacroData.java b/src/main/java/electrosphere/server/macro/MacroData.java index 1e934929..4e491916 100644 --- a/src/main/java/electrosphere/server/macro/MacroData.java +++ b/src/main/java/electrosphere/server/macro/MacroData.java @@ -74,7 +74,7 @@ public class MacroData { * @param seed The seed for the world * @return The world */ - public static MacroData generateWorld(long seed){ + public static MacroData generateWorld(long seed, ServerWorldData serverWorldData){ Random random = new Random(seed); MacroData rVal = new MacroData(); @@ -110,10 +110,10 @@ public class MacroData { rVal.characters.add(testChar); //add a test character - Structure struct = Structure.createStructure( - Globals.gameConfigCurrent.getStructureData().getType("test1"), - new Vector3d(ServerWorldData.convertChunkToRealSpace(new Vector3i(32774, 1, 32770)).sub(0,10,0)) - ); + Vector3d structPos = ServerWorldData.convertChunkToRealSpace(new Vector3i(32774, 0, 32770)); + double elevationAtStruct = serverWorldData.getServerTerrainManager().getElevation(32774, 32770, 0, 0); + structPos.y = elevationAtStruct; + Structure struct = Structure.createStructure(Globals.gameConfigCurrent.getStructureData().getType("test1"),structPos); rVal.structures.add(struct); //spawn initial characters in each race diff --git a/src/main/java/electrosphere/server/macro/MacroDataLoader.java b/src/main/java/electrosphere/server/macro/MacroDataLoader.java index 0704af83..2583abfc 100644 --- a/src/main/java/electrosphere/server/macro/MacroDataLoader.java +++ b/src/main/java/electrosphere/server/macro/MacroDataLoader.java @@ -17,7 +17,9 @@ public class MacroDataLoader { * @return The macro data */ 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()); @@ -30,6 +32,7 @@ public class MacroDataLoader { } structure.setFab(fab); } + return rVal; } diff --git a/src/main/java/electrosphere/server/physics/terrain/generation/heightmap/PlainsGen.java b/src/main/java/electrosphere/server/physics/terrain/generation/heightmap/PlainsGen.java index 08385751..50b10e21 100644 --- a/src/main/java/electrosphere/server/physics/terrain/generation/heightmap/PlainsGen.java +++ b/src/main/java/electrosphere/server/physics/terrain/generation/heightmap/PlainsGen.java @@ -42,7 +42,7 @@ public class PlainsGen implements HeightmapGenerator { * @return The height */ public float getHeight(long SEED, double x, double y){ - return sampleAllNoise(SEED, x, y) + HEIGHT_OFFSET; + return PlainsGen.sampleAllNoise(SEED, x, y) + HEIGHT_OFFSET; } diff --git a/src/main/java/electrosphere/server/physics/terrain/manager/ServerChunkCache.java b/src/main/java/electrosphere/server/physics/terrain/manager/ServerChunkCache.java index fb5f9fc3..32aa9293 100644 --- a/src/main/java/electrosphere/server/physics/terrain/manager/ServerChunkCache.java +++ b/src/main/java/electrosphere/server/physics/terrain/manager/ServerChunkCache.java @@ -6,10 +6,10 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.Semaphore; import electrosphere.server.physics.terrain.diskmap.ChunkDiskMap; +import electrosphere.util.math.HashUtils; /** * Caches chunk data on the server @@ -177,7 +177,7 @@ public class ServerChunkCache { * @return The key */ public long getKey(int worldX, int worldY, int worldZ){ - return Objects.hash(worldX, worldY, worldZ); + return HashUtils.hashIVec(worldX, worldY, worldZ); } /** diff --git a/src/main/java/electrosphere/server/saves/SaveUtils.java b/src/main/java/electrosphere/server/saves/SaveUtils.java index a887ae89..42d01da7 100644 --- a/src/main/java/electrosphere/server/saves/SaveUtils.java +++ b/src/main/java/electrosphere/server/saves/SaveUtils.java @@ -124,8 +124,11 @@ public class SaveUtils { ServerFluidManager serverFluidManager = new ServerFluidManager(serverWorldData, serverTerrainManager, 0, new DefaultFluidGenerator()); serverFluidManager.save(saveName); + //attach terrain to world data prior to pushing into macro data + serverWorldData.setManagers(serverTerrainManager, serverFluidManager, null); + //macro data - MacroData macroData = MacroData.generateWorld(sceneFile.getSeed()); + MacroData macroData = MacroData.generateWorld(sceneFile.getSeed(), serverWorldData); macroData.save(saveName); } else { //just save to disk diff --git a/src/main/java/electrosphere/util/math/HashUtils.java b/src/main/java/electrosphere/util/math/HashUtils.java index 4836cb9b..29b5dd61 100644 --- a/src/main/java/electrosphere/util/math/HashUtils.java +++ b/src/main/java/electrosphere/util/math/HashUtils.java @@ -5,8 +5,35 @@ package electrosphere.util.math; */ public class HashUtils { - private static final int SHIFT_Y = 16; // 16 bits per value - private static final int SHIFT_Z = 32; // 2 * 16 bits + /** + * Bits to shift the y value by + */ + private static final int SHIFT_Y = 16; + + /** + * Bits to shift the z value by + */ + private static final int SHIFT_Z = 32; + + /** + * Mask used when unhashing components + */ + private static final long UNHASH_MASK = 0xFFFFL; + + /** + * Unhashes the x component + */ + public static final int UNHASH_COMPONENT_X = 0; + + /** + * Unhashes the y component + */ + public static final int UNHASH_COMPONENT_Y = 1; + + /** + * Unhashes the z component + */ + public static final int UNHASH_COMPONENT_Z = 2; /** * Hashes an integer vector. Must be within the range [0,65536] @@ -22,4 +49,27 @@ public class HashUtils { return ((long) x) | ((long) y << SHIFT_Y) | ((long) z << SHIFT_Z); } + /** + * Unhashes an ivec-hashed value + * @param hash The hash + * @param component The component (x, y, or z) to pull from the hash (x=0, y=1, z=2) + * @return The value + */ + public static int unhashIVec(long hash, int component){ + switch(component){ + case 0: { + return (int)(hash & UNHASH_MASK); + } + case 1: { + return (int)(hash >> SHIFT_Y & UNHASH_MASK); + } + case 2: { + return (int)(hash >> SHIFT_Z & UNHASH_MASK); + } + default: { + throw new Error("Provided undefined component! " + component); + } + } + } + } diff --git a/src/test/java/electrosphere/util/math/HashUtilsTests.java b/src/test/java/electrosphere/util/math/HashUtilsTests.java new file mode 100644 index 00000000..8785b341 --- /dev/null +++ b/src/test/java/electrosphere/util/math/HashUtilsTests.java @@ -0,0 +1,28 @@ +package electrosphere.util.math; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import electrosphere.test.annotations.UnitTest; + +/** + * Tests for hash utils + */ +public class HashUtilsTests { + + @UnitTest + public void test_unhashIVec(){ + int x = 1; + int y = 2; + int z = 3; + long hash = HashUtils.hashIVec(x, y, z); + + int x_r = HashUtils.unhashIVec(hash, HashUtils.UNHASH_COMPONENT_X); + int y_r = HashUtils.unhashIVec(hash, HashUtils.UNHASH_COMPONENT_Y); + int z_r = HashUtils.unhashIVec(hash, HashUtils.UNHASH_COMPONENT_Z); + + assertEquals(x, x_r); + assertEquals(y, y_r); + assertEquals(z, z_r); + } + +}