From bfd7690403b664985d512373d6cd6256a3552291 Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 19 Mar 2024 20:03:21 -0400 Subject: [PATCH] fix fluid chunk model creating and flickering --- assets/Shaders/shaderlib/shaderlib.glsl | 5 + buildNumber.properties | 4 +- .../highlevel-design/locations/biomeideas.md | 16 ++++ .../locations/largelocationideas.md | 5 + docs/src/progress/renderertodo.md | 11 +++ net/terrain.json | 9 ++ .../client/fluid/cache/FluidChunkData.java | 59 +----------- .../client/fluid/cells/FluidCell.java | 4 +- .../fluid/manager/ClientFluidManager.java | 94 ++++++++++++------- .../engine/loadingthreads/ArenaLoading.java | 2 +- .../entity/types/fluid/FluidChunk.java | 1 + .../net/client/protocol/TerrainProtocol.java | 3 + .../parser/net/message/NetworkMessage.java | 5 + .../parser/net/message/TerrainMessage.java | 77 +++++++++++++++ .../net/parser/net/message/TypeBytes.java | 1 + .../net/server/protocol/TerrainProtocol.java | 90 ++++++++++-------- .../meshgen/FluidChunkModelGeneration.java | 4 +- .../datacell/GriddedDataCellManager.java | 25 ++++- .../fluid/generation/ArenaFluidGenerator.java | 6 +- .../fluid/manager/ServerFluidManager.java | 27 +++++- .../fluid/simulator/ServerFluidSimulator.java | 22 +++++ .../FluidCellularAutomataSimulator.java | 71 ++++++++++++++ .../generation/ArenaChunkGenerator.java | 14 ++- 23 files changed, 405 insertions(+), 150 deletions(-) create mode 100644 assets/Shaders/shaderlib/shaderlib.glsl create mode 100644 src/main/java/electrosphere/server/fluid/simulator/ServerFluidSimulator.java create mode 100644 src/main/java/electrosphere/server/fluid/simulator/cellularautomata/FluidCellularAutomataSimulator.java diff --git a/assets/Shaders/shaderlib/shaderlib.glsl b/assets/Shaders/shaderlib/shaderlib.glsl new file mode 100644 index 00000000..28fa669e --- /dev/null +++ b/assets/Shaders/shaderlib/shaderlib.glsl @@ -0,0 +1,5 @@ + +//courtesy https://stackoverflow.com/a/4275343 +float rand(vec2 co){ + return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); +} \ No newline at end of file diff --git a/buildNumber.properties b/buildNumber.properties index 65462928..1f39ff5f 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Sun Mar 17 09:54:23 EDT 2024 -buildNumber=75 +#Tue Mar 19 19:54:15 EDT 2024 +buildNumber=77 diff --git a/docs/src/highlevel-design/locations/biomeideas.md b/docs/src/highlevel-design/locations/biomeideas.md index 7dd744d4..88f6ee78 100644 --- a/docs/src/highlevel-design/locations/biomeideas.md +++ b/docs/src/highlevel-design/locations/biomeideas.md @@ -41,6 +41,8 @@ ## Ruins +## Light Valley +Valley with lights shooting around the sky from projectors placed sporadically around the valley, tie into lore and provide some kind of function (portal controller or something?) @@ -287,3 +289,17 @@ Plains with lots of fire ## Thunder Planes Constant Thunderstorms and lots of lightning strikes + + + + + + + + + +# Sky Specific + +## Sky Reefs +Full of kelp dangling from island, lots of sea-themed flying creatures + diff --git a/docs/src/highlevel-design/locations/largelocationideas.md b/docs/src/highlevel-design/locations/largelocationideas.md index aea94c25..a7efef0a 100644 --- a/docs/src/highlevel-design/locations/largelocationideas.md +++ b/docs/src/highlevel-design/locations/largelocationideas.md @@ -5,3 +5,8 @@ ### "Black Hole" Zone Zone with a big magical fire in the center or something like that. IE it's a huge pit and the zone you play in is around this pit. + +### Solitairy Giant +Enclosed zone centered around a silent giant performing some toil (walking in circles around the periphery? Moving boulders? idk) +Ambient audio effect of him doing work the whole time + diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 03cad141..64918f81 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -181,6 +181,17 @@ Fix Character creation preview not working Clean up main method/class +Shader library system + - Abiltiy to include the shader library in individual files (ie implement #include) + +Level loading/saving + Basic Editor + - Spin up voxel level (think arena mode) + - Save voxel level + - Basic editor functionality + - Menu of types of entities to spawn + - Button to spawn them at cursor + + Transvoxel Algorithm Client Terrain Entity Management (specifically creation/teardown for client) - Also queries for far out chunks to load far away terrain diff --git a/net/terrain.json b/net/terrain.json index 0c34fab0..36dce603 100644 --- a/net/terrain.json +++ b/net/terrain.json @@ -201,6 +201,15 @@ "worldZ", "chunkData" ] + }, + { + "messageName" : "updateFluidData", + "data" : [ + "worldX", + "worldY", + "worldZ", + "chunkData" + ] } ] } diff --git a/src/main/java/electrosphere/client/fluid/cache/FluidChunkData.java b/src/main/java/electrosphere/client/fluid/cache/FluidChunkData.java index 195016be..4d4eccdc 100644 --- a/src/main/java/electrosphere/client/fluid/cache/FluidChunkData.java +++ b/src/main/java/electrosphere/client/fluid/cache/FluidChunkData.java @@ -25,10 +25,6 @@ public class FluidChunkData { float[][][] velocityY; float[][][] velocityZ; - //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(); - /** * Gets the voxel weight array in this container @@ -43,43 +39,10 @@ public class FluidChunkData { * @param voxelWeight The voxel weight array */ public void setVoxelWeight(float[][][] voxelWeight){ - //mark changed cells - if(this.voxelWeight != null){ - for(int x = 0; x < CHUNK_SIZE; x++){ - for(int y = 0; y < CHUNK_SIZE; y++){ - for(int z = 0; z < CHUNK_SIZE; z++){ - if(voxelWeight[x][y][z] != this.voxelWeight[x][y][z]){ - String key = getVoxelPositionKey(new Vector3i(x,y,z)); - if(!modifiedSinceLastGeneration.contains(key)){ - modifiedSinceLastGeneration.add(key); - } - } - } - } - } - } //update data this.voxelWeight = voxelWeight; } - // /** - // * Updates the value of a single voxel in the chunk - // * @param localX The local position X - // * @param localY The local position Y - // * @param localZ The local position Z - // * @param weight The weight to set it to - // * @param type The type to set the voxel to - // */ - // public void updatePosition(int localX, int localY, int localZ, float weight, int type){ - // voxelWeight[localX][localY][localZ] = weight; - // voxelType[localX][localY][localZ] = type; - // //store as modified in cache - // String key = getVoxelPositionKey(new Vector3i(localX,localY,localZ)); - // if(!modifiedSinceLastGeneration.contains(key)){ - // modifiedSinceLastGeneration.add(key); - // } - // } - /** * Gets the weight of a voxel at a poisiton * @param localPosition The local position @@ -94,27 +57,7 @@ public class FluidChunkData { * @return The weight of the specified voxel */ public float getWeight(int x, int y, int z){ - return voxelWeight[z][y][z]; - } - - /** - * Resets the cache of modified positions - */ - public void resetModifiedPositions(){ - this.modifiedSinceLastGeneration.clear(); - } - - /** - * Gets the set of all modified positions since the last call to resetModifiedPositions - * @return The set of all modified positions - */ - 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]))); - } - return rVal; + return voxelWeight[x][y][z]; } /** diff --git a/src/main/java/electrosphere/client/fluid/cells/FluidCell.java b/src/main/java/electrosphere/client/fluid/cells/FluidCell.java index 049fe552..6d8fc8d5 100644 --- a/src/main/java/electrosphere/client/fluid/cells/FluidCell.java +++ b/src/main/java/electrosphere/client/fluid/cells/FluidCell.java @@ -65,7 +65,7 @@ public class FluidCell { rVal.data = data; return rVal; } - + /** * Generates a drawable entity based on this chunk */ @@ -76,7 +76,7 @@ public class FluidCell { fillInData(); - modelEntity = FluidChunk.clientCreateFluidChunkEntity(data.getVoxelWeight()); + modelEntity = FluidChunk.clientCreateFluidChunkEntity(weights); ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos()); } diff --git a/src/main/java/electrosphere/client/fluid/manager/ClientFluidManager.java b/src/main/java/electrosphere/client/fluid/manager/ClientFluidManager.java index b8952cfd..91eb6671 100644 --- a/src/main/java/electrosphere/client/fluid/manager/ClientFluidManager.java +++ b/src/main/java/electrosphere/client/fluid/manager/ClientFluidManager.java @@ -58,47 +58,24 @@ public class ClientFluidManager { messageQueue.remove(message); switch(message.getMessageSubtype()){ case SENDFLUIDDATA: { - float[][][] weights = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE]; - float[][][] velocityX = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE]; - float[][][] velocityY = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE]; - float[][][] velocityZ = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE]; ByteBuffer buffer = ByteBuffer.wrap(message.getchunkData()); - FloatBuffer floatBuffer = buffer.asFloatBuffer(); - for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){ - for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){ - for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){ - weights[x][y][z] = floatBuffer.get(); - } - } - } - for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){ - for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){ - for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){ - velocityX[x][y][z] = floatBuffer.get(); - } - } - } - for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){ - for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){ - for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){ - velocityY[x][y][z] = floatBuffer.get(); - } - } - } - for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){ - for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){ - for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){ - velocityZ[x][y][z] = floatBuffer.get(); - } - } - } - FluidChunkData data = new FluidChunkData(); - data.setVoxelWeight(weights); + FluidChunkData data = parseFluidDataBuffer(buffer); fluidCache.addChunkDataToCache( message.getworldX(), message.getworldY(), message.getworldZ(), data ); } break; + case UPDATEFLUIDDATA: { + ByteBuffer buffer = ByteBuffer.wrap(message.getchunkData()); + FluidChunkData data = parseFluidDataBuffer(buffer); + fluidCache.addChunkDataToCache( + message.getworldX(), message.getworldY(), message.getworldZ(), + data + ); + if(Globals.fluidCellManager != null){ + Globals.fluidCellManager.markUpdateable(message.getworldX(), message.getworldY(), message.getworldZ()); + } + } break; default: LoggerInterface.loggerEngine.WARNING("ClientFluidManager: unhandled network message of type" + message.getMessageSubtype()); break; @@ -172,5 +149,52 @@ public class ClientFluidManager { } fluidChunkGenerationQueue.clear(); } + + /** + * Parses a byte buffer into a fluid data object + * @param buffer the buffer + * @return the object + */ + private FluidChunkData parseFluidDataBuffer(ByteBuffer buffer){ + float[][][] weights = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE]; + float[][][] velocityX = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE]; + float[][][] velocityY = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE]; + float[][][] velocityZ = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE]; + FloatBuffer floatBuffer = buffer.asFloatBuffer(); + for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){ + for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){ + for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){ + weights[x][y][z] = floatBuffer.get(); + if(weights[x][y][z] <= 0){ + weights[x][y][z] = -1f; + } + } + } + } + for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){ + for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){ + for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){ + velocityX[x][y][z] = floatBuffer.get(); + } + } + } + for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){ + for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){ + for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){ + velocityY[x][y][z] = floatBuffer.get(); + } + } + } + for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){ + for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){ + for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){ + velocityZ[x][y][z] = floatBuffer.get(); + } + } + } + FluidChunkData data = new FluidChunkData(); + data.setVoxelWeight(weights); + return data; + } } diff --git a/src/main/java/electrosphere/engine/loadingthreads/ArenaLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ArenaLoading.java index 6a5c80c6..6ca36be3 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ArenaLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ArenaLoading.java @@ -57,7 +57,7 @@ public class ArenaLoading { private static void initServerArenaTerrainManager(){ Globals.serverTerrainManager = ServerTerrainManager.constructArenaTerrainManager(); - Globals.serverFluidManager = ServerFluidManager.constructArenaFluidManager(); + Globals.serverFluidManager = ServerFluidManager.constructArenaFluidManager(Globals.serverTerrainManager); } private static void initServerArenaWorldData(){ diff --git a/src/main/java/electrosphere/entity/types/fluid/FluidChunk.java b/src/main/java/electrosphere/entity/types/fluid/FluidChunk.java index 7f63bccb..f7d72a90 100644 --- a/src/main/java/electrosphere/entity/types/fluid/FluidChunk.java +++ b/src/main/java/electrosphere/entity/types/fluid/FluidChunk.java @@ -30,6 +30,7 @@ public class FluidChunk { rVal.putData(EntityDataStrings.FLUID_IS_FLUID, true); rVal.putData(EntityDataStrings.DRAW_TRANSPARENT_PASS, true); + rVal.removeData(EntityDataStrings.DRAW_SOLID_PASS); return rVal; } diff --git a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java index bf973373..361ed53f 100644 --- a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java @@ -53,6 +53,9 @@ public class TerrainProtocol { case SENDFLUIDDATA: { Globals.clientFluidManager.attachFluidMessage(message); } break; + case UPDATEFLUIDDATA: { + Globals.clientFluidManager.attachFluidMessage(message); + } break; default: LoggerInterface.loggerNetworking.WARNING("Client networking: Unhandled message of type: " + message.getMessageSubtype()); break; diff --git a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java index d4c06a5d..3e2886ae 100644 --- a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java @@ -246,6 +246,11 @@ SYNCHRONIZATION_MESSAGE, rVal = TerrainMessage.parsesendFluidDataMessage(byteBuffer); } break; + case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA: + if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ + rVal = TerrainMessage.parseupdateFluidDataMessage(byteBuffer); + } + break; } break; case TypeBytes.MESSAGE_TYPE_SERVER: diff --git a/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java b/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java index 5f2a0e4e..7399df5e 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java @@ -19,6 +19,7 @@ public class TerrainMessage extends NetworkMessage { SENDCHUNKDATA, REQUESTFLUIDDATA, SENDFLUIDDATA, + UPDATEFLUIDDATA, } TerrainMessageType messageType; @@ -276,6 +277,8 @@ public class TerrainMessage extends NetworkMessage { } case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA: return TerrainMessage.canParsesendFluidDataMessage(byteBuffer); + case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA: + return TerrainMessage.canParseupdateFluidDataMessage(byteBuffer); } return false; } @@ -564,6 +567,54 @@ public class TerrainMessage extends NetworkMessage { return rVal; } + public static boolean canParseupdateFluidDataMessage(CircularByteBuffer byteBuffer){ + int currentStreamLength = byteBuffer.getRemaining(); + List temporaryByteQueue = new LinkedList(); + if(currentStreamLength < 6){ + return false; + } + if(currentStreamLength < 10){ + return false; + } + if(currentStreamLength < 14){ + return false; + } + int chunkDataSize = 0; + if(currentStreamLength < 18){ + return false; + } else { + temporaryByteQueue.add(byteBuffer.peek(14 + 0)); + temporaryByteQueue.add(byteBuffer.peek(14 + 1)); + temporaryByteQueue.add(byteBuffer.peek(14 + 2)); + temporaryByteQueue.add(byteBuffer.peek(14 + 3)); + chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue); + } + if(currentStreamLength < 18 + chunkDataSize){ + return false; + } + return true; + } + + public static TerrainMessage parseupdateFluidDataMessage(CircularByteBuffer byteBuffer){ + TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA); + stripPacketHeader(byteBuffer); + rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer)); + return rVal; + } + + public static TerrainMessage constructupdateFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){ + TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA); + rVal.setworldX(worldX); + rVal.setworldY(worldY); + rVal.setworldZ(worldZ); + rVal.setchunkData(chunkData); + rVal.serialize(); + return rVal; + } + @Override void serialize(){ byte[] intValues = new byte[8]; @@ -839,6 +890,32 @@ public class TerrainMessage extends NetworkMessage { rawBytes[18+i] = chunkData[i]; } break; + case UPDATEFLUIDDATA: + rawBytes = new byte[2+4+4+4+4+chunkData.length]; + //message header + rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN; + //entity messaage header + rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA; + intValues = ByteStreamUtils.serializeIntToBytes(worldX); + for(int i = 0; i < 4; i++){ + rawBytes[2+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(worldY); + for(int i = 0; i < 4; i++){ + rawBytes[6+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(worldZ); + for(int i = 0; i < 4; i++){ + rawBytes[10+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length); + for(int i = 0; i < 4; i++){ + rawBytes[14+i] = intValues[i]; + } + for(int i = 0; i < chunkData.length; i++){ + rawBytes[18+i] = chunkData[i]; + } + break; } serialized = true; } diff --git a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java index 9dfacff9..fae2cb87 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java +++ b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java @@ -90,6 +90,7 @@ Message categories public static final byte TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA = 8; public static final byte TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA = 9; public static final byte TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA = 10; + public static final byte TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA = 11; /* Terrain packet sizes */ diff --git a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java index e3ac1433..a20ab71b 100644 --- a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java @@ -45,6 +45,7 @@ public class TerrainProtocol { ); } break; //all ignored message types + case UPDATEFLUIDDATA: case RESPONSEMETADATA: case SPAWNPOSITION: case UPDATEVOXEL: @@ -207,11 +208,55 @@ public class TerrainProtocol { // System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY()); ServerFluidChunk chunk = Globals.serverFluidManager.getChunk(worldX, worldY, worldZ); - - // float[][] macroValues = chunk.getMacroValues();//Globals.serverTerrainManager.getRad5MacroValues(message.getworldX(), message.getworldY()); - // long[][] randomizer = chunk.getRandomizer();//Globals.serverTerrainManager.getRandomizer(message.getworldX(), message.getworldY()); + + ByteBuffer buffer = constructFluidByteBuffer(chunk); + connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructsendFluidDataMessage(worldX, worldY, worldZ, buffer.array())); + + } + + static void sendWorldMetadata(ServerConnectionHandler connectionHandler){ + //world metadata + connectionHandler.addMessagetoOutgoingQueue( + TerrainMessage.constructResponseMetadataMessage( + Globals.serverWorldData.getWorldSizeDiscrete(), + Globals.serverWorldData.getDynamicInterpolationRatio(), + Globals.serverWorldData.getRandomDampener(), + (int)Globals.serverWorldData.getWorldBoundMin().x, + (int)Globals.serverWorldData.getWorldBoundMin().z, + (int)Globals.serverWorldData.getWorldBoundMax().x, + (int)Globals.serverWorldData.getWorldBoundMax().z + ) + ); + } + + /** + * Attempts to perform an edit requested by a client + * @param message The message containing the edit request + */ + static void attemptTerrainEdit(ServerConnectionHandler connectionHandler, TerrainMessage message){ + Player player = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId()); + } + + /** + * Attempts to use an edit palette on the terrain + * @param connectionHandler The connection handler + * @param message The message that contains the request to use an edit palette + */ + static void attemptUseTerrainEditPalette(ServerConnectionHandler connectionHandler, TerrainMessage message){ + Player player = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId()); + Realm realm = Globals.realmManager.getPlayerRealm(player); + Vector3d location = new Vector3d(message.getrealLocationX(), message.getrealLocationY(), message.getrealLocationZ()); + TerrainEditing.editTerrain(realm, location, message.getvalue(), message.getterrainValue(), message.getterrainWeight()); + } + + /** + * Constructs a buffer to send a fluid chunk to the client + * @param chunk The chunk to send + * @return the buffer + */ + public static ByteBuffer constructFluidByteBuffer(ServerFluidChunk chunk){ //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 //chunk data that connects seamlessly to the next chunk. @@ -254,44 +299,7 @@ public class TerrainProtocol { } } - - connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructsendFluidDataMessage(worldX, worldY, worldZ, buffer.array())); - - } - - static void sendWorldMetadata(ServerConnectionHandler connectionHandler){ - //world metadata - connectionHandler.addMessagetoOutgoingQueue( - TerrainMessage.constructResponseMetadataMessage( - Globals.serverWorldData.getWorldSizeDiscrete(), - Globals.serverWorldData.getDynamicInterpolationRatio(), - Globals.serverWorldData.getRandomDampener(), - (int)Globals.serverWorldData.getWorldBoundMin().x, - (int)Globals.serverWorldData.getWorldBoundMin().z, - (int)Globals.serverWorldData.getWorldBoundMax().x, - (int)Globals.serverWorldData.getWorldBoundMax().z - ) - ); - } - - /** - * Attempts to perform an edit requested by a client - * @param message The message containing the edit request - */ - static void attemptTerrainEdit(ServerConnectionHandler connectionHandler, TerrainMessage message){ - Player player = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId()); - } - - /** - * Attempts to use an edit palette on the terrain - * @param connectionHandler The connection handler - * @param message The message that contains the request to use an edit palette - */ - static void attemptUseTerrainEditPalette(ServerConnectionHandler connectionHandler, TerrainMessage message){ - Player player = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId()); - Realm realm = Globals.realmManager.getPlayerRealm(player); - Vector3d location = new Vector3d(message.getrealLocationX(), message.getrealLocationY(), message.getrealLocationZ()); - TerrainEditing.editTerrain(realm, location, message.getvalue(), message.getterrainValue(), message.getterrainWeight()); + return buffer; } } diff --git a/src/main/java/electrosphere/renderer/meshgen/FluidChunkModelGeneration.java b/src/main/java/electrosphere/renderer/meshgen/FluidChunkModelGeneration.java index dd19e7ea..09eaca3d 100644 --- a/src/main/java/electrosphere/renderer/meshgen/FluidChunkModelGeneration.java +++ b/src/main/java/electrosphere/renderer/meshgen/FluidChunkModelGeneration.java @@ -588,7 +588,7 @@ public class FluidChunkModelGeneration { List faceElements = new LinkedList(); //List of UVs List UVs = new LinkedList(); - + for(int x = 0; x < weightGrid.length - 1; x++){ @@ -602,7 +602,7 @@ public class FluidChunkModelGeneration { weightGrid[x+0][y+1][z+0], weightGrid[x+0][y+1][z+1], weightGrid[x+1][y+1][z+1], weightGrid[x+1][y+1][z+0] ); //polygonize the current gridcell - polygonize(currentCell, 0, triangles, vertMap, verts, normals, trianglesSharingVert); + polygonize(currentCell, 0.0f, triangles, vertMap, verts, normals, trianglesSharingVert); } } } diff --git a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java index 6b713686..12a428eb 100644 --- a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java +++ b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java @@ -17,6 +17,7 @@ import electrosphere.game.server.world.ServerWorldData; import electrosphere.logger.LoggerInterface; import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.server.player.Player; +import electrosphere.net.server.protocol.TerrainProtocol; import electrosphere.server.content.ServerContentManager; import electrosphere.server.datacell.interfaces.DataCellManager; import electrosphere.server.datacell.interfaces.VoxelCellManager; @@ -353,9 +354,14 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager loadedCellsLock.acquireUninterruptibly(); for(ServerDataCell cell : loadedCells){ Globals.microSimulation.simulate(cell, parent.getHitboxManager()); + + //simulate fluid + Vector3i cellPos = this.getCellWorldPosition(cell); + boolean update = this.serverFluidManager.simulate(cellPos.x,cellPos.y,cellPos.z); + if(update){ + rebroadcastFluidChunk(cellPos); + } } - //simulate fluid - this.serverFluidManager.simulate(); loadedCellsLock.release(); updatePlayerPositions(); } @@ -482,8 +488,23 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager } @Override + /** + * Gets the fluid chunk at a given position + */ public ServerFluidChunk getFluidChunkAtPosition(Vector3i worldPosition) { return serverFluidManager.getChunk(worldPosition.x, worldPosition.y, worldPosition.z); } + /** + * Rebroadcasts the fluid cell at a given position + * @param worldPosition the world position + */ + private void rebroadcastFluidChunk(Vector3i worldPosition){ + ServerDataCell cell = getCellAtWorldPosition(worldPosition); + ServerFluidChunk chunk = getFluidChunkAtPosition(worldPosition); + cell.broadcastNetworkMessage( + TerrainMessage.constructupdateFluidDataMessage(worldPosition.x, worldPosition.y, worldPosition.z, TerrainProtocol.constructFluidByteBuffer(chunk).array()) + ); + } + } diff --git a/src/main/java/electrosphere/server/fluid/generation/ArenaFluidGenerator.java b/src/main/java/electrosphere/server/fluid/generation/ArenaFluidGenerator.java index 384c25ef..7651db00 100644 --- a/src/main/java/electrosphere/server/fluid/generation/ArenaFluidGenerator.java +++ b/src/main/java/electrosphere/server/fluid/generation/ArenaFluidGenerator.java @@ -17,12 +17,14 @@ public class ArenaFluidGenerator implements FluidGenerator { for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ - weights[x][y][z] = -1.0f; + weights[x][y][z] = 0f; } } } - weights[3][3][3] = 0.8f; + if(worldX == 0 && worldY == 0 && worldZ == 0){ + weights[7][5][7] = 1f; + } return chunk; } diff --git a/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java b/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java index 9fc60742..0e3a3a74 100644 --- a/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java +++ b/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java @@ -6,6 +6,10 @@ import electrosphere.server.fluid.diskmap.FluidDiskMap; import electrosphere.server.fluid.generation.ArenaFluidGenerator; import electrosphere.server.fluid.generation.FluidGenerator; import electrosphere.server.fluid.models.FluidModel; +import electrosphere.server.fluid.simulator.ServerFluidSimulator; +import electrosphere.server.fluid.simulator.cellularautomata.FluidCellularAutomataSimulator; +import electrosphere.server.terrain.manager.ServerTerrainChunk; +import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.util.FileUtils; import java.nio.ByteBuffer; import java.nio.FloatBuffer; @@ -48,18 +52,26 @@ public class ServerFluidManager { //The generation algorithm for this fluid manager FluidGenerator chunkGenerator; + + //the fluid simulator + ServerFluidSimulator serverFluidSimulator; + + //the terrain manager associated + ServerTerrainManager serverTerrainManager; /** * Constructor */ public ServerFluidManager( + ServerTerrainManager serverTerrainManager, int worldSizeDiscrete, int verticalInterpolationRatio, float interpolationRandomDampener, long seed, FluidGenerator chunkGenerator ){ + this.serverTerrainManager = serverTerrainManager; this.worldSizeDiscrete = worldSizeDiscrete; this.verticalInterpolationRatio = verticalInterpolationRatio; this.chunkCache = new ConcurrentHashMap(); @@ -77,14 +89,16 @@ public class ServerFluidManager { * Constructs an arena fluid manager * @return The arena fluid manager */ - public static ServerFluidManager constructArenaFluidManager(){ + public static ServerFluidManager constructArenaFluidManager(ServerTerrainManager serverTerrainManager){ ServerFluidManager rVal = new ServerFluidManager(); + rVal.serverTerrainManager = serverTerrainManager; rVal.worldSizeDiscrete = 2; rVal.verticalInterpolationRatio = 0; rVal.chunkCache = new ConcurrentHashMap(); rVal.chunkCacheContents = new CopyOnWriteArrayList(); rVal.interpolationRandomDampener = 0.0f; rVal.chunkGenerator = new ArenaFluidGenerator(); + rVal.serverFluidSimulator = new FluidCellularAutomataSimulator(); return rVal; } @@ -275,10 +289,15 @@ public class ServerFluidManager { } /** - * Simulates all active fluid chunks + * Simulates a chunk */ - public void simulate(){ - //TODO: implement + public boolean simulate(int worldX, int worldY, int worldZ){ + ServerFluidChunk fluidChunk = this.getChunk(worldX, worldY, worldZ); + ServerTerrainChunk terrainChunk = this.serverTerrainManager.getChunk(worldX, worldY, worldZ); + if(fluidChunk != null && terrainChunk != null && this.serverFluidSimulator != null){ + return this.serverFluidSimulator.simulate(fluidChunk,terrainChunk,worldX,worldY,worldZ); + } + return false; } } diff --git a/src/main/java/electrosphere/server/fluid/simulator/ServerFluidSimulator.java b/src/main/java/electrosphere/server/fluid/simulator/ServerFluidSimulator.java new file mode 100644 index 00000000..dfa3e36f --- /dev/null +++ b/src/main/java/electrosphere/server/fluid/simulator/ServerFluidSimulator.java @@ -0,0 +1,22 @@ +package electrosphere.server.fluid.simulator; + +import electrosphere.server.fluid.manager.ServerFluidChunk; +import electrosphere.server.terrain.manager.ServerTerrainChunk; + +/** + * A system capable of simulating a server fluid chunk for a single frame + */ +public interface ServerFluidSimulator { + + /** + * Simulates the chunk for single step + * @param fluidChunk The fluid chunk + * @param terrainChunk The terrain chunk + * @param worldX the world x coordinate + * @param worldY the world y coordinate + * @param worldZ the world z coordinate + * @return True if the server data cell should update all players connected to it with new values, false otherwise + */ + public boolean simulate(ServerFluidChunk fluidChunk, ServerTerrainChunk terrainChunk, int worldX, int worldY, int worldZ); + +} diff --git a/src/main/java/electrosphere/server/fluid/simulator/cellularautomata/FluidCellularAutomataSimulator.java b/src/main/java/electrosphere/server/fluid/simulator/cellularautomata/FluidCellularAutomataSimulator.java new file mode 100644 index 00000000..b3f1a325 --- /dev/null +++ b/src/main/java/electrosphere/server/fluid/simulator/cellularautomata/FluidCellularAutomataSimulator.java @@ -0,0 +1,71 @@ +package electrosphere.server.fluid.simulator.cellularautomata; + +import electrosphere.server.fluid.manager.ServerFluidChunk; +import electrosphere.server.fluid.simulator.ServerFluidSimulator; +import electrosphere.server.terrain.manager.ServerTerrainChunk; + +/** + * Simulates server fluid chunks via cellular automata + */ +public class FluidCellularAutomataSimulator implements ServerFluidSimulator { + + static final float MAX_WEIGHT = 1.0f; + + static final float GRAVITY_DIFF = 0.04f; + + @Override + public boolean simulate(ServerFluidChunk fluidChunk, ServerTerrainChunk terrainChunk, int worldX, int worldY, int worldZ) { + + float[][][] weights = fluidChunk.getWeights(); + float[][][] terrainWeights = terrainChunk.getWeights(); + + //if true, alerts the server data cell to broadcast a new update message to all clients within it + boolean update = false; + + if(worldX == 0 && worldY == 0 && worldZ == 0){ + if(weights[8][1][8] < MAX_WEIGHT){ + weights[8][1][8] = MAX_WEIGHT; + } + } + + for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ + for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ + for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ + if(weights[x][y][z] <= 0){ + continue; + } else { + if(y > 0 && weights[x][y-1][z] < MAX_WEIGHT){ + update = true; + weights[x][y][z] -= GRAVITY_DIFF; + weights[x][y-1][z] += GRAVITY_DIFF; + } else { + //propagate sideways + int[] offsetX = new int[]{-1,1,0,0}; + int[] offsetZ = new int[]{0,0,-1,1}; + for(int i = 0; i < 4; i++){ + int realX = x + offsetX[i]; + int realZ = z + offsetZ[i]; + if(realX > 0 && realX < ServerTerrainChunk.CHUNK_DIMENSION - 1 && + realZ > 0 && realZ < ServerTerrainChunk.CHUNK_DIMENSION - 1 + ){ + if( + weights[realX][y][realZ] < weights[x][y][z] && + terrainWeights[realX][y][realZ] < MAX_WEIGHT + ){ + update = true; + weights[x][y][z] -= GRAVITY_DIFF; + weights[realX][y][realZ] += GRAVITY_DIFF; + } + } + } + } + } + } + } + } + + return update; + } + + +} diff --git a/src/main/java/electrosphere/server/terrain/generation/ArenaChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/ArenaChunkGenerator.java index 21396a99..08a42682 100644 --- a/src/main/java/electrosphere/server/terrain/generation/ArenaChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/ArenaChunkGenerator.java @@ -27,7 +27,19 @@ public class ArenaChunkGenerator implements ChunkGenerator { for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DIMENSION; weightX++){ for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DIMENSION; weightZ++){ weights[weightX][0][weightZ] = 0.1f; - values[weightX][0][weightZ] = 2; + values[weightX][0][weightZ] = 1; + } + } + } + if(worldX == 0 && worldY == 0 && worldZ == 0){ + for(int x = 5; x < 11; x++){ + for(int z = 5; z < 11; z++){ + if((x == 10 || x == 5 || z == 10 || z == 5)){ + weights[x][0][z] = 1.0f; + weights[x][1][z] = 1.0f; + values[x][0][z] = 1; + values[x][1][z] = 1; + } } } }