Reworked packet writer

This commit is contained in:
TheMode 2019-08-23 15:37:38 +02:00
parent c407e384ab
commit d5d3dab6c7
22 changed files with 376 additions and 82 deletions

View File

@ -21,6 +21,13 @@ import java.lang.reflect.InvocationTargetException;
public class Main { public class Main {
// Thread number
public static final int THREAD_COUNT_PACKET_WRITER = 3;
public static final int THREAD_COUNT_CHUNK_BATCH = 2;
public static final int THREAD_COUNT_OBJECTS_ENTITIES = 2;
public static final int THREAD_COUNT_CREATURES_ENTITIES = 2;
public static final int THREAD_COUNT_PLAYERS_ENTITIES = 2;
public static final int TICK_MS = 50; public static final int TICK_MS = 50;
// Networking // Networking
@ -101,12 +108,14 @@ public class Main {
currentTime = System.nanoTime(); currentTime = System.nanoTime();
// Keep Alive Handling // Keep Alive Handling
server.getConnections().stream().filter(connection -> packetProcessor.hasPlayerConnection(connection) && connectionManager.getPlayer(packetProcessor.getPlayerConnection(connection)) != null && System.currentTimeMillis() - connectionManager.getPlayer(packetProcessor.getPlayerConnection(connection)).getLastKeepAlive() > 20000).map(connection -> connectionManager.getPlayer(packetProcessor.getPlayerConnection(connection))).forEach(player -> { for (Player player : getConnectionManager().getOnlinePlayers()) {
long id = System.currentTimeMillis(); if (System.currentTimeMillis() - player.getLastKeepAlive() > 20000) {
player.refreshKeepAlive(id); long id = System.currentTimeMillis();
KeepAlivePacket keepAlivePacket = new KeepAlivePacket(id); player.refreshKeepAlive(id);
player.getPlayerConnection().sendPacket(keepAlivePacket); KeepAlivePacket keepAlivePacket = new KeepAlivePacket(id);
}); player.getPlayerConnection().sendPacket(keepAlivePacket);
}
}
// Entities update // Entities update
entityManager.update(); entityManager.update();

View File

@ -1,6 +1,7 @@
package fr.themode.minestom; package fr.themode.minestom;
import fr.themode.minestom.entity.Player; import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.PacketWriter;
import fr.themode.minestom.net.packet.server.ServerPacket; import fr.themode.minestom.net.packet.server.ServerPacket;
import java.util.Set; import java.util.Set;
@ -21,8 +22,13 @@ public interface Viewable {
if (getViewers().isEmpty()) if (getViewers().isEmpty())
return; return;
//Packet p = PacketUtils.writePacket(packet); PacketWriter.writeCallbackPacket(packet, buffer -> {
getViewers().forEach(player -> player.getPlayerConnection().sendPacket(packet)); buffer.getData().retain(getViewers().size()).markReaderIndex();
getViewers().forEach(player -> {
player.getPlayerConnection().sendUnencodedPacket(buffer);
buffer.getData().resetReaderIndex();
});
});
} }
default void sendPacketsToViewers(ServerPacket... packets) { default void sendPacketsToViewers(ServerPacket... packets) {
@ -30,17 +36,29 @@ public interface Viewable {
return; return;
for (ServerPacket packet : packets) { for (ServerPacket packet : packets) {
//Packet p = PacketUtils.writePacket(packet); PacketWriter.writeCallbackPacket(packet, buffer -> {
getViewers().forEach(player -> player.getPlayerConnection().sendPacket(packet)); buffer.getData().retain(getViewers().size()).markReaderIndex();
getViewers().forEach(player -> {
player.getPlayerConnection().sendUnencodedPacket(buffer);
buffer.getData().resetReaderIndex();
});
});
} }
} }
default void sendPacketToViewersAndSelf(ServerPacket packet) { default void sendPacketToViewersAndSelf(ServerPacket packet) {
if (this instanceof Player) { if (this instanceof Player) {
//Packet p = PacketUtils.writePacket(packet); PacketWriter.writeCallbackPacket(packet, buffer -> {
((Player) this).getPlayerConnection().sendPacket(packet); buffer.getData().retain(getViewers().size() + 1).markReaderIndex();
if (!getViewers().isEmpty()) ((Player) this).getPlayerConnection().sendUnencodedPacket(buffer);
getViewers().forEach(player -> player.getPlayerConnection().sendPacket(packet)); buffer.getData().resetReaderIndex();
if (!getViewers().isEmpty()) {
getViewers().forEach(player -> {
buffer.getData().resetReaderIndex();
player.getPlayerConnection().sendUnencodedPacket(buffer);
});
}
});
} }
} }

View File

@ -225,7 +225,12 @@ public abstract class Entity implements Viewable {
public void setOnFire(boolean fire) { public void setOnFire(boolean fire) {
this.onFire = fire; this.onFire = fire;
sendMetadata(0); sendMetadataIndex(0);
}
public void setNoGravity(boolean noGravity) {
this.noGravity = noGravity;
sendMetadataIndex(5);
} }
public boolean isChunkUnloaded(float x, float z) { public boolean isChunkUnloaded(float x, float z) {
@ -264,12 +269,12 @@ public abstract class Entity implements Viewable {
public void refreshSneaking(boolean sneaking) { public void refreshSneaking(boolean sneaking) {
this.crouched = sneaking; this.crouched = sneaking;
sendMetadata(0); sendMetadataIndex(0);
} }
public void refreshSprinting(boolean sprinting) { public void refreshSprinting(boolean sprinting) {
this.sprinting = sprinting; this.sprinting = sprinting;
sendMetadata(0); sendMetadataIndex(0);
} }
public Position getPosition() { public Position getPosition() {
@ -302,10 +307,11 @@ public abstract class Entity implements Viewable {
Buffer buffer = Buffer.create(); Buffer buffer = Buffer.create();
fillMetadataIndex(buffer, 0); fillMetadataIndex(buffer, 0);
fillMetadataIndex(buffer, 1); fillMetadataIndex(buffer, 1);
fillMetadataIndex(buffer, 5);
return buffer; return buffer;
} }
private void sendMetadata(int index) { protected void sendMetadataIndex(int index) {
Buffer buffer = Buffer.create(); Buffer buffer = Buffer.create();
fillMetadataIndex(buffer, index); fillMetadataIndex(buffer, index);
@ -329,6 +335,9 @@ public abstract class Entity implements Viewable {
case 2: case 2:
fillCustomNameMetaData(buffer); fillCustomNameMetaData(buffer);
break; break;
case 5:
fillNoGravityMetaData(buffer);
break;
} }
} }
@ -367,6 +376,12 @@ public abstract class Entity implements Viewable {
Utils.writeString(buffer, customName); Utils.writeString(buffer, customName);
} }
private void fillNoGravityMetaData(Buffer buffer) {
buffer.putByte((byte) 5);
buffer.putByte(METADATA_BOOLEAN);
buffer.putBoolean(noGravity);
}
private boolean shouldUpdate() { private boolean shouldUpdate() {
return (float) (System.currentTimeMillis() - lastUpdate) >= Main.TICK_MS * 0.9f; // Margin of error return (float) (System.currentTimeMillis() - lastUpdate) >= Main.TICK_MS * 0.9f; // Margin of error
} }

View File

@ -13,9 +13,9 @@ public class EntityManager {
private static InstanceManager instanceManager = Main.getInstanceManager(); private static InstanceManager instanceManager = Main.getInstanceManager();
private ExecutorService objectsPool = Executors.newFixedThreadPool(2); private ExecutorService objectsPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_OBJECTS_ENTITIES);
private ExecutorService creaturesPool = Executors.newFixedThreadPool(2); private ExecutorService creaturesPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_CREATURES_ENTITIES);
private ExecutorService playersPool = Executors.newFixedThreadPool(2); private ExecutorService playersPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_PLAYERS_ENTITIES);
// TODO API for custom thread division ( // TODO API for custom thread division (
public void update() { public void update() {

View File

@ -2,6 +2,7 @@ package fr.themode.minestom.entity;
import fr.adamaq01.ozao.net.Buffer; import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.item.ItemStack; import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.packet.server.play.EntityRelativeMovePacket;
import fr.themode.minestom.utils.Utils; import fr.themode.minestom.utils.Utils;
public class ItemEntity extends ObjectEntity { public class ItemEntity extends ObjectEntity {
@ -16,7 +17,11 @@ public class ItemEntity extends ObjectEntity {
@Override @Override
public void update() { public void update() {
// TODO how to keep items at the same position?
EntityRelativeMovePacket entityRelativeMovePacket = new EntityRelativeMovePacket();
entityRelativeMovePacket.entityId = getEntityId();
entityRelativeMovePacket.onGround = false;
sendPacketToViewers(entityRelativeMovePacket);
} }
@Override @Override
@ -37,6 +42,11 @@ public class ItemEntity extends ObjectEntity {
return itemStack; return itemStack;
} }
public void setItemStack(ItemStack itemStack) {
this.itemStack = itemStack;
sendMetadataIndex(7); // Refresh itemstack for viewers
}
public boolean isPickable() { public boolean isPickable() {
return pickable; return pickable;
} }

View File

@ -1,16 +1,9 @@
package fr.themode.minestom.entity; package fr.themode.minestom.entity;
import fr.themode.minestom.Viewable;
import fr.themode.minestom.net.packet.server.play.SpawnObjectPacket; import fr.themode.minestom.net.packet.server.play.SpawnObjectPacket;
import fr.themode.minestom.net.player.PlayerConnection; import fr.themode.minestom.net.player.PlayerConnection;
import java.util.Collections; public abstract class ObjectEntity extends Entity {
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
public abstract class ObjectEntity extends Entity implements Viewable {
private Set<Player> viewers = new CopyOnWriteArraySet<>();
public ObjectEntity(int entityType) { public ObjectEntity(int entityType) {
super(entityType); super(entityType);
@ -37,9 +30,4 @@ public abstract class ObjectEntity extends Entity implements Viewable {
public void removeViewer(Player player) { public void removeViewer(Player player) {
super.removeViewer(player); super.removeViewer(player);
} }
@Override
public Set<Player> getViewers() {
return Collections.unmodifiableSet(viewers);
}
} }

View File

@ -20,9 +20,9 @@ import fr.themode.minestom.world.Dimension;
import fr.themode.minestom.world.LevelType; import fr.themode.minestom.world.LevelType;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
public class Player extends LivingEntity { public class Player extends LivingEntity {
@ -31,7 +31,7 @@ public class Player extends LivingEntity {
private String username; private String username;
private PlayerConnection playerConnection; private PlayerConnection playerConnection;
private LinkedList<ClientPlayPacket> packets = new LinkedList<>(); private ConcurrentLinkedQueue<ClientPlayPacket> packets = new ConcurrentLinkedQueue<>();
private Dimension dimension; private Dimension dimension;
private GameMode gameMode; private GameMode gameMode;
@ -87,6 +87,7 @@ public class Player extends LivingEntity {
((EntityCreature) entity).kill(); ((EntityCreature) entity).kill();
sendMessage("You killed an entity!"); sendMessage("You killed an entity!");
} }
sendMessage("ATTACK");
/*UpdateHealthPacket updateHealthPacket = new UpdateHealthPacket(); /*UpdateHealthPacket updateHealthPacket = new UpdateHealthPacket();
updateHealthPacket.health = -1f; updateHealthPacket.health = -1f;
updateHealthPacket.food = 5; updateHealthPacket.food = 5;
@ -101,11 +102,9 @@ public class Player extends LivingEntity {
@Override @Override
public void update() { public void update() {
synchronized (packets) { ClientPlayPacket packet = null;
while (!packets.isEmpty()) { while ((packet = packets.poll()) != null) {
ClientPlayPacket packet = packets.pollFirst(); packet.process(this);
packet.process(this);
}
} }
// Target block stage // Target block stage
@ -422,9 +421,7 @@ public class Player extends LivingEntity {
} }
public void addPacketToQueue(ClientPlayPacket packet) { public void addPacketToQueue(ClientPlayPacket packet) {
synchronized (packets) { this.packets.add(packet);
this.packets.add(packet);
}
} }
public void refreshDimension(Dimension dimension) { public void refreshDimension(Dimension dimension) {

View File

@ -0,0 +1,24 @@
package fr.themode.minestom.entity.demo;
import fr.themode.minestom.entity.LivingEntity;
import fr.themode.minestom.entity.ObjectEntity;
public class TestArrow extends ObjectEntity {
private LivingEntity shooter;
public TestArrow(LivingEntity shooter) {
super(2);
this.shooter = shooter;
}
@Override
public int getData() {
return shooter.getEntityId() + 1;
}
@Override
public void update() {
}
}

View File

@ -1,10 +1,14 @@
package fr.themode.minestom.instance; 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.Main;
import fr.themode.minestom.entity.Entity; import fr.themode.minestom.entity.Entity;
import fr.themode.minestom.entity.EntityCreature; import fr.themode.minestom.entity.EntityCreature;
import fr.themode.minestom.entity.ObjectEntity; import fr.themode.minestom.entity.ObjectEntity;
import fr.themode.minestom.entity.Player; import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
import fr.themode.minestom.utils.PacketUtils;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
@ -26,10 +30,14 @@ public class Chunk {
// Block entities // Block entities
private Set<Integer> blockEntities = new CopyOnWriteArraySet<>(); private Set<Integer> blockEntities = new CopyOnWriteArraySet<>();
// Cache
private Buffer fullDataPacket;
public Chunk(Biome biome, int chunkX, int chunkZ) { public Chunk(Biome biome, int chunkX, int chunkZ) {
this.biome = biome; this.biome = biome;
this.chunkX = chunkX; this.chunkX = chunkX;
this.chunkZ = chunkZ; this.chunkZ = chunkZ;
//refreshDataPacket(); // TODO remove
} }
protected void setBlock(byte x, byte y, byte z, short blockId) { protected void setBlock(byte x, byte y, byte z, short blockId) {
@ -130,6 +138,10 @@ public class Chunk {
return Collections.unmodifiableSet(players); return Collections.unmodifiableSet(players);
} }
public Buffer getFullDataPacket() {
return fullDataPacket;
}
private boolean isBlockEntity(short blockId) { private boolean isBlockEntity(short blockId) {
// TODO complete // TODO complete
return blockId == 2033; return blockId == 2033;
@ -145,4 +157,20 @@ public class Chunk {
index |= (z << 12) & 0xF000; index |= (z << 12) & 0xF000;
return index & 0xffff; return index & 0xffff;
} }
public void setFullDataPacket(Buffer fullDataPacket) {
this.fullDataPacket = fullDataPacket;
}
protected ChunkDataPacket getFreshFullDataPacket() {
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
fullDataPacket.chunk = this;
fullDataPacket.fullChunk = true;
return fullDataPacket;
}
protected void refreshDataPacket() {
Packet packet = PacketUtils.writePacket(getFreshFullDataPacket());
this.fullDataPacket = PacketUtils.encode(packet); // TODO write packet buffer in another thread (heavy calculations)
}
} }

View File

@ -1,5 +1,7 @@
package fr.themode.minestom.instance; package fr.themode.minestom.instance;
import fr.themode.minestom.Main;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -7,7 +9,7 @@ import java.util.concurrent.Executors;
public class ChunkBatch implements BlockModifier { public class ChunkBatch implements BlockModifier {
private static volatile ExecutorService batchesPool = Executors.newFixedThreadPool(3); private static volatile ExecutorService batchesPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_CHUNK_BATCH);
private Instance instance; private Instance instance;
private Chunk chunk; private Chunk chunk;
@ -47,6 +49,7 @@ public class ChunkBatch implements BlockModifier {
for (BlockData data : dataList) { for (BlockData data : dataList) {
data.apply(chunk); data.apply(chunk);
} }
chunk.refreshDataPacket(); // TODO partial refresh instead of full
instance.sendChunkUpdate(chunk); // TODO partial chunk data instance.sendChunkUpdate(chunk); // TODO partial chunk data
}); });
} }

View File

@ -1,10 +1,12 @@
package fr.themode.minestom.instance; package fr.themode.minestom.instance;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.entity.Entity; import fr.themode.minestom.entity.Entity;
import fr.themode.minestom.entity.EntityCreature; import fr.themode.minestom.entity.EntityCreature;
import fr.themode.minestom.entity.ObjectEntity; import fr.themode.minestom.entity.ObjectEntity;
import fr.themode.minestom.entity.Player; import fr.themode.minestom.entity.Player;
import fr.themode.minestom.event.BlockBreakEvent; import fr.themode.minestom.event.BlockBreakEvent;
import fr.themode.minestom.net.PacketWriter;
import fr.themode.minestom.net.packet.server.play.ChunkDataPacket; import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
import fr.themode.minestom.net.packet.server.play.DestroyEntitiesPacket; import fr.themode.minestom.net.packet.server.play.DestroyEntitiesPacket;
import fr.themode.minestom.net.packet.server.play.ParticlePacket; import fr.themode.minestom.net.packet.server.play.ParticlePacket;
@ -39,7 +41,10 @@ public class Instance implements BlockModifier {
Chunk chunk = getChunkAt(x, z); Chunk chunk = getChunkAt(x, z);
synchronized (chunk) { synchronized (chunk) {
chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId); chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId);
sendChunkUpdate(chunk); PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
chunk.setFullDataPacket(buffer);
sendChunkUpdate(chunk);
});
} }
} }
@ -48,7 +53,10 @@ public class Instance implements BlockModifier {
Chunk chunk = getChunkAt(x, z); Chunk chunk = getChunkAt(x, z);
synchronized (chunk) { synchronized (chunk) {
chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId); chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId);
sendChunkUpdate(chunk); PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
chunk.setFullDataPacket(buffer);
sendChunkUpdate(chunk);
});
} }
} }
@ -56,6 +64,7 @@ public class Instance implements BlockModifier {
BlockBreakEvent blockBreakEvent = new BlockBreakEvent(blockPosition); BlockBreakEvent blockBreakEvent = new BlockBreakEvent(blockPosition);
player.callEvent(BlockBreakEvent.class, blockBreakEvent); player.callEvent(BlockBreakEvent.class, blockBreakEvent);
if (!blockBreakEvent.isCancelled()) { if (!blockBreakEvent.isCancelled()) {
// TODO blockbreak setBlock result
int x = blockPosition.getX(); int x = blockPosition.getX();
int y = blockPosition.getY(); int y = blockPosition.getY();
int z = blockPosition.getZ(); int z = blockPosition.getZ();
@ -67,7 +76,7 @@ public class Instance implements BlockModifier {
particlePacket.y = y; particlePacket.y = y;
particlePacket.z = z + 0.5f; particlePacket.z = z + 0.5f;
particlePacket.offsetX = 0.4f; particlePacket.offsetX = 0.4f;
particlePacket.offsetY = 0.65f; particlePacket.offsetY = 0.6f;
particlePacket.offsetZ = 0.4f; particlePacket.offsetZ = 0.4f;
particlePacket.particleData = 0.3f; particlePacket.particleData = 0.3f;
particlePacket.particleCount = 75; particlePacket.particleCount = 75;
@ -221,18 +230,29 @@ public class Instance implements BlockModifier {
} }
protected void sendChunkUpdate(Chunk chunk) { protected void sendChunkUpdate(Chunk chunk) {
ChunkDataPacket chunkDataPacket = new ChunkDataPacket(); Buffer chunkData = chunk.getFullDataPacket();
chunkDataPacket.fullChunk = false; chunkData.getData().retain(getPlayers().size()).markReaderIndex();
chunkDataPacket.chunk = chunk; getPlayers().forEach(player -> {
getPlayers().forEach(player -> player.getPlayerConnection().sendPacket(chunkDataPacket)); // TODO write packet buffer in another thread (Chunk packets are heavy) player.getPlayerConnection().sendUnencodedPacket(chunkData);
chunkData.getData().resetReaderIndex();
});
} }
private void sendChunks(Player player) { private void sendChunks(Player player) {
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
chunkDataPacket.fullChunk = true;
for (Chunk chunk : getChunks()) { for (Chunk chunk : getChunks()) {
chunkDataPacket.chunk = chunk; Buffer chunkData = chunk.getFullDataPacket();
player.getPlayerConnection().sendPacket(chunkDataPacket); // TODO write packet buffer in another thread (Chunk packets are heavy) if (chunkData == null) {
PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
buffer.getData().retain(1).markReaderIndex();
player.getPlayerConnection().sendUnencodedPacket(buffer);
buffer.getData().resetReaderIndex();
chunk.setFullDataPacket(buffer);
});
} else {
chunkData.getData().retain(1).markReaderIndex();
player.getPlayerConnection().sendUnencodedPacket(chunkData);
chunkData.getData().resetReaderIndex();
}
} }
} }

View File

@ -14,10 +14,8 @@ public class ChunkGeneratorDemo extends ChunkGenerator {
public void generateChunkData(ChunkBatch batch, int chunkX, int chunkZ) { public void generateChunkData(ChunkBatch batch, int chunkX, int chunkZ) {
for (byte x = 0; x < 16; x++) for (byte x = 0; x < 16; x++)
for (byte z = 0; z < 16; z++) { for (byte z = 0; z < 16; z++) {
if (random.nextInt(10) > 5) { for (byte y = 0; y < 65; y++) {
batch.setBlock(x, (byte) 4, z, (short) 10); batch.setBlock(x, y, z, "custom_block");
} else {
batch.setBlock(x, (byte) 4, z, "custom_block");
} }
} }
} }

View File

@ -7,6 +7,7 @@ import fr.themode.minestom.net.packet.client.play.ClientChatMessagePacket;
public class ChatMessageListener { public class ChatMessageListener {
public static void listener(ClientChatMessagePacket packet, Player player) { public static void listener(ClientChatMessagePacket packet, Player player) {
// TODO commands check
Main.getConnectionManager().getOnlinePlayers().forEach(p -> p.sendMessage(String.format("<%s> %s", player.getUsername(), packet.message))); Main.getConnectionManager().getOnlinePlayers().forEach(p -> p.sendMessage(String.format("<%s> %s", player.getUsername(), packet.message)));
} }

View File

@ -44,14 +44,15 @@ public class PacketProcessor {
public void process(Connection connection, Packet packet) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { public void process(Connection connection, Packet packet) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
int id = packet.get(PACKET_ID_IDENTIFIER); int id = packet.get(PACKET_ID_IDENTIFIER);
if (!printBlackList.contains(id)) {
//System.out.println("RECEIVED ID: 0x" + Integer.toHexString(id));
}
Buffer buffer = packet.getPayload(); Buffer buffer = packet.getPayload();
connectionPlayerConnectionMap.get(connection); connectionPlayerConnectionMap.get(connection);
PlayerConnection playerConnection = connectionPlayerConnectionMap.computeIfAbsent(connection, c -> new PlayerConnection(c)); PlayerConnection playerConnection = connectionPlayerConnectionMap.computeIfAbsent(connection, c -> new PlayerConnection(c));
ConnectionState connectionState = playerConnection.getConnectionState(); ConnectionState connectionState = playerConnection.getConnectionState();
if (!printBlackList.contains(id)) {
System.out.println("RECEIVED ID: 0x" + Integer.toHexString(id) + " State: " + connectionState);
}
if (connectionState == ConnectionState.UNKNOWN) { if (connectionState == ConnectionState.UNKNOWN) {
// Should be handshake packet // Should be handshake packet
if (id == 0) { if (id == 0) {

View File

@ -0,0 +1,24 @@
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.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.PacketUtils;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
public class PacketWriter {
private static volatile ExecutorService batchesPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_PACKET_WRITER);
public static void writeCallbackPacket(ServerPacket serverPacket, Consumer<Buffer> consumer) {
batchesPool.submit(() -> {
Packet p = PacketUtils.writePacket(serverPacket);
consumer.accept(PacketUtils.encode(p));
});
}
}

View File

@ -9,6 +9,7 @@ import fr.themode.minestom.entity.GameMode;
import fr.themode.minestom.entity.ItemEntity; import fr.themode.minestom.entity.ItemEntity;
import fr.themode.minestom.entity.Player; import fr.themode.minestom.entity.Player;
import fr.themode.minestom.entity.demo.ChickenCreature; import fr.themode.minestom.entity.demo.ChickenCreature;
import fr.themode.minestom.entity.demo.TestArrow;
import fr.themode.minestom.instance.Instance; import fr.themode.minestom.instance.Instance;
import fr.themode.minestom.instance.demo.ChunkGeneratorDemo; import fr.themode.minestom.instance.demo.ChunkGeneratorDemo;
import fr.themode.minestom.inventory.PlayerInventory; import fr.themode.minestom.inventory.PlayerInventory;
@ -19,6 +20,7 @@ import fr.themode.minestom.net.ConnectionState;
import fr.themode.minestom.net.packet.client.ClientPreplayPacket; 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.JoinGamePacket;
import fr.themode.minestom.net.packet.server.login.LoginSuccessPacket; import fr.themode.minestom.net.packet.server.login.LoginSuccessPacket;
import fr.themode.minestom.net.packet.server.play.DeclareCommandsPacket;
import fr.themode.minestom.net.packet.server.play.PlayerInfoPacket; import fr.themode.minestom.net.packet.server.play.PlayerInfoPacket;
import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket; import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket;
import fr.themode.minestom.net.packet.server.play.SpawnPositionPacket; import fr.themode.minestom.net.packet.server.play.SpawnPositionPacket;
@ -31,6 +33,9 @@ import java.util.UUID;
public class LoginStartPacket implements ClientPreplayPacket { public class LoginStartPacket implements ClientPreplayPacket {
// Test
private static Instance instance;
static { static {
ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo(); ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo();
instance = Main.getInstanceManager().createInstance(); instance = Main.getInstanceManager().createInstance();
@ -45,9 +50,6 @@ public class LoginStartPacket implements ClientPreplayPacket {
System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms"); System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms");
} }
// Test
private static Instance instance;
public String username; public String username;
@Override @Override
@ -72,7 +74,7 @@ public class LoginStartPacket implements ClientPreplayPacket {
Dimension dimension = Dimension.OVERWORLD; Dimension dimension = Dimension.OVERWORLD;
LevelType levelType = LevelType.DEFAULT; LevelType levelType = LevelType.DEFAULT;
float x = 5; float x = 5;
float y = 5; float y = 65;
float z = 5; float z = 5;
player.refreshDimension(dimension); player.refreshDimension(dimension);
@ -117,12 +119,15 @@ public class LoginStartPacket implements ClientPreplayPacket {
playerInfoPacket.playerInfos.add(addPlayer); playerInfoPacket.playerInfos.add(addPlayer);
connection.sendPacket(playerInfoPacket); connection.sendPacket(playerInfoPacket);
// Next is optional TODO put all that somewhere else (LoginEvent)
// TODO LoginEvent in another thread (here we are in netty thread)
player.setInstance(instance); player.setInstance(instance);
for (int cx = 0; cx < 4; cx++) for (int cx = 0; cx < 4; cx++)
for (int cz = 0; cz < 4; cz++) { for (int cz = 0; cz < 4; cz++) {
ChickenCreature chickenCreature = new ChickenCreature(); ChickenCreature chickenCreature = new ChickenCreature();
chickenCreature.refreshPosition(0 + (float) cx * 1, 5, 0 + (float) cz * 1); chickenCreature.refreshPosition(0 + (float) cx * 1, 65, 0 + (float) cz * 1);
//chickenCreature.setOnFire(true); //chickenCreature.setOnFire(true);
chickenCreature.setInstance(instance); chickenCreature.setInstance(instance);
//chickenCreature.addPassenger(player); //chickenCreature.addPassenger(player);
@ -144,10 +149,36 @@ public class LoginStartPacket implements ClientPreplayPacket {
for (int ix = 0; ix < 4; ix++) for (int ix = 0; ix < 4; ix++)
for (int iz = 0; iz < 4; iz++) { for (int iz = 0; iz < 4; iz++) {
ItemEntity itemEntity = new ItemEntity(new ItemStack(1, (byte) 32)); ItemEntity itemEntity = new ItemEntity(new ItemStack(1, (byte) 32));
itemEntity.refreshPosition(ix, 5, iz); itemEntity.refreshPosition(ix, 66, iz);
itemEntity.setNoGravity(true);
itemEntity.setInstance(instance); itemEntity.setInstance(instance);
//itemEntity.remove(); //itemEntity.remove();
} }
TestArrow arrow = new TestArrow(player);
arrow.refreshPosition(5, 65, 5);
arrow.setInstance(instance);
arrow.setNoGravity(true);
DeclareCommandsPacket declareCommandsPacket = new DeclareCommandsPacket();
DeclareCommandsPacket.Node argumentNode = new DeclareCommandsPacket.Node();
argumentNode.flags = 0b1010;
argumentNode.children = new int[0];
argumentNode.name = "arg name";
argumentNode.parser = "minecraft:nbt_path";
DeclareCommandsPacket.Node literalNode = new DeclareCommandsPacket.Node();
literalNode.flags = 0b1;
literalNode.children = new int[]{2};
literalNode.name = "hey";
DeclareCommandsPacket.Node rootNode = new DeclareCommandsPacket.Node();
rootNode.flags = 0;
rootNode.children = new int[]{1};
declareCommandsPacket.nodes = new DeclareCommandsPacket.Node[]{rootNode, literalNode, argumentNode};
declareCommandsPacket.rootIndex = 0;
connection.sendPacket(declareCommandsPacket);
} }
@Override @Override

View File

@ -0,0 +1,77 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
import java.util.function.Consumer;
public class DeclareCommandsPacket implements ServerPacket {
public Node[] nodes;
public int rootIndex;
@Override
public void write(Buffer buffer) {
Utils.writeVarInt(buffer, nodes.length);
for (Node node : nodes) {
node.write(buffer);
}
Utils.writeVarInt(buffer, rootIndex);
}
@Override
public int getId() {
return 0x11;
}
public static class Node {
public byte flags;
public int[] children;
public int redirectedNode; // Only if flags & 0x08
public String name; // Only for literal and argument
public String parser; // Only for argument
public Consumer<Buffer> properties; // Only for argument
public String suggestionsType; // Only if flags 0x10
private void write(Buffer buffer) {
buffer.putByte(flags);
Utils.writeVarInt(buffer, children.length);
for (int child : children) {
Utils.writeVarInt(buffer, child);
}
if ((flags & 0x08) != 0) {
Utils.writeVarInt(buffer, redirectedNode);
}
if (isLiteral() || isArgument()) {
Utils.writeString(buffer, name);
}
if (isArgument()) {
Utils.writeString(buffer, parser);
if (properties != null) {
properties.accept(buffer);
}
}
if ((flags & 0x10) != 0) {
Utils.writeString(buffer, suggestionsType);
}
}
private boolean isLiteral() {
return (flags & 0b1) != 0;
}
private boolean isArgument() {
return (flags & 0b10) != 0;
}
}
}

View File

@ -1,10 +1,14 @@
package fr.themode.minestom.net.player; package fr.themode.minestom.net.player;
import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet; import fr.adamaq01.ozao.net.packet.Packet;
import fr.adamaq01.ozao.net.server.Connection; import fr.adamaq01.ozao.net.server.Connection;
import fr.themode.minestom.net.ConnectionState; import fr.themode.minestom.net.ConnectionState;
import fr.themode.minestom.net.packet.server.ServerPacket; import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.PacketUtils; import fr.themode.minestom.utils.PacketUtils;
import io.netty.channel.socket.SocketChannel;
import java.lang.reflect.Field;
public class PlayerConnection { public class PlayerConnection {
@ -20,6 +24,30 @@ public class PlayerConnection {
this.connection.sendPacket(packet); this.connection.sendPacket(packet);
} }
// TODO make that proper (remove reflection)
private Field field;
{
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) {
try {
SocketChannel channel = ((SocketChannel) field.get(connection));
channel.writeAndFlush(packet.getData());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public void sendPacket(ServerPacket serverPacket) { public void sendPacket(ServerPacket serverPacket) {
sendPacket(PacketUtils.writePacket(serverPacket)); sendPacket(PacketUtils.writePacket(serverPacket));
} }

View File

@ -3,12 +3,12 @@ package fr.themode.minestom.net.protocol;
import fr.adamaq01.ozao.net.Buffer; import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet; import fr.adamaq01.ozao.net.packet.Packet;
import fr.adamaq01.ozao.net.protocol.Protocol; import fr.adamaq01.ozao.net.protocol.Protocol;
import fr.themode.minestom.utils.PacketUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import static fr.themode.minestom.utils.Utils.readVarInt; import static fr.themode.minestom.utils.Utils.readVarInt;
import static fr.themode.minestom.utils.Utils.writeVarInt;
public class MinecraftProtocol extends Protocol { public class MinecraftProtocol extends Protocol {
@ -29,7 +29,7 @@ public class MinecraftProtocol extends Protocol {
@Override @Override
public boolean verify(Packet packet) { public boolean verify(Packet packet) {
return packet.get("id") != null; return PacketUtils.verify(packet);
} }
@Override @Override
@ -56,12 +56,6 @@ public class MinecraftProtocol extends Protocol {
@Override @Override
public Buffer encode(Packet packet) { public Buffer encode(Packet packet) {
Buffer buffer = Buffer.create(); return PacketUtils.encode(packet);
Buffer idAndPayload = Buffer.create();
writeVarInt(idAndPayload, packet.get(PACKET_ID_IDENTIFIER));
idAndPayload.putBuffer(packet.getPayload());
writeVarInt(buffer, idAndPayload.length());
buffer.putBuffer(idAndPayload);
return buffer;
} }
} }

View File

@ -43,6 +43,6 @@ public class BlockPosition {
@Override @Override
public String toString() { public String toString() {
return "Position[" + x + ":" + y + ":" + z + "]"; return "BlockPosition[" + x + ":" + y + ":" + z + "]";
} }
} }

View File

@ -5,6 +5,7 @@ import fr.adamaq01.ozao.net.packet.Packet;
import fr.themode.minestom.net.packet.server.ServerPacket; import fr.themode.minestom.net.packet.server.ServerPacket;
import static fr.themode.minestom.net.protocol.MinecraftProtocol.PACKET_ID_IDENTIFIER; import static fr.themode.minestom.net.protocol.MinecraftProtocol.PACKET_ID_IDENTIFIER;
import static fr.themode.minestom.utils.Utils.writeVarInt;
public class PacketUtils { public class PacketUtils {
@ -13,10 +14,22 @@ public class PacketUtils {
Packet packet = Packet.create(); Packet packet = Packet.create();
Buffer buffer = packet.getPayload(); Buffer buffer = packet.getPayload();
serverPacket.write(buffer); serverPacket.write(buffer);
/*if (id != 40 && id != 64)
System.out.println("ID: 0x" + Integer.toHexString(id));*/
packet.put(PACKET_ID_IDENTIFIER, id); packet.put(PACKET_ID_IDENTIFIER, id);
return packet; 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;
}
} }

View File

@ -21,10 +21,20 @@ public class Position {
this(0, 0, 0); this(0, 0, 0);
} }
public void add(float x, float y, float z) {
this.x += x;
this.y += y;
this.z += z;
}
public float getDistance(Position position) { public float getDistance(Position position) {
return (float) Math.sqrt(Math.pow(position.getX() - getX(), 2) + Math.pow(position.getY() - getY(), 2) + Math.pow(position.getZ() - getZ(), 2)); return (float) Math.sqrt(Math.pow(position.getX() - getX(), 2) + Math.pow(position.getY() - getY(), 2) + Math.pow(position.getZ() - getZ(), 2));
} }
public Position clone() {
return new Position(getX(), getY(), getZ(), getYaw(), getPitch());
}
public float getX() { public float getX() {
return x; return x;
} }
@ -64,4 +74,9 @@ public class Position {
public void setPitch(float pitch) { public void setPitch(float pitch) {
this.pitch = pitch; this.pitch = pitch;
} }
@Override
public String toString() {
return "Position[" + x + ":" + y + ":" + z + "] (" + yaw + "/" + pitch + ")";
}
} }