diff --git a/core/src/main/java/mc/core/Location.java b/core/src/main/java/mc/core/Location.java index 1171d68..9332165 100644 --- a/core/src/main/java/mc/core/Location.java +++ b/core/src/main/java/mc/core/Location.java @@ -7,6 +7,7 @@ package mc.core; import lombok.Data; import mc.core.exception.ResourceUnloadedException; import mc.core.world.Chunk; +import mc.core.world.Region; import mc.core.world.World; import sun.reflect.generics.reflectiveObjects.NotImplementedException; @@ -114,7 +115,8 @@ public class Location implements Serializable{ } public Chunk getChunk() { - throw new NotImplementedException(); + Region region = getWorld().getRegion((int) (x / 256), (int) (z / 256)); + return region.getChunk((int) ((x % 256) / 16), (int) ((z % 256) / 16)); } } diff --git a/core/src/main/java/mc/core/world/Region.java b/core/src/main/java/mc/core/world/Region.java index fc91637..7b57123 100644 --- a/core/src/main/java/mc/core/world/Region.java +++ b/core/src/main/java/mc/core/world/Region.java @@ -24,6 +24,11 @@ public interface Region extends Serializable{ Chunk getChunk (int x, int z); void setChunk(int x, int z, Chunk chunk); + @Deprecated + ChunkSection getChunkAt(int x, int y, int z); + @Deprecated + void setChunk(int x, int y, int z, ChunkSection chunkSection); + int getX(); int getZ(); diff --git a/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkImpl.java b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkImpl.java new file mode 100644 index 0000000..7954603 --- /dev/null +++ b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkImpl.java @@ -0,0 +1,61 @@ +package mc.world.generated_world.chunk; + +import lombok.Getter; +import mc.core.exception.ResourceUnloadedException; +import mc.core.world.Chunk; +import mc.core.world.ChunkSection; +import mc.core.world.Region; +import mc.core.world.World; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; + +import static mc.world.generated_world.WorldConstants.WORLD_CHUNK_SIZE; + +public class ChunkImpl implements Chunk { + @Getter + private final int x; + @Getter + private final int z; + private Reference 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 World getWorld() { + Region region = getRegion(); + if (region == null) { + throw new ResourceUnloadedException("Region is unloaded"); + } + return region.getWorld(); + } + + @Override + 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; + } + + if (regionReference.get() == null) { + throw new ResourceUnloadedException("Region is unloaded"); + } + + return regionReference.get(); + } +} diff --git a/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionImpl.java b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionImpl.java new file mode 100644 index 0000000..dce85eb --- /dev/null +++ b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionImpl.java @@ -0,0 +1,127 @@ +package mc.world.generated_world.chunk; + +import lombok.Getter; +import mc.core.block.Block; +import mc.core.block.BlockType; +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 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; + + 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 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 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(int x, int y, int z, Block block) { + if (block.getBlockType() == BlockType.AIR) { + blocks[x][y][z] = null; + return; + } + blocks[x][y][z] = 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]; + } + + @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(); + } +} diff --git a/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionProxy.java b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionProxy.java new file mode 100644 index 0000000..c945af8 --- /dev/null +++ b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionProxy.java @@ -0,0 +1,146 @@ +package mc.world.generated_world.chunk; + +import mc.core.block.Block; +import mc.core.world.Biome; +import mc.core.world.ChunkSection; +import mc.core.world.Region; +import mc.core.world.World; + +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class ChunkSectionProxy implements ChunkSection { + private final ChunkSection chunkSection; + private volatile transient long lastUsage = System.currentTimeMillis(); + private final transient ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + + public ChunkSectionProxy(ChunkSection chunkSection) { + this.chunkSection = chunkSection; + } + + public long getLastUsage() { + synchronized (chunkSection) { + return lastUsage; + } + } + + private final void use () { + synchronized (chunkSection) { + lastUsage = System.currentTimeMillis(); + } + } + + @Override + public int getBlockType(int x, int y, int z) { + use(); + return chunkSection.getBlockType(x, y, z); + } + + @Override + public void setBlockType(int x, int y, int z, int type) { + use(); + chunkSection.setBlockType(x, y, z, type); + } + + @Override + public int getBlockMetadata(int x, int y, int z) { + use(); + return chunkSection.getBlockMetadata(x, y, z); + } + + @Override + public void setBlockMetadata(int x, int y, int z, int metadata) { + use(); + chunkSection.setBlockMetadata(x, y, z, metadata); + } + + @Override + public int getBlockLight(int x, int y, int z) { + use(); + return chunkSection.getBlockLight(x, y, z); + } + + @Override + public void setBlockLight(int x, int y, int z, int lightLevel) { + use(); + chunkSection.setBlockLight(x, y, z, lightLevel); + } + + @Override + public int getSkyLight(int x, int y, int z) { + use(); + return chunkSection.getSkyLight(x, y, z); + } + + @Override + public void setSkyLight(int x, int y, int z, int lightLevel) { + use(); + chunkSection.setSkyLight(x, y, z, lightLevel); + } + + @Override + public int getAddition(int x, int y, int z) { + use(); + return chunkSection.getAddition(x, y, z); + } + + @Override + public void setAddition(int x, int y, int z, int value) { + use(); + chunkSection.setAddition(x, y, z, value); + } + + @Override + public Biome getBiome(int x, int z) { + use(); + return chunkSection.getBiome(x, z); + } + + @Override + public void setBiome(int x, int z, Biome biome) { + use(); + chunkSection.setBiome(x, z, biome); + } + + @Override + public int getX() { + use(); + return chunkSection.getX(); + } + + @Override + public int getY() { + use(); + return chunkSection.getY(); + } + + @Override + public int getZ() { + use(); + return chunkSection.getZ(); + } + + @Override + public void setBlock(int x, int y, int z, Block block) { + use(); + chunkSection.setBlock(x, y, z, block); + } + + @Override + public Block getBlock(int x, int y, int z) { + use(); + return chunkSection.getBlock(x, y, z); + } + + @Override + public Region getRegion() { + use(); + return chunkSection.getRegion(); + } + + @Override + public World getWorld() { + use(); + return chunkSection.getWorld(); + } +} 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 acd8e57..9f96d49 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 @@ -6,6 +6,7 @@ import mc.core.exception.ResourceUnloadedException; import mc.core.serialization.IRegionReaderWriter; import mc.core.serialization.Serializer; import mc.core.world.*; +import mc.world.generated_world.chunk.ChunkImpl; import mc.world.generated_world.chunk.ChunkSectionImpl; import mc.world.generated_world.chunk.ChunkSectionProxy; import mc.world.generated_world.chunk.InMemoryCacheChunkLoader; @@ -26,9 +27,10 @@ public class RegionImpl implements Region{ private final int x; @Getter private final int z; - private final ChunkSectionProxy[][][] chunks = new ChunkSectionProxy[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]; private final transient Reference world; + private final Chunk[][] chunks = new Chunk[WORLD_REGION_SIZE/WORLD_CHUNK_SIZE][WORLD_REGION_SIZE/WORLD_CHUNK_SIZE]; @Autowired private ChunkLoader chunkLoader; @@ -38,6 +40,27 @@ public class RegionImpl implements Region{ this.world = new WeakReference<>(world); } + @Override + 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)); + } + + Chunk chunk = chunks[x][z]; + if (chunk == null) { + 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 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) { @@ -46,10 +69,10 @@ public class RegionImpl implements Region{ if (chunkLoader == null) { chunkLoader = new InMemoryCacheChunkLoader(getWorld()); } - ChunkSection chunkSection = chunks[x][y][z]; + 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)); - chunks[x][y][z] = new ChunkSectionProxy(chunkSection); + chunkSectionProxies[x][y][z] = new ChunkSectionProxy(chunkSection); } return chunkSection; } @@ -59,7 +82,7 @@ public class RegionImpl implements Region{ 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)); } - chunks[x][y][z] = new ChunkSectionProxy(chunkSection); + chunkSectionProxies[x][y][z] = new ChunkSectionProxy(chunkSection); } @Override