From 6efa9505129a38a5ea61fe15baa508468b2e6033 Mon Sep 17 00:00:00 2001 From: AlexProgrammerDE <40795980+AlexProgrammerDE@users.noreply.github.com> Date: Mon, 20 May 2024 18:17:56 +0200 Subject: [PATCH 1/2] First version of NetworkCodec --- .../protocol/codec/NetworkCodec.java | 25 +++++++++++++++++++ .../protocol/data/game/KnownPack.java | 12 +++++++++ .../ClientboundSelectKnownPacks.java | 8 ++---- .../ServerboundSelectKnownPacks.java | 6 ++--- 4 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/NetworkCodec.java diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/NetworkCodec.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/NetworkCodec.java new file mode 100644 index 000000000..72838ec61 --- /dev/null +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/NetworkCodec.java @@ -0,0 +1,25 @@ +package org.geysermc.mcprotocollib.protocol.codec; + +import io.netty.buffer.ByteBuf; + +public record NetworkCodec(Writer writer, Reader reader) { + public static NetworkCodec of(Writer writer, Reader reader) { + return new NetworkCodec<>(writer, reader); + } + + public void write(T t, ByteBuf byteBuf, MinecraftCodecHelper helper) { + writer.write(t, byteBuf, helper); + } + + public T read(ByteBuf byteBuf, MinecraftCodecHelper helper) { + return reader.read(byteBuf, helper); + } + + public interface Writer { + void write(T t, ByteBuf out, MinecraftCodecHelper helper); + } + + public interface Reader { + T read(ByteBuf in, MinecraftCodecHelper helper); + } +} diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/KnownPack.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/KnownPack.java index f8079e454..48ea89980 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/KnownPack.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/KnownPack.java @@ -2,10 +2,22 @@ import lombok.AllArgsConstructor; import lombok.Data; +import org.geysermc.mcprotocollib.protocol.codec.NetworkCodec; @Data @AllArgsConstructor public class KnownPack { + public static NetworkCodec NETWORK_CODEC = NetworkCodec.of((knownPack, out, helper) -> { + helper.writeString(out, knownPack.getNamespace()); + helper.writeString(out, knownPack.getId()); + helper.writeString(out, knownPack.getVersion()); + }, (in, helper) -> { + String namespace = helper.readString(in); + String id = helper.readString(in); + String version = helper.readString(in); + return new KnownPack(namespace, id, version); + }); + private String namespace; private String id; private String version; diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/clientbound/ClientboundSelectKnownPacks.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/clientbound/ClientboundSelectKnownPacks.java index e5dfa96f4..ced57ff51 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/clientbound/ClientboundSelectKnownPacks.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/clientbound/ClientboundSelectKnownPacks.java @@ -17,15 +17,11 @@ public class ClientboundSelectKnownPacks implements MinecraftPacket { private final List knownPacks; public ClientboundSelectKnownPacks(ByteBuf in, MinecraftCodecHelper helper) { - this.knownPacks = helper.readList(in, buf -> new KnownPack(helper.readString(buf), helper.readString(buf), helper.readString(buf))); + this.knownPacks = helper.readList(in, buf -> KnownPack.NETWORK_CODEC.read(buf, helper)); } @Override public void serialize(ByteBuf out, MinecraftCodecHelper helper) { - helper.writeList(out, this.knownPacks, (buf, entry) -> { - helper.writeString(buf, entry.getNamespace()); - helper.writeString(buf, entry.getId()); - helper.writeString(buf, entry.getVersion()); - }); + helper.writeList(out, this.knownPacks, (buf, entry) -> KnownPack.NETWORK_CODEC.write(entry, buf, helper)); } } diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/serverbound/ServerboundSelectKnownPacks.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/serverbound/ServerboundSelectKnownPacks.java index d8cfd6d7f..66fbcaccb 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/serverbound/ServerboundSelectKnownPacks.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/serverbound/ServerboundSelectKnownPacks.java @@ -22,7 +22,7 @@ public ServerboundSelectKnownPacks(ByteBuf in, MinecraftCodecHelper helper) { int entryCount = Math.min(helper.readVarInt(in), 64); for (int i = 0; i < entryCount; i++) { - this.knownPacks.add(new KnownPack(helper.readString(in), helper.readString(in), helper.readString(in))); + this.knownPacks.add(KnownPack.NETWORK_CODEC.read(in, helper)); } } @@ -34,9 +34,7 @@ public void serialize(ByteBuf out, MinecraftCodecHelper helper) { helper.writeVarInt(out, this.knownPacks.size()); for (KnownPack entry : this.knownPacks) { - helper.writeString(out, entry.getNamespace()); - helper.writeString(out, entry.getId()); - helper.writeString(out, entry.getVersion()); + KnownPack.NETWORK_CODEC.write(entry, out, helper); } } } From 2534bdde340f12307b89106e9b74f766f737fc14 Mon Sep 17 00:00:00 2001 From: AlexProgrammerDE <40795980+AlexProgrammerDE@users.noreply.github.com> Date: Fri, 24 Jan 2025 22:16:50 +0100 Subject: [PATCH 2/2] Refactor without codec helpers --- .../protocol/codec/MinecraftTypes.java | 56 +++++++++++++++ .../protocol/codec/NetworkCodec.java | 70 +++++++++++++++++-- .../protocol/data/game/KnownPack.java | 17 ++--- .../ClientboundSelectKnownPacks.java | 9 +-- .../ServerboundSelectKnownPacks.java | 18 +---- 5 files changed, 133 insertions(+), 37 deletions(-) diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/MinecraftTypes.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/MinecraftTypes.java index 597fe13fc..f9c144059 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/MinecraftTypes.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/MinecraftTypes.java @@ -104,6 +104,9 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; @@ -120,6 +123,59 @@ @NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) public class MinecraftTypes { + public static final NetworkCodec BOOLEAN = NetworkCodec.of( + ByteBuf::writeBoolean, + ByteBuf::readBoolean + ); + public static final NetworkCodec STRING = NetworkCodec.of( + MinecraftTypes::writeString, + MinecraftTypes::readString + ); + public static final NetworkCodec VAR_INT = NetworkCodec.of( + MinecraftTypes::writeVarInt, + MinecraftTypes::readVarInt + ); + public static final NetworkCodec VAR_LONG = NetworkCodec.of( + MinecraftTypes::writeVarLong, + MinecraftTypes::readVarLong + ); + public static final NetworkCodec UUID = NetworkCodec.of( + MinecraftTypes::writeUUID, + MinecraftTypes::readUUID + ); + public static final NetworkCodec BYTE_ARRAY = NetworkCodec.of( + MinecraftTypes::writeByteArray, + MinecraftTypes::readByteArray + ); + public static final NetworkCodec GREEDY_BYTE_ARRAY = NetworkCodec.of( + ByteBuf::writeBytes, + buf -> MinecraftTypes.readByteArray(buf, ByteBuf::readableBytes) + ); + public static final NetworkCodec LONG_ARRAY = NetworkCodec.of( + MinecraftTypes::writeLongArray, + MinecraftTypes::readLongArray + ); + public static final NetworkCodec COMPOUND_TAG = NetworkCodec.of( + MinecraftTypes::writeAnyTag, + MinecraftTypes::readCompoundTag + ); + public static final NetworkCodec COMPONENT = NetworkCodec.of( + MinecraftTypes::writeComponent, + MinecraftTypes::readComponent + ); + public static final NetworkCodec PUBLIC_KEY = BYTE_ARRAY.map(key -> { + try { + return key.getEncoded(); + } catch (Exception e) { + throw new IllegalArgumentException("Could not encode public key", e); + } + }, bytes -> { + try { + return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(bytes)); + } catch (Exception e) { + throw new IllegalArgumentException("Could not decode public key", e); + } + }); private static final int POSITION_X_SIZE = 38; private static final int POSITION_Y_SIZE = 12; private static final int POSITION_Z_SIZE = 38; diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/NetworkCodec.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/NetworkCodec.java index 72838ec61..46665ab97 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/NetworkCodec.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/NetworkCodec.java @@ -2,24 +2,82 @@ import io.netty.buffer.ByteBuf; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + public record NetworkCodec(Writer writer, Reader reader) { public static NetworkCodec of(Writer writer, Reader reader) { return new NetworkCodec<>(writer, reader); } - public void write(T t, ByteBuf byteBuf, MinecraftCodecHelper helper) { - writer.write(t, byteBuf, helper); + public NetworkCodec map(Function to, Function from) { + return NetworkCodec.of( + (buf, o) -> writer.write(buf, to.apply(o)), + buf -> from.apply(reader.read(buf)) + ); + } + + public NetworkCodec> optional() { + return NetworkCodec.of( + (buf, optional) -> { + MinecraftTypes.BOOLEAN.write(buf, optional.isPresent()); + optional.ifPresent(t -> this.write(buf, t)); + }, + buf -> MinecraftTypes.BOOLEAN.read(buf) ? Optional.of(this.read(buf)) : Optional.empty() + ); + } + + public NetworkCodec nullable() { + return optional().map(Optional::ofNullable, optional -> optional.orElse(null)); + } + + public NetworkCodec> list() { + return list(Integer.MAX_VALUE); + } + + public NetworkCodec> list(int maxSize) { + return new NetworkCodec<>( + (buf, list) -> { + int size = list.size(); + if (size > maxSize) { + throw new IllegalArgumentException("List size " + size + " is greater than maximum allowed size " + maxSize); + } + + MinecraftTypes.writeVarInt(buf, list.size()); + for (T t : list) { + write(buf, t); + } + }, + buf -> { + int size = MinecraftTypes.readVarInt(buf); + if (size > maxSize) { + throw new IllegalArgumentException("List size " + size + " is greater than maximum allowed size " + maxSize); + } + + List list = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + list.add(read(buf)); + } + return list; + } + ); + } + + public void write(ByteBuf byteBuf, T t) { + writer.write(byteBuf, t); } - public T read(ByteBuf byteBuf, MinecraftCodecHelper helper) { - return reader.read(byteBuf, helper); + public T read(ByteBuf byteBuf) { + return reader.read(byteBuf); } public interface Writer { - void write(T t, ByteBuf out, MinecraftCodecHelper helper); + void write(ByteBuf buf, T t); } public interface Reader { - T read(ByteBuf in, MinecraftCodecHelper helper); + T read(ByteBuf buf); } } diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/KnownPack.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/KnownPack.java index 48ea89980..4cf9945c6 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/KnownPack.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/data/game/KnownPack.java @@ -2,19 +2,20 @@ import lombok.AllArgsConstructor; import lombok.Data; +import org.geysermc.mcprotocollib.protocol.codec.MinecraftTypes; import org.geysermc.mcprotocollib.protocol.codec.NetworkCodec; @Data @AllArgsConstructor public class KnownPack { - public static NetworkCodec NETWORK_CODEC = NetworkCodec.of((knownPack, out, helper) -> { - helper.writeString(out, knownPack.getNamespace()); - helper.writeString(out, knownPack.getId()); - helper.writeString(out, knownPack.getVersion()); - }, (in, helper) -> { - String namespace = helper.readString(in); - String id = helper.readString(in); - String version = helper.readString(in); + public static NetworkCodec NETWORK_CODEC = NetworkCodec.of((buf, knownPack) -> { + MinecraftTypes.writeString(buf, knownPack.getNamespace()); + MinecraftTypes.writeString(buf, knownPack.getId()); + MinecraftTypes.writeString(buf, knownPack.getVersion()); + }, buf -> { + String namespace = MinecraftTypes.readString(buf); + String id = MinecraftTypes.readString(buf); + String version = MinecraftTypes.readString(buf); return new KnownPack(namespace, id, version); }); diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/clientbound/ClientboundSelectKnownPacks.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/clientbound/ClientboundSelectKnownPacks.java index 81e2ee362..f368c507c 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/clientbound/ClientboundSelectKnownPacks.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/clientbound/ClientboundSelectKnownPacks.java @@ -5,7 +5,6 @@ import lombok.Data; import lombok.With; import org.geysermc.mcprotocollib.protocol.codec.MinecraftPacket; -import org.geysermc.mcprotocollib.protocol.codec.MinecraftTypes; import org.geysermc.mcprotocollib.protocol.data.game.KnownPack; import java.util.List; @@ -17,16 +16,12 @@ public class ClientboundSelectKnownPacks implements MinecraftPacket { private final List knownPacks; public ClientboundSelectKnownPacks(ByteBuf in) { - this.knownPacks = MinecraftTypes.readList(in, buf -> new KnownPack(MinecraftTypes.readString(buf), MinecraftTypes.readString(buf), MinecraftTypes.readString(buf))); + this.knownPacks = KnownPack.NETWORK_CODEC.list().read(in); } @Override public void serialize(ByteBuf out) { - MinecraftTypes.writeList(out, this.knownPacks, (buf, entry) -> { - MinecraftTypes.writeString(buf, entry.getNamespace()); - MinecraftTypes.writeString(buf, entry.getId()); - MinecraftTypes.writeString(buf, entry.getVersion()); - }); + KnownPack.NETWORK_CODEC.list().write(out, knownPacks); } @Override diff --git a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/serverbound/ServerboundSelectKnownPacks.java b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/serverbound/ServerboundSelectKnownPacks.java index 2393017b2..85360d217 100644 --- a/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/serverbound/ServerboundSelectKnownPacks.java +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/packet/configuration/serverbound/ServerboundSelectKnownPacks.java @@ -18,26 +18,12 @@ public class ServerboundSelectKnownPacks implements MinecraftPacket { private final List knownPacks; public ServerboundSelectKnownPacks(ByteBuf in) { - this.knownPacks = new ArrayList<>(); - - int entryCount = Math.min(MinecraftTypes.readVarInt(in), 64); - for (int i = 0; i < entryCount; i++) { - this.knownPacks.add(new KnownPack(MinecraftTypes.readString(in), MinecraftTypes.readString(in), MinecraftTypes.readString(in))); - } + this.knownPacks = KnownPack.NETWORK_CODEC.list(64).read(in); } @Override public void serialize(ByteBuf out) { - if (this.knownPacks.size() > 64) { - throw new IllegalArgumentException("KnownPacks is longer than maximum allowed length"); - } - - MinecraftTypes.writeVarInt(out, this.knownPacks.size()); - for (KnownPack entry : this.knownPacks) { - MinecraftTypes.writeString(out, entry.getNamespace()); - MinecraftTypes.writeString(out, entry.getId()); - MinecraftTypes.writeString(out, entry.getVersion()); - } + KnownPack.NETWORK_CODEC.list(64).write(out, knownPacks); } @Override