update chunk data types

This commit is contained in:
bixilon 2020-06-04 00:29:22 +02:00
parent 00239e9f18
commit 166093e641
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
19 changed files with 398 additions and 200 deletions

View File

@ -33,4 +33,8 @@ public class BlockPosition {
BlockPosition pos = (BlockPosition) obj;
return pos.getX() == getX() && pos.getY() == getY() && pos.getZ() == getZ();
}
public ChunkLocation getChunkLocation() {
return new ChunkLocation(getX() / 16, getZ() / 16);
}
}

View File

@ -1,22 +1,33 @@
package de.bixilon.minosoft.game.datatypes;
import de.bixilon.minosoft.game.datatypes.blocks.Block;
import java.util.HashMap;
/**
* Collection of 16x16x16 blocks
* Collection of 16 chunks
*/
public class Chunk {
private final HashMap<ChunkLocation, WorldBlock> blocks;
private final HashMap<Byte, ChunkNibble> chunks;
public Chunk(HashMap<ChunkLocation, WorldBlock> blocks) {
this.blocks = blocks;
public Chunk(HashMap<Byte, ChunkNibble> chunks) {
this.chunks = chunks;
}
public WorldBlock getWorldBlock(ChunkLocation loc) {
return blocks.get(loc);
public Block getBlock(int x, int y, int z) {
if (x > 16 || y > 255 || z > 16 || x < 0 || y < 0 || z < 0) {
throw new IllegalArgumentException(String.format("Invalid chunk location %s %s %s", x, y, z));
}
byte section = (byte) (y / 16);
if (section == 0) {
return Block.COBBLESTONE;
} else if (section == 1) {
return Block.DIRT;
} else {
return Block.AIR;
}
//ToDo
//return chunks.get(section).getBlock(x, y % 16,z);
}
public WorldBlock getWorldBlock(byte x, byte y, byte z) {
return getWorldBlock(new ChunkLocation(x, y, z));
}
}

View File

@ -1,36 +0,0 @@
package de.bixilon.minosoft.game.datatypes;
import de.bixilon.minosoft.game.datatypes.blocks.Blocks;
import java.util.HashMap;
/**
* Collection of 16 chunks
*/
public class ChunkColumn {
private final HashMap<Byte, Chunk> chunks;
final int x;
final int z;
public ChunkColumn(int x, int z) {
chunks = new HashMap<>();
this.x = x;
this.z = z;
}
public WorldBlock getWorldBlock(byte x, short y, byte z) {
if (x > 16 || y > 255 || z > 16 || x < 0 || y < 0 || z < 0) {
throw new IllegalArgumentException(String.format("Invalid chunk location %s %s %s", x, y, z));
}
byte heightNumber = (byte) (y / 16);
BlockPosition pos = new BlockPosition(this.x * 16 + x, y, this.z * 16 + z);
if (heightNumber == 0) {
return Blocks.getBlockInstance(Blocks.DIRT, pos);
} else {
return Blocks.getBlockInstance(Blocks.AIR, pos);
}
//ToDo
//return chunks.get(heightNumber).getWorldBlock(x, (byte) (y - (heightNumber * 16)), z);
}
}

View File

@ -1,31 +0,0 @@
package de.bixilon.minosoft.game.datatypes;
/**
* Chunk X and Z location (block position / 16, rounded down)
*/
public class ChunkColumnLocation {
int x;
int z;
public ChunkColumnLocation(int x, int z) {
this.x = x;
this.z = z;
}
public int getX() {
return x;
}
public int getZ() {
return z;
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj)) {
return true;
}
ChunkColumnLocation that = (ChunkColumnLocation) obj;
return getX() == that.getX() && getZ() == that.getZ();
}
}

View File

@ -1,28 +1,22 @@
package de.bixilon.minosoft.game.datatypes;
/**
* Chunk X, Y and Z location (max 16x16x16)
* Chunk X and Z location (block position / 16, rounded down)
*/
public class ChunkLocation {
byte x;
byte y;
byte z;
int x;
int z;
public ChunkLocation(byte x, byte y, byte z) {
public ChunkLocation(int x, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public byte getX() {
public int getX() {
return x;
}
public byte getY() {
return y;
}
public byte getZ() {
public int getZ() {
return z;
}
@ -32,6 +26,6 @@ public class ChunkLocation {
return true;
}
ChunkLocation that = (ChunkLocation) obj;
return getX() == that.getX() && getY() == that.getY() && getZ() == that.getZ();
return getX() == that.getX() && getZ() == that.getZ();
}
}

View File

@ -0,0 +1,24 @@
package de.bixilon.minosoft.game.datatypes;
import de.bixilon.minosoft.game.datatypes.blocks.Block;
import java.util.HashMap;
/**
* Collection of 16x16x16 blocks
*/
public class ChunkNibble {
private final HashMap<ChunkNibbleLocation, Block> blocks;
public ChunkNibble(HashMap<ChunkNibbleLocation, Block> blocks) {
this.blocks = blocks;
}
public Block getBlock(ChunkNibbleLocation loc) {
return blocks.get(loc);
}
public Block getBlock(int x, int y, int z) {
return getBlock(new ChunkNibbleLocation(x, y, z));
}
}

View File

@ -0,0 +1,37 @@
package de.bixilon.minosoft.game.datatypes;
/**
* Chunk X, Y and Z location (max 16x16x16)
*/
public class ChunkNibbleLocation {
int x;
int y;
int z;
public ChunkNibbleLocation(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getZ() {
return z;
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj)) {
return true;
}
ChunkNibbleLocation that = (ChunkNibbleLocation) obj;
return getX() == that.getX() && getY() == that.getY() && getZ() == that.getZ();
}
}

View File

@ -0,0 +1,35 @@
package de.bixilon.minosoft.game.datatypes;
public class Identifier {
final String legacy;
String mod = "minecraft"; // by default minecraft
String water;
public Identifier(String mod, String legacy, String water) { // water for water update name (post 1.13.x)
this.mod = mod;
this.legacy = legacy;
this.water = water;
}
public Identifier(String legacy, String water) {
this.legacy = legacy;
this.water = water;
}
public Identifier(String name) {
// legacy and water are the same
this.legacy = name;
}
public String getMod() {
return mod;
}
public String getLegacy() {
return legacy;
}
public String getWaterUpdateName() {
return ((water == null) ? legacy : water);
}
}

View File

@ -1,12 +1,14 @@
package de.bixilon.minosoft.game.datatypes;
import de.bixilon.minosoft.game.datatypes.blocks.Block;
import java.util.HashMap;
/**
* Collection of ChunkColumns
*/
public class World {
public final HashMap<ChunkColumnLocation, ChunkColumn> chunks;
public final HashMap<ChunkLocation, Chunk> chunks;
final String name;
public World(String name) {
@ -18,9 +20,16 @@ public class World {
return name;
}
public ChunkColumn getChunkColumn(ChunkColumnLocation loc) {
return new ChunkColumn(loc.getX(), loc.getZ());
public Chunk getChunk(ChunkLocation loc) {
return chunks.get(loc);
}
public Block getBlock(BlockPosition pos) {
//ToDo
//return chunks.get(loc);
ChunkLocation loc = pos.getChunkLocation();
if (getChunk(loc) != null) {
return getChunk(loc).getBlock(pos.getX() % 16, pos.getX(), pos.getZ() % 16);
}
return Block.AIR;
}
}

View File

@ -1,8 +0,0 @@
package de.bixilon.minosoft.game.datatypes;
/**
* Basically raw chunk stuff. This is not a block like dirt (it can be). Can be dirt, a flower, fire, torch, sapling, fluid (water/lava), nether wart, banner, ...
*/
public interface WorldBlock {
BlockPosition getBlockPosition();
}

View File

@ -1,26 +0,0 @@
package de.bixilon.minosoft.game.datatypes.blocks;
import de.bixilon.minosoft.game.datatypes.BlockPosition;
public class Air implements Block {
final BlockPosition position;
public Air(BlockPosition position) {
this.position = position;
}
@Override
public BlockPosition getBlockPosition() {
return position;
}
@Override
public int getLegacyId() {
return 0;
}
@Override
public String getLegacyIdentifier() {
return "minecraft:air";
}
}

View File

@ -1,11 +1,52 @@
package de.bixilon.minosoft.game.datatypes.blocks;
import de.bixilon.minosoft.game.datatypes.WorldBlock;
import de.bixilon.minosoft.game.datatypes.Identifier;
public interface Block extends WorldBlock {
public enum Block {
AIR(new Identifier("air"), 0),
DIRT(new Identifier("stone"), 1),
GRASS(new Identifier("grass"), 2),
COBBLESTONE(new Identifier("stone"), 4),
WHITE_WOOL(new Identifier("wool"), 35, 0),
RED_WOOL(new Identifier("wool", "red_wool"), 35, 14),
DROPPER(new Identifier("dropper", "dropper"), 158, BlockRotation.RotationType.NORMAL);
String getLegacyIdentifier();
Identifier identifier;
int legacyId;
int legacyData;
BlockRotation.RotationType rotationType;
int getLegacyId();
Block(Identifier identifier, int legacyId, BlockRotation.RotationType rotationType) {
this.identifier = identifier;
this.legacyId = legacyId;
this.rotationType = rotationType;
}
}
Block(Identifier identifier, int legacyId, int legacyData) {
this.identifier = identifier;
this.legacyId = legacyId;
this.legacyData = legacyData;
}
Block(Identifier identifier, int legacyId) {
this.identifier = identifier;
this.legacyId = legacyId;
}
public Identifier getIdentifier() {
return identifier;
}
public int getLegacyId() {
return legacyId;
}
public int getLegacyData() {
return legacyData;
}
public BlockRotation.RotationType getRotationType() {
return rotationType;
}
}

View File

@ -0,0 +1,104 @@
package de.bixilon.minosoft.game.datatypes.blocks;
public interface BlockRotation {
int getId();
enum RotationType {
BARELY,
NORMAL,
EXTENDED
}
enum Barely implements BlockRotation {
EAST(0),
NORTH(1),
SOUTH(2),
WEST(3);
int id;
Barely(int id) {
this.id = id;
}
public static Barely byId(int id) {
for (Barely b : values()) {
if (b.getId() == id) {
return b;
}
}
return null;
}
public int getId() {
return id;
}
}
enum Normal implements BlockRotation {
DOWN(0),
EAST(1),
NORTH(2),
SOUTH(3),
UP(4),
WEST(5);
int id;
Normal(int id) {
this.id = id;
}
public static Normal byId(int id) {
for (Normal n : values()) {
if (n.getId() == id) {
return n;
}
}
return null;
}
public int getId() {
return id;
}
}
enum Extended implements BlockRotation {
SOUTH(0),
SOUTH_SOUTH_WEST(1),
SOUTH_WEST(2),
WEST_SOUTH_WEST(3),
WEST(4),
WEST_NORTH_WEST(5),
NORTH_WEST(6),
NORTH_NORTH_WEST(7),
NORTH(8),
NORTH_NORTH_EAST(9),
NORTH_EAST(10),
EAST_NORTH_EAST(11),
EAST(12),
EAST_SOUTH_EAST(13),
SOUTH_EAST(14),
SOUTH_SOUTH_EAST(15);
int id;
Extended(int id) {
this.id = id;
}
public static Extended byId(int id) {
for (Extended e : values()) {
if (e.getId() == id) {
return e;
}
}
return null;
}
public int getId() {
return id;
}
}
}

View File

@ -1,33 +0,0 @@
package de.bixilon.minosoft.game.datatypes.blocks;
import de.bixilon.minosoft.game.datatypes.BlockPosition;
import java.lang.reflect.InvocationTargetException;
public enum Blocks {
DIRT(Dirt.class),
AIR(Air.class);
private final Class<? extends Block> clazz;
Blocks(Class<? extends Block> clazz) {
this.clazz = clazz;
}
public static Block getBlockInstance(Class<? extends Block> clazz, BlockPosition pos) {
try {
return clazz.getConstructor(BlockPosition.class).newInstance(pos);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
throw new RuntimeException("Invalid block constructor!");
}
}
public static Block getBlockInstance(Blocks b, BlockPosition pos) {
return getBlockInstance(b.getClazz(), pos);
}
public Class<? extends Block> getClazz() {
return clazz;
}
}

View File

@ -1,26 +0,0 @@
package de.bixilon.minosoft.game.datatypes.blocks;
import de.bixilon.minosoft.game.datatypes.BlockPosition;
public class Dirt implements Block {
final BlockPosition position;
public Dirt(BlockPosition position) {
this.position = position;
}
@Override
public BlockPosition getBlockPosition() {
return position;
}
@Override
public int getLegacyId() {
return 0;
}
@Override
public String getLegacyIdentifier() {
return "minecraft:dirt";
}
}

View File

@ -0,0 +1,93 @@
package de.bixilon.minosoft.protocol.packets.clientbound.play;
import de.bixilon.minosoft.logging.Log;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
import de.bixilon.minosoft.protocol.protocol.InPacketBuffer;
import de.bixilon.minosoft.protocol.protocol.PacketHandler;
import de.bixilon.minosoft.protocol.protocol.ProtocolVersion;
import de.bixilon.minosoft.util.BitByte;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
public class PlayChunkBulk implements ClientboundPacket {
short chunkColumnCount;
@Override
public void read(InPacketBuffer buffer, ProtocolVersion v) {
switch (v) {
case VERSION_1_7_10:
// ToDo only implement once, not twice (chunk data and chunk bulk)
this.chunkColumnCount = buffer.readShort();
int dataLen = buffer.readInteger();
boolean containsSkyLight = buffer.readBoolean();
byte[] data = buffer.readBytes(dataLen);
// decompressing chunk data
Inflater inflater = new Inflater();
inflater.setInput(data, 0, dataLen);
byte[] result = new byte[4096];
ByteArrayOutputStream stream = new ByteArrayOutputStream(dataLen);
try {
while (!inflater.finished()) {
stream.write(result, 0, inflater.inflate(result));
}
} catch (DataFormatException e) {
e.printStackTrace();
} finally {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
InByteBuffer decompressed = new InByteBuffer(stream.toByteArray());
// chunk meta data
for (int i = 0; i < chunkColumnCount; i++) {
int x = buffer.readInteger();
int y = buffer.readInteger();
short primaryBitMask = buffer.readShort();
short addBitMask = buffer.readShort();
System.out.println(String.format("Meta data: %s %s", x, y));
for (int c = 0; c < 16; c++) { // max sections per chunks in chunk column
if (BitByte.isBitSet(primaryBitMask, c)) {
short[] blockType = BitByte.byteArrayToShortArray(decompressed.readBytes(4096)); // 16 * 16 * 16
byte[] metadata = decompressed.readBytes(2048); // 16 * 16 * 16 / 2 (only half bit per block)
byte[] light;
if (containsSkyLight) {
light = decompressed.readBytes(2048);
}
byte[] addBlockType;
if (BitByte.isBitSet(addBitMask, c)) {
addBlockType = decompressed.readBytes(2048);
}
byte[] biome = decompressed.readBytes(256);
}
}
}
break;
}
log();
}
@Override
public void log() {
Log.protocol(String.format("Chunk bulk packet received (chunks: %s)", chunkColumnCount));
}
@Override
public void handle(PacketHandler h) {
h.handle(this);
}
}

View File

@ -5,10 +5,7 @@ import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.protocol.packets.clientbound.login.PacketEncryptionKeyRequest;
import de.bixilon.minosoft.protocol.packets.clientbound.login.PacketLoginDisconnect;
import de.bixilon.minosoft.protocol.packets.clientbound.login.PacketLoginSuccess;
import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketJoinGame;
import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketKeepAlive;
import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketPlayerInfo;
import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketTimeUpdate;
import de.bixilon.minosoft.protocol.packets.clientbound.play.*;
import de.bixilon.minosoft.protocol.packets.clientbound.status.PacketStatusPong;
import de.bixilon.minosoft.protocol.packets.clientbound.status.PacketStatusResponse;
import de.bixilon.minosoft.protocol.packets.serverbound.login.PacketEncryptionResponse;
@ -68,4 +65,7 @@ public class PacketHandler {
public void handle(PacketKeepAlive pkg) {
connection.sendPacket(new PacketKeepAliveResponse(pkg.getId()));
}
public void handle(PlayChunkBulk pkg) {
}
}

View File

@ -4,10 +4,7 @@ import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.packets.clientbound.login.PacketEncryptionKeyRequest;
import de.bixilon.minosoft.protocol.packets.clientbound.login.PacketLoginDisconnect;
import de.bixilon.minosoft.protocol.packets.clientbound.login.PacketLoginSuccess;
import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketJoinGame;
import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketKeepAlive;
import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketPlayerInfo;
import de.bixilon.minosoft.protocol.packets.clientbound.play.PacketTimeUpdate;
import de.bixilon.minosoft.protocol.packets.clientbound.play.*;
import de.bixilon.minosoft.protocol.packets.clientbound.status.PacketStatusPong;
import de.bixilon.minosoft.protocol.packets.clientbound.status.PacketStatusResponse;
@ -41,5 +38,6 @@ public interface Protocol {
packetClassMapping.put(Packets.Clientbound.PLAY_PLAYER_INFO, PacketPlayerInfo.class);
packetClassMapping.put(Packets.Clientbound.PLAY_TIME_UPDATE, PacketTimeUpdate.class);
packetClassMapping.put(Packets.Clientbound.PLAY_KEEP_ALIVE, PacketKeepAlive.class);
packetClassMapping.put(Packets.Clientbound.PLAY_CHUNK_BULK, PlayChunkBulk.class);
}
}

View File

@ -1,10 +1,18 @@
package de.bixilon.minosoft.util;
public class BitByte {
public static boolean isBitSet(byte in, int pos) {
public static boolean isBitSet(int in, int pos) {
boolean bitSet;
int mask = 1 << pos;
bitSet = ((in & mask) == mask);
return bitSet;
}
public static short[] byteArrayToShortArray(byte[] readBytes) {
short[] ret = new short[readBytes.length];
for (int i = 0; i < readBytes.length; i++) {
ret[0] = readBytes[0];
}
return ret;
}
}