diff --git a/core/src/main/java/mc/core/serialization/IChunkReader.java b/core/src/main/java/mc/core/serialization/IChunkReader.java new file mode 100644 index 0000000..0ecf126 --- /dev/null +++ b/core/src/main/java/mc/core/serialization/IChunkReader.java @@ -0,0 +1,10 @@ +package mc.core.serialization; + +import mc.core.world.Chunk; +import mc.core.world.Region; + +import java.io.IOException; + +public interface IChunkReader { + Chunk read (Region region, int x, int y, int z) throws IOException; +} diff --git a/core/src/main/java/mc/core/serialization/IRegionReaderWriter.java b/core/src/main/java/mc/core/serialization/IRegionReaderWriter.java new file mode 100644 index 0000000..76a7023 --- /dev/null +++ b/core/src/main/java/mc/core/serialization/IRegionReaderWriter.java @@ -0,0 +1,12 @@ +package mc.core.serialization; + +import mc.core.world.Region; +import mc.core.world.World; + +import java.io.IOException; + +public interface IRegionReaderWriter { + + Region read (int x, int z, World world) throws IOException; + void write (Region region) throws IOException; +} diff --git a/core/src/main/java/mc/core/world/Biome.java b/core/src/main/java/mc/core/world/Biome.java index a28aa47..5a60047 100644 --- a/core/src/main/java/mc/core/world/Biome.java +++ b/core/src/main/java/mc/core/world/Biome.java @@ -43,4 +43,8 @@ public enum Biome { this.name = name; this.color = color; } + + public static Biome getById(int id) { + return Biome.values()[id]; + } } diff --git a/core/src/main/java/mc/core/world/Chunk.java b/core/src/main/java/mc/core/world/Chunk.java index a8c09cf..1300d42 100644 --- a/core/src/main/java/mc/core/world/Chunk.java +++ b/core/src/main/java/mc/core/world/Chunk.java @@ -14,15 +14,11 @@ import java.io.Serializable; * +-------------+----------------+------------+ * | param | range | bits | * +-------------+----------------+------------+ - * | | | 3 | - * +-------------+----------------+------------+ - * | block_count | 0:4096 | 13 | - * +-------------+----------------+------------+ * | blocks | array | 24*count | * +-------------+----------------+------------+ * - * Total: 16 bits header (2 bytes) + 24 * block_count bits (3 * block_count bytes) - * Max size: 12290 bytes (~12 Kb per chunk) + * Total: 24 * block_count bits (3 * block_count bytes) + * Max size: 12288 bytes (~12 Kb per chunk) * */ /* 16x16x16 */ diff --git a/core/src/main/java/mc/core/world/Region.java b/core/src/main/java/mc/core/world/Region.java index ebf921e..6791088 100644 --- a/core/src/main/java/mc/core/world/Region.java +++ b/core/src/main/java/mc/core/world/Region.java @@ -1,5 +1,6 @@ package mc.core.world; +import mc.core.serialization.IRegionReaderWriter; import mc.core.serialization.Serializer; import java.io.IOException; @@ -29,5 +30,5 @@ public interface Region extends Serializable{ Biome getBiomeAt (int x, int z); void setBiome (int x, int z, Biome biome); - void save(Serializer chunkSerializer, Serializer regionSerializer) throws IOException; + void save(Serializer chunkSerializer, IRegionReaderWriter regionReaderWritter) throws IOException; } diff --git a/generated_world/src/main/java/mc/world/generated_world/chunk/InMemoryCacheChunkLoader.java b/generated_world/src/main/java/mc/world/generated_world/chunk/InMemoryCacheChunkLoader.java index dc36e0e..2724497 100644 --- a/generated_world/src/main/java/mc/world/generated_world/chunk/InMemoryCacheChunkLoader.java +++ b/generated_world/src/main/java/mc/world/generated_world/chunk/InMemoryCacheChunkLoader.java @@ -1,16 +1,15 @@ package mc.world.generated_world.chunk; import lombok.extern.slf4j.Slf4j; -import mc.core.serialization.Deserializer; import mc.core.serialization.Serializer; import mc.core.world.*; +import mc.world.generated_world.serialization.ChunkReader; +import mc.world.generated_world.serialization.RegionReaderWriter; import org.springframework.beans.factory.annotation.Autowired; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; import java.text.MessageFormat; import java.util.Optional; @@ -23,15 +22,12 @@ public class InMemoryCacheChunkLoader implements ChunkLoader { private File worldFolder; @Autowired private WorldGenerator worldGenerator; - @Autowired - private Deserializer chunkDeserializer; + private ChunkReader chunkReader; @Autowired private Serializer chunkSerializer; @Autowired - private Serializer regionSerializer; - @Autowired - private Deserializer regionDeserializer; + private RegionReaderWriter regionReaderWritter; public InMemoryCacheChunkLoader(World world) { this.world = world; @@ -54,8 +50,8 @@ public class InMemoryCacheChunkLoader implements ChunkLoader { return Optional.empty(); } else { try { - byte[] bytes = Files.readAllBytes(Paths.get(file.toURI())); - return Optional.of(chunkDeserializer.deserialize(bytes)); + Chunk chunk = chunkReader.read(world.getRegion(x / WORLD_CHUNK_SIZE, z / WORLD_CHUNK_SIZE), x, y, z); + return Optional.of(chunk); } catch (IOException e) { log.error("Error occurred while reading chunk file: " + file.getAbsolutePath(), e); return Optional.empty(); @@ -65,8 +61,8 @@ public class InMemoryCacheChunkLoader implements ChunkLoader { @Override public Chunk loadOrGenerateChunk(int x, int y, int z) { - int regX = x / WORLD_REGION_SIZE; - int regZ = z / WORLD_REGION_SIZE; + int regX = x / WORLD_CHUNK_SIZE; + int regZ = z / WORLD_CHUNK_SIZE; File regionFile = new File(worldFolder, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, regX, regZ)); Region region; Chunk chunk; @@ -74,32 +70,22 @@ public class InMemoryCacheChunkLoader implements ChunkLoader { log.debug("Region [{}, {}] not found. Generating!", regX, regZ); regionFile.mkdirs(); region = worldGenerator.generateRegion(regX, regZ, world); - File biomeMapFile = new File(regionFile, BIOME_FILE_NAME_TEMPLATE); - byte[] biomeMapBytes = regionSerializer.serialize(region); - try (FileOutputStream writer = new FileOutputStream(biomeMapFile)) { - writer.write(biomeMapBytes); + try { + regionReaderWritter.write(region); } catch (IOException e) { log.error("Error occurred while writting biome file", e); } saveRegion(region); chunk = region.getChunkAt(x % WORLD_CHUNK_SIZE, y % WORLD_CHUNK_SIZE, z % WORLD_CHUNK_SIZE); } else { - File chunkFile = new File(regionFile, MessageFormat.format(CHUNK_FILE_NAME_TEMPLATE, x % WORLD_CHUNK_SIZE, y % WORLD_CHUNK_SIZE, z % WORLD_CHUNK_SIZE)); try { - byte[] chunkBytes = Files.readAllBytes(Paths.get(chunkFile.toURI())); - byte[] regionBytes = Files.readAllBytes(Paths.get(new File(regionFile, BIOME_FILE_NAME_TEMPLATE).toURI())); - region = regionDeserializer.deserialize(regionBytes); - chunk = chunkDeserializer.deserialize(chunkBytes); + region = regionReaderWritter.read(regX, regZ, world); + chunk = chunkReader.read(region, x, y, z); } catch (IOException e) { log.error("Error occurred while reading chunk file", e); return null; } } - for (int tx = 0; tx < WORLD_CHUNK_SIZE; tx++) { - for (int tz = 0; tz < WORLD_CHUNK_SIZE; tz ++) { - chunk.setBiome(tx, tz, region.getBiomeAt(chunk.getX() * WORLD_CHUNK_SIZE + x, chunk.getZ() * WORLD_CHUNK_SIZE + z)); - } - } return chunk; } diff --git a/generated_world/src/main/java/mc/world/generated_world/generator/SeedBasedWorldGenerator.java b/generated_world/src/main/java/mc/world/generated_world/generator/SeedBasedWorldGenerator.java index 2a7ab64..3ede02c 100644 --- a/generated_world/src/main/java/mc/world/generated_world/generator/SeedBasedWorldGenerator.java +++ b/generated_world/src/main/java/mc/world/generated_world/generator/SeedBasedWorldGenerator.java @@ -24,7 +24,7 @@ public class SeedBasedWorldGenerator implements WorldGenerator { WorldGenerator worldGenerator = new SeedBasedWorldGenerator(); World world = new CubicWorld(UUID.fromString("00000000-0000-0000-C000-000000000046"), 2626949); Region region = worldGenerator.generateRegion(0, 0, world); - //region.save(new ChunkSerializerDeserializer(), new RegionSerializerDeserializer()); + //region.save(new ChunkSerializer(), new RegionSerializerDeserializer()); worldGenerator.generateRegion(1, 0, world); worldGenerator.generateRegion(-1, 0, world); worldGenerator.generateRegion(0, 1, world); diff --git a/generated_world/src/main/java/mc/world/generated_world/region/RegionImpl.java b/generated_world/src/main/java/mc/world/generated_world/region/RegionImpl.java index 0750ecd..8637f47 100644 --- a/generated_world/src/main/java/mc/world/generated_world/region/RegionImpl.java +++ b/generated_world/src/main/java/mc/world/generated_world/region/RegionImpl.java @@ -3,6 +3,7 @@ package mc.world.generated_world.region; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import mc.core.serialization.IRegionReaderWriter; import mc.core.serialization.Serializer; import mc.core.world.*; import mc.world.generated_world.chunk.InMemoryCacheChunkLoader; @@ -72,18 +73,14 @@ public class RegionImpl implements Region{ } @Override - public void save(Serializer chunkSerializer, Serializer regionSerializer) throws IOException { + public void save(Serializer chunkSerializer, IRegionReaderWriter regionReaderWriter) throws IOException { String worldPath = System.getProperty("worlds.folder", "worlds"); File worldFile = new File(worldPath, world.getWorldId().toString()); File regionFile = new File(worldFile, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, this.getX(), this.getZ())); if (!regionFile.exists()) { regionFile.mkdirs(); } - File biomeMapFile = new File(regionFile, BIOME_FILE_NAME_TEMPLATE); - byte[] biomeBytes = regionSerializer.serialize(this); - try (FileOutputStream fileOutputStream = new FileOutputStream(biomeMapFile)){ - fileOutputStream.write(biomeBytes); - } + regionReaderWriter.write(this); for (int x = 0; x < WORLD_CHUNK_SIZE; x ++) { for (int z = 0; z < WORLD_CHUNK_SIZE; z ++) { for (int y = 0; y < WORLD_CHUNK_SIZE; y++) { diff --git a/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkReader.java b/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkReader.java new file mode 100644 index 0000000..8b77dde --- /dev/null +++ b/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkReader.java @@ -0,0 +1,49 @@ +package mc.world.generated_world.serialization; + +import mc.core.Location; +import mc.core.block.Block; +import mc.core.serialization.Deserializer; +import mc.core.serialization.IChunkReader; +import mc.core.world.Chunk; +import mc.core.world.Region; +import mc.world.generated_world.chunk.ChunkImpl; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.MessageFormat; + +import static mc.world.generated_world.WorldConstants.*; + +public class ChunkReader implements IChunkReader{ + private final File worldFolder; + @Autowired + private Deserializer blockDeserializer; + + public ChunkReader (File worldFolder) { + this.worldFolder = worldFolder; + } + + @Override + public Chunk read (Region region, int x, int y, int z) throws IOException { + x %= WORLD_REGION_SIZE; + y %= WORLD_REGION_SIZE; + z %= WORLD_REGION_SIZE; + File chunkFile = new File(new File(worldFolder, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, region.getX(), region.getZ())), MessageFormat.format(CHUNK_FILE_NAME_TEMPLATE, x, y, z)); + byte[] chunkBytes = Files.readAllBytes(Paths.get(chunkFile.toURI())); + int blocks = (chunkBytes.length) / 3; + Chunk chunk = new ChunkImpl(x, y, z, region); + for (int i = 0; i < blocks; i ++) { + byte[] blockBytes = new byte[3]; + blockBytes[0] = chunkBytes[3 * i]; + blockBytes[1] = chunkBytes[1 + 3 * i]; + blockBytes[2] = chunkBytes[2 + 3 * i]; + Block block = blockDeserializer.deserialize(blockBytes); + Location blockLocation = block.getLocation(); + chunk.setBlock(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ(), block); + } + return chunk; + } +} diff --git a/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkSerializer.java b/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkSerializer.java new file mode 100644 index 0000000..b854c64 --- /dev/null +++ b/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkSerializer.java @@ -0,0 +1,26 @@ +package mc.world.generated_world.serialization; + +import mc.core.block.Block; +import mc.core.serialization.Serializer; +import mc.core.world.Chunk; +import org.springframework.beans.factory.annotation.Autowired; + +public class ChunkSerializer implements Serializer { + + @Autowired + private Serializer blockSerializer; + + @Override + public byte[] serialize(Chunk chunk) { + int blocks = chunk.getModifiedBlocks().length; + byte[] bytes = new byte[3 * blocks]; + + for (int i = 0; i < blocks; i ++) { + byte[] blockSerialized = blockSerializer.serialize(chunk.getModifiedBlocks()[i]); + for (int j = 0; j < 3; j ++) { + bytes[i * 3 + j] = blockSerialized[j]; + } + } + return bytes; + } +} diff --git a/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkSerializerDeserializer.java b/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkSerializerDeserializer.java deleted file mode 100644 index b0b3feb..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkSerializerDeserializer.java +++ /dev/null @@ -1,40 +0,0 @@ -package mc.world.generated_world.serialization; - -import mc.core.block.Block; -import mc.core.block.BlockFactory; -import mc.core.serialization.Deserializer; -import mc.core.serialization.Serializer; -import mc.core.world.Chunk; - -public class ChunkSerializerDeserializer implements Serializer, Deserializer { - - private Serializer blockSerializer; - private Deserializer blockDeserializer; - - @Override - public Chunk deserialize(byte[] bytes) { - return null; - } - - @Override - public byte[] serialize(Chunk chunk) { - Serializer blockSerializer = new BlockSerializerDeserializer(new BlockFactory(), chunk); - int blocks = chunk.getModifiedBlocks().length; - byte[] bytes = new byte[6 + 3 * blocks]; - - bytes[0] = (byte) ((chunk.getX() >> 6) & 0xff); - bytes[1] = (byte) (((chunk.getX() & 0x3f) << 2) | ((chunk.getY()) >> 2) & 0x03); - bytes[2] = (byte) (((chunk.getY() & 0x03) << 6) | ((chunk.getZ() >> 8) & 0x3f)); - bytes[3] = (byte) (chunk.getZ() & 0xff); - bytes[4] = (byte) ((blocks >> 5) & 0xff); - bytes[5] = (byte) ((blocks & 0x1f) << 3); - - for (int i = 0; i < blocks; i ++) { - byte[] blockSerialized = blockSerializer.serialize(chunk.getModifiedBlocks()[i]); - for (int j = 0; j < 3; j ++) { - bytes[6 + i * 3 + j] = blockSerialized[j]; - } - } - return bytes; - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/serialization/RegionReaderWriter.java b/generated_world/src/main/java/mc/world/generated_world/serialization/RegionReaderWriter.java new file mode 100644 index 0000000..2a44fda --- /dev/null +++ b/generated_world/src/main/java/mc/world/generated_world/serialization/RegionReaderWriter.java @@ -0,0 +1,53 @@ +package mc.world.generated_world.serialization; + +import mc.core.serialization.IRegionReaderWriter; +import mc.core.world.Biome; +import mc.core.world.Region; +import mc.core.world.World; +import mc.world.generated_world.region.RegionImpl; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.MessageFormat; + +import static mc.world.generated_world.WorldConstants.*; + +public class RegionReaderWriter implements IRegionReaderWriter { + private final File worldFolder; + + public RegionReaderWriter(File worldFolder) { + this.worldFolder = worldFolder; + } + + @Override + public Region read (int x, int z, World world) throws IOException{ + File regionFolder = new File(worldFolder, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, x, z)); + File biomesFile = new File(regionFolder, BIOME_FILE_NAME_TEMPLATE); + byte[] biomesBytes = Files.readAllBytes(Paths.get(biomesFile.toURI())); + Region region = new RegionImpl(x, z, world); + for (int tx = 0; tx < WORLD_REGION_SIZE; tx ++) { + for (int tz = 0; tz < WORLD_REGION_SIZE; tz ++) { + region.setBiome(tx, tz, Biome.getById(biomesBytes[tx * WORLD_REGION_SIZE + tz])); + } + } + return region; + } + + @Override + public void write (Region region) throws IOException{ + File regionFolder = new File(worldFolder, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, region.getX(), region.getZ())); + File biomesFile = new File(regionFolder, BIOME_FILE_NAME_TEMPLATE); + byte[] biomesBytes = new byte[WORLD_REGION_SIZE * WORLD_REGION_SIZE]; + for (int x = 0; x < WORLD_REGION_SIZE; x ++) { + for (int z = 0; z < WORLD_REGION_SIZE; z ++) { + biomesBytes[x * WORLD_REGION_SIZE + z] = (byte) region.getBiomeAt(x, z).getId(); + } + } + try (FileOutputStream fos = new FileOutputStream(biomesFile)) { + fos.write(biomesBytes); + } + } +} diff --git a/generated_world/src/main/java/mc/world/generated_world/serialization/RegionSerializerDeserializer.java b/generated_world/src/main/java/mc/world/generated_world/serialization/RegionSerializerDeserializer.java deleted file mode 100644 index 11e8a53..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/serialization/RegionSerializerDeserializer.java +++ /dev/null @@ -1,17 +0,0 @@ -package mc.world.generated_world.serialization; - -import mc.core.serialization.Deserializer; -import mc.core.serialization.Serializer; -import mc.core.world.Region; - -public class RegionSerializerDeserializer implements Serializer, Deserializer { - @Override - public Region deserialize(byte[] bytes) { - return null; - } - - @Override - public byte[] serialize(Region region) { - return new byte[0]; - } -}