package electrosphere.server.terrain.manager; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.Semaphore; /** * Caches chunk data on the server */ public class ServerChunkCache { /** * Number of chunks to cache */ static final int CACHE_SIZE = 5000; /** * The size of the cache */ int cacheSize = CACHE_SIZE; /** * The cached data */ Map chunkCache = new HashMap(); /** * Tracks how recently a chunk has been queries for (used for evicting old chunks from cache) */ List queryRecencyQueue = new LinkedList(); /** * Tracks what chunks are already queued to be asynchronously loaded. Used so we don't have two threads generating/fetching the same chunk */ Map queuedChunkMap = new HashMap(); /** * The lock for thread safety */ Semaphore lock = new Semaphore(1); /** * Gets the collection of server terrain chunks that are cached * @return The collection of chunks */ public Collection getContents(){ lock.acquireUninterruptibly(); Collection rVal = Collections.unmodifiableCollection(chunkCache.values()); lock.release(); return rVal; } /** * Evicts all chunks in the cache */ public void clear(){ lock.acquireUninterruptibly(); chunkCache.clear(); lock.release(); } /** * Gets the chunk at a given world position * @param worldX The world x coordinate * @param worldY The world y coordinate * @param worldZ The world z coordinate * @return The chunk */ public ServerTerrainChunk get(int worldX, int worldY, int worldZ){ ServerTerrainChunk rVal = null; String key = this.getKey(worldX, worldY, worldZ); lock.acquireUninterruptibly(); queryRecencyQueue.remove(key); queryRecencyQueue.add(0, key); rVal = this.chunkCache.get(key); lock.release(); return rVal; } /** * Adds a chunk to the cache * @param worldX The world x coordinate of the chunk * @param worldY The world y coordinate of the chunk * @param worldZ The world z coordinate of the chunk * @param chunk The chunk itself */ public void add(int worldX, int worldY, int worldZ, ServerTerrainChunk chunk){ String key = this.getKey(worldX, worldY, worldZ); lock.acquireUninterruptibly(); queryRecencyQueue.add(0, key); this.chunkCache.put(key, chunk); lock.release(); } /** * Checks if the cache contains the chunk at a given world position * @param worldX The world x coordinate * @param worldY The world y coordinate * @param worldZ The world z coordinate * @return true if the cache contains this chunk, false otherwise */ public boolean containsChunk(int worldX, int worldY, int worldZ){ String key = this.getKey(worldX,worldY,worldZ); lock.acquireUninterruptibly(); boolean rVal = this.chunkCache.containsKey(key); lock.release(); return rVal; } /** * Gets the key for a given world position * @param worldX The x component * @param worldY The y component * @param worldZ The z component * @return The key */ public String getKey(int worldX, int worldY, int worldZ){ return worldX + "_" + worldY + "_" + worldZ; } /** * Checks if the chunk is already queued or not * @param worldX The world x position of the chunk * @param worldY The world y position of the chunk * @param worldZ The world z position of the chunk * @return true if the chunk is already queued, false otherwise */ public boolean chunkIsQueued(int worldX, int worldY, int worldZ){ String key = this.getKey(worldX,worldY,worldZ); lock.acquireUninterruptibly(); boolean rVal = this.queuedChunkMap.containsKey(key); lock.release(); return rVal; } /** * Flags a chunk as queued * @param worldX The world x position of the chunk * @param worldY The world y position of the chunk * @param worldZ The world z position of the chunk */ public void queueChunk(int worldX, int worldY, int worldZ){ String key = this.getKey(worldX,worldY,worldZ); lock.acquireUninterruptibly(); this.queuedChunkMap.put(key,true); lock.release(); } /** * Unflags a chunk as queued * @param worldX The world x position of the chunk * @param worldY The world y position of the chunk * @param worldZ The world z position of the chunk */ public void unqueueChunk(int worldX, int worldY, int worldZ){ String key = this.getKey(worldX,worldY,worldZ); lock.acquireUninterruptibly(); this.queuedChunkMap.remove(key); lock.release(); } }