diff --git a/gameserver/src/main/java/brainwine/gameserver/zone/ChunkManager.java b/gameserver/src/main/java/brainwine/gameserver/zone/ChunkManager.java index 817c270..ef7ddfa 100644 --- a/gameserver/src/main/java/brainwine/gameserver/zone/ChunkManager.java +++ b/gameserver/src/main/java/brainwine/gameserver/zone/ChunkManager.java @@ -7,6 +7,12 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -17,6 +23,7 @@ import org.msgpack.jackson.dataformat.MessagePackFactory; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; +import brainwine.gameserver.entity.player.Player; import brainwine.gameserver.serialization.BlockDeserializer; import brainwine.gameserver.serialization.BlockSerializer; import brainwine.gameserver.util.ZipUtils; @@ -29,6 +36,7 @@ public class ChunkManager { .registerModule(new SimpleModule() .addDeserializer(Block.class, BlockDeserializer.INSTANCE) .addSerializer(BlockSerializer.INSTANCE)); + private final Map chunks = new HashMap<>(); private final Zone zone; private final File blocksFile; private RandomAccessFile file; @@ -84,32 +92,35 @@ public class ChunkManager { } } - public Chunk loadChunk(int index) { - try { - if(file == null) { - file = new RandomAccessFile(blocksFile, "rw"); - } - - file.seek(dataOffset + index * allocSize); - byte[] bytes = new byte[file.readShort()]; - file.read(bytes); - return mapper.readValue(ZipUtils.inflateBytes(bytes), Chunk.class); - } catch(Exception e) { - logger.error("Could not load chunk {} of zone {}", index, zone.getDocumentId(), e); - } + public void saveChunks() { + List inactiveChunks = new ArrayList<>(); - return null; - } - - public void saveModifiedChunks() { for(Chunk chunk : zone.getChunks()) { if(chunk.isModified()) { saveChunk(chunk); } + + boolean active = false; + + for(Player player : zone.getPlayers()) { + if(player.isChunkActive(chunk)) { + active = true; + break; + } + } + + if(!active) { + inactiveChunks.add(chunk); + } + } + + for(Chunk chunk : inactiveChunks) { + chunks.remove(getChunkIndex(chunk.getX(), chunk.getY())); + zone.onChunkUnloaded(chunk); } } - public void saveChunk(Chunk chunk) { + private void saveChunk(Chunk chunk) { int index = zone.getChunkIndex(chunk.getX(), chunk.getY()); try { @@ -131,4 +142,68 @@ public class ChunkManager { logger.error("Could not save chunk {} of zone {}", index, zone.getDocumentId(), e); } } + + private Chunk loadChunk(int index) { + try { + if(file == null) { + file = new RandomAccessFile(blocksFile, "rw"); + } + + file.seek(dataOffset + index * allocSize); + byte[] bytes = new byte[file.readShort()]; + file.read(bytes); + return mapper.readValue(ZipUtils.inflateBytes(bytes), Chunk.class); + } catch(Exception e) { + logger.error("Could not load chunk {} of zone {}", index, zone.getDocumentId(), e); + } + + return null; + } + + public void putChunk(int index, Chunk chunk) { + if(!chunks.containsKey(index) && isChunkIndexInBounds(index)) { + chunk.setModified(true); + chunks.put(index, chunk); + } + } + + public boolean isChunkLoaded(int x, int y) { + return isChunkLoaded(getChunkIndex(x, y)); + } + + public boolean isChunkLoaded(int index) { + return chunks.containsKey(index); + } + + public boolean isChunkIndexInBounds(int index) { + return index >= 0 && index < zone.getNumChunksWidth() * zone.getNumChunksHeight(); + } + + public int getChunkIndex(int x, int y) { + return y / zone.getChunkHeight() * zone.getNumChunksWidth() + x / zone.getChunkWidth(); + } + + public Chunk getChunk(int x, int y) { + return getChunk(getChunkIndex(x, y)); + } + + public Chunk getChunk(int index) { + if(!isChunkIndexInBounds(index)) { + return null; + } + + Chunk chunk = chunks.get(index); + + if(chunk == null) { + chunk = loadChunk(index); + chunks.put(index, chunk); + zone.onChunkLoaded(chunk); + } + + return chunk; + } + + public Collection getChunks() { + return Collections.unmodifiableCollection(chunks.values()); + } } diff --git a/gameserver/src/main/java/brainwine/gameserver/zone/Zone.java b/gameserver/src/main/java/brainwine/gameserver/zone/Zone.java index 5d51175..ecbc050 100644 --- a/gameserver/src/main/java/brainwine/gameserver/zone/Zone.java +++ b/gameserver/src/main/java/brainwine/gameserver/zone/Zone.java @@ -8,10 +8,8 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Queue; import java.util.Random; import java.util.Set; @@ -71,7 +69,6 @@ public class Zone { private final Set pendingSunlight = new HashSet<>(); private final Map entities = new HashMap<>(); private final List players = new ArrayList<>(); - private final Map chunks = new HashMap<>(); private final Map dungeons = new HashMap<>(); private final Map metaBlocks = new HashMap<>(); private final Map globalMetaBlocks = new HashMap<>(); @@ -144,11 +141,6 @@ public class Zone { } } - public void saveModifiedChunks() { - chunkManager.saveModifiedChunks(); - removeInactiveChunks(); - } - /** * Sends a message to each player in this zone. * @@ -678,75 +670,8 @@ public class Zone { return x >= 0 && y >= 0 && x < width && y < height; } - private void removeInactiveChunks() { - Iterator> iterator = chunks.entrySet().iterator(); - - while(iterator.hasNext()) { - Entry entry = iterator.next(); - Chunk chunk = entry.getValue(); - boolean active = false; - - for(Player player : players) { - if(player.isChunkActive(chunk)) { - active = true; - break; - } - } - - if(!active) { - iterator.remove(); - } - } - } - - public boolean isChunkIndexInBounds(int index) { - return index >= 0 && index < numChunksWidth * numChunksHeight; - } - - public boolean isChunkLoaded(int x, int y) { - return isChunkLoaded(getChunkIndex(x, y)); - } - - public boolean isChunkLoaded(int index) { - return chunks.containsKey(index); - } - - public void putChunk(int index, Chunk chunk) { - if(!chunks.containsKey(index) && isChunkIndexInBounds(index)) { - chunk.setModified(true); - chunks.put(index, chunk); - } - } - - public int getChunkIndex(int x, int y) { - return y / chunkHeight * numChunksWidth + x / chunkWidth; - } - - public Chunk getChunk(int x, int y) { - return getChunk(getChunkIndex(x, y)); - } - - public Chunk getChunk(int index) { - if(!isChunkIndexInBounds(index)) { - return null; - } - - Chunk chunk = chunks.get(index); - - if(chunk == null) { - chunk = chunkManager.loadChunk(index); - tryRecalculatePendingSunlight(chunk); - chunks.put(index, chunk); - } - - return chunk; - } - - public Collection getChunks() { - return chunks.values(); - } - - private void tryRecalculatePendingSunlight(Chunk chunk) { + protected void onChunkLoaded(Chunk chunk) { + // Update sunlight if(!pendingSunlight.isEmpty()) { int chunkX = chunk.getX(); @@ -757,6 +682,52 @@ public class Zone { } } } + + // TODO more chunk related thingies, such as surface calculations, + // entity spawning and block indexing + } + + protected void onChunkUnloaded(Chunk chunk) { + // TODO + } + + public void saveChunks() { + chunkManager.saveChunks(); + } + + /** + * Should only be called by zone gen. + */ + public void putChunk(int index, Chunk chunk) { + chunkManager.putChunk(index, chunk); + } + + public boolean isChunkLoaded(int x, int y) { + return chunkManager.isChunkLoaded(x, y); + } + + public boolean isChunkLoaded(int index) { + return chunkManager.isChunkLoaded(index); + } + + public boolean isChunkIndexInBounds(int index) { + return chunkManager.isChunkIndexInBounds(index); + } + + public int getChunkIndex(int x, int y) { + return chunkManager.getChunkIndex(x, y); + } + + public Chunk getChunk(int x, int y) { + return chunkManager.getChunk(x, y); + } + + public Chunk getChunk(int index) { + return chunkManager.getChunk(index); + } + + public Collection getChunks() { + return chunkManager.getChunks(); } public void setSurface(int x, int surface) {