Archived
0

Удяляем все модули, кроме core

This commit is contained in:
2019-01-29 12:02:11 +03:00
parent ed51174b4d
commit 9826df5d5b
117 changed files with 6 additions and 6868 deletions

View File

@@ -1,9 +0,0 @@
group 'mc'
version '1.0-SNAPSHOT'
dependencies {
/* Core */
compile_excludeCopy project(':core')
compile (group: 'net.sf.trove4j', name: 'trove4j', version: '3.0.3')
}

View File

@@ -1,73 +0,0 @@
package com.flowpowered.nbt;
import java.util.Arrays;
public class LongArrayTag extends Tag<long[]> {
/**
* The value.
*/
private final long[] value;
/**
* Creates the tag.
*
* @param name The name.
* @param value The value.
*/
public LongArrayTag(String name, long[] value) {
super(TagType.TAG_LONG_ARRAY, name);
this.value = value;
}
@Override
public long[] getValue() {
return value;
}
@Override
public String toString() {
StringBuilder hex = new StringBuilder();
for (long s : value) {
String hexDigits = Long.toHexString(s).toUpperCase();
if (hexDigits.length() == 1) {
hex.append("0");
}
hex.append(hexDigits).append(" ");
}
String name = getName();
String append = "";
if (name != null && !name.equals("")) {
append = "(\"" + this.getName() + "\")";
}
return "TAG_Long_Array" + append + ": " + hex.toString();
}
@Override
public LongArrayTag clone() {
long[] clonedArray = cloneArray(value);
return new LongArrayTag(getName(), clonedArray);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof IntArrayTag)) {
return false;
}
LongArrayTag tag = (LongArrayTag) other;
return Arrays.equals(value, tag.value) && getName().equals(tag.getName());
}
private long[] cloneArray(long[] longArray) {
if (longArray == null) {
return null;
} else {
int length = longArray.length;
byte[] newArray = new byte[length];
System.arraycopy(longArray, 0, newArray, 0, length);
return longArray;
}
}
}

View File

@@ -1,38 +0,0 @@
package com.flowpowered.nbt;
import java.nio.charset.Charset;
/**
* A class which holds constant values.
*/
public final class NBTConstants {
/**
* The character set used by NBT (UTF-8).
*/
public static final Charset CHARSET = Charset.forName("UTF-8");
/**
* Tag type constants.
*/
@Deprecated
public static final int TYPE_END = TagType.TAG_END.getId(),
TYPE_BYTE = TagType.TAG_BYTE.getId(),
TYPE_SHORT = TagType.TAG_SHORT.getId(),
TYPE_INT = TagType.TAG_INT.getId(),
TYPE_LONG = TagType.TAG_LONG.getId(),
TYPE_FLOAT = TagType.TAG_FLOAT.getId(),
TYPE_DOUBLE = TagType.TAG_DOUBLE.getId(),
TYPE_BYTE_ARRAY = TagType.TAG_BYTE_ARRAY.getId(),
TYPE_STRING = TagType.TAG_STRING.getId(),
TYPE_LIST = TagType.TAG_LIST.getId(),
TYPE_COMPOUND = TagType.TAG_COMPOUND.getId(),
TYPE_INT_ARRAY = TagType.TAG_INT_ARRAY.getId(),
TYPE_SHORT_ARRAY = TagType.TAG_SHORT_ARRAY.getId(),
TYPE_LONG_ARRAY = TagType.TAG_LONG_ARRAY.getId();
/**
* Default private constructor.
*/
private NBTConstants() {
}
}

View File

@@ -1,94 +0,0 @@
package com.flowpowered.nbt;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public enum TagType {
TAG_END(EndTag.class, "TAG_End", 0),
TAG_BYTE(ByteTag.class, "TAG_Byte", 1),
TAG_SHORT(ShortTag.class, "TAG_Short", 2),
TAG_INT(IntTag.class, "TAG_Int", 3),
TAG_LONG(LongTag.class, "TAG_Long", 4),
TAG_FLOAT(FloatTag.class, "TAG_Float", 5),
TAG_DOUBLE(DoubleTag.class, "TAG_Double", 6),
TAG_BYTE_ARRAY(ByteArrayTag.class, "TAG_Byte_Array", 7),
TAG_STRING(StringTag.class, "TAG_String", 8),
@SuppressWarnings("unchecked")
TAG_LIST((Class) ListTag.class, "TAG_List", 9),
// Java generics, y u so suck
TAG_COMPOUND(CompoundTag.class, "TAG_Compound", 10),
TAG_INT_ARRAY(IntArrayTag.class, "TAG_Int_Array", 11),
TAG_LONG_ARRAY(LongArrayTag.class, "TAG_Long_Array", 12),
TAG_SHORT_ARRAY(ShortArrayTag.class, "TAG_Short_Array", 100),;
private static final Map<Class<? extends Tag<?>>, TagType> BY_CLASS = new HashMap<Class<? extends Tag<?>>, TagType>();
private static final Map<String, TagType> BY_NAME = new HashMap<String, TagType>();
private static final TagType[] BY_ID;
static {
BY_ID = new TagType[BaseData.maxId + 1];
for (TagType type : TagType.values()) {
BY_CLASS.put(type.getTagClass(), type);
BY_NAME.put(type.getTypeName(), type);
BY_ID[type.getId()] = type;
}
}
private final Class<? extends Tag<?>> tagClass;
private final String typeName;
private final int id;
private TagType(Class<? extends Tag<?>> tagClass, String typeName, int id) {
this.tagClass = tagClass;
this.typeName = typeName;
this.id = id;
// Such a hack, shame that Java makes this such a pain
if (this.id > BaseData.maxId) {
BaseData.maxId = this.id;
}
}
public Class<? extends Tag<?>> getTagClass() {
return tagClass;
}
public String getTypeName() {
return typeName;
}
public int getId() {
return id;
}
public static TagType getByTagClass(Class<? extends Tag<?>> clazz) {
TagType ret = BY_CLASS.get(clazz);
if (ret == null) {
throw new IllegalArgumentException("Tag type " + clazz + " is unknown!");
}
return ret;
}
public static TagType getByTypeName(String typeName) {
TagType ret = BY_NAME.get(typeName);
if (ret == null) {
throw new IllegalArgumentException("Tag type " + typeName + " is unknown!");
}
return ret;
}
public static TagType getById(int id) {
if (id >= 0 && id < BY_ID.length) {
TagType ret = BY_ID[id];
if (ret == null) {
throw new IllegalArgumentException("Tag type id " + id + " is unknown!");
}
return ret;
} else {
throw new IndexOutOfBoundsException("Tag type id " + id + " is out of bounds!");
}
}
private static class BaseData {
private static int maxId = 0;
}
}

View File

@@ -1,210 +0,0 @@
package com.flowpowered.nbt.stream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import com.flowpowered.nbt.*;
/**
* This class reads NBT, or Named Binary Tag streams, and produces an object graph of subclasses of the {@link Tag} object. <p /> The NBT format was created by Markus Persson, and the specification
* may be found at <a href="https://flowpowered.com/nbt/spec.txt"> https://flowpowered.com/nbt/spec.txt</a>.
*/
public final class NBTInputStream implements Closeable {
/**
* The data input stream.
*/
private final EndianSwitchableInputStream is;
/**
* Creates a new {@link NBTInputStream}, which will source its data from the specified input stream. This assumes the stream is compressed.
*
* @param is The input stream.
* @throws java.io.IOException if an I/O error occurs.
*/
public NBTInputStream(InputStream is) throws IOException {
this(is, true, ByteOrder.BIG_ENDIAN);
}
/**
* Creates a new {@link NBTInputStream}, which sources its data from the specified input stream. A flag must be passed which indicates if the stream is compressed with GZIP or not. This assumes the
* stream uses big endian encoding.
*
* @param is The input stream.
* @param compressed A flag indicating if the stream is compressed.
* @throws java.io.IOException if an I/O error occurs.
*/
public NBTInputStream(InputStream is, boolean compressed) throws IOException {
this(is, compressed, ByteOrder.BIG_ENDIAN);
}
/**
* Creates a new {@link NBTInputStream}, which sources its data from the specified input stream. A flag must be passed which indicates if the stream is compressed with GZIP or not.
*
* @param is The input stream.
* @param compressed A flag indicating if the stream is compressed.
* @param endianness Whether to read numbers from the InputStream with little endian encoding.
* @throws java.io.IOException if an I/O error occurs.
*/
public NBTInputStream(InputStream is, boolean compressed, ByteOrder endianness) throws IOException {
this.is = new EndianSwitchableInputStream(compressed ? new GZIPInputStream(is) : is, endianness);
}
/**
* Reads an NBT {@link Tag} from the stream.
*
* @return The tag that was read.
* @throws java.io.IOException if an I/O error occurs.
*/
public Tag readTag() throws IOException {
return readTag(0);
}
/**
* Reads an NBT {@link Tag} from the stream.
*
* @param depth The depth of this tag.
* @return The tag that was read.
* @throws java.io.IOException if an I/O error occurs.
*/
private Tag readTag(int depth) throws IOException {
int typeId = is.readByte() & 0xFF;
TagType type = TagType.getById(typeId);
String name;
if (type != TagType.TAG_END) {
int nameLength = is.readShort() & 0xFFFF;
byte[] nameBytes = new byte[nameLength];
is.readFully(nameBytes);
name = new String(nameBytes, NBTConstants.CHARSET.name());
} else {
name = "";
}
return readTagPayload(type, name, depth);
}
/**
* Reads the payload of a {@link Tag}, given the name and type.
*
* @param type The type.
* @param name The name.
* @param depth The depth.
* @return The tag.
* @throws java.io.IOException if an I/O error occurs.
*/
@SuppressWarnings ({"unchecked", "rawtypes"})
private Tag readTagPayload(TagType type, String name, int depth) throws IOException {
switch (type) {
case TAG_END:
if (depth == 0) {
throw new IOException("TAG_End found without a TAG_Compound/TAG_List tag preceding it.");
} else {
return new EndTag();
}
case TAG_BYTE:
return new ByteTag(name, is.readByte());
case TAG_SHORT:
return new ShortTag(name, is.readShort());
case TAG_INT:
return new IntTag(name, is.readInt());
case TAG_LONG:
return new LongTag(name, is.readLong());
case TAG_FLOAT:
return new FloatTag(name, is.readFloat());
case TAG_DOUBLE:
return new DoubleTag(name, is.readDouble());
case TAG_BYTE_ARRAY:
int length = is.readInt();
byte[] bytes = new byte[length];
is.readFully(bytes);
return new ByteArrayTag(name, bytes);
case TAG_STRING:
length = is.readShort();
bytes = new byte[length];
is.readFully(bytes);
return new StringTag(name, new String(bytes, NBTConstants.CHARSET.name()));
case TAG_LIST:
TagType childType = TagType.getById(is.readByte());
length = is.readInt();
Class<? extends Tag> clazz = childType.getTagClass();
List<Tag> tagList = new ArrayList<Tag>(length);
for (int i = 0; i < length; i++) {
Tag tag = readTagPayload(childType, "", depth + 1);
if (tag instanceof EndTag) {
throw new IOException("TAG_End not permitted in a list.");
} else if (!clazz.isInstance(tag)) {
throw new IOException("Mixed tag types within a list.");
}
tagList.add(tag);
}
return new ListTag(name, clazz, tagList);
case TAG_COMPOUND:
CompoundMap compoundTagList = new CompoundMap();
while (true) {
Tag tag = readTag(depth + 1);
if (tag instanceof EndTag) {
break;
} else {
compoundTagList.put(tag);
}
}
return new CompoundTag(name, compoundTagList);
case TAG_INT_ARRAY:
length = is.readInt();
int[] ints = new int[length];
for (int i = 0; i < length; i++) {
ints[i] = is.readInt();
}
return new IntArrayTag(name, ints);
case TAG_SHORT_ARRAY:
length = is.readInt();
short[] shorts = new short[length];
for (int i = 0; i < length; i++) {
shorts[i] = is.readShort();
}
return new ShortArrayTag(name, shorts);
case TAG_LONG_ARRAY:
length = is.readInt();
long[] longs = new long[length];
for (int i = 0; i < length; i++) {
longs[i] = is.readLong();
}
return new LongArrayTag(name, longs);
default:
throw new IOException("Invalid tag type: " + type + ".");
}
}
public void close() throws IOException {
is.close();
}
/**
* @return whether this NBTInputStream reads numbers in little-endian format.
*/
public ByteOrder getByteOrder() {
return is.getEndianness();
}
}

View File

@@ -1,80 +0,0 @@
package mc.world.anvil;
import com.flowpowered.nbt.CompoundTag;
import lombok.extern.slf4j.Slf4j;
import mc.core.world.block.Block;
import mc.core.world.block.BlockLocation;
import mc.core.world.block.BlockType;
@Slf4j
public class AnvilBlock implements Block {
private final AnvilChunkSection chunkSection;
private final BlockLocation location;
private BlockLocation globalLocation;
public AnvilBlock(AnvilChunkSection chunkSection, int x, int y, int z) {
this.chunkSection = chunkSection;
this.location = new BlockLocation(x, y, z);
}
@Override
public int getLight() {
return chunkSection.getBlockLight().get(location);
}
@Override
public void setLight(int light) {
// nope...
}
@Override
public BlockType getType() {
final byte id = chunkSection.getBlocks().get((location.getY() << 8) + (location.getZ() << 4) + location.getX());
final int meta = chunkSection.getBlocksMeta().get(location);
BlockType type = BlockType.getByIdMeta(id & 0xFF, meta);
if (type.equals(BlockType.BEDROCK) && id != 7) {
log.warn("ChunkSection: {},{},{} | Block: {}",
chunkSection.getParent().getX(),
chunkSection.getY(),
chunkSection.getParent().getZ(),
location.toString());
}
return type;
}
@Override
public BlockLocation getLocation() {
if (globalLocation == null) {
globalLocation = new BlockLocation(
(chunkSection.getParent().getX() << 4) + location.getX(),
(chunkSection.getY() << 4) + location.getY(),
(chunkSection.getParent().getZ() << 4) + location.getZ()
);
}
return globalLocation;
}
@Override
public CompoundTag getNBTData() {
CompoundTag compoundTag = ((AnvilChunk)chunkSection.getParent()).getNbtByGlobalXYZ(
(chunkSection.getParent().getX() << 4) + location.getX(),
(chunkSection.getY() << 4) + location.getY(),
(chunkSection.getParent().getZ() << 4) + location.getZ()
);
if (compoundTag != null) {
compoundTag.getValue().remove("Items");
compoundTag.getValue().remove("Lock");
}
return compoundTag;
}
@Override
public String toString() {
return "AnvilBlock{" +
"location=" + getLocation() +
", type=" + getType() +
'}';
}
}

View File

@@ -1,137 +0,0 @@
package mc.world.anvil;
import com.flowpowered.nbt.ByteArrayTag;
import com.flowpowered.nbt.ByteTag;
import com.flowpowered.nbt.CompoundMap;
import com.flowpowered.nbt.CompoundTag;
import com.flowpowered.nbt.IntTag;
import com.flowpowered.nbt.ListTag;
import gnu.trove.list.TByteList;
import gnu.trove.list.array.TByteArrayList;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import mc.core.utils.NibbleArray;
import mc.core.world.Biome;
import mc.core.world.block.Block;
import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkSection;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Getter
public class AnvilChunk implements Chunk {
private int x;
private int z;
private TByteList biomes = new TByteArrayList(256);
private List<ChunkSection> sections;
private ListTag<CompoundTag> tileEntities;
@SuppressWarnings("unchecked")
AnvilChunk(CompoundTag chunkTag) {
CompoundMap levelTagMap = ((CompoundTag) chunkTag.getValue().get("Level")).getValue();
this.x = ((IntTag) levelTagMap.get("xPos")).getValue();
this.z = ((IntTag) levelTagMap.get("zPos")).getValue();
biomes.add(((ByteArrayTag) levelTagMap.get("Biomes")).getValue());
tileEntities = (ListTag<CompoundTag>) levelTagMap.get("TileEntities");
List<CompoundTag> sections = ((ListTag<CompoundTag>) levelTagMap.get("Sections")).getValue();
this.sections = new ArrayList<>(sections.size());
for (CompoundTag sectionTag : sections) {
CompoundMap sectionTagValue = sectionTag.getValue();
AnvilChunkSection chunkSection = new AnvilChunkSection();
chunkSection.setParent(this);
chunkSection.setY(((ByteTag) sectionTagValue.get("Y")).getValue());
chunkSection.setBlockLight(new NibbleArray(((ByteArrayTag) sectionTagValue.get("BlockLight")).getValue()));
chunkSection.setSkyLight(new NibbleArray(((ByteArrayTag) sectionTagValue.get("SkyLight")).getValue()));
chunkSection.getBlocks().add(((ByteArrayTag) sectionTagValue.get("Blocks")).getValue());
chunkSection.setBlocksMeta(new NibbleArray(((ByteArrayTag) sectionTagValue.get("Data")).getValue()));
this.sections.add(chunkSection);
}
}
CompoundTag getNbtByGlobalXYZ(int x, int y, int z) {
for (CompoundTag compoundTag : tileEntities.getValue()) {
CompoundMap compoundMap = compoundTag.getValue();
if (((IntTag)compoundMap.get("x")).getValue() == x
&& ((IntTag)compoundMap.get("y")).getValue() == y
&& ((IntTag)compoundMap.get("z")).getValue() == z) {
return compoundTag;
}
}
return null;
}
@Override
public ChunkSection getChunkSection(int height) {
if (height > sections.size()-1) return null;
return sections.get(height);
}
@Override
public void setChunkSection(int height, ChunkSection chunkSection) {
// nope...
}
@Override
public Block getBlock(int x, int y, int z) {
final int height = y >> 4;
return sections.get(height).getBlock(
x - getX() << 4,
y - height << 4,
z - getZ() << 4
);
}
@Override
public void setBlock(Block block) {
// nope...
}
@Override
public int getSkyLight(int x, int y, int z) {
final int height = y >> 4;
return sections.get(height).getSkyLight(
x - getX() << 4,
y - height << 4,
z - getZ() << 4
);
}
@Override
public void setSkyLight(int x, int y, int z, int lightLevel) {
// nope...
}
@Override
public int getAddition(int x, int y, int z) {
final int height = y >> 4;
return sections.get(height).getAddition(
x - getX() << 4,
y - height << 4,
z - getZ() << 4
);
}
@Override
public void setAddition(int x, int y, int z, int value) {
// nope...
}
@Override
public Biome getBiome(int x, int z) {
return Biome.getById(biomes.get((z >> 4) << 4 | (x >> 4)) & 255);
}
@Override
public void setBiome(int x, int z, Biome biome) {
// nope...
}
}

View File

@@ -1,50 +0,0 @@
package mc.world.anvil;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkProvider;
import org.springframework.stereotype.Component;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@Slf4j
@Component
@NoArgsConstructor
@Setter
public class AnvilChunkProvider implements ChunkProvider {
private RegionManager regionManager;
public AnvilChunkProvider(String mapPath) {
Path pathMap = Paths.get(mapPath);
if (Files.exists(pathMap)) {
log.info("Use Anvil map from \"{}\"", pathMap);
this.setRegionManager(new RegionManager(pathMap.resolve("region")));
} else {
log.error("Anvil map: path \"{}\" not found!!!", pathMap);
}
}
@Override
public Chunk getChunk(int x, int z) {
Region region = regionManager.getRegion(x >> 5, z >> 5);
if (region == null) {
return new EmptyChunk(x, z);
} else {
return region.getChunk(x, z);
}
}
@Override
public void saveChunk(Chunk chunk) {
// nope
}
@Override
public void saveChunk(Chunk... chunks) {
// nope
}
}

View File

@@ -1,57 +0,0 @@
package mc.world.anvil;
import gnu.trove.list.TByteList;
import gnu.trove.list.linked.TByteLinkedList;
import lombok.Getter;
import lombok.Setter;
import mc.core.utils.NibbleArray;
import mc.core.world.block.Block;
import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkSection;
@Getter
public class AnvilChunkSection implements ChunkSection {
@Setter
private Chunk parent;
@Setter
private int y;
private TByteList blocks = new TByteLinkedList();
@Setter
private NibbleArray blocksMeta;
@Setter
private NibbleArray blockLight;
@Setter
private NibbleArray skyLight;
@Override
public Block getBlock(int x, int y, int z) {
return new AnvilBlock(this, x, y, z);
}
@Override
public void setBlock(Block block) {
// nope...
}
@Override
public int getSkyLight(int x, int y, int z) {
return skyLight.get(x, y, z);
}
@Override
public void setSkyLight(int x, int y, int z, int lightLevel) {
// nope...
}
@Override
public int getAddition(int x, int y, int z) {
return 0;
}
@Override
public void setAddition(int x, int y, int z, int value) {
// nope...
}
}

View File

@@ -1,153 +0,0 @@
package mc.world.anvil;
import lombok.Getter;
import lombok.NoArgsConstructor;
import mc.core.world.Biome;
import mc.core.world.block.Block;
import mc.core.world.block.BlockLocation;
import mc.core.world.block.BlockType;
import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkSection;
import java.util.LinkedList;
import java.util.List;
@Getter
public class EmptyChunk implements Chunk {
private int x;
private int z;
private List<ChunkSection> $sections = new LinkedList<>();
public EmptyChunk(int x, int z) {
this.x = x;
this.z = z;
for (int i = 0; i < 16; i++) {
this.$sections.add(null);
}
}
@Override
public ChunkSection getChunkSection(int height) {
ChunkSection section;
if ((section = $sections.get(height)) == null) {
section = new EmptySection(height);
$sections.set(height, section);
}
return section;
}
@Override
public void setChunkSection(int height, ChunkSection chunkSection) {
}
@Override
public Block getBlock(int x, int y, int z) {
return getChunkSection(y >> 4).getBlock(
x - getX() << 4,
y - (y >> 4) << 4,
z - getZ() << 4
);
}
@Override
public void setBlock(Block block) {
}
@Override
public int getSkyLight(int x, int y, int z) {
return getChunkSection(y >> 4).getSkyLight(x, y, z);
}
@Override
public void setSkyLight(int x, int y, int z, int lightLevel) {
}
@Override
public int getAddition(int x, int y, int z) {
return getChunkSection(y >> 4).getAddition(x, y, z);
}
@Override
public void setAddition(int x, int y, int z, int value) {
}
@Override
public Biome getBiome(int x, int z) {
return Biome.PLAINS;
}
@Override
public void setBiome(int x, int z, Biome biome) {
}
@NoArgsConstructor
@Getter
public class EmptySection implements ChunkSection {
private int y;
EmptySection(int y) {
this.y = y;
}
@Override
public Chunk getParent() {
return EmptyChunk.this;
}
@Override
public void setParent(Chunk chunk) {
}
@Override
public Block getBlock(int localX, int localY, int localZ) {
return new Block() {
@Override
public int getLight() {
return 15;
}
@Override
public void setLight(int light) {
}
@Override
public BlockType getType() {
return BlockType.AIR;
}
@Override
public BlockLocation getLocation() {
return new BlockLocation(
(getParent().getX() << 4) + localX,
(getY() << 4) + localY,
(getParent().getZ() << 4) + localZ
);
}
};
}
@Override
public void setBlock(Block block) {
}
@Override
public int getSkyLight(int localX, int localY, int localZ) {
return 15;
}
@Override
public void setSkyLight(int localX, int localY, int localZ, int lightLevel) {
}
@Override
public int getAddition(int localX, int localY, int localZ) {
return 0;
}
@Override
public void setAddition(int localX, int localY, int localZ, int value) {
}
}
}

View File

@@ -1,26 +0,0 @@
package mc.world.anvil;
import com.flowpowered.nbt.*;
import lombok.Getter;
import lombok.ToString;
import mc.core.world.block.BlockLocation;
@Getter
@ToString
class LevelInfo {
private long seed;
private BlockLocation spawn;
private int version;
LevelInfo(CompoundTag levelDatTag) {
CompoundMap dataMapTag = ((CompoundTag) levelDatTag.getValue().get("Data")).getValue();
seed = ((LongTag) dataMapTag.get("RandomSeed")).getValue();
spawn = new BlockLocation(
((IntTag) dataMapTag.get("SpawnX")).getValue(),
((IntTag) dataMapTag.get("SpawnY")).getValue(),
((IntTag) dataMapTag.get("SpawnZ")).getValue()
);
version = ((IntTag) dataMapTag.get("version")).getValue();
}
}

View File

@@ -1,108 +0,0 @@
package mc.world.anvil;
import com.flowpowered.nbt.CompoundTag;
import com.flowpowered.nbt.Tag;
import com.flowpowered.nbt.stream.NBTInputStream;
import gnu.trove.list.TByteList;
import gnu.trove.list.array.TByteArrayList;
import lombok.extern.slf4j.Slf4j;
import mc.core.world.chunk.Chunk;
import javax.annotation.Nullable;
import java.io.*;
import java.util.zip.InflaterInputStream;
@Slf4j
class Region implements Closeable {
private static final byte BYTE_TRUE = 1,
BYTE_FALSE = 0;
private RandomAccessFile file;
private TByteList sectorFree;
private final int[] offsets = new int[1024];
Region(File file) throws IOException {
this.file = new RandomAccessFile(file, "rw");
int sizeOfSectorFree = (int)this.file.length() / 4096;
sectorFree = new TByteArrayList(sizeOfSectorFree);
sectorFree.add(BYTE_FALSE);
sectorFree.add(BYTE_FALSE);
for (int i = 0; i < sizeOfSectorFree-2; i++) {
sectorFree.add(BYTE_TRUE);
}
for (int i = 0; i < offsets.length; ++i) {
int read = this.file.readInt();
offsets[i] = read;
if (read != 0 && (read >> 8) + (read & 255) <= this.sectorFree.size()) {
for (int j = 0; j < (read & 255); ++j) {
this.sectorFree.set((read >> 8) + j, BYTE_FALSE);
}
}
}
this.file.skipBytes(1024);
}
@Nullable
Chunk getChunk(int x, int z) {
int offset;
try {
offset = getOffset(x & 31, z & 31);
} catch (Exception e) {
return new EmptyChunk(x, z);
}
if (offset == 0) {
return new EmptyChunk(x, z);
}
int v1 = offset >> 8;
int v2 = offset & 255;
if (v1 + v2 > sectorFree.size()) {
return new EmptyChunk(x, z);
}
try {
file.seek((long) (v1 * 4096));
int read = file.readInt();
if (read <= 0 || read > 4096 * v2) {
return new EmptyChunk(x, z);
}
boolean gzippedData = (file.readByte() == 0x01);
if (gzippedData) {
log.warn("GZipped");
} else {
byte[] buffer = new byte[read - 1];
file.read(buffer);
InflaterInputStream inputStream = new InflaterInputStream(new ByteArrayInputStream(buffer));
NBTInputStream nbtInputStream = new NBTInputStream(inputStream, false);
Tag rootTag = nbtInputStream.readTag();
nbtInputStream.close();
return new AnvilChunk((CompoundTag) rootTag);
}
} catch (IOException e) {
log.error("Get chunk", e);
return null;
}
return null;
}
private int getOffset(int x, int z) {
return offsets[x + z * 32];
}
@Override
public void close() throws IOException {
if (file != null) file.close();
}
}

View File

@@ -1,49 +0,0 @@
package mc.world.anvil;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import lombok.extern.slf4j.Slf4j;
import mc.core.utils.CompactedCoords;
import org.springframework.lang.Nullable;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@Slf4j
public class RegionManager {
private final Path regionFilesPath;
private final TIntObjectMap<Region> regions = new TIntObjectHashMap<>();
public RegionManager(String regionFilesPath) {
this(Paths.get(regionFilesPath));
}
public RegionManager(Path regionFilesPath) {
this.regionFilesPath = regionFilesPath;
}
@Nullable
public Region getRegion(int x, int z) {
final int xz = CompactedCoords.compressXZ(x, z);
if (regions.containsKey(xz)) {
return regions.get(xz);
} else {
Path regionFilePath = regionFilesPath.resolve("r." + x + "." + z + ".mca");
if (Files.exists(regionFilePath)) {
try {
Region region = new Region(regionFilePath.toFile());
regions.put(xz, region);
return region;
} catch (IOException e) {
log.error("load region from file", e);
return null;
}
} else {
return null;
}
}
}
}

View File

@@ -1,213 +0,0 @@
package mc.world.anvil;
import com.flowpowered.nbt.CompoundMap;
import com.flowpowered.nbt.CompoundTag;
import com.flowpowered.nbt.IntTag;
import com.flowpowered.nbt.StringTag;
import lombok.SneakyThrows;
import mc.core.world.block.Block;
import mc.core.world.block.BlockType;
import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkSection;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class RegionTest {
private static RegionManager regionManager;
@BeforeAll
@SneakyThrows
static void before() {
regionManager = new RegionManager(Paths.get(RegionTest.class.getResource("/region/").toURI()));
}
private static void assertZeroPlast(int x, int z, Block block, String msg) {
// @formatter:off
if (x == 0 && z == 0) assertEquals(BlockType.STONE, block.getType(), msg);
else if (x == 15 && z == 0) assertEquals(BlockType.GRANITE, block.getType(), msg);
else if (x == 0 && z == 15) assertEquals(BlockType.POLISHED_GRANITE, block.getType(), msg);
else if (x == 15 && z == 15) assertEquals(BlockType.DIORITE, block.getType(), msg);
else assertEquals(BlockType.BEDROCK, block.getType(), msg);
// @formatter:on
}
private static ChunkChecker chunkChecker00() {
return new ChunkChecker() {
private void checkSection0(ChunkSection chunkSection) {
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
Block block = chunkSection.getBlock(x, y, z);
String msg = String.format("coords: %d %d %d", x, y, z);
if (y == 0) {
assertZeroPlast(x, z, block, msg);
} else {
assertEquals(BlockType.STONE, block.getType(), msg);
}
}
}
}
}
private CompoundTag createExceptedNBT(Block block) {
CompoundMap compoundMap = new CompoundMap();
compoundMap.put(new IntTag("x", block.getLocation().getX()));
compoundMap.put(new IntTag("y", block.getLocation().getY()));
compoundMap.put(new IntTag("z", block.getLocation().getZ()));
compoundMap.put(new StringTag("id", block.getType().getNamedId()));
return new CompoundTag("", compoundMap);
}
private void checkSection1(ChunkSection chunkSection) {
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
Block block = chunkSection.getBlock(x, y, z);
String msg = String.format("coords: %d %d %d", x, y, z);
// @formatter:off
if (y == 0) assertEquals(BlockType.DIRT, block.getType(), msg);
else if (y == 1) assertEquals(BlockType.GRASS, block.getType(), msg);
else if (y == 2) {
if ((x == 2 || x == 4 || x == 5) && z == 1) {
assertEquals(BlockType.CHEST_NORTH, block.getType(), msg);
assertEquals(createExceptedNBT(block), block.getNBTData());
} else if ((x == 2 || x == 3 || x == 5) && z == 6) {
assertEquals(BlockType.CHEST_SOUTH, block.getType(), msg);
assertEquals(createExceptedNBT(block), block.getNBTData());
} else if (x == 1 && (z == 2 || z == 3 || z == 5)) {
assertEquals(BlockType.CHEST_WEST, block.getType(), msg);
assertEquals(createExceptedNBT(block), block.getNBTData());
} else if (x == 6 && (z == 2 || z == 4 || z == 5)) {
assertEquals(BlockType.CHEST_EAST, block.getType(), msg);
assertEquals(createExceptedNBT(block), block.getNBTData());
} else {
assertEquals(BlockType.AIR, block.getType(), msg);
}
}
else assertEquals(BlockType.AIR, block.getType(), msg);
// @formatter:on
}
}
}
}
@Override
public void check(Chunk chunk) {
ChunkSection chunkSection = chunk.getChunkSection(0);
assertNotNull(chunkSection);
checkSection0(chunkSection);
chunkSection = chunk.getChunkSection(1);
assertNotNull(chunkSection);
checkSection1(chunkSection);
}
};
}
private static ChunkChecker chunkChecker01() {
return new ChunkChecker() {
@Override
public void check(Chunk chunk) {
ChunkSection section = chunk.getChunkSection(0);
assertNotNull(section);
final List<BlockType> exceptedTypes = Arrays.asList(
BlockType.CLAY,
BlockType.ORE_REDSTONE,
BlockType.ORE_DIAMOND,
BlockType.OBSIDIAN,
BlockType.STONE_MOSS,
BlockType.SANDSTONE,
BlockType.ORE_LAPIS,
BlockType.WOOD_JUNGLE,
BlockType.WOOD_BIRCH,
BlockType.WOOD_SPRUCE,
BlockType.WOOD_OAK,
BlockType.ORE_COAL,
BlockType.ORE_IRON,
BlockType.ORE_GOLD,
BlockType.GRAVEL,
BlockType.SAND
);
for (int y = 0; y < 2; y++) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
Block block = section.getBlock(x, y, z);
String msg = String.format("coords: %d %d %d", x, y, z);
if (y == 0) {
assertZeroPlast(x, z, block, msg);
} else {
assertEquals(exceptedTypes.get(x), block.getType(), msg);
}
}
}
}
}
};
}
private static ChunkChecker chunkChecker0N1() {
return new ChunkChecker() {
@Override
public void check(Chunk chunk) {
ChunkSection section = chunk.getChunkSection(0);
assertNotNull(section);
for (int y = 0; y < 1; y++) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
assertZeroPlast(x, z,
section.getBlock(x, y, z),
String.format("coords: %d %d %d", x, y, z));
}
}
}
}
};
}
private static Stream<Arguments> streamArguments() {
return Stream.of(
Arguments.of(0, 0, chunkChecker00()),
Arguments.of(0, 1, chunkChecker01()),
Arguments.of(0, -1, chunkChecker0N1())
);
}
@DisplayName("testGetChunk")
@ParameterizedTest(name = "[{index}] chunk {0},{1}")
@MethodSource("streamArguments")
void testGetChunk(int chunkX, int chunkZ, ChunkChecker chunkChecker) {
final Region region = regionManager.getRegion(chunkX >> 5, chunkZ >> 5);
assertNotNull(region);
final Chunk chunk = region.getChunk(chunkX, chunkZ);
assertNotNull(chunk);
assertFalse(chunk instanceof EmptyChunk);
chunkChecker.check(chunk);
}
interface ChunkChecker {
void check(Chunk chunk);
}
}

View File

@@ -1,4 +1,4 @@
version '1.0-SNAPSHOT' version '0.1'
apply plugin: 'maven' apply plugin: 'maven'
apply plugin: 'application' apply plugin: 'application'

View File

@@ -1,15 +1,15 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-29
*/
package mc.core.embedded; package mc.core.embedded;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.Server; import mc.core.network.Server;
import mc.core.network.StartServerException; import mc.core.network.StartServerException;
@Slf4j
public class FakeServer implements Server { public class FakeServer implements Server {
@Override @Override
public void start() throws StartServerException { public void start() {
log.info("Hello. I'm FakeServer. And i do nothing.");
} }
@Override @Override

View File

@@ -1,17 +0,0 @@
version '1.0-SNAPSHOT'
ext {
spring_data_version = '2.1.0.RELEASE'
}
dependencies {
/* Core */
compile_excludeCopy project(':core')
/* Spring */
compile (group: 'org.springframework.data', name: 'spring-data-jpa', version: spring_data_version)
/* Database */
compile (group: 'org.hibernate', name: 'hibernate-entitymanager', version: '5.3.6.Final')
compile (group: 'com.h2database', name: 'h2', version: '1.4.197')
}

View File

@@ -1,63 +0,0 @@
package mc.core.h2db;
import lombok.Data;
import mc.core.EntityLocation;
import mc.core.exception.ResourceUnloadedException;
import mc.core.network.NetChannel;
import mc.core.player.Player;
import mc.core.player.PlayerSettings;
import mc.core.world.World;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@Data
public class H2Player implements Player {
private int id;
private UUID uuid;
private String name;
private boolean online = false;
private List<Integer> loadedChunks;
private NetChannel channel;
private EntityLocation location;
private Reference<World> $refWorld;
private boolean flying = false;
private PlayerSettings settings;
@Override
public World getWorld() {
if ($refWorld == null) {
return null;
} else if ($refWorld.get() == null) {
throw new ResourceUnloadedException("You're trying to get unloaded world");
} else {
return $refWorld.get();
}
}
@Override
public void setWorld(World world) {
if (world == null) {
this.$refWorld = null;
} else {
this.$refWorld = new WeakReference<>(world);
}
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
H2Player player = (H2Player) obj;
return id == player.id &&
Objects.equals(uuid, player.uuid);
}
@Override
public int hashCode() {
return Objects.hash(id, uuid);
}
}

View File

@@ -1,98 +0,0 @@
package mc.core.h2db;
import com.google.common.collect.ImmutableList;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import mc.core.EntityLocation;
import mc.core.h2db.service.H2PlayerService;
import mc.core.network.BroadcastNetChannel;
import mc.core.network.NetChannel;
import mc.core.player.Player;
import mc.core.player.PlayerManager;
import mc.core.player.PlayerSettings;
import mc.core.world.World;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@Slf4j
@Component
public class H2PlayerManager implements PlayerManager {
@Setter
@Autowired
private H2PlayerService h2PlayerService;
private List<H2Player> playerList = Collections.synchronizedList(new ArrayList<>());
@Override
public Player createPlayer(String name, EntityLocation location, World world) {
H2Player h2Player = new H2Player();
h2Player.setName(name);
h2Player.setUuid(UUID.randomUUID());
h2Player.setLocation(location.clone());
h2Player.setLoadedChunks(new ArrayList<>());
h2Player.setWorld(world);
h2Player.setSettings(new PlayerSettings());
return h2PlayerService.save(h2Player);
}
@Override
public void joinServer(Player player) {
//TODO в дальнейшем следует именно этому методу передать функции инсерта в БД
H2Player h2Player = (H2Player) player;
playerList.add(h2Player);
h2Player.setOnline(true);
}
@Override
public void leftServer(Player player) {
H2Player h2Player = (H2Player) player;
h2PlayerService.save(h2Player);
h2Player.setOnline(false);
h2Player.getLoadedChunks().clear();
}
@Override
public Player getPlayer(String name) {
return playerList.stream()
.filter(player -> player.getName().equals(name))
.filter(H2Player::isOnline)
.findFirst().orElse(null);
}
@Override
public List<Player> getPlayers() {
return playerList.stream()
.filter(H2Player::isOnline)
.collect(ImmutableList.toImmutableList());
}
@Override
public int getCountPlayers() {
return (int) playerList.stream()
.filter(H2Player::isOnline)
.count();
}
@Override
public NetChannel getBroadcastChannel() {
return new BroadcastNetChannel(
playerList.stream()
.filter(H2Player::isOnline)
.map(player -> (Player)player)
);
}
@Override
public Player getOfflinePlayer(String name) {
return playerList.stream()
.filter(player -> player.getName().equals(name))
.filter(player -> !player.isOnline())
.findFirst().orElseGet(() -> h2PlayerService.getByName(name));
}
}

View File

@@ -1,102 +0,0 @@
package mc.core.h2db.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import mc.core.EntityLocation;
import mc.core.h2db.H2Player;
import mc.core.world.World;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.context.ApplicationContext;
import javax.persistence.*;
import java.util.UUID;
@Entity
@Table(name = "players",
indexes = {@Index(name = "idx_players_uuid", columnList = "uuid", unique = true),
@Index(name = "idx_players_name", columnList = "name")})
@NoArgsConstructor
@Data
public class H2PlayerEntity {
@Id
@GeneratedValue(generator = "increment")
@GenericGenerator(name= "increment", strategy= "increment")
@Column(nullable = false)
private Long id;
@Column(length = 36, nullable = false)
private String uuid;
@Column(length = 16, nullable = false)
private String name;
@Column(name = "location_x", nullable = false)
private Double locationX;
@Column(name = "location_y", nullable = false)
private Double locationY;
@Column(name = "location_z", nullable = false)
private Double locationZ;
@Column(name = "location_yaw", nullable = false)
private Float locationYaw;
@Column(name = "location_pitch", nullable = false)
private Float locationPitch;
@Column(name = "location_world", length = 64, nullable = false)
private String locationWorld;
public H2PlayerEntity(H2Player player) {
this.id = (long) player.getId();
setUuid(player.getUuid().toString());
setName(this.name = player.getName());
this.locationX = player.getLocation().getX();
this.locationY = player.getLocation().getY();
this.locationZ = player.getLocation().getZ();
this.locationYaw = player.getLocation().getYaw();
this.locationPitch = player.getLocation().getPitch();
this.locationWorld = player.getWorld().getName();
}
public void setUuid(String uuid) {
if (uuid == null || uuid.trim().isEmpty()) {
this.uuid = null;
} else {
this.uuid = uuid;
}
}
public void setName(String name) {
if (name == null || name.trim().isEmpty()) {
this.name = null;
} else {
this.name = name;
}
}
public H2Player toPlayer(ApplicationContext context) {
H2Player player = new H2Player();
return toPlayer(player, context);
}
public H2Player toPlayer(H2Player player, ApplicationContext context) {
player.setId(this.id.intValue());
player.setUuid(UUID.fromString(this.uuid));
player.setName(this.name);
if (player.getLocation() == null) {
player.setLocation(new EntityLocation(
this.locationX, this.locationY, this.locationZ,
this.locationYaw, this.locationPitch
));
} else {
player.getLocation().setXYZ(this.locationX, this.locationY, this.locationZ);
player.getLocation().setYawPitch(this.locationYaw, this.locationPitch);
}
player.setWorld(context.getBean(this.locationWorld, World.class));
return player;
}
}

View File

@@ -1,12 +0,0 @@
package mc.core.h2db.repository;
import mc.core.h2db.entity.H2PlayerEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface H2PlayerEntityRepository extends JpaRepository<H2PlayerEntity, Long> {
Optional<H2PlayerEntity> findByName(String name);
}

View File

@@ -1,11 +0,0 @@
package mc.core.h2db.service;
import mc.core.h2db.H2Player;
public interface H2PlayerService {
H2Player save(H2Player player);
void remove(H2Player player);
H2Player getByName(String name);
H2Player getById(int id);
}

View File

@@ -1,44 +0,0 @@
package mc.core.h2db.service;
import mc.core.h2db.H2Player;
import mc.core.h2db.entity.H2PlayerEntity;
import mc.core.h2db.repository.H2PlayerEntityRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class H2PlayerServiceImpl implements H2PlayerService {
@Autowired
private ApplicationContext context;
@Autowired
private H2PlayerEntityRepository h2PlayerEntityRepository;
@Override
public H2Player save(H2Player player) {
H2PlayerEntity entity = new H2PlayerEntity(player);
//TODO возможно имеет смысл здесь оптимизация
//вместо toPlayer() сделать toPlayer(H2Player) который в существующий
//будет дописывать/обновлять данные
return h2PlayerEntityRepository.saveAndFlush(entity).toPlayer(player, context);
}
@Override
public void remove(H2Player player) {
h2PlayerEntityRepository.deleteById((long) player.getId());
}
@Override
public H2Player getByName(String name) {
Optional<H2PlayerEntity> optEntity = h2PlayerEntityRepository.findByName(name);
return optEntity.map(entiry -> entiry.toPlayer(context)).orElse(null);
}
@Override
public H2Player getById(int id) {
Optional<H2PlayerEntity> optEntity = h2PlayerEntityRepository.findById((long) id);
return optEntity.map(entiry -> entiry.toPlayer(context)).orElse(null);
}
}

View File

@@ -1,159 +0,0 @@
package mc.core.h2db;
import mc.core.EntityLocation;
import mc.core.h2db.service.H2PlayerService;
import mc.core.player.Player;
import mc.core.world.World;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {TestSpringConfig.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class H2PlayerManagerTest {
@Autowired
private H2PlayerService h2PlayerService;
@Autowired
private World mockWorld;
@Autowired
private H2PlayerManager playerManager;
@Test
void createPlayer() {
final String playerName = "NEW_PLAYER";
final Player newPlayer = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
assertNotNull(newPlayer);
assertEquals(H2Player.class, newPlayer.getClass());
assertTrue(newPlayer.getId() > 0);
final H2Player queryPlayer = h2PlayerService.getByName(playerName);
assertTrue(queryPlayer.getId() > 0);
assertEquals(newPlayer, queryPlayer);
assertEquals(newPlayer.getName(), queryPlayer.getName());
assertEquals(newPlayer.getLocation(), queryPlayer.getLocation());
assertEquals(newPlayer.getWorld(), queryPlayer.getWorld());
}
@Test
void joinServer() {
assertEquals(0, playerManager.getCountPlayers());
final String playerName = "NEW_PLAYER";
final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
playerManager.joinServer(player);
assertEquals(1, playerManager.getCountPlayers());
assertTrue(player.isOnline());
}
@Test
void leftServer() {
assertEquals(0, playerManager.getCountPlayers());
final String playerName = "NEW_PLAYER";
final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
playerManager.joinServer(player);
assertEquals(1, playerManager.getCountPlayers());
assertTrue(player.isOnline());
final int playerId = player.getId();
final String anotherName = "ANOTHER_NAME";
((H2Player)player).setName(anotherName);
playerManager.leftServer(player);
assertEquals(0, playerManager.getCountPlayers());
assertFalse(player.isOnline());
assertTrue(player.getLoadedChunks().isEmpty());
final H2Player queryPlayer = h2PlayerService.getById(playerId);
assertNotNull(queryPlayer);
((H2Player)player).setId(playerId);
assertEquals(player, queryPlayer);
}
@Test
void getPlayer() {
assertEquals(0, playerManager.getCountPlayers());
final String playerName = "NEW_PLAYER";
final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
assertNotNull(player);
playerManager.joinServer(player);
assertEquals(1, playerManager.getCountPlayers());
Player queryPlayer = playerManager.getPlayer(playerName);
assertEquals(player, queryPlayer);
}
@Test
void getPlayers() {
assertEquals(0, playerManager.getCountPlayers());
final String playerName = "NEW_PLAYER";
final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
assertNotNull(player);
playerManager.joinServer(player);
assertEquals(1, playerManager.getCountPlayers());
List<Player> players = playerManager.getPlayers();
assertEquals(1, players.size());
try {
players.add(new H2Player());
fail();
} catch (Exception e) {
assertTrue(true);
}
assertEquals(player, players.get(0));
}
@Test
void getCountPlayers() {
assertEquals(0, playerManager.getCountPlayers());
final String playerName = "NEW_PLAYER";
final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
assertNotNull(player);
playerManager.joinServer(player);
assertEquals(1, playerManager.getCountPlayers());
playerManager.leftServer(player);
assertEquals(0, playerManager.getCountPlayers());
}
@Test
void getOfflinePlayer() {
final String playerName = "NEW_PLAYER";
final Player player = playerManager.createPlayer(playerName, EntityLocation.ZERO(), mockWorld);
playerManager.joinServer(player);
playerManager.leftServer(player);
assertEquals(0, playerManager.getCountPlayers());
Player offlinePlayer = playerManager.getOfflinePlayer(playerName);
assertEquals(player, offlinePlayer);
}
}

View File

@@ -1,36 +0,0 @@
package mc.core.h2db;
import org.junit.jupiter.api.Test;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
class H2PlayerTest {
@Test
void equals_() {
UUID uuid = UUID.randomUUID();
H2Player player1 = new H2Player();
player1.setId(1);
player1.setUuid(uuid);
player1.setName("Player1");
H2Player player2 = new H2Player();
player2.setId(1);
player2.setUuid(uuid);
player2.setName("Player2");
assertEquals(player1, player2);
player2.setId(2);
assertNotEquals(player1, player2);
player2.setId(1);
player2.setUuid(UUID.randomUUID());
assertNotEquals(player1, player2);
}
}

View File

@@ -1,90 +0,0 @@
package mc.core.h2db;
import mc.core.h2db.service.H2PlayerService;
import mc.core.world.World;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@Configuration
@EnableJpaRepositories
@EnableTransactionManagement
@ComponentScan("mc.core.h2db")
public class TestSpringConfig {
private static final String DATABASE_DRIVER = "org.h2.Driver";
private static final String DATABASE_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";
private static final String DATABASE_USERNAME = "sa";
private static final String DATABASE_PASSWORD = "s3cReT";
static {
System.setProperty("org.jboss.logging.provider", "slf4j");
}
private Properties hibernateProp() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.format_sql", "true");
properties.put("hibernate.use_sql_comments", "true");
properties.put("hibernate.hbm2ddl.auto", "create");
return properties;
}
@Bean("mockWorld")
public World mockWorld() {
World mockWorld = mock(World.class);
when(mockWorld.getName()).thenReturn("mockWorld");
return mockWorld;
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dmds = new DriverManagerDataSource();
dmds.setDriverClassName(DATABASE_DRIVER);
dmds.setUrl(DATABASE_URL);
dmds.setUsername(DATABASE_USERNAME);
dmds.setPassword(DATABASE_PASSWORD);
return dmds;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
entityManagerFactoryBean.setPackagesToScan("mc.core.h2db.entity");
entityManagerFactoryBean.setJpaProperties(hibernateProp());
return entityManagerFactoryBean;
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
@Bean
public H2PlayerManager h2PlayerManager(H2PlayerService h2PlayerService) {
H2PlayerManager playerManager = new H2PlayerManager();
playerManager.setH2PlayerService(h2PlayerService);
return playerManager;
}
}

View File

@@ -1,155 +0,0 @@
package mc.core.h2db.service;
import mc.core.EntityLocation;
import mc.core.h2db.H2Player;
import mc.core.h2db.TestSpringConfig;
import mc.core.world.World;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {TestSpringConfig.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class H2PlayerServiceTest {
@Autowired
private H2PlayerService h2PlayerService;
@Autowired
private World world;
private H2Player buildPlayer() {
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
final double minD = 0.0d, maxD = 10.0d;
final float minF = 0.0f, maxF = 359.9f;
final int minI = 1000, maxI = 9999;
final H2Player player = new H2Player();
player.setUuid(UUID.randomUUID());
player.setName("player" + rnd.nextInt(minI, maxI));
player.setLocation(new EntityLocation(
rnd.nextDouble(minD, maxD),
rnd.nextDouble(minD, maxD),
rnd.nextDouble(minD, maxD),
rnd.nextFloat() * (maxF - minF) + minF,
rnd.nextFloat() * (maxF - minF) + minF
));
player.setWorld(world);
return player;
}
private void assertPlayers(H2Player expected, H2Player actual) {
assertEquals(expected, actual);
assertEquals(expected.getName(), actual.getName());
assertEquals(expected.getLocation(), actual.getLocation());
assertNotNull(actual.getWorld());
assertEquals(expected.getWorld(), actual.getWorld());
}
@Test
void save() {
H2Player player = buildPlayer();
H2Player savedPlayer = h2PlayerService.save(player);
player.setId(savedPlayer.getId()); //FIXME костыль, однако
assertPlayers(player, savedPlayer);
}
@Test
void save_NameEmpty() {
assertThrows(Exception.class, () -> {
H2Player player = buildPlayer();
player.setName("");
h2PlayerService.save(player);
});
}
@Test
void save_NameNull() {
assertThrows(Exception.class, () -> {
H2Player player = buildPlayer();
player.setName(null);
h2PlayerService.save(player);
});
}
@Test
void save_UuidNull() {
assertThrows(Exception.class, () -> {
H2Player player = buildPlayer();
player.setUuid(null);
h2PlayerService.save(player);
});
}
@Test
void save_LocationNull() {
assertThrows(Exception.class, () -> {
H2Player player = buildPlayer();
player.setLocation(null);
h2PlayerService.save(player);
});
}
@Test
void remove() {
H2Player player = h2PlayerService.save(buildPlayer());
h2PlayerService.remove(player);
H2Player player2 = h2PlayerService.getById(player.getId());
assertNull(player2);
}
@Test
void remove_NotExists() {
assertThrows(Exception.class, () -> {
H2Player player = h2PlayerService.save(buildPlayer());
h2PlayerService.remove(player);
h2PlayerService.remove(player);
});
}
@Test
void getByName() {
H2Player player = h2PlayerService.save(buildPlayer());
H2Player player2 = h2PlayerService.getByName(player.getName());
assertPlayers(player, player2);
}
@Test
void getByName_NotExists() {
assertNull(h2PlayerService.getByName("UNKNOW_PLAYER"));
}
@Test
void getByName_Empty() {
assertNull(h2PlayerService.getByName(""));
}
@Test
void getByName_Null() {
assertNull(h2PlayerService.getByName(null));
}
@Test
void getById() {
H2Player player = h2PlayerService.save(buildPlayer());
H2Player player2 = h2PlayerService.getById(player.getId());
assertPlayers(player, player2);
}
@Test
void getById_NotExists() {
assertNull(h2PlayerService.getById(9999));
}
}

View File

@@ -1,10 +0,0 @@
version '1.0-SNAPSHOT'
dependencies {
/* Core */
compile_excludeCopy project(':core')
/* Components */
compile (group: 'com.google.code.gson', name: 'gson', version: '2.8.5')
compile (group: 'net.sf.trove4j', name: 'trove4j', version: '3.0.3')
}

View File

@@ -1,71 +0,0 @@
package mc.core.network.proto_1_12_2;
import java.io.ByteArrayOutputStream;
public class ByteArrayOutputNetStream extends NetOutputStream_p340 {
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
@Override
public void writeBoolean(boolean value) {
baos.write(value ? 1 : 0);
}
@Override
public void writeByte(int value) {
baos.write(value);
}
@Override
public void writeUnsignedByte(int value) {
baos.write((byte)(value & 0xFF));
}
@Override
public void writeBytes(byte[] buffer, int offset, int lengtn) {
baos.write(buffer, offset, lengtn);
}
@Override
public void writeShort(int value) {
baos.write((byte) (value >>> 8));
baos.write((byte) value);
}
@Override
public void writeInt(int value) {
baos.write((value >>> 24) & 0xFF);
baos.write((value >>> 16) & 0xFF);
baos.write((value >>> 8) & 0xFF);
baos.write(value & 0xFF);
}
@Override
public void writeLong(long value) {
baos.write((int) ((value >>> 56) & 0xFF));
baos.write((int) ((value >>> 48) & 0xFF));
baos.write((int) ((value >>> 40) & 0xFF));
baos.write((int) ((value >>> 32) & 0xFF));
baos.write((int) ((value >>> 24) & 0xFF));
baos.write((int) ((value >>> 16) & 0xFF));
baos.write((int) ((value >>> 8) & 0xFF));
baos.write((int) (value & 0xFF));
}
@Override
public void writeFloat(float value) {
writeInt(Float.floatToIntBits(value));
}
@Override
public void writeDouble(double value) {
writeLong(Double.doubleToLongBits(value));
}
public int size() {
return baos.size();
}
public byte[] toByteArray() {
return baos.toByteArray();
}
}

View File

@@ -1,26 +0,0 @@
package mc.core.network.proto_1_12_2;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
@RequiredArgsConstructor
public enum Direction {
BOTTOM(0), // -Y
TOP(1), // +Y
NORTH(2), // -Z
SOUTH(3), // +Z
WEST(4), // -X
EAST(5); // +X
public static Direction getById(final int id) {
return Arrays.stream(Direction.values())
.filter(direction -> direction.id == id)
.findFirst()
.orElse(null);
}
@Getter
private final int id;
}

View File

@@ -1,82 +0,0 @@
package mc.core.network.proto_1_12_2;
import com.flowpowered.nbt.Tag;
import com.flowpowered.nbt.stream.NBTInputStream;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.NetInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public abstract class NetInputStream_p340 extends NetInputStream {
private NBTInputStream nbtInputStream;
@Override
public int readVarInt(AtomicInteger countReadBytes) {
int numRead = 0;
int result = 0;
byte read;
do {
if ((numRead+1) > 5) {
log.warn("VarInt is too big");
break;
}
read = readByte();
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
numRead++;
} while ((read & 0b10000000) != 0);
if (countReadBytes != null) {
countReadBytes.set(numRead);
}
return result;
}
@Override
public int readVarInt() {
return readVarInt(null);
}
@Override
public String readString() {
int size = readVarInt();
if (size == 0) {
log.warn("String zero length??");
return "";
}
byte[] bytes = new byte[size];
readBytes(bytes);
return new String(bytes, StandardCharsets.UTF_8);
}
@Override
public UUID readUUID() {
return new UUID(readLong(), readLong());
}
@Override
public Tag<?> readNBT() {
if (nbtInputStream == null) {
try {
nbtInputStream = new NBTInputStream(this, false);
} catch (IOException e) {
log.error("Create NBT stream", e);
return null;
}
}
try {
return nbtInputStream.readTag();
} catch (IOException e) {
log.error("Read NBT", e);
return null;
}
}
}

View File

@@ -1,63 +0,0 @@
package mc.core.network.proto_1_12_2;
import com.flowpowered.nbt.Tag;
import com.flowpowered.nbt.stream.NBTOutputStream;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.NetOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
@Slf4j
public abstract class NetOutputStream_p340 extends NetOutputStream {
private NBTOutputStream nbtOutputStream;
@Override
public void writeVarInt(int value) {
while ((value & -128) != 0) {
writeByte(value & 127 | 128);
value >>>= 7;
}
writeByte(value);
}
@Override
public void writeString(String value) {
if (value.length() > Short.MAX_VALUE) {
log.warn("String \"{}\" too long!", value);
byte[] buf = value.substring(0, Short.MAX_VALUE).getBytes(StandardCharsets.UTF_8);
writeVarInt(Short.MAX_VALUE);
writeBytes(buf);
} else {
byte[] buf = value.getBytes(StandardCharsets.UTF_8);
writeVarInt(value.length());
writeBytes(buf);
}
}
@Override
public void writeUUID(UUID uuid) {
writeLong(uuid.getMostSignificantBits());
writeLong(uuid.getLeastSignificantBits());
}
@Override
public void writeNBT(Tag<?> tag) {
if (nbtOutputStream == null) {
try {
nbtOutputStream = new NBTOutputStream(this, false);
} catch (IOException e) {
log.error("Create NBT stream", e);
return;
}
}
try {
nbtOutputStream.writeTag(tag);
} catch (IOException e) {
log.error("Write NBT", e);
}
}
}

View File

@@ -1,132 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-08
*/
package mc.core.network.proto_1_12_2;
import com.google.common.collect.ImmutableMap;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.CSPacket;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.packets.*;
import java.util.Map;
/**
* Для каждого состояния протокола имеется свой набор пакетов.
*/
@Slf4j
@RequiredArgsConstructor
public enum State {
/**
* Не известная стадия.
* Переход на этут стадию является следствием ошибки в работе протокола (умышленного или нет)
*/
UNKNOWN(-1, ImmutableMap.of(), ImmutableMap.of()),
/**
* Рукопожатие.
* С этого состояния начинается сюбое соединение с сервером.
*/
HANDSHAKE(0,
ImmutableMap.<Integer, Class<? extends CSPacket>>builder()
.put(0x00, HandshakePacket.class)
.build(),
null
),
/**
* Информация о сервере.
* Используется для получения Motd, кол-ва слотов и т.д.
*/
STATUS(1,
ImmutableMap.<Integer, Class<? extends CSPacket>>builder()
.put(0x00, StatusRequestPacket.class)
.put(0x01, PingPacket.class)
.build(),
ImmutableMap.<Class<? extends SCPacket>, Integer>builder()
.put(StatusResponsePacket.class, 0x00)
.put(PingPacket.class, 0x01)
.build()
),
/**
* Стадия логина/авторизации.
*/
LOGIN(2,
ImmutableMap.<Integer, Class<? extends CSPacket>>builder()
.put(0x00, LoginStartPacket.class)
.build(),
ImmutableMap.<Class<? extends SCPacket>, Integer>builder()
.put(DisconnectPacket.class, 0x00)
.put(LoginSuccessPacket.class, 0x02)
.build()
),
/**
* Игровая стадия.
* Основная стадия протокола.
*/
PLAY(3,
ImmutableMap.<Integer, Class<? extends CSPacket>>builder()
.put(0x00, TeleportConfirmPacket.class)
.put(0x01, TabCompletePacket.class)
.put(0x02, ChatMessageClientPacket.class)
.put(0x04, ClientSettingsPacket.class)
.put(0x09, PluginMessagePacket.class)
.put(0x0B, KeepAlivePacket.class)
.put(0x0D, PlayerPositionPacket.class)
.put(0x0E, PlayerPositionAndLookPacket.class)
.put(0x0F, PlayerLookPacket.class)
.put(0x13, PlayerAbilitiesPacket.class)
.put(0x14, PlayerDiggingPacket.class)
.put(0x15, EntityActionPacket.class)
.put(0x1A, HeldItemChangePacket.class)
.put(0x1D, AnimationPacket.class)
.put(0x1F, PlayerBlockPlacementPacket.class)
.build(),
ImmutableMap.<Class<? extends SCPacket>, Integer>builder()
.put(BossBarPacket.class, 0x0C)
.put(ChatMessageServerPacket.class, 0x0F)
.put(PluginMessagePacket.class, 0x18)
.put(UnloadChunkPacket.class, 0x1D)
.put(ChangeGameState.class, 0x1E)
.put(KeepAlivePacket.class, 0x1F)
.put(ChunkDataPacket.class, 0x20)
.put(JoinGamePacket.class, 0x23)
.put(PlayerAbilitiesPacket.class, 0x2C)
.put(PlayerListItemPacket.class, 0x2E)
.put(PlayerPositionAndLookPacket.class, 0x2F)
.put(SpawnPositionPacket.class, 0x46)
.put(TimeUpdatePacket.class, 0x47)
.put(TitlePacket.class, 0x48)
.put(PlayerListHeaderAndFooterPacket.class, 0x4A)
.build()
);
public static State valueOf(int id) {
if (id == 0) return HANDSHAKE;
else if (id == 1) return STATUS;
else if (id == 2) return LOGIN;
else if (id == 3) return PLAY;
else {
log.warn("Unknown state: {}", id);
return UNKNOWN;
}
}
@Getter
private final int id;
private final Map<Integer, Class<? extends CSPacket>> clientSidePacketsMap;
private final Map<Class<? extends SCPacket>, Integer> serverSidePacketsMap;
public Class<? extends CSPacket> getClientSidePacket(int id) {
return clientSidePacketsMap.get(id);
}
public Integer getServerSidePacket(Class<? extends SCPacket> clazz) {
return serverSidePacketsMap.get(clazz);
}
}

View File

@@ -1,55 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-23
*/
package mc.core.network.proto_1_12_2;
import lombok.AllArgsConstructor;
import mc.core.EntityLocation;
import mc.core.player.Player;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class TeleportManager {
private static TeleportManager instance = new TeleportManager();
public static TeleportManager getInstance() {
return instance;
}
@AllArgsConstructor
private class TpData {
public Player player;
public EntityLocation newLocation;
// TODO необходимо добавить TimeStamp, что бы понимать, когда клиент отвергнул телепортацию
// т.е. идея такова: долгое молчание клиента знак отвержения телепортации.
}
private final Random RAND = new Random();
private final Map<Integer, TpData> teleportMap = new HashMap<>();
private TeleportManager() {}
public int append(Player player, EntityLocation location) {
int teleportId;
do {
teleportId = RAND.nextInt(9999);
} while (teleportMap.containsKey(teleportId));
teleportMap.put(teleportId, new TpData(player, location.clone()));
return teleportId;
}
public void apply(int teleportId) {
if (teleportMap.containsKey(teleportId)) {
TpData data = teleportMap.remove(teleportId);
data.player.getLocation().set(data.newLocation);
}
}
public void removeDataPlayer(Player player) {
teleportMap.entrySet().removeIf(entry -> entry.getValue().player.equals(player));
}
}

View File

@@ -1,32 +0,0 @@
/**
* Протокол Minecraft версии 1.12.2 (номер версии протокола - 340)
*
*
* Типы данных.
*
* (см. http://wiki.vg/Protocol#Data_types)
*
*
* Формат пакетов.
*
* Есть два варианта: без использования сжатия и с использованием.
* Регулируется это пакетом {@link mc.core.network.proto_1_12_2.packets.SetCompressionPacket}
*
* Формат без использования сжатия:
*
* +---------------+------------+--------------------+
* | Название | Тип | Комментарий |
* +---------------+------------+--------------------+
* | Размер пакета | VarInt | ID пакета + данные |
* +---------------+------------+--------------------+
* | ID пакета | VarInt | |
* +---------------+------------+--------------------+
* | Данные | Byte Array | |
* +---------------+------------+--------------------+
*
* Формат с использованием сжатия:
*
* (см. http://wiki.vg/Protocol#With_compression)
*/
package mc.core.network.proto_1_12_2;

View File

@@ -1,17 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-17
*/
package mc.core.network.proto_1_12_2.packets;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
public class AnimationPacket implements CSPacket {
private int handAnimation;
@Override
public void readSelf(NetInputStream netStream) {
this.handAnimation = netStream.readVarInt();
}
}

View File

@@ -1,117 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-07-12
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.serializers.TextMapper;
import mc.core.text.Text;
import java.util.UUID;
@Setter
@ToString
public class BossBarPacket implements SCPacket {
@RequiredArgsConstructor
public enum Action {
ADD(0),
REMOVE(1),
UPDATE_HEALTH(2),
UPDATE_TITLE(3),
UPDATE_STYLE(4),
UPDATE_FLAGS(5);
@Getter
private final int id;
}
@RequiredArgsConstructor
public enum Color {
PINK(0),
BLUE(1),
RED(2),
GREEN(3),
YELLOW(4),
PURPLE(5),
WHITE(5);
@Getter
private final int id;
}
@RequiredArgsConstructor
public enum Division {
NO(0),
_0(0),
_6(1),
_10(2),
_12(3),
_20(4);
@Getter
private final int id;
}
@RequiredArgsConstructor
public enum Flag {
NO(0x00),
DAKR_SKY(0x01),
DRAGON_BAR(0x02);
@Getter
private final int id;
}
@Getter
@Setter
public static class BarData {
private Text title;
/*
* From 0 to 1.
* Values greater than 1 do not crash a Notchian client,
* and start rendering part of a second health bar at around 1.5.
* (https://i.johni0702.de/nA.png)
*/
private float health;
private Color color;
private Division division;
private Flag flags;
}
private UUID uuid; // Unique ID for this bar
private Action action;
private BarData barData;
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeUUID(uuid);
netStream.writeVarInt(action.id);
if (action == Action.REMOVE) {
return;
}
if (action == Action.ADD || action == Action.UPDATE_TITLE) {
netStream.writeString(TextMapper.getInstance().mapping(barData.title));
}
if (action == Action.ADD || action == Action.UPDATE_HEALTH) {
netStream.writeFloat(barData.health);
}
if (action == Action.ADD || action == Action.UPDATE_STYLE) {
netStream.writeVarInt(barData.color.id);
netStream.writeVarInt(barData.division.id);
}
if (action == Action.ADD || action == Action.UPDATE_FLAGS) {
netStream.writeUnsignedByte(barData.flags.id);
}
}
}

View File

@@ -1,36 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-07-27
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
@Setter
public class ChangeGameState implements SCPacket {
@RequiredArgsConstructor
public enum Reason {
INVALID_BED(0), // Would be used to switch between messages, but the only used message is 0 for invalid bed (wat?)
RAINING_END(1),
RAINING_BEGIN(2),
CHANGE_GAMEMODE(3), // 0: Survival, 1: Creative, 2: Adventure, 3: Spectator
ARROW_HITTING_PLAYER(6), // Appears to be played when an arrow strikes another player in Multiplayer
FADE_VALUE(7), // The current darkness value. 1 = Dark, 0 = Bright, Setting the value higher causes the game to change color and freeze
FADE_TIME(8), // Time in ticks for the sky to fade
GUARDIAN_APPEARANCE(10); // Play elder guardian mob appearance (effect and sound)
private final int id;
}
private Reason reason;
private float value;
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeUnsignedByte(reason.id);
netStream.writeFloat(value);
}
}

View File

@@ -1,21 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-17
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
@Getter
@ToString
public class ChatMessageClientPacket implements CSPacket {
private String message;
@Override
public void readSelf(NetInputStream netStream) {
this.message = netStream.readString();
}
}

View File

@@ -1,30 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-24
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import mc.core.chat.MessageType;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.serializers.TextMapper;
import mc.core.text.Text;
@AllArgsConstructor
@NoArgsConstructor
@Setter
@ToString
public class ChatMessageServerPacket implements SCPacket {
private Text text;
private MessageType type;
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeString(TextMapper.getInstance().mapping(text));
netStream.writeByte(type.getId());
}
}

View File

@@ -1,317 +0,0 @@
package mc.core.network.proto_1_12_2.packets;
import com.flowpowered.nbt.CompoundTag;
import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
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.utils.NibbleArray;
import mc.core.world.block.Block;
import mc.core.world.block.BlockLocation;
import mc.core.world.block.BlockType;
import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkSection;
import java.util.ArrayList;
import java.util.Comparator;
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"
private Chunk chunk;
private List<ChunkSection> sectionList;
public void setChunk(Chunk chunk) {
this.sectionList = null;
this.chunk = chunk;
}
public void setChunkSectionList(List<ChunkSection> sectionList) {
this.chunk = null;
this.sectionList = sectionList;
}
@Override
public void writeSelf(NetOutputStream netStream) {
if (sectionList == null && chunk == null) {
log.warn("Empty chunk data!"); //TODO для такого нужна заглушка
return;
}
netStream.writeInt(x); // Chunk X
netStream.writeInt(z); // Chunk Y
netStream.writeBoolean(initChunk); // Init Chunk
int maxH = 0;
int bitMask = 0;
if (sectionList == null && chunk != null) {
for (int h = 15; h >= 0; h--) {
bitMask = bitMask << 1;
ChunkSection chunkSection = chunk.getChunkSection(h);
if (chunkSection != null && chunkSection.getY() == h) {
bitMask |= 0x01;
maxH++;
} else {
bitMask |= 0x00;
}
}
} else if (sectionList != null && chunk == null) {
sectionList.sort(Comparator.comparingInt(ChunkSection::getY));
for (int h = 15, i = 0; h >= 0; h--) {
bitMask = bitMask << 1;
ChunkSection chunkSection = sectionList.get(i);
if (chunkSection != null && chunkSection.getY() == h) {
bitMask |= 0x01;
maxH++;
} else {
bitMask |= 0x00;
}
}
}
netStream.writeVarInt(bitMask); // Primary Bit Mask
final ByteArrayOutputNetStream data = new ByteArrayOutputNetStream();
final ByteArrayOutputNetStream biomes = new ByteArrayOutputNetStream();
boolean biomeWrite = true;
List<CompoundTag> nbtList = new ArrayList<>();
for (int h = 0; h < maxH; h++) {
ChunkSection chunkSection = null;
if (chunk != null) {
chunkSection = chunk.getChunkSection(h);
} else if (sectionList != null) {
chunkSection = sectionList.remove(0);
}
if (chunkSection == null) {
continue;
}
final PalettedChunkSection palettedChunkSection = new PalettedChunkSection();
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
Block block = chunkSection.getBlock(x, y, z);
palettedChunkSection.addBlock(
block,
chunkSection.getSkyLight(x, y, z)
);
CompoundTag nbt = block.getNBTData();
if (nbt != null) {
nbtList.add(nbt);
}
if (biomeWrite) {
biomes.writeByte(chunk.getBiome(
(chunk.getX() << 4) + x,
(chunk.getZ() << 4) + z
).getId());
if (x == 15 && z == 15) {
biomeWrite = false;
}
}
}
}
}
// <Chunk Section>
palettedChunkSection.writeToNetStream(data);
// </Chunk Section>
}
// <Biomes>
data.writeBytes(biomes.toByteArray());
// </Biomes>
netStream.writeVarInt(data.size()); // Size of Data
netStream.writeBytes(data.toByteArray()); // Data
netStream.writeVarInt(nbtList.size()); // Number of block entities
// <NBT>
for (CompoundTag compoundTag : nbtList) {
netStream.writeNBT(compoundTag);
}
// </NBT>
}
@Override
public String toString() {
return "ChunkDataPacket{" +
"x=" + x +
", z=" + z +
", chunk=" + chunk +
'}';
}
private class PalettedChunkSection {
private TIntList palette = new TIntArrayList();
private byte[] blocks = new byte[4096];
private NibbleArray blockLight = new NibbleArray();
private NibbleArray skyLight = new NibbleArray();
private int coordsToIndex(BlockLocation location) {
return coordsToIndex(location.getX(), location.getY(), location.getZ());
}
private int coordsToIndex(int x, int y, int z) {
return y << 8 | z << 4 | x;
}
private int serializeBlockState(BlockType blockType) {
return (blockType.getId() << 4) | blockType.getMeta();
}
byte addBlockType(BlockType blockType) {
int blockState = serializeBlockState(blockType);
int idx = palette.indexOf(blockState);
if (idx == -1) {
palette.add(blockState);
idx = palette.size()-1;
}
return (byte) idx;
}
void addBlock(Block block, int skyLight) {
BlockLocation location = new BlockLocation(
block.getLocation().getX() - ((block.getLocation().getX() >> 4) << 4),
block.getLocation().getY() - ((block.getLocation().getY() >> 4) << 4),
block.getLocation().getZ() - ((block.getLocation().getZ() >> 4) << 4)
);
blocks[coordsToIndex(location)] = addBlockType(block.getType());
blockLight.set(location, block.getLight());
this.skyLight.set(location, skyLight);
}
void writeToNetStream(final NetOutputStream netOutputStream) {
int bitsPerBlock = 4;
if (palette.size() > 15) {
if (palette.size() <= 31)
bitsPerBlock = 5;
else if (palette.size() <= 63)
bitsPerBlock = 6;
else if (palette.size() <= 127)
bitsPerBlock = 7;
else if (palette.size() <= 255)
bitsPerBlock = 8;
}
// <Palette>
netOutputStream.writeUnsignedByte(bitsPerBlock); // Bits Per Block
netOutputStream.writeVarInt(palette.size()); // Size of palette
palette.forEach(value -> { netOutputStream.writeVarInt(value); return true; }); // Palette
// </Palette>
// <Data Array>
final int dataLength = (4096/*16*16*16*/ * bitsPerBlock) / 64/*size of long in bits*/;
netOutputStream.writeVarInt(dataLength); // Size of Data Array
// <Array>
long value = 0;
int lastPos = 0;
boolean fairy = false;
long fairyValue = 0;
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
final int blockNumber = (((y << 4) + z) << 4) + x;
final int startLong = ( blockNumber * bitsPerBlock ) / 64;
final int startOffset = ( blockNumber * bitsPerBlock ) % 64;
final int endLong = ((blockNumber + 1) * bitsPerBlock - 1) / 64;
final long idxBlockInPalette = blocks[coordsToIndex(x, y, z)];
if (startLong != lastPos) {
netOutputStream.writeLong(value);
lastPos = startLong;
if (fairy) {
value = fairyValue;
fairy = false;
} else {
value = 0;
}
}
value |= (idxBlockInPalette << startOffset);
if (startLong != endLong) {
fairyValue = idxBlockInPalette >> (64 - startOffset);
fairy = true;
}
}
}
}
netOutputStream.writeLong(value);
// </Array>
// </Data Array>
// <Block Light>
netOutputStream.writeBytes(blockLight.getRawData());
// </Block Light>
// <Sky Light>
netOutputStream.writeBytes(skyLight.getRawData());
// </Sky Light>
}
}
}

View File

@@ -1,48 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-11
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
@NoArgsConstructor
@Getter
@ToString
public class ClientSettingsPacket implements CSPacket {
private String locale;
private int viewDistance;
private int chatMode;
private boolean chatColors;
private boolean capeEnabled,
jacketEnabled,
leftSleeveEnabled,
rightSleeveEnabled,
leftPantsLegEnabled,
rightPantsLegEnabled,
hatEnabled;
private int mainHand;
@Override
public void readSelf(NetInputStream netStream) {
locale = netStream.readString();
viewDistance = netStream.readByte();
chatMode = netStream.readVarInt();
chatColors = netStream.readBoolean();
int bitmask = netStream.readUnsignedByte();
capeEnabled = (bitmask & 0x01) > 0;
jacketEnabled = (bitmask & 0x02) > 0;
leftSleeveEnabled = (bitmask & 0x04) > 0;
rightSleeveEnabled = (bitmask & 0x08) > 0;
leftPantsLegEnabled = (bitmask & 0x10) > 0;
rightPantsLegEnabled = (bitmask & 0x20) > 0;
hatEnabled = (bitmask & 0x40) > 0;
mainHand = netStream.readVarInt();
}
}

View File

@@ -1,25 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.serializers.TextMapper;
import mc.core.text.Text;
@AllArgsConstructor
@NoArgsConstructor
@Setter
public class DisconnectPacket implements SCPacket {
private Text reason;
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeString(TextMapper.getInstance().mapping(reason));
}
}

View File

@@ -1,32 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-11
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
import java.security.PublicKey;
@AllArgsConstructor
@NoArgsConstructor
@Setter
public class EncryptionRequestPacket implements SCPacket {
private String serverId;
private PublicKey publicKey;
private byte[] verifyToken;
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeString(serverId);
byte[] bytes = publicKey.getEncoded();
netStream.writeVarInt(bytes.length);
netStream.writeBytes(bytes);
netStream.writeVarInt(verifyToken.length);
netStream.writeBytes(verifyToken);
}
}

View File

@@ -1,45 +0,0 @@
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import java.util.Arrays;
@Getter
public class EntityActionPacket implements CSPacket {
@RequiredArgsConstructor
public enum Action {
START_SNEAKING(0),
STOP_SNEAKING(1),
LEAVE_BED(2), // Leave bed is only sent when the “Leave Bed” button is clicked on the sleep GUI, not when waking up due today time.
START_SPRINTING(3),
STOP_SPRINTING(4),
START_JUMP_WITH_HORSE(5),
STOP_JUMP_WITH_HORSE(6),
OPEN_HORSE_INVENTORY(7), // Open horse inventory is only sent when pressing the inventory key (default: E) while on a horse — all other methods of opening a horse's inventory (involving right-clicking or shift-right-clicking it) do not use this packet.
START_FLYING_WITH_ELYTRA(8);
public static Action getById(final int id) {
return Arrays.stream(Action.values())
.filter(action -> action.id == id)
.findFirst()
.orElse(null);
}
@Getter
private final int id;
}
private int entityId;
private Action action;
private int jumpBoost; // Only used by the “start jump with horse” action, in which case it ranges from 0 to 100. In all other cases it is 0.
@Override
public void readSelf(NetInputStream netStream) {
entityId = netStream.readVarInt();
action = Action.getById(netStream.readVarInt());
jumpBoost = netStream.readVarInt();
}
}

View File

@@ -1,30 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.network.proto_1_12_2.State;
@NoArgsConstructor
@Getter
@ToString
public class HandshakePacket implements CSPacket {
private int protocolVersion;
private String address;
private int serverPort;
private State nextState;
@Override
public void readSelf(NetInputStream netStream) {
this.protocolVersion = netStream.readVarInt();
this.address = netStream.readString();
this.serverPort = netStream.readUnsignedShort();
this.nextState = State.valueOf(netStream.readVarInt());
}
}

View File

@@ -1,17 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-17
*/
package mc.core.network.proto_1_12_2.packets;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
public class HeldItemChangePacket implements CSPacket {
private int slot;
@Override
public void readSelf(NetInputStream netStream) {
this.slot = netStream.readShort();
}
}

View File

@@ -1,34 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-11
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
import mc.core.player.PlayerMode;
@NoArgsConstructor
@Setter
@ToString
public class JoinGamePacket implements SCPacket {
private int entityId;
private PlayerMode mode;
private int dimension;
private int difficulty;
private String levelType;
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeInt(entityId);
netStream.writeUnsignedByte(mode.getId());
netStream.writeInt(dimension);
netStream.writeUnsignedByte(difficulty);
netStream.writeUnsignedByte(0); // Max Players, unused
netStream.writeString(levelType);
netStream.writeBoolean(false); // Reduced Debug Info
}
}

View File

@@ -1,32 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-23
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
@AllArgsConstructor
@NoArgsConstructor
@Setter
@ToString
public class KeepAlivePacket implements CSPacket, SCPacket {
private long payload;
@Override
public void readSelf(NetInputStream netStream) {
this.payload = netStream.readLong();
}
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeLong(this.payload);
}
}

View File

@@ -1,21 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
@Getter
@ToString
public class LoginStartPacket implements CSPacket {
private String playerName;
@Override
public void readSelf(NetInputStream netStream) {
this.playerName = netStream.readString();
}
}

View File

@@ -1,29 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-11
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
import java.util.UUID;
@AllArgsConstructor
@NoArgsConstructor
@Setter
@ToString
public class LoginSuccessPacket implements SCPacket {
private UUID uuid;
private String playerName;
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeString(uuid.toString());
netStream.writeString(playerName);
}
}

View File

@@ -1,26 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
@ToString
public class PingPacket implements CSPacket, SCPacket {
private long payload;
@Override
public void readSelf(NetInputStream netStream) {
this.payload = netStream.readLong();
}
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeLong(payload);
}
}

View File

@@ -1,58 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-11
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
@NoArgsConstructor
@Getter
@Setter
@ToString
public class PlayerAbilitiesPacket implements SCPacket, CSPacket {
private static final byte $GOD_MODE_MASK = 0x01,
$FLYING_MASK = 0x02,
$CAN_FLY_MASK = 0x04,
$IDB_MASK = 0x08;
private boolean godMode = false;
private boolean flying = false;
private boolean canFly = false;
private boolean instantDestroyBlocks = false;
private float flyingSpeed = 0.05f;
private float fieldOfView = flyingSpeed;
private float walkingSpeed;
@Override
public void writeSelf(NetOutputStream netStream) {
byte flag = 0;
if (godMode) flag = (byte)(flag | $GOD_MODE_MASK);
if (flying) flag = (byte)(flag | $FLYING_MASK);
if (canFly) flag = (byte)(flag | $CAN_FLY_MASK);
if (instantDestroyBlocks) flag = (byte)(flag | $IDB_MASK);
netStream.writeByte(flag);
netStream.writeFloat(flyingSpeed);
netStream.writeFloat(fieldOfView);
}
@Override
public void readSelf(NetInputStream netStream) {
byte flag = netStream.readByte();
godMode = (flag & $GOD_MODE_MASK) > 0;
canFly = (flag & $CAN_FLY_MASK) > 0;
flying = (flag & $FLYING_MASK) > 0;
instantDestroyBlocks = (flag & $IDB_MASK) > 0;
flyingSpeed = netStream.readFloat();
walkingSpeed = netStream.readFloat();
}
}

View File

@@ -1,30 +0,0 @@
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.network.proto_1_12_2.Direction;
import mc.core.network.proto_1_12_2.serializers.BlockLocationSerializer;
import mc.core.world.block.BlockLocation;
@Getter
@ToString
public class PlayerBlockPlacementPacket implements CSPacket {
private BlockLocation location;
private Direction face;
/** true - main hand; false - off hand */
private boolean hand;
private float cursorX, cursorY, cursorZ;
@Override
public void readSelf(NetInputStream netStream) {
long compactedCoords = netStream.readLong();
location = BlockLocationSerializer.fromLong(compactedCoords);
face = Direction.getById(netStream.readVarInt());
hand = (netStream.readVarInt() == 1);
cursorX = netStream.readFloat();
cursorY = netStream.readFloat();
cursorZ = netStream.readFloat();
}
}

View File

@@ -1,55 +0,0 @@
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.network.proto_1_12_2.Direction;
import mc.core.network.proto_1_12_2.serializers.BlockLocationSerializer;
import mc.core.world.block.BlockLocation;
import java.util.Arrays;
@Getter
@ToString
public class PlayerDiggingPacket implements CSPacket {
@RequiredArgsConstructor
public enum Status {
STARTED_DIGGING(0),
CANCELLED_DIGGING(1),
FINISHED_DIGGING(2),
DROP_ITEM_STACK(3),
DROP_ITEM(4),
/* Indicates that the currently held item should have its
* state updated such as eating food, pulling back bows,
* using buckets, etc. Location is always set to 0/0/0,
* Face is always set to -Y.
*/
SHOOT_ARROW(5),
FINISH_EATING(5),
SWAP_ITEM_IN_HAND(6);
public static Status getById(final int id) {
return Arrays.stream(Status.values())
.filter(status -> status.id == id)
.findFirst()
.orElse(null);
}
@Getter
private final int id;
}
private Status status;
private BlockLocation location;
private Direction face;
@Override
public void readSelf(NetInputStream netStream) {
status = Status.getById(netStream.readVarInt());
long compactCoord = netStream.readLong();
location = BlockLocationSerializer.fromLong(compactCoord);
face = Direction.getById(netStream.readByte());
}
}

View File

@@ -1,35 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-07-11
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.Setter;
import lombok.ToString;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.serializers.TextMapper;
import mc.core.text.Text;
@Setter
@ToString
public class PlayerListHeaderAndFooterPacket implements SCPacket {
// To remove the header/footer, send a empty translatable component: {"translate":""}
private Text header;
private Text footer;
@Override
public void writeSelf(NetOutputStream netStream) {
if (header == null) {
netStream.writeString("{\"translate\":\"\"}");
} else {
netStream.writeString(TextMapper.getInstance().mapping(header));
}
if (footer == null) {
netStream.writeString("{\"translate\":\"\"}");
} else {
netStream.writeString(TextMapper.getInstance().mapping(footer));
}
}
}

View File

@@ -1,84 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-07-11
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.serializers.TextMapper;
import mc.core.player.PlayerMode;
import mc.core.text.Text;
import java.util.*;
@Slf4j
@Getter
@Setter
@ToString
public class PlayerListItemPacket implements SCPacket {
@RequiredArgsConstructor
public enum Action {
ADD_PLAYER(0),
UPDATE_GAMEMODE(1),
UPDATE_LATENCY(2),
UPDATE_DISPLAY_NAME(3),
REMOVE_PLAYER(4);
@Getter
private final int id;
}
@Data
@ToString
public static class PlayerData {
private UUID uuid;
private String name;
private Properties properties = new Properties();
private PlayerMode gameMode;
private int ping;
private boolean hasDisplayName = false;
private Text displayName;
}
private Action action;
private List<PlayerData> listPlayers = new ArrayList<>();
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeVarInt(action.id);
netStream.writeVarInt(listPlayers.size());
for (PlayerData playerData : listPlayers) {
netStream.writeUUID(playerData.uuid);
if (action == Action.ADD_PLAYER) {
netStream.writeString(playerData.name);
netStream.writeVarInt(playerData.properties.size());
for (Map.Entry<Object, Object> entry : playerData.properties.entrySet()) {
netStream.writeString(entry.getKey().toString());
netStream.writeString(entry.getValue().toString());
netStream.writeBoolean(false); // Is Signed
}
}
if (action == Action.ADD_PLAYER || action == Action.UPDATE_GAMEMODE) {
netStream.writeVarInt(playerData.gameMode.getId());
}
if (action == Action.ADD_PLAYER || action == Action.UPDATE_LATENCY) {
netStream.writeVarInt(playerData.ping);
}
if (action == Action.ADD_PLAYER || action == Action.UPDATE_DISPLAY_NAME) {
netStream.writeBoolean(playerData.hasDisplayName);
if (playerData.hasDisplayName) {
netStream.writeString(TextMapper.getInstance().mapping(playerData.displayName));
}
}
}
}
}

View File

@@ -1,18 +0,0 @@
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
@Getter
public class PlayerLookPacket implements CSPacket {
private float yaw, pitch;
private boolean onGround; // True if the client is on the ground, false otherwise
@Override
public void readSelf(NetInputStream netStream) {
this.yaw = netStream.readFloat();
this.pitch = netStream.readFloat();
this.onGround = netStream.readBoolean();
}
}

View File

@@ -1,56 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-11
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import mc.core.EntityLocation;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
@NoArgsConstructor
@Getter
@ToString
public class PlayerPositionAndLookPacket implements SCPacket, CSPacket {
@Setter
private EntityLocation location;
@Setter
private int teleportId;
private boolean onGround = false;
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeDouble(location.getX());
netStream.writeDouble(location.getY());
netStream.writeDouble(location.getZ());
netStream.writeFloat(location.getYaw());
netStream.writeFloat(location.getPitch());
netStream.writeByte(0); // It's a bitfield, X/Y/Z/Y_ROT/X_ROT. If X is setXYZ, the x value is relative and not absolute.
/* X - 0x01
* Y - 0x02
* Z - 0x04
* Y_ROT - 0x08
* X_ROT - 0x10
*/
netStream.writeVarInt(teleportId); // Client should confirm this packet with Teleport Confirm containing the same Teleport ID
}
@Override
public void readSelf(NetInputStream netStream) {
this.location = new EntityLocation(
netStream.readDouble(),
netStream.readDouble(),
netStream.readDouble(),
netStream.readFloat(),
netStream.readFloat()
);
this.onGround = netStream.readBoolean();
}
}

View File

@@ -1,19 +0,0 @@
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
@Getter
public class PlayerPositionPacket implements CSPacket {
private double x, y, z; // Y - is feet position. Normally Head Y - +1.62d
private boolean onGround; // True if the client is on the ground, false otherwise
@Override
public void readSelf(NetInputStream netStream) {
this.x = netStream.readDouble();
this.y = netStream.readDouble();
this.z = netStream.readDouble();
this.onGround = netStream.readBoolean();
}
}

View File

@@ -1,34 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-11
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.*;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class PluginMessagePacket implements SCPacket, CSPacket {
private String channelName;
private byte[] data;
@Override
public void readSelf(NetInputStream netStream) {
channelName = netStream.readString();
data = new byte[netStream.getDataSize() - channelName.getBytes().length - 1];
netStream.readBytes(data);
}
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeString(channelName);
netStream.writeBytes(data);
}
}

View File

@@ -1,28 +0,0 @@
package mc.core.network.proto_1_12_2.packets;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import mc.core.EntityLocation;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
@AllArgsConstructor
@NoArgsConstructor
@Setter
@ToString
public class SpawnPositionPacket implements SCPacket {
private EntityLocation location;
private long location2long(EntityLocation entityLocation) {
return (((long) entityLocation.getBlockX() & 0x3FFFFFF) << 38)
| (((long) entityLocation.getBlockY() & 0x0000FFF) << 26)
| ((long) entityLocation.getBlockZ() & 0x3FFFFFF);
}
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeLong(location2long(location));
}
}

View File

@@ -1,16 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
@ToString
public class StatusRequestPacket implements CSPacket {
@Override
public void readSelf(NetInputStream netStream) {
}
}

View File

@@ -1,53 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.packets;
import com.google.gson.JsonObject;
import lombok.Setter;
import lombok.ToString;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
@Setter
@ToString
public class StatusResponsePacket implements SCPacket {
public static final String NAME = "1.12.2";
public static final int PROTOCOL = 340;
private static final JsonObject versionObj;
static {
versionObj = new JsonObject();
versionObj.addProperty("name", NAME);
versionObj.addProperty("protocol", PROTOCOL);
}
private int maxOnline;
private int online;
private String description;
private byte[] faviconBase64;
@Override
public void writeSelf(NetOutputStream netStream) {
JsonObject playersObj = new JsonObject();
playersObj.addProperty("max", maxOnline);
playersObj.addProperty("online", online);
JsonObject descriptionObj = new JsonObject();
descriptionObj.addProperty("text", description);
JsonObject rootObj = new JsonObject();
rootObj.add("version", versionObj);
rootObj.add("players", playersObj);
rootObj.add("description", descriptionObj);
if (faviconBase64 != null && faviconBase64.length > 0) {
rootObj.addProperty("favicon",
"data:image/png;base64," + new String(faviconBase64)
);
}
netStream.writeString(rootObj.toString());
}
}

View File

@@ -1,33 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-17
*/
package mc.core.network.proto_1_12_2.packets;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.world.block.BlockLocation;
public class TabCompletePacket implements CSPacket {
private String text;
private boolean assumeCommand;
private boolean hasPosition;
private BlockLocation location;
@Override
public void readSelf(NetInputStream netStream) {
this.text = netStream.readString();
this.assumeCommand = netStream.readBoolean();
this.hasPosition = netStream.readBoolean();
if (this.hasPosition) {
long compactValue = netStream.readLong();
double x = compactValue >> 38;
double y = (compactValue >> 26) & 0xFFF;
double z = compactValue << 38 >> 38; // is normal?
this.location = new BlockLocation((int)x, (int)y, (int)z); //FIXME
}
}
}

View File

@@ -1,21 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-12
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.ToString;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
@Getter
@ToString
public class TeleportConfirmPacket implements CSPacket {
private int teleportId;
@Override
public void readSelf(NetInputStream netStream) {
teleportId = netStream.readVarInt();
}
}

View File

@@ -1,27 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-24
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
@AllArgsConstructor
@NoArgsConstructor
@Setter
@ToString
public class TimeUpdatePacket implements SCPacket {
private long time;
private long worldage;
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeLong(worldage);
netStream.writeLong(time);
}
}

View File

@@ -1,117 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-24
*/
package mc.core.network.proto_1_12_2.packets;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.serializers.TextMapper;
import mc.core.text.Text;
@RequiredArgsConstructor
@Setter
@ToString
public class TitlePacket implements SCPacket {
private static final int TICKS_PER_SEC = 20,
MIN_MS = (1000 / TICKS_PER_SEC);
@RequiredArgsConstructor
public enum Action {
SET_TITLE(0),
SET_SUBTITLE(1),
SET_ACTION_BAR(2),
SET_DISPLAY_TIME(3),
HIDE(4),
RESET(5);
@Getter
private final int id;
}
private final Action action;
private Text text = null;
private Integer fadeInTime = null;
private Integer stayTime = null;
private Integer fadeOutTime = null;
public TitlePacket(Action action, Object... values) {
if (values.length == 0 && (action != Action.HIDE && action != Action.RESET)) {
this.action = Action.HIDE;
return;
}
this.action = action;
switch (this.action) {
case SET_TITLE:
case SET_SUBTITLE:
case SET_ACTION_BAR:
if (values[0] == null) {
this.text = Text.of();
} else if (values[0] instanceof Text) {
this.text = (Text) values[0];
} else {
this.text = Text.of(values[0].toString());
}
break;
case SET_DISPLAY_TIME:
if (values.length < 3) {
this.fadeInTime = 0;
this.stayTime = 0;
this.fadeOutTime = 0;
} else {
if (values[0] instanceof Integer) {
if (((Integer) values[0]) < MIN_MS) {
this.fadeInTime = 1;
} else {
this.fadeInTime = ((Integer) values[0]) / MIN_MS;
}
} else {
this.fadeInTime = 0;
}
if (values[1] instanceof Integer) {
if (((Integer) values[1]) < MIN_MS) {
this.stayTime = 1;
} else {
this.stayTime = ((Integer) values[1]) / MIN_MS;
}
} else {
this.stayTime = 0;
}
if (values[2] instanceof Integer) {
if (((Integer) values[2]) < MIN_MS) {
this.fadeOutTime = 1;
} else {
this.fadeOutTime = ((Integer) values[2]) / MIN_MS;
}
} else {
this.fadeOutTime = 0;
}
}
}
}
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeVarInt(action.id);
switch (action) {
case SET_TITLE:
case SET_SUBTITLE:
case SET_ACTION_BAR:
netStream.writeString(TextMapper.getInstance().mapping(this.text));
break;
case SET_DISPLAY_TIME:
netStream.writeInt(this.fadeInTime);
netStream.writeInt(this.stayTime);
netStream.writeInt(this.fadeOutTime);
}
}
}

View File

@@ -1,16 +0,0 @@
package mc.core.network.proto_1_12_2.packets;
import lombok.Setter;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
public class UnloadChunkPacket implements SCPacket {
@Setter
private int x, z;
@Override
public void writeSelf(NetOutputStream netStream) {
netStream.writeInt(x);
netStream.writeInt(z);
}
}

View File

@@ -1,63 +0,0 @@
package mc.core.network.proto_1_12_2.serializers;
import mc.core.world.block.BlockLocation;
import static com.google.common.math.IntMath.isPowerOfTwo;
public class BlockLocationSerializer {
private static final int[] MULTIPLY_DE_BRUIJN_BIT_POSITION = new int[] {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
private static final int NUM_X_BITS = 1 + log2(smallestEncompassingPowerOfTwo(30000000));
private static final int NUM_Z_BITS = NUM_X_BITS;
private static final int NUM_Y_BITS = 64 - NUM_X_BITS - NUM_Z_BITS;
private static final int Y_SHIFT = NUM_Z_BITS;
private static final int X_SHIFT = Y_SHIFT + NUM_Y_BITS;
private static final long X_MASK = (1L << NUM_X_BITS) - 1L;
private static final long Y_MASK = (1L << NUM_Y_BITS) - 1L;
private static final long Z_MASK = (1L << NUM_Z_BITS) - 1L;
/*
* net.minecraft.util.math.MathHelper#log2(int)
*/
private static int log2(int value) {
return log2DeBruijn(value) - (isPowerOfTwo(value) ? 0 : 1);
}
/*
* net.minecraft.util.math.MathHelper#log2DeBruijn(int)
*/
private static int log2DeBruijn(int value) {
value = isPowerOfTwo(value) ? value : smallestEncompassingPowerOfTwo(value);
return MULTIPLY_DE_BRUIJN_BIT_POSITION[(int)((long)value * 125613361L >> 27) & 31];
}
/*
* net.minecraft.util.math.MathHelper#smallestEncompassingPowerOfTwo(int)
*/
private static int smallestEncompassingPowerOfTwo(int value) {
int i = value - 1;
i = i | i >> 1;
i = i | i >> 2;
i = i | i >> 4;
i = i | i >> 8;
i = i | i >> 16;
return i + 1;
}
public static long toLong(BlockLocation location) {
return ((long)location.getX() & X_MASK) << X_SHIFT |
((long)location.getY() & Y_MASK) << Y_SHIFT |
((long)location.getZ() & Z_MASK);
}
public static BlockLocation fromLong(long value) {
BlockLocation location = BlockLocation.ZERO();
fromLong(value, location);
return location;
}
public static void fromLong(long value, BlockLocation location) {
location.setX((int)(value << 64 - X_SHIFT - NUM_X_BITS >> 64 - NUM_X_BITS));
location.setY((int)(value << 64 - Y_SHIFT - NUM_Y_BITS >> 64 - NUM_Y_BITS));
location.setZ((int)(value << 64 - NUM_Z_BITS >> 64 - NUM_Z_BITS));
}
}

View File

@@ -1,5 +0,0 @@
package mc.core.network.proto_1_12_2.serializers;
public interface Mapper<F, T> {
T mapping(F fromObject);
}

View File

@@ -1,55 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-11
*/
package mc.core.network.proto_1_12_2.serializers;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import lombok.Getter;
import mc.core.text.Text;
public class TextMapper implements Mapper<Text, String> {
@Getter
private static TextMapper instance = new TextMapper();
private JsonObject serialize(Text text) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("text", text.getContent());
if (text.getColor() != null) {
jsonObject.addProperty("color", text.getColor().getName());
}
if (text.getStyle() != null) {
if (text.getStyle().getBold().isPresent()) {
jsonObject.addProperty("bold", text.getStyle().getBold().get());
}
if (text.getStyle().getItalic().isPresent()) {
jsonObject.addProperty("italic", text.getStyle().getItalic().get());
}
if (text.getStyle().getObfuscated().isPresent()) {
jsonObject.addProperty("obfuscated", text.getStyle().getObfuscated().get());
}
if (text.getStyle().getStrikethrough().isPresent()) {
jsonObject.addProperty("strikethrough", text.getStyle().getStrikethrough().get());
}
if (text.getStyle().getUnderline().isPresent()) {
jsonObject.addProperty("underlined", text.getStyle().getUnderline().get());
}
}
if (text.getChildren() != null) {
JsonArray extra = new JsonArray();
text.getChildren().forEach(child -> extra.add(serialize(child)));
jsonObject.add("extra", extra);
}
return jsonObject;
}
@Override
public String mapping(Text fromObject) {
return serialize(fromObject).toString();
}
}

View File

@@ -1,84 +0,0 @@
package mc.core.network.proto_1_12_2;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@Slf4j
public class ByteArrayInputNetStream extends NetInputStream_p340 {
private ByteArrayInputStream bais;
public ByteArrayInputNetStream(byte[] buff) {
bais = new ByteArrayInputStream(buff);
}
@Override
public boolean readBoolean() {
return readByte() != 0;
}
@Override
public byte readByte() {
return (byte) bais.read();
}
@Override
public int readBytes(byte[] buffer, int offset, int length) {
try {
int read = bais.read(buffer, offset, length);
if (read < length) {
throw new IOException("not enough data");
}
return read;
} catch (IOException e) {
log.error("", e);
return -1;
}
}
@Override
public int readUnsignedByte() {
return bais.read() & 0xFF;
}
@Override
public int readUnsignedShort() {
throw new UnsupportedOperationException();
}
@Override
public short readShort() {
throw new UnsupportedOperationException();
}
@Override
public int readInt() {
int ch1 = bais.read();
int ch2 = bais.read();
int ch3 = bais.read();
int ch4 = bais.read();
if ((ch1 | ch2 | ch3 | ch4) < 0) return 0;
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4));
}
@Override
public long readLong() {
throw new UnsupportedOperationException();
}
@Override
public float readFloat() {
return Float.intBitsToFloat(readInt());
}
@Override
public double readDouble() {
throw new UnsupportedOperationException();
}
@Override
public void skipBytes(int count) {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,135 +0,0 @@
package mc.core.network.proto_1_12_2;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ByteArrayInputNetStreamTest {
private Random random;
@BeforeEach
void before() {
random = new Random(System.currentTimeMillis());
}
@Test
void testReadBoolean() {
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeBoolean(true);
ByteArrayInputNetStream byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
assertTrue(byteArrayInputNetStream.readBoolean());
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeBoolean(false);
byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
assertFalse(byteArrayInputNetStream.readBoolean());
}
@Test
void testReadByte() throws IOException {
final byte[] bytes = new byte[1];
random.nextBytes(bytes);
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeByte(bytes[0]);
ByteArrayInputNetStream byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
assertEquals(bytes[0], byteArrayInputNetStream.readByte());
byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
assertEquals(bytes[0], byteArrayInputNetStream.read());
}
@Test
void testReadBytes() throws IOException {
final byte[] expectedBytes = new byte[10];
random.nextBytes(expectedBytes);
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeBytes(expectedBytes);
ByteArrayInputNetStream byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
byte[] actualBytes = new byte[10];
byteArrayInputNetStream.readBytes(actualBytes);
assertArrayEquals(expectedBytes, actualBytes);
byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
actualBytes = new byte[10];
int r = byteArrayInputNetStream.read(actualBytes);
assertArrayEquals(expectedBytes, actualBytes);
assertEquals(expectedBytes.length, r);
byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
actualBytes = new byte[10];
byteArrayInputNetStream.readBytes(actualBytes, 2, 5);
byte[] nibbleExpectedBytes = new byte[10];
System.arraycopy(expectedBytes, 0, nibbleExpectedBytes, 2, 5);
assertArrayEquals(nibbleExpectedBytes, actualBytes);
byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
actualBytes = new byte[10];
r = byteArrayInputNetStream.read(actualBytes, 2, 5);
nibbleExpectedBytes = new byte[10];
System.arraycopy(expectedBytes, 0, nibbleExpectedBytes, 2, 5);
assertArrayEquals(nibbleExpectedBytes, actualBytes);
assertEquals(5, r);
}
@Test
void testReadUnsignedByte() {
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeUnsignedByte(30);
ByteArrayInputNetStream byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
assertEquals(30, byteArrayInputNetStream.readUnsignedByte());
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeUnsignedByte(130);
byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
assertEquals(130, byteArrayInputNetStream.readUnsignedByte());
}
@Test
void testReadInt() {
final int integerDig = random.nextInt();
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeInt(integerDig);
ByteArrayInputNetStream byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
assertEquals(integerDig, byteArrayInputNetStream.readInt());
}
@Test
void readFloat() {
final float floatDig = random.nextFloat();
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeFloat(floatDig);
ByteArrayInputNetStream byteArrayInputNetStream = new ByteArrayInputNetStream(byteArrayOutputNetStream.toByteArray());
assertEquals(floatDig, byteArrayInputNetStream.readFloat());
}
}

View File

@@ -1,259 +0,0 @@
package mc.core.network.proto_1_12_2;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Random;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
class ByteArrayOutputNetStreamTest {
private Random random;
@BeforeEach
void before() {
random = new Random(System.currentTimeMillis());
}
@Test
void testWriteBoolean() {
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeBoolean(true);
assertArrayEquals(new byte[]{0x01}, byteArrayOutputNetStream.toByteArray());
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeBoolean(false);
assertArrayEquals(new byte[]{0x00}, byteArrayOutputNetStream.toByteArray());
}
@Test
void testWriteByte() throws IOException {
final byte[] bytes = new byte[1];
random.nextBytes(bytes);
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeByte(bytes[0]);
assertArrayEquals(bytes, byteArrayOutputNetStream.toByteArray());
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.write(bytes[0]);
assertArrayEquals(bytes, byteArrayOutputNetStream.toByteArray());
}
@Test
void testWriteUnsignedByte() {
final byte[] bytes = new byte[1];
random.nextBytes(bytes);
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeUnsignedByte(bytes[0]);
assertArrayEquals(bytes, byteArrayOutputNetStream.toByteArray());
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeUnsignedByte(0xFF);
assertArrayEquals(new byte[]{(byte) 0xFF}, byteArrayOutputNetStream.toByteArray());
}
@Test
void testWriteBytes() throws IOException {
final byte[] expectedBytes = new byte[10];
random.nextBytes(expectedBytes);
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeBytes(expectedBytes);
assertArrayEquals(expectedBytes, byteArrayOutputNetStream.toByteArray());
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.write(expectedBytes);
assertArrayEquals(expectedBytes, byteArrayOutputNetStream.toByteArray());
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeBytes(expectedBytes, 2, 5);
byte[] nibbleExpectedBytes = new byte[5];
System.arraycopy(expectedBytes, 2, nibbleExpectedBytes, 0, 5);
assertArrayEquals(nibbleExpectedBytes, byteArrayOutputNetStream.toByteArray());
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.write(expectedBytes, 2, 5);
nibbleExpectedBytes = new byte[5];
System.arraycopy(expectedBytes, 2, nibbleExpectedBytes, 0, 5);
assertArrayEquals(nibbleExpectedBytes, byteArrayOutputNetStream.toByteArray());
}
@Test
void testWriteShort() {
int smallInt;
do {
smallInt = random.nextInt();
} while (smallInt > Short.MAX_VALUE || smallInt < Short.MIN_VALUE);
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeShort(smallInt);
assertArrayEquals(new byte[]{ (byte) (smallInt >>> 8),
(byte) smallInt },
byteArrayOutputNetStream.toByteArray());
}
@Test
void testWriteInt() {
final int integerDig = random.nextInt();
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeInt(integerDig);
assertArrayEquals(new byte[]{ (byte) ((integerDig >>> 24) & 0xFF),
(byte) ((integerDig >>> 16) & 0xFF),
(byte) ((integerDig >>> 8) & 0xFF),
(byte) (integerDig & 0xFF) },
byteArrayOutputNetStream.toByteArray());
}
@Test
void testWriteLong() {
final long longDig = random.nextLong();
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeLong(longDig);
assertArrayEquals(new byte[]{ (byte) ((longDig >>> 56) & 0xFF),
(byte) ((longDig >>> 48) & 0xFF),
(byte) ((longDig >>> 40) & 0xFF),
(byte) ((longDig >>> 32) & 0xFF),
(byte) ((longDig >>> 24) & 0xFF),
(byte) ((longDig >>> 16) & 0xFF),
(byte) ((longDig >>> 8) & 0xFF),
(byte) (longDig & 0xFF) },
byteArrayOutputNetStream.toByteArray());
}
@Test
void testWriteFloat() {
final float floatDig = random.nextFloat();
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeFloat(floatDig);
final int floatBits = Float.floatToIntBits(floatDig);
assertArrayEquals(new byte[]{ (byte) ((floatBits >>> 24) & 0xFF),
(byte) ((floatBits >>> 16) & 0xFF),
(byte) ((floatBits >>> 8) & 0xFF),
(byte) (floatBits & 0xFF) },
byteArrayOutputNetStream.toByteArray());
}
@Test
void testWriteDouble() {
final double doubleDig = random.nextDouble();
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeDouble(doubleDig);
final long doubleBits = Double.doubleToLongBits(doubleDig);
assertArrayEquals(new byte[]{ (byte) ((doubleBits >>> 56) & 0xFF),
(byte) ((doubleBits >>> 48) & 0xFF),
(byte) ((doubleBits >>> 40) & 0xFF),
(byte) ((doubleBits >>> 32) & 0xFF),
(byte) ((doubleBits >>> 24) & 0xFF),
(byte) ((doubleBits >>> 16) & 0xFF),
(byte) ((doubleBits >>> 8) & 0xFF),
(byte) (doubleBits & 0xFF) },
byteArrayOutputNetStream.toByteArray());
}
@Test
void testWriteVarInt() {
final int b1Int = 120;
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeVarInt(b1Int);
assertArrayEquals(new byte[]{ 0x78 },
byteArrayOutputNetStream.toByteArray());
final int b2Int = 12000;
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeVarInt(b2Int);
assertArrayEquals(new byte[]{ (byte) 0xE0, 0x5D },
byteArrayOutputNetStream.toByteArray());
final int b3Int = 120000;
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeVarInt(b3Int);
assertArrayEquals(new byte[]{ (byte) 0xC0, (byte) 0xA9, 0x07 },
byteArrayOutputNetStream.toByteArray());
final int b4Int = 120000000;
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeVarInt(b4Int);
assertArrayEquals(new byte[]{ (byte) 0x80, (byte) 0x9C, (byte) 0x9C, (byte) 0x39 },
byteArrayOutputNetStream.toByteArray());
final int b5Int = 1200000000;
byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeVarInt(b5Int);
assertArrayEquals(new byte[]{ (byte) 0x80, (byte) 0x98, (byte) 0x9A, (byte) 0xBC, 0x04 },
byteArrayOutputNetStream.toByteArray());
}
@Test
void testWriteString() {
final String string = "Hello? Есть тут кто?";
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeString(string);
final byte[] strBytes = string.getBytes(StandardCharsets.UTF_8);
final byte[] bytes = new byte[strBytes.length + 1];
bytes[0] = (byte) string.length(); // здесь считается, что размер поместится в один байт
System.arraycopy(strBytes, 0, bytes, 1, strBytes.length);
assertArrayEquals(bytes, byteArrayOutputNetStream.toByteArray());
}
@Test
void testWriteUUID() {
final UUID uuid = UUID.randomUUID();
ByteArrayOutputNetStream byteArrayOutputNetStream = new ByteArrayOutputNetStream();
byteArrayOutputNetStream.writeUUID(uuid);
final long mostSignificantBits = uuid.getMostSignificantBits();
final long leastSignificantBits = uuid.getLeastSignificantBits();
assertArrayEquals(new byte[]{ (byte) ((mostSignificantBits >>> 56) & 0xFF),
(byte) ((mostSignificantBits >>> 48) & 0xFF),
(byte) ((mostSignificantBits >>> 40) & 0xFF),
(byte) ((mostSignificantBits >>> 32) & 0xFF),
(byte) ((mostSignificantBits >>> 24) & 0xFF),
(byte) ((mostSignificantBits >>> 16) & 0xFF),
(byte) ((mostSignificantBits >>> 8) & 0xFF),
(byte) (mostSignificantBits & 0xFF),
(byte) ((leastSignificantBits >>> 56) & 0xFF),
(byte) ((leastSignificantBits >>> 48) & 0xFF),
(byte) ((leastSignificantBits >>> 40) & 0xFF),
(byte) ((leastSignificantBits >>> 32) & 0xFF),
(byte) ((leastSignificantBits >>> 24) & 0xFF),
(byte) ((leastSignificantBits >>> 16) & 0xFF),
(byte) ((leastSignificantBits >>> 8) & 0xFF),
(byte) (leastSignificantBits & 0xFF) },
byteArrayOutputNetStream.toByteArray());
}
}

View File

@@ -1,312 +0,0 @@
package mc.core.network.proto_1_12_2.packets;
import com.flowpowered.nbt.*;
import javafx.util.Pair;
import mc.core.network.proto_1_12_2.ByteArrayOutputNetStream;
import mc.core.network.proto_1_12_2.packets.DumbChunkData.DumbChunkSection;
import mc.core.world.Biome;
import mc.core.world.block.*;
import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkSection;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.*;
class ChunkDataPacketTest {
private static List<Pair<DumbChunkData, DumbChunkData>> listOfParams;
private static DumbChunkData createExpectedData(String xz) throws IOException {
InputStream inputStream = ChunkDataPacketTest.class.getResourceAsStream(String.format("ChunkDataPacket%s.bin", xz));
assertNotNull(inputStream);
return DumbChunkData.ReadFromNetInputStream(IOUtils.toByteArray(inputStream));
}
private static Block createChestBlock00(BlockType type, int x, int y, int z, int height) {
final BlockLocation location = new BlockLocation(x, y, z);
final CompoundMap compoundMap = new CompoundMap();
compoundMap.put(new IntTag("x", x));
compoundMap.put(new IntTag("y", (height << 4) + y));
compoundMap.put(new IntTag("z", z));
compoundMap.put(new StringTag("id", type.getNamedId()));
final CompoundTag compoundTag = new CompoundTag("", compoundMap);
return new AbstractBlock(type) {
@Override
public BlockLocation getLocation() {
return location;
}
@Override
public CompoundTag getNBTData() {
return compoundTag;
}
};
}
private static ChunkSection createChunkSection00(int height) {
final ChunkSection chunkSection = mock(ChunkSection.class);
when(chunkSection.getSkyLight(anyInt(), anyInt(), anyInt())).thenReturn(0);
when(chunkSection.getY()).thenReturn(height);
if (height == 0) {
when(chunkSection.getBlock(anyInt(), anyInt(), anyInt())).thenAnswer(invocation -> {
Object[] args = invocation.getArguments();
final int x = (int) args[0];
final int y = (int) args[1];
final int z = (int) args[2];
BlockFactory blockFactory = new BlockFactory();
if (y == 0) {
// @formatter:off
if (x == 0 && z == 0) return blockFactory.create(BlockType.STONE, x, y, z);
else if (x == 15 && z == 0) return blockFactory.create(BlockType.GRANITE, x, y, z);
else if (x == 0 && z == 15) return blockFactory.create(BlockType.POLISHED_GRANITE, x, y, z);
else if (x == 15 && z == 15) return blockFactory.create(BlockType.DIORITE, x, y, z);
else return blockFactory.create(BlockType.BEDROCK, x, y, z);
// @formatter:on
} else {
return blockFactory.create(BlockType.STONE, x, y, z);
}
});
} else {
when(chunkSection.getBlock(anyInt(), anyInt(), anyInt())).thenAnswer(invocation -> {
Object[] args = invocation.getArguments();
final int x = (int) args[0];
final int y = (int) args[1];
final int z = (int) args[2];
BlockFactory blockFactory = new BlockFactory();
// @formatter:off
if (y == 0) return blockFactory.create(BlockType.DIRT, x, y, z);
else if (y == 1) return blockFactory.create(BlockType.GRASS, x, y, z);
else if (y == 2) {
if ((x == 2 || x == 4 || x == 5) && z == 1)
return createChestBlock00(BlockType.CHEST_NORTH, x, y, z, height);
else if ((x == 2 || x == 3 || x == 5) && z == 6)
return createChestBlock00(BlockType.CHEST_SOUTH, x, y, z, height);
else if (x == 1 && (z == 2 || z == 3 || z == 5))
return createChestBlock00(BlockType.CHEST_WEST, x, y, z, height);
else if (x == 6 && (z == 2 || z == 4 || z == 5))
return createChestBlock00(BlockType.CHEST_EAST, x, y, z, height);
else
return blockFactory.create(BlockType.AIR, x, y, z);
}
else return blockFactory.create(BlockType.AIR, x, y, z);
// @formatter:on
});
}
return chunkSection;
}
private static Chunk createMockChunk00() {
final ChunkSection chunkSection0 = createChunkSection00(0);
final ChunkSection chunkSection1 = createChunkSection00(1);
final Chunk chunk = mock(Chunk.class);
when(chunk.getX()).thenReturn(0);
when(chunk.getZ()).thenReturn(0);
when(chunk.getBiome(anyInt(), anyInt())).thenReturn(Biome.PLAINS);
when(chunk.getChunkSection(0)).thenReturn(chunkSection0);
when(chunk.getChunkSection(1)).thenReturn(chunkSection1);
return chunk;
}
private static ChunkSection createChunkSection01() {
final ChunkSection chunkSection = mock(ChunkSection.class);
when(chunkSection.getSkyLight(anyInt(), anyInt(), anyInt())).thenReturn(0);
when(chunkSection.getY()).thenReturn(0);
final List<BlockType> types = Arrays.asList(
BlockType.CLAY,
BlockType.ORE_REDSTONE,
BlockType.ORE_DIAMOND,
BlockType.OBSIDIAN,
BlockType.STONE_MOSS,
BlockType.SANDSTONE,
BlockType.ORE_LAPIS,
BlockType.WOOD_JUNGLE,
BlockType.WOOD_BIRCH,
BlockType.WOOD_SPRUCE,
BlockType.WOOD_OAK,
BlockType.ORE_COAL,
BlockType.ORE_IRON,
BlockType.ORE_GOLD,
BlockType.GRAVEL,
BlockType.SAND
);
when(chunkSection.getBlock(anyInt(), anyInt(), anyInt())).thenAnswer(invocation -> {
Object[] args = invocation.getArguments();
final int x = (int) args[0];
final int y = (int) args[1];
final int z = (int) args[2];
BlockFactory blockFactory = new BlockFactory();
if (y == 0) {
// @formatter:off
if (x == 0 && z == 0) return blockFactory.create(BlockType.STONE, x, y, z);
else if (x == 15 && z == 0) return blockFactory.create(BlockType.GRANITE, x, y, z);
else if (x == 0 && z == 15) return blockFactory.create(BlockType.POLISHED_GRANITE, x, y, z);
else if (x == 15 && z == 15) return blockFactory.create(BlockType.DIORITE, x, y, z);
else return blockFactory.create(BlockType.BEDROCK, x, y, z);
// @formatter:on
} else if (y == 1) {
return blockFactory.create(types.get(x), x, y, z);
} else {
return blockFactory.create(BlockType.AIR, x, y, z);
}
});
return chunkSection;
}
private static Chunk createMockChunk01() {
final ChunkSection chunkSection0 = createChunkSection01();
final Chunk chunk = mock(Chunk.class);
when(chunk.getX()).thenReturn(0);
when(chunk.getZ()).thenReturn(1);
when(chunk.getBiome(anyInt(), anyInt())).thenReturn(Biome.PLAINS);
when(chunk.getChunkSection(0)).thenReturn(chunkSection0);
return chunk;
}
private static void verifyMock(Chunk chunk) {
verify(chunk, atLeast(1)).getX();
verify(chunk, atLeast(1)).getZ();
verify(chunk, times(256)).getBiome(anyInt(), anyInt());
verify(chunk, atLeast(2)).getChunkSection(anyInt());
}
private static DumbChunkData createActualData(Chunk chunk) {
ChunkDataPacket packet = new ChunkDataPacket();
packet.setX(chunk.getX());
packet.setZ(chunk.getZ());
packet.setChunk(chunk);
packet.setInitChunk(true);
ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream();
packet.writeSelf(netStream);
verifyMock(chunk);
return DumbChunkData.ReadFromNetInputStream(netStream.toByteArray());
}
@BeforeAll
static void beforeClassTest() throws IOException {
listOfParams = Arrays.asList(
new Pair<>(createExpectedData("00"), createActualData(createMockChunk00())),
new Pair<>(createExpectedData("01"), createActualData(createMockChunk01()))
);
}
private static Stream<Arguments> streamArguments() {
return listOfParams.stream().map(pair -> Arguments.of(pair.getKey(), pair.getValue()));
}
@DisplayName("testGeneral")
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("streamArguments")
void testGeneral(DumbChunkData expected, DumbChunkData actual) {
assertEquals(expected.getX(), actual.getX(), "X coord not equals");
assertEquals(expected.getZ(), actual.getZ(), "Z coord not equals");
assertEquals(expected.isInitChunk(), actual.isInitChunk(), "Flag init chunk not equals");
assertEquals(expected.getBitMask(), actual.getBitMask(), "BitMask not equals");
assertArrayEquals(expected.getBiomes(), actual.getBiomes(), "Biomes not equals");
}
@DisplayName("testNBT")
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("streamArguments")
void testNBT(DumbChunkData expected, DumbChunkData actual) {
assertEquals(expected.getNumberNBT(), actual.getNumberNBT());
assertEquals(expected.getNbt().size(), actual.getNbt().size());
for (Tag<?> tag : actual.getNbt()) {
assertTrue(expected.getNbt().contains(tag));
}
}
@DisplayName("testData (disabled light test)")
@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("streamArguments")
void testData(DumbChunkData expected, DumbChunkData actual) {
assertEquals(expected.getData().length, actual.getData().length);
for (int numberSection = 0; numberSection < expected.getData().length; numberSection++) {
final DumbChunkSection expectedSection = expected.getData()[numberSection];
final DumbChunkSection actualSection = actual.getData()[numberSection];
// Palette
testPalette(expectedSection, actualSection, numberSection);
// Data
testDataBlock(expectedSection, actualSection, numberSection);
// Block and Sky light
// DISABLE //
//testLight(expectedSection, actualSection, numberSection);
}
}
private void testPalette(DumbChunkSection expected, DumbChunkSection actual, int numberSection) {
assertEquals(expected.getBitsPerBlock(), actual.getBitsPerBlock());
if (expected.getPalette().size() > actual.getPalette().size()) {
for (int j = 0; j < actual.getPalette().size(); j++) {
assertTrue(expected.getPalette().contains(
actual.getPalette().get(j)
), String.format("[%d] Palette not contains %s", numberSection, actual.getPalette().get(j)));
}
} else {
for (int j = 0; j < expected.getPalette().size(); j++) {
assertTrue(actual.getPalette().contains(
expected.getPalette().get(j)
), String.format("[%d] Palette not contains %s", numberSection, actual.getPalette().get(j)));
}
}
}
private void testDataBlock(DumbChunkSection expected, DumbChunkSection actual, int numberSection) {
assertEquals(expected.getData().size(), actual.getData().size());
for (int j = 0; j < expected.getData().size(); j++) {
assertEquals(
expected.getData().get(j),
actual.getData().get(j),
String.format("[%d] Data (blocks)", numberSection)
);
}
}
private void testLight(DumbChunkSection expected, DumbChunkSection actual, int numberSection) {
// Block light
assertArrayEquals(expected.getBlockLight(), actual.getBlockLight(),
String.format("[%d] Block light", numberSection));
// Sky light
assertArrayEquals(expected.getSkyLight(), actual.getSkyLight(),
String.format("[%d] Sky light", numberSection));
}
}

View File

@@ -1,133 +0,0 @@
package mc.core.network.proto_1_12_2.packets;
import com.flowpowered.nbt.Tag;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import mc.core.network.proto_1_12_2.ByteArrayInputNetStream;
import mc.core.world.block.BlockType;
import java.nio.ByteBuffer;
import java.nio.LongBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
class DumbChunkData {
private int x;
private int z;
private boolean initChunk;
private int bitMask;
private int sizeOfData;
private DumbChunkSection[] data;
private byte[] biomes;
private int numberNBT;
private List<Tag<?>> nbt;
private static BlockType deserializeBlockState(int blockState) {
return BlockType.getByIdMeta(blockState >> 4, blockState & 0x0F);
}
static DumbChunkData ReadFromNetInputStream(byte[] bytes) {
ByteArrayInputNetStream netStream = new ByteArrayInputNetStream(bytes);
DumbChunkData dumbChunkData = new DumbChunkData();
dumbChunkData.x = netStream.readInt();
dumbChunkData.z = netStream.readInt();
dumbChunkData.initChunk = netStream.readBoolean();
dumbChunkData.bitMask = netStream.readVarInt();
int countOfSections = 0;
for (int shift = 0; shift < 8; shift++) {
countOfSections += ((dumbChunkData.bitMask >> shift) & 0x01) > 0 ? 1 : 0;
}
dumbChunkData.sizeOfData = netStream.readVarInt();
dumbChunkData.data = new DumbChunkSection[countOfSections];
for (int c = 0; c < countOfSections; c++) {
DumbChunkSection dumbChunkSection = new DumbChunkSection();
dumbChunkSection.bitsPerBlock = netStream.readUnsignedByte();
int sizePalette = netStream.readVarInt();
dumbChunkSection.palette = new ArrayList<>(sizePalette);
for (int i = 0; i < sizePalette; i++) {
dumbChunkSection.palette.add(deserializeBlockState(netStream.readVarInt()));
}
final byte[] rawData = new byte[netStream.readVarInt() * 8];
netStream.readBytes(rawData);
LongBuffer data = ByteBuffer.wrap(rawData).asLongBuffer();
final int bitMask = (1 << dumbChunkSection.bitsPerBlock) - 1;
dumbChunkSection.data = new ArrayList<>(4096);
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
final int blockNumber = (((y << 4) + z) << 4) + x;
final int startLong = ( blockNumber * dumbChunkSection.bitsPerBlock ) / 64;
final int startOffset = ( blockNumber * dumbChunkSection.bitsPerBlock ) % 64;
final int endLong = ((blockNumber + 1) * dumbChunkSection.bitsPerBlock - 1) / 64;
int idxBlock;
if (startLong == endLong) {
idxBlock = (int)(data.get(startLong) >> startOffset);
} else {
int endOffset = 64 - startOffset;
long mask = (1 << endOffset) - 1;
idxBlock = (int)(((data.get(startLong) >> startOffset) & mask) | data.get(endLong) << endOffset);
}
dumbChunkSection.data.add(dumbChunkSection.palette.get(idxBlock & bitMask));
}
}
}
dumbChunkSection.blockLight = new byte[2048];
netStream.readBytes(dumbChunkSection.blockLight);
dumbChunkSection.skyLight = new byte[2048];
netStream.readBytes(dumbChunkSection.skyLight);
dumbChunkData.data[c] = dumbChunkSection;
}
dumbChunkData.biomes = new byte[256];
netStream.readBytes(dumbChunkData.biomes);
dumbChunkData.numberNBT = netStream.readVarInt();
if (dumbChunkData.numberNBT > 0) {
dumbChunkData.nbt = new ArrayList<>(dumbChunkData.numberNBT);
for (int i = 0; i < dumbChunkData.numberNBT; i++) {
dumbChunkData.nbt.add(netStream.readNBT());
}
} else {
dumbChunkData.nbt = Collections.emptyList();
}
return dumbChunkData;
}
@Override
public String toString() {
return "DumbChunkData{" +
"x=" + x +
", z=" + z +
'}';
}
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
static class DumbChunkSection {
private int bitsPerBlock;
private List<BlockType> palette;
private List<BlockType> data;
private byte[] blockLight;
private byte[] skyLight;
}
}

View File

@@ -1,41 +0,0 @@
package mc.core.network.proto_1_12_2.packets;
import mc.core.network.proto_1_12_2.ByteArrayInputNetStream;
import mc.core.network.proto_1_12_2.ByteArrayOutputNetStream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.assertEquals;
class PlayerAbilitiesPacketTest {
private Random rnd = new Random();
private PlayerAbilitiesPacket packet;
@BeforeEach
void before() {
packet = new PlayerAbilitiesPacket();
packet.setGodMode(rnd.nextBoolean());
packet.setFlying(rnd.nextBoolean());
packet.setCanFly(rnd.nextBoolean());
packet.setInstantDestroyBlocks(rnd.nextBoolean());
packet.setFlyingSpeed(rnd.nextFloat());
}
@Test
void writePacket() {
ByteArrayOutputNetStream netOutputStream = new ByteArrayOutputNetStream();
packet.writeSelf(netOutputStream);
ByteArrayInputNetStream netInputStream = new ByteArrayInputNetStream(netOutputStream.toByteArray());
PlayerAbilitiesPacket outPkt = new PlayerAbilitiesPacket();
outPkt.readSelf(netInputStream);
assertEquals(packet.isGodMode(), outPkt.isGodMode(), "god mode");
assertEquals(packet.isFlying(), outPkt.isFlying(), "flying");
assertEquals(packet.isCanFly(), outPkt.isCanFly(), "can fly");
assertEquals(packet.isInstantDestroyBlocks(), outPkt.isInstantDestroyBlocks(), "instant destroy block");
assertEquals(packet.getFlyingSpeed(), outPkt.getFlyingSpeed(), 0.00001f, "flying speed");
}
}

View File

@@ -1,32 +0,0 @@
package mc.core.network.proto_1_12_2.serializers;
import mc.core.world.block.BlockLocation;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.concurrent.ThreadLocalRandom;
import static org.junit.jupiter.api.Assertions.assertEquals;
class BlockLocationSerializerTest {
private static final ThreadLocalRandom rnd = ThreadLocalRandom.current();
private static final int minI = 0, maxI = 10;
private int x, y, z;
@BeforeEach
void before() {
x = rnd.nextInt(minI, maxI);
y = rnd.nextInt(minI, maxI);
z = rnd.nextInt(minI, maxI);
}
@Test
void serialize() {
BlockLocation location = new BlockLocation(x, y, z);
final long serializedCoords = BlockLocationSerializer.toLong(location);
BlockLocation deserLoc = BlockLocationSerializer.fromLong(serializedCoords);
assertEquals(location, deserLoc);
}
}

View File

@@ -1,13 +0,0 @@
version '1.0-SNAPSHOT'
ext {
netty_version = '4.1.22.Final'
}
dependencies {
/* Protocol 1.12.2 */
compile_excludeCopy project(':proto_1.12.2')
/* Netty */
compile (group: 'io.netty', name: 'netty-all', version: netty_version)
}

View File

@@ -1,48 +0,0 @@
package mc.core.network.proto_1_12_2.netty;
import lombok.Setter;
import mc.core.network.proto_1_12_2.packets.KeepAlivePacket;
import mc.core.player.PlayerManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class KeepAliveThread extends Thread {
private final Object lock = new Object();
@Autowired
private PlayerManager playerManager;
@Setter
private int interval = 10;
public void notifyLock() {
synchronized (lock) {
lock.notify();
}
}
@Override
public void run() {
final KeepAlivePacket keepAlivePacket = new KeepAlivePacket();
while (!Thread.currentThread().isInterrupted()) {
while(playerManager.getCountPlayers() == 0) {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
return;
}
}
}
keepAlivePacket.setPayload(System.currentTimeMillis());
playerManager.getBroadcastChannel().writeAndFlush(keepAlivePacket);
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
return;
}
}
}
}

View File

@@ -1,92 +0,0 @@
package mc.core.network.proto_1_12_2.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.AttributeKey;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import mc.core.eventbus.EventBus;
import mc.core.network.Server;
import mc.core.network.StartServerException;
import mc.core.network.proto_1_12_2.State;
import mc.core.network.proto_1_12_2.packets.StatusResponsePacket;
import mc.core.player.Player;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.util.Map;
@Slf4j
@Component
public class NettyServer implements Server {
public static final AttributeKey<State> ATTR_STATE = AttributeKey.newInstance("ATTR_STATE");
public static final AttributeKey<Player> ATTR_PLAYER = AttributeKey.newInstance("ATTR_PLAYER");
@Autowired
private ApplicationContext context;
@Autowired
private KeepAliveThread keepAliveThread;
@Setter
private String host;
@Setter
private int port;
@Setter
private int workerGroupCount = 0;
private EventLoopGroup bossGroup, workerGroup;
private ChannelInitializer buildChannelInitializer() {
return new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) {
Map<String, ChannelHandler> beans = context.getBeansOfType(ChannelHandler.class);
beans.forEach(socketChannel.pipeline()::addLast);
}
};
}
private ServerBootstrap buildServerBootstrap() {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(buildChannelInitializer());
return bootstrap;
}
@Override
public void start() throws StartServerException {
log.info("Use protocol {}", StatusResponsePacket.NAME);
EventBus.getInstance().registerSubscribes(new PlayerEventListener());
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup(workerGroupCount);
ServerBootstrap serverBootstrap = buildServerBootstrap();
log.info("Start server: {}:{}", host, port);
try {
ChannelFuture channelFuture = serverBootstrap.bind(host, port).sync();
keepAliveThread.start();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
throw new StartServerException(e);
}
}
@Override
public void stop() {
log.info("Server shutdown");
keepAliveThread.interrupt();
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}

View File

@@ -1,66 +0,0 @@
package mc.core.network.proto_1_12_2.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.CSPacket;
import mc.core.network.NetInputStream;
import mc.core.network.proto_1_12_2.State;
import mc.core.network.proto_1_12_2.netty.wrappers.WrapperNetInputStream;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_STATE;
@Slf4j
public class PacketDecoder extends ReplayingDecoder<CSPacket> {
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.channel().attr(ATTR_STATE).set(State.HANDSHAKE);
ctx.fireChannelActive();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
ctx.channel().attr(ATTR_STATE).set(null);
ctx.fireChannelInactive();
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
State state = ctx.channel().attr(ATTR_STATE).get();
NetInputStream netStream = new WrapperNetInputStream(in);
int packetSize = netStream.readVarInt();
log.debug("Packet size: {}", packetSize);
int leftDataPacket = packetSize;
final AtomicInteger countReadBytes = new AtomicInteger(0);
int packetId = netStream.readVarInt(countReadBytes);
String hexPacketId = Integer.toHexString(packetId).toUpperCase();
if (hexPacketId.length() == 1) hexPacketId = "0" + hexPacketId;
log.debug("Packet id: 0x{}", hexPacketId);
leftDataPacket = leftDataPacket - countReadBytes.get();
Class<? extends CSPacket> packetClass = state.getClientSidePacket(packetId);
if (packetClass == null) {
log.warn("Unknown packet: {}:0x{}", state.name(), hexPacketId);
in.skipBytes(leftDataPacket);
} else {
netStream.setDataSize(leftDataPacket);
CSPacket packet = packetClass.newInstance();
try {
packet.readSelf(netStream);
log.debug("Known packet: {}:{}", state.name(), packet.toString());
out.add(packet);
} catch (Exception e) {
log.warn("Known packet: {}:{}. But throw exception. See debug log.", state.name(), packet.getClass().getSimpleName());
log.debug("Read packet", e);
in.skipBytes(leftDataPacket);
}
}
}
}

View File

@@ -1,41 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.NetOutputStream;
import mc.core.network.SCPacket;
import mc.core.network.proto_1_12_2.State;
import mc.core.network.proto_1_12_2.netty.wrappers.WrapperNetOutputStream;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_STATE;
import static org.slf4j.helpers.MessageFormatter.format;
@Slf4j
public class PacketEncoder extends MessageToByteEncoder<SCPacket> {
@Override
protected void encode(ChannelHandlerContext ctx, SCPacket packet, ByteBuf out) {
State state = ctx.channel().attr(ATTR_STATE).get();
Integer id = state.getServerSidePacket(packet.getClass());
if (id == null) {
log.error("Not defined ID packet: {}:{}", state.name(), packet.getClass());
return;
}
log.debug("Send {}:{}", state, packet);
try {
NetOutputStream netStream = new WrapperNetOutputStream(out);
netStream.writeVarInt(id);
packet.writeSelf(netStream);
} catch (Throwable t) {
log.error(format("Error encoding packet {}:{}", state, packet).getMessage(), t);
}
}
}

View File

@@ -1,76 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-10
*/
package mc.core.network.proto_1_12_2.netty;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import mc.core.chat.ChatProcessor;
import mc.core.network.CSPacket;
import mc.core.network.proto_1_12_2.State;
import mc.core.network.proto_1_12_2.netty.handlers.*;
import mc.core.player.Player;
import mc.core.player.PlayerManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import javax.annotation.PostConstruct;
import java.util.Collection;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_PLAYER;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_STATE;
@Slf4j
@ComponentScan("mc.core.network.proto_1_12_2.netty.handlers")
public class PacketHandler extends SimpleChannelInboundHandler<CSPacket> {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private PlayerManager playerManager;
@Autowired
private ChatProcessor chatProcessor;
private ImmutableMap<State, ImmutableList<StateHandler>> handlersMap = ImmutableMap.of();
@PostConstruct
public void init() {
ImmutableMap.Builder<State, ImmutableList<StateHandler>> builder = ImmutableMap.builder();
Collection<HandshakeStateHandler> beans1 = applicationContext.getBeansOfType(HandshakeStateHandler.class).values();
builder.put(State.HANDSHAKE, ImmutableList.copyOf(beans1));
Collection<StatusStateHandler> beans2 = applicationContext.getBeansOfType(StatusStateHandler.class).values();
builder.put(State.STATUS, ImmutableList.copyOf(beans2));
Collection<LoginStateHandler> beans3 = applicationContext.getBeansOfType(LoginStateHandler.class).values();
builder.put(State.LOGIN, ImmutableList.copyOf(beans3));
Collection<PlayStateHandler> beans4 = applicationContext.getBeansOfType(PlayStateHandler.class).values();
builder.put(State.PLAY, ImmutableList.copyOf(beans4));
this.handlersMap = builder.build();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
Player player = ctx.channel().attr(ATTR_PLAYER).get();
if (player != null) {
playerManager.leftServer(player);
player.setChannel(null);
}
ctx.channel().attr(ATTR_PLAYER).set(null);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, CSPacket packet) throws Exception {
ImmutableList<StateHandler> stateHandlers = this.handlersMap.get(ctx.channel().attr(ATTR_STATE).get());
for (StateHandler handler : stateHandlers) {
handler.handle(ctx.channel(), packet);
}
}
}

View File

@@ -1,38 +0,0 @@
package mc.core.network.proto_1_12_2.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.proto_1_12_2.netty.wrappers.WrapperNetOutputStream;
/**
* Подсчет размера отправляемого пакета
*/
@Slf4j
public class PacketPostEncoder extends MessageToByteEncoder<ByteBuf> {
private static int getVarIntSize(int input) {
for (int i = 1; i < 5; ++i) {
if ((input & -1 << i * 7) == 0) {
return i;
}
}
return 5;
}
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) {
final int pktSize = msg.readableBytes();
final int sizeOfPktSize = getVarIntSize(pktSize);
if (sizeOfPktSize > 3) {
log.warn("unable to fit {} into {}", pktSize, 3);
}
out.ensureWritable(sizeOfPktSize + pktSize);
(new WrapperNetOutputStream(out)).writeVarInt(pktSize);
out.writeBytes(msg);
}
}

View File

@@ -1,70 +0,0 @@
package mc.core.network.proto_1_12_2.netty;
import lombok.extern.slf4j.Slf4j;
import mc.core.eventbus.Subscriber;
import mc.core.eventbus.events.SC_ChunkLoadEvent;
import mc.core.eventbus.events.SC_ChunkUnloadEvent;
import mc.core.eventbus.events.SC_PlayerMoveEvent;
import mc.core.network.NetChannel;
import mc.core.network.proto_1_12_2.TeleportManager;
import mc.core.network.proto_1_12_2.packets.ChunkDataPacket;
import mc.core.network.proto_1_12_2.packets.PlayerPositionAndLookPacket;
import mc.core.network.proto_1_12_2.packets.UnloadChunkPacket;
import mc.core.utils.CompactedCoords;
import mc.core.world.chunk.Chunk;
@Slf4j
public class PlayerEventListener {
@Subscriber
public void playerMoveEventHandler(SC_PlayerMoveEvent event) {
log.debug("(SC) playerMoveEventHandler()");
PlayerPositionAndLookPacket packet = new PlayerPositionAndLookPacket();
packet.setLocation(event.getNewLocation());
int tpId = TeleportManager.getInstance().append(event.getPlayer(), event.getNewLocation());
packet.setTeleportId(tpId);
event.getPlayer().getChannel().writeAndFlush(packet);
}
@Subscriber
public void playerChunkLoadHandler(SC_ChunkLoadEvent event) {
if (event.getNeedLoadChunks().size() == 0) return;
final NetChannel channel = event.getPlayer().getChannel();
for(Integer compressXZ : event.getNeedLoadChunks()) {
int[] xz = CompactedCoords.uncompressXZ(compressXZ);
Chunk chunk = event.getPlayer().getWorld().getChunk(xz[0], xz[1]);
if (chunk == null) continue;
ChunkDataPacket packet = new ChunkDataPacket();
packet.setX(xz[0]);
packet.setZ(xz[1]);
packet.setInitChunk(true);
packet.setChunk(chunk);
channel.write(packet);
}
channel.flush();
}
@Subscriber
public void playerChunkUnloadHandler(SC_ChunkUnloadEvent event) {
if (event.getNeedUnloadChunks().size() == 0) return;
final NetChannel channel = event.getPlayer().getChannel();
for(Integer compressXZ : event.getNeedUnloadChunks()) {
int[] xz = CompactedCoords.uncompressXZ(compressXZ);
UnloadChunkPacket packet = new UnloadChunkPacket();
packet.setX(xz[0]);
packet.setZ(xz[1]);
channel.write(packet);
}
channel.flush();
}
}

View File

@@ -1,54 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-23
*/
package mc.core.network.proto_1_12_2.netty.handlers;
import com.google.common.collect.ImmutableMap;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.CSPacket;
import javax.annotation.PostConstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.function.Function;
import java.util.stream.Stream;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_STATE;
@Slf4j
public abstract class AbstractStateHandler implements StateHandler {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
protected @interface Handler {
}
private ImmutableMap<Class<? /*extends CSPacket*/>, Method> methods = ImmutableMap.of();
@PostConstruct
protected void init() {
this.methods = Stream.of(this.getClass().getDeclaredMethods())
.filter(method -> method.isAnnotationPresent(Handler.class)
&& method.getParameterCount() == 2
&& method.getParameterTypes()[0].isAssignableFrom(Channel.class)
&& CSPacket.class.isAssignableFrom(method.getParameterTypes()[1]))
.collect(ImmutableMap.toImmutableMap(
method -> method.getParameterTypes()[1],
Function.identity()));
}
@Override
public void handle(Channel channel, CSPacket packet) throws Exception {
if (this.methods.containsKey(packet.getClass())) {
this.methods.get(packet.getClass()).invoke(this, channel, packet);
} else {
log.trace("No def listener of {}:{}",
channel.attr(ATTR_STATE).get(),
packet.getClass().getSimpleName());
}
}
}

View File

@@ -1,25 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-23
*/
package mc.core.network.proto_1_12_2.netty.handlers;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import mc.core.network.proto_1_12_2.State;
import mc.core.network.proto_1_12_2.packets.HandshakePacket;
import org.springframework.stereotype.Component;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_STATE;
@Slf4j
@Component
public class HandshakeHandler extends AbstractStateHandler implements HandshakeStateHandler {
@Handler
public void onHandshake(Channel channel, HandshakePacket packet) {
if (packet.getNextState().equals(State.UNKNOWN)) return;
//FIXME обрати внимание: хацкер может намеренно передать state:03 и тогда может начатся пиздец
log.debug("New state: {}", packet.getNextState());
channel.attr(ATTR_STATE).set(packet.getNextState());
}
}

View File

@@ -1,11 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-23
*/
package mc.core.network.proto_1_12_2.netty.handlers;
/**
* Marker interface
*/
public interface HandshakeStateHandler extends StateHandler {
}

View File

@@ -1,127 +0,0 @@
package mc.core.network.proto_1_12_2.netty.handlers;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import mc.core.eventbus.EventBus;
import mc.core.eventbus.events.CS_PlayerMoveEvent;
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.KeepAliveThread;
import mc.core.network.proto_1_12_2.netty.wrappers.WrapperNetChannel;
import mc.core.network.proto_1_12_2.packets.*;
import mc.core.player.Player;
import mc.core.player.PlayerManager;
import mc.core.player.PlayerMode;
import mc.core.text.Text;
import mc.core.text.TextColor;
import mc.core.text.TextStyle;
import mc.core.world.World;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_PLAYER;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_STATE;
@Component
public class LoginHandler extends AbstractStateHandler implements LoginStateHandler {
@Autowired
private PlayerManager playerManager;
@Autowired
private KeepAliveThread keepAliveThread;
@Autowired
private World world;
@Handler
public void onLoginStart(Channel channel, LoginStartPacket packet) {
Player player = playerManager.getPlayer(packet.getPlayerName());
if (player != null) {
channel.writeAndFlush(new DisconnectPacket(
Text.builder("Player \"")
.append(Text.of(packet.getPlayerName(), TextColor.YELLOW))
.append(Text.of("\" is online", TextColor.WHITE))
.build()))
.addListener(ChannelFutureListener.CLOSE);
} else {
player = playerManager.getOfflinePlayer(packet.getPlayerName());
if (player == null) {
player = playerManager.createPlayer(
packet.getPlayerName(),
world.getSpawn(),
world
);
if (player == null) {
channel.writeAndFlush(new DisconnectPacket(
Text.of("Internal server error: can't create new player"))
).addListener(ChannelFutureListener.CLOSE);
return;
}
}
channel.writeAndFlush(new LoginSuccessPacket(
player.getUuid(),
packet.getPlayerName()));
channel.attr(ATTR_PLAYER).set(player);
channel.attr(ATTR_STATE).set(State.PLAY);
// Join Game
JoinGamePacket pkt1 = new JoinGamePacket();
pkt1.setEntityId(player.getId()); //TODO отделить системный ID от EntityID
pkt1.setMode(PlayerMode.CREATIVE); //TODO перенести в Config
pkt1.setDimension(0/*Overworld*/); //TODO перенести в World
pkt1.setDifficulty(0/*Peaceful*/); //TODO перенести в Config
pkt1.setLevelType(world.getType().getName());
channel.write(pkt1);
// Spawn Position
SpawnPositionPacket pkt2 = new SpawnPositionPacket();
pkt2.setLocation(world.getSpawn());
channel.write(pkt2);
// Player Abilities
PlayerAbilitiesPacket pkt3 = new PlayerAbilitiesPacket(); //TODO перенести в Player
pkt3.setCanFly(true);
pkt3.setFlying(true);
pkt3.setGodMode(true);
pkt3.setInstantDestroyBlocks(true);
channel.write(pkt3);
channel.flush();
// Player Position And Look
PlayerPositionAndLookPacket pkt4 = new PlayerPositionAndLookPacket();
pkt4.setLocation(player.getLocation());
pkt4.setTeleportId(TeleportManager.getInstance().append(player, player.getLocation()));
channel.writeAndFlush(pkt4);
player.setChannel(new WrapperNetChannel(channel));
// Send <Tab> items
//TODO обновление должно приходить всем игрокам на сервере
PlayerListItemPacket pkt5 = new PlayerListItemPacket();
pkt5.setAction(PlayerListItemPacket.Action.ADD_PLAYER);
PlayerListItemPacket.PlayerData playerData = new PlayerListItemPacket.PlayerData();
playerData.setUuid(player.getUuid());
playerData.setName(player.getName());
playerData.setGameMode(PlayerMode.CREATIVE);
playerData.setPing(0);
playerData.setHasDisplayName(true);
playerData.setDisplayName(Text.builder()
.append(Text.of(TextColor.RED, TextStyle.BOLD, player.getName().substring(0,1)))
.append(Text.of(TextColor.WHITE, player.getName().substring(1)))
.build()
);
pkt5.getListPlayers().add(playerData);
channel.writeAndFlush(pkt5);
playerManager.joinServer(player);
keepAliveThread.notifyLock();
CS_PlayerMoveEvent event = new CS_PlayerMoveEvent(player, player.getLocation());
event.setNewLocation(player.getLocation());
event.setRecalcChunk(true);
EventBus.getInstance().post(event);
}
}
}

View File

@@ -1,11 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-23
*/
package mc.core.network.proto_1_12_2.netty.handlers;
/**
* Marker interface
*/
public interface LoginStateHandler extends StateHandler {
}

View File

@@ -1,90 +0,0 @@
package mc.core.network.proto_1_12_2.netty.handlers;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import mc.core.EntityLocation;
import mc.core.chat.ChatProcessor;
import mc.core.eventbus.EventBus;
import mc.core.eventbus.events.CS_PlayerMoveEvent;
import mc.core.network.proto_1_12_2.TeleportManager;
import mc.core.network.proto_1_12_2.packets.*;
import mc.core.player.Player;
import mc.core.player.PlayerSettings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import static mc.core.network.proto_1_12_2.netty.NettyServer.ATTR_PLAYER;
@Slf4j
@Component
public class PlayHandler extends AbstractStateHandler implements PlayStateHandler {
@Autowired
private ChatProcessor chatProcessor;
private TeleportManager teleport = TeleportManager.getInstance();
@Handler
public void onClientSettings(Channel channel, ClientSettingsPacket packet) {
Player player = channel.attr(ATTR_PLAYER).get();
player.getSettings().setLocate(packet.getLocale());
player.getSettings().setViewDistance(packet.getViewDistance());
player.getSettings().setChatMode(PlayerSettings.ChatMode.getById(packet.getChatMode()));
player.getSettings().setChatColors(packet.isChatColors());
player.getSettings().setCapeEnabled(packet.isCapeEnabled());
player.getSettings().setJacketEnabled(packet.isJacketEnabled());
player.getSettings().setLeftSleeveEnabled(packet.isLeftSleeveEnabled());
player.getSettings().setRightSleeveEnabled(packet.isRightSleeveEnabled());
player.getSettings().setLeftPantsLegEnabled(packet.isLeftPantsLegEnabled());
player.getSettings().setRightPantsLegEnabled(packet.isRightPantsLegEnabled());
player.getSettings().setHatEnabled(packet.isHatEnabled());
player.getSettings().setMainHand(PlayerSettings.Hand.getById(packet.getMainHand()));
}
@Handler
public void onTeleportConfirm(Channel channel, TeleportConfirmPacket packet) {
this.teleport.apply(packet.getTeleportId());
}
@Handler
public void onPositionAndLook(Channel channel, PlayerPositionAndLookPacket packet) {
Player player = channel.attr(ATTR_PLAYER).get();
player.getLocation().set(packet.getLocation());
}
@Handler
public void onChat(Channel channel, ChatMessageClientPacket packet) {
chatProcessor.process(
channel.attr(ATTR_PLAYER).get(),
packet.getMessage()
);
}
@Handler
public void onPlayerMove(Channel channel, PlayerPositionPacket packet) {
log.debug("(Netty) onPlayerMove()");
Player player = channel.attr(ATTR_PLAYER).get();
if (player.getLocation().getX() == packet.getX() &&
player.getLocation().getY() == packet.getY() &&
player.getLocation().getZ() == packet.getZ()) {
return;
}
CS_PlayerMoveEvent event = new CS_PlayerMoveEvent(player, player.getLocation().clone());
event.setNewLocation(new EntityLocation(
packet.getX(), packet.getY(), packet.getZ(),
player.getLocation().getYaw(),
player.getLocation().getPitch()
));
EventBus.getInstance().post(event);
}
@Handler
public void onPlayerLook(Channel channel, PlayerLookPacket packet) {
Player player = channel.attr(ATTR_PLAYER).get();
player.getLocation().setYawPitch(packet.getYaw(), packet.getPitch());
}
}

View File

@@ -1,11 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-23
*/
package mc.core.network.proto_1_12_2.netty.handlers;
/**
* Marker interface
*/
public interface PlayStateHandler extends StateHandler {
}

View File

@@ -1,12 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-23
*/
package mc.core.network.proto_1_12_2.netty.handlers;
import io.netty.channel.Channel;
import mc.core.network.CSPacket;
public interface StateHandler {
void handle(Channel channel, CSPacket packet) throws Exception;
}

View File

@@ -1,38 +0,0 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-06-23
*/
package mc.core.network.proto_1_12_2.netty.handlers;
import io.netty.channel.Channel;
import mc.core.Config;
import mc.core.network.proto_1_12_2.packets.PingPacket;
import mc.core.network.proto_1_12_2.packets.StatusRequestPacket;
import mc.core.network.proto_1_12_2.packets.StatusResponsePacket;
import mc.core.player.PlayerManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class StatusHandler extends AbstractStateHandler implements StatusStateHandler {
@Autowired
private Config config;
@Autowired
private PlayerManager playerManager;
@Handler
public void onStatusRequest(Channel channel, StatusRequestPacket packet) {
StatusResponsePacket responsePacket = new StatusResponsePacket();
responsePacket.setMaxOnline(config.getMaxPlayers());
responsePacket.setDescription(config.getDescriptionServer());
responsePacket.setFaviconBase64(config.getFaviconBase64());
responsePacket.setOnline(playerManager.getCountPlayers());
channel.writeAndFlush(responsePacket);
}
@Handler
public void onPing(Channel channel, PingPacket packet) {
channel.writeAndFlush(packet);
}
}

Some files were not shown because too many files have changed in this diff Show More