Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First version of NetworkCodec #819

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -120,6 +123,59 @@

@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE)
public class MinecraftTypes {
public static final NetworkCodec<Boolean> BOOLEAN = NetworkCodec.of(
ByteBuf::writeBoolean,
ByteBuf::readBoolean
);
public static final NetworkCodec<String> STRING = NetworkCodec.of(
MinecraftTypes::writeString,
MinecraftTypes::readString
);
public static final NetworkCodec<Integer> VAR_INT = NetworkCodec.of(
MinecraftTypes::writeVarInt,
MinecraftTypes::readVarInt
);
public static final NetworkCodec<Long> VAR_LONG = NetworkCodec.of(
MinecraftTypes::writeVarLong,
MinecraftTypes::readVarLong
);
public static final NetworkCodec<UUID> UUID = NetworkCodec.of(
MinecraftTypes::writeUUID,
MinecraftTypes::readUUID
);
public static final NetworkCodec<byte[]> BYTE_ARRAY = NetworkCodec.of(
MinecraftTypes::writeByteArray,
MinecraftTypes::readByteArray
);
public static final NetworkCodec<byte[]> GREEDY_BYTE_ARRAY = NetworkCodec.of(
ByteBuf::writeBytes,
buf -> MinecraftTypes.readByteArray(buf, ByteBuf::readableBytes)
);
public static final NetworkCodec<long[]> LONG_ARRAY = NetworkCodec.of(
MinecraftTypes::writeLongArray,
MinecraftTypes::readLongArray
);
public static final NetworkCodec<NbtMap> COMPOUND_TAG = NetworkCodec.of(
MinecraftTypes::writeAnyTag,
MinecraftTypes::readCompoundTag
);
public static final NetworkCodec<Component> COMPONENT = NetworkCodec.of(
MinecraftTypes::writeComponent,
MinecraftTypes::readComponent
);
public static final NetworkCodec<PublicKey> 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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T>(Writer<T> writer, Reader<T> reader) {
public static <T> NetworkCodec<T> of(Writer<T> writer, Reader<T> reader) {
return new NetworkCodec<>(writer, reader);
}

public <O> NetworkCodec<O> map(Function<O, T> to, Function<T, O> from) {
return NetworkCodec.of(
(buf, o) -> writer.write(buf, to.apply(o)),
buf -> from.apply(reader.read(buf))
);
}

public NetworkCodec<Optional<T>> 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<T> nullable() {
return optional().map(Optional::ofNullable, optional -> optional.orElse(null));
}

public NetworkCodec<List<T>> list() {
return list(Integer.MAX_VALUE);
}

public NetworkCodec<List<T>> 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<T> 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<T> {
void write(ByteBuf buf, T t);
}

public interface Reader<T> {
T read(ByteBuf buf);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<KnownPack> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -17,16 +16,12 @@ public class ClientboundSelectKnownPacks implements MinecraftPacket {
private final List<KnownPack> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,12 @@ public class ServerboundSelectKnownPacks implements MinecraftPacket {
private final List<KnownPack> 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
Expand Down
Loading