diff --git a/docs/src/progress/bigthings.md b/docs/src/progress/bigthings.md index a63abd03..5027bf9f 100644 --- a/docs/src/progress/bigthings.md +++ b/docs/src/progress/bigthings.md @@ -13,6 +13,7 @@ TODO(?): - Network optimization - Building cube voxels w/ LOD + - Tools to build voxels - Audio Ray Tracing diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 74cc2691..224fa306 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1189,6 +1189,7 @@ Spawn characters from database Fluid spawning item Fix fluid shader Re-enable fluid simulation +Remove concurrent datastructure usage in cell management # TODO diff --git a/src/main/java/electrosphere/client/block/BlockChunkCache.java b/src/main/java/electrosphere/client/block/BlockChunkCache.java index 8c60323f..0770fc91 100644 --- a/src/main/java/electrosphere/client/block/BlockChunkCache.java +++ b/src/main/java/electrosphere/client/block/BlockChunkCache.java @@ -6,7 +6,6 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; import io.github.studiorailgun.HashUtils; @@ -29,27 +28,27 @@ public class BlockChunkCache { /** * The map of full res chunk key -> chunk data */ - Map cacheMapFullRes = new ConcurrentHashMap(); + Map cacheMapFullRes = new HashMap(); /** * The map of half res chunk key -> chunk data */ - Map cacheMapHalfRes = new ConcurrentHashMap(); + Map cacheMapHalfRes = new HashMap(); /** * The map of quarter res chunk key -> chunk data */ - Map cacheMapQuarterRes = new ConcurrentHashMap(); + Map cacheMapQuarterRes = new HashMap(); /** * The map of eighth res chunk key -> chunk data */ - Map cacheMapEighthRes = new ConcurrentHashMap(); + Map cacheMapEighthRes = new HashMap(); /** * The map of sixteenth res chunk key -> chunk data */ - Map cacheMapSixteenthRes = new ConcurrentHashMap(); + Map cacheMapSixteenthRes = new HashMap(); /** * Tracks how recently a chunk has been queries for (used for evicting old chunks from cache) diff --git a/src/main/java/electrosphere/client/fluid/cache/ClientFluidCache.java b/src/main/java/electrosphere/client/fluid/cache/ClientFluidCache.java index fc45cc96..9878c047 100644 --- a/src/main/java/electrosphere/client/fluid/cache/ClientFluidCache.java +++ b/src/main/java/electrosphere/client/fluid/cache/ClientFluidCache.java @@ -1,9 +1,9 @@ package electrosphere.client.fluid.cache; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; /** @@ -14,9 +14,9 @@ public class ClientFluidCache { //cache capacity int cacheSize; //the map of chunk key -> chunk data - Map cacheMap = new ConcurrentHashMap(); + Map cacheMap = new HashMap(); //the list of keys in the cache - List cacheList = new CopyOnWriteArrayList(); + List cacheList = new LinkedList(); /** * Constructor diff --git a/src/main/java/electrosphere/client/fluid/manager/ClientFluidManager.java b/src/main/java/electrosphere/client/fluid/manager/ClientFluidManager.java index 66cec95d..cd8fe104 100644 --- a/src/main/java/electrosphere/client/fluid/manager/ClientFluidManager.java +++ b/src/main/java/electrosphere/client/fluid/manager/ClientFluidManager.java @@ -5,7 +5,7 @@ import java.nio.FloatBuffer; import java.util.LinkedList; import java.util.List; import java.util.UUID; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.ReentrantLock; import org.joml.Vector3i; @@ -27,7 +27,7 @@ import electrosphere.server.terrain.manager.ServerTerrainManager; public class ClientFluidManager { //queues messages from server - List messageQueue = new CopyOnWriteArrayList(); + List messageQueue = new LinkedList(); //The interpolation ratio of fluid public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO; @@ -42,7 +42,12 @@ public class ClientFluidManager { ClientWorldData clientWorldData; //The queue of fluid chunk data to be buffered to gpu - static List fluidChunkGenerationQueue = new CopyOnWriteArrayList(); + static List fluidChunkGenerationQueue = new LinkedList(); + + /** + * Lock for thread-safeing the manager + */ + static ReentrantLock lock = new ReentrantLock(); /** * Constructor @@ -53,9 +58,9 @@ public class ClientFluidManager { public void handleMessages(){ + lock.lock(); List bouncedMessages = new LinkedList(); for(TerrainMessage message : messageQueue){ - messageQueue.remove(message); switch(message.getMessageSubtype()){ case SENDFLUIDDATA: { ByteBuffer buffer = ByteBuffer.wrap(message.getchunkData()); @@ -81,13 +86,17 @@ public class ClientFluidManager { break; } } + messageQueue.clear(); for(TerrainMessage message : bouncedMessages){ messageQueue.add(message); } + lock.unlock(); } public void attachFluidMessage(TerrainMessage message){ + lock.lock(); messageQueue.add(message); + lock.unlock(); } public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ @@ -135,7 +144,9 @@ public class ClientFluidManager { UUID newUUID = UUID.randomUUID(); promisedHash = newUUID.toString(); FluidChunkGenQueueItem queueItem = new FluidChunkGenQueueItem(data, promisedHash); + lock.lock(); fluidChunkGenerationQueue.add(queueItem); + lock.unlock(); return promisedHash; } @@ -144,11 +155,13 @@ public class ClientFluidManager { */ public static void generateFluidChunkGeometry(){ Globals.profiler.beginCpuSample("generateFluidChunkGeometry"); + lock.lock(); for(FluidChunkGenQueueItem queueItem : fluidChunkGenerationQueue){ Model fluidModel = FluidChunkModelGeneration.generateFluidModel(queueItem.getData()); Globals.assetManager.registerModelToSpecificString(fluidModel, queueItem.getPromisedHash()); } fluidChunkGenerationQueue.clear(); + lock.unlock(); Globals.profiler.endCpuSample(); } diff --git a/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java b/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java index 80e35689..06274a42 100644 --- a/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java +++ b/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java @@ -11,12 +11,15 @@ import electrosphere.server.fluid.simulator.cellularautomata.FluidCellularAutoma import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.util.FileUtils; +import electrosphere.util.annotation.Exclude; + import java.nio.ByteBuffer; import java.nio.FloatBuffer; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.ReentrantLock; import org.joml.Vector3i; @@ -37,28 +40,40 @@ public class ServerFluidManager { //While we incur a penalty with converting ints -> string, think this will //offset regenerating the array every time we want a new one int cacheSize = 500; + @Exclude Map chunkCache; + @Exclude List chunkCacheContents; //The map of chunk position <-> file on disk containing chunk data FluidDiskMap chunkDiskMap = null; + @Exclude //The generation algorithm for this fluid manager FluidGenerator chunkGenerator; + @Exclude //the fluid simulator ServerFluidSimulator serverFluidSimulator; + @Exclude //the terrain manager associated ServerTerrainManager serverTerrainManager; //controls whether fluid simulation should actually happen or not boolean simulate = false; + @Exclude /** * The parent world data */ ServerWorldData parent; + + @Exclude + /** + * Locks the fluid manager + */ + ReentrantLock lock = new ReentrantLock(); /** @@ -72,8 +87,8 @@ public class ServerFluidManager { ){ this.parent = parent; this.serverTerrainManager = serverTerrainManager; - this.chunkCache = new ConcurrentHashMap(); - this.chunkCacheContents = new CopyOnWriteArrayList(); + this.chunkCache = new HashMap(); + this.chunkCacheContents = new LinkedList(); this.seed = seed; this.chunkGenerator = chunkGenerator; this.serverFluidSimulator = new FluidCellularAutomataSimulator(); @@ -94,6 +109,7 @@ public class ServerFluidManager { * @param saveName The name of the save */ public void save(String saveName){ + lock.lock(); if(model != null){ ByteBuffer buffer = ByteBuffer.allocate(model.getElevation().length * model.getElevation()[0].length * 4); FloatBuffer floatView = buffer.asFloatBuffer(); @@ -115,6 +131,7 @@ public class ServerFluidManager { if(chunkDiskMap != null){ chunkDiskMap.save(); } + lock.unlock(); } /** @@ -203,14 +220,14 @@ public class ServerFluidManager { * @return The ServerFluidChunk */ public ServerFluidChunk getChunk(int worldX, int worldY, int worldZ){ + ServerFluidChunk returnedChunk = null; + lock.lock(); //THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING String key = getKey(worldX,worldY,worldZ); - ServerFluidChunk returnedChunk = null; if(chunkCache.containsKey(key)){ chunkCacheContents.remove(key); chunkCacheContents.add(0, key); returnedChunk = chunkCache.get(key); - return returnedChunk; } else { if(chunkCacheContents.size() > cacheSize){ String oldChunk = chunkCacheContents.remove(chunkCacheContents.size() - 1); @@ -228,8 +245,9 @@ public class ServerFluidManager { } chunkCache.put(key, returnedChunk); chunkCacheContents.add(key); - return returnedChunk; } + lock.unlock(); + return returnedChunk; } /** @@ -238,7 +256,9 @@ public class ServerFluidManager { * @param position The position to save */ public void savePositionToDisk(Vector3i position){ + lock.lock(); chunkDiskMap.saveToDisk(getChunk(position.x, position.y, position.z)); + lock.unlock(); } /** @@ -259,8 +279,10 @@ public class ServerFluidManager { // ServerFluidChunk chunk = chunkCache.get(key); // chunk.addModification(modification); // } + lock.lock(); ServerFluidChunk fluidChunk = this.getChunk(worldPos.x, worldPos.y, worldPos.z); fluidChunk.setWeight(voxelPos.x, voxelPos.y, voxelPos.z, weight); + lock.unlock(); } /** @@ -269,6 +291,7 @@ public class ServerFluidManager { public boolean simulate(int worldX, int worldY, int worldZ){ boolean rVal = false; Globals.profiler.beginAggregateCpuSample("ServerFluidManager.simulate"); + lock.lock(); if(simulate){ ServerFluidChunk fluidChunk = this.getChunk(worldX, worldY, worldZ); ServerTerrainChunk terrainChunk = this.serverTerrainManager.getChunk(worldX, worldY, worldZ); @@ -276,6 +299,7 @@ public class ServerFluidManager { rVal = this.serverFluidSimulator.simulate(fluidChunk,terrainChunk,worldX,worldY,worldZ); } } + lock.unlock(); Globals.profiler.endCpuSample(); return rVal; } diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java index e7fbf1b7..4fe5ad39 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerChunkCache.java @@ -6,7 +6,6 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; import io.github.studiorailgun.HashUtils; @@ -29,27 +28,27 @@ public class ServerChunkCache { /** * The map of full res chunk key -> chunk data */ - Map cacheMapFullRes = new ConcurrentHashMap(); + Map cacheMapFullRes = new HashMap(); /** * The map of half res chunk key -> chunk data */ - Map cacheMapHalfRes = new ConcurrentHashMap(); + Map cacheMapHalfRes = new HashMap(); /** * The map of quarter res chunk key -> chunk data */ - Map cacheMapQuarterRes = new ConcurrentHashMap(); + Map cacheMapQuarterRes = new HashMap(); /** * The map of eighth res chunk key -> chunk data */ - Map cacheMapEighthRes = new ConcurrentHashMap(); + Map cacheMapEighthRes = new HashMap(); /** * The map of sixteenth res chunk key -> chunk data */ - Map cacheMapSixteenthRes = new ConcurrentHashMap(); + Map cacheMapSixteenthRes = new HashMap(); /** * Tracks how recently a chunk has been queries for (used for evicting old chunks from cache)