Merge branch 'develop' into dmitriymx/location-refactory
# Conflicts: # core/src/main/java/mc/core/Location.java # flat_world/src/main/java/mc/world/flat/FlatWorld.java # generated_world/src/main/java/mc/world/generated_world/world/CubicWorld.java # proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java
This commit is contained in:
@@ -1,77 +1,61 @@
|
||||
package mc.world.generated_world.chunk;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import mc.core.world.block.Block;
|
||||
import mc.core.world.block.BlockFactory;
|
||||
import mc.core.world.block.BlockType;
|
||||
import mc.core.world.Biome;
|
||||
import mc.core.world.chunk.Chunk;
|
||||
import mc.core.exception.ResourceUnloadedException;
|
||||
import mc.core.world.ChunkSection;
|
||||
import mc.core.world.Region;
|
||||
import mc.core.world.World;
|
||||
import mc.core.world.chunk.Chunk;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import static mc.world.generated_world.WorldConstants.WORLD_CHUNK_SIZE;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ChunkImpl implements Chunk{
|
||||
private static final BlockFactory blockFactory = new BlockFactory();
|
||||
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 Region region;
|
||||
private Reference<Region> regionReference;
|
||||
private ChunkSection[] sections = new ChunkSection[WORLD_CHUNK_SIZE];
|
||||
|
||||
public ChunkImpl (int x, int z, Region region) {
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
this.regionReference = new WeakReference<>(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getBlock(int x, int y, int z) {
|
||||
Block block = blocks[x][y][z];
|
||||
if (block == null) {
|
||||
block = blockFactory.create(BlockType.AIR, 0, x, y, z);
|
||||
public World getWorld() {
|
||||
Region region = getRegion();
|
||||
if (region == null) {
|
||||
throw new ResourceUnloadedException("Region is unloaded");
|
||||
}
|
||||
block.setLight(15);
|
||||
return block;
|
||||
return region.getWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(Block block) {
|
||||
if (block.getBlockType() == BlockType.AIR) {
|
||||
blocks[block.getLocation().getBlockX()]
|
||||
[block.getLocation().getBlockY()]
|
||||
[block.getLocation().getBlockZ()] = null;
|
||||
public ChunkSection getChunkSection(int height) {
|
||||
return sections[height];
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkSection setChunkSection(int height, ChunkSection chunkSection) {
|
||||
sections[height] = chunkSection;
|
||||
return chunkSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region getRegion() {
|
||||
if (regionReference == null) {
|
||||
return null;
|
||||
}
|
||||
blocks[block.getLocation().getBlockX()]
|
||||
[block.getLocation().getBlockY()]
|
||||
[block.getLocation().getBlockZ()] = block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 15;
|
||||
}
|
||||
if (regionReference.get() == null) {
|
||||
throw new ResourceUnloadedException("Region is unloaded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkyLight(int x, int y, int z, int lightLevel) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAddition(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAddition(int x, int y, int z, int value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@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);
|
||||
return regionReference.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package mc.world.generated_world.chunk;
|
||||
|
||||
import lombok.Getter;
|
||||
import mc.core.exception.ResourceUnloadedException;
|
||||
import mc.core.world.Biome;
|
||||
import mc.core.world.ChunkSection;
|
||||
import mc.core.world.Region;
|
||||
import mc.core.world.World;
|
||||
import mc.core.world.block.Block;
|
||||
import mc.core.world.block.BlockFactory;
|
||||
import mc.core.world.block.BlockType;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import static mc.world.generated_world.WorldConstants.WORLD_CHUNK_SIZE;
|
||||
|
||||
public class ChunkSectionImpl implements ChunkSection {
|
||||
@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 Reference<Region> region;
|
||||
private BlockFactory blockFactory = new BlockFactory();
|
||||
|
||||
public ChunkSectionImpl(int x, int y, int z, Region region) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.region = new WeakReference<>(region);
|
||||
}
|
||||
|
||||
@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 getRegion().getBiomeAt(x + this.x * WORLD_CHUNK_SIZE,z + this.z * WORLD_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiome(int x, int z, Biome biome) {
|
||||
getRegion().setBiome(x + this.x * WORLD_CHUNK_SIZE,z + this.z * WORLD_CHUNK_SIZE, biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(Block block) {
|
||||
if (block.getBlockType() == BlockType.AIR) {
|
||||
blocks[block.getLocation().getBlockX()][block.getLocation().getBlockY()][block.getLocation().getBlockZ()] = null;
|
||||
return;
|
||||
}
|
||||
blocks[block.getLocation().getBlockX()][block.getLocation().getBlockY()][block.getLocation().getBlockZ()] = block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getBlock(int x, int y, int z) {
|
||||
Block block = blocks[x][y][z];
|
||||
if (block == null) {
|
||||
return blockFactory.create(BlockType.AIR, 0, x, y, z);
|
||||
}
|
||||
return blocks[x][y][z];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region getRegion() {
|
||||
if (region == null) {
|
||||
return null;
|
||||
}
|
||||
if (region.get() == null) {
|
||||
throw new ResourceUnloadedException("Region is unloaded");
|
||||
}
|
||||
return region.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return getRegion().getWorld();
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
package mc.world.generated_world.chunk;
|
||||
|
||||
import mc.core.world.ChunkSection;
|
||||
import mc.core.world.Region;
|
||||
import mc.core.world.World;
|
||||
import mc.core.world.block.Block;
|
||||
import mc.core.world.Biome;
|
||||
import mc.core.world.chunk.Chunk;
|
||||
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
public class ChunkProxy implements Chunk {
|
||||
private final Chunk chunk;
|
||||
public class ChunkSectionProxy implements ChunkSection {
|
||||
private final ChunkSection chunk;
|
||||
private volatile transient long lastUsage = System.currentTimeMillis();
|
||||
private final transient ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||
|
||||
public ChunkProxy(Chunk chunk) {
|
||||
public ChunkSectionProxy(ChunkSection chunk) {
|
||||
this.chunk = chunk;
|
||||
}
|
||||
|
||||
@@ -34,6 +36,16 @@ public class ChunkProxy implements Chunk {
|
||||
return chunk.getBlock(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region getRegion() {
|
||||
return chunk.getRegion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return chunk.getWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(Block block) {
|
||||
use();
|
||||
@@ -27,7 +27,7 @@ public class InMemoryCacheChunkLoader implements ChunkLoader {
|
||||
@Autowired
|
||||
private ChunkReader chunkReader;
|
||||
@Autowired
|
||||
private Serializer<Chunk> chunkSerializer;
|
||||
private Serializer<ChunkSection> chunkSerializer;
|
||||
@Autowired
|
||||
private RegionReaderWriter regionReaderWritter;
|
||||
|
||||
@@ -46,14 +46,14 @@ public class InMemoryCacheChunkLoader implements ChunkLoader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Chunk> loadChunk(int x, int y, int z) {
|
||||
public Optional<ChunkSection> loadChunk(int x, int y, int z) {
|
||||
File file = getChuckFile(x, y, z);
|
||||
if (!file.exists()) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
try {
|
||||
Chunk chunk = chunkReader.read(world.getRegion(x / WORLD_CHUNK_SIZE, z / WORLD_CHUNK_SIZE), x, y, z);
|
||||
return Optional.of(chunk);
|
||||
ChunkSection chunkSection = chunkReader.read(world.getRegion(x / WORLD_CHUNK_SIZE, z / WORLD_CHUNK_SIZE), x, y, z);
|
||||
return Optional.of(chunkSection);
|
||||
} catch (IOException e) {
|
||||
log.error("Error occurred while reading chunk file: " + file.getAbsolutePath(), e);
|
||||
return Optional.empty();
|
||||
@@ -62,12 +62,12 @@ public class InMemoryCacheChunkLoader implements ChunkLoader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk loadOrGenerateChunk(int x, int y, int z) {
|
||||
public ChunkSection loadOrGenerateChunk(int x, int y, int z) {
|
||||
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;
|
||||
ChunkSection chunkSection;
|
||||
if (!regionFile.exists()) {
|
||||
log.debug("Region [{}, {}] not found. Generating!", regX, regZ);
|
||||
regionFile.mkdirs();
|
||||
@@ -78,17 +78,17 @@ public class InMemoryCacheChunkLoader implements ChunkLoader {
|
||||
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);
|
||||
chunkSection = region.getChunkAt(x % WORLD_CHUNK_SIZE, y % WORLD_CHUNK_SIZE, z % WORLD_CHUNK_SIZE);
|
||||
} else {
|
||||
try {
|
||||
region = regionReaderWritter.read(regX, regZ, world);
|
||||
chunk = chunkReader.read(region, x, y, z);
|
||||
chunkSection = chunkReader.read(region, x, y, z);
|
||||
} catch (IOException e) {
|
||||
log.error("Error occurred while reading chunk file", e);
|
||||
log.error("Error occurred while reading chunkSection file", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return chunk;
|
||||
return chunkSection;
|
||||
}
|
||||
|
||||
private void saveRegion (Region region) {
|
||||
|
||||
@@ -5,7 +5,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import mc.core.world.block.BlockFactory;
|
||||
import mc.core.world.block.BlockType;
|
||||
import mc.core.world.*;
|
||||
import mc.core.world.chunk.Chunk;
|
||||
import mc.world.generated_world.region.RegionImpl;
|
||||
import mc.world.generated_world.world.CubicWorld;
|
||||
import mc.world.generated_world.world.Temperature;
|
||||
@@ -226,7 +225,7 @@ public class SeedBasedWorldGenerator implements WorldGenerator {
|
||||
region.setBiome(x, z, biomes[x][z]);
|
||||
if (heightMap[x][z] < WORLD_SEA_LEVEL) {
|
||||
for (int y = 0; y < WORLD_SEA_LEVEL; y ++) {
|
||||
Chunk chunk = region.getChunkAt(x / 16, y / 16, z / 16);
|
||||
ChunkSection chunk = region.getChunkAt(x / 16, y / 16, z / 16);
|
||||
if (y == 0) {
|
||||
chunk.setBlock(blockFactory.create(BlockType.BEDROCK, 0, x % 16, y % 16, z % 16));
|
||||
continue;
|
||||
@@ -243,7 +242,7 @@ public class SeedBasedWorldGenerator implements WorldGenerator {
|
||||
}
|
||||
} else {
|
||||
for (int y = 0; y < heightMap[x][z]; y++) {
|
||||
Chunk chunk = region.getChunkAt(x / 16, y / 16, z / 16);
|
||||
ChunkSection chunk = region.getChunkAt(x / 16, y / 16, z / 16);
|
||||
if (y == 0) {
|
||||
chunk.setBlock(blockFactory.create(BlockType.BEDROCK, 0, x % 16, y % 16, z % 16));
|
||||
continue;
|
||||
|
||||
@@ -1,61 +1,90 @@
|
||||
package mc.world.generated_world.region;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import mc.core.exception.ResourceUnloadedException;
|
||||
import mc.core.serialization.IRegionReaderWriter;
|
||||
import mc.core.serialization.Serializer;
|
||||
import mc.core.world.*;
|
||||
import mc.core.world.chunk.Chunk;
|
||||
import mc.core.world.chunk.ChunkLoader;
|
||||
import mc.world.generated_world.chunk.ChunkSectionProxy;
|
||||
import mc.world.generated_world.chunk.InMemoryCacheChunkLoader;
|
||||
import mc.world.generated_world.chunk.ChunkImpl;
|
||||
import mc.world.generated_world.chunk.ChunkProxy;
|
||||
import mc.world.generated_world.chunk.ChunkSectionImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import static mc.world.generated_world.WorldConstants.*;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class RegionImpl implements Region{
|
||||
@Getter
|
||||
private final int x;
|
||||
@Getter
|
||||
private final int z;
|
||||
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 ChunkSection[][][] chunkSectionProxies = new ChunkSectionProxy[WORLD_REGION_SIZE/WORLD_CHUNK_SIZE][WORLD_REGION_SIZE/WORLD_CHUNK_SIZE][WORLD_REGION_SIZE/WORLD_CHUNK_SIZE];
|
||||
private final Biome[][] biomes = new Biome[WORLD_REGION_SIZE][WORLD_REGION_SIZE];
|
||||
@Getter
|
||||
private final transient World world;
|
||||
private final transient Reference<World> world;
|
||||
private final Chunk[][] chunks = new Chunk[WORLD_REGION_SIZE/WORLD_CHUNK_SIZE][WORLD_REGION_SIZE/WORLD_CHUNK_SIZE];
|
||||
@Autowired
|
||||
private ChunkLoader chunkLoader;
|
||||
|
||||
public RegionImpl (int x, int z, World world) {
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
this.world = new WeakReference<>(world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk getChunkAt(int x, int y, int z) {
|
||||
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));
|
||||
public Chunk getChunk(int x, int z) {
|
||||
if (x < 0 || z < 0 || x >= 16 || z >= 16) {
|
||||
throw new RuntimeException(MessageFormat.format("Invalid chunk coordinates [{0} {1}]", x, z));
|
||||
}
|
||||
if (chunkLoader == null) {
|
||||
chunkLoader = new InMemoryCacheChunkLoader(world);
|
||||
}
|
||||
Chunk chunk = chunks[x][y][z];
|
||||
|
||||
Chunk chunk = chunks[x][z];
|
||||
if (chunk == null) {
|
||||
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);
|
||||
chunk = new ChunkImpl(x, z, this);
|
||||
for (int y = 0; y < WORLD_CHUNK_SIZE; y ++) {
|
||||
chunk.setChunkSection(y, getChunkAt(x, y, z));
|
||||
}
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChunk(int x, int y, int z, Chunk chunk) {
|
||||
public void setChunk(int x, int z, Chunk chunk) {
|
||||
chunks[x][z] = chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkSection getChunkAt(int x, int y, int z) {
|
||||
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 chunkSection coordinates [{0} {1} {2}]", x, y, z));
|
||||
}
|
||||
chunks[x][y][z] = new ChunkProxy(chunk);
|
||||
if (chunkLoader == null) {
|
||||
chunkLoader = new InMemoryCacheChunkLoader(getWorld());
|
||||
}
|
||||
ChunkSection chunkSection = chunkSectionProxies[x][y][z];
|
||||
if (chunkSection == null) {
|
||||
chunkSection = chunkLoader.loadChunk(x + this.x * WORLD_REGION_SIZE, y, this.z * WORLD_REGION_SIZE).orElse(new ChunkSectionImpl(x, y, z, this));
|
||||
chunkSectionProxies[x][y][z] = new ChunkSectionProxy(chunkSection);
|
||||
}
|
||||
return chunkSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChunk(int x, int y, int z, ChunkSection chunkSection) {
|
||||
if (x < 0 || y < 0 || z < 0 || x >= 16 || y >= 16 || z >= 16) {
|
||||
throw new RuntimeException(MessageFormat.format("Invalid chunkSection coordinates [{0} {1} {2}]", x, y, z));
|
||||
}
|
||||
chunkSectionProxies[x][y][z] = new ChunkSectionProxy(chunkSection);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,9 +104,20 @@ public class RegionImpl implements Region{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Serializer<Chunk> chunkSerializer, IRegionReaderWriter regionReaderWriter) throws IOException {
|
||||
public World getWorld() {
|
||||
if (world == null) {
|
||||
return null;
|
||||
}
|
||||
if (world.get() == null) {
|
||||
throw new ResourceUnloadedException("World is unloaded");
|
||||
}
|
||||
return world.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Serializer<ChunkSection> chunkSerializer, IRegionReaderWriter regionReaderWriter) throws IOException {
|
||||
String worldPath = System.getProperty("worlds.folder", "worlds");
|
||||
File worldFile = new File(worldPath, world.getWorldId().toString());
|
||||
File worldFile = new File(worldPath, getWorld().getWorldId().toString());
|
||||
File regionFile = new File(worldFile, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, this.getX(), this.getZ()));
|
||||
if (!regionFile.exists()) {
|
||||
regionFile.mkdirs();
|
||||
@@ -86,8 +126,8 @@ public class RegionImpl implements Region{
|
||||
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++) {
|
||||
Chunk chunk = this.getChunkAt(x, y, z);
|
||||
byte[] chunkBytes = chunkSerializer.serialize(chunk);
|
||||
ChunkSection chunkSection = this.getChunkAt(x, y, z);
|
||||
byte[] chunkBytes = chunkSerializer.serialize(chunkSection);
|
||||
if (chunkBytes.length > 0) {
|
||||
File chunkFile = new File(regionFile, MessageFormat.format(CHUNK_FILE_NAME_TEMPLATE, x, y, z));
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(chunkFile)) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import mc.core.world.block.BlockFactory;
|
||||
import mc.core.world.block.BlockType;
|
||||
import mc.core.serialization.Deserializer;
|
||||
import mc.core.serialization.Serializer;
|
||||
import mc.core.world.chunk.Chunk;
|
||||
import mc.core.world.ChunkSection;
|
||||
|
||||
/**
|
||||
* Prototype
|
||||
@@ -13,20 +13,20 @@ import mc.core.world.chunk.Chunk;
|
||||
public class BlockSerializerDeserializer implements Serializer<Block>, Deserializer<Block> {
|
||||
|
||||
private BlockFactory blockFactory;
|
||||
private Chunk chunk;
|
||||
private ChunkSection chunkSection;
|
||||
|
||||
public BlockSerializerDeserializer(BlockFactory blockFactory, Chunk chunk) {
|
||||
public BlockSerializerDeserializer(BlockFactory blockFactory, ChunkSection chunkSection) {
|
||||
this.blockFactory = blockFactory;
|
||||
this.chunk = chunk;
|
||||
this.chunkSection = chunkSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block deserialize(byte[] bytes) {
|
||||
int id = bytes[0] + 128;
|
||||
int meta = bytes[1] >> 4;
|
||||
int x = (bytes[1] & 0xf) + chunk.getX() * 16;
|
||||
int y = bytes[2] >> 4 + chunk.getY() * 16;
|
||||
int z = (bytes[2] & 0xf) + chunk.getZ() * 16;
|
||||
int x = (bytes[1] & 0xf) + chunkSection.getX() * 16;
|
||||
int y = bytes[2] >> 4 + chunkSection.getY() * 16;
|
||||
int z = (bytes[2] & 0xf) + chunkSection.getZ() * 16;
|
||||
BlockType type = BlockType.values()[id];
|
||||
Block block = blockFactory.create(type, meta);
|
||||
block.getLocation().setX(x);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package mc.world.generated_world.serialization;
|
||||
|
||||
import mc.core.Location;
|
||||
import mc.core.world.block.Block;
|
||||
import mc.core.serialization.Deserializer;
|
||||
import mc.core.serialization.IChunkReader;
|
||||
import mc.core.world.chunk.Chunk;
|
||||
import mc.core.world.ChunkSection;
|
||||
import mc.core.world.Region;
|
||||
import mc.world.generated_world.chunk.ChunkImpl;
|
||||
import mc.world.generated_world.chunk.ChunkSectionImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.File;
|
||||
@@ -26,22 +27,22 @@ public class ChunkReader implements IChunkReader{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk read (Region region, int x, int y, int z) throws IOException {
|
||||
public ChunkSection 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);
|
||||
ChunkSection chunkSection = new ChunkSectionImpl(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);
|
||||
chunk.setBlock(block);
|
||||
chunkSection.setBlock(block);
|
||||
}
|
||||
return chunk;
|
||||
return chunkSection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import mc.core.world.block.Block;
|
||||
import mc.core.world.block.BlockFactory;
|
||||
import mc.core.world.block.BlockType;
|
||||
import mc.core.serialization.Serializer;
|
||||
import mc.core.world.chunk.Chunk;
|
||||
import mc.core.world.ChunkSection;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -14,20 +14,20 @@ import java.io.IOException;
|
||||
import static mc.world.generated_world.WorldConstants.WORLD_CHUNK_SIZE;
|
||||
|
||||
@Slf4j
|
||||
public class ChunkSerializer implements Serializer<Chunk> {
|
||||
public class ChunkSerializer implements Serializer<ChunkSection> {
|
||||
|
||||
@Autowired
|
||||
private Serializer<Block> blockSerializer;
|
||||
|
||||
@Override
|
||||
public byte[] serialize(Chunk chunk) {
|
||||
Serializer<Block> blockSerializer = new BlockSerializerDeserializer(new BlockFactory(), chunk);
|
||||
public byte[] serialize(ChunkSection chunkSection) {
|
||||
Serializer<Block> blockSerializer = new BlockSerializerDeserializer(new BlockFactory(), chunkSection);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
Block current;
|
||||
for (int x = 0; x < WORLD_CHUNK_SIZE; x ++) {
|
||||
for (int y = 0; y < WORLD_CHUNK_SIZE; y ++) {
|
||||
for (int z = 0; z < WORLD_CHUNK_SIZE; z ++) {
|
||||
current = chunk.getBlock(x, y, z);
|
||||
current = chunkSection.getBlock(x, y, z);
|
||||
if (current != null && current.getBlockType() != BlockType.AIR) {
|
||||
try {
|
||||
baos.write(blockSerializer.serialize(current));
|
||||
|
||||
@@ -4,32 +4,58 @@ import com.flowpowered.nbt.Tag;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import mc.core.Direction;
|
||||
import mc.core.EntityLocation;
|
||||
import mc.core.world.IWorldType;
|
||||
import mc.core.world.Region;
|
||||
import mc.core.world.World;
|
||||
import mc.core.world.block.BlockType;
|
||||
import mc.core.world.chunk.Chunk;
|
||||
import mc.core.world.*;
|
||||
import mc.world.generated_world.serialization.RegionReaderWriter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static mc.world.generated_world.WorldConstants.WORLD_CHUNK_SIZE;
|
||||
import static mc.world.generated_world.WorldConstants.WORLD_MAX_HEIGHT;
|
||||
import static mc.world.generated_world.WorldConstants.REGION_FILE_NAME_TEMPLATE;
|
||||
|
||||
/*
|
||||
* NORTH
|
||||
*
|
||||
* EAST WEST
|
||||
*
|
||||
* SOUTH
|
||||
*
|
||||
* + ----> X
|
||||
* |
|
||||
* |
|
||||
* |
|
||||
* V Z
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
public class CubicWorld implements World {
|
||||
private int pointX = -1;
|
||||
private int pointZ = -1;
|
||||
private int sizeX = 2;
|
||||
private int sizeZ = 2;
|
||||
private Region[][] regions = new Region[sizeX][sizeZ];
|
||||
private final Lock regionSaveLock = new ReentrantLock();
|
||||
@Autowired
|
||||
private RegionReaderWriter regionReaderWriter;
|
||||
@Autowired
|
||||
private WorldGenerator worldGenerator;
|
||||
@Setter
|
||||
private boolean autoSaveRegionAfterGenerating = true;
|
||||
@Getter
|
||||
private final UUID worldId;
|
||||
private final int seed;
|
||||
private volatile EntityLocation warpPosition;
|
||||
private volatile EntityLocation spawn;
|
||||
private final transient Object spawnLocationLock = new Object();
|
||||
private final Map<String, Tag<?>> nbtTagMap = new HashMap<>();
|
||||
@Autowired
|
||||
private RegionManager regionManager;
|
||||
@Getter@Setter
|
||||
private String name;
|
||||
|
||||
@@ -55,55 +81,76 @@ public class CubicWorld implements World {
|
||||
|
||||
@Override
|
||||
public IWorldType getWorldType() {
|
||||
return null;
|
||||
return null; //FIXME
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityLocation getSpawn() {
|
||||
if (warpPosition == null) {
|
||||
synchronized (spawnLocationLock) {
|
||||
if (warpPosition == null) {
|
||||
log.warn("Spawn location is not defined. Trying to select best location");
|
||||
warpPosition = new EntityLocation(0d, 10d, 0d, 0f, 0f, this);
|
||||
for (int y = WORLD_MAX_HEIGHT; y > 0; y --) {
|
||||
Chunk chunk = getChunk(0,y / WORLD_CHUNK_SIZE, 0);
|
||||
if (chunk.getBlock(0, y, 0).getBlockType() != BlockType.AIR) {
|
||||
warpPosition = new EntityLocation(0d, y + 1d, 0d, 0f, 0f, this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
warpPosition = new EntityLocation(0d, 10d, 0d, 0f, 0f, this);
|
||||
}
|
||||
}
|
||||
/* FIXME */
|
||||
if (spawn == null) {
|
||||
log.warn("Spawn is not defined! Set default spawn: [8, 128, 8]");
|
||||
setSpawn(new EntityLocation(8d, 128d, 8d, 0f, 0f, this));
|
||||
}
|
||||
return warpPosition;
|
||||
return spawn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpawn(EntityLocation warpPosition) {
|
||||
public void setSpawn(EntityLocation entityLocation) {
|
||||
synchronized (spawnLocationLock) {
|
||||
this.warpPosition = warpPosition;
|
||||
entityLocation.setWorld(this);
|
||||
this.spawn = entityLocation;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk getChunk(int x, int y, int z) {
|
||||
return null;
|
||||
public ChunkSection getChunk(int x, int y, int z) {
|
||||
Region region = getRegion(x / 16, z / 16);
|
||||
return region.getChunkAt(x % 16, y % 16, z % 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChunk(int x, int y, int z, Chunk chunk) {
|
||||
|
||||
public void setChunk(int x, int y, int z, ChunkSection chunkSection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region getRegion(int x, int z) {
|
||||
return null;
|
||||
checkCoordsInCache(x, z);
|
||||
Region region;
|
||||
if (regions[x - pointX][z - pointZ] == null) {
|
||||
File file = new File(new File("worlds", this.getWorldId().toString()), MessageFormat.format(REGION_FILE_NAME_TEMPLATE, x, z));
|
||||
if (!file.exists()) {
|
||||
region = worldGenerator.generateRegion(x, z, this);
|
||||
if (autoSaveRegionAfterGenerating) {
|
||||
try {
|
||||
regionReaderWriter.write(region);
|
||||
} catch (IOException e) {
|
||||
log.error("Error occurred while saving region data");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
region = regionReaderWriter.read(x, z, this);
|
||||
} catch (IOException e) {
|
||||
log.error("Error occurred while loading region");
|
||||
region = null;
|
||||
}
|
||||
}
|
||||
setRegion(region.getX(), region.getZ(), region);
|
||||
} else {
|
||||
region = regions[x - pointX][z - pointZ];
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegion(int x, int z, Region region) {
|
||||
|
||||
try {
|
||||
regionSaveLock.lock();
|
||||
regions[x - pointX][z - pointZ] = region;
|
||||
} finally {
|
||||
regionSaveLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,4 +172,53 @@ public class CubicWorld implements World {
|
||||
public Stream<Tag<?>> tagStream() {
|
||||
return nbtTagMap.values().stream();
|
||||
}
|
||||
|
||||
private void checkCoordsInCache (int x, int z) {
|
||||
if (x < pointX) {
|
||||
addLines(Direction.EAST, pointX - x);
|
||||
} else if (x > pointX + sizeX) {
|
||||
addLines(Direction.WEST, x - (pointX + sizeX));
|
||||
} else if (z < pointZ) {
|
||||
addLines(Direction.NORTH, pointZ - z);
|
||||
} else if (z > pointZ + sizeZ) {
|
||||
addLines(Direction.SOUTH, z - (pointZ + sizeZ));
|
||||
}
|
||||
}
|
||||
|
||||
private void addLines (Direction direction, int amount) {
|
||||
int addBeforeX = 0;
|
||||
int addAfterX = 0;
|
||||
int addBeforeZ = 0;
|
||||
int addAfterZ = 0;
|
||||
switch (direction) {
|
||||
case NORTH:
|
||||
addBeforeZ = amount;
|
||||
break;
|
||||
case EAST:
|
||||
addBeforeX = amount;
|
||||
break;
|
||||
case WEST:
|
||||
addAfterX = amount;
|
||||
break;
|
||||
case SOUTH:
|
||||
addAfterZ = amount;
|
||||
break;
|
||||
}
|
||||
try {
|
||||
regionSaveLock.lock();
|
||||
int tempSizeX = sizeX + addAfterX + addBeforeX;
|
||||
int tempSizeZ = sizeZ + addAfterZ + addBeforeZ;
|
||||
Region[][] temp = new Region[tempSizeX][tempSizeZ];
|
||||
for (int x = 0; x < sizeX; x ++) {
|
||||
System.arraycopy(regions[x], 0, temp[x + addBeforeX], addBeforeZ, sizeZ);
|
||||
}
|
||||
|
||||
this.sizeX = tempSizeX;
|
||||
this.sizeZ = tempSizeZ;
|
||||
this.pointX = pointX - addBeforeX;
|
||||
this.pointZ = pointZ - addBeforeZ;
|
||||
} finally {
|
||||
regionSaveLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
package mc.world.generated_world.world;
|
||||
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import mc.core.Direction;
|
||||
import mc.core.world.Region;
|
||||
import mc.core.world.World;
|
||||
import mc.core.world.WorldGenerator;
|
||||
import mc.world.generated_world.serialization.RegionReaderWriter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static mc.world.generated_world.WorldConstants.REGION_FILE_NAME_TEMPLATE;
|
||||
|
||||
/*
|
||||
* NORTH
|
||||
*
|
||||
* EAST WEST
|
||||
*
|
||||
* SOUTH
|
||||
*
|
||||
* + ----> X
|
||||
* |
|
||||
* |
|
||||
* |
|
||||
* V Z
|
||||
*/
|
||||
@Slf4j
|
||||
public class RegionManager {
|
||||
private final World world;
|
||||
private int pointX = -1;
|
||||
private int pointZ = -1;
|
||||
private int sizeX = 2;
|
||||
private int sizeZ = 2;
|
||||
private Region[][] regions = new Region[sizeX][sizeZ];
|
||||
private final Lock regionSaveLock = new ReentrantLock();
|
||||
@Autowired
|
||||
private RegionReaderWriter regionReaderWriter;
|
||||
@Autowired
|
||||
private WorldGenerator worldGenerator;
|
||||
@Setter
|
||||
private boolean autoSaveRegionAfterGenerating = true;
|
||||
|
||||
|
||||
public RegionManager(World world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public void setRegion (Region region) {
|
||||
int x = region.getX();
|
||||
int z = region.getZ();
|
||||
|
||||
try {
|
||||
regionSaveLock.lock();
|
||||
regions[x - pointX][z - pointZ] = region;
|
||||
} finally {
|
||||
regionSaveLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCoordsInCache (int x, int z) {
|
||||
if (x < pointX) {
|
||||
addLines(Direction.EAST, pointX - x);
|
||||
} else if (x > pointX + sizeX) {
|
||||
addLines(Direction.WEST, x - (pointX + sizeX));
|
||||
} else if (z < pointZ) {
|
||||
addLines(Direction.NORTH, pointZ - z);
|
||||
} else if (z > pointZ + sizeZ) {
|
||||
addLines(Direction.SOUTH, z - (pointZ + sizeZ));
|
||||
}
|
||||
}
|
||||
|
||||
public Region getRegion (int x, int z) {
|
||||
checkCoordsInCache(x, z);
|
||||
Region region;
|
||||
if (regions[x - pointX][z - pointZ] == null) {
|
||||
File file = new File(new File("worlds", world.getWorldId().toString()), MessageFormat.format(REGION_FILE_NAME_TEMPLATE, x, z));
|
||||
if (!file.exists()) {
|
||||
region = worldGenerator.generateRegion(x, z, world);
|
||||
if (autoSaveRegionAfterGenerating) {
|
||||
try {
|
||||
regionReaderWriter.write(region);
|
||||
} catch (IOException e) {
|
||||
log.error("Error occurred while saving region data");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
region = regionReaderWriter.read(x, z, world);
|
||||
} catch (IOException e) {
|
||||
log.error("Error occurred while loading region");
|
||||
region = null;
|
||||
}
|
||||
}
|
||||
setRegion(region);
|
||||
} else {
|
||||
region = regions[x - pointX][z - pointZ];
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
private void addLines (Direction direction, int amount) {
|
||||
int addBeforeX = 0;
|
||||
int addAfterX = 0;
|
||||
int addBeforeZ = 0;
|
||||
int addAfterZ = 0;
|
||||
switch (direction) {
|
||||
case NORTH:
|
||||
addBeforeZ = amount;
|
||||
break;
|
||||
case EAST:
|
||||
addBeforeX = amount;
|
||||
break;
|
||||
case WEST:
|
||||
addAfterX = amount;
|
||||
break;
|
||||
case SOUTH:
|
||||
addAfterZ = amount;
|
||||
break;
|
||||
}
|
||||
try {
|
||||
int tempSizeX = sizeX + addAfterX + addBeforeX;
|
||||
int tempSizeZ = sizeZ + addAfterZ + addBeforeZ;
|
||||
Region[][] temp = new Region[tempSizeX][tempSizeZ];
|
||||
for (int x = 0; x < sizeX; x ++) {
|
||||
System.arraycopy(regions[x], 0, temp[x + addBeforeX], addBeforeZ, sizeZ);
|
||||
}
|
||||
|
||||
this.sizeX = tempSizeX;
|
||||
this.sizeZ = tempSizeZ;
|
||||
this.pointX = pointX - addBeforeX;
|
||||
this.pointZ = pointZ - addBeforeZ;
|
||||
} finally {
|
||||
regionSaveLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package mc.world.generated_world;
|
||||
|
||||
import mc.world.generated_world.generator.SeedRandomGenerator;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -9,6 +10,7 @@ import java.io.File;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@Ignore
|
||||
public class SeedRandomGeneratorTest {
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user