mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-26 18:11:40 +01:00
Added chunks & some location packets
This commit is contained in:
parent
81a880d3cd
commit
076f4d558b
@ -15,4 +15,5 @@ repositories {
|
||||
dependencies {
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
implementation 'com.github.Adamaq01:ozao-net:2.3.1'
|
||||
compile 'com.github.Querz:NBT:4.1'
|
||||
}
|
||||
|
@ -4,9 +4,29 @@ import fr.themode.minestom.net.player.PlayerConnection;
|
||||
|
||||
public class Player {
|
||||
|
||||
private double x, y, z;
|
||||
private float yaw, pitch;
|
||||
private boolean onGround;
|
||||
|
||||
private PlayerConnection playerConnection;
|
||||
|
||||
public PlayerConnection getPlayerConnection() {
|
||||
return playerConnection;
|
||||
}
|
||||
|
||||
public void refreshPosition(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public void refreshView(float yaw, float pitch) {
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public void refreshOnGround(boolean onGround) {
|
||||
this.onGround = onGround;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
package fr.themode.minestom.net.packet.client.handler;
|
||||
|
||||
import fr.themode.minestom.net.packet.client.play.ClientPlayerPositionAndLookPacket;
|
||||
import fr.themode.minestom.net.packet.client.play.ClientPluginMessagePacket;
|
||||
import fr.themode.minestom.net.packet.client.play.ClientSettingsPacket;
|
||||
import fr.themode.minestom.net.packet.client.play.ClientTeleportConfirmPacket;
|
||||
import fr.themode.minestom.net.packet.client.play.*;
|
||||
|
||||
public class ClientPlayPacketsHandler extends ClientPacketsHandler {
|
||||
|
||||
public ClientPlayPacketsHandler() {
|
||||
register(0x05, ClientSettingsPacket.class);
|
||||
register(0x0B, ClientPluginMessagePacket.class);
|
||||
register(0x11, ClientPlayerPositionPacket.class);
|
||||
register(0x12, ClientPlayerPositionAndLookPacket.class);
|
||||
register(0x00, ClientTeleportConfirmPacket.class);
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
package fr.themode.minestom.net.packet.client.login;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.adamaq01.ozao.net.packet.Packet;
|
||||
import fr.themode.minestom.entity.GameMode;
|
||||
import fr.themode.minestom.net.ConnectionManager;
|
||||
import fr.themode.minestom.net.ConnectionState;
|
||||
import fr.themode.minestom.net.packet.client.ClientPreplayPacket;
|
||||
import fr.themode.minestom.net.packet.server.login.JoinGamePacket;
|
||||
import fr.themode.minestom.net.packet.server.login.LoginSuccessPacket;
|
||||
import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
|
||||
import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket;
|
||||
import fr.themode.minestom.net.packet.server.play.SpawnPositionPacket;
|
||||
import fr.themode.minestom.net.player.PlayerConnection;
|
||||
import fr.themode.minestom.utils.Utils;
|
||||
import fr.themode.minestom.world.Dimension;
|
||||
import fr.themode.minestom.world.*;
|
||||
|
||||
public class LoginStartPacket implements ClientPreplayPacket {
|
||||
|
||||
@ -44,31 +44,22 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
||||
|
||||
// TODO player abilities
|
||||
|
||||
for (int x = 0; x < 8; x++) {
|
||||
for (int z = 0; z < 8; z++) {
|
||||
ChunkDataPacket.ChunkSection chunkSection = new ChunkDataPacket.ChunkSection();
|
||||
chunkSection.bitsPerBlock = 13;
|
||||
chunkSection.data = new long[]{0x1001880C0060020L, 0x200D0068004C020L, 1111L};
|
||||
CustomChunk customChunk = new CustomChunk(CustomBiome.VOID);
|
||||
for (int x = 0; x < 16; x++)
|
||||
for (int z = 0; z < 16; z++)
|
||||
customChunk.setBlock(x, 4, z, new CustomBlock(1)); // Stone
|
||||
|
||||
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
|
||||
chunkDataPacket.columnX = x;
|
||||
chunkDataPacket.columnZ = z;
|
||||
chunkDataPacket.fullChunk = true;
|
||||
chunkDataPacket.mask = 0xFFFF; // 16 bits
|
||||
ChunkDataPacket.ChunkSection[] sections = new ChunkDataPacket.ChunkSection[16];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
sections[i] = chunkSection;
|
||||
}
|
||||
chunkDataPacket.chunkSections = sections;
|
||||
|
||||
connection.sendPacket(chunkDataPacket);
|
||||
for (int x = -5; x < 5; x++) {
|
||||
for (int z = -5; z < 5; z++) {
|
||||
Packet packet = ChunkPacketCreator.create(x, z, customChunk, 0, 15);
|
||||
connection.getConnection().sendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
SpawnPositionPacket spawnPositionPacket = new SpawnPositionPacket();
|
||||
spawnPositionPacket.x = 50;
|
||||
spawnPositionPacket.y = 5;
|
||||
spawnPositionPacket.z = 50;
|
||||
spawnPositionPacket.x = 0;
|
||||
spawnPositionPacket.y = 18;
|
||||
spawnPositionPacket.z = 0;
|
||||
connection.sendPacket(spawnPositionPacket);
|
||||
|
||||
PlayerPositionAndLookPacket playerPositionAndLookPacket = new PlayerPositionAndLookPacket();
|
||||
|
@ -0,0 +1,24 @@
|
||||
package fr.themode.minestom.net.packet.client.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
|
||||
|
||||
public class ClientPlayerLookPacket implements ClientPlayPacket {
|
||||
|
||||
public float yaw, pitch;
|
||||
public boolean onGround;
|
||||
|
||||
@Override
|
||||
public void process(Player player) {
|
||||
player.refreshView(yaw, pitch);
|
||||
player.refreshOnGround(onGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Buffer buffer) {
|
||||
this.yaw = buffer.getFloat();
|
||||
this.pitch = buffer.getFloat();
|
||||
this.onGround = buffer.getBoolean();
|
||||
}
|
||||
}
|
@ -12,7 +12,9 @@ public class ClientPlayerPositionAndLookPacket implements ClientPlayPacket {
|
||||
|
||||
@Override
|
||||
public void process(Player player) {
|
||||
|
||||
player.refreshPosition(x, y, z);
|
||||
player.refreshView(yaw, pitch);
|
||||
player.refreshOnGround(onGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,25 @@
|
||||
package fr.themode.minestom.net.packet.client.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
|
||||
|
||||
public class ClientPlayerPositionPacket implements ClientPlayPacket {
|
||||
|
||||
public double x, y, z;
|
||||
public boolean onGround;
|
||||
|
||||
@Override
|
||||
public void process(Player player) {
|
||||
player.refreshPosition(x, y, z);
|
||||
player.refreshOnGround(onGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Buffer buffer) {
|
||||
this.x = buffer.getDouble();
|
||||
this.y = buffer.getDouble();
|
||||
this.z = buffer.getDouble();
|
||||
this.onGround = buffer.getBoolean();
|
||||
}
|
||||
}
|
@ -25,16 +25,23 @@ public class ChunkDataPacket implements ServerPacket {
|
||||
|
||||
// Nbt
|
||||
buffer.putByte((byte) 10);
|
||||
buffer.putShort((short) 0);
|
||||
buffer.putByte((byte) 12);
|
||||
buffer.putShort((short) "MOTION_BLOCKING".length());
|
||||
try {
|
||||
buffer.putBytes("MOTION_BLOCKING".getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
buffer.putInt(256);
|
||||
for (int i = 0; i < 256; i++) {
|
||||
buffer.putLong(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
buffer.putByte((byte) 12);
|
||||
buffer.putShort((short) "MOTION_BLOCKING".length());
|
||||
buffer.putShort((short) "WORLD_SURFACE".length());
|
||||
try {
|
||||
buffer.putBytes("MOTION_BLOCKING".getBytes("UTF-8"));
|
||||
buffer.putBytes("WORLD_SURFACE".getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package fr.themode.minestom.utils;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.world.CustomBlock;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Utils {
|
||||
|
||||
@ -121,4 +124,46 @@ public class Utils {
|
||||
buffer.putLong(((x & 0x3FFFFFF) << 38) | ((y & 0xFFF) << 26) | (z & 0x3FFFFFF));
|
||||
}
|
||||
|
||||
public static void writeBlocks(Buffer buffer, CustomBlock[] blocks, int bitsPerEntry) {
|
||||
buffer.putShort((short) Arrays.stream(blocks).filter(customBlock -> customBlock.getType() != 0).collect(Collectors.toList()).size());
|
||||
buffer.putByte((byte) bitsPerEntry);
|
||||
int[] blocksData = new int[16 * 16 * 16];
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
int sectionIndex = (((y * 16) + x) * 16) + z;
|
||||
int index = y << 8 | z << 4 | x;
|
||||
blocksData[index] = blocks[sectionIndex].getType();
|
||||
}
|
||||
}
|
||||
}
|
||||
long[] data = encodeBlocks(blocksData, 14);
|
||||
writeVarInt(buffer, data.length);
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
buffer.putLong(data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static long[] encodeBlocks(int[] blocks, int bitsPerEntry) {
|
||||
long maxEntryValue = (1L << bitsPerEntry) - 1;
|
||||
|
||||
int length = (int) Math.ceil(blocks.length * bitsPerEntry / 64.0);
|
||||
long[] data = new long[length];
|
||||
|
||||
for (int index = 0; index < blocks.length; index++) {
|
||||
int value = blocks[index];
|
||||
int bitIndex = index * bitsPerEntry;
|
||||
int startIndex = bitIndex / 64;
|
||||
int endIndex = ((index + 1) * bitsPerEntry - 1) / 64;
|
||||
int startBitSubIndex = bitIndex % 64;
|
||||
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
||||
if (startIndex != endIndex) {
|
||||
int endBitSubIndex = 64 - startBitSubIndex;
|
||||
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,82 @@
|
||||
package fr.themode.minestom.world;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.adamaq01.ozao.net.packet.Packet;
|
||||
import fr.themode.minestom.utils.Utils;
|
||||
import net.querz.nbt.CompoundTag;
|
||||
import net.querz.nbt.LongArrayTag;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ChunkPacketCreator {
|
||||
|
||||
public static Packet create(int chunkX, int chunkZ, CustomChunk customChunk, int start, int end) {
|
||||
Packet packet = Packet.create();
|
||||
packet.put("id", 0x21);
|
||||
Buffer payload = packet.getPayload();
|
||||
|
||||
payload.putInt(chunkX);
|
||||
payload.putInt(chunkZ);
|
||||
payload.putBoolean(true); // Send biome data (loading chunk, not modifying it)
|
||||
int mask = 0;
|
||||
Buffer blocks = Buffer.create();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
mask |= 1 << i;
|
||||
CustomBlock[] section = getSection(customChunk, i);
|
||||
Utils.writeBlocks(blocks, section, 14);
|
||||
}
|
||||
// Biome data
|
||||
int[] biomeData = new int[256];
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
biomeData[z * 16 | x] = customChunk.getBiome().getId();
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < biomeData.length; i++) {
|
||||
blocks.putInt(biomeData[i]);
|
||||
}
|
||||
Utils.writeVarInt(payload, mask);
|
||||
|
||||
// Heightmap
|
||||
int[] motionBlocking = new int[16 * 16];
|
||||
int[] worldSurface = new int[16 * 16];
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
motionBlocking[x + z * 16] = 4;
|
||||
worldSurface[x + z * 16] = 5;
|
||||
}
|
||||
}
|
||||
CompoundTag compound = new CompoundTag();
|
||||
compound.put("MOTION_BLOCKING", new LongArrayTag(Utils.encodeBlocks(motionBlocking, 9)));
|
||||
compound.put("WORLD_SURFACE", new LongArrayTag(Utils.encodeBlocks(worldSurface, 9)));
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try {
|
||||
compound.serialize(new DataOutputStream(outputStream), 100);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
byte[] data = outputStream.toByteArray();
|
||||
payload.putBytes(data);
|
||||
|
||||
Utils.writeVarInt(payload, blocks.length());
|
||||
payload.putBuffer(blocks);
|
||||
Utils.writeVarInt(payload, 0);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
public static CustomBlock[] getSection(CustomChunk customChunk, int section) {
|
||||
CustomBlock[] blocks = new CustomBlock[16 * 16 * 16];
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
int index = (((y * 16) + x) * 16) + z;
|
||||
blocks[index] = customChunk.getBlock(x, y + 16 * section, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
}
|
24
src/main/java/fr/themode/minestom/world/CustomBiome.java
Normal file
24
src/main/java/fr/themode/minestom/world/CustomBiome.java
Normal file
@ -0,0 +1,24 @@
|
||||
package fr.themode.minestom.world;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public enum CustomBiome {
|
||||
|
||||
OCEAN(0),
|
||||
PLAINS(1),
|
||||
VOID(127);
|
||||
|
||||
private int id;
|
||||
|
||||
CustomBiome(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static CustomBiome fromId(int id) {
|
||||
return Arrays.stream(values()).filter(customBiome -> customBiome.id == id).findFirst().get();
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
37
src/main/java/fr/themode/minestom/world/CustomBlock.java
Normal file
37
src/main/java/fr/themode/minestom/world/CustomBlock.java
Normal file
@ -0,0 +1,37 @@
|
||||
package fr.themode.minestom.world;
|
||||
|
||||
public class CustomBlock {
|
||||
|
||||
private short typeAndDamage;
|
||||
|
||||
public CustomBlock(int type) {
|
||||
this.typeAndDamage = (short) (type & 0x0FFF);
|
||||
this.typeAndDamage |= (0 << 12) & 0xF000;
|
||||
}
|
||||
|
||||
public CustomBlock(int type, int damage) {
|
||||
this.typeAndDamage = (short) (type & 0x0FFF);
|
||||
this.typeAndDamage |= (damage << 12) & 0xF000;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return typeAndDamage & 0x0FFF;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.typeAndDamage |= type & 0x0FFF;
|
||||
}
|
||||
|
||||
public int getDamage() {
|
||||
return (typeAndDamage & 0xF000) >>> 12;
|
||||
}
|
||||
|
||||
public void setDamage(int damage) {
|
||||
this.typeAndDamage |= (damage << 12) & 0xF000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("CustomBlock{type=%s, damage=%s}", getType(), getDamage());
|
||||
}
|
||||
}
|
41
src/main/java/fr/themode/minestom/world/CustomChunk.java
Normal file
41
src/main/java/fr/themode/minestom/world/CustomChunk.java
Normal file
@ -0,0 +1,41 @@
|
||||
package fr.themode.minestom.world;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class CustomChunk {
|
||||
|
||||
private CustomBiome biome;
|
||||
private HashMap<Short, CustomBlock> blocks;
|
||||
|
||||
public CustomChunk(CustomBiome biome) {
|
||||
this.biome = biome;
|
||||
this.blocks = new HashMap<>();
|
||||
}
|
||||
|
||||
public CustomBiome getBiome() {
|
||||
return biome;
|
||||
}
|
||||
|
||||
public HashMap<Short, CustomBlock> getBlocks() {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
public void setBlock(int x, int y, int z, CustomBlock customBlock) {
|
||||
short index = (short) (x & 0x000F);
|
||||
index |= (y << 4) & 0x0FF0;
|
||||
index |= (z << 12) & 0xF000;
|
||||
this.blocks.put(index, customBlock);
|
||||
}
|
||||
|
||||
public CustomBlock getBlock(int x, int y, int z) {
|
||||
short index = (short) (x & 0x000F);
|
||||
index |= (y << 4) & 0x0FF0;
|
||||
index |= (z << 12) & 0xF000;
|
||||
return this.blocks.getOrDefault(index, new CustomBlock(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("CustomChunk{biome=%s, blocks=%s}", biome, blocks.values());
|
||||
}
|
||||
}
|
51
src/main/java/fr/themode/minestom/world/CustomWorld.java
Normal file
51
src/main/java/fr/themode/minestom/world/CustomWorld.java
Normal file
@ -0,0 +1,51 @@
|
||||
package fr.themode.minestom.world;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class CustomWorld {
|
||||
|
||||
private String name;
|
||||
private HashMap<Integer, CustomChunk> chunks;
|
||||
|
||||
public CustomWorld(String name) {
|
||||
this.name = name;
|
||||
this.chunks = new HashMap<>();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public HashMap<Integer, CustomChunk> getChunks() {
|
||||
return chunks;
|
||||
}
|
||||
|
||||
public void setChunk(int x, int z, CustomChunk customChunk) {
|
||||
int index = x & 0x0000FFFF;
|
||||
index |= (z << 16) & 0xFFFF0000;
|
||||
this.chunks.put(index, customChunk);
|
||||
}
|
||||
|
||||
public CustomChunk getChunk(int x, int z) {
|
||||
int index = x & 0x0000FFFF;
|
||||
index |= (z << 16) & 0xFFFF0000;
|
||||
return this.chunks.get(index);
|
||||
}
|
||||
|
||||
public void setBlock(int x, int y, int z, CustomBlock customBlock) {
|
||||
int chunkX = x / 16;
|
||||
int chunkZ = z / 16;
|
||||
getChunk(chunkX, chunkZ).setBlock(x % 16, y, z % 16, customBlock);
|
||||
}
|
||||
|
||||
public CustomBlock getBlock(int x, int y, int z) {
|
||||
int chunkX = x / 16;
|
||||
int chunkZ = z / 16;
|
||||
return getChunk(chunkX, chunkZ).getBlock(x % 16, y, z % 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("CustomWorld{name=%s, chunks=%s}", name, chunks.values());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user