Fix some packet serializing issues

This commit is contained in:
Sollace 2024-10-04 21:44:21 +01:00
parent b2f0a846de
commit 0e2b0bcc34
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
11 changed files with 89 additions and 83 deletions

View file

@ -10,7 +10,6 @@ import com.google.common.base.Strings;
import com.minelittlepony.unicopia.ability.Abilities;
import com.minelittlepony.unicopia.ability.Ability;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.network.track.TrackableDataType;
import com.minelittlepony.unicopia.util.RegistryUtils;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
@ -45,7 +44,6 @@ public record Race (
public static final String DEFAULT_ID = "unicopia:unset";
public static final Registry<Race> REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race"), DEFAULT_ID);
public static final Registry<Race> COMMAND_REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race/grantable"), DEFAULT_ID);
public static final TrackableDataType<Race> TRACKABLE_TYPE = TrackableDataType.RACE;
public static final RegistryKey<? extends Registry<Race>> REGISTRY_KEY = REGISTRY.getKey();
private static final DynamicCommandExceptionType UNKNOWN_RACE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("commands.race.fail", id));
private static final Function<Race, Composite> COMPOSITES = Util.memoize(race -> new Composite(race, null, null));

View file

@ -17,9 +17,10 @@ import com.minelittlepony.unicopia.network.track.TrackableObject;
import com.minelittlepony.unicopia.network.track.MsgTrackedValues.TrackerEntries;
import com.minelittlepony.unicopia.util.serialization.NbtSerialisable;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
@ -147,22 +148,24 @@ class MultiSpellSlot implements SpellSlots, NbtSerialisable {
}
@Override
public void read(PacketByteBuf buffer, WrapperLookup lookup) {
public void read(ByteBuf buffer, WrapperLookup lookup) {
byte contentType = buffer.readByte();
if (contentType == 1) {
readTrackedNbt(PacketCodecs.NBT_COMPOUND.decode(buffer), lookup);
} else {
T spell = this.spell.get();
if (spell != null) {
spell.getDataTracker().load(MsgTrackedValues.TrackerEntries.PACKET_CODEC.decode(buffer), lookup);
spell.getDataTracker().load(MsgTrackedValues.TrackerEntries.PACKET_CODEC.decode(
buffer instanceof RegistryByteBuf r ? r : new RegistryByteBuf(buffer, owner.asEntity().getRegistryManager())
), lookup);
}
}
}
@Override
public Optional<PacketByteBuf> write(Status status, WrapperLookup lookup) {
public Optional<? extends ByteBuf> write(Status status, WrapperLookup lookup) {
if (status != Status.DEFAULT) {
PacketByteBuf buffer = new PacketByteBuf(Unpooled.buffer());
ByteBuf buffer = Unpooled.buffer();
buffer.writeByte(1);
PacketCodecs.NBT_COMPOUND.encode(buffer, spell.toNBT(lookup));
return Optional.of(buffer);
@ -172,7 +175,7 @@ class MultiSpellSlot implements SpellSlots, NbtSerialisable {
return Optional.empty();
}
return spell.getDataTracker().getDirtyPairs(lookup).map(entries -> {
PacketByteBuf buffer = new PacketByteBuf(Unpooled.buffer());
RegistryByteBuf buffer = new RegistryByteBuf(Unpooled.buffer(), owner.asEntity().getRegistryManager());
buffer.writeByte(0);
TrackerEntries.PACKET_CODEC.encode(buffer, entries);
return buffer;

View file

@ -7,6 +7,7 @@ import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.network.track.DataTracker;
import com.minelittlepony.unicopia.network.track.DataTrackerManager;
import com.minelittlepony.unicopia.network.track.Trackable;
import com.minelittlepony.unicopia.network.track.TrackableDataType;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.UParticles;
@ -39,7 +40,7 @@ public class ItemImpl implements Equine<ItemEntity> {
this.tracker = trackers.getPrimaryTracker();
this.physics = new ItemPhysics(owner);
race = tracker.startTracking(Race.TRACKABLE_TYPE, Race.HUMAN);
race = tracker.startTracking(TrackableDataType.RACE, Race.HUMAN);
}
@Override

View file

@ -37,6 +37,7 @@ import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.util.*;
import com.minelittlepony.unicopia.network.*;
import com.minelittlepony.unicopia.network.track.DataTracker;
import com.minelittlepony.unicopia.network.track.TrackableDataType;
import com.minelittlepony.unicopia.server.world.UGameRules;
import com.minelittlepony.common.util.animation.LinearInterpolator;
import com.google.common.collect.Streams;
@ -121,8 +122,8 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
}
});
race = this.tracker.startTracking(Race.TRACKABLE_TYPE, Race.UNSET);
suppressedRace = this.tracker.startTracking(Race.TRACKABLE_TYPE, Race.UNSET);
race = this.tracker.startTracking(TrackableDataType.RACE, Race.UNSET);
suppressedRace = this.tracker.startTracking(TrackableDataType.RACE, Race.UNSET);
this.levels = new PlayerLevelStore(this, tracker, true, USounds.Vanilla.ENTITY_PLAYER_LEVELUP);
this.corruption = new PlayerLevelStore(this, tracker, false, USounds.ENTITY_PLAYER_CORRUPTION);
this.mana = addTicker(new ManaContainer(this, tracker));

View file

@ -7,11 +7,15 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import com.minelittlepony.unicopia.util.Untyped;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
public class DataTracker {
@ -44,14 +48,14 @@ public class DataTracker {
}
private <T> T get(Entry<T> entry) {
return getPair(entry).value;
return getPair(entry).value.value;
}
private <T> void set(Entry<T> entry, T value) {
Pair<T> pair = getPair(entry);
if (!Objects.equals(pair.value, value)) {
synchronized (this) {
pair.value = value;
pair.value.value = value;
dirtyIndices.add(entry.id());
}
}
@ -60,7 +64,7 @@ public class DataTracker {
@SuppressWarnings({ "unchecked", "rawtypes" })
synchronized void copyTo(DataTracker destination) {
for (int i = 0; i < codecs.size(); i++) {
((Pair<Object>)destination.codecs.get(i)).value = codecs.get(i).value;
((Pair<Object>)destination.codecs.get(i)).value.value = codecs.get(i).value.value;
if (i < persistentObjects.size() && i < destination.persistentObjects.size()) {
TrackableObject<?> a = persistentObjects.get(i);
TrackableObject<?> b = destination.persistentObjects.get(i);
@ -74,7 +78,7 @@ public class DataTracker {
synchronized Optional<MsgTrackedValues.TrackerEntries> getInitialPairs(WrapperLookup lookup) {
initial = false;
dirtyIndices = new IntOpenHashSet();
return Optional.of(new MsgTrackedValues.TrackerEntries(id, true, codecs, writePersistentObjects(lookup, true)));
return Optional.of(new MsgTrackedValues.TrackerEntries(id, true, codecs, Untyped.cast(writePersistentObjects(lookup, true))));
}
public synchronized Optional<MsgTrackedValues.TrackerEntries> getDirtyPairs(WrapperLookup lookup) {
@ -82,7 +86,7 @@ public class DataTracker {
return getInitialPairs(lookup);
}
Map<Integer, PacketByteBuf> updates = writePersistentObjects(lookup, false);
Map<Integer, ByteBuf> updates = writePersistentObjects(lookup, false);
if (dirtyIndices.isEmpty() && updates.isEmpty()) {
return Optional.empty();
@ -94,11 +98,11 @@ public class DataTracker {
for (int i : toSend) {
pairs.add(codecs.get(i));
}
return Optional.of(new MsgTrackedValues.TrackerEntries(id, false, pairs, updates));
return Optional.of(new MsgTrackedValues.TrackerEntries(id, false, pairs, Untyped.cast(updates)));
}
private Map<Integer, PacketByteBuf> writePersistentObjects(WrapperLookup lookup, boolean initial) {
Map<Integer, PacketByteBuf> updates = new HashMap<>();
private Map<Integer, ByteBuf> writePersistentObjects(WrapperLookup lookup, boolean initial) {
Map<Integer, ByteBuf> updates = new HashMap<>();
for (int i = 0; i < persistentObjects.size(); i++) {
TrackableObject<?> o = persistentObjects.get(i);
TrackableObject.Status status = initial ? TrackableObject.Status.NEW : o.getStatus();
@ -115,7 +119,7 @@ public class DataTracker {
} else {
for (var value : values.values()) {
if (value.id >= 0 && value.id < codecs.size()) {
if (codecs.get(value.id).type == value.type) {
if (codecs.get(value.id).value.type == value.value.type) {
codecs.set(value.id, value);
}
}
@ -140,28 +144,16 @@ public class DataTracker {
}
}
static class Pair<T> {
public static final PacketCodec<PacketByteBuf, Pair<?>> PACKET_CODEC = PacketCodec.ofStatic((buffer, pair) -> pair.write(buffer), Pair::new);
private final TrackableDataType<T> type;
public final int id;
public T value;
record Pair<T> (int id, TrackableDataType.TypedValue<T> value) {
@SuppressWarnings("unchecked")
public static final PacketCodec<RegistryByteBuf, Pair<?>> PACKET_CODEC = PacketCodec.tuple(
PacketCodecs.INTEGER, Pair::id,
TrackableDataType.PACKET_CODEC, Pair::value,
Pair::new
);
public Pair(int id, TrackableDataType<T> type, T value) {
this.id = id;
this.type = type;
this.value = value;
}
private Pair(PacketByteBuf buffer) {
this.id = buffer.readInt();
this.type = TrackableDataType.of(buffer);
this.value = type.read(buffer);
}
private void write(PacketByteBuf buffer) {
buffer.writeInt(id);
type.write(buffer, value);
this(id, new TrackableDataType.TypedValue<>(type, value));
}
}

View file

@ -5,8 +5,6 @@ import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.network.Channel;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.entity.Entity;
@ -23,13 +21,13 @@ public class DataTrackerManager {
private final List<ObjectTracker<?>> objectTrackers = new ObjectArrayList<>();
private final List<PacketEmitter> packetEmitters = new ObjectArrayList<>();
@Nullable
private DataTracker primaryTracker;
public DataTrackerManager(Entity entity) {
this.entity = entity;
this.lookup = entity.getWorld().getRegistryManager();
this.isClient = entity.getWorld().isClient;
this.primaryTracker = checkoutTracker();
}
public synchronized void addPacketEmitter(PacketEmitter packetEmitter) {
@ -37,9 +35,6 @@ public class DataTrackerManager {
}
public DataTracker getPrimaryTracker() {
if (primaryTracker == null) {
primaryTracker = checkoutTracker();
}
return primaryTracker;
}

View file

@ -11,9 +11,11 @@ import java.util.UUID;
import com.minelittlepony.unicopia.util.serialization.PacketCodecUtils;
import com.sollace.fabwork.api.packets.Handled;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.util.Uuids;
@ -23,7 +25,7 @@ public record MsgTrackedValues(
Optional<TrackerObjects> updatedObjects,
Optional<TrackerEntries> updatedTrackers
) implements Handled<PlayerEntity> {
public static final PacketCodec<PacketByteBuf, MsgTrackedValues> PACKET_CODEC = PacketCodec.tuple(
public static final PacketCodec<RegistryByteBuf, MsgTrackedValues> PACKET_CODEC = PacketCodec.tuple(
PacketCodecs.INTEGER, MsgTrackedValues::owner,
PacketCodecs.optional(TrackerObjects.PACKET_CODEC), MsgTrackedValues::updatedObjects,
PacketCodecs.optional(TrackerEntries.PACKET_CODEC), MsgTrackedValues::updatedTrackers,
@ -38,21 +40,21 @@ public record MsgTrackedValues(
}
}
public record TrackerObjects(int id, Set<UUID> removedValues, Map<UUID, PacketByteBuf> values) {
public static final PacketCodec<PacketByteBuf, TrackerObjects> PACKET_CODEC = PacketCodec.tuple(
public record TrackerObjects(int id, Set<UUID> removedValues, Map<UUID, ByteBuf> values) {
public static final PacketCodec<RegistryByteBuf, TrackerObjects> PACKET_CODEC = PacketCodec.tuple(
PacketCodecs.INTEGER, TrackerObjects::id,
Uuids.PACKET_CODEC.collect(PacketCodecs.toCollection(HashSet::new)), TrackerObjects::removedValues,
PacketCodecs.map(HashMap::new, Uuids.PACKET_CODEC, PacketCodecUtils.BUFFER), TrackerObjects::values,
PacketCodecs.map(HashMap::new, Uuids.PACKET_CODEC, PacketCodecUtils.REGISTRY_BUFFER), TrackerObjects::values,
TrackerObjects::new
);
}
public record TrackerEntries(int id, boolean wipe, List<DataTracker.Pair<?>> values, Map<Integer, PacketByteBuf> objects) {
public static final PacketCodec<PacketByteBuf, TrackerEntries> PACKET_CODEC = PacketCodec.tuple(
public record TrackerEntries(int id, boolean wipe, List<DataTracker.Pair<?>> values, Map<Integer, ByteBuf> objects) {
public static final PacketCodec<RegistryByteBuf, TrackerEntries> PACKET_CODEC = PacketCodec.tuple(
PacketCodecs.INTEGER, TrackerEntries::id,
PacketCodecs.BOOL, TrackerEntries::wipe,
DataTracker.Pair.PACKET_CODEC.collect(PacketCodecs.toCollection(ArrayList::new)), TrackerEntries::values,
PacketCodecs.map(HashMap::new, PacketCodecs.INTEGER, PacketCodecUtils.BUFFER), TrackerEntries::objects,
PacketCodecs.map(HashMap::new, PacketCodecs.INTEGER, PacketCodecUtils.REGISTRY_BUFFER), TrackerEntries::objects,
TrackerEntries::new
);
}

View file

@ -12,8 +12,8 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.network.track.TrackableObject.Status;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
public class ObjectTracker<T extends TrackableObject<T>> {
@ -89,7 +89,7 @@ public class ObjectTracker<T extends TrackableObject<T>> {
return Optional.empty();
}
Map<UUID, PacketByteBuf> updates = new HashMap<>();
Map<UUID, ByteBuf> updates = new HashMap<>();
quickAccess.entrySet().forEach(object -> {
object.getValue().write(Status.NEW, lookup).ifPresent(data -> {
updates.put(object.getKey(), data);
@ -101,7 +101,7 @@ public class ObjectTracker<T extends TrackableObject<T>> {
synchronized Optional<MsgTrackedValues.TrackerObjects> getDirtyPairs(WrapperLookup lookup) {
if (!trackedObjects.isEmpty()) {
Map<UUID, PacketByteBuf> updates = new HashMap<>();
Map<UUID, ByteBuf> updates = new HashMap<>();
Set<UUID> removedTrackableObjects = new HashSet<>();
trackedObjects.entrySet().removeIf(object -> {
TrackableObject.Status status = object.getValue().getStatus();

View file

@ -2,17 +2,19 @@ package com.minelittlepony.unicopia.network.track;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.util.Untyped;
import com.minelittlepony.unicopia.util.serialization.PacketCodecUtils;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.registry.RegistryKey;
@ -21,8 +23,12 @@ import net.minecraft.util.Uuids;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
public record TrackableDataType<T>(int id, PacketCodec<PacketByteBuf, T> codec) {
public record TrackableDataType<T>(int id, PacketCodec<RegistryByteBuf, TypedValue<T>> codec) {
private static final Int2ObjectMap<TrackableDataType<?>> REGISTRY = new Int2ObjectOpenHashMap<>();
public static final PacketCodec<RegistryByteBuf, TypedValue<?>> PACKET_CODEC = PacketCodecs.INTEGER.<RegistryByteBuf>cast().xmap(
id -> Objects.requireNonNull(REGISTRY.get(id.intValue()), "Unknown trackable data type id: " + id),
type -> type.id()
).dispatch(value -> Untyped.cast(value.type), TrackableDataType::codec);
public static final TrackableDataType<Integer> INT = of(Identifier.of("integer"), PacketCodecs.INTEGER);
public static final TrackableDataType<Float> FLOAT = of(Identifier.of("float"), PacketCodecs.FLOAT);
@ -36,30 +42,30 @@ public record TrackableDataType<T>(int id, PacketCodec<PacketByteBuf, T> codec)
public static final TrackableDataType<Optional<Vec3d>> OPTIONAL_VECTOR = of(Identifier.of("optional_vector"), PacketCodecUtils.OPTIONAL_VECTOR);
private static final TrackableDataType<Optional<RegistryKey<?>>> OPTIONAL_REGISTRY_KEY = of(Identifier.of("optional_registry_key"), PacketCodecUtils.OPTIONAL_REGISTRY_KEY);
public static final TrackableDataType<Race> RACE = TrackableDataType.of(Unicopia.id("race"), PacketCodecs.registryValue(Race.REGISTRY_KEY));
public static final TrackableDataType<Race> RACE = of(Unicopia.id("race"), PacketCodecs.registryValue(Race.REGISTRY_KEY));
@SuppressWarnings({ "rawtypes", "unchecked" })
public static <T> TrackableDataType<Optional<RegistryKey<T>>> ofRegistryKey() {
return (TrackableDataType)OPTIONAL_REGISTRY_KEY;
return Untyped.cast(OPTIONAL_REGISTRY_KEY);
}
@SuppressWarnings("unchecked")
public static <T> TrackableDataType<T> of(PacketByteBuf buffer) {
int id = buffer.readInt();
return Objects.requireNonNull((TrackableDataType<T>)REGISTRY.get(id), "Unknown trackable data type id: " + id);
public static <T> TrackableDataType<T> of(Identifier typeName, PacketCodec<? super RegistryByteBuf, T> codec) {
return Untyped.cast(REGISTRY.computeIfAbsent(typeName.hashCode(), t -> {
AtomicReference<TrackableDataType<T>> ref = new AtomicReference<>();
ref.set(new TrackableDataType<>(t, codec.<RegistryByteBuf>cast().xmap(
value -> new TypedValue<>(ref.get(), value),
value -> value.value
)));
return ref.get();
}));
}
@SuppressWarnings("unchecked")
public static <T> TrackableDataType<T> of(Identifier typeName, PacketCodec<? extends ByteBuf, T> codec) {
return (TrackableDataType<T>)REGISTRY.computeIfAbsent(typeName.hashCode(), t -> new TrackableDataType<>(t, (PacketCodec<PacketByteBuf, T>)codec));
}
public static class TypedValue<T> {
final TrackableDataType<T> type;
public T value;
public T read(PacketByteBuf buffer) {
return codec().decode(buffer);
public TypedValue(TrackableDataType<T> type, T value) {
this.type = type;
this.value = value;
}
public void write(PacketByteBuf buffer, T value) {
buffer.writeInt(id);
codec().encode(buffer, value);
}
}

View file

@ -2,22 +2,22 @@ package com.minelittlepony.unicopia.network.track;
import java.util.Optional;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
public interface TrackableObject<T extends TrackableObject<T>> {
Status getStatus();
default void read(PacketByteBuf buffer, WrapperLookup lookup) {
default void read(ByteBuf buffer, WrapperLookup lookup) {
readTrackedNbt(PacketCodecs.NBT_COMPOUND.decode(buffer), lookup);
}
default Optional<PacketByteBuf> write(Status status, WrapperLookup lookup) {
default Optional<? extends ByteBuf> write(Status status, WrapperLookup lookup) {
if (status == Status.NEW || status == Status.UPDATED) {
PacketByteBuf buffer = new PacketByteBuf(Unpooled.buffer());
ByteBuf buffer = Unpooled.buffer();
PacketCodecs.NBT_COMPOUND.encode(buffer, writeTrackedNbt(lookup));
return Optional.of(buffer);
}

View file

@ -10,6 +10,7 @@ import java.util.function.IntFunction;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.registry.RegistryKey;
@ -24,6 +25,13 @@ public interface PacketCodecUtils {
buffer.writeBytes(bytes);
}, buffer -> new PacketByteBuf(buffer.readBytes(buffer.readInt())));
PacketCodec<PacketByteBuf, Optional<PacketByteBuf>> OPTIONAL_BUFFER = PacketCodecs.optional(BUFFER);
PacketCodec<RegistryByteBuf, ByteBuf> REGISTRY_BUFFER = PacketCodec.of((buffer, bytes) -> {
buffer.writeInt(bytes.writerIndex());
buffer.writeBytes(bytes);
}, buffer -> new RegistryByteBuf(buffer.readBytes(buffer.readInt()), buffer.getRegistryManager()));
PacketCodec<RegistryByteBuf, Optional<ByteBuf>> OPTIONAL_REGISTRY_BUFFER = PacketCodecs.optional(REGISTRY_BUFFER);
PacketCodec<PacketByteBuf, RegistryKey<?>> REGISTRY_KEY = PacketCodec.tuple(
Identifier.PACKET_CODEC, RegistryKey::getRegistry,
Identifier.PACKET_CODEC, RegistryKey::getValue,