Switched to SimpleNet

This commit is contained in:
TheMode 2019-09-02 06:02:12 +02:00
parent dedc17f42e
commit 81c0626e57
53 changed files with 726 additions and 493 deletions

View File

@ -20,6 +20,6 @@ dependencies {
implementation 'com.github.luben:zstd-jni:1.4.3-1'
implementation 'com.esotericsoftware:reflectasm:1.11.9'
implementation 'com.github.LynnOwens:starlite:9971b899f7'
//implementation 'com.github.jhg023:SimpleNet:1.4.14'
implementation 'com.github.jhg023:SimpleNet:1.4.14'
}

View File

@ -1,10 +1,5 @@
package fr.themode.minestom;
import fr.adamaq01.ozao.net.packet.Packet;
import fr.adamaq01.ozao.net.server.Connection;
import fr.adamaq01.ozao.net.server.Server;
import fr.adamaq01.ozao.net.server.ServerHandler;
import fr.adamaq01.ozao.net.server.backend.tcp.TCPServer;
import fr.themode.minestom.data.DataManager;
import fr.themode.minestom.entity.EntityManager;
import fr.themode.minestom.entity.Player;
@ -13,17 +8,21 @@ import fr.themode.minestom.instance.InstanceManager;
import fr.themode.minestom.instance.demo.StoneBlock;
import fr.themode.minestom.listener.PacketListenerManager;
import fr.themode.minestom.net.ConnectionManager;
import fr.themode.minestom.net.ConnectionUtils;
import fr.themode.minestom.net.PacketProcessor;
import fr.themode.minestom.net.packet.PacketReader;
import fr.themode.minestom.net.packet.client.status.LegacyServerListPingPacket;
import fr.themode.minestom.net.packet.server.play.KeepAlivePacket;
import fr.themode.minestom.net.protocol.MinecraftProtocol;
import fr.themode.minestom.utils.Utils;
import simplenet.Server;
public class Main {
// Thread number
public static final int THREAD_COUNT_PACKET_WRITER = 5;
public static final int THREAD_COUNT_PACKET_WRITER = 2;
public static final int THREAD_COUNT_CHUNK_IO = 2;
public static final int THREAD_COUNT_CHUNK_BATCH = 3;
public static final int THREAD_COUNT_ENTITIES = 3;
public static final int THREAD_COUNT_CHUNK_BATCH = 2;
public static final int THREAD_COUNT_ENTITIES = 2;
public static final int THREAD_COUNT_PLAYERS_ENTITIES = 2;
public static final int TICK_MS = 50;
@ -57,39 +56,42 @@ public class Main {
blockManager.registerBlock(StoneBlock::new);
server = new TCPServer(new MinecraftProtocol()).addHandler(new ServerHandler() {
@Override
public void onConnect(Server server, Connection connection) {
System.out.println("A connection");
}
server = new Server(136434);
@Override
public void onDisconnect(Server server, Connection connection) {
server.onConnect(client -> {
System.out.println("CONNECTION");
client.postDisconnect(() -> {
System.out.println("A Disconnection");
if (packetProcessor.hasPlayerConnection(connection)) {
Player player = connectionManager.getPlayer(packetProcessor.getPlayerConnection(connection));
if (packetProcessor.hasPlayerConnection(client)) {
Player player = connectionManager.getPlayer(packetProcessor.getPlayerConnection(client));
if (player != null) {
player.remove();
connectionManager.removePlayer(player.getPlayerConnection());
}
packetProcessor.removePlayerConnection(connection);
packetProcessor.removePlayerConnection(client);
}
}
});
@Override
public void onPacketReceive(Server server, Connection connection, Packet packet) {
packetProcessor.process(connection, packet);
}
@Override
public void onException(Server server, Connection connection, Throwable cause) {
cause.printStackTrace();
}
ConnectionUtils.readVarIntAlways(client, length -> {
if (length == 0xFE) { // Legacy server ping
LegacyServerListPingPacket legacyServerListPingPacket = new LegacyServerListPingPacket();
legacyServerListPingPacket.read(new PacketReader(client, 0, 0), () -> {
legacyServerListPingPacket.process(null, null);
});
} else {
final int varIntLength = Utils.lengthVarInt(length);
ConnectionUtils.readVarInt(client, packetId -> {
int offset = varIntLength + Utils.lengthVarInt(packetId);
packetProcessor.process(client, packetId, length, offset);
});
}
});
});
server.bind(25565);
server.bind("localhost", 25565);
System.out.println("Server started");
long tickDistance = TICK_MS * 1000000;

View File

@ -1,6 +1,5 @@
package fr.themode.minestom.entity;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.Main;
import fr.themode.minestom.Viewable;
import fr.themode.minestom.collision.BoundingBox;
@ -15,11 +14,13 @@ import fr.themode.minestom.net.packet.server.play.*;
import fr.themode.minestom.net.player.PlayerConnection;
import fr.themode.minestom.utils.Vector;
import fr.themode.minestom.utils.*;
import simplenet.packet.Packet;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
public abstract class Entity implements Viewable, DataContainer {
@ -569,49 +570,48 @@ public abstract class Entity implements Viewable, DataContainer {
public EntityMetaDataPacket getMetadataPacket() {
EntityMetaDataPacket metaDataPacket = new EntityMetaDataPacket();
metaDataPacket.entityId = getEntityId();
metaDataPacket.data = getMetadataBuffer();
metaDataPacket.consumer = getMetadataConsumer();
return metaDataPacket;
}
public Buffer getMetadataBuffer() {
Buffer buffer = Buffer.create();
fillMetadataIndex(buffer, 0);
fillMetadataIndex(buffer, 1);
fillMetadataIndex(buffer, 5);
return buffer;
public Consumer<Packet> getMetadataConsumer() {
return packet -> {
fillMetadataIndex(packet, 0);
fillMetadataIndex(packet, 1);
fillMetadataIndex(packet, 5);
};
}
protected void sendMetadataIndex(int index) {
Buffer buffer = Buffer.create();
fillMetadataIndex(buffer, index);
EntityMetaDataPacket metaDataPacket = new EntityMetaDataPacket();
metaDataPacket.entityId = getEntityId();
metaDataPacket.data = buffer;
metaDataPacket.consumer = packet -> {
fillMetadataIndex(packet, index);
};
sendPacketToViewersAndSelf(metaDataPacket);
}
private void fillMetadataIndex(Buffer buffer, int index) {
private void fillMetadataIndex(Packet packet, int index) {
switch (index) {
case 0:
fillStateMetadata(buffer);
fillStateMetadata(packet);
break;
case 1:
fillAirTickMetaData(buffer);
fillAirTickMetaData(packet);
break;
case 2:
fillCustomNameMetaData(buffer);
fillCustomNameMetaData(packet);
break;
case 5:
fillNoGravityMetaData(buffer);
fillNoGravityMetaData(packet);
break;
}
}
private void fillStateMetadata(Buffer buffer) {
buffer.putByte((byte) 0);
buffer.putByte(METADATA_BYTE);
private void fillStateMetadata(Packet packet) {
packet.putByte((byte) 0);
packet.putByte(METADATA_BYTE);
byte index0 = 0;
if (onFire)
index0 += 1;
@ -629,7 +629,25 @@ public abstract class Entity implements Viewable, DataContainer {
index0 += 64;
if (usingElytra)
index0 += 128;
buffer.putByte(index0);
packet.putByte(index0);
}
private void fillAirTickMetaData(Packet packet) {
packet.putByte((byte) 1);
packet.putByte(METADATA_VARINT);
Utils.writeVarInt(packet, air);
}
private void fillCustomNameMetaData(Packet packet) {
packet.putByte((byte) 2);
packet.putByte(METADATA_CHAT);
Utils.writeString(packet, customName);
}
private void fillNoGravityMetaData(Packet packet) {
packet.putByte((byte) 5);
packet.putByte(METADATA_BOOLEAN);
packet.putBoolean(noGravity);
}
protected void sendSynchronization() {
@ -643,24 +661,6 @@ public abstract class Entity implements Viewable, DataContainer {
sendPacketToViewers(getPassengersPacket());
}
private void fillAirTickMetaData(Buffer buffer) {
buffer.putByte((byte) 1);
buffer.putByte(METADATA_VARINT);
Utils.writeVarInt(buffer, air);
}
private void fillCustomNameMetaData(Buffer buffer) {
buffer.putByte((byte) 2);
buffer.putByte(METADATA_CHAT);
Utils.writeString(buffer, customName);
}
private void fillNoGravityMetaData(Buffer buffer) {
buffer.putByte((byte) 5);
buffer.putByte(METADATA_BOOLEAN);
buffer.putBoolean(noGravity);
}
private boolean shouldUpdate() {
return (float) (System.currentTimeMillis() - lastUpdate) >= Main.TICK_MS * 0.9f; // Margin of error
}

View File

@ -94,7 +94,7 @@ public abstract class EntityCreature extends LivingEntity {
spawnMobPacket.entityType = getEntityType();
spawnMobPacket.position = getPosition();
spawnMobPacket.headPitch = 0;
spawnMobPacket.metadata = getMetadataBuffer();
spawnMobPacket.consumer = getMetadataConsumer();
playerConnection.sendPacket(entityPacket);
playerConnection.sendPacket(spawnMobPacket);
}

View File

@ -1,8 +1,10 @@
package fr.themode.minestom.entity;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.utils.Utils;
import simplenet.packet.Packet;
import java.util.function.Consumer;
public class ItemEntity extends ObjectEntity {
@ -36,12 +38,13 @@ public class ItemEntity extends ObjectEntity {
}
@Override
public Buffer getMetadataBuffer() {
Buffer buffer = super.getMetadataBuffer();
buffer.putByte((byte) 7);
buffer.putByte(METADATA_SLOT);
Utils.writeItemStack(buffer, itemStack);
return buffer;
public Consumer<Packet> getMetadataConsumer() {
return packet -> {
super.getMetadataConsumer().accept(packet);
packet.putByte((byte) 7);
packet.putByte(METADATA_SLOT);
Utils.writeItemStack(packet, itemStack);
};
}
@Override

View File

@ -1,6 +1,5 @@
package fr.themode.minestom.entity;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.collision.BoundingBox;
import fr.themode.minestom.entity.property.Attribute;
import fr.themode.minestom.event.PickupItemEvent;
@ -9,8 +8,10 @@ import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.packet.server.play.AnimationPacket;
import fr.themode.minestom.net.packet.server.play.CollectItemPacket;
import fr.themode.minestom.net.packet.server.play.EntityPropertiesPacket;
import simplenet.packet.Packet;
import java.util.Set;
import java.util.function.Consumer;
public abstract class LivingEntity extends Entity {
@ -67,20 +68,21 @@ public abstract class LivingEntity extends Entity {
}
@Override
public Buffer getMetadataBuffer() {
Buffer buffer = super.getMetadataBuffer();
buffer.putByte((byte) 7);
buffer.putByte(METADATA_BYTE);
byte activeHandValue = 0;
if (isHandActive) {
activeHandValue += 1;
if (activeHand)
activeHandValue += 2;
if (riptideSpinAttack)
activeHandValue += 4;
}
buffer.putByte(activeHandValue);
return buffer;
public Consumer<Packet> getMetadataConsumer() {
return packet -> {
super.getMetadataConsumer().accept(packet);
packet.putByte((byte) 7);
packet.putByte(METADATA_BYTE);
byte activeHandValue = 0;
if (isHandActive) {
activeHandValue += 1;
if (activeHand)
activeHandValue += 2;
if (riptideSpinAttack)
activeHandValue += 4;
}
packet.putByte(activeHandValue);
};
}
public void damage(float value) {

View File

@ -156,7 +156,7 @@ public class Player extends LivingEntity {
setEventCallback(PlayerSpawnEvent.class, event -> {
System.out.println("SPAWN");
setGameMode(GameMode.CREATIVE);
setGameMode(GameMode.SURVIVAL);
teleport(new Position(0, 66, 0));
/*ChickenCreature chickenCreature = new ChickenCreature();
@ -191,7 +191,7 @@ public class Player extends LivingEntity {
teamsPacket.entities = new String[]{getUsername()};
sendPacketToViewersAndSelf(teamsPacket);
setAttribute(Attribute.MAX_HEALTH, 40);
setAttribute(Attribute.MAX_HEALTH, 10);
heal();
setExp(0.9f);
@ -502,6 +502,8 @@ public class Player extends LivingEntity {
sendPacketToViewers(spawnPlayerPacket);
playerConnection.sendPacket(getPropertiesPacket());
sendUpdateHealthPacket();
syncEquipments();
});
}
@ -637,7 +639,7 @@ public class Player extends LivingEntity {
DisconnectPacket disconnectPacket = new DisconnectPacket();
disconnectPacket.message = message;
playerConnection.sendPacket(disconnectPacket);
playerConnection.getConnection().close();
playerConnection.getClient().close();
}
public LevelType getLevelType() {
@ -736,6 +738,12 @@ public class Player extends LivingEntity {
sendPacketToViewers(getEquipmentPacket(slot));
}
public void syncEquipments() {
for (EntityEquipmentPacket.Slot slot : EntityEquipmentPacket.Slot.values()) {
syncEquipment(slot);
}
}
protected EntityEquipmentPacket getEquipmentPacket(EntityEquipmentPacket.Slot slot) {
EntityEquipmentPacket equipmentPacket = new EntityEquipmentPacket();
equipmentPacket.entityId = getEntityId();
@ -820,7 +828,11 @@ public class Player extends LivingEntity {
public int getChunkRange() {
int serverRange = Main.CHUNK_VIEW_DISTANCE;
int playerRange = getSettings().viewDistance;
return serverRange;//playerRange < serverRange ? playerRange : serverRange;
if (playerRange == 0) {
return serverRange; // Didn't receive settings packet yet
} else {
return playerRange < serverRange ? playerRange : serverRange;
}
}
public long getLastKeepAlive() {

View File

@ -1,13 +1,12 @@
package fr.themode.minestom.instance;
import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet;
import fr.themode.minestom.Main;
import fr.themode.minestom.Viewable;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
import fr.themode.minestom.utils.PacketUtils;
import fr.themode.minestom.utils.SerializerUtils;
import simplenet.packet.Packet;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
@ -33,7 +32,7 @@ public class Chunk implements Viewable {
// Cache
private Set<Player> viewers = new CopyOnWriteArraySet<>();
private Buffer fullDataPacket;
private Packet fullDataPacket;
public Chunk(Biome biome, int chunkX, int chunkZ) {
this.biome = biome;
@ -93,7 +92,7 @@ public class Chunk implements Viewable {
return chunkZ;
}
public Buffer getFullDataPacket() {
public Packet getFullDataPacket() {
return fullDataPacket;
}
@ -106,7 +105,7 @@ public class Chunk implements Viewable {
return blockEntities;
}
public void setFullDataPacket(Buffer fullDataPacket) {
public void setFullDataPacket(Packet fullDataPacket) {
this.fullDataPacket = fullDataPacket;
}
@ -151,8 +150,7 @@ public class Chunk implements Viewable {
}
protected void refreshDataPacket() {
Packet packet = PacketUtils.writePacket(getFreshFullDataPacket());
this.fullDataPacket = PacketUtils.encode(packet); // TODO write packet buffer in another thread (heavy calculations)
PacketUtils.writePacket(getFreshFullDataPacket(), packet -> fullDataPacket = packet); // TODO write packet buffer in another thread (heavy calculations)
}
@Override

View File

@ -1,11 +1,11 @@
package fr.themode.minestom.instance;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.Main;
import fr.themode.minestom.entity.*;
import fr.themode.minestom.utils.BlockPosition;
import fr.themode.minestom.utils.ChunkUtils;
import fr.themode.minestom.utils.Position;
import simplenet.packet.Packet;
import java.io.File;
import java.util.*;
@ -70,11 +70,9 @@ public abstract class Instance implements BlockModifier {
//
protected void sendChunkUpdate(Collection<Player> players, Chunk chunk) {
Buffer chunkData = chunk.getFullDataPacket();
chunkData.getData().retain(players.size()).markReaderIndex();
Packet chunkData = chunk.getFullDataPacket();
players.forEach(player -> {
player.getPlayerConnection().sendUnencodedPacket(chunkData);
chunkData.getData().resetReaderIndex();
player.getPlayerConnection().sendPacket(chunkData);
});
}
//

View File

@ -1,6 +1,5 @@
package fr.themode.minestom.instance;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.event.PlayerBlockBreakEvent;
import fr.themode.minestom.net.PacketWriterUtils;
@ -151,10 +150,7 @@ public class InstanceContainer extends Instance {
@Override
public void sendChunkUpdate(Player player, Chunk chunk) {
Buffer chunkData = chunk.getFullDataPacket();
chunkData.getData().retain(1).markReaderIndex();
player.getPlayerConnection().sendUnencodedPacket(chunkData);
chunkData.getData().resetReaderIndex();
player.getPlayerConnection().sendPacket(chunk.getFullDataPacket());
}
@Override

View File

@ -0,0 +1,170 @@
package fr.themode.minestom.net;
import simplenet.Client;
import simplenet.packet.Packet;
import simplenet.utility.exposed.consumer.ByteConsumer;
import simplenet.utility.exposed.predicate.BytePredicate;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.function.LongConsumer;
import static java.nio.charset.StandardCharsets.UTF_8;
public class ConnectionUtils {
/**
* Reads a variable-length number from a {@link Client}
*
* @param client The {@link Client} to read from
* @param maxBytes The maximum size (in bytes) of the VarNum
* @param callback The {@link LongConsumer} callback for the read VarNum
*/
private static void readVarNum(Client client, int maxBytes, LongConsumer callback) {
client.readByteUntil(new BytePredicate() {
int count;
long value;
@Override
public boolean test(byte data) {
value |= ((int) data & 0x7F) << (7 * count);
if (++count > maxBytes) throw new RuntimeException("VarNum is too big");
if ((data & 0x80) == 0) {
callback.accept(value);
return false;
} else return true;
}
});
}
/**
* Writes a variable-length number to a byte array
*
* @param value The value to wrapPacket
* @return The array containing the VarNum's bytes
*/
public static byte[] writeVarNum(long value) {
final byte[] bytes = new byte[10];
int written = 0;
do {
byte temp = (byte) (value & 0x7F);
value >>>= 7;
if (value != 0) {
temp |= 0x80;
}
bytes[written++] = temp;
} while (value != 0);
return Arrays.copyOfRange(bytes, 0, written);
}
/**
* Reads a variable-length String from the {@link Client}
*
* @param client The {@link Client} to read from
* @param callback The {@link Consumer<String>} callback for the read String
*/
public static void readString(Client client, Consumer<String> callback) {
readVarInt(client, stringLength ->
client.readBytes(stringLength, bytes ->
callback.accept(new String(bytes, UTF_8))
)
);
}
/**
* Reads a variable-length integer from the client, up to 5 bytes long
*
* @param client The client to read from
* @param callback See {@link ConnectionUtils#readVarNum(Client, int, LongConsumer)}
*/
public static void readVarInt(Client client, IntConsumer callback) {
readVarNum(client, 5, it -> callback.accept((int) it));
}
/**
* Reads a variable-length long from the client, up to 10 bytes long
*
* @param client The client to read from
* @param callback See {@link ConnectionUtils#readVarNum(Client, int, LongConsumer)}
*/
public static void readVarLong(Client client, LongConsumer callback) {
readVarNum(client, 10, callback);
}
/**
* Writes a legacy string to a packet container
* Legacy strings are prefixed with their length in characters as a short
* Modern strings are prefixed with their length in bytes as a VarInt
*
* @param string The String to write to the packet container
* @param charset The {@link Charset} this string is encoded in
* @param packet The {@link Packet} for this string to be written to
*/
public static void writeLegacyString(String string, Charset charset, Packet packet) {
final int length = string.length();
packet.putShort(length);
packet.putBytes(string.getBytes(charset));
}
/**
* Writes a legacy UTF-8 String to a packet container
*
* @param string The String to write
* @param packet The {@link Packet} for this String to be written to
*/
public static void writeLegacyString(String string, Packet packet) {
writeLegacyString(string, UTF_8, packet);
}
/**
* Writes a standard string to a packet container
*
* @param string The String to write to the packet container
* @param charset The {@link Charset} this String is encoded in
* @param packet The {@link Packet} for this string to be written to
*/
public static void writeString(String string, Charset charset, Packet packet) {
final byte[] bytes = string.getBytes(charset);
final int length = bytes.length;
final byte[] strLen = writeVarNum(length);
packet.putBytes(strLen);
packet.putBytes(bytes);
}
/**
* Writes a standard UTF-8 string to a packet container
*
* @param string The String to write
* @param packet The {@link Packet} for this String to be written to
*/
public static void writeString(String string, Packet packet) {
writeString(string, UTF_8, packet);
}
/**
* Reads a Variable Length integer from the {@link Client}, restarting when complete
*
* @param client The {@link Client} to read from
* @param callback The {@link IntConsumer} callback for the read VarInt
*/
public static void readVarIntAlways(Client client, IntConsumer callback) {
client.readByteAlways(new ByteConsumer() {
int count;
long value;
@Override
public void accept(byte data) {
value |= ((int) data & 0x7F) << (7 * count);
if (++count > 5) throw new RuntimeException("VarNum is too big");
if ((data & 0x80) == 0) {
callback.accept((int) value);
count = 0;
value = 0;
}
}
});
}
}

View File

@ -1,8 +1,5 @@
package fr.themode.minestom.net;
import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet;
import fr.adamaq01.ozao.net.server.Connection;
import fr.themode.minestom.Main;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.PacketReader;
@ -13,17 +10,16 @@ import fr.themode.minestom.net.packet.client.handler.ClientPlayPacketsHandler;
import fr.themode.minestom.net.packet.client.handler.ClientStatusPacketsHandler;
import fr.themode.minestom.net.packet.client.handshake.HandshakePacket;
import fr.themode.minestom.net.player.PlayerConnection;
import simplenet.Client;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static fr.themode.minestom.net.protocol.MinecraftProtocol.PACKET_ID_IDENTIFIER;
public class PacketProcessor {
private Map<Connection, PlayerConnection> connectionPlayerConnectionMap = new HashMap<>();
private Map<Client, PlayerConnection> connectionPlayerConnectionMap = new HashMap<>();
private ConnectionManager connectionManager;
@ -42,25 +38,20 @@ public class PacketProcessor {
private List<Integer> printBlackList = Arrays.asList(17, 18, 19);
public void process(Connection connection, Packet packet) {
int id = packet.get(PACKET_ID_IDENTIFIER);
Buffer buffer = packet.getPayload();
connectionPlayerConnectionMap.get(connection);
PlayerConnection playerConnection = connectionPlayerConnectionMap.computeIfAbsent(connection, c -> new PlayerConnection(c));
public void process(Client client, int id, int length, int offset) {
PlayerConnection playerConnection = connectionPlayerConnectionMap.computeIfAbsent(client, c -> new PlayerConnection(client));
ConnectionState connectionState = playerConnection.getConnectionState();
if (!printBlackList.contains(id)) {
//System.out.println("RECEIVED ID: 0x" + Integer.toHexString(id) + " State: " + connectionState);
}
PacketReader packetReader = new PacketReader(buffer);
PacketReader packetReader = new PacketReader(client, length, offset);
if (connectionState == ConnectionState.UNKNOWN) {
// Should be handshake packet
if (id == 0) {
HandshakePacket handshakePacket = new HandshakePacket();
handshakePacket.read(packetReader);
handshakePacket.process(playerConnection, connectionManager);
handshakePacket.read(packetReader, () -> handshakePacket.process(playerConnection, connectionManager));
}
return;
}
@ -69,18 +60,15 @@ public class PacketProcessor {
case PLAY:
Player player = connectionManager.getPlayer(playerConnection);
ClientPlayPacket playPacket = (ClientPlayPacket) playPacketsHandler.getPacketInstance(id);
playPacket.read(packetReader);
player.addPacketToQueue(playPacket); // Processed during player tick update
playPacket.read(packetReader, () -> player.addPacketToQueue(playPacket));
break;
case LOGIN:
ClientPreplayPacket loginPacket = (ClientPreplayPacket) loginPacketsHandler.getPacketInstance(id);
loginPacket.read(packetReader);
loginPacket.process(playerConnection, connectionManager);
loginPacket.read(packetReader, () -> loginPacket.process(playerConnection, connectionManager));
break;
case STATUS:
ClientPreplayPacket statusPacket = (ClientPreplayPacket) statusPacketsHandler.getPacketInstance(id);
statusPacket.read(packetReader);
statusPacket.process(playerConnection, connectionManager);
statusPacket.read(packetReader, () -> statusPacket.process(playerConnection, connectionManager));
break;
case UNKNOWN:
// Ignore packet (unexpected)
@ -88,15 +76,15 @@ public class PacketProcessor {
}
}
public PlayerConnection getPlayerConnection(Connection connection) {
return connectionPlayerConnectionMap.get(connection);
public PlayerConnection getPlayerConnection(Client client) {
return connectionPlayerConnectionMap.get(client);
}
public boolean hasPlayerConnection(Connection connection) {
return connectionPlayerConnectionMap.containsKey(connection);
public boolean hasPlayerConnection(Client client) {
return connectionPlayerConnectionMap.containsKey(client);
}
public void removePlayerConnection(Connection connection) {
connectionPlayerConnectionMap.remove(connection);
public void removePlayerConnection(Client client) {
connectionPlayerConnectionMap.remove(client);
}
}

View File

@ -1,12 +1,11 @@
package fr.themode.minestom.net;
import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet;
import fr.themode.minestom.Main;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.net.player.PlayerConnection;
import fr.themode.minestom.utils.PacketUtils;
import simplenet.packet.Packet;
import java.util.Collection;
import java.util.concurrent.ExecutorService;
@ -15,12 +14,11 @@ import java.util.function.Consumer;
public class PacketWriterUtils {
private static volatile ExecutorService batchesPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_PACKET_WRITER);
private static ExecutorService batchesPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_PACKET_WRITER);
public static void writeCallbackPacket(ServerPacket serverPacket, Consumer<Buffer> consumer) {
public static void writeCallbackPacket(ServerPacket serverPacket, Consumer<Packet> consumer) {
batchesPool.execute(() -> {
Packet p = PacketUtils.writePacket(serverPacket);
consumer.accept(PacketUtils.encode(p));
PacketUtils.writePacket(serverPacket, packet -> consumer.accept(packet));
});
}
@ -30,22 +28,17 @@ public class PacketWriterUtils {
if (size == 0)
return;
Packet p = PacketUtils.writePacket(serverPacket);
Buffer encoded = PacketUtils.encode(p);
encoded.getData().retain(size).markReaderIndex();
for (Player player : players) {
player.getPlayerConnection().writeUnencodedPacket(encoded);
encoded.getData().resetReaderIndex();
}
PacketUtils.writePacket(serverPacket, packet -> {
for (Player player : players) {
player.getPlayerConnection().writeUnencodedPacket(packet);
}
});
});
}
public static void writeAndSend(PlayerConnection playerConnection, ServerPacket serverPacket) {
batchesPool.execute(() -> {
Packet p = PacketUtils.writePacket(serverPacket);
Buffer encoded = PacketUtils.encode(p);
playerConnection.writeUnencodedPacket(encoded);
PacketUtils.writePacket(serverPacket, packet -> playerConnection.sendPacket(packet));
});
}

View File

@ -1,55 +1,88 @@
package fr.themode.minestom.net.packet;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.ConnectionUtils;
import fr.themode.minestom.utils.BlockPosition;
import fr.themode.minestom.utils.Utils;
import fr.themode.minestom.utils.consumer.StringConsumer;
import simplenet.Client;
import simplenet.utility.exposed.consumer.BooleanConsumer;
import simplenet.utility.exposed.consumer.ByteConsumer;
import simplenet.utility.exposed.consumer.FloatConsumer;
import simplenet.utility.exposed.consumer.ShortConsumer;
import java.util.function.Consumer;
import java.util.function.DoubleConsumer;
import java.util.function.IntConsumer;
import java.util.function.LongConsumer;
public class PacketReader {
private Buffer buffer;
private Client client;
private int length;
private int sizeOffset;
public PacketReader(Buffer buffer) {
this.buffer = buffer;
public PacketReader(Client client, int length, int sizeOffset) {
this.client = client;
this.length = length;
this.sizeOffset = sizeOffset;
}
public int readVarInt() {
return Utils.readVarInt(buffer);
public void readVarInt(IntConsumer consumer) {
ConnectionUtils.readVarInt(client, value -> {
consumer.accept(value);
sizeOffset += Utils.lengthVarInt(value);
});
}
public boolean readBoolean() {
return buffer.getBoolean();
public void readBoolean(BooleanConsumer consumer) {
sizeOffset += Byte.BYTES;
client.readBoolean(consumer);
}
public byte readByte() {
return buffer.getByte();
public void readByte(ByteConsumer consumer) {
sizeOffset += Byte.BYTES;
client.readByte(consumer);
}
public short readShort() {
return buffer.getShort();
public void readShort(ShortConsumer consumer) {
sizeOffset += Short.BYTES;
client.readShort(consumer);
}
public long readLong() {
return buffer.getLong();
public void readLong(LongConsumer consumer) {
sizeOffset += Long.BYTES;
client.readLong(consumer);
}
public float readFloat() {
return buffer.getFloat();
public void readFloat(FloatConsumer consumer) {
sizeOffset += Float.BYTES;
client.readFloat(consumer);
}
public double readDouble() {
return buffer.getDouble();
public void readDouble(DoubleConsumer consumer) {
sizeOffset += Double.BYTES;
client.readDouble(consumer);
}
public String readSizedString() {
return Utils.readString(buffer);
public void readSizedString(StringConsumer consumer) {
Utils.readString(client, consumer);
}
public byte[] getRemainingBytes() {
return buffer.getAllBytes();
public void readSizedString(Consumer<String> consumer) {
readSizedString((string, length1) -> consumer.accept(string));
}
public BlockPosition readBlockPosition() {
return Utils.readPosition(buffer);
public void getRemainingBytes(int offset, Consumer<byte[]> consumer) {
int size = length - 1 - offset;
client.readBytes(size, consumer);
}
public void getRemainingBytes(Consumer<byte[]> consumer) {
getRemainingBytes(0, consumer);
}
public void readBlockPosition(Consumer<BlockPosition> consumer) {
Utils.readPosition(client, consumer);
}
}

View File

@ -1,54 +1,55 @@
package fr.themode.minestom.net.packet;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.utils.BlockPosition;
import fr.themode.minestom.utils.Utils;
import simplenet.packet.Packet;
import java.util.UUID;
import java.util.function.Consumer;
public class PacketWriter {
private Buffer buffer;
private Packet packet;
public PacketWriter(Buffer buffer) {
this.buffer = buffer;
public PacketWriter(Packet packet) {
this.packet = packet;
}
public void writeBoolean(boolean b) {
buffer.putBoolean(b);
packet.putBoolean(b);
}
public void writeByte(byte b) {
buffer.putByte(b);
packet.putByte(b);
}
public void writeShort(short s) {
buffer.putShort(s);
packet.putShort(s);
}
public void writeInt(int i) {
buffer.putInt(i);
packet.putInt(i);
}
public void writeLong(long l) {
buffer.putLong(l);
packet.putLong(l);
}
public void writeFloat(float f) {
buffer.putFloat(f);
packet.putFloat(f);
}
public void writeDouble(double d) {
buffer.putDouble(d);
packet.putDouble(d);
}
public void writeVarInt(int i) {
Utils.writeVarInt(buffer, i);
Utils.writeVarInt(packet, i);
}
public void writeSizedString(String string) {
Utils.writeString(buffer, string);
Utils.writeString(packet, string);
}
public void writeVarIntArray(int[] array) {
@ -63,7 +64,7 @@ public class PacketWriter {
}
public void writeBytes(byte[] bytes) {
buffer.putBytes(bytes);
packet.putBytes(bytes);
}
public void writeStringArray(String[] array) {
@ -77,8 +78,9 @@ public class PacketWriter {
}
}
public void writeBuffer(Buffer buffer) {
this.buffer.putBuffer(buffer);
public void write(Consumer<Packet> consumer) {
if (consumer != null)
consumer.accept(packet);
}
public void writeUuid(UUID uuid) {
@ -87,15 +89,15 @@ public class PacketWriter {
}
public void writeBlockPosition(BlockPosition blockPosition) {
Utils.writePosition(buffer, blockPosition);
Utils.writePosition(packet, blockPosition);
}
public void writeBlockPosition(int x, int y, int z) {
Utils.writePosition(buffer, x, y, z);
Utils.writePosition(packet, x, y, z);
}
public void writeItemStack(ItemStack itemStack) {
Utils.writeItemStack(buffer, itemStack);
Utils.writeItemStack(packet, itemStack);
}
}

View File

@ -4,6 +4,6 @@ import fr.themode.minestom.net.packet.PacketReader;
public interface ClientPacket {
void read(PacketReader reader);
void read(PacketReader reader, Runnable callback);
}

View File

@ -3,21 +3,24 @@ package fr.themode.minestom.net.packet.client.handler;
import com.esotericsoftware.reflectasm.ConstructorAccess;
import fr.themode.minestom.net.packet.client.ClientPacket;
import java.util.HashMap;
import java.util.Map;
public class ClientPacketsHandler {
private Map<Integer, ConstructorAccess<? extends ClientPacket>> constructorAccessMap = new HashMap<>();
private static final int SIZE = 0xFF;
private ConstructorAccess[] constructorAccesses = new ConstructorAccess[SIZE];
public void register(int id, Class<? extends ClientPacket> packet) {
this.constructorAccessMap.put(id, ConstructorAccess.get(packet));
constructorAccesses[id] = ConstructorAccess.get(packet);
}
public ClientPacket getPacketInstance(int id) {
ClientPacket packet = constructorAccessMap.get(id).newInstance();
if (packet == null)
if (id > SIZE)
throw new IllegalStateException("Packet ID 0x" + Integer.toHexString(id) + " has been tried to be parsed, debug needed");
ConstructorAccess<? extends ClientPacket> constructorAccess = constructorAccesses[id];
if (constructorAccess == null)
System.err.println("Packet id 0x" + Integer.toHexString(id) + " isn't registered!");
ClientPacket packet = constructorAccess.newInstance();
return packet;
}

View File

@ -8,14 +8,20 @@ import fr.themode.minestom.net.player.PlayerConnection;
public class HandshakePacket implements ClientPreplayPacket {
private int protocolVersion;
private String serverAddress;
private short serverPort;
private int nextState;
@Override
public void read(PacketReader reader) {
int protocolVersion = reader.readVarInt();
String serverAddress = reader.readSizedString();
short serverPort = reader.readShort();
this.nextState = reader.readVarInt();
public void read(PacketReader reader, Runnable callback) {
reader.readVarInt(value -> protocolVersion = value);
reader.readSizedString(s -> serverAddress = s);
reader.readShort(value -> serverPort = value);
reader.readVarInt(value -> {
nextState = value;
callback.run();
});
}
@Override

View File

@ -106,7 +106,10 @@ public class LoginStartPacket implements ClientPreplayPacket {
}
@Override
public void read(PacketReader reader) {
this.username = reader.readSizedString();
public void read(PacketReader reader, Runnable callback) {
reader.readSizedString(s -> {
username = s;
callback.run();
});
}
}

View File

@ -9,7 +9,10 @@ public class ClientAnimationPacket extends ClientPlayPacket {
public Player.Hand hand;
@Override
public void read(PacketReader reader) {
this.hand = Player.Hand.values()[reader.readVarInt()];
public void read(PacketReader reader, Runnable callback) {
reader.readVarInt(value -> {
this.hand = Player.Hand.values()[value];
callback.run();
});
}
}

View File

@ -8,7 +8,10 @@ public class ClientChatMessagePacket extends ClientPlayPacket {
public String message;
@Override
public void read(PacketReader reader) {
this.message = reader.readSizedString();
public void read(PacketReader reader, Runnable callback) {
reader.readSizedString(s -> {
message = s;
callback.run();
});
}
}

View File

@ -13,12 +13,15 @@ public class ClientClickWindowPacket extends ClientPlayPacket {
// TODO clicked item
@Override
public void read(PacketReader reader) {
this.windowId = reader.readByte();
this.slot = reader.readShort();
this.button = reader.readByte();
this.actionNumber = reader.readShort();
this.mode = reader.readVarInt();
public void read(PacketReader reader, Runnable callback) {
reader.readByte(value -> windowId = value);
reader.readShort(value -> slot = value);
reader.readByte(value -> button = value);
reader.readShort(value -> actionNumber = value);
reader.readVarInt(value -> {
mode = value;
callback.run();
});
// TODO read clicked item
}
}

View File

@ -8,7 +8,10 @@ public class ClientCloseWindow extends ClientPlayPacket {
public int windowId;
@Override
public void read(PacketReader reader) {
this.windowId = reader.readVarInt();
public void read(PacketReader reader, Runnable callback) {
reader.readVarInt(value -> {
windowId = value;
callback.run();
});
}
}

View File

@ -10,7 +10,8 @@ public class ClientConfirmTransactionPacket extends ClientPlayPacket {
public boolean accepted;
@Override
public void read(PacketReader reader) {
public void read(PacketReader reader, Runnable callback) {
callback.run();
// TODO
}
}

View File

@ -10,10 +10,13 @@ public class ClientEntityActionPacket extends ClientPlayPacket {
public int horseJumpBoost;
@Override
public void read(PacketReader reader) {
this.playerId = reader.readVarInt();
this.action = Action.values()[reader.readVarInt()];
this.horseJumpBoost = reader.readVarInt();
public void read(PacketReader reader, Runnable callback) {
reader.readVarInt(value -> playerId = value);
reader.readVarInt(value -> action = Action.values()[value]);
reader.readVarInt(value -> {
horseJumpBoost = value;
callback.run();
});
}
public enum Action {

View File

@ -8,7 +8,10 @@ public class ClientHeldItemChangePacket extends ClientPlayPacket {
public short slot;
@Override
public void read(PacketReader reader) {
this.slot = reader.readShort();
public void read(PacketReader reader, Runnable callback) {
reader.readShort(value -> {
slot = value;
callback.run();
});
}
}

View File

@ -8,7 +8,10 @@ public class ClientKeepAlivePacket extends ClientPlayPacket {
public long id;
@Override
public void read(PacketReader reader) {
this.id = reader.readLong();
public void read(PacketReader reader, Runnable callback) {
reader.readLong(value -> {
id = value;
callback.run();
});
}
}

View File

@ -10,9 +10,12 @@ public class ClientPlayerAbilitiesPacket extends ClientPlayPacket {
public float walkingSpeed;
@Override
public void read(PacketReader reader) {
this.flags = reader.readByte();
this.flyingSpeed = reader.readFloat();
this.walkingSpeed = reader.readFloat();
public void read(PacketReader reader, Runnable callback) {
reader.readByte(value -> flags = value);
reader.readFloat(value -> flyingSpeed = value);
reader.readFloat(value -> {
walkingSpeed = value;
callback.run();
});
}
}

View File

@ -14,14 +14,17 @@ public class ClientPlayerBlockPlacementPacket extends ClientPlayPacket {
public boolean insideBlock;
@Override
public void read(PacketReader reader) {
this.hand = Player.Hand.values()[reader.readVarInt()];
this.blockPosition = reader.readBlockPosition();
this.blockFace = ClientPlayerDiggingPacket.BlockFace.values()[reader.readVarInt()];
this.cursorPositionX = reader.readFloat();
this.cursorPositionY = reader.readFloat();
this.cursorPositionZ = reader.readFloat();
this.insideBlock = reader.readBoolean();
public void read(PacketReader reader, Runnable callback) {
reader.readVarInt(value -> hand = Player.Hand.values()[value]);
reader.readBlockPosition(blockPosition1 -> blockPosition = blockPosition1);
reader.readVarInt(value -> blockFace = ClientPlayerDiggingPacket.BlockFace.values()[value]);
reader.readFloat(value -> cursorPositionX = value);
reader.readFloat(value -> cursorPositionY = value);
reader.readFloat(value -> cursorPositionZ = value);
reader.readBoolean(value -> {
insideBlock = value;
callback.run();
});
}
}

View File

@ -11,10 +11,13 @@ public class ClientPlayerDiggingPacket extends ClientPlayPacket {
public BlockFace blockFace;
@Override
public void read(PacketReader reader) {
this.status = Status.values()[reader.readVarInt()];
this.blockPosition = reader.readBlockPosition();
this.blockFace = BlockFace.values()[reader.readVarInt()];
public void read(PacketReader reader, Runnable callback) {
reader.readVarInt(value -> status = Status.values()[value]);
reader.readBlockPosition(blockPosition1 -> blockPosition = blockPosition1);
reader.readVarInt(value -> {
blockFace = BlockFace.values()[value];
callback.run();
});
}
public enum Status {

View File

@ -9,9 +9,12 @@ public class ClientPlayerLookPacket extends ClientPlayPacket {
public boolean onGround;
@Override
public void read(PacketReader reader) {
this.yaw = reader.readFloat();
this.pitch = reader.readFloat();
this.onGround = reader.readBoolean();
public void read(PacketReader reader, Runnable callback) {
reader.readFloat(value -> yaw = value);
reader.readFloat(value -> pitch = value);
reader.readBoolean(value -> {
onGround = value;
callback.run();
});
}
}

View File

@ -8,7 +8,10 @@ public class ClientPlayerPacket extends ClientPlayPacket {
public boolean onGround;
@Override
public void read(PacketReader reader) {
this.onGround = reader.readBoolean();
public void read(PacketReader reader, Runnable callback) {
reader.readBoolean(value -> {
onGround = value;
callback.run();
});
}
}

View File

@ -10,12 +10,15 @@ public class ClientPlayerPositionAndLookPacket extends ClientPlayPacket {
public boolean onGround;
@Override
public void read(PacketReader reader) {
this.x = reader.readDouble();
this.y = reader.readDouble();
this.z = reader.readDouble();
this.yaw = reader.readFloat();
this.pitch = reader.readFloat();
this.onGround = reader.readBoolean();
public void read(PacketReader reader, Runnable callback) {
reader.readDouble(value -> x = value);
reader.readDouble(value -> y = value);
reader.readDouble(value -> z = value);
reader.readFloat(value -> yaw = value);
reader.readFloat(value -> pitch = value);
reader.readBoolean(value -> {
onGround = value;
callback.run();
});
}
}

View File

@ -9,10 +9,13 @@ public class ClientPlayerPositionPacket extends ClientPlayPacket {
public boolean onGround;
@Override
public void read(PacketReader reader) {
this.x = reader.readDouble();
this.y = reader.readDouble();
this.z = reader.readDouble();
this.onGround = reader.readBoolean();
public void read(PacketReader reader, Runnable callback) {
reader.readDouble(value -> x = value);
reader.readDouble(value -> y = value);
reader.readDouble(value -> z = value);
reader.readBoolean(value -> {
onGround = value;
callback.run();
});
}
}

View File

@ -9,8 +9,13 @@ public class ClientPluginMessagePacket extends ClientPlayPacket {
private byte[] data;
@Override
public void read(PacketReader reader) {
this.identifier = reader.readSizedString();
this.data = reader.getRemainingBytes();
public void read(PacketReader reader, Runnable callback) {
reader.readSizedString((s, l) -> {
identifier = s;
reader.getRemainingBytes(l, bytes -> {
data = bytes;
callback.run();
});
});
}
}

View File

@ -14,12 +14,15 @@ public class ClientSettingsPacket extends ClientPlayPacket {
public Player.MainHand mainHand;
@Override
public void read(PacketReader reader) {
this.locale = reader.readSizedString();
this.viewDistance = reader.readByte();
this.chatMode = Player.ChatMode.values()[reader.readVarInt()];
this.chatColors = reader.readBoolean();
this.displayedSkinParts = reader.readByte();
this.mainHand = Player.MainHand.values()[reader.readVarInt()];
public void read(PacketReader reader, Runnable callback) {
reader.readSizedString(s -> locale = s);
reader.readByte(value -> viewDistance = value);
reader.readVarInt(value -> chatMode = Player.ChatMode.values()[value]);
reader.readBoolean(value -> chatColors = value);
reader.readByte(value -> displayedSkinParts = value);
reader.readVarInt(value -> {
mainHand = Player.MainHand.values()[value];
callback.run();
});
}
}

View File

@ -8,8 +8,11 @@ public class ClientStatusPacket extends ClientPlayPacket {
public Action action;
@Override
public void read(PacketReader reader) {
this.action = Action.values()[reader.readVarInt()];
public void read(PacketReader reader, Runnable callback) {
reader.readVarInt(value -> {
action = Action.values()[value];
callback.run();
});
}
public enum Action {

View File

@ -10,9 +10,12 @@ public class ClientSteerVehiclePacket extends ClientPlayPacket {
public byte flags;
@Override
public void read(PacketReader reader) {
this.sideways = reader.readFloat();
this.forward = reader.readFloat();
this.flags = reader.readByte();
public void read(PacketReader reader, Runnable callback) {
reader.readFloat(value -> sideways = value);
reader.readFloat(value -> forward = value);
reader.readByte(value -> {
flags = value;
callback.run();
});
}
}

View File

@ -8,7 +8,10 @@ public class ClientTeleportConfirmPacket extends ClientPlayPacket {
public int teleportId;
@Override
public void read(PacketReader reader) {
this.teleportId = reader.readVarInt();
public void read(PacketReader reader, Runnable callback) {
reader.readVarInt(value -> {
teleportId = value;
callback.run();
});
}
}

View File

@ -14,16 +14,26 @@ public class ClientUseEntityPacket extends ClientPlayPacket {
public Player.Hand hand;
@Override
public void read(PacketReader reader) {
this.targetId = reader.readVarInt();
this.type = Type.values()[reader.readVarInt()];
public void read(PacketReader reader, Runnable callback) {
reader.readVarInt(value -> targetId = value);
reader.readVarInt(value -> {
type = Type.values()[value];
if (type == Type.ATTACK)
callback.run();
});
if (this.type == Type.INTERACT_AT) {
this.x = reader.readFloat();
this.y = reader.readFloat();
this.z = reader.readFloat();
reader.readFloat(value -> x = value);
reader.readFloat(value -> y = value);
reader.readFloat(value -> {
z = value;
});
}
if (type == Type.INTERACT || type == Type.INTERACT_AT)
this.hand = Player.Hand.values()[reader.readVarInt()];
reader.readVarInt(value -> {
hand = Player.Hand.values()[value];
callback.run();
});
}
public enum Type {

View File

@ -9,7 +9,10 @@ public class ClientUseItemPacket extends ClientPlayPacket {
public Player.Hand hand;
@Override
public void read(PacketReader reader) {
this.hand = Player.Hand.values()[reader.readVarInt()];
public void read(PacketReader reader, Runnable callback) {
reader.readVarInt(value -> {
hand = Player.Hand.values()[value];
callback.run();
});
}
}

View File

@ -0,0 +1,24 @@
package fr.themode.minestom.net.packet.client.status;
import fr.themode.minestom.net.ConnectionManager;
import fr.themode.minestom.net.packet.PacketReader;
import fr.themode.minestom.net.packet.client.ClientPreplayPacket;
import fr.themode.minestom.net.player.PlayerConnection;
public class LegacyServerListPingPacket implements ClientPreplayPacket {
private byte payload;
@Override
public void process(PlayerConnection connection, ConnectionManager connectionManager) {
}
@Override
public void read(PacketReader reader, Runnable callback) {
reader.readByte(value -> {
payload = value;
callback.run();
});
}
}

View File

@ -14,11 +14,14 @@ public class PingPacket implements ClientPreplayPacket {
public void process(PlayerConnection connection, ConnectionManager connectionManager) {
PongPacket pongPacket = new PongPacket(number);
connection.sendPacket(pongPacket);
connection.getConnection().close();
connection.getClient().close();
}
@Override
public void read(PacketReader reader) {
this.number = reader.readLong();
public void read(PacketReader reader, Runnable callback) {
reader.readLong(value -> {
number = value;
callback.run();
});
}
}

View File

@ -15,7 +15,8 @@ public class StatusRequestPacket implements ClientPreplayPacket {
}
@Override
public void read(PacketReader reader) {
public void read(PacketReader reader, Runnable callback) {
callback.run();
// Empty
}
}

View File

@ -77,7 +77,7 @@ public class ChunkDataPacket implements ServerPacket {
}
writer.writeVarInt(blocks.length());
writer.writeBuffer(blocks);
writer.writeBytes(blocks.getAllBytes());
// Block entities
Set<Integer> blockEntities = chunk.getBlockEntities();

View File

@ -1,18 +1,20 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.PacketWriter;
import fr.themode.minestom.net.packet.server.ServerPacket;
import simplenet.packet.Packet;
import java.util.function.Consumer;
public class EntityMetaDataPacket implements ServerPacket {
public int entityId;
public Buffer data;
public Consumer<Packet> consumer;
@Override
public void write(PacketWriter writer) {
writer.writeVarInt(entityId);
writer.writeBuffer(data);
writer.write(consumer);
writer.writeByte((byte) 0xFF);
}

View File

@ -1,11 +1,12 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.PacketWriter;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Position;
import simplenet.packet.Packet;
import java.util.UUID;
import java.util.function.Consumer;
public class SpawnMobPacket implements ServerPacket {
@ -15,7 +16,7 @@ public class SpawnMobPacket implements ServerPacket {
public Position position;
public float headPitch;
public short velocityX, velocityY, velocityZ;
public Buffer metadata;
public Consumer<Packet> consumer;
@Override
public void write(PacketWriter writer) {
@ -31,8 +32,8 @@ public class SpawnMobPacket implements ServerPacket {
writer.writeShort(velocityX);
writer.writeShort(velocityY);
writer.writeShort(velocityZ);
if (metadata != null) {
writer.writeBuffer(metadata);
if (consumer != null) {
writer.write(consumer);
} else {
writer.writeByte((byte) 0xff);
}

View File

@ -1,18 +1,19 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.PacketWriter;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Position;
import simplenet.packet.Packet;
import java.util.UUID;
import java.util.function.Consumer;
public class SpawnPlayerPacket implements ServerPacket {
public int entityId;
public UUID playerUuid;
public Position position;
public Buffer metadata;
public Consumer<Packet> metadataConsumer;
@Override
public void write(PacketWriter writer) {
@ -24,8 +25,8 @@ public class SpawnPlayerPacket implements ServerPacket {
writer.writeByte((byte) (position.getYaw() * 256f / 360f));
writer.writeByte((byte) (position.getPitch() * 256f / 360f));
if (metadata != null) {
writer.writeBuffer(metadata);
if (metadataConsumer != null) {
writer.write(metadataConsumer);
} else {
writer.writeByte((byte) 0xff);
}

View File

@ -1,63 +1,40 @@
package fr.themode.minestom.net.player;
import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet;
import fr.adamaq01.ozao.net.server.Connection;
import fr.themode.minestom.net.ConnectionState;
import fr.themode.minestom.net.PacketWriterUtils;
import fr.themode.minestom.net.packet.server.ServerPacket;
import io.netty.channel.Channel;
import io.netty.channel.socket.SocketChannel;
import java.lang.reflect.Field;
import fr.themode.minestom.utils.PacketUtils;
import simplenet.Client;
import simplenet.packet.Packet;
public class PlayerConnection {
private Connection connection;
private Client client;
private ConnectionState connectionState;
public PlayerConnection(Connection connection) {
this.connection = connection;
public PlayerConnection(Client client) {
this.client = client;
this.connectionState = ConnectionState.UNKNOWN;
}
public void sendPacket(Packet packet) {
this.connection.sendPacket(packet);
packet.writeAndFlush(client);
}
// TODO make that proper (remove reflection)
private static Field field;
static {
try {
field = Class.forName("fr.adamaq01.ozao.net.server.backend.tcp.TCPConnection").getDeclaredField("channel");
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
field.setAccessible(true);
}
public void sendUnencodedPacket(Buffer packet) {
getChannel().writeAndFlush(packet.getData());
}
public void writeUnencodedPacket(Buffer packet) {
getChannel().write(packet.getData());
public void writeUnencodedPacket(Packet packet) {
packet.write(client);
}
public void sendPacket(ServerPacket serverPacket) {
PacketWriterUtils.writeAndSend(this, serverPacket);
PacketUtils.writePacket(serverPacket, packet -> sendPacket(packet));
//PacketWriterUtils.writeAndSend(this, serverPacket);
}
public void flush() {
getChannel().flush();
client.flush();
}
public Connection getConnection() {
return connection;
public Client getClient() {
return client;
}
public void setConnectionState(ConnectionState connectionState) {
@ -67,13 +44,4 @@ public class PlayerConnection {
public ConnectionState getConnectionState() {
return connectionState;
}
private Channel getChannel() {
try {
return ((SocketChannel) field.get(connection));
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -1,61 +0,0 @@
package fr.themode.minestom.net.protocol;
import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet;
import fr.adamaq01.ozao.net.protocol.Protocol;
import fr.themode.minestom.utils.PacketUtils;
import java.util.ArrayList;
import java.util.Collection;
import static fr.themode.minestom.utils.Utils.readVarInt;
public class MinecraftProtocol extends Protocol {
public static final String PACKET_ID_IDENTIFIER = "id";
public MinecraftProtocol() {
super("minecraft");
}
@Override
public boolean verify(Buffer buffer) {
int length = readVarInt(buffer);
int realLength = buffer.slice(buffer.readerIndex()).length();
int id = readVarInt(buffer);
buffer.readerIndex(0);
return length == realLength && id >= 0;
}
@Override
public boolean verify(Packet packet) {
return PacketUtils.verify(packet);
}
@Override
public Collection<Buffer> cut(Buffer buffer) {
ArrayList<Buffer> buffers = new ArrayList<>();
int read = 0;
while (read < buffer.length()) {
int lengthLength = buffer.readerIndex(read).readerIndex();
int length = readVarInt(buffer);
lengthLength = buffer.readerIndex() - lengthLength;
buffers.add(buffer.sliceCopy(read, length + lengthLength));
read += length + lengthLength;
}
return buffers;
}
@Override
public Packet decode(Buffer buffer) {
int length = readVarInt(buffer);
int id = readVarInt(buffer);
Buffer packetPayload = buffer.sliceCopy(buffer.readerIndex());
return Packet.create(packetPayload).put(PACKET_ID_IDENTIFIER, id);
}
@Override
public Buffer encode(Packet packet) {
return PacketUtils.encode(packet);
}
}

View File

@ -1,40 +1,24 @@
package fr.themode.minestom.utils;
import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet;
import fr.themode.minestom.net.packet.PacketWriter;
import fr.themode.minestom.net.packet.server.ServerPacket;
import simplenet.packet.Packet;
import static fr.themode.minestom.net.protocol.MinecraftProtocol.PACKET_ID_IDENTIFIER;
import static fr.themode.minestom.utils.Utils.writeVarInt;
import java.util.function.Consumer;
public class PacketUtils {
public static Packet writePacket(ServerPacket serverPacket) {
public static void writePacket(ServerPacket serverPacket, Consumer<Packet> callback) {
int id = serverPacket.getId();
Packet packet = Packet.create();
Buffer buffer = packet.getPayload();
PacketWriter packetWriter = new PacketWriter(buffer);
//System.out.println("SEND PACKET: 0x"+Integer.toHexString(id));
Packet packet = Packet.builder();
Utils.writeVarInt(packet, id);
PacketWriter packetWriter = new PacketWriter(packet);
serverPacket.write(packetWriter);
packet.put(PACKET_ID_IDENTIFIER, id);
return packet;
}
public static boolean verify(Packet packet) {
return packet.get("id") != null;
}
public static Buffer encode(Packet packet) {
Buffer buffer = Buffer.create();
Buffer idAndPayload = Buffer.create();
writeVarInt(idAndPayload, packet.get(PACKET_ID_IDENTIFIER));
idAndPayload.putBuffer(packet.getPayload());
writeVarInt(buffer, idAndPayload.length());
buffer.putBuffer(idAndPayload);
return buffer;
callback.accept(packet.prepend(p -> {
Utils.writeVarInt(packet, packet.getSize());
}));
}
}

View File

@ -3,12 +3,17 @@ package fr.themode.minestom.utils;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.chat.Chat;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.ConnectionUtils;
import fr.themode.minestom.utils.consumer.StringConsumer;
import simplenet.Client;
import simplenet.packet.Packet;
import java.io.UnsupportedEncodingException;
import java.util.function.Consumer;
public class Utils {
public static void writeString(Buffer buffer, String value) {
public static void writeString(Packet packet, String value) {
byte[] bytes = new byte[0];
try {
bytes = value.getBytes("UTF-8");
@ -18,23 +23,26 @@ public class Utils {
if (bytes.length > 32767) {
System.out.println("String too big (was " + value.length() + " bytes encoded, max " + 32767 + ")");
} else {
writeVarInt(buffer, bytes.length);
buffer.putBytes(bytes);
writeVarInt(packet, bytes.length);
packet.putBytes(bytes);
}
}
public static String readString(Buffer buffer) {
int length = readVarInt(buffer);
byte bytes[] = buffer.getBytes(length);
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
public static void readString(Client client, StringConsumer consumer) {
ConnectionUtils.readVarInt(client, length -> {
int stringLength = Utils.lengthVarInt(length) + length;
client.readBytes(length, bytes -> {
try {
consumer.accept(new String(bytes, "UTF-8"), stringLength);
} catch (UnsupportedEncodingException e) {
consumer.accept(null, stringLength);
e.printStackTrace();
}
});
});
}
public static void writeVarInt(Buffer buffer, int value) {
public static void writeVarIntBuffer(Buffer buffer, int value) {
do {
byte temp = (byte) (value & 0b01111111);
value >>>= 7;
@ -45,12 +53,23 @@ public class Utils {
} while (value != 0);
}
public static int readVarInt(Buffer buffer) {
public static void writeVarInt(Packet packet, int value) {
do {
byte temp = (byte) (value & 0b01111111);
value >>>= 7;
if (value != 0) {
temp |= 0b10000000;
}
packet.putByte(temp);
} while (value != 0);
}
public static int readVarInt(Client client) {
int numRead = 0;
int result = 0;
byte read;
do {
read = buffer.getByte();
read = client.readByte();
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
@ -90,23 +109,23 @@ public class Utils {
return i;
}
public static void writeVarLong(Buffer buffer, long value) {
public static void writeVarLong(Packet packet, long value) {
do {
byte temp = (byte) (value & 0b01111111);
value >>>= 7;
if (value != 0) {
temp |= 0b10000000;
}
buffer.putByte(temp);
packet.putByte(temp);
} while (value != 0);
}
public static long readVarLong(Buffer buffer) {
public static long readVarLong(Client client) {
int numRead = 0;
long result = 0;
byte read;
do {
read = buffer.getByte();
read = client.readByte();
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
@ -119,54 +138,56 @@ public class Utils {
return result;
}
public static void writePosition(Buffer buffer, int x, int y, int z) {
buffer.putLong(SerializerUtils.positionToLong(x, y, z));
public static void writePosition(Packet packet, int x, int y, int z) {
packet.putLong(SerializerUtils.positionToLong(x, y, z));
}
public static void writePosition(Buffer buffer, BlockPosition blockPosition) {
writePosition(buffer, blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
public static void writePosition(Packet packet, BlockPosition blockPosition) {
writePosition(packet, blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
}
public static BlockPosition readPosition(Buffer buffer) {
return SerializerUtils.longToBlockPosition(buffer.getLong());
public static void readPosition(Client client, Consumer<BlockPosition> consumer) {
client.readLong(value -> {
consumer.accept(SerializerUtils.longToBlockPosition(value));
});
}
public static void writeItemStack(Buffer buffer, ItemStack itemStack) {
public static void writeItemStack(Packet packet, ItemStack itemStack) {
if (itemStack == null) {
buffer.putBoolean(false);
packet.putBoolean(false);
} else {
buffer.putBoolean(true);
Utils.writeVarInt(buffer, itemStack.getMaterial().getId());
buffer.putByte(itemStack.getAmount());
packet.putBoolean(true);
Utils.writeVarInt(packet, itemStack.getMaterial().getId());
packet.putByte(itemStack.getAmount());
buffer.putByte((byte) 0x0A); // Compound
buffer.putShort((short) 0);
packet.putByte((byte) 0x0A); // Compound
packet.putShort((short) 0);
// Unbreakable
if (itemStack.isUnbreakable()) {
buffer.putByte((byte) 0x03); // Integer
buffer.putString("Unbreakable");
buffer.putInt(1);
packet.putByte((byte) 0x03); // Integer
packet.putString("Unbreakable");
packet.putInt(1);
}
// Display
buffer.putByte((byte) 0x0A); // Compound
buffer.putString("display");
packet.putByte((byte) 0x0A); // Compound
packet.putString("display");
if (itemStack.getDisplayName() != null) {
buffer.putByte((byte) 0x08);
buffer.putString("Name");
buffer.putString(Chat.rawText(itemStack.getDisplayName()));
packet.putByte((byte) 0x08);
packet.putString("Name");
packet.putString(Chat.rawText(itemStack.getDisplayName()));
}
// TODO lore
buffer.putByte((byte) 0x08);
buffer.putString("Lore");
buffer.putString(Chat.rawText("a line"));
packet.putByte((byte) 0x08);
packet.putString("Lore");
packet.putString(Chat.rawText("a line"));
buffer.putByte((byte) 0); // End display compound
packet.putByte((byte) 0); // End display compound
buffer.putByte((byte) 0); // End nbt
packet.putByte((byte) 0); // End nbt
}
}
@ -190,7 +211,7 @@ public class Utils {
}
}
long[] data = encodeBlocks(blocksData, 14);
writeVarInt(buffer, data.length);
writeVarIntBuffer(buffer, data.length);
for (int i = 0; i < data.length; i++) {
buffer.putLong(data[i]);
}

View File

@ -0,0 +1,7 @@
package fr.themode.minestom.utils.consumer;
public interface StringConsumer {
void accept(String string, int length);
}