diff --git a/core/src/main/java/mc/core/Location.java b/core/src/main/java/mc/core/Location.java index adee609..b97aa16 100644 --- a/core/src/main/java/mc/core/Location.java +++ b/core/src/main/java/mc/core/Location.java @@ -4,15 +4,20 @@ */ package mc.core; -import lombok.AllArgsConstructor; import lombok.Data; +import mc.core.exception.ResourceUnloadedException; +import mc.core.world.Region; +import mc.core.world.World; +import mc.core.world.chunk.Chunk; import java.io.Serializable; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; -@AllArgsConstructor @Data public class Location implements Serializable{ private double x, y, z; + private Reference world; private static int floor_double(double value) { int i = (int)value; @@ -23,16 +28,36 @@ public class Location implements Serializable{ return new Location( location.x, location.y, - location.z + location.z, + location.getWorld() ); } + public Location (double x, double y, double z, World world) { + this.x = x; + this.y = y; + this.z = z; + this.world = new WeakReference<>(world); + } + + public Location (double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + public static Location startPointLocation () { - return new Location(0,10,0); + return new Location(0,10,0, null); } public Location(long compactValue) { set(compactValue); + this.world = new WeakReference<>(null); + } + + public Location(long compactValue, World world) { + set(compactValue); + this.world = new WeakReference<>(world); } public void set(double x, double y, double z) { @@ -57,7 +82,8 @@ public class Location implements Serializable{ return new Location( this.x - location.x, this.y - location.y, - this.z - location.z + this.z - location.z, + this.getWorld().equals(location.getWorld()) ? this.getWorld() : null ); } @@ -78,4 +104,24 @@ public class Location implements Serializable{ | ((floor_double(y) & 0xFFF) << 26) | (floor_double(z) & 0x3FFFFFF); } + + public World getWorld () { + if (world == null) { + return null; + } + if (world.get() == null) { + throw new ResourceUnloadedException("You're trying to get unloaded world"); + } + return this.world.get(); + } + + public void setWorld (World world) { + this.world = new WeakReference<>(world); + } + + public Chunk getChunk() { + 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/WarpPosition.java b/core/src/main/java/mc/core/WarpPosition.java index 6e1aacf..4a0e501 100644 --- a/core/src/main/java/mc/core/WarpPosition.java +++ b/core/src/main/java/mc/core/WarpPosition.java @@ -1,14 +1,90 @@ package mc.core; -import lombok.AllArgsConstructor; -import lombok.Data; +import mc.core.player.ILook; import mc.core.player.Look; +import mc.core.world.World; import java.io.Serializable; +import java.util.Objects; -@Data -@AllArgsConstructor -public class WarpPosition implements Serializable { - private Location location; - private Look look; +public class WarpPosition extends Location implements Serializable, ILook { + private ILook look; + + public WarpPosition(double x, double y, double z, World world) { + super(x, y, z, world); + } + + public WarpPosition(double x, double y, double z, float yaw, float pitch, World world) { + super(x, y, z, world); + this.look = new Look(yaw, pitch); + } + + public WarpPosition(double x, double y, double z) { + super(x, y, z); + } + + public WarpPosition(double x, double y, double z, float yaw, float pitch) { + super(x, y, z); + this.look = new Look(yaw, pitch); + } + + public WarpPosition(long compactValue) { + super(compactValue); + } + + public WarpPosition(Location location) { + super(location.getX(), location.getY(), location.getZ()); + } + + public WarpPosition(Location location, Look look) { + super(location.getX(), location.getY(), location.getZ()); + this.look = look; + } + + public WarpPosition(long compactValue, World world) { + super(compactValue, world); + } + + @Override + public void set(Look look) { + this.look = look; + } + + @Override + public float getYaw() { + return this.look.getYaw(); + } + + @Override + public void setYaw(float yaw) { + this.look.setYaw(yaw); + } + + @Override + public float getPitch() { + return this.look.getPitch(); + } + + @Override + public void setPitch(float pitch) { + this.look.setPitch(pitch); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + WarpPosition that = (WarpPosition) o; + return Objects.equals(look, that.look); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), look); + } + + public boolean hasLook() { + return look != null; + } } diff --git a/core/src/main/java/mc/core/exception/McCoreUncheckedException.java b/core/src/main/java/mc/core/exception/McCoreUncheckedException.java new file mode 100644 index 0000000..15313f3 --- /dev/null +++ b/core/src/main/java/mc/core/exception/McCoreUncheckedException.java @@ -0,0 +1,12 @@ +package mc.core.exception; + +public abstract class McCoreUncheckedException extends RuntimeException { + + public McCoreUncheckedException() { + super(); + } + + public McCoreUncheckedException(String msg) { + super(msg); + } +} diff --git a/core/src/main/java/mc/core/exception/ResourceUnloadedException.java b/core/src/main/java/mc/core/exception/ResourceUnloadedException.java new file mode 100644 index 0000000..07ac21f --- /dev/null +++ b/core/src/main/java/mc/core/exception/ResourceUnloadedException.java @@ -0,0 +1,8 @@ +package mc.core.exception; + +public class ResourceUnloadedException extends McCoreUncheckedException { + + public ResourceUnloadedException(String msg) { + super(msg); + } +} diff --git a/core/src/main/java/mc/core/player/ILook.java b/core/src/main/java/mc/core/player/ILook.java new file mode 100644 index 0000000..fdebd51 --- /dev/null +++ b/core/src/main/java/mc/core/player/ILook.java @@ -0,0 +1,15 @@ +package mc.core.player; + +import java.io.Serializable; + +public interface ILook extends Serializable { + void set(Look look); + + float getYaw(); + + float getPitch(); + + void setYaw(float yaw); + + void setPitch(float pitch); +} diff --git a/core/src/main/java/mc/core/player/Look.java b/core/src/main/java/mc/core/player/Look.java index 1c0f7f4..f15f6f7 100644 --- a/core/src/main/java/mc/core/player/Look.java +++ b/core/src/main/java/mc/core/player/Look.java @@ -7,13 +7,12 @@ package mc.core.player; import lombok.AllArgsConstructor; import lombok.Data; -import java.io.Serializable; - @Data @AllArgsConstructor -public class Look implements Serializable{ +public class Look implements ILook { private float yaw, pitch; + @Override public void set(Look look) { this.yaw = look.yaw; this.pitch = look.pitch; diff --git a/core/src/main/java/mc/core/serialization/IChunkReader.java b/core/src/main/java/mc/core/serialization/IChunkReader.java index 4cb4530..a332385 100644 --- a/core/src/main/java/mc/core/serialization/IChunkReader.java +++ b/core/src/main/java/mc/core/serialization/IChunkReader.java @@ -1,10 +1,10 @@ package mc.core.serialization; -import mc.core.world.chunk.Chunk; +import mc.core.world.ChunkSection; import mc.core.world.Region; import java.io.IOException; public interface IChunkReader { - Chunk read (Region region, int x, int y, int z) throws IOException; + ChunkSection read (Region region, int x, int y, int z) throws IOException; } diff --git a/core/src/main/java/mc/core/world/ChunkSection.java b/core/src/main/java/mc/core/world/ChunkSection.java new file mode 100644 index 0000000..8a0e13f --- /dev/null +++ b/core/src/main/java/mc/core/world/ChunkSection.java @@ -0,0 +1,45 @@ +/* + * DmitriyMX + * 2018-04-15 + */ +package mc.core.world; + +import mc.core.world.block.Block; + +import java.io.Serializable; + +/** + * Serialization chunk info + * + * +-------------+----------------+------------+ + * | param | range | bits | + * +-------------+----------------+------------+ + * | blocks | array | 24*count | + * +-------------+----------------+------------+ + * + * Total: 24 * block_count bits (3 * block_count bytes) + * Max size: 12288 bytes (~12 Kb per chunk) + * + */ +/* 16x16x16 */ +public interface ChunkSection extends Serializable{ + + int getSkyLight(int x, int y, int z); + void setSkyLight(int x, int y, int z, int lightLevel); + + int getAddition(int x, int y, int z); + void setAddition(int x, int y, int z, int value); + + Biome getBiome(int x, int z); + void setBiome(int x, int z, Biome biome); + + int getX(); + int getY(); + int getZ(); + + void setBlock(Block block); + Block getBlock(int x, int y, int z); + + Region getRegion(); + World getWorld(); +} diff --git a/core/src/main/java/mc/core/world/Region.java b/core/src/main/java/mc/core/world/Region.java index 8448422..def5584 100644 --- a/core/src/main/java/mc/core/world/Region.java +++ b/core/src/main/java/mc/core/world/Region.java @@ -22,8 +22,13 @@ import java.io.Serializable; * */ public interface Region extends Serializable{ - Chunk getChunkAt(int x, int y, int z); - void setChunk(int x, int y, int z, Chunk chunk); + 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(); @@ -31,5 +36,7 @@ public interface Region extends Serializable{ Biome getBiomeAt (int x, int z); void setBiome (int x, int z, Biome biome); - void save(Serializer chunkSerializer, IRegionReaderWriter regionReaderWritter) throws IOException; + World getWorld(); + + void save(Serializer chunkSerializer, IRegionReaderWriter regionReaderWritter) throws IOException; } diff --git a/core/src/main/java/mc/core/world/World.java b/core/src/main/java/mc/core/world/World.java index 0bde5b1..1202b4c 100644 --- a/core/src/main/java/mc/core/world/World.java +++ b/core/src/main/java/mc/core/world/World.java @@ -6,7 +6,6 @@ package mc.core.world; import mc.core.WarpPosition; import mc.core.nbt.Taggable; -import mc.core.world.chunk.Chunk; import java.io.Serializable; import java.util.UUID; @@ -49,8 +48,8 @@ public interface World extends Taggable, Serializable{ WarpPosition getSpawn(); void setSpawn(WarpPosition location); - Chunk getChunk(int x, int y, int z); - void setChunk(int x, int y, int z, Chunk chunk); + ChunkSection getChunk(int x, int y, int z); + void setChunk(int x, int y, int z, ChunkSection chunkSection); Region getRegion(int x, int z); void setRegion(int x, int z, Region region); diff --git a/core/src/main/java/mc/core/world/chunk/Chunk.java b/core/src/main/java/mc/core/world/chunk/Chunk.java index 38593d4..9196607 100644 --- a/core/src/main/java/mc/core/world/chunk/Chunk.java +++ b/core/src/main/java/mc/core/world/chunk/Chunk.java @@ -1,46 +1,16 @@ -/* - * DmitriyMX - * 2018-04-15 - */ package mc.core.world.chunk; -import mc.core.Location; -import mc.core.world.Biome; -import mc.core.world.block.Block; +import mc.core.world.ChunkSection; +import mc.core.world.Region; +import mc.core.world.World; -import java.io.Serializable; +public interface Chunk { -/** - * Serialization chunk info - * - * +-------------+----------------+------------+ - * | param | range | bits | - * +-------------+----------------+------------+ - * | blocks | array | 24*count | - * +-------------+----------------+------------+ - * - * Total: 24 * block_count bits (3 * block_count bytes) - * Max size: 12288 bytes (~12 Kb per chunk) - * - */ -/* 16x16x16 */ -public interface Chunk extends Serializable{ - Block getBlock(int x, int y, int z); - default Block getBlock(Location location) { - return getBlock(location.getBlockX(), location.getBlockY(), location.getBlockZ()); - } - void setBlock(Block block); - - int getSkyLight(int x, int y, int z); - void setSkyLight(int x, int y, int z, int lightLevel); - - int getAddition(int x, int y, int z); - void setAddition(int x, int y, int z, int value); - - Biome getBiome(int x, int z); - void setBiome(int x, int z, Biome biome); + World getWorld(); + ChunkSection getChunkSection(int height); + ChunkSection setChunkSection(int height, ChunkSection chunkSection); + Region getRegion(); int getX(); - int getY(); int getZ(); } diff --git a/core/src/main/java/mc/core/world/chunk/ChunkLoader.java b/core/src/main/java/mc/core/world/chunk/ChunkLoader.java index cd0f06f..473bb47 100644 --- a/core/src/main/java/mc/core/world/chunk/ChunkLoader.java +++ b/core/src/main/java/mc/core/world/chunk/ChunkLoader.java @@ -1,5 +1,7 @@ package mc.core.world.chunk; +import mc.core.world.ChunkSection; + import java.util.Optional; public interface ChunkLoader { @@ -12,7 +14,7 @@ public interface ChunkLoader { * @param z chunk position * @return optional of chunk (nullable) */ - Optional loadChunk (int x, int y, int z); + Optional loadChunk (int x, int y, int z); /** * Tries to load chunk like {@link #loadChunk(int, int, int)} @@ -23,5 +25,5 @@ public interface ChunkLoader { * @param z chunk position * @return chunk */ - Chunk loadOrGenerateChunk (int x, int y, int z); + ChunkSection loadOrGenerateChunk (int x, int y, int z); } diff --git a/flat_world/src/main/java/mc/world/flat/FlatWorld.java b/flat_world/src/main/java/mc/world/flat/FlatWorld.java index 8813b51..7e6e72d 100644 --- a/flat_world/src/main/java/mc/world/flat/FlatWorld.java +++ b/flat_world/src/main/java/mc/world/flat/FlatWorld.java @@ -11,7 +11,6 @@ import mc.core.Location; import mc.core.WarpPosition; import mc.core.player.Look; import mc.core.world.*; -import mc.core.world.chunk.Chunk; import java.util.UUID; import java.util.stream.Stream; @@ -28,7 +27,7 @@ public class FlatWorld implements World { @Getter @Setter private WarpPosition spawn = new WarpPosition(new Location(0, 6, 0), new Look(0, 0)); - private Chunk chunk = new SimpleChunk(0, 0, 0); //FIXME temporary dummy + private ChunkSection chunkSection = new SimpleChunkSection(); @Override public IWorldType getWorldType() { @@ -36,12 +35,12 @@ public class FlatWorld implements World { } @Override - public Chunk getChunk(int x, int y, int z) { - return chunk; + public ChunkSection getChunk(int x, int y, int z) { + return chunkSection; } @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(); } diff --git a/flat_world/src/main/java/mc/world/flat/SimpleChunk.java b/flat_world/src/main/java/mc/world/flat/SimpleChunkSection.java similarity index 58% rename from flat_world/src/main/java/mc/world/flat/SimpleChunk.java rename to flat_world/src/main/java/mc/world/flat/SimpleChunkSection.java index 7a9cb4a..f8e5234 100644 --- a/flat_world/src/main/java/mc/world/flat/SimpleChunk.java +++ b/flat_world/src/main/java/mc/world/flat/SimpleChunkSection.java @@ -5,34 +5,14 @@ package mc.world.flat; 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.chunk.Chunk; - -import static mc.core.world.block.BlockType.*; - -public class SimpleChunk implements Chunk { - private static BlockFactory blockFactory = new BlockFactory(); - private final int x, y, z; - - public SimpleChunk(int x, int y, int z) { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public Block getBlock(int x, int y, int z) { - if (y == 0) return blockFactory.create(BEDROCK); - else if (y >= 1 && y <= 2) return blockFactory.create(DIRT); - else if (y == 3) return blockFactory.create(GRASS); - else return blockFactory.create(AIR); - } - - @Override - public void setBlock(Block block) { - } +import mc.core.world.block.BlockType; +public class SimpleChunkSection implements ChunkSection { @Override public int getSkyLight(int x, int y, int z) { if (y <= 3) return 0; @@ -66,16 +46,41 @@ public class SimpleChunk implements Chunk { @Override public int getX() { - return x; + return 0; } @Override public int getY() { - return y; + return 0; } @Override public int getZ() { - return z; + return 0; + } + + @Override + public void setBlock(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 blockFactory.create(BlockType.AIR, 0); + } + + @Override + public Region getRegion() { + return null; + } + + @Override + public World getWorld() { + return null; } } 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 index f2cd8fd..7bb26a0 100644 --- 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 @@ -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 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(); } } 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..e1e3fc3 --- /dev/null +++ b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionImpl.java @@ -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; + 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(); + } +} diff --git a/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkProxy.java b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionProxy.java similarity index 82% rename from generated_world/src/main/java/mc/world/generated_world/chunk/ChunkProxy.java rename to generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionProxy.java index 12e628e..5b4c58e 100644 --- a/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkProxy.java +++ b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionProxy.java @@ -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(); 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 c499d7f..ebc350b 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 @@ -27,7 +27,7 @@ public class InMemoryCacheChunkLoader implements ChunkLoader { @Autowired private ChunkReader chunkReader; @Autowired - private Serializer chunkSerializer; + private Serializer chunkSerializer; @Autowired private RegionReaderWriter regionReaderWritter; @@ -46,14 +46,14 @@ public class InMemoryCacheChunkLoader implements ChunkLoader { } @Override - public Optional loadChunk(int x, int y, int z) { + public Optional 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) { 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 dedaf29..11a83d1 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 @@ -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; 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 83cb6c6..66abe5d 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 @@ -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; + 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 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 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)) { diff --git a/generated_world/src/main/java/mc/world/generated_world/serialization/BlockSerializerDeserializer.java b/generated_world/src/main/java/mc/world/generated_world/serialization/BlockSerializerDeserializer.java index 3863fb0..5291a23 100644 --- a/generated_world/src/main/java/mc/world/generated_world/serialization/BlockSerializerDeserializer.java +++ b/generated_world/src/main/java/mc/world/generated_world/serialization/BlockSerializerDeserializer.java @@ -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, Deserializer { 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); 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 index 5e7c419..f6e44cb 100644 --- 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 @@ -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; } } 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 index cba4572..46fdede 100644 --- 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 @@ -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 { +public class ChunkSerializer implements Serializer { @Autowired private Serializer blockSerializer; @Override - public byte[] serialize(Chunk chunk) { - Serializer blockSerializer = new BlockSerializerDeserializer(new BlockFactory(), chunk); + public byte[] serialize(ChunkSection chunkSection) { + Serializer 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)); diff --git a/generated_world/src/main/java/mc/world/generated_world/world/CubicWorld.java b/generated_world/src/main/java/mc/world/generated_world/world/CubicWorld.java index 85c3290..1ae64b7 100644 --- a/generated_world/src/main/java/mc/world/generated_world/world/CubicWorld.java +++ b/generated_world/src/main/java/mc/world/generated_world/world/CubicWorld.java @@ -8,6 +8,7 @@ import mc.core.Direction; import mc.core.Location; import mc.core.WarpPosition; import mc.core.player.Look; +import mc.core.world.ChunkSection; import mc.core.world.IWorldType; import mc.core.world.Region; import mc.core.world.World; @@ -105,13 +106,13 @@ public class CubicWorld implements World { } @Override - public Chunk getChunk(int x, int y, int z) { + 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) { } diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java index 122ff39..d700abf 100644 --- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java @@ -11,8 +11,8 @@ import lombok.extern.slf4j.Slf4j; import mc.core.network.NetOutputStream; import mc.core.network.SCPacket; import mc.core.network.proto_1_12_2.ByteArrayOutputNetStream; +import mc.core.world.ChunkSection; import mc.core.world.block.Block; -import mc.core.world.chunk.Chunk; import java.util.ArrayList; import java.util.List; @@ -76,7 +76,7 @@ public class ChunkDataPacket implements SCPacket { @Setter private boolean initChunk = true; // "Ground-Up Continuous" @Getter - private List chunks = new ArrayList<>(); + private List chunks = new ArrayList<>(); private int serializeBlockState(int id, int meta) { return (id << 4) | meta; @@ -93,7 +93,7 @@ public class ChunkDataPacket implements SCPacket { int dataItems = 0; final int airBlockPalette = serializeBlockState(0, 0); - for (Chunk chunk : chunks) { + for (ChunkSection chunk : chunks) { final List palette = new ArrayList<>(); palette.add(airBlockPalette); final ByteArrayOutputNetStream dataArray = new ByteArrayOutputNetStream(); diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java index efc67d3..6e712ee 100644 --- a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java +++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java @@ -48,7 +48,7 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand Player player = playerManager.getPlayer(packet.getPlayerName()) .orElseGet(() -> playerManager.createPlayer( packet.getPlayerName(), - world.getSpawn().getLocation(), + world.getSpawn(), new Look(0f, 0f))); channel.writeAndFlush(new LoginSuccessPacket( @@ -68,7 +68,7 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand // Spawn Position SpawnPositionPacket pkt2 = new SpawnPositionPacket(); - pkt2.setLocation(world.getSpawn().getLocation()); + pkt2.setLocation(world.getSpawn()); channel.write(pkt2); // Player Abilities