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 new file mode 100644 index 000000000..46665ab97 --- /dev/null +++ b/protocol/src/main/java/org/geysermc/mcprotocollib/protocol/codec/NetworkCodec.java @@ -0,0 +1,83 @@ +package org.geysermc.mcprotocollib.protocol.codec; + +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 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) { + return reader.read(byteBuf); + } + + public interface Writer { + void write(ByteBuf buf, T t); + } + + public interface Reader { + 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 f8079e454..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,10 +2,23 @@ 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((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); + }); + 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 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