Archived
0

Merge & resolve conflicts

This commit is contained in:
Forwolk
2018-08-09 20:59:02 +03:00
34 changed files with 591 additions and 557 deletions

View File

@@ -1,4 +1,4 @@
subprojects { allprojects {
apply plugin: 'java' apply plugin: 'java'
compileJava { compileJava {
@@ -10,7 +10,9 @@ subprojects {
repositories { repositories {
mavenCentral() mavenCentral()
} }
}
subprojects {
ext { ext {
slf4j_version = '1.7.21' slf4j_version = '1.7.21'
spring_version = '4.2.5.RELEASE' spring_version = '4.2.5.RELEASE'
@@ -33,7 +35,6 @@ subprojects {
/* Components */ /* Components */
compile (group: 'org.projectlombok', name: 'lombok', version: '1.16.16') compile (group: 'org.projectlombok', name: 'lombok', version: '1.16.16')
compile 'com.flowpowered:flow-nbt:1.0.0' //Named Binary Tags
} }
task copyDep(type: Copy) { task copyDep(type: Copy) {
@@ -45,3 +46,23 @@ subprojects {
delete 'libs' 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
}

View File

@@ -14,4 +14,6 @@ dependencies {
/* Components */ /* Components */
compile (group: 'commons-io', name: 'commons-io', version: '2.6') compile (group: 'commons-io', name: 'commons-io', version: '2.6')
compile (group: 'com.google.guava', name: 'guava', version: '24.1-jre') 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')
} }

View File

@@ -6,10 +6,9 @@ package mc.core;
import lombok.Data; import lombok.Data;
import mc.core.exception.ResourceUnloadedException; import mc.core.exception.ResourceUnloadedException;
import mc.core.world.Chunk;
import mc.core.world.Region; import mc.core.world.Region;
import mc.core.world.World; import mc.core.world.World;
import sun.reflect.generics.reflectiveObjects.NotImplementedException; import mc.core.world.chunk.Chunk;
import java.io.Serializable; import java.io.Serializable;
import java.lang.ref.Reference; import java.lang.ref.Reference;
@@ -61,6 +60,12 @@ public class Location implements Serializable{
this.world = new WeakReference<>(world); 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) { public void set(Location location) {
this.x = location.x; this.x = location.x;
this.y = location.y; this.y = location.y;

View File

@@ -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));
}
}
}

View File

@@ -2,6 +2,8 @@ package mc.core.world;
import lombok.Getter; import lombok.Getter;
import java.util.EnumSet;
public enum Biome { public enum Biome {
OCEAN(0, "Ocean", 0x0000cd), OCEAN(0, "Ocean", 0x0000cd),
PLAINS(1, "Plains", 0x008000), PLAINS(1, "Plains", 0x008000),
@@ -23,13 +25,24 @@ public enum Biome {
DESERT_HILLS(17, "Desert hills", 0xffe4b5), DESERT_HILLS(17, "Desert hills", 0xffe4b5),
FOREST_HILLS(18, "Forest hills", 0x006400), FOREST_HILLS(18, "Forest hills", 0x006400),
TAIGA_HILLS(19, "Taiga hills", 0xf0f8ff), 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(21, "Jungle", 0xadff2f),
JUNGLE_HILLS(22, "Jungle hills", 0xadff2f), JUNGLE_HILLS(22, "Jungle hills", 0xadff2f),
DEEP_OCEAN(23, "Deep ocean", 0x000080), JUNGLE_HILLS_2(23, "Jungle hills", 0xadff2f), //WTF?
TUNDRA(24, "Tundra", 0xc0c0c0), DEEP_OCEAN(24, "Deep ocean", 0x000080),
SAVANNA(25, "Savana", 0xcd8513), STONE_BEACH(25, "Stone beach", 0xffffff),
SAVANNA_FOREST(26, "Savana forest", 0x8b4513); 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 @Getter
private final int id; private final int id;
@@ -38,6 +51,8 @@ public enum Biome {
@Getter @Getter
private final int color; private final int color;
private final static EnumSet<Biome> waterBiomes = EnumSet.of(OCEAN, RIVER, FROZEN_OCEAN, FROZEN_RIVER, DEEP_OCEAN);
Biome(int id, String name, int color) { Biome(int id, String name, int color) {
this.id = id; this.id = id;
this.name = name; this.name = name;
@@ -47,4 +62,8 @@ public enum Biome {
public static Biome getById(int id) { public static Biome getById(int id) {
return Biome.values()[id]; return Biome.values()[id];
} }
public static boolean isWaterBiome (Biome biome) {
return waterBiomes.contains(biome);
}
} }

View File

@@ -4,7 +4,7 @@
*/ */
package mc.core.world; package mc.core.world;
import mc.core.block.Block; import mc.core.world.block.Block;
import java.io.Serializable; import java.io.Serializable;
@@ -23,14 +23,6 @@ import java.io.Serializable;
*/ */
/* 16x16x16 */ /* 16x16x16 */
public interface ChunkSection extends Serializable{ 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); int getSkyLight(int x, int y, int z);
void setSkyLight(int x, int y, int z, int lightLevel); void setSkyLight(int x, int y, int z, int lightLevel);
@@ -45,7 +37,7 @@ public interface ChunkSection extends Serializable{
int getY(); int getY();
int getZ(); int getZ();
void setBlock(int x, int y, int z, Block block); void setBlock(Block block);
Block getBlock(int x, int y, int z); Block getBlock(int x, int y, int z);
Region getRegion(); Region getRegion();

View File

@@ -2,6 +2,7 @@ package mc.core.world;
import mc.core.serialization.IRegionReaderWriter; import mc.core.serialization.IRegionReaderWriter;
import mc.core.serialization.Serializer; import mc.core.serialization.Serializer;
import mc.core.world.chunk.Chunk;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;

View File

@@ -1,4 +1,4 @@
package mc.core.block; package mc.core.world.block;
import com.flowpowered.nbt.Tag; import com.flowpowered.nbt.Tag;
import lombok.Getter; import lombok.Getter;
@@ -10,11 +10,15 @@ import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
public abstract class AbstractBlock implements Block { public abstract class AbstractBlock implements Block {
@Getter@Setter @Getter
@Setter
private Location location; private Location location;
@Getter @Getter
private int meta; private int meta;
@Getter @Getter
@Setter
private int light = 0; //TODO need to know range of values
@Getter
private final BlockType blockType; private final BlockType blockType;
private final Map<String, Tag<?>> nbtTagsMap = new HashMap<>(); private final Map<String, Tag<?>> nbtTagsMap = new HashMap<>();

View File

@@ -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.Location;
import mc.core.nbt.Taggable; import mc.core.nbt.Taggable;
@@ -29,20 +28,6 @@ import java.io.Serializable;
public interface Block extends Taggable, 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 */ /** Block id */
int getId(); int getId();
@@ -53,6 +38,9 @@ public interface Block extends Taggable, Serializable{
*/ */
int getMeta(); int getMeta();
int getLight();
void setLight(int light);
/** /**
* Getting block type * Getting block type
*/ */

View File

@@ -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));
}
}
}

View File

@@ -1,16 +1,16 @@
package mc.core.block; package mc.core.world.block;
import lombok.Getter; import lombok.Getter;
public enum BlockType { public enum BlockType {
AIR(0, "Air"),
STONE(1, "Stone"), STONE(1, "Stone"),
GRASS(2, "Grass"), GRASS(2, "Grass"),
DIRT(3, "Dirt"), DIRT(3, "Dirt"),
BEDROCK(7, "Bedrock"), BEDROCK(7, "Bedrock"),
WATER(8, "Water"), WATER(8, "Water"),
SAND(12, "Sand"), SAND(12, "Sand"),
SNOW(32, "Snow"), SNOW(32, "Snow");
AIR(0, "Air");
@Getter @Getter
private final int id; private final int id;

View File

@@ -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 { public interface Chunk {

View File

@@ -1,4 +1,6 @@
package mc.core.world; package mc.core.world.chunk;
import mc.core.world.ChunkSection;
import java.util.Optional; import java.util.Optional;

View File

@@ -17,9 +17,11 @@ import java.util.stream.Stream;
public class FlatWorld implements World { public class FlatWorld implements World {
@Getter@Setter @Getter
@Setter
private UUID worldId = UUID.fromString("00000000-0000-0000-C000-000000000046"); private UUID worldId = UUID.fromString("00000000-0000-0000-C000-000000000046");
@Getter@Setter @Getter
@Setter
private String name; private String name;
@Getter @Getter
@@ -49,7 +51,7 @@ public class FlatWorld implements World {
@Override @Override
public void setRegion(int x, int z, Region region) { public void setRegion(int x, int z, Region region) {
throw new UnsupportedOperationException();
} }
@Override @Override

View File

@@ -4,48 +4,15 @@
*/ */
package mc.world.flat; 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.Biome;
import mc.core.world.ChunkSection; import mc.core.world.ChunkSection;
import mc.core.world.Region; import mc.core.world.Region;
import mc.core.world.World; 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 { 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 @Override
public int getSkyLight(int x, int y, int z) { public int getSkyLight(int x, int y, int z) {
if (y <= 3) return 0; if (y <= 3) return 0;
@@ -93,7 +60,7 @@ public class SimpleChunkSection implements ChunkSection {
} }
@Override @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); if (y == 0) return blockFactory.create(BlockType.BEDROCK, 0);
else if (y >= 1 && y <= 2) return blockFactory.create(BlockType.DIRT, 0); else if (y >= 1 && y <= 2) return blockFactory.create(BlockType.DIRT, 0);
else if (y == 3) return blockFactory.create(BlockType.GRASS, 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 @Override

View File

@@ -2,10 +2,10 @@ package mc.world.generated_world.chunk;
import lombok.Getter; import lombok.Getter;
import mc.core.exception.ResourceUnloadedException; import mc.core.exception.ResourceUnloadedException;
import mc.core.world.Chunk;
import mc.core.world.ChunkSection; import mc.core.world.ChunkSection;
import mc.core.world.Region; import mc.core.world.Region;
import mc.core.world.World; import mc.core.world.World;
import mc.core.world.chunk.Chunk;
import java.lang.ref.Reference; import java.lang.ref.Reference;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;

View File

@@ -1,13 +1,14 @@
package mc.world.generated_world.chunk; package mc.world.generated_world.chunk;
import lombok.Getter; import lombok.Getter;
import mc.core.block.Block;
import mc.core.block.BlockType;
import mc.core.exception.ResourceUnloadedException; import mc.core.exception.ResourceUnloadedException;
import mc.core.world.Biome; import mc.core.world.Biome;
import mc.core.world.ChunkSection; import mc.core.world.ChunkSection;
import mc.core.world.Region; import mc.core.world.Region;
import mc.core.world.World; 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.Reference;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@@ -23,6 +24,7 @@ public class ChunkSectionImpl implements ChunkSection {
private final int z; private final int z;
private final Block[][][] blocks = new Block[WORLD_CHUNK_SIZE][WORLD_CHUNK_SIZE][WORLD_CHUNK_SIZE]; private final Block[][][] blocks = new Block[WORLD_CHUNK_SIZE][WORLD_CHUNK_SIZE][WORLD_CHUNK_SIZE];
private final transient Reference<Region> region; private final transient Reference<Region> region;
private BlockFactory blockFactory = new BlockFactory();
public ChunkSectionImpl(int x, int y, int z, Region region) { public ChunkSectionImpl(int x, int y, int z, Region region) {
this.x = x; this.x = x;
@@ -31,36 +33,6 @@ public class ChunkSectionImpl implements ChunkSection {
this.region = new WeakReference<>(region); 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 @Override
public int getSkyLight(int x, int y, int z) { public int getSkyLight(int x, int y, int z) {
return 15; return 15;
@@ -92,19 +64,19 @@ public class ChunkSectionImpl implements ChunkSection {
} }
@Override @Override
public void setBlock(int x, int y, int z, Block block) { public void setBlock(Block block) {
if (block.getBlockType() == BlockType.AIR) { if (block.getBlockType() == BlockType.AIR) {
blocks[x][y][z] = null; blocks[block.getLocation().getBlockX()][block.getLocation().getBlockY()][block.getLocation().getBlockZ()] = null;
return; return;
} }
blocks[x][y][z] = block; blocks[block.getLocation().getBlockX()][block.getLocation().getBlockY()][block.getLocation().getBlockZ()] = block;
} }
@Override @Override
public Block getBlock(int x, int y, int z) { public Block getBlock(int x, int y, int z) {
Block block = blocks[x][y][z]; Block block = blocks[x][y][z];
if (block == null) { if (block == null) {
return Block.airBlock(x, y, z); return blockFactory.create(BlockType.AIR, 0, x, y, z);
} }
return blocks[x][y][z]; return blocks[x][y][z];
} }

View File

@@ -1,146 +1,108 @@
package mc.world.generated_world.chunk; 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.ChunkSection;
import mc.core.world.Region; import mc.core.world.Region;
import mc.core.world.World; 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.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ChunkSectionProxy implements ChunkSection { public class ChunkSectionProxy implements ChunkSection {
private final ChunkSection chunkSection; private final ChunkSection chunk;
private volatile transient long lastUsage = System.currentTimeMillis(); private volatile transient long lastUsage = System.currentTimeMillis();
private final transient ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final transient ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public ChunkSectionProxy(ChunkSection chunkSection) { public ChunkSectionProxy(ChunkSection chunk) {
this.chunkSection = chunkSection; this.chunk = chunk;
} }
public long getLastUsage() { public long getLastUsage() {
synchronized (chunkSection) { synchronized (chunk) {
return lastUsage; return lastUsage;
} }
} }
private final void use () { private final void use () {
synchronized (chunkSection) { synchronized (chunk) {
lastUsage = System.currentTimeMillis(); lastUsage = System.currentTimeMillis();
} }
} }
@Override @Override
public int getBlockType(int x, int y, int z) { public Block getBlock(int x, int y, int z) {
use(); use();
return chunkSection.getBlockType(x, y, z); return chunk.getBlock(x, y, z);
} }
@Override @Override
public void setBlockType(int x, int y, int z, int type) { public Region getRegion() {
use(); return chunk.getRegion();
chunkSection.setBlockType(x, y, z, type);
} }
@Override @Override
public int getBlockMetadata(int x, int y, int z) { public World getWorld() {
use(); return chunk.getWorld();
return chunkSection.getBlockMetadata(x, y, z);
} }
@Override @Override
public void setBlockMetadata(int x, int y, int z, int metadata) { public void setBlock(Block block) {
use(); use();
chunkSection.setBlockMetadata(x, y, z, metadata); chunk.setBlock(block);
}
@Override
public int getBlockLight(int x, int y, int z) {
use();
return chunkSection.getBlockLight(x, y, z);
}
@Override
public void setBlockLight(int x, int y, int z, int lightLevel) {
use();
chunkSection.setBlockLight(x, y, z, lightLevel);
} }
@Override @Override
public int getSkyLight(int x, int y, int z) { public int getSkyLight(int x, int y, int z) {
use(); use();
return chunkSection.getSkyLight(x, y, z); return chunk.getSkyLight(x, y, z);
} }
@Override @Override
public void setSkyLight(int x, int y, int z, int lightLevel) { public void setSkyLight(int x, int y, int z, int lightLevel) {
use(); use();
chunkSection.setSkyLight(x, y, z, lightLevel); chunk.setSkyLight(x, y, z, lightLevel);
} }
@Override @Override
public int getAddition(int x, int y, int z) { public int getAddition(int x, int y, int z) {
use(); use();
return chunkSection.getAddition(x, y, z); return chunk.getAddition(x, y, z);
} }
@Override @Override
public void setAddition(int x, int y, int z, int value) { public void setAddition(int x, int y, int z, int value) {
use(); use();
chunkSection.setAddition(x, y, z, value); chunk.setAddition(x, y, z, value);
} }
@Override @Override
public Biome getBiome(int x, int z) { public Biome getBiome(int x, int z) {
use(); use();
return chunkSection.getBiome(x, z); return chunk.getBiome(x, z);
} }
@Override @Override
public void setBiome(int x, int z, Biome biome) { public void setBiome(int x, int z, Biome biome) {
use(); use();
chunkSection.setBiome(x, z, biome); chunk.setBiome(x, z, biome);
} }
@Override @Override
public int getX() { public int getX() {
use(); use();
return chunkSection.getX(); return chunk.getX();
} }
@Override @Override
public int getY() { public int getY() {
use(); use();
return chunkSection.getY(); return chunk.getY();
} }
@Override @Override
public int getZ() { public int getZ() {
use(); use();
return chunkSection.getZ(); return chunk.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();
} }
} }

View File

@@ -3,6 +3,8 @@ package mc.world.generated_world.chunk;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import mc.core.serialization.Serializer; import mc.core.serialization.Serializer;
import mc.core.world.*; 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.ChunkReader;
import mc.world.generated_world.serialization.RegionReaderWriter; import mc.world.generated_world.serialization.RegionReaderWriter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -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));
}
}

View File

@@ -2,12 +2,10 @@ package mc.world.generated_world.generator;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import mc.core.block.BlockFactory; import mc.core.world.block.BlockFactory;
import mc.core.block.BlockType; import mc.core.world.block.BlockType;
import mc.core.world.*; import mc.core.world.*;
import mc.world.generated_world.region.RegionImpl; import mc.world.generated_world.region.RegionImpl;
import mc.world.generated_world.serialization.ChunkSerializer;
import mc.world.generated_world.serialization.RegionReaderWriter;
import mc.world.generated_world.world.CubicWorld; import mc.world.generated_world.world.CubicWorld;
import mc.world.generated_world.world.Temperature; import mc.world.generated_world.world.Temperature;
import mc.world.generated_world.world.Wetness; import mc.world.generated_world.world.Wetness;
@@ -15,6 +13,7 @@ import mc.world.generated_world.world.Wetness;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static mc.world.generated_world.WorldConstants.*; import static mc.world.generated_world.WorldConstants.*;
@@ -25,24 +24,26 @@ public class SeedBasedWorldGenerator implements WorldGenerator {
public static void main(String[] args) throws Exception{ public static void main(String[] args) throws Exception{
WorldGenerator worldGenerator = new SeedBasedWorldGenerator(); WorldGenerator worldGenerator = new SeedBasedWorldGenerator();
World world = new CubicWorld(UUID.fromString("00000000-0000-0000-C000-000000000046"), 2626949); 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()))); region.save(new ChunkSerializer(), new RegionReaderWriter(new File("worlds", world.getWorldId().toString())));
// new WorldReaderWriter(new File("worlds")).writeWorldInfo(world); new WorldReaderWriter(new File("worlds")).writeWorldInfo(world);*/
/*worldGenerator.generateRegion(1, 0, world);
worldGenerator.generateRegion(-1, 0, world); createBigImage(worldGenerator, world);
worldGenerator.generateRegion(0, 1, world); }
worldGenerator.generateRegion(0, -1, world);
worldGenerator.generateRegion(-1, -1, world); private static void createBigImage (WorldGenerator worldGenerator, World world) throws IOException {
worldGenerator.generateRegion(1, -1, world);
worldGenerator.generateRegion(-1, 1, world);
worldGenerator.generateRegion(1, 1, world);
BufferedImage image = new BufferedImage(3 * 256, 3 * 256, BufferedImage.TYPE_INT_RGB); BufferedImage image = new BufferedImage(3 * 256, 3 * 256, BufferedImage.TYPE_INT_RGB);
BufferedImage currentImage; for (int x = 0; x <= 2; x ++) {
int shiftX; for (int z = 0; z <= 2; z ++) {
int shiftY; worldGenerator.generateRegion(x - 1, z - 1, world);
currentImage = ImageIO.read(new File("out/0.0", "biomeMap.png")); addToBigImage(x, z, image);
shiftX = 1; }
shiftY = 1; }
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 x = 0; x < 256; x ++){
for (int y = 0; y < 256; y ++){ for (int y = 0; y < 256; y ++){
int tx = 256 * shiftX + x; int tx = 256 * shiftX + x;
@@ -50,87 +51,6 @@ public class SeedBasedWorldGenerator implements WorldGenerator {
image.setRGB(tx, ty, currentImage.getRGB(x, y)); 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 @Override
@@ -160,7 +80,7 @@ public class SeedBasedWorldGenerator implements WorldGenerator {
return 40960 + x; 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()); log.debug("Starting generating region [{}, {}] for world '{}' with seed '{}'", region.getX(), region.getZ(), world.getWorldId(), world.getSeed());
noiseGenerator = new NoiseGenerator(world.getSeed()); noiseGenerator = new NoiseGenerator(world.getSeed());
@@ -305,37 +225,37 @@ public class SeedBasedWorldGenerator implements WorldGenerator {
region.setBiome(x, z, biomes[x][z]); region.setBiome(x, z, biomes[x][z]);
if (heightMap[x][z] < WORLD_SEA_LEVEL) { if (heightMap[x][z] < WORLD_SEA_LEVEL) {
for (int y = 0; y < WORLD_SEA_LEVEL; y ++) { 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) { 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; continue;
} }
if (y < heightMap[x][z]) { if (y < heightMap[x][z]) {
if (y < heightMap[x][z] - grassMap[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 { } 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 { } 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 { } else {
for (int y = 0; y < heightMap[x][z]; y++) { 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) { 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; continue;
} }
if (y < heightMap[x][z] - grassMap[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 { } else {
if (biomes[x][z] == Biome.DESERT || biomes[x][z] == Biome.DESERT_HILLS) { 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) { } 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 { } 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 (temperature == Temperature.FROST) {
if (wetness == Wetness.DRIEST || wetness == Wetness.DRY) { if (wetness == Wetness.DRIEST || wetness == Wetness.DRY) {
return Biome.TUNDRA; return Biome.COLD_TAIGA;
} else { } else {
if (height > HILLS_HEIGHT) { if (height > HILLS_HEIGHT) {
return Biome.ICE_MOUNTAINS; return Biome.ICE_MOUNTAINS;
@@ -433,7 +353,7 @@ public class SeedBasedWorldGenerator implements WorldGenerator {
return Biome.FOREST; return Biome.FOREST;
} }
} else { } 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));
}
}
} }

View File

@@ -6,10 +6,12 @@ import mc.core.exception.ResourceUnloadedException;
import mc.core.serialization.IRegionReaderWriter; import mc.core.serialization.IRegionReaderWriter;
import mc.core.serialization.Serializer; import mc.core.serialization.Serializer;
import mc.core.world.*; import mc.core.world.*;
import mc.world.generated_world.chunk.ChunkImpl; import mc.core.world.chunk.Chunk;
import mc.world.generated_world.chunk.ChunkSectionImpl; import mc.core.world.chunk.ChunkLoader;
import mc.world.generated_world.chunk.ChunkSectionProxy; import mc.world.generated_world.chunk.ChunkSectionProxy;
import mc.world.generated_world.chunk.InMemoryCacheChunkLoader; 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 org.springframework.beans.factory.annotation.Autowired;
import java.io.File; import java.io.File;

View File

@@ -1,8 +1,8 @@
package mc.world.generated_world.serialization; package mc.world.generated_world.serialization;
import mc.core.block.Block; import mc.core.world.block.Block;
import mc.core.block.BlockFactory; import mc.core.world.block.BlockFactory;
import mc.core.block.BlockType; import mc.core.world.block.BlockType;
import mc.core.serialization.Deserializer; import mc.core.serialization.Deserializer;
import mc.core.serialization.Serializer; import mc.core.serialization.Serializer;
import mc.core.world.ChunkSection; import mc.core.world.ChunkSection;

View File

@@ -1,7 +1,7 @@
package mc.world.generated_world.serialization; package mc.world.generated_world.serialization;
import mc.core.Location; import mc.core.Location;
import mc.core.block.Block; import mc.core.world.block.Block;
import mc.core.serialization.Deserializer; import mc.core.serialization.Deserializer;
import mc.core.serialization.IChunkReader; import mc.core.serialization.IChunkReader;
import mc.core.world.ChunkSection; import mc.core.world.ChunkSection;
@@ -41,8 +41,7 @@ public class ChunkReader implements IChunkReader{
blockBytes[1] = chunkBytes[1 + 3 * i]; blockBytes[1] = chunkBytes[1 + 3 * i];
blockBytes[2] = chunkBytes[2 + 3 * i]; blockBytes[2] = chunkBytes[2 + 3 * i];
Block block = blockDeserializer.deserialize(blockBytes); Block block = blockDeserializer.deserialize(blockBytes);
Location blockLocation = block.getLocation(); chunkSection.setBlock(block);
chunkSection.setBlock(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ(), block);
} }
return chunkSection; return chunkSection;
} }

View File

@@ -1,9 +1,9 @@
package mc.world.generated_world.serialization; package mc.world.generated_world.serialization;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import mc.core.block.Block; import mc.core.world.block.Block;
import mc.core.block.BlockFactory; import mc.core.world.block.BlockFactory;
import mc.core.block.BlockType; import mc.core.world.block.BlockType;
import mc.core.serialization.Serializer; import mc.core.serialization.Serializer;
import mc.core.world.ChunkSection; import mc.core.world.ChunkSection;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -4,34 +4,66 @@ import com.flowpowered.nbt.Tag;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import mc.core.Direction;
import mc.core.Location; import mc.core.Location;
import mc.core.WarpPosition; import mc.core.WarpPosition;
import mc.core.block.BlockType;
import mc.core.player.Look; import mc.core.player.Look;
import mc.core.world.ChunkSection; import mc.core.world.ChunkSection;
import mc.core.world.IWorldType; import mc.core.world.IWorldType;
import mc.core.world.Region; import mc.core.world.Region;
import mc.core.world.World; 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 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.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream; import java.util.stream.Stream;
import static mc.world.generated_world.WorldConstants.WORLD_CHUNK_SIZE; import static mc.world.generated_world.WorldConstants.*;
import static mc.world.generated_world.WorldConstants.WORLD_MAX_HEIGHT;
/*
* NORTH
*
* EAST WEST
*
* SOUTH
*
* + ----> X
* |
* |
* |
* V Z
*/
@Slf4j @Slf4j
public class CubicWorld implements World { 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 @Getter
private final UUID worldId; private final UUID worldId;
private final int seed; private final int seed;
private volatile WarpPosition warpPosition; private volatile WarpPosition warpPosition;
private final transient Object spawnLocationLock = new Object(); private final transient Object spawnLocationLock = new Object();
private final Map<String, Tag<?>> nbtTagMap = new HashMap<>(); private final Map<String, Tag<?>> nbtTagMap = new HashMap<>();
@Autowired
private RegionManager regionManager;
@Getter@Setter @Getter@Setter
private String name; private String name;
@@ -62,23 +94,8 @@ public class CubicWorld implements World {
@Override @Override
public WarpPosition getSpawn() { public WarpPosition getSpawn() {
if (warpPosition == null) { /* FIXME */
synchronized (spawnLocationLock) { return new WarpPosition(new Location(0, 100, 0), new Look(0, 0));
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;
} }
@Override @Override
@@ -100,12 +117,42 @@ public class CubicWorld implements World {
@Override @Override
public Region getRegion(int x, int z) { 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 @Override
public void setRegion(int x, int z, Region region) { public void setRegion(int x, int z, Region region) {
try {
regionSaveLock.lock();
regions[x - pointX][z - pointZ] = region;
} finally {
regionSaveLock.unlock();
}
} }
@Override @Override
@@ -127,4 +174,53 @@ public class CubicWorld implements World {
public Stream<Tag<?>> tagStream() { public Stream<Tag<?>> tagStream() {
return nbtTagMap.values().stream(); 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();
}
}
} }

View File

@@ -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();
}
}
}

View File

@@ -1,6 +1,7 @@
package mc.world.generated_world; package mc.world.generated_world;
import mc.world.generated_world.generator.SeedRandomGenerator; import mc.world.generated_world.generator.SeedRandomGenerator;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -9,6 +10,7 @@ import java.io.File;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@Ignore
public class SeedRandomGeneratorTest { public class SeedRandomGeneratorTest {
@Test @Test

View File

@@ -65,6 +65,10 @@ public class ByteArrayOutputNetStream extends NetOutputStream_p340 {
writeLong(Double.doubleToLongBits(value)); writeLong(Double.doubleToLongBits(value));
} }
public int size() {
return baos.size();
}
public byte[] toByteArray() { public byte[] toByteArray() {
return baos.toByteArray(); return baos.toByteArray();
} }

View File

@@ -14,17 +14,16 @@ import java.util.UUID;
public abstract class NetOutputStream_p340 extends NetOutputStream { public abstract class NetOutputStream_p340 extends NetOutputStream {
@Override @Override
public void writeVarInt(int value) { public void writeVarInt(int value) {
do { while ((value & -128) != 0) {
byte temp = (byte)(value & 0b01111111); writeByte(value & 127 | 128);
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
value >>>= 7; value >>>= 7;
if (value != 0) { }
temp |= 0b10000000;
} writeByte(value);
writeByte(temp);
} while (value != 0);
} }
@Override @Override
public void writeString(String value) { public void writeString(String value) {
if (value.length() > Short.MAX_VALUE) { if (value.length() > Short.MAX_VALUE) {

View File

@@ -87,6 +87,7 @@ public enum State {
.put(PluginMessagePacket.class, 0x18) .put(PluginMessagePacket.class, 0x18)
.put(ChangeGameState.class, 0x1E) .put(ChangeGameState.class, 0x1E)
.put(KeepAlivePacket.class, 0x1F) .put(KeepAlivePacket.class, 0x1F)
.put(ChunkDataPacket.class, 0x20)
.put(JoinGamePacket.class, 0x23) .put(JoinGamePacket.class, 0x23)
.put(PlayerAbilitiesPacket.class, 0x2C) .put(PlayerAbilitiesPacket.class, 0x2C)
.put(PlayerListItemPacket.class, 0x2E) .put(PlayerListItemPacket.class, 0x2E)

View File

@@ -0,0 +1,188 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 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<ChunkSection> 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<Integer> 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;
}
}
}
}
}
// <Chunk Section>
// <Palette>
data.writeUnsignedByte(4); // Bits Per Block
data.writeVarInt(palette.size()); // Size of palette
palette.forEach(data::writeVarInt); // Palette
// </Palette>
// <Data Array>
data.writeVarInt(dataItems); // Size of Data Array
data.writeBytes(dataArray.toByteArray()); // Data Array
// </Data Array>
// <Block Light>
data.writeBytes(blockLight.toByteArray());
// </Block Light>
// <Sky Light>
data.writeBytes(skyLight.toByteArray());
// </Sky Light>
// </Chunk Section>
// <Biomes>
data.writeBytes(biomes.toByteArray());
// </Biomes>
}
netStream.writeVarInt(data.size()); // Size of Data
netStream.writeBytes(data.toByteArray()); // Data
netStream.writeVarInt(0); // Number of block entities
/* writeNBT */
}
}

View File

@@ -48,8 +48,8 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand
Player player = playerManager.getPlayer(packet.getPlayerName()) Player player = playerManager.getPlayer(packet.getPlayerName())
.orElseGet(() -> playerManager.createPlayer( .orElseGet(() -> playerManager.createPlayer(
packet.getPlayerName(), packet.getPlayerName(),
world.getSpawn().getLocation(), world.getSpawn(),
world.getSpawn().getLook())); new Look(0f, 0f)));
channel.writeAndFlush(new LoginSuccessPacket( channel.writeAndFlush(new LoginSuccessPacket(
player.getUUID(), player.getUUID(),
@@ -68,7 +68,7 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand
// Spawn Position // Spawn Position
SpawnPositionPacket pkt2 = new SpawnPositionPacket(); SpawnPositionPacket pkt2 = new SpawnPositionPacket();
pkt2.setLocation(world.getSpawn().getLocation()); pkt2.setLocation(world.getSpawn());
channel.write(pkt2); channel.write(pkt2);
// Player Abilities // Player Abilities
@@ -80,6 +80,14 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand
channel.write(pkt3); channel.write(pkt3);
channel.flush(); 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 // Player Position And Look
PlayerPositionAndLookPacket pkt4 = new PlayerPositionAndLookPacket(); PlayerPositionAndLookPacket pkt4 = new PlayerPositionAndLookPacket();
pkt4.setLocation(player.getLocation()); pkt4.setLocation(player.getLocation());

View File

@@ -5,5 +5,5 @@ include('flat_world')
include('vanilla_commands') include('vanilla_commands')
include('proto_1.12.2') // Protocol 1.12.2 include('proto_1.12.2') // Protocol 1.12.2
include('proto_1.12.2_netty') // Protocol 1.12.2 (Netty impl.) include('proto_1.12.2_netty') // Protocol 1.12.2 (Netty impl.)
include 'generated_world' include('generated_world')