package net.minecraft.network.codec; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; import com.mojang.authlib.properties.PropertyMap; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.EncoderException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.OptionalInt; import java.util.UUID; import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.Supplier; import java.util.function.ToIntFunction; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.core.IdMap; import net.minecraft.core.Registry; import net.minecraft.core.UUIDUtil; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.EndTag; import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.Utf8String; import net.minecraft.network.VarInt; import net.minecraft.network.VarLong; import net.minecraft.resources.RegistryOps; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; import net.minecraft.util.Mth; import org.joml.Quaternionf; import org.joml.Vector3f; public interface ByteBufCodecs { int MAX_INITIAL_COLLECTION_SIZE = 65536; StreamCodec BOOL = new StreamCodec() { public Boolean decode(ByteBuf byteBuf) { return byteBuf.readBoolean(); } public void encode(ByteBuf byteBuf, Boolean boolean_) { byteBuf.writeBoolean(boolean_); } }; StreamCodec BYTE = new StreamCodec() { public Byte decode(ByteBuf byteBuf) { return byteBuf.readByte(); } public void encode(ByteBuf byteBuf, Byte byte_) { byteBuf.writeByte(byte_); } }; StreamCodec ROTATION_BYTE = BYTE.map(Mth::unpackDegrees, Mth::packDegrees); StreamCodec SHORT = new StreamCodec() { public Short decode(ByteBuf byteBuf) { return byteBuf.readShort(); } public void encode(ByteBuf byteBuf, Short short_) { byteBuf.writeShort(short_); } }; StreamCodec UNSIGNED_SHORT = new StreamCodec() { public Integer decode(ByteBuf byteBuf) { return byteBuf.readUnsignedShort(); } public void encode(ByteBuf byteBuf, Integer integer) { byteBuf.writeShort(integer); } }; StreamCodec INT = new StreamCodec() { public Integer decode(ByteBuf byteBuf) { return byteBuf.readInt(); } public void encode(ByteBuf byteBuf, Integer integer) { byteBuf.writeInt(integer); } }; StreamCodec VAR_INT = new StreamCodec() { public Integer decode(ByteBuf byteBuf) { return VarInt.read(byteBuf); } public void encode(ByteBuf byteBuf, Integer integer) { VarInt.write(byteBuf, integer); } }; StreamCodec OPTIONAL_VAR_INT = VAR_INT.map( integer -> integer == 0 ? OptionalInt.empty() : OptionalInt.of(integer - 1), optionalInt -> optionalInt.isPresent() ? optionalInt.getAsInt() + 1 : 0 ); StreamCodec LONG = new StreamCodec() { public Long decode(ByteBuf byteBuf) { return byteBuf.readLong(); } public void encode(ByteBuf byteBuf, Long long_) { byteBuf.writeLong(long_); } }; StreamCodec VAR_LONG = new StreamCodec() { public Long decode(ByteBuf byteBuf) { return VarLong.read(byteBuf); } public void encode(ByteBuf byteBuf, Long long_) { VarLong.write(byteBuf, long_); } }; StreamCodec FLOAT = new StreamCodec() { public Float decode(ByteBuf byteBuf) { return byteBuf.readFloat(); } public void encode(ByteBuf byteBuf, Float float_) { byteBuf.writeFloat(float_); } }; StreamCodec DOUBLE = new StreamCodec() { public Double decode(ByteBuf byteBuf) { return byteBuf.readDouble(); } public void encode(ByteBuf byteBuf, Double double_) { byteBuf.writeDouble(double_); } }; StreamCodec BYTE_ARRAY = new StreamCodec() { public byte[] decode(ByteBuf byteBuf) { return FriendlyByteBuf.readByteArray(byteBuf); } public void encode(ByteBuf byteBuf, byte[] bs) { FriendlyByteBuf.writeByteArray(byteBuf, bs); } }; StreamCodec STRING_UTF8 = stringUtf8(32767); StreamCodec TAG = tagCodec(() -> NbtAccounter.create(2097152L)); StreamCodec TRUSTED_TAG = tagCodec(NbtAccounter::unlimitedHeap); StreamCodec COMPOUND_TAG = compoundTagCodec(() -> NbtAccounter.create(2097152L)); StreamCodec TRUSTED_COMPOUND_TAG = compoundTagCodec(NbtAccounter::unlimitedHeap); StreamCodec> OPTIONAL_COMPOUND_TAG = new StreamCodec>() { public Optional decode(ByteBuf byteBuf) { return Optional.ofNullable(FriendlyByteBuf.readNbt(byteBuf)); } public void encode(ByteBuf byteBuf, Optional optional) { FriendlyByteBuf.writeNbt(byteBuf, (Tag)optional.orElse(null)); } }; StreamCodec VECTOR3F = new StreamCodec() { public Vector3f decode(ByteBuf byteBuf) { return FriendlyByteBuf.readVector3f(byteBuf); } public void encode(ByteBuf byteBuf, Vector3f vector3f) { FriendlyByteBuf.writeVector3f(byteBuf, vector3f); } }; StreamCodec QUATERNIONF = new StreamCodec() { public Quaternionf decode(ByteBuf byteBuf) { return FriendlyByteBuf.readQuaternion(byteBuf); } public void encode(ByteBuf byteBuf, Quaternionf quaternionf) { FriendlyByteBuf.writeQuaternion(byteBuf, quaternionf); } }; StreamCodec CONTAINER_ID = new StreamCodec() { public Integer decode(ByteBuf byteBuf) { return FriendlyByteBuf.readContainerId(byteBuf); } public void encode(ByteBuf byteBuf, Integer integer) { FriendlyByteBuf.writeContainerId(byteBuf, integer); } }; StreamCodec GAME_PROFILE_PROPERTIES = new StreamCodec() { private static final int MAX_PROPERTY_NAME_LENGTH = 64; private static final int MAX_PROPERTY_VALUE_LENGTH = 32767; private static final int MAX_PROPERTY_SIGNATURE_LENGTH = 1024; private static final int MAX_PROPERTIES = 16; public PropertyMap decode(ByteBuf byteBuf) { int i = ByteBufCodecs.readCount(byteBuf, 16); PropertyMap propertyMap = new PropertyMap(); for (int j = 0; j < i; j++) { String string = Utf8String.read(byteBuf, 64); String string2 = Utf8String.read(byteBuf, 32767); String string3 = FriendlyByteBuf.readNullable(byteBuf, byteBufx -> Utf8String.read(byteBufx, 1024)); Property property = new Property(string, string2, string3); propertyMap.put(property.name(), property); } return propertyMap; } public void encode(ByteBuf byteBuf, PropertyMap propertyMap) { ByteBufCodecs.writeCount(byteBuf, propertyMap.size(), 16); for (Property property : propertyMap.values()) { Utf8String.write(byteBuf, property.name(), 64); Utf8String.write(byteBuf, property.value(), 32767); FriendlyByteBuf.writeNullable(byteBuf, property.signature(), (byteBufx, string) -> Utf8String.write(byteBufx, string, 1024)); } } }; StreamCodec GAME_PROFILE = new StreamCodec() { public GameProfile decode(ByteBuf byteBuf) { UUID uUID = UUIDUtil.STREAM_CODEC.decode(byteBuf); String string = Utf8String.read(byteBuf, 16); GameProfile gameProfile = new GameProfile(uUID, string); gameProfile.getProperties().putAll(ByteBufCodecs.GAME_PROFILE_PROPERTIES.decode(byteBuf)); return gameProfile; } public void encode(ByteBuf byteBuf, GameProfile gameProfile) { UUIDUtil.STREAM_CODEC.encode(byteBuf, gameProfile.getId()); Utf8String.write(byteBuf, gameProfile.getName(), 16); ByteBufCodecs.GAME_PROFILE_PROPERTIES.encode(byteBuf, gameProfile.getProperties()); } }; static StreamCodec byteArray(int maxSize) { return new StreamCodec() { public byte[] decode(ByteBuf buffer) { return FriendlyByteBuf.readByteArray(buffer, maxSize); } public void encode(ByteBuf buffer, byte[] value) { if (value.length > maxSize) { throw new EncoderException("ByteArray with size " + value.length + " is bigger than allowed " + maxSize); } else { FriendlyByteBuf.writeByteArray(buffer, value); } } }; } static StreamCodec stringUtf8(int maxLength) { return new StreamCodec() { public String decode(ByteBuf byteBuf) { return Utf8String.read(byteBuf, maxLength); } public void encode(ByteBuf byteBuf, String string) { Utf8String.write(byteBuf, string, maxLength); } }; } static StreamCodec tagCodec(Supplier accounter) { return new StreamCodec() { public Tag decode(ByteBuf byteBuf) { Tag tag = FriendlyByteBuf.readNbt(byteBuf, (NbtAccounter)accounter.get()); if (tag == null) { throw new DecoderException("Expected non-null compound tag"); } else { return tag; } } public void encode(ByteBuf byteBuf, Tag tag) { if (tag == EndTag.INSTANCE) { throw new EncoderException("Expected non-null compound tag"); } else { FriendlyByteBuf.writeNbt(byteBuf, tag); } } }; } static StreamCodec compoundTagCodec(Supplier accounterSupplier) { return tagCodec(accounterSupplier).map(tag -> { if (tag instanceof CompoundTag compoundTag) { return compoundTag; } else { throw new DecoderException("Not a compound tag: " + tag); } }, compoundTag -> compoundTag); } static StreamCodec fromCodecTrusted(Codec codec) { return fromCodec(codec, NbtAccounter::unlimitedHeap); } static StreamCodec fromCodec(Codec codec) { return fromCodec(codec, () -> NbtAccounter.create(2097152L)); } static StreamCodec fromCodec(Codec codec, Supplier accounterSupplier) { return tagCodec(accounterSupplier) .map( tag -> codec.parse(NbtOps.INSTANCE, tag).getOrThrow(string -> new DecoderException("Failed to decode: " + string + " " + tag)), object -> codec.encodeStart(NbtOps.INSTANCE, (T)object).getOrThrow(string -> new EncoderException("Failed to encode: " + string + " " + object)) ); } static StreamCodec fromCodecWithRegistriesTrusted(Codec codec) { return fromCodecWithRegistries(codec, NbtAccounter::unlimitedHeap); } static StreamCodec fromCodecWithRegistries(Codec codec) { return fromCodecWithRegistries(codec, () -> NbtAccounter.create(2097152L)); } static StreamCodec fromCodecWithRegistries(Codec codec, Supplier accounterSupplier) { final StreamCodec streamCodec = tagCodec(accounterSupplier); return new StreamCodec() { public T decode(RegistryFriendlyByteBuf registryFriendlyByteBuf) { Tag tag = streamCodec.decode(registryFriendlyByteBuf); RegistryOps registryOps = registryFriendlyByteBuf.registryAccess().createSerializationContext(NbtOps.INSTANCE); return codec.parse(registryOps, tag).getOrThrow(string -> new DecoderException("Failed to decode: " + string + " " + tag)); } public void encode(RegistryFriendlyByteBuf registryFriendlyByteBuf, T object) { RegistryOps registryOps = registryFriendlyByteBuf.registryAccess().createSerializationContext(NbtOps.INSTANCE); Tag tag = codec.encodeStart(registryOps, object).getOrThrow(string -> new EncoderException("Failed to encode: " + string + " " + object)); streamCodec.encode(registryFriendlyByteBuf, tag); } }; } static StreamCodec> optional(StreamCodec codec) { return new StreamCodec>() { public Optional decode(B byteBuf) { return byteBuf.readBoolean() ? Optional.of(codec.decode(byteBuf)) : Optional.empty(); } public void encode(B byteBuf, Optional optional) { if (optional.isPresent()) { byteBuf.writeBoolean(true); codec.encode(byteBuf, (V)optional.get()); } else { byteBuf.writeBoolean(false); } } }; } static int readCount(ByteBuf buffer, int maxSize) { int i = VarInt.read(buffer); if (i > maxSize) { throw new DecoderException(i + " elements exceeded max size of: " + maxSize); } else { return i; } } static void writeCount(ByteBuf buffer, int count, int maxSize) { if (count > maxSize) { throw new EncoderException(count + " elements exceeded max size of: " + maxSize); } else { VarInt.write(buffer, count); } } static > StreamCodec collection(IntFunction factory, StreamCodec codec) { return collection(factory, codec, Integer.MAX_VALUE); } static > StreamCodec collection(IntFunction factory, StreamCodec codec, int maxSize) { return new StreamCodec() { public C decode(B byteBuf) { int i = ByteBufCodecs.readCount(byteBuf, maxSize); C collection = (C)factory.apply(Math.min(i, 65536)); for (int j = 0; j < i; j++) { collection.add(codec.decode(byteBuf)); } return collection; } public void encode(B byteBuf, C collection) { ByteBufCodecs.writeCount(byteBuf, collection.size(), maxSize); for (V object : collection) { codec.encode(byteBuf, object); } } }; } static > StreamCodec.CodecOperation collection(IntFunction factory) { return streamCodec -> collection(factory, streamCodec); } static StreamCodec.CodecOperation> list() { return streamCodec -> collection(ArrayList::new, streamCodec); } static StreamCodec.CodecOperation> list(int maxSize) { return streamCodec -> collection(ArrayList::new, streamCodec, maxSize); } static > StreamCodec map( IntFunction factory, StreamCodec keyCodec, StreamCodec valueCodec ) { return map(factory, keyCodec, valueCodec, Integer.MAX_VALUE); } static > StreamCodec map( IntFunction factory, StreamCodec keyCodec, StreamCodec valueCodec, int maxSize ) { return new StreamCodec() { public void encode(B byteBuf, M map) { ByteBufCodecs.writeCount(byteBuf, map.size(), maxSize); map.forEach((object, object2) -> { keyCodec.encode(byteBuf, (K)object); valueCodec.encode(byteBuf, (V)object2); }); } public M decode(B byteBuf) { int i = ByteBufCodecs.readCount(byteBuf, maxSize); M map = (M)factory.apply(Math.min(i, 65536)); for (int j = 0; j < i; j++) { K object = keyCodec.decode(byteBuf); V object2 = valueCodec.decode(byteBuf); map.put(object, object2); } return map; } }; } static StreamCodec> either(StreamCodec leftCodec, StreamCodec rightCodec) { return new StreamCodec>() { public Either decode(B byteBuf) { return byteBuf.readBoolean() ? Either.left(leftCodec.decode(byteBuf)) : Either.right(rightCodec.decode(byteBuf)); } public void encode(B byteBuf, Either either) { either.ifLeft(object -> { byteBuf.writeBoolean(true); leftCodec.encode(byteBuf, (L)object); }).ifRight(object -> { byteBuf.writeBoolean(false); rightCodec.encode(byteBuf, (R)object); }); } }; } static StreamCodec idMapper(IntFunction idLookup, ToIntFunction idGetter) { return new StreamCodec() { public T decode(ByteBuf byteBuf) { int i = VarInt.read(byteBuf); return (T)idLookup.apply(i); } public void encode(ByteBuf byteBuf, T object) { int i = idGetter.applyAsInt(object); VarInt.write(byteBuf, i); } }; } static StreamCodec idMapper(IdMap idMap) { return idMapper(idMap::byIdOrThrow, idMap::getIdOrThrow); } private static StreamCodec registry( ResourceKey> registryKey, Function, IdMap> idGetter ) { return new StreamCodec() { private IdMap getRegistryOrThrow(RegistryFriendlyByteBuf registryFriendlyByteBuf) { return (IdMap)idGetter.apply(registryFriendlyByteBuf.registryAccess().lookupOrThrow(registryKey)); } public R decode(RegistryFriendlyByteBuf registryFriendlyByteBuf) { int i = VarInt.read(registryFriendlyByteBuf); return (R)this.getRegistryOrThrow(registryFriendlyByteBuf).byIdOrThrow(i); } public void encode(RegistryFriendlyByteBuf registryFriendlyByteBuf, R object) { int i = this.getRegistryOrThrow(registryFriendlyByteBuf).getIdOrThrow(object); VarInt.write(registryFriendlyByteBuf, i); } }; } static StreamCodec registry(ResourceKey> registryKey) { return registry(registryKey, registry -> registry); } static StreamCodec> holderRegistry(ResourceKey> registryKey) { return registry(registryKey, Registry::asHolderIdMap); } static StreamCodec> holder( ResourceKey> registryKey, StreamCodec codec ) { return new StreamCodec>() { private static final int DIRECT_HOLDER_ID = 0; private IdMap> getRegistryOrThrow(RegistryFriendlyByteBuf registryFriendlyByteBuf) { return registryFriendlyByteBuf.registryAccess().lookupOrThrow(registryKey).asHolderIdMap(); } public Holder decode(RegistryFriendlyByteBuf registryFriendlyByteBuf) { int i = VarInt.read(registryFriendlyByteBuf); return i == 0 ? Holder.direct(codec.decode(registryFriendlyByteBuf)) : (Holder)this.getRegistryOrThrow(registryFriendlyByteBuf).byIdOrThrow(i - 1); } public void encode(RegistryFriendlyByteBuf registryFriendlyByteBuf, Holder holder) { switch (holder.kind()) { case REFERENCE: int i = this.getRegistryOrThrow(registryFriendlyByteBuf).getIdOrThrow(holder); VarInt.write(registryFriendlyByteBuf, i + 1); break; case DIRECT: VarInt.write(registryFriendlyByteBuf, 0); codec.encode(registryFriendlyByteBuf, holder.value()); } } }; } static StreamCodec> holderSet(ResourceKey> registryKey) { return new StreamCodec>() { private static final int NAMED_SET = -1; private final StreamCodec> holderCodec = ByteBufCodecs.holderRegistry(registryKey); public HolderSet decode(RegistryFriendlyByteBuf registryFriendlyByteBuf) { int i = VarInt.read(registryFriendlyByteBuf) - 1; if (i == -1) { Registry registry = registryFriendlyByteBuf.registryAccess().lookupOrThrow(registryKey); return (HolderSet)registry.get(TagKey.create(registryKey, ResourceLocation.STREAM_CODEC.decode(registryFriendlyByteBuf))).orElseThrow(); } else { List> list = new ArrayList(Math.min(i, 65536)); for (int j = 0; j < i; j++) { list.add(this.holderCodec.decode(registryFriendlyByteBuf)); } return HolderSet.direct(list); } } public void encode(RegistryFriendlyByteBuf registryFriendlyByteBuf, HolderSet holderSet) { Optional> optional = holderSet.unwrapKey(); if (optional.isPresent()) { VarInt.write(registryFriendlyByteBuf, 0); ResourceLocation.STREAM_CODEC.encode(registryFriendlyByteBuf, ((TagKey)optional.get()).location()); } else { VarInt.write(registryFriendlyByteBuf, holderSet.size() + 1); for (Holder holder : holderSet) { this.holderCodec.encode(registryFriendlyByteBuf, holder); } } } }; } }