diff --git a/build.gradle b/build.gradle index 3226f44..4497056 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,4 @@ -subprojects { +allprojects { apply plugin: 'java' compileJava { @@ -10,7 +10,9 @@ subprojects { repositories { mavenCentral() } +} +subprojects { ext { slf4j_version = '1.7.21' spring_version = '4.2.5.RELEASE' @@ -46,3 +48,23 @@ subprojects { delete 'libs' } } + +task runApp(type: JavaExec) { + main = 'mc.core.Main' + + workingDir = (project.hasProperty("workDir") ? project.workDir : '.') + + subprojects.findAll().each{ prj -> + classpath += prj.sourceSets.main.runtimeClasspath + } + /* Uncomment, if your Log Implements are folder '{workDir}/log-impl' */ + //classpath += files(fileTree(dir: new File(workingDir, "log-impl"))) + + /* Uncomment, if you used VM args */ + //jvmArgs = [ + // "-DspringConfig=app.xml", + // "-Dlog4j.configurationFile=log4j2.xml" + //] + + ignoreExitValue = true +} diff --git a/core/build.gradle b/core/build.gradle index 98cd941..0ae659a 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -14,4 +14,6 @@ dependencies { /* Components */ compile (group: 'commons-io', name: 'commons-io', version: '2.6') compile (group: 'com.google.guava', name: 'guava', version: '24.1-jre') + /* Named Binary Tags */ + compile (group: 'com.flowpowered', name: 'flow-nbt', version: '1.0.0') } diff --git a/core/src/main/java/mc/core/Location.java b/core/src/main/java/mc/core/Location.java index 9332165..b97aa16 100644 --- a/core/src/main/java/mc/core/Location.java +++ b/core/src/main/java/mc/core/Location.java @@ -6,10 +6,9 @@ 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; +import mc.core.world.chunk.Chunk; import java.io.Serializable; import java.lang.ref.Reference; @@ -61,6 +60,12 @@ public class Location implements Serializable{ this.world = new WeakReference<>(world); } + public void set(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + public void set(Location location) { this.x = location.x; this.y = location.y; diff --git a/core/src/main/java/mc/core/block/BlockFactory.java b/core/src/main/java/mc/core/block/BlockFactory.java deleted file mode 100644 index 8552e4c..0000000 --- a/core/src/main/java/mc/core/block/BlockFactory.java +++ /dev/null @@ -1,20 +0,0 @@ -package mc.core.block; - -import mc.core.Location; - -public class BlockFactory { - - public Block create(BlockType blockType, int meta) { - return new EmbeddedBlock(blockType, meta); - } - - /** - * For first-time generation - */ - private class EmbeddedBlock extends AbstractBlock { - EmbeddedBlock(BlockType type, int meta) { - super(type, meta); - super.setLocation(new Location(0,0,0)); - } - } -} diff --git a/core/src/main/java/mc/core/world/Biome.java b/core/src/main/java/mc/core/world/Biome.java index 5a60047..6753ad4 100644 --- a/core/src/main/java/mc/core/world/Biome.java +++ b/core/src/main/java/mc/core/world/Biome.java @@ -2,6 +2,8 @@ package mc.core.world; import lombok.Getter; +import java.util.EnumSet; + public enum Biome { OCEAN(0, "Ocean", 0x0000cd), PLAINS(1, "Plains", 0x008000), @@ -23,13 +25,24 @@ public enum Biome { DESERT_HILLS(17, "Desert hills", 0xffe4b5), FOREST_HILLS(18, "Forest hills", 0x006400), TAIGA_HILLS(19, "Taiga hills", 0xf0f8ff), - EXTREME_HILLS_EDGE(20, "Extreme hills edge", 0xffffff), + EXTREME_HILLS_ED(20, "Extreme hills edge", 0xffffff), JUNGLE(21, "Jungle", 0xadff2f), JUNGLE_HILLS(22, "Jungle hills", 0xadff2f), - DEEP_OCEAN(23, "Deep ocean", 0x000080), - TUNDRA(24, "Tundra", 0xc0c0c0), - SAVANNA(25, "Savana", 0xcd8513), - SAVANNA_FOREST(26, "Savana forest", 0x8b4513); + JUNGLE_HILLS_2(23, "Jungle hills", 0xadff2f), //WTF? + DEEP_OCEAN(24, "Deep ocean", 0x000080), + STONE_BEACH(25, "Stone beach", 0xffffff), + COLD_BEACH(26, "Cold beach", 0xffffff), + BIRCH_FOREST(27, "Birch forest", 0xffffff), + BIRCH_FOREST_HILLS(28, "Birch forest hills", 0xffffff), + DARK_FOREST(29, "Dark forest", 0xffffff), + COLD_TAIGA(30, "Cold taiga", 0xffffff), + COLD_TAIGA_HILLS(31, "Cold taiga hills", 0xffffff), + MEGA_TAIGA(32, "Mega taiga", 0xffffff), + MEGA_TAIGA_HILLS(33, "Mega taiga hills", 0xffffff), + EXTREME_HILLS_PLUS(34, "Extreme hills plus", 0xffffff), + SAVANNA(35, "Savana", 0xcd8513), + SAVANNA_PLATO(36, "Savana plato", 0x8b4513), + VOID(127, "Void", 0xffffff); @Getter private final int id; @@ -38,6 +51,8 @@ public enum Biome { @Getter private final int color; + private final static EnumSet waterBiomes = EnumSet.of(OCEAN, RIVER, FROZEN_OCEAN, FROZEN_RIVER, DEEP_OCEAN); + Biome(int id, String name, int color) { this.id = id; this.name = name; @@ -47,4 +62,8 @@ public enum Biome { public static Biome getById(int id) { return Biome.values()[id]; } + + public static boolean isWaterBiome (Biome biome) { + return waterBiomes.contains(biome); + } } diff --git a/core/src/main/java/mc/core/world/ChunkSection.java b/core/src/main/java/mc/core/world/ChunkSection.java index 3cd2d59..8a0e13f 100644 --- a/core/src/main/java/mc/core/world/ChunkSection.java +++ b/core/src/main/java/mc/core/world/ChunkSection.java @@ -4,7 +4,7 @@ */ package mc.core.world; -import mc.core.block.Block; +import mc.core.world.block.Block; import java.io.Serializable; @@ -23,14 +23,6 @@ import java.io.Serializable; */ /* 16x16x16 */ public interface ChunkSection extends Serializable{ - int getBlockType(int x, int y, int z); - void setBlockType(int x, int y, int z, int type); - - int getBlockMetadata(int x, int y, int z); - void setBlockMetadata(int x, int y, int z, int metadata); - - int getBlockLight(int x, int y, int z); - void setBlockLight(int x, int y, int z, int lightLevel); int getSkyLight(int x, int y, int z); void setSkyLight(int x, int y, int z, int lightLevel); @@ -45,7 +37,7 @@ public interface ChunkSection extends Serializable{ int getY(); int getZ(); - void setBlock(int x, int y, int z, Block block); + void setBlock(Block block); Block getBlock(int x, int y, int z); Region getRegion(); diff --git a/core/src/main/java/mc/core/world/Region.java b/core/src/main/java/mc/core/world/Region.java index 7b57123..def5584 100644 --- a/core/src/main/java/mc/core/world/Region.java +++ b/core/src/main/java/mc/core/world/Region.java @@ -2,6 +2,7 @@ package mc.core.world; import mc.core.serialization.IRegionReaderWriter; import mc.core.serialization.Serializer; +import mc.core.world.chunk.Chunk; import java.io.IOException; import java.io.Serializable; diff --git a/core/src/main/java/mc/core/block/AbstractBlock.java b/core/src/main/java/mc/core/world/block/AbstractBlock.java similarity index 87% rename from core/src/main/java/mc/core/block/AbstractBlock.java rename to core/src/main/java/mc/core/world/block/AbstractBlock.java index 126e605..5d0572c 100644 --- a/core/src/main/java/mc/core/block/AbstractBlock.java +++ b/core/src/main/java/mc/core/world/block/AbstractBlock.java @@ -1,4 +1,4 @@ -package mc.core.block; +package mc.core.world.block; import com.flowpowered.nbt.Tag; import lombok.Getter; @@ -10,11 +10,15 @@ import java.util.Map; import java.util.stream.Stream; public abstract class AbstractBlock implements Block { - @Getter@Setter + @Getter + @Setter private Location location; @Getter private int meta; @Getter + @Setter + private int light = 0; //TODO need to know range of values + @Getter private final BlockType blockType; private final Map> nbtTagsMap = new HashMap<>(); diff --git a/core/src/main/java/mc/core/block/Block.java b/core/src/main/java/mc/core/world/block/Block.java similarity index 71% rename from core/src/main/java/mc/core/block/Block.java rename to core/src/main/java/mc/core/world/block/Block.java index fd67fa8..a140ab0 100644 --- a/core/src/main/java/mc/core/block/Block.java +++ b/core/src/main/java/mc/core/world/block/Block.java @@ -1,6 +1,5 @@ -package mc.core.block; +package mc.core.world.block; -import com.flowpowered.nbt.Tag; import mc.core.Location; import mc.core.nbt.Taggable; @@ -29,20 +28,6 @@ import java.io.Serializable; public interface Block extends Taggable, Serializable{ - static Block airBlock (int x, int y, int z) { - return new AbstractBlock(BlockType.AIR) { - @Override - public Tag getTag(String name) { - return null; - } - - @Override - public Location getLocation() { - return new Location(x, y, z); - } - }; - } - /** Block id */ int getId(); @@ -53,6 +38,9 @@ public interface Block extends Taggable, Serializable{ */ int getMeta(); + int getLight(); + void setLight(int light); + /** * Getting block type */ diff --git a/core/src/main/java/mc/core/world/block/BlockFactory.java b/core/src/main/java/mc/core/world/block/BlockFactory.java new file mode 100644 index 0000000..868eb52 --- /dev/null +++ b/core/src/main/java/mc/core/world/block/BlockFactory.java @@ -0,0 +1,28 @@ +package mc.core.world.block; + +import mc.core.Location; + +public class BlockFactory { + + public Block create(BlockType blockType, int meta, int x, int y, int z) { + return new EmbeddedBlock(blockType, meta, x, y, z); + } + + public Block create(BlockType blockType, int meta) { + return new EmbeddedBlock(blockType, meta, 0, 0, 0); + } + + public Block create(BlockType blockType) { + return create(blockType, 0, 0, 0, 0); + } + + /** + * For first-time generation + */ + private class EmbeddedBlock extends AbstractBlock { + EmbeddedBlock(BlockType type, int meta, int x, int y, int z) { + super(type, meta); + super.setLocation(new Location(x,y,z)); + } + } +} diff --git a/core/src/main/java/mc/core/block/BlockType.java b/core/src/main/java/mc/core/world/block/BlockType.java similarity index 83% rename from core/src/main/java/mc/core/block/BlockType.java rename to core/src/main/java/mc/core/world/block/BlockType.java index 4b4b467..7adb32a 100644 --- a/core/src/main/java/mc/core/block/BlockType.java +++ b/core/src/main/java/mc/core/world/block/BlockType.java @@ -1,16 +1,16 @@ -package mc.core.block; +package mc.core.world.block; import lombok.Getter; public enum BlockType { + AIR(0, "Air"), STONE(1, "Stone"), GRASS(2, "Grass"), DIRT(3, "Dirt"), BEDROCK(7, "Bedrock"), WATER(8, "Water"), SAND(12, "Sand"), - SNOW(32, "Snow"), - AIR(0, "Air"); + SNOW(32, "Snow"); @Getter private final int id; diff --git a/core/src/main/java/mc/core/world/Chunk.java b/core/src/main/java/mc/core/world/chunk/Chunk.java similarity index 65% rename from core/src/main/java/mc/core/world/Chunk.java rename to core/src/main/java/mc/core/world/chunk/Chunk.java index 694ce54..9196607 100644 --- a/core/src/main/java/mc/core/world/Chunk.java +++ b/core/src/main/java/mc/core/world/chunk/Chunk.java @@ -1,4 +1,8 @@ -package mc.core.world; +package mc.core.world.chunk; + +import mc.core.world.ChunkSection; +import mc.core.world.Region; +import mc.core.world.World; public interface Chunk { diff --git a/core/src/main/java/mc/core/world/ChunkLoader.java b/core/src/main/java/mc/core/world/chunk/ChunkLoader.java similarity index 91% rename from core/src/main/java/mc/core/world/ChunkLoader.java rename to core/src/main/java/mc/core/world/chunk/ChunkLoader.java index 4a0c7b3..473bb47 100644 --- a/core/src/main/java/mc/core/world/ChunkLoader.java +++ b/core/src/main/java/mc/core/world/chunk/ChunkLoader.java @@ -1,4 +1,6 @@ -package mc.core.world; +package mc.core.world.chunk; + +import mc.core.world.ChunkSection; import java.util.Optional; 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 62c32fe..7e6e72d 100644 --- a/flat_world/src/main/java/mc/world/flat/FlatWorld.java +++ b/flat_world/src/main/java/mc/world/flat/FlatWorld.java @@ -17,9 +17,11 @@ import java.util.stream.Stream; public class FlatWorld implements World { - @Getter@Setter + @Getter + @Setter private UUID worldId = UUID.fromString("00000000-0000-0000-C000-000000000046"); - @Getter@Setter + @Getter + @Setter private String name; @Getter @@ -49,7 +51,7 @@ public class FlatWorld implements World { @Override public void setRegion(int x, int z, Region region) { - + throw new UnsupportedOperationException(); } @Override diff --git a/flat_world/src/main/java/mc/world/flat/SimpleChunkSection.java b/flat_world/src/main/java/mc/world/flat/SimpleChunkSection.java index 1355c18..f8e5234 100644 --- a/flat_world/src/main/java/mc/world/flat/SimpleChunkSection.java +++ b/flat_world/src/main/java/mc/world/flat/SimpleChunkSection.java @@ -4,48 +4,15 @@ */ package mc.world.flat; -import mc.core.block.Block; -import mc.core.block.BlockFactory; -import mc.core.block.BlockType; 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; public class SimpleChunkSection implements ChunkSection { - @Override - public int getBlockType(int x, int y, int z) { - if (y == 0) return 7; - else if (y >= 1 && y <= 2) return 3; - else if (y == 3) return 2; - else return 0; - } - - @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 0; - } - - @Override - public void setBlockLight(int x, int y, int z, int lightLevel) { - - } - @Override public int getSkyLight(int x, int y, int z) { if (y <= 3) return 0; @@ -93,7 +60,7 @@ public class SimpleChunkSection implements ChunkSection { } @Override - public void setBlock(int x, int y, int z, Block block) { + public void setBlock(Block block) { } @@ -104,7 +71,7 @@ public class SimpleChunkSection implements ChunkSection { if (y == 0) return blockFactory.create(BlockType.BEDROCK, 0); else if (y >= 1 && y <= 2) return blockFactory.create(BlockType.DIRT, 0); else if (y == 3) return blockFactory.create(BlockType.GRASS, 0); - else return Block.airBlock(x, y, z); + else return blockFactory.create(BlockType.AIR, 0); } @Override 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 7954603..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 @@ -2,10 +2,10 @@ 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 mc.core.world.chunk.Chunk; import java.lang.ref.Reference; import java.lang.ref.WeakReference; 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 index dce85eb..e1e3fc3 100644 --- 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 @@ -1,13 +1,14 @@ 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 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; @@ -23,6 +24,7 @@ public class ChunkSectionImpl implements ChunkSection { 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; @@ -31,36 +33,6 @@ public class ChunkSectionImpl implements ChunkSection { 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; @@ -92,19 +64,19 @@ public class ChunkSectionImpl implements ChunkSection { } @Override - public void setBlock(int x, int y, int z, Block block) { + public void setBlock(Block block) { if (block.getBlockType() == BlockType.AIR) { - blocks[x][y][z] = null; + blocks[block.getLocation().getBlockX()][block.getLocation().getBlockY()][block.getLocation().getBlockZ()] = null; return; } - blocks[x][y][z] = block; + 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 Block.airBlock(x, y, z); + return blockFactory.create(BlockType.AIR, 0, x, y, z); } return blocks[x][y][z]; } 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 index c945af8..5b4c58e 100644 --- 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 @@ -1,146 +1,108 @@ 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 mc.core.world.block.Block; +import mc.core.world.Biome; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ChunkSectionProxy implements ChunkSection { - private final ChunkSection chunkSection; + private final ChunkSection chunk; private volatile transient long lastUsage = System.currentTimeMillis(); private final transient ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - public ChunkSectionProxy(ChunkSection chunkSection) { - this.chunkSection = chunkSection; + public ChunkSectionProxy(ChunkSection chunk) { + this.chunk = chunk; } public long getLastUsage() { - synchronized (chunkSection) { + synchronized (chunk) { return lastUsage; } } private final void use () { - synchronized (chunkSection) { + synchronized (chunk) { lastUsage = System.currentTimeMillis(); } } @Override - public int getBlockType(int x, int y, int z) { + public Block getBlock(int x, int y, int z) { use(); - return chunkSection.getBlockType(x, y, z); + return chunk.getBlock(x, y, z); } @Override - public void setBlockType(int x, int y, int z, int type) { - use(); - chunkSection.setBlockType(x, y, z, type); + public Region getRegion() { + return chunk.getRegion(); } @Override - public int getBlockMetadata(int x, int y, int z) { - use(); - return chunkSection.getBlockMetadata(x, y, z); + public World getWorld() { + return chunk.getWorld(); } @Override - public void setBlockMetadata(int x, int y, int z, int metadata) { + public void setBlock(Block block) { 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); + chunk.setBlock(block); } @Override public int getSkyLight(int x, int y, int z) { use(); - return chunkSection.getSkyLight(x, y, z); + return chunk.getSkyLight(x, y, z); } @Override public void setSkyLight(int x, int y, int z, int lightLevel) { use(); - chunkSection.setSkyLight(x, y, z, lightLevel); + chunk.setSkyLight(x, y, z, lightLevel); } @Override public int getAddition(int x, int y, int z) { use(); - return chunkSection.getAddition(x, y, z); + return chunk.getAddition(x, y, z); } @Override public void setAddition(int x, int y, int z, int value) { use(); - chunkSection.setAddition(x, y, z, value); + chunk.setAddition(x, y, z, value); } @Override public Biome getBiome(int x, int z) { use(); - return chunkSection.getBiome(x, z); + return chunk.getBiome(x, z); } @Override public void setBiome(int x, int z, Biome biome) { use(); - chunkSection.setBiome(x, z, biome); + chunk.setBiome(x, z, biome); } @Override public int getX() { use(); - return chunkSection.getX(); + return chunk.getX(); } @Override public int getY() { use(); - return chunkSection.getY(); + return chunk.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(); + return chunk.getZ(); } } 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 f304f84..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 @@ -3,6 +3,8 @@ package mc.world.generated_world.chunk; import lombok.extern.slf4j.Slf4j; 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.serialization.ChunkReader; import mc.world.generated_world.serialization.RegionReaderWriter; import org.springframework.beans.factory.annotation.Autowired; diff --git a/generated_world/src/main/java/mc/world/generated_world/generator/NoiseGenerator.java b/generated_world/src/main/java/mc/world/generated_world/generator/NoiseGenerator.java new file mode 100644 index 0000000..b3c84c7 --- /dev/null +++ b/generated_world/src/main/java/mc/world/generated_world/generator/NoiseGenerator.java @@ -0,0 +1,57 @@ +package mc.world.generated_world.generator; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import static mc.world.generated_world.WorldConstants.WORLD_REGION_SIZE; + +@Slf4j +@RequiredArgsConstructor +public class NoiseGenerator { + private int[] perm = new int[WORLD_REGION_SIZE]; + private double[] gradsX = new double[WORLD_REGION_SIZE]; + private double[] gradsY = new double[WORLD_REGION_SIZE]; + private final int seed; + + void init() { + for (int i = 0; i < WORLD_REGION_SIZE; ++i) { + int other = rand(i) % (i + 1); + if (i > other) + perm[i] = perm[other]; + perm[other] = i; + gradsX[i] = Math.cos(2.0f * Math.PI * i / WORLD_REGION_SIZE); + gradsY[i] = Math.sin(2.0f * Math.PI * i / WORLD_REGION_SIZE); + } + log.debug("Noise generator is initialized"); + } + + double f(double t) { + t = Math.abs(t); + return t >= 1.0f ? 0.0f : 1.0f - + (3.0f - 2.0f * t) * t * t; + } + + private double surflet(double x, double y, double gradX, double gradY) { + return f(x) * f(y) * (gradX * x + gradY * y); + } + + double noise(double x, double y) { + float result = 0.0f; + int cellX = (int)(x); + int cellY = (int)(y); + int mask = WORLD_REGION_SIZE - 1; + for (int gridY = cellY; gridY <= cellY + 1; ++gridY) + for (int gridX = cellX; gridX <= cellX + 1; ++gridX) { + int hash = perm[(perm[gridX & mask] + gridY) & mask]; + result += surflet(x - gridX, y - gridY, + gradsX[hash], gradsY[hash]); + } + return (result + 1) / 2; + } + + private int rand(int i) { + int x = (i * i) % WORLD_REGION_SIZE; + int y = (i + i * x) % WORLD_REGION_SIZE; + return (int) (Integer.MAX_VALUE * SeedRandomGenerator.random(x, y, seed)); + } +} 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 1b5d8dd..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 @@ -2,12 +2,10 @@ package mc.world.generated_world.generator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import mc.core.block.BlockFactory; -import mc.core.block.BlockType; +import mc.core.world.block.BlockFactory; +import mc.core.world.block.BlockType; import mc.core.world.*; import mc.world.generated_world.region.RegionImpl; -import mc.world.generated_world.serialization.ChunkSerializer; -import mc.world.generated_world.serialization.RegionReaderWriter; import mc.world.generated_world.world.CubicWorld; import mc.world.generated_world.world.Temperature; import mc.world.generated_world.world.Wetness; @@ -15,6 +13,7 @@ import mc.world.generated_world.world.Wetness; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; +import java.io.IOException; import java.util.UUID; import static mc.world.generated_world.WorldConstants.*; @@ -25,24 +24,26 @@ public class SeedBasedWorldGenerator implements WorldGenerator { public static void main(String[] args) throws Exception{ WorldGenerator worldGenerator = new SeedBasedWorldGenerator(); World world = new CubicWorld(UUID.fromString("00000000-0000-0000-C000-000000000046"), 2626949); - Region region = worldGenerator.generateRegion(0, 0, world); + /*Region region = worldGenerator.generateRegion(0, 0, world); region.save(new ChunkSerializer(), new RegionReaderWriter(new File("worlds", world.getWorldId().toString()))); -// new WorldReaderWriter(new File("worlds")).writeWorldInfo(world); - /*worldGenerator.generateRegion(1, 0, world); - worldGenerator.generateRegion(-1, 0, world); - worldGenerator.generateRegion(0, 1, world); - worldGenerator.generateRegion(0, -1, world); - worldGenerator.generateRegion(-1, -1, world); - worldGenerator.generateRegion(1, -1, world); - worldGenerator.generateRegion(-1, 1, world); - worldGenerator.generateRegion(1, 1, world); + new WorldReaderWriter(new File("worlds")).writeWorldInfo(world);*/ + + createBigImage(worldGenerator, world); + } + + private static void createBigImage (WorldGenerator worldGenerator, World world) throws IOException { BufferedImage image = new BufferedImage(3 * 256, 3 * 256, BufferedImage.TYPE_INT_RGB); - BufferedImage currentImage; - int shiftX; - int shiftY; - currentImage = ImageIO.read(new File("out/0.0", "biomeMap.png")); - shiftX = 1; - shiftY = 1; + for (int x = 0; x <= 2; x ++) { + for (int z = 0; z <= 2; z ++) { + worldGenerator.generateRegion(x - 1, z - 1, world); + addToBigImage(x, z, image); + } + } + ImageIO.write(image, "png", new File("out", "merged.png")); + } + + private static void addToBigImage (int shiftX, int shiftY, BufferedImage image) throws IOException{ + BufferedImage currentImage = ImageIO.read(new File("out/" + (shiftX - 1) + "." + (shiftY - 1), "biomeMap.png")); for (int x = 0; x < 256; x ++){ for (int y = 0; y < 256; y ++){ int tx = 256 * shiftX + x; @@ -50,87 +51,6 @@ public class SeedBasedWorldGenerator implements WorldGenerator { image.setRGB(tx, ty, currentImage.getRGB(x, y)); } } - currentImage = ImageIO.read(new File("out/0.1", "biomeMap.png")); - shiftX = 1; - shiftY = 2; - for (int x = 0; x < 256; x ++){ - for (int y = 0; y < 256; y ++){ - int tx = 256 * shiftX + x; - int ty = 256 * shiftY + y; - image.setRGB(tx, ty, currentImage.getRGB(x, y)); - } - } - currentImage = ImageIO.read(new File("out/1.0", "biomeMap.png")); - shiftX = 2; - shiftY = 1; - for (int x = 0; x < 256; x ++){ - for (int y = 0; y < 256; y ++){ - int tx = 256 * shiftX + x; - int ty = 256 * shiftY + y; - image.setRGB(tx, ty, currentImage.getRGB(x, y)); - } - } - currentImage = ImageIO.read(new File("out/-1.0", "biomeMap.png")); - shiftX = 0; - shiftY = 1; - for (int x = 0; x < 256; x ++){ - for (int y = 0; y < 256; y ++){ - int tx = 256 * shiftX + x; - int ty = 256 * shiftY + y; - image.setRGB(tx, ty, currentImage.getRGB(x, y)); - } - } - currentImage = ImageIO.read(new File("out/0.-1", "biomeMap.png")); - shiftX = 1; - shiftY = 0; - for (int x = 0; x < 256; x ++){ - for (int y = 0; y < 256; y ++){ - int tx = 256 * shiftX + x; - int ty = 256 * shiftY + y; - image.setRGB(tx, ty, currentImage.getRGB(x, y)); - } - } - currentImage = ImageIO.read(new File("out/-1.-1", "biomeMap.png")); - shiftX = 0; - shiftY = 0; - for (int x = 0; x < 256; x ++){ - for (int y = 0; y < 256; y ++){ - int tx = 256 * shiftX + x; - int ty = 256 * shiftY + y; - image.setRGB(tx, ty, currentImage.getRGB(x, y)); - } - } - currentImage = ImageIO.read(new File("out/1.-1", "biomeMap.png")); - shiftX = 2; - shiftY = 0; - for (int x = 0; x < 256; x ++){ - for (int y = 0; y < 256; y ++){ - int tx = 256 * shiftX + x; - int ty = 256 * shiftY + y; - image.setRGB(tx, ty, currentImage.getRGB(x, y)); - } - } - currentImage = ImageIO.read(new File("out/1.1", "biomeMap.png")); - shiftX = 2; - shiftY = 2; - for (int x = 0; x < 256; x ++){ - for (int y = 0; y < 256; y ++){ - int tx = 256 * shiftX + x; - int ty = 256 * shiftY + y; - image.setRGB(tx, ty, currentImage.getRGB(x, y)); - } - } - currentImage = ImageIO.read(new File("out/-1.1", "biomeMap.png")); - shiftX = 0; - shiftY = 2; - for (int x = 0; x < 256; x ++){ - for (int y = 0; y < 256; y ++){ - int tx = 256 * shiftX + x; - int ty = 256 * shiftY + y; - image.setRGB(tx, ty, currentImage.getRGB(x, y)); - } - } - ImageIO.write(image, "png", new File("out", "merged.png"));*/ } @Override @@ -160,7 +80,7 @@ public class SeedBasedWorldGenerator implements WorldGenerator { return 40960 + x; } - public void generate() { + private void generate() { log.debug("Starting generating region [{}, {}] for world '{}' with seed '{}'", region.getX(), region.getZ(), world.getWorldId(), world.getSeed()); noiseGenerator = new NoiseGenerator(world.getSeed()); @@ -305,37 +225,37 @@ 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 ++) { - ChunkSection chunkSection = region.getChunkAt(x / 16, y / 16, z / 16); + ChunkSection chunk = region.getChunkAt(x / 16, y / 16, z / 16); if (y == 0) { - chunkSection.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.BEDROCK, 0)); + chunk.setBlock(blockFactory.create(BlockType.BEDROCK, 0, x % 16, y % 16, z % 16)); continue; } if (y < heightMap[x][z]) { if (y < heightMap[x][z] - grassMap[x][z]) { - chunkSection.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.STONE, 0)); + chunk.setBlock(blockFactory.create(BlockType.STONE, 0, x % 16, y % 16, z % 16)); } else { - chunkSection.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.SAND, 0)); + chunk.setBlock(blockFactory.create(BlockType.SAND, 0, x % 16, y % 16, z % 16)); } } else { - chunkSection.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.WATER, 0)); + chunk.setBlock(blockFactory.create(BlockType.WATER, 0, x % 16, y % 16, z % 16)); } } } else { for (int y = 0; y < heightMap[x][z]; y++) { - ChunkSection chunkSection = region.getChunkAt(x / 16, y / 16, z / 16); + ChunkSection chunk = region.getChunkAt(x / 16, y / 16, z / 16); if (y == 0) { - chunkSection.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.BEDROCK, 0)); + chunk.setBlock(blockFactory.create(BlockType.BEDROCK, 0, x % 16, y % 16, z % 16)); continue; } if (y < heightMap[x][z] - grassMap[x][z]) { - chunkSection.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.STONE, 0)); + chunk.setBlock(blockFactory.create(BlockType.STONE, 0, x % 16, y % 16, z % 16)); } else { if (biomes[x][z] == Biome.DESERT || biomes[x][z] == Biome.DESERT_HILLS) { - chunkSection.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.SAND, 0)); + chunk.setBlock(blockFactory.create(BlockType.SAND, 0, x % 16, y % 16, z % 16)); } else if (biomes[x][z] == Biome.TAIGA || biomes[x][z] == Biome.TAIGA_HILLS) { - chunkSection.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.DIRT, 0)); + chunk.setBlock(blockFactory.create(BlockType.DIRT, 0, x % 16, y % 16, z % 16)); } else { - chunkSection.setBlock(x % 16, y % 16, z % 16, blockFactory.create(BlockType.GRASS, 0)); + chunk.setBlock(blockFactory.create(BlockType.GRASS, 0, x % 16, y % 16, z % 16)); } } } @@ -379,7 +299,7 @@ public class SeedBasedWorldGenerator implements WorldGenerator { if (temperature == Temperature.FROST) { if (wetness == Wetness.DRIEST || wetness == Wetness.DRY) { - return Biome.TUNDRA; + return Biome.COLD_TAIGA; } else { if (height > HILLS_HEIGHT) { return Biome.ICE_MOUNTAINS; @@ -433,7 +353,7 @@ public class SeedBasedWorldGenerator implements WorldGenerator { return Biome.FOREST; } } else { - return Biome.SAVANNA_FOREST; + return Biome.SAVANNA_PLATO; } } @@ -468,54 +388,4 @@ public class SeedBasedWorldGenerator implements WorldGenerator { } } - @RequiredArgsConstructor - private class NoiseGenerator { - int mask = WORLD_REGION_SIZE - 1; - int[] perm = new int[WORLD_REGION_SIZE]; - double[] gradsX = new double[WORLD_REGION_SIZE]; - double[] gradsY = new double[WORLD_REGION_SIZE]; - private final int seed; - - void init() { - for (int i = 0; i < WORLD_REGION_SIZE; ++i) { - int other = rand(i) % (i + 1); - if (i > other) - perm[i] = perm[other]; - perm[other] = i; - gradsX[i] = Math.cos(2.0f * Math.PI * i / WORLD_REGION_SIZE); - gradsY[i] = Math.sin(2.0f * Math.PI * i / WORLD_REGION_SIZE); - } - log.debug("Noise generator is initialized"); - } - - double f(double t) { - t = Math.abs(t); - return t >= 1.0f ? 0.0f : 1.0f - - (3.0f - 2.0f * t) * t * t; - } - - double surflet(double x, double y, double gradX, double gradY) { - return f(x) * f(y) * (gradX * x + gradY * y); - } - - double noise(double x, double y) { - float result = 0.0f; - int cellX = (int)(x); - int cellY = (int)(y); - for (int gridY = cellY; gridY <= cellY + 1; ++gridY) - for (int gridX = cellX; gridX <= cellX + 1; ++gridX) { - int hash = perm[(perm[gridX & mask] + gridY) & mask]; - result += surflet(x - gridX, y - gridY, - gradsX[hash], gradsY[hash]); - } - return (result + 1) / 2; - } - - int rand(int i) { - int x = (i * i) % WORLD_REGION_SIZE; - int y = (i + i * x) % WORLD_REGION_SIZE; - return (int) (Integer.MAX_VALUE * SeedRandomGenerator.random(x, y, seed)); - } - } - } \ No newline at end of file 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 9f96d49..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 @@ -6,10 +6,12 @@ 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.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.ChunkSectionImpl; import org.springframework.beans.factory.annotation.Autowired; import java.io.File; 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 ae308fb..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 @@ -1,8 +1,8 @@ package mc.world.generated_world.serialization; -import mc.core.block.Block; -import mc.core.block.BlockFactory; -import mc.core.block.BlockType; +import mc.core.world.block.Block; +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.ChunkSection; 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 c117150..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,7 +1,7 @@ package mc.world.generated_world.serialization; import mc.core.Location; -import mc.core.block.Block; +import mc.core.world.block.Block; import mc.core.serialization.Deserializer; import mc.core.serialization.IChunkReader; import mc.core.world.ChunkSection; @@ -41,8 +41,7 @@ public class ChunkReader implements IChunkReader{ blockBytes[1] = chunkBytes[1 + 3 * i]; blockBytes[2] = chunkBytes[2 + 3 * i]; Block block = blockDeserializer.deserialize(blockBytes); - Location blockLocation = block.getLocation(); - chunkSection.setBlock(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ(), block); + chunkSection.setBlock(block); } 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 ce592ce..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 @@ -1,9 +1,9 @@ package mc.world.generated_world.serialization; import lombok.extern.slf4j.Slf4j; -import mc.core.block.Block; -import mc.core.block.BlockFactory; -import mc.core.block.BlockType; +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.ChunkSection; import org.springframework.beans.factory.annotation.Autowired; 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 8dc502c..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 @@ -4,34 +4,66 @@ import com.flowpowered.nbt.Tag; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import mc.core.Direction; import mc.core.Location; import mc.core.WarpPosition; -import mc.core.block.BlockType; 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; +import mc.core.world.WorldGenerator; +import mc.core.world.block.BlockType; +import mc.core.world.chunk.Chunk; +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.*; + +/* + * 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 WarpPosition warpPosition; private final transient Object spawnLocationLock = new Object(); private final Map> nbtTagMap = new HashMap<>(); - @Autowired - private RegionManager regionManager; @Getter@Setter private String name; @@ -62,23 +94,8 @@ public class CubicWorld implements World { @Override public WarpPosition getSpawn() { - if (warpPosition == null) { - synchronized (spawnLocationLock) { - if (warpPosition == null) { - log.warn("Spawn location is not defined. Trying to select best location"); - warpPosition = new WarpPosition(Location.startPointLocation(), new Look(0, 0)); - for (int y = WORLD_MAX_HEIGHT; y > 0; y --) { - ChunkSection chunkSection = getChunk(0,y / WORLD_CHUNK_SIZE, 0); - if (chunkSection.getBlock(0, y, 0).getBlockType() != BlockType.AIR) { - warpPosition = new WarpPosition(new Location(0, y + 1, 0), new Look(0, 0)); - break; - } - } - warpPosition = new WarpPosition(Location.startPointLocation(), new Look(0,0)); - } - } - } - return warpPosition; + /* FIXME */ + return new WarpPosition(new Location(0, 100, 0), new Look(0, 0)); } @Override @@ -90,7 +107,8 @@ public class CubicWorld implements World { @Override public ChunkSection getChunk(int x, int y, int z) { - return null; + Region region = getRegion(x / 16, z / 16); + return region.getChunkAt(x % 16, y % 16, z % 16); } @Override @@ -100,12 +118,42 @@ public class CubicWorld implements World { @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 @@ -127,4 +175,53 @@ public class CubicWorld implements World { public Stream> 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(); + } + } } diff --git a/generated_world/src/main/java/mc/world/generated_world/world/RegionManager.java b/generated_world/src/main/java/mc/world/generated_world/world/RegionManager.java deleted file mode 100644 index 0cc0ef4..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/world/RegionManager.java +++ /dev/null @@ -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(); - } - } - -} diff --git a/generated_world/src/test/java/mc/world/generated_world/SeedRandomGeneratorTest.java b/generated_world/src/test/java/mc/world/generated_world/SeedRandomGeneratorTest.java index a99a251..6c5fbe4 100644 --- a/generated_world/src/test/java/mc/world/generated_world/SeedRandomGeneratorTest.java +++ b/generated_world/src/test/java/mc/world/generated_world/SeedRandomGeneratorTest.java @@ -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 diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/ByteArrayOutputNetStream.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/ByteArrayOutputNetStream.java index 15810b7..d496e05 100644 --- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/ByteArrayOutputNetStream.java +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/ByteArrayOutputNetStream.java @@ -65,6 +65,10 @@ public class ByteArrayOutputNetStream extends NetOutputStream_p340 { writeLong(Double.doubleToLongBits(value)); } + public int size() { + return baos.size(); + } + public byte[] toByteArray() { return baos.toByteArray(); } diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetOutputStream_p340.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetOutputStream_p340.java index ea609cf..cbc3239 100644 --- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetOutputStream_p340.java +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetOutputStream_p340.java @@ -14,17 +14,16 @@ import java.util.UUID; public abstract class NetOutputStream_p340 extends NetOutputStream { @Override public void writeVarInt(int value) { - do { - byte temp = (byte)(value & 0b01111111); - // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone + while ((value & -128) != 0) { + writeByte(value & 127 | 128); value >>>= 7; - if (value != 0) { - temp |= 0b10000000; - } - writeByte(temp); - } while (value != 0); + } + + writeByte(value); } + + @Override public void writeString(String value) { if (value.length() > Short.MAX_VALUE) { diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/State.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/State.java index 237aa4f..feb4f26 100644 --- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/State.java +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/State.java @@ -87,6 +87,7 @@ public enum State { .put(PluginMessagePacket.class, 0x18) .put(ChangeGameState.class, 0x1E) .put(KeepAlivePacket.class, 0x1F) + .put(ChunkDataPacket.class, 0x20) .put(JoinGamePacket.class, 0x23) .put(PlayerAbilitiesPacket.class, 0x2C) .put(PlayerListItemPacket.class, 0x2E) 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 new file mode 100644 index 0000000..d700abf --- /dev/null +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java @@ -0,0 +1,188 @@ +/* + * DmitriyMX + * 2018-07-21 + */ +package mc.core.network.proto_1_12_2.packets; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +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 java.util.ArrayList; +import java.util.List; + +/* +Packet structure + +- https://wiki.vg/Chunk_Format#Packet_structure + ++------------------------------------------------------+ +| Field | Type | +|--------------------------|---------------------------| +| Chunk X | int | +|--------------------------|---------------------------| +| Chunk Y | int | +|--------------------------|---------------------------| +| Init Chunk | boolean | ("Ground-Up Continuous") +|--------------------------|---------------------------| +| Primary Bit Mask | VarInt | +|--------------------------|---------------------------| +| Size of Data | VarInt | +|--------------------------|---------------------------| +| Data | Byte array | - https://wiki.vg/Chunk_Format#Data_structure +| +------------------------------------------------+ | +| | Chunk Section | Byte array | | - https://wiki.vg/Chunk_Format#Chunk_Section_structure +| | +------------------------------------------+ | | +| | | Bits Per Block | Unsigned Byte | | | (we use 4 bits per block) +| | |--------------------|---------------------| | | +| | | Palette | Byte array | | | - https://wiki.vg/Chunk_Format#Palettes +| | | +------------------------------------+ | | | (we use Indirect type palette) +| | | | Size of palette | VarInt | | | | +| | | |-----------------|------------------| | | | +| | | | Palette | Array of VarInt | | | | +| | | +------------------------------------+ | | | +| | |--------------------|---------------------| | | +| | | Size of Data Array | VarInt | | | +| | |--------------------|---------------------| | | +| | | Data Array | Array of Long | | | +| | |--------------------|---------------------| | | +| | | Block Light | Byte Array | | | (Half byte per block) +| | |--------------------|---------------------| | | +| | | Sky Light | Optional Byte Array | | | (Only if in the Overworld; half byte per block) +| | +------------------------------------------+ | | +| |-----------------------|------------------------| | +| | Biomes | Optional Byte array | | +| +------------------------------------------------+ | +|--------------------------|---------------------------| +| Number of block entities | VarInt | +|--------------------------|---------------------------| +| Block entities | Array of NBT | ++------------------------------------------------------+ + */ + +@Slf4j +@NoArgsConstructor +public class ChunkDataPacket implements SCPacket { + @Setter + private int x; + @Setter + private int z; + @Setter + private boolean initChunk = true; // "Ground-Up Continuous" + @Getter + private List chunks = new ArrayList<>(); + + private int serializeBlockState(int id, int meta) { + return (id << 4) | meta; + } + + @Override + public void writeSelf(NetOutputStream netStream) { + netStream.writeInt(x); // Chunk X + netStream.writeInt(z); // Chunk Y + netStream.writeBoolean(initChunk); // Init Chunk + netStream.writeVarInt(0b00000001); // Primary Bit Mask + + final ByteArrayOutputNetStream data = new ByteArrayOutputNetStream(); + int dataItems = 0; + final int airBlockPalette = serializeBlockState(0, 0); + + for (ChunkSection chunk : chunks) { + final List palette = new ArrayList<>(); + palette.add(airBlockPalette); + final ByteArrayOutputNetStream dataArray = new ByteArrayOutputNetStream(); + final ByteArrayOutputNetStream blockLight = new ByteArrayOutputNetStream(); + final ByteArrayOutputNetStream skyLight = new ByteArrayOutputNetStream(); + final ByteArrayOutputNetStream biomes = new ByteArrayOutputNetStream(); + + long dataValueCompacted = 0; + int blockLightCompacted = 0; + int skyLightCompacted = 0; + + int idxHalfLong = 0; + int idxHalfByte = 0; + boolean biomeFinally = false; + + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + Block block = chunk.getBlock(x, y, z); + int blockState = serializeBlockState(block.getId(), block.getMeta()); + + int currentIndexPaletteBlock; + if (!palette.contains(blockState)) { + palette.add(blockState); + currentIndexPaletteBlock = palette.size()-1; + } else { + currentIndexPaletteBlock = palette.indexOf(blockState); + } + + if (idxHalfLong == 0) { + dataValueCompacted = currentIndexPaletteBlock; + idxHalfLong++; + } else if (idxHalfLong > 0 && idxHalfLong < 15) { + dataValueCompacted = (dataValueCompacted << 4) | currentIndexPaletteBlock; + idxHalfLong++; + } else { + dataValueCompacted = (dataValueCompacted << 4) | currentIndexPaletteBlock; + dataArray.writeLong(dataValueCompacted); + idxHalfLong = 0; + dataItems++; + } + + if (idxHalfByte == 0) { + blockLightCompacted = block.getLight(); + skyLightCompacted = chunk.getSkyLight(x, y, z); + idxHalfByte++; + } else { + blockLightCompacted = (blockLightCompacted << 4) | block.getLight(); + blockLight.writeByte(blockLightCompacted); + skyLightCompacted = (skyLightCompacted << 4) | chunk.getSkyLight(x, y, z); + skyLight.writeByte(skyLightCompacted); + idxHalfByte = 0; + } + + if (!biomeFinally) { + biomes.writeByte(chunk.getBiome(x, z).getId()); + if (x == 15 && z == 15) { + biomeFinally = true; + } + } + } + } + } + + // + // + data.writeUnsignedByte(4); // Bits Per Block + data.writeVarInt(palette.size()); // Size of palette + palette.forEach(data::writeVarInt); // Palette + // + // + data.writeVarInt(dataItems); // Size of Data Array + data.writeBytes(dataArray.toByteArray()); // Data Array + // + // + data.writeBytes(blockLight.toByteArray()); + // + // + data.writeBytes(skyLight.toByteArray()); + // + // + // + data.writeBytes(biomes.toByteArray()); + // + } + + netStream.writeVarInt(data.size()); // Size of Data + netStream.writeBytes(data.toByteArray()); // Data + netStream.writeVarInt(0); // Number of block entities + /* writeNBT */ + } +} 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 a136bd2..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 @@ -10,6 +10,7 @@ import mc.core.network.proto_1_12_2.State; import mc.core.network.proto_1_12_2.TeleportManager; import mc.core.network.proto_1_12_2.netty.wrappers.WrapperNetChannel; import mc.core.network.proto_1_12_2.packets.*; +import mc.core.player.Look; import mc.core.player.Player; import mc.core.player.PlayerManager; import mc.core.player.PlayerMode; @@ -48,7 +49,7 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand .orElseGet(() -> playerManager.createPlayer( packet.getPlayerName(), world.getSpawn(), - world.getSpawn())); + new Look(0f, 0f))); channel.writeAndFlush(new LoginSuccessPacket( player.getUUID(), @@ -79,6 +80,14 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand channel.write(pkt3); channel.flush(); + // One Chunk + ChunkDataPacket pkt8 = new ChunkDataPacket(); + pkt8.setX(0); + pkt8.setZ(0); + pkt8.getChunks().add(world.getChunk(0, 0,0)); + pkt8.setInitChunk(true); + channel.writeAndFlush(pkt8); + // Player Position And Look PlayerPositionAndLookPacket pkt4 = new PlayerPositionAndLookPacket(); pkt4.setLocation(player.getLocation());