Merge remote-tracking branch 'origin/develop' into anarok/merge/loop-3
# Conflicts: # build.gradle # proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java # settings.gradle
This commit is contained in:
24
build.gradle
24
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
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Biome> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<String, Tag<?>> nbtTagsMap = new HashMap<>();
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
28
core/src/main/java/mc/core/world/block/BlockFactory.java
Normal file
28
core/src/main/java/mc/core/world/block/BlockFactory.java
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
package mc.core.world;
|
||||
package mc.core.world.chunk;
|
||||
|
||||
import mc.core.world.ChunkSection;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> 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];
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<String, Tag<?>> 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<Tag<?>> tagStream() {
|
||||
return nbtTagMap.values().stream();
|
||||
}
|
||||
|
||||
private void checkCoordsInCache (int x, int z) {
|
||||
if (x < pointX) {
|
||||
addLines(Direction.EAST, pointX - x);
|
||||
} else if (x > pointX + sizeX) {
|
||||
addLines(Direction.WEST, x - (pointX + sizeX));
|
||||
} else if (z < pointZ) {
|
||||
addLines(Direction.NORTH, pointZ - z);
|
||||
} else if (z > pointZ + sizeZ) {
|
||||
addLines(Direction.SOUTH, z - (pointZ + sizeZ));
|
||||
}
|
||||
}
|
||||
|
||||
private void addLines (Direction direction, int amount) {
|
||||
int addBeforeX = 0;
|
||||
int addAfterX = 0;
|
||||
int addBeforeZ = 0;
|
||||
int addAfterZ = 0;
|
||||
switch (direction) {
|
||||
case NORTH:
|
||||
addBeforeZ = amount;
|
||||
break;
|
||||
case EAST:
|
||||
addBeforeX = amount;
|
||||
break;
|
||||
case WEST:
|
||||
addAfterX = amount;
|
||||
break;
|
||||
case SOUTH:
|
||||
addAfterZ = amount;
|
||||
break;
|
||||
}
|
||||
try {
|
||||
regionSaveLock.lock();
|
||||
int tempSizeX = sizeX + addAfterX + addBeforeX;
|
||||
int tempSizeZ = sizeZ + addAfterZ + addBeforeZ;
|
||||
Region[][] temp = new Region[tempSizeX][tempSizeZ];
|
||||
for (int x = 0; x < sizeX; x ++) {
|
||||
System.arraycopy(regions[x], 0, temp[x + addBeforeX], addBeforeZ, sizeZ);
|
||||
}
|
||||
|
||||
this.sizeX = tempSizeX;
|
||||
this.sizeZ = tempSizeZ;
|
||||
this.pointX = pointX - addBeforeX;
|
||||
this.pointZ = pointZ - addBeforeZ;
|
||||
} finally {
|
||||
regionSaveLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
package mc.world.generated_world.world;
|
||||
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import mc.core.Direction;
|
||||
import mc.core.world.Region;
|
||||
import mc.core.world.World;
|
||||
import mc.core.world.WorldGenerator;
|
||||
import mc.world.generated_world.serialization.RegionReaderWriter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static mc.world.generated_world.WorldConstants.REGION_FILE_NAME_TEMPLATE;
|
||||
|
||||
/*
|
||||
* NORTH
|
||||
*
|
||||
* EAST WEST
|
||||
*
|
||||
* SOUTH
|
||||
*
|
||||
* + ----> X
|
||||
* |
|
||||
* |
|
||||
* |
|
||||
* V Z
|
||||
*/
|
||||
@Slf4j
|
||||
public class RegionManager {
|
||||
private final World world;
|
||||
private int pointX = -1;
|
||||
private int pointZ = -1;
|
||||
private int sizeX = 2;
|
||||
private int sizeZ = 2;
|
||||
private Region[][] regions = new Region[sizeX][sizeZ];
|
||||
private final Lock regionSaveLock = new ReentrantLock();
|
||||
@Autowired
|
||||
private RegionReaderWriter regionReaderWriter;
|
||||
@Autowired
|
||||
private WorldGenerator worldGenerator;
|
||||
@Setter
|
||||
private boolean autoSaveRegionAfterGenerating = true;
|
||||
|
||||
|
||||
public RegionManager(World world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public void setRegion (Region region) {
|
||||
int x = region.getX();
|
||||
int z = region.getZ();
|
||||
|
||||
try {
|
||||
regionSaveLock.lock();
|
||||
regions[x - pointX][z - pointZ] = region;
|
||||
} finally {
|
||||
regionSaveLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCoordsInCache (int x, int z) {
|
||||
if (x < pointX) {
|
||||
addLines(Direction.EAST, pointX - x);
|
||||
} else if (x > pointX + sizeX) {
|
||||
addLines(Direction.WEST, x - (pointX + sizeX));
|
||||
} else if (z < pointZ) {
|
||||
addLines(Direction.NORTH, pointZ - z);
|
||||
} else if (z > pointZ + sizeZ) {
|
||||
addLines(Direction.SOUTH, z - (pointZ + sizeZ));
|
||||
}
|
||||
}
|
||||
|
||||
public Region getRegion (int x, int z) {
|
||||
checkCoordsInCache(x, z);
|
||||
Region region;
|
||||
if (regions[x - pointX][z - pointZ] == null) {
|
||||
File file = new File(new File("worlds", world.getWorldId().toString()), MessageFormat.format(REGION_FILE_NAME_TEMPLATE, x, z));
|
||||
if (!file.exists()) {
|
||||
region = worldGenerator.generateRegion(x, z, world);
|
||||
if (autoSaveRegionAfterGenerating) {
|
||||
try {
|
||||
regionReaderWriter.write(region);
|
||||
} catch (IOException e) {
|
||||
log.error("Error occurred while saving region data");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
region = regionReaderWriter.read(x, z, world);
|
||||
} catch (IOException e) {
|
||||
log.error("Error occurred while loading region");
|
||||
region = null;
|
||||
}
|
||||
}
|
||||
setRegion(region);
|
||||
} else {
|
||||
region = regions[x - pointX][z - pointZ];
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
private void addLines (Direction direction, int amount) {
|
||||
int addBeforeX = 0;
|
||||
int addAfterX = 0;
|
||||
int addBeforeZ = 0;
|
||||
int addAfterZ = 0;
|
||||
switch (direction) {
|
||||
case NORTH:
|
||||
addBeforeZ = amount;
|
||||
break;
|
||||
case EAST:
|
||||
addBeforeX = amount;
|
||||
break;
|
||||
case WEST:
|
||||
addAfterX = amount;
|
||||
break;
|
||||
case SOUTH:
|
||||
addAfterZ = amount;
|
||||
break;
|
||||
}
|
||||
try {
|
||||
int tempSizeX = sizeX + addAfterX + addBeforeX;
|
||||
int tempSizeZ = sizeZ + addAfterZ + addBeforeZ;
|
||||
Region[][] temp = new Region[tempSizeX][tempSizeZ];
|
||||
for (int x = 0; x < sizeX; x ++) {
|
||||
System.arraycopy(regions[x], 0, temp[x + addBeforeX], addBeforeZ, sizeZ);
|
||||
}
|
||||
|
||||
this.sizeX = tempSizeX;
|
||||
this.sizeZ = tempSizeZ;
|
||||
this.pointX = pointX - addBeforeX;
|
||||
this.pointZ = pointZ - addBeforeZ;
|
||||
} finally {
|
||||
regionSaveLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package mc.world.generated_world;
|
||||
|
||||
import mc.world.generated_world.generator.SeedRandomGenerator;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -9,6 +10,7 @@ import java.io.File;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@Ignore
|
||||
public class SeedRandomGeneratorTest {
|
||||
|
||||
@Test
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user