Chunk generations & basic saving
This commit is contained in:
@@ -25,6 +25,30 @@ import mc.core.Location;
|
|||||||
|
|
||||||
public interface Block {
|
public interface Block {
|
||||||
|
|
||||||
|
static Block airBlock (int x, int y, int z) {
|
||||||
|
return new Block() {
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMeta() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockType getBlockType() {
|
||||||
|
return BlockType.AIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Location getLocation() {
|
||||||
|
return new Location(x, y, z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** Block id */
|
/** Block id */
|
||||||
int getId();
|
int getId();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package mc.core.block;
|
package mc.core.block;
|
||||||
|
|
||||||
|
import mc.core.Location;
|
||||||
|
|
||||||
public class BlockFactory {
|
public class BlockFactory {
|
||||||
|
|
||||||
public Block create(BlockType blockType, int meta) {
|
public Block create(BlockType blockType, int meta) {
|
||||||
@@ -12,6 +14,7 @@ public class BlockFactory {
|
|||||||
private class EmbeddedBlock extends AbstractBlock {
|
private class EmbeddedBlock extends AbstractBlock {
|
||||||
EmbeddedBlock(BlockType type, int meta) {
|
EmbeddedBlock(BlockType type, int meta) {
|
||||||
super(type, meta);
|
super(type, meta);
|
||||||
|
super.setLocation(new Location(0,0,0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ public enum BlockType {
|
|||||||
BEDROCK(7, "Bedrock"),
|
BEDROCK(7, "Bedrock"),
|
||||||
WATER(8, "Water"),
|
WATER(8, "Water"),
|
||||||
SAND(12, "Sand"),
|
SAND(12, "Sand"),
|
||||||
SNOW(32, "Snow");
|
SNOW(32, "Snow"),
|
||||||
|
AIR(0, "Air");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final int id;
|
private final int id;
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ public interface Chunk {
|
|||||||
int getY();
|
int getY();
|
||||||
int getZ();
|
int getZ();
|
||||||
|
|
||||||
Block[] getNotAirBlocks();
|
Block[] getModifiedBlocks();
|
||||||
void setBlock (int x, int y, int z, Block block);
|
void setBlock (int x, int y, int z, Block block);
|
||||||
|
Block getBlock (int x, int y, int z);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package mc.core.world;
|
package mc.core.world;
|
||||||
|
|
||||||
|
import mc.core.serialization.Serializer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple world generation unit
|
* Simple world generation unit
|
||||||
* 16x16x16 chunks
|
* 16x16x16 chunks
|
||||||
@@ -23,4 +27,6 @@ public interface Region {
|
|||||||
|
|
||||||
Biome getBiomeAt (int x, int z);
|
Biome getBiomeAt (int x, int z);
|
||||||
void setBiome (int x, int z, Biome biome);
|
void setBiome (int x, int z, Biome biome);
|
||||||
|
|
||||||
|
void save(Serializer<Chunk> chunkSerializer, Serializer<Region> regionSerializer) throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
package mc.world.flat;
|
package mc.world.flat;
|
||||||
|
|
||||||
import mc.core.block.Block;
|
import mc.core.block.Block;
|
||||||
|
import mc.core.block.BlockFactory;
|
||||||
|
import mc.core.block.BlockType;
|
||||||
import mc.core.world.Biome;
|
import mc.core.world.Biome;
|
||||||
import mc.core.world.Chunk;
|
import mc.core.world.Chunk;
|
||||||
|
|
||||||
@@ -89,7 +91,7 @@ public class SimpleChunk implements Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Block[] getNotAirBlocks() {
|
public Block[] getModifiedBlocks() {
|
||||||
return new Block[0];
|
return new Block[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,4 +99,14 @@ public class SimpleChunk implements Chunk {
|
|||||||
public void setBlock(int x, int y, int z, Block block) {
|
public void setBlock(int x, int y, int z, Block block) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Block getBlock(int x, int y, int z) {
|
||||||
|
BlockFactory blockFactory = new BlockFactory();
|
||||||
|
|
||||||
|
if (y == 0) return blockFactory.create(BlockType.BEDROCK, 0);
|
||||||
|
else if (y >= 1 && y <= 2) return blockFactory.create(BlockType.DIRT, 0);
|
||||||
|
else if (y == 3) return blockFactory.create(BlockType.GRASS, 0);
|
||||||
|
else return Block.airBlock(x, y, z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package mc.world.generated_world;
|
||||||
|
|
||||||
|
import mc.core.block.BlockFactory;
|
||||||
|
import mc.core.serialization.BlockDeserializer;
|
||||||
|
import mc.core.serialization.BlockSerializer;
|
||||||
|
import mc.core.serialization.ChunkSerializer;
|
||||||
|
import mc.core.serialization.ChunkDeserializer;
|
||||||
|
import mc.core.world.Chunk;
|
||||||
|
|
||||||
|
public class ChunkSerializerDeserializer implements ChunkSerializer, ChunkDeserializer {
|
||||||
|
|
||||||
|
private BlockSerializer blockSerializer;
|
||||||
|
private BlockDeserializer blockDeserializer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Chunk deserialize(byte[] bytes) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] serialize(Chunk chunk) {
|
||||||
|
BlockSerializer 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,25 +16,25 @@ public class CubicWorld implements World {
|
|||||||
|
|
||||||
public CubicWorld(UUID worldId, int seed) {
|
public CubicWorld(UUID worldId, int seed) {
|
||||||
this.worldId = worldId;
|
this.worldId = worldId;
|
||||||
chunkLoader = new InMemoryCacheChunkLoader(worldId);
|
chunkLoader = new InMemoryCacheChunkLoader(this);
|
||||||
this.seed = seed;
|
this.seed = seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CubicWorld(int seed) {
|
public CubicWorld(int seed) {
|
||||||
this.worldId = UUID.randomUUID();
|
this.worldId = UUID.randomUUID();
|
||||||
chunkLoader = new InMemoryCacheChunkLoader(worldId);
|
chunkLoader = new InMemoryCacheChunkLoader(this);
|
||||||
this.seed = seed;
|
this.seed = seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CubicWorld(UUID worldId) {
|
public CubicWorld(UUID worldId) {
|
||||||
this.worldId = worldId;
|
this.worldId = worldId;
|
||||||
chunkLoader = new InMemoryCacheChunkLoader(worldId);
|
chunkLoader = new InMemoryCacheChunkLoader(this);
|
||||||
this.seed = 0;
|
this.seed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CubicWorld () {
|
public CubicWorld () {
|
||||||
this.worldId = UUID.randomUUID();
|
this.worldId = UUID.randomUUID();
|
||||||
chunkLoader = new InMemoryCacheChunkLoader(worldId);
|
chunkLoader = new InMemoryCacheChunkLoader(this);
|
||||||
this.seed = 0;
|
this.seed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package mc.world.generated_world;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import mc.core.serialization.ChunkDeserializer;
|
||||||
|
import mc.core.serialization.ChunkSerializer;
|
||||||
|
import mc.core.serialization.Deserializer;
|
||||||
|
import mc.core.serialization.Serializer;
|
||||||
|
import mc.core.world.*;
|
||||||
|
import mc.world.generated_world.region.RegionImpl;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static mc.world.generated_world.WorldConstants.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class InMemoryCacheChunkLoader implements ChunkLoader {
|
||||||
|
|
||||||
|
private final World world;
|
||||||
|
private File worldFolder;
|
||||||
|
@Autowired
|
||||||
|
private WorldGenerator worldGenerator;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChunkDeserializer chunkDeserializer;
|
||||||
|
@Autowired
|
||||||
|
private ChunkSerializer chunkSerializer;
|
||||||
|
@Autowired
|
||||||
|
private Serializer<Region> regionSerializer;
|
||||||
|
@Autowired
|
||||||
|
private Deserializer<Region> regionDeserializer;
|
||||||
|
|
||||||
|
public InMemoryCacheChunkLoader(World world) {
|
||||||
|
this.world = world;
|
||||||
|
String worldPath = System.getProperty("worlds.folder", "worlds");
|
||||||
|
worldFolder = new File(worldPath, world.getWorldId().toString());
|
||||||
|
if (!worldFolder.exists()) {
|
||||||
|
log.info("Created folder for world with uuid '{}'", world.getWorldId());
|
||||||
|
worldFolder.mkdirs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getChuckFile(int x, int y, int z) {
|
||||||
|
return new File(worldFolder, MessageFormat.format(CHUNK_FILE_NAME_TEMPLATE, x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Chunk> loadChunk(int x, int y, int z) {
|
||||||
|
File file = getChuckFile(x, y, z);
|
||||||
|
if (!file.exists()) {
|
||||||
|
return Optional.empty();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
byte[] bytes = Files.readAllBytes(Paths.get(file.toURI()));
|
||||||
|
return Optional.of(chunkDeserializer.deserialize(bytes));
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error occurred while reading chunk file: " + file.getAbsolutePath(), e);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Chunk loadOrGenerateChunk(int x, int y, int z) {
|
||||||
|
int regX = x / WORLD_REGION_SIZE;
|
||||||
|
int regZ = z / WORLD_REGION_SIZE;
|
||||||
|
File regionFile = new File(worldFolder, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, regX, regZ));
|
||||||
|
Region region;
|
||||||
|
Chunk chunk;
|
||||||
|
if (!regionFile.exists()) {
|
||||||
|
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);
|
||||||
|
} 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);
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveRegion (Region region) {
|
||||||
|
File file = new File(worldFolder, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, region.getX(), region.getZ()));
|
||||||
|
for (int x = 0; x < WORLD_REGION_SIZE / WORLD_CHUNK_SIZE; x ++) {
|
||||||
|
for (int y = 0; y < WORLD_REGION_SIZE / WORLD_CHUNK_SIZE; y ++) {
|
||||||
|
for (int z = 0; z < WORLD_REGION_SIZE / WORLD_CHUNK_SIZE; z ++) {
|
||||||
|
byte[] chunkBytes = chunkSerializer.serialize(region.getChunkAt(x, y, z));
|
||||||
|
File chunkFile = new File(file, MessageFormat.format(CHUNK_FILE_NAME_TEMPLATE, x, y, z));
|
||||||
|
try (FileOutputStream writer = new FileOutputStream(chunkFile)) {
|
||||||
|
writer.write(chunkBytes);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error occurred while writting chunk to file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package mc.world.generated_world;
|
||||||
|
|
||||||
|
import mc.core.serialization.Deserializer;
|
||||||
|
import mc.core.serialization.Serializer;
|
||||||
|
import mc.core.world.Region;
|
||||||
|
|
||||||
|
public class RegionSerializerDeserializer implements Serializer<Region>, Deserializer<Region> {
|
||||||
|
@Override
|
||||||
|
public Region deserialize(byte[] bytes) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] serialize(Region region) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package mc.world.generated_world;
|
|||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import mc.core.block.BlockFactory;
|
import mc.core.block.BlockFactory;
|
||||||
|
import mc.core.block.BlockType;
|
||||||
import mc.core.world.*;
|
import mc.core.world.*;
|
||||||
import mc.world.generated_world.region.RegionImpl;
|
import mc.world.generated_world.region.RegionImpl;
|
||||||
import mc.world.generated_world.word.Temperature;
|
import mc.world.generated_world.word.Temperature;
|
||||||
@@ -19,7 +20,8 @@ public class SeedBasedWorldGenerator implements WorldGenerator {
|
|||||||
public static void main(String[] args) throws Exception{
|
public static void main(String[] args) throws Exception{
|
||||||
WorldGenerator worldGenerator = new SeedBasedWorldGenerator();
|
WorldGenerator worldGenerator = new SeedBasedWorldGenerator();
|
||||||
World world = new CubicWorld(UUID.fromString("00000000-0000-0000-C000-000000000046"), 123);
|
World world = new CubicWorld(UUID.fromString("00000000-0000-0000-C000-000000000046"), 123);
|
||||||
worldGenerator.generateRegion(0, 0, world);
|
Region region = worldGenerator.generateRegion(0, 0, world);
|
||||||
|
region.save(new ChunkSerializerDeserializer(), new RegionSerializerDeserializer());
|
||||||
/*worldGenerator.generateRegion(1, 0, world);
|
/*worldGenerator.generateRegion(1, 0, world);
|
||||||
worldGenerator.generateRegion(-1, 0, world);
|
worldGenerator.generateRegion(-1, 0, world);
|
||||||
worldGenerator.generateRegion(0, 1, world);
|
worldGenerator.generateRegion(0, 1, world);
|
||||||
@@ -83,7 +85,7 @@ public class SeedBasedWorldGenerator implements WorldGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Region generateRegion(int x, int z, World world) {
|
public Region generateRegion(int x, int z, World world) {
|
||||||
Region region = new RegionImpl(x,z);
|
Region region = new RegionImpl(x, z, world);
|
||||||
RegionGenerator regionGenerator = new RegionGenerator(world, region);
|
RegionGenerator regionGenerator = new RegionGenerator(world, region);
|
||||||
regionGenerator.generate();
|
regionGenerator.generate();
|
||||||
return region;
|
return region;
|
||||||
@@ -233,7 +235,7 @@ public class SeedBasedWorldGenerator implements WorldGenerator {
|
|||||||
} catch (Exception e) {}
|
} catch (Exception e) {}
|
||||||
// ================================ DEBUG FINISH =======================================
|
// ================================ DEBUG FINISH =======================================
|
||||||
|
|
||||||
/*for (int x = 0; x < WorldConstants.WORLD_REGION_SIZE; x ++) {
|
for (int x = 0; x < WorldConstants.WORLD_REGION_SIZE; x ++) {
|
||||||
for (int z = 0; z < WorldConstants.WORLD_REGION_SIZE; z ++) {
|
for (int z = 0; z < WorldConstants.WORLD_REGION_SIZE; z ++) {
|
||||||
region.setBiome(x, z, biomes[x][z]);
|
region.setBiome(x, z, biomes[x][z]);
|
||||||
if (heightMap[x][z] < WORLD_SEA_LEVEL) {
|
if (heightMap[x][z] < WORLD_SEA_LEVEL) {
|
||||||
@@ -274,7 +276,7 @@ public class SeedBasedWorldGenerator implements WorldGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Biome selectBiome (Temperature temperature, Wetness wetness, int height) {
|
private Biome selectBiome (Temperature temperature, Wetness wetness, int height) {
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ package mc.world.generated_world;
|
|||||||
public final class WorldConstants {
|
public final class WorldConstants {
|
||||||
|
|
||||||
public static final String CHUNK_FILE_NAME_TEMPLATE = "chunk_{0}_{1}_{2}.dat";
|
public static final String CHUNK_FILE_NAME_TEMPLATE = "chunk_{0}_{1}_{2}.dat";
|
||||||
public static final String BIOME_FILE_NAME_TEMPLATE = "biome_{0}_{1}.dat";
|
public static final String BIOME_FILE_NAME_TEMPLATE = "biomes.dat";
|
||||||
public static final String REGION_FILE_NAME_TEMPLATE = "r.{0}.{1}";
|
public static final String REGION_FILE_NAME_TEMPLATE = "r.{0}.{1}";
|
||||||
|
|
||||||
public static final int WORLD_MIN_HEIGHT = 28;
|
public static final int WORLD_MIN_HEIGHT = 28;
|
||||||
public static final int WORLD_SEA_LEVEL = 64;
|
public static final int WORLD_SEA_LEVEL = 64;
|
||||||
public static final int WORLD_MAX_HEIGHT = 128;
|
public static final int WORLD_MAX_HEIGHT = 128;
|
||||||
public static final int WORLD_REGION_SIZE = 256;
|
public static final int WORLD_REGION_SIZE = 256;
|
||||||
|
public static final int WORLD_CHUNK_SIZE = 16;
|
||||||
public static final int WORLD_MAX_TEMPERATURE = 100;
|
public static final int WORLD_MAX_TEMPERATURE = 100;
|
||||||
public static final int WORLD_MAX_WETNESS = 100;
|
public static final int WORLD_MAX_WETNESS = 100;
|
||||||
public static final int WORLD_BASE_WETNESS = 80;
|
public static final int WORLD_BASE_WETNESS = 80;
|
||||||
@@ -23,7 +24,7 @@ public final class WorldConstants {
|
|||||||
|
|
||||||
|
|
||||||
public static final int LANDFILL_GRASS_SURFACE_THIN = 5;
|
public static final int LANDFILL_GRASS_SURFACE_THIN = 5;
|
||||||
public static final double WORLD_ROUGHNRESS = 0.35;
|
public static final double WORLD_ROUGHNESS = 0.35;
|
||||||
|
|
||||||
private WorldConstants () {}
|
private WorldConstants () {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package mc.world.generated_world.chunk;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import mc.core.block.Block;
|
||||||
|
import mc.core.world.Biome;
|
||||||
|
import mc.core.world.Chunk;
|
||||||
|
import mc.core.world.Region;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static mc.world.generated_world.WorldConstants.WORLD_CHUNK_SIZE;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ChunkImpl implements Chunk{
|
||||||
|
@Getter
|
||||||
|
private final int x;
|
||||||
|
@Getter
|
||||||
|
private final int y;
|
||||||
|
@Getter
|
||||||
|
private final int z;
|
||||||
|
private final Block[][][] blocks = new Block[WORLD_CHUNK_SIZE][WORLD_CHUNK_SIZE][WORLD_CHUNK_SIZE];
|
||||||
|
private final transient List<Block> modifiedBlocks = new LinkedList<>();
|
||||||
|
private final transient Region region;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBlockType(int x, int y, int z) {
|
||||||
|
return blocks[x][y][z].getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlockType(int x, int y, int z, int type) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBlockMetadata(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlockMetadata(int x, int y, int z, int metadata) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBlockLight(int x, int y, int z) {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlockLight(int x, int y, int z, int lightLevel) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSkyLight(int x, int y, int z) {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSkyLight(int x, int y, int z, int lightLevel) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAddition(int x, int y, int z) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAddition(int x, int y, int z, int value) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Biome getBiome(int x, int z) {
|
||||||
|
return region.getBiomeAt(x + this.x * WORLD_CHUNK_SIZE,z + this.z * WORLD_CHUNK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBiome(int x, int z, Biome biome) {
|
||||||
|
region.setBiome(x + this.x * WORLD_CHUNK_SIZE,z + this.z * WORLD_CHUNK_SIZE, biome);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Block[] getModifiedBlocks() {
|
||||||
|
return modifiedBlocks.toArray(new Block[modifiedBlocks.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlock(int x, int y, int z, Block block) {
|
||||||
|
blocks[x][y][z] = block;
|
||||||
|
modifiedBlocks.add(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Block getBlock(int x, int y, int z) {
|
||||||
|
Block block = blocks[x][y][z];
|
||||||
|
if (block == null) {
|
||||||
|
return Block.airBlock(x, y, z);
|
||||||
|
}
|
||||||
|
return blocks[x][y][z];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -115,9 +115,9 @@ public class ChunkProxy implements Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Block[] getNotAirBlocks() {
|
public Block[] getModifiedBlocks() {
|
||||||
use();
|
use();
|
||||||
return chunk.getNotAirBlocks();
|
return chunk.getModifiedBlocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -125,4 +125,10 @@ public class ChunkProxy implements Chunk {
|
|||||||
use();
|
use();
|
||||||
chunk.setBlock(x, y, z, block);
|
chunk.setBlock(x, y, z, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Block getBlock(int x, int y, int z) {
|
||||||
|
use();
|
||||||
|
return chunk.getBlock(x, y, z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,21 @@ package mc.world.generated_world.region;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import mc.core.serialization.Serializer;
|
||||||
import mc.core.world.*;
|
import mc.core.world.*;
|
||||||
|
import mc.world.generated_world.InMemoryCacheChunkLoader;
|
||||||
|
import mc.world.generated_world.chunk.ChunkImpl;
|
||||||
import mc.world.generated_world.chunk.ChunkProxy;
|
import mc.world.generated_world.chunk.ChunkProxy;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
|
import static mc.world.generated_world.WorldConstants.*;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class RegionImpl implements Region{
|
public class RegionImpl implements Region{
|
||||||
@@ -17,10 +24,10 @@ public class RegionImpl implements Region{
|
|||||||
private final int x;
|
private final int x;
|
||||||
@Getter
|
@Getter
|
||||||
private final int z;
|
private final int z;
|
||||||
private final ChunkProxy[][][] chunks = new ChunkProxy[16][16][16];
|
private final ChunkProxy[][][] chunks = new ChunkProxy[WORLD_REGION_SIZE/WORLD_CHUNK_SIZE][WORLD_REGION_SIZE/WORLD_CHUNK_SIZE][WORLD_REGION_SIZE/WORLD_CHUNK_SIZE];
|
||||||
private final Biome[][] biomes = new Biome[16][16];
|
private final Biome[][] biomes = new Biome[WORLD_REGION_SIZE][WORLD_REGION_SIZE];
|
||||||
@Getter@Setter
|
@Getter
|
||||||
private transient World world;
|
private final transient World world;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ChunkLoader chunkLoader;
|
private ChunkLoader chunkLoader;
|
||||||
|
|
||||||
@@ -29,9 +36,12 @@ public class RegionImpl implements Region{
|
|||||||
if (x < 0 || y < 0 || z < 0 || x >= 16 || y >= 16 || z >= 16) {
|
if (x < 0 || y < 0 || z < 0 || x >= 16 || y >= 16 || z >= 16) {
|
||||||
throw new RuntimeException(MessageFormat.format("Invalid chunk coordinates [{0} {1} {2}]", x, y, z));
|
throw new RuntimeException(MessageFormat.format("Invalid chunk coordinates [{0} {1} {2}]", x, y, z));
|
||||||
}
|
}
|
||||||
|
if (chunkLoader == null) {
|
||||||
|
chunkLoader = new InMemoryCacheChunkLoader(world);
|
||||||
|
}
|
||||||
Chunk chunk = chunks[x][y][z];
|
Chunk chunk = chunks[x][y][z];
|
||||||
if (chunk == null) {
|
if (chunk == null) {
|
||||||
chunk = chunkLoader.loadOrGenerateChunk(x, y, z);
|
chunk = chunkLoader.loadChunk(x + this.x * WORLD_REGION_SIZE, y, this.z * WORLD_REGION_SIZE).orElse(new ChunkImpl(x, y, z, this));
|
||||||
chunks[x][y][z] = new ChunkProxy(chunk);
|
chunks[x][y][z] = new ChunkProxy(chunk);
|
||||||
}
|
}
|
||||||
return chunk;
|
return chunk;
|
||||||
@@ -60,4 +70,30 @@ public class RegionImpl implements Region{
|
|||||||
}
|
}
|
||||||
biomes[x][z] = biome;
|
biomes[x][z] = biome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(Serializer<Chunk> chunkSerializer, Serializer<Region> regionSerializer) 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);
|
||||||
|
}
|
||||||
|
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++) {
|
||||||
|
File chunkFile = new File(regionFile, MessageFormat.format(CHUNK_FILE_NAME_TEMPLATE, x, y, z));
|
||||||
|
byte[] chunkBytes = chunkSerializer.serialize(this.getChunkAt(x, y, z));
|
||||||
|
try (FileOutputStream fileOutputStream = new FileOutputStream(chunkFile)){
|
||||||
|
fileOutputStream.write(chunkBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
generated_world/src/main/resources/log4j2.xml
Normal file
18
generated_world/src/main/resources/log4j2.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration>
|
||||||
|
<Appenders>
|
||||||
|
<Console name="Console" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss}] [%-5p] %m%n"/>
|
||||||
|
</Console>
|
||||||
|
<RollingFile name="File" fileName="log/log_file.log" filePattern="log/log_file-%d{MM-dd-yyyy}.log.gz">
|
||||||
|
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/>
|
||||||
|
<TimeBasedTriggeringPolicy />
|
||||||
|
</RollingFile>
|
||||||
|
</Appenders>
|
||||||
|
<Loggers>
|
||||||
|
<Root level="Trace">
|
||||||
|
<AppenderRef ref="Console"/>
|
||||||
|
<AppenderRef ref="File"/>
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
||||||
Reference in New Issue
Block a user