diff --git a/src/main/java/ua/nanit/limbo/connection/ClientConnection.java b/src/main/java/ua/nanit/limbo/connection/ClientConnection.java index 7e0c74d..717f2ac 100644 --- a/src/main/java/ua/nanit/limbo/connection/ClientConnection.java +++ b/src/main/java/ua/nanit/limbo/connection/ClientConnection.java @@ -94,7 +94,7 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { @Override public void channelInactive(@NotNull ChannelHandlerContext ctx) throws Exception { - if (state.equals(State.PLAY)) { + if (state.equals(State.PLAY) || state.equals(State.CONFIGURATION)) { server.getConnections().removeConnection(this); } super.channelInactive(ctx); @@ -125,9 +125,21 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { } sendPacket(PacketSnapshots.PACKET_LOGIN_SUCCESS); - updateState(State.PLAY); + server.getConnections().addConnection(this); + // Preparing for configuration mode + if (clientVersion.moreOrEqual(Version.V1_20_2)) { + updateEncoderState(State.CONFIGURATION); + return; + } + + spawnPlayer(); + } + + public void spawnPlayer() { + updateState(State.PLAY); + Runnable sendPlayPackets = () -> { writePacket(PacketSnapshots.PACKET_JOIN_GAME); writePacket(PacketSnapshots.PACKET_PLAYER_ABILITIES); @@ -173,6 +185,16 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { } } + public void onLoginAcknowledgedReceived() { + updateState(State.CONFIGURATION); + + if (PacketSnapshots.PACKET_PLUGIN_MESSAGE != null) + writePacket(PacketSnapshots.PACKET_PLUGIN_MESSAGE); + writePacket(PacketSnapshots.PACKET_REGISTRY_DATA); + + sendPacket(PacketSnapshots.PACKET_FINISH_CONFIGURATION); + } + public void disconnectLogin(String reason) { if (isConnected() && state == State.LOGIN) { PacketDisconnect disconnect = new PacketDisconnect(); @@ -226,6 +248,14 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { encoder.updateState(state); } + public void updateDecoderState(State state) { + decoder.updateState(state); + } + + public void updateEncoderState(State state) { + encoder.updateState(state); + } + public void updateVersion(Version version) { clientVersion = version; decoder.updateVersion(version); diff --git a/src/main/java/ua/nanit/limbo/connection/PacketHandler.java b/src/main/java/ua/nanit/limbo/connection/PacketHandler.java index 3e22a34..fc3832b 100644 --- a/src/main/java/ua/nanit/limbo/connection/PacketHandler.java +++ b/src/main/java/ua/nanit/limbo/connection/PacketHandler.java @@ -20,6 +20,8 @@ package ua.nanit.limbo.connection; import io.netty.buffer.Unpooled; import ua.nanit.limbo.LimboConstants; import ua.nanit.limbo.protocol.packets.PacketHandshake; +import ua.nanit.limbo.protocol.packets.configuration.PacketFinishConfiguration; +import ua.nanit.limbo.protocol.packets.login.PacketLoginAcknowledged; import ua.nanit.limbo.protocol.packets.login.PacketLoginPluginRequest; import ua.nanit.limbo.protocol.packets.login.PacketLoginPluginResponse; import ua.nanit.limbo.protocol.packets.login.PacketLoginStart; @@ -127,4 +129,12 @@ public class PacketHandler { } } + public void handle(ClientConnection conn, PacketLoginAcknowledged packet) { + conn.onLoginAcknowledgedReceived(); + } + + public void handle(ClientConnection conn, PacketFinishConfiguration packet) { + conn.spawnPlayer(); + } + } diff --git a/src/main/java/ua/nanit/limbo/connection/PacketSnapshots.java b/src/main/java/ua/nanit/limbo/connection/PacketSnapshots.java index 5559a62..b52ef26 100644 --- a/src/main/java/ua/nanit/limbo/connection/PacketSnapshots.java +++ b/src/main/java/ua/nanit/limbo/connection/PacketSnapshots.java @@ -19,6 +19,8 @@ package ua.nanit.limbo.connection; import ua.nanit.limbo.LimboConstants; import ua.nanit.limbo.protocol.PacketSnapshot; +import ua.nanit.limbo.protocol.packets.configuration.PacketFinishConfiguration; +import ua.nanit.limbo.protocol.packets.configuration.PacketRegistryData; import ua.nanit.limbo.protocol.packets.login.PacketLoginSuccess; import ua.nanit.limbo.protocol.packets.play.*; import ua.nanit.limbo.server.LimboServer; @@ -54,6 +56,8 @@ public final class PacketSnapshots { public static PacketSnapshot PACKET_TITLE_LEGACY_SUBTITLE; public static PacketSnapshot PACKET_TITLE_LEGACY_TIMES; + public static PacketSnapshot PACKET_REGISTRY_DATA; + public static PacketSnapshot PACKET_FINISH_CONFIGURATION; private PacketSnapshots() { } @@ -178,5 +182,11 @@ public final class PacketSnapshots { PACKET_TITLE_LEGACY_SUBTITLE = PacketSnapshot.of(legacySubtitle); PACKET_TITLE_LEGACY_TIMES = PacketSnapshot.of(legacyTimes); } + + PacketRegistryData packetRegistryData = new PacketRegistryData(); + packetRegistryData.setDimensionRegistry(server.getDimensionRegistry()); + + PACKET_REGISTRY_DATA = PacketSnapshot.of(packetRegistryData); + PACKET_FINISH_CONFIGURATION = PacketSnapshot.of(new PacketFinishConfiguration()); } } diff --git a/src/main/java/ua/nanit/limbo/connection/pipeline/PacketDecoder.java b/src/main/java/ua/nanit/limbo/connection/pipeline/PacketDecoder.java index 43aaffb..5fe1eec 100644 --- a/src/main/java/ua/nanit/limbo/connection/pipeline/PacketDecoder.java +++ b/src/main/java/ua/nanit/limbo/connection/pipeline/PacketDecoder.java @@ -47,11 +47,17 @@ public class PacketDecoder extends MessageToMessageDecoder { Packet packet = mappings.getPacket(packetId); if (packet != null) { - Logger.debug("Received packet %s[0x%s]", packet.toString(), Integer.toHexString(packetId)); + Logger.debug("Received packet %s[0x%s] (%d bytes)", packet.toString(), Integer.toHexString(packetId), msg.readableBytes()); try { packet.decode(msg, version); } catch (Exception e) { - Logger.warning("Cannot decode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage()); + if (Logger.isDebug()) { + Logger.warning("Cannot decode packet 0x%s", Integer.toHexString(packetId)); + e.printStackTrace(); + } + else { + Logger.warning("Cannot decode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage()); + } } ctx.fireChannelRead(packet); diff --git a/src/main/java/ua/nanit/limbo/connection/pipeline/PacketEncoder.java b/src/main/java/ua/nanit/limbo/connection/pipeline/PacketEncoder.java index d3a137c..6a49139 100644 --- a/src/main/java/ua/nanit/limbo/connection/pipeline/PacketEncoder.java +++ b/src/main/java/ua/nanit/limbo/connection/pipeline/PacketEncoder.java @@ -51,7 +51,7 @@ public class PacketEncoder extends MessageToByteEncoder { } if (packetId == -1) { - Logger.warning("Undefined packet class: %s[0x%s]", packet.getClass().getName(), Integer.toHexString(packetId)); + Logger.warning("Undefined packet class: %s[0x%s] (%d bytes)", packet.getClass().getName(), Integer.toHexString(packetId), msg.readableBytes()); return; } diff --git a/src/main/java/ua/nanit/limbo/protocol/ByteMessage.java b/src/main/java/ua/nanit/limbo/protocol/ByteMessage.java index b4b0128..65fe318 100644 --- a/src/main/java/ua/nanit/limbo/protocol/ByteMessage.java +++ b/src/main/java/ua/nanit/limbo/protocol/ByteMessage.java @@ -22,6 +22,7 @@ import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.EncoderException; import io.netty.util.ByteProcessor; import net.kyori.adventure.nbt.BinaryTagIO; +import net.kyori.adventure.nbt.BinaryTagTypes; import net.kyori.adventure.nbt.CompoundBinaryTag; import java.io.IOException; @@ -193,6 +194,16 @@ public class ByteMessage extends ByteBuf { } } + public void writeNamelessCompoundTag(CompoundBinaryTag compoundTag) { + try (ByteBufOutputStream stream = new ByteBufOutputStream(buf)) { + stream.writeByte(10); // CompoundTag ID + BinaryTagTypes.COMPOUND.write(compoundTag, stream); + } + catch (IOException e) { + throw new EncoderException("Cannot write NBT CompoundTag"); + } + } + public > void writeEnumSet(EnumSet enumset, Class oclass) { E[] enums = oclass.getEnumConstants(); BitSet bits = new BitSet(enums.length); diff --git a/src/main/java/ua/nanit/limbo/protocol/packets/configuration/PacketFinishConfiguration.java b/src/main/java/ua/nanit/limbo/protocol/packets/configuration/PacketFinishConfiguration.java new file mode 100644 index 0000000..bf739ea --- /dev/null +++ b/src/main/java/ua/nanit/limbo/protocol/packets/configuration/PacketFinishConfiguration.java @@ -0,0 +1,19 @@ +package ua.nanit.limbo.protocol.packets.configuration; + +import ua.nanit.limbo.connection.ClientConnection; +import ua.nanit.limbo.protocol.PacketIn; +import ua.nanit.limbo.protocol.PacketOut; +import ua.nanit.limbo.server.LimboServer; + +public class PacketFinishConfiguration implements PacketIn, PacketOut { + + @Override + public void handle(ClientConnection conn, LimboServer server) { + server.getPacketHandler().handle(conn, this); + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } +} diff --git a/src/main/java/ua/nanit/limbo/protocol/packets/configuration/PacketRegistryData.java b/src/main/java/ua/nanit/limbo/protocol/packets/configuration/PacketRegistryData.java new file mode 100644 index 0000000..3f3a6e9 --- /dev/null +++ b/src/main/java/ua/nanit/limbo/protocol/packets/configuration/PacketRegistryData.java @@ -0,0 +1,20 @@ +package ua.nanit.limbo.protocol.packets.configuration; + +import ua.nanit.limbo.protocol.ByteMessage; +import ua.nanit.limbo.protocol.PacketOut; +import ua.nanit.limbo.protocol.registry.Version; +import ua.nanit.limbo.world.DimensionRegistry; + +public class PacketRegistryData implements PacketOut { + + private DimensionRegistry dimensionRegistry; + + public void setDimensionRegistry(DimensionRegistry dimensionRegistry) { + this.dimensionRegistry = dimensionRegistry; + } + + @Override + public void encode(ByteMessage msg, Version version) { + msg.writeNamelessCompoundTag(dimensionRegistry.getCodec_1_20()); + } +} diff --git a/src/main/java/ua/nanit/limbo/protocol/packets/login/PacketLoginAcknowledged.java b/src/main/java/ua/nanit/limbo/protocol/packets/login/PacketLoginAcknowledged.java new file mode 100644 index 0000000..96c60c7 --- /dev/null +++ b/src/main/java/ua/nanit/limbo/protocol/packets/login/PacketLoginAcknowledged.java @@ -0,0 +1,19 @@ +package ua.nanit.limbo.protocol.packets.login; + +import ua.nanit.limbo.connection.ClientConnection; +import ua.nanit.limbo.protocol.PacketIn; +import ua.nanit.limbo.protocol.PacketOut; +import ua.nanit.limbo.server.LimboServer; + +public class PacketLoginAcknowledged implements PacketIn, PacketOut { + + @Override + public void handle(ClientConnection conn, LimboServer server) { + server.getPacketHandler().handle(conn, this); + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } +} diff --git a/src/main/java/ua/nanit/limbo/protocol/packets/play/PacketJoinGame.java b/src/main/java/ua/nanit/limbo/protocol/packets/play/PacketJoinGame.java index 15d1651..fbb190f 100644 --- a/src/main/java/ua/nanit/limbo/protocol/packets/play/PacketJoinGame.java +++ b/src/main/java/ua/nanit/limbo/protocol/packets/play/PacketJoinGame.java @@ -38,6 +38,7 @@ public class PacketJoinGame implements PacketOut { private boolean enableRespawnScreen; private boolean isDebug; private boolean isFlat; + private boolean limitedCrafting; public void setEntityId(int entityId) { this.entityId = entityId; @@ -95,6 +96,10 @@ public class PacketJoinGame implements PacketOut { isFlat = flat; } + public void setLimitedCrafting(boolean limitedCrafting) { + this.limitedCrafting = limitedCrafting; + } + @Override public void encode(ByteMessage msg, Version version) { msg.writeInt(entityId); @@ -230,7 +235,7 @@ public class PacketJoinGame implements PacketOut { msg.writeBoolean(false); } - if (version.moreOrEqual(Version.V1_20)) { + if (version.equals(Version.V1_20)) { msg.writeBoolean(isHardcore); msg.writeByte(gameMode); msg.writeByte(previousGameMode); @@ -249,6 +254,26 @@ public class PacketJoinGame implements PacketOut { msg.writeBoolean(false); msg.writeVarInt(0); } + + if (version.moreOrEqual(Version.V1_20_2)) { + msg.writeBoolean(isHardcore); + msg.writeStringsArray(worldNames); + msg.writeVarInt(maxPlayers); + msg.writeVarInt(viewDistance); + msg.writeVarInt(viewDistance); // Simulation Distance + msg.writeBoolean(reducedDebugInfo); + msg.writeBoolean(enableRespawnScreen); + msg.writeBoolean(limitedCrafting); + msg.writeString(worldName); + msg.writeString(worldName); + msg.writeLong(hashedSeed); + msg.writeByte(gameMode); + msg.writeByte(previousGameMode); + msg.writeBoolean(isDebug); + msg.writeBoolean(isFlat); + msg.writeBoolean(false); + msg.writeVarInt(0); + } } } diff --git a/src/main/java/ua/nanit/limbo/protocol/packets/play/PacketPluginMessage.java b/src/main/java/ua/nanit/limbo/protocol/packets/play/PacketPluginMessage.java index 753b741..1a7ecca 100644 --- a/src/main/java/ua/nanit/limbo/protocol/packets/play/PacketPluginMessage.java +++ b/src/main/java/ua/nanit/limbo/protocol/packets/play/PacketPluginMessage.java @@ -39,4 +39,5 @@ public class PacketPluginMessage implements PacketOut { msg.writeString(channel); msg.writeString(message); } + } diff --git a/src/main/java/ua/nanit/limbo/protocol/registry/State.java b/src/main/java/ua/nanit/limbo/protocol/registry/State.java index 03beeea..7bf9262 100644 --- a/src/main/java/ua/nanit/limbo/protocol/registry/State.java +++ b/src/main/java/ua/nanit/limbo/protocol/registry/State.java @@ -21,6 +21,7 @@ import ua.nanit.limbo.protocol.Packet; import ua.nanit.limbo.protocol.packets.PacketHandshake; import ua.nanit.limbo.protocol.packets.login.*; import ua.nanit.limbo.protocol.packets.play.*; +import ua.nanit.limbo.protocol.packets.configuration.*; import ua.nanit.limbo.protocol.packets.status.PacketStatusPing; import ua.nanit.limbo.protocol.packets.status.PacketStatusRequest; import ua.nanit.limbo.protocol.packets.status.PacketStatusResponse; @@ -63,6 +64,10 @@ public enum State { serverBound.register(PacketLoginPluginResponse::new, map(0x02, Version.getMin(), Version.getMax()) ); + serverBound.register( + PacketLoginAcknowledged::new, + map(0x03, V1_20_2, V1_20_2) + ); clientBound.register(PacketDisconnect::new, map(0x00, Version.getMin(), Version.getMax()) ); @@ -74,7 +79,44 @@ public enum State { ); } }, - PLAY(3) { + CONFIGURATION(3) { + { + clientBound.register( + PacketPluginMessage::new, + map(0x00, V1_20_2, V1_20_2) + ); + clientBound.register( + PacketDisconnect::new, + map(0x01, V1_20_2, V1_20_2) + ); + clientBound.register( + PacketFinishConfiguration::new, + map(0x02, V1_20_2, V1_20_2) + ); + clientBound.register( + PacketKeepAlive::new, + map(0x03, V1_20_2, V1_20_2) + ); + clientBound.register( + PacketRegistryData::new, + map(0x05, V1_20_2, V1_20_2) + ); + + serverBound.register( + PacketPluginMessage::new, + map(0x01, V1_20_2, V1_20_2) + ); + serverBound.register( + PacketFinishConfiguration::new, + map(0x02, V1_20_2, V1_20_2) + ); + serverBound.register( + PacketKeepAlive::new, + map(0x03, V1_20_2, V1_20_2) + ); + } + }, + PLAY(4) { { serverBound.register(PacketKeepAlive::new, map(0x00, V1_7_2, V1_8), @@ -88,7 +130,8 @@ public enum State { map(0x11, V1_19, V1_19), map(0x12, V1_19_1, V1_19_1), map(0x11, V1_19_3, V1_19_3), - map(0x12, V1_19_4, V1_20) + map(0x12, V1_19_4, V1_20), + map(0x14, V1_20_2, V1_20_2) ); clientBound.register(PacketDeclareCommands::new, @@ -99,7 +142,8 @@ public enum State { map(0x12, V1_17, V1_18_2), map(0x0F, V1_19, V1_19_1), map(0x0E, V1_19_3, V1_19_3), - map(0x10, V1_19_4, V1_20) + map(0x10, V1_19_4, V1_20), + map(0x11, V1_20_2, V1_20_2) ); clientBound.register(PacketJoinGame::new, map(0x01, V1_7_2, V1_8), @@ -112,7 +156,8 @@ public enum State { map(0x23, V1_19, V1_19), map(0x25, V1_19_1, V1_19_1), map(0x24, V1_19_3, V1_19_3), - map(0x28, V1_19_4, V1_20) + map(0x28, V1_19_4, V1_20), + map(0x29, V1_20_2, V1_20_2) ); clientBound.register(PacketPluginMessage::new, map(0x19, V1_13, V1_13_2), @@ -124,7 +169,8 @@ public enum State { map(0x15, V1_19, V1_19), map(0x16, V1_19_1, V1_19_1), map(0x15, V1_19_3, V1_19_3), - map(0x17, V1_19_4, V1_20) + map(0x17, V1_19_4, V1_20), + map(0x18, V1_20_2, V1_20_2) ); clientBound.register(PacketPlayerAbilities::new, map(0x39, V1_7_2, V1_8), @@ -139,7 +185,8 @@ public enum State { map(0x2F, V1_19, V1_19), map(0x31, V1_19_1, V1_19_1), map(0x30, V1_19_3, V1_19_3), - map(0x34, V1_19_4, V1_20) + map(0x34, V1_19_4, V1_20), + map(0x36, V1_20_2, V1_20_2) ); clientBound.register(PacketPlayerPositionAndLook::new, map(0x08, V1_7_2, V1_8), @@ -154,7 +201,8 @@ public enum State { map(0x36, V1_19, V1_19), map(0x39, V1_19_1, V1_19_1), map(0x38, V1_19_3, V1_19_3), - map(0x3C, V1_19_4, V1_20) + map(0x3C, V1_19_4, V1_20), + map(0x3E, V1_20_2, V1_20_2) ); clientBound.register(PacketKeepAlive::new, map(0x00, V1_7_2, V1_8), @@ -168,7 +216,8 @@ public enum State { map(0x1E, V1_19, V1_19), map(0x20, V1_19_1, V1_19_1), map(0x1F, V1_19_3, V1_19_3), - map(0x23, V1_19_4, V1_20) + map(0x23, V1_19_4, V1_20), + map(0x24, V1_20_2, V1_20_2) ); clientBound.register(PacketChatMessage::new, map(0x02, V1_7_2, V1_8), @@ -180,7 +229,8 @@ public enum State { map(0x5F, V1_19, V1_19), map(0x62, V1_19_1, V1_19_1), map(0x60, V1_19_3, V1_19_3), - map(0x64, V1_19_4, V1_20) + map(0x64, V1_19_4, V1_20), + map(0x67, V1_20_2, V1_20_2) ); clientBound.register(PacketBossBar::new, map(0x0C, V1_9, V1_14_4), @@ -188,7 +238,8 @@ public enum State { map(0x0C, V1_16, V1_16_4), map(0x0D, V1_17, V1_18_2), map(0x0A, V1_19, V1_19_3), - map(0x0B, V1_19_4, V1_20) + map(0x0B, V1_19_4, V1_20), + map(0x0A, V1_20_2, V1_20_2) ); clientBound.register(PacketPlayerInfo::new, map(0x38, V1_7_2, V1_8), @@ -203,7 +254,8 @@ public enum State { map(0x34, V1_19, V1_19), map(0x37, V1_19_1, V1_19_1), map(0x36, V1_19_3, V1_19_3), - map(0x3A, V1_19_4, V1_20) + map(0x3A, V1_19_4, V1_20), + map(0x3C, V1_20_2, V1_20_2) ); clientBound.register(PacketTitleLegacy::new, map(0x45, V1_8, V1_11_1), @@ -219,21 +271,24 @@ public enum State { map(0x5A, V1_18, V1_19), map(0x5D, V1_19_1, V1_19_1), map(0x5B, V1_19_3, V1_19_3), - map(0x5F, V1_19_4, V1_20) + map(0x5F, V1_19_4, V1_20), + map(0x61, V1_20_2, V1_20_2) ); clientBound.register(PacketTitleSetSubTitle::new, map(0x57, V1_17, V1_17_1), map(0x58, V1_18, V1_19), map(0x5B, V1_19_1, V1_19_1), map(0x59, V1_19_3, V1_19_3), - map(0x5D, V1_19_4, V1_20) + map(0x5D, V1_19_4, V1_20), + map(0x5F, V1_20_2, V1_20_2) ); clientBound.register(PacketTitleTimes::new, map(0x5A, V1_17, V1_17_1), map(0x5B, V1_18, V1_19), map(0x5E, V1_19_1, V1_19_1), map(0x5C, V1_19_3, V1_19_3), - map(0x60, V1_19_4, V1_20) + map(0x60, V1_19_4, V1_20), + map(0x62, V1_20_2, V1_20_2) ); clientBound.register(PacketPlayerListHeader::new, map(0x47, V1_8, V1_8), @@ -250,11 +305,13 @@ public enum State { map(0x60, V1_19, V1_19), map(0x63, V1_19_1, V1_19_1), map(0x61, V1_19_3, V1_19_3), - map(0x65, V1_19_4, V1_20) + map(0x65, V1_19_4, V1_20), + map(0x68, V1_20_2, V1_20_2) ); clientBound.register(PacketSpawnPosition::new, map(0x4C, V1_19_3, V1_19_3), - map(0x50, V1_19_4, V1_20) + map(0x50, V1_19_4, V1_20), + map(0x52, V1_20_2, V1_20_2) ); } }; diff --git a/src/main/java/ua/nanit/limbo/protocol/registry/Version.java b/src/main/java/ua/nanit/limbo/protocol/registry/Version.java index faedd9e..0857ba5 100644 --- a/src/main/java/ua/nanit/limbo/protocol/registry/Version.java +++ b/src/main/java/ua/nanit/limbo/protocol/registry/Version.java @@ -68,8 +68,9 @@ public enum Version { // 1.19.2 has same protocol number V1_19_3(761), V1_19_4(762), - V1_20(763); + V1_20(763), // 1.20.1 has same protocol number + V1_20_2(764); private static final Map VERSION_MAP; private static final Version MAX; diff --git a/src/main/java/ua/nanit/limbo/server/Logger.java b/src/main/java/ua/nanit/limbo/server/Logger.java index 46ab0bf..c910a3e 100644 --- a/src/main/java/ua/nanit/limbo/server/Logger.java +++ b/src/main/java/ua/nanit/limbo/server/Logger.java @@ -62,6 +62,10 @@ public final class Logger { } } + public static boolean isDebug() { + return debugLevel >= Level.DEBUG.getIndex(); + } + private static String getPrefix(Level level) { return String.format("[%s] [%s]", getTime(), level.getDisplay()); }