786 lines
31 KiB
Java
786 lines
31 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.ImmutableMultimap
|
|
* com.google.common.collect.ImmutableMultimap$Builder
|
|
* com.google.common.collect.Multimap
|
|
* com.google.gson.Gson
|
|
* com.google.gson.GsonBuilder
|
|
* com.google.gson.JsonElement
|
|
* com.google.gson.JsonSyntaxException
|
|
* com.mojang.authlib.GameProfile
|
|
* com.mojang.authlib.properties.Property
|
|
* com.mojang.authlib.properties.PropertyMap
|
|
* com.mojang.datafixers.util.Either
|
|
* com.mojang.serialization.Codec
|
|
* com.mojang.serialization.DynamicOps
|
|
* io.netty.buffer.ByteBuf
|
|
* io.netty.handler.codec.DecoderException
|
|
* io.netty.handler.codec.EncoderException
|
|
* org.joml.Quaternionfc
|
|
* org.joml.Vector3fc
|
|
*/
|
|
package net.minecraft.network.codec;
|
|
|
|
import com.google.common.collect.ImmutableMultimap;
|
|
import com.google.common.collect.Multimap;
|
|
import com.google.gson.Gson;
|
|
import com.google.gson.GsonBuilder;
|
|
import com.google.gson.JsonElement;
|
|
import com.google.gson.JsonSyntaxException;
|
|
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 com.mojang.serialization.DynamicOps;
|
|
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.function.BiFunction;
|
|
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.HolderLookup;
|
|
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.network.codec.StreamCodec;
|
|
import net.minecraft.resources.Identifier;
|
|
import net.minecraft.resources.RegistryOps;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.tags.TagKey;
|
|
import net.minecraft.util.ARGB;
|
|
import net.minecraft.util.LenientJsonParser;
|
|
import net.minecraft.util.Mth;
|
|
import org.joml.Quaternionfc;
|
|
import org.joml.Vector3fc;
|
|
|
|
public interface ByteBufCodecs {
|
|
public static final int MAX_INITIAL_COLLECTION_SIZE = 65536;
|
|
public static final StreamCodec<ByteBuf, Boolean> BOOL = new StreamCodec<ByteBuf, Boolean>(){
|
|
|
|
@Override
|
|
public Boolean decode(ByteBuf input) {
|
|
return input.readBoolean();
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Boolean value) {
|
|
output.writeBoolean(value.booleanValue());
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, Byte> BYTE = new StreamCodec<ByteBuf, Byte>(){
|
|
|
|
@Override
|
|
public Byte decode(ByteBuf input) {
|
|
return input.readByte();
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Byte value) {
|
|
output.writeByte((int)value.byteValue());
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, Float> ROTATION_BYTE = BYTE.map(Mth::unpackDegrees, Mth::packDegrees);
|
|
public static final StreamCodec<ByteBuf, Short> SHORT = new StreamCodec<ByteBuf, Short>(){
|
|
|
|
@Override
|
|
public Short decode(ByteBuf input) {
|
|
return input.readShort();
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Short value) {
|
|
output.writeShort((int)value.shortValue());
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, Integer> UNSIGNED_SHORT = new StreamCodec<ByteBuf, Integer>(){
|
|
|
|
@Override
|
|
public Integer decode(ByteBuf input) {
|
|
return input.readUnsignedShort();
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Integer value) {
|
|
output.writeShort(value.intValue());
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, Integer> INT = new StreamCodec<ByteBuf, Integer>(){
|
|
|
|
@Override
|
|
public Integer decode(ByteBuf input) {
|
|
return input.readInt();
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Integer value) {
|
|
output.writeInt(value.intValue());
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, Integer> VAR_INT = new StreamCodec<ByteBuf, Integer>(){
|
|
|
|
@Override
|
|
public Integer decode(ByteBuf input) {
|
|
return VarInt.read(input);
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Integer value) {
|
|
VarInt.write(output, value);
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, OptionalInt> OPTIONAL_VAR_INT = VAR_INT.map(i -> i == 0 ? OptionalInt.empty() : OptionalInt.of(i - 1), o -> o.isPresent() ? o.getAsInt() + 1 : 0);
|
|
public static final StreamCodec<ByteBuf, Long> LONG = new StreamCodec<ByteBuf, Long>(){
|
|
|
|
@Override
|
|
public Long decode(ByteBuf input) {
|
|
return input.readLong();
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Long value) {
|
|
output.writeLong(value.longValue());
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, Long> VAR_LONG = new StreamCodec<ByteBuf, Long>(){
|
|
|
|
@Override
|
|
public Long decode(ByteBuf input) {
|
|
return VarLong.read(input);
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Long value) {
|
|
VarLong.write(output, value);
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, Float> FLOAT = new StreamCodec<ByteBuf, Float>(){
|
|
|
|
@Override
|
|
public Float decode(ByteBuf input) {
|
|
return Float.valueOf(input.readFloat());
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Float value) {
|
|
output.writeFloat(value.floatValue());
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, Double> DOUBLE = new StreamCodec<ByteBuf, Double>(){
|
|
|
|
@Override
|
|
public Double decode(ByteBuf input) {
|
|
return input.readDouble();
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Double value) {
|
|
output.writeDouble(value.doubleValue());
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, byte[]> BYTE_ARRAY = new StreamCodec<ByteBuf, byte[]>(){
|
|
|
|
@Override
|
|
public byte[] decode(ByteBuf input) {
|
|
return FriendlyByteBuf.readByteArray(input);
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, byte[] value) {
|
|
FriendlyByteBuf.writeByteArray(output, value);
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, long[]> LONG_ARRAY = new StreamCodec<ByteBuf, long[]>(){
|
|
|
|
@Override
|
|
public long[] decode(ByteBuf input) {
|
|
return FriendlyByteBuf.readLongArray(input);
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, long[] value) {
|
|
FriendlyByteBuf.writeLongArray(output, value);
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, String> STRING_UTF8 = ByteBufCodecs.stringUtf8(Short.MAX_VALUE);
|
|
public static final StreamCodec<ByteBuf, Tag> TAG = ByteBufCodecs.tagCodec(() -> NbtAccounter.create(0x200000L));
|
|
public static final StreamCodec<ByteBuf, Tag> TRUSTED_TAG = ByteBufCodecs.tagCodec(NbtAccounter::unlimitedHeap);
|
|
public static final StreamCodec<ByteBuf, CompoundTag> COMPOUND_TAG = ByteBufCodecs.compoundTagCodec(() -> NbtAccounter.create(0x200000L));
|
|
public static final StreamCodec<ByteBuf, CompoundTag> TRUSTED_COMPOUND_TAG = ByteBufCodecs.compoundTagCodec(NbtAccounter::unlimitedHeap);
|
|
public static final StreamCodec<ByteBuf, Optional<CompoundTag>> OPTIONAL_COMPOUND_TAG = new StreamCodec<ByteBuf, Optional<CompoundTag>>(){
|
|
|
|
@Override
|
|
public Optional<CompoundTag> decode(ByteBuf input) {
|
|
return Optional.ofNullable(FriendlyByteBuf.readNbt(input));
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Optional<CompoundTag> value) {
|
|
FriendlyByteBuf.writeNbt(output, value.orElse(null));
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, Vector3fc> VECTOR3F = new StreamCodec<ByteBuf, Vector3fc>(){
|
|
|
|
@Override
|
|
public Vector3fc decode(ByteBuf input) {
|
|
return FriendlyByteBuf.readVector3f(input);
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Vector3fc value) {
|
|
FriendlyByteBuf.writeVector3f(output, value);
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, Quaternionfc> QUATERNIONF = new StreamCodec<ByteBuf, Quaternionfc>(){
|
|
|
|
@Override
|
|
public Quaternionfc decode(ByteBuf input) {
|
|
return FriendlyByteBuf.readQuaternion(input);
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Quaternionfc value) {
|
|
FriendlyByteBuf.writeQuaternion(output, value);
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, Integer> CONTAINER_ID = new StreamCodec<ByteBuf, Integer>(){
|
|
|
|
@Override
|
|
public Integer decode(ByteBuf input) {
|
|
return FriendlyByteBuf.readContainerId(input);
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Integer value) {
|
|
FriendlyByteBuf.writeContainerId(output, value);
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, PropertyMap> GAME_PROFILE_PROPERTIES = new StreamCodec<ByteBuf, PropertyMap>(){
|
|
private static final int MAX_PROPERTY_NAME_LENGTH = 64;
|
|
private static final int MAX_PROPERTY_VALUE_LENGTH = Short.MAX_VALUE;
|
|
private static final int MAX_PROPERTY_SIGNATURE_LENGTH = 1024;
|
|
private static final int MAX_PROPERTIES = 16;
|
|
|
|
@Override
|
|
public PropertyMap decode(ByteBuf input) {
|
|
int propertyCount = ByteBufCodecs.readCount(input, 16);
|
|
ImmutableMultimap.Builder result = ImmutableMultimap.builder();
|
|
for (int i = 0; i < propertyCount; ++i) {
|
|
String name = Utf8String.read(input, 64);
|
|
String value = Utf8String.read(input, Short.MAX_VALUE);
|
|
String signature = FriendlyByteBuf.readNullable(input, in -> Utf8String.read(in, 1024));
|
|
Property property = new Property(name, value, signature);
|
|
result.put((Object)property.name(), (Object)property);
|
|
}
|
|
return new PropertyMap((Multimap)result.build());
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, PropertyMap properties) {
|
|
ByteBufCodecs.writeCount(output, properties.size(), 16);
|
|
for (Property property : properties.values()) {
|
|
Utf8String.write(output, property.name(), 64);
|
|
Utf8String.write(output, property.value(), Short.MAX_VALUE);
|
|
FriendlyByteBuf.writeNullable(output, property.signature(), (out, signature) -> Utf8String.write(out, signature, 1024));
|
|
}
|
|
}
|
|
};
|
|
public static final StreamCodec<ByteBuf, String> PLAYER_NAME = ByteBufCodecs.stringUtf8(16);
|
|
public static final StreamCodec<ByteBuf, GameProfile> GAME_PROFILE = StreamCodec.composite(UUIDUtil.STREAM_CODEC, GameProfile::id, PLAYER_NAME, GameProfile::name, GAME_PROFILE_PROPERTIES, GameProfile::properties, GameProfile::new);
|
|
public static final StreamCodec<ByteBuf, Integer> RGB_COLOR = new StreamCodec<ByteBuf, Integer>(){
|
|
|
|
@Override
|
|
public Integer decode(ByteBuf input) {
|
|
return ARGB.color(input.readByte() & 0xFF, input.readByte() & 0xFF, input.readByte() & 0xFF);
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Integer value) {
|
|
output.writeByte(ARGB.red(value));
|
|
output.writeByte(ARGB.green(value));
|
|
output.writeByte(ARGB.blue(value));
|
|
}
|
|
};
|
|
|
|
public static StreamCodec<ByteBuf, byte[]> byteArray(final int maxSize) {
|
|
return new StreamCodec<ByteBuf, byte[]>(){
|
|
|
|
@Override
|
|
public byte[] decode(ByteBuf input) {
|
|
return FriendlyByteBuf.readByteArray(input, maxSize);
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, byte[] value) {
|
|
if (value.length > maxSize) {
|
|
throw new EncoderException("ByteArray with size " + value.length + " is bigger than allowed " + maxSize);
|
|
}
|
|
FriendlyByteBuf.writeByteArray(output, value);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static StreamCodec<ByteBuf, String> stringUtf8(final int maxStringLength) {
|
|
return new StreamCodec<ByteBuf, String>(){
|
|
|
|
@Override
|
|
public String decode(ByteBuf input) {
|
|
return Utf8String.read(input, maxStringLength);
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, String value) {
|
|
Utf8String.write(output, value, maxStringLength);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static StreamCodec<ByteBuf, Optional<Tag>> optionalTagCodec(final Supplier<NbtAccounter> accounter) {
|
|
return new StreamCodec<ByteBuf, Optional<Tag>>(){
|
|
|
|
@Override
|
|
public Optional<Tag> decode(ByteBuf input) {
|
|
return Optional.ofNullable(FriendlyByteBuf.readNbt(input, (NbtAccounter)accounter.get()));
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Optional<Tag> value) {
|
|
FriendlyByteBuf.writeNbt(output, value.orElse(null));
|
|
}
|
|
};
|
|
}
|
|
|
|
public static StreamCodec<ByteBuf, Tag> tagCodec(final Supplier<NbtAccounter> accounter) {
|
|
return new StreamCodec<ByteBuf, Tag>(){
|
|
|
|
@Override
|
|
public Tag decode(ByteBuf input) {
|
|
Tag result = FriendlyByteBuf.readNbt(input, (NbtAccounter)accounter.get());
|
|
if (result == null) {
|
|
throw new DecoderException("Expected non-null compound tag");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, Tag value) {
|
|
if (value == EndTag.INSTANCE) {
|
|
throw new EncoderException("Expected non-null compound tag");
|
|
}
|
|
FriendlyByteBuf.writeNbt(output, value);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static StreamCodec<ByteBuf, CompoundTag> compoundTagCodec(Supplier<NbtAccounter> accounter) {
|
|
return ByteBufCodecs.tagCodec(accounter).map(tag -> {
|
|
if (tag instanceof CompoundTag) {
|
|
CompoundTag compoundTag = (CompoundTag)tag;
|
|
return compoundTag;
|
|
}
|
|
throw new DecoderException("Not a compound tag: " + String.valueOf(tag));
|
|
}, compoundTag -> compoundTag);
|
|
}
|
|
|
|
public static <T> StreamCodec<ByteBuf, T> fromCodecTrusted(Codec<T> codec) {
|
|
return ByteBufCodecs.fromCodec(codec, NbtAccounter::unlimitedHeap);
|
|
}
|
|
|
|
public static <T> StreamCodec<ByteBuf, T> fromCodec(Codec<T> codec) {
|
|
return ByteBufCodecs.fromCodec(codec, () -> NbtAccounter.create(0x200000L));
|
|
}
|
|
|
|
public static <T, B extends ByteBuf, V> StreamCodec.CodecOperation<B, T, V> fromCodec(final DynamicOps<T> ops, final Codec<V> codec) {
|
|
return original -> new StreamCodec<B, V>(){
|
|
|
|
@Override
|
|
public V decode(B input) {
|
|
Object payload = original.decode(input);
|
|
return codec.parse(ops, payload).getOrThrow(msg -> new DecoderException("Failed to decode: " + msg + " " + String.valueOf(payload)));
|
|
}
|
|
|
|
@Override
|
|
public void encode(B output, V value) {
|
|
Object payload = codec.encodeStart(ops, value).getOrThrow(msg -> new EncoderException("Failed to encode: " + msg + " " + String.valueOf(value)));
|
|
original.encode(output, payload);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <T> StreamCodec<ByteBuf, T> fromCodec(Codec<T> codec, Supplier<NbtAccounter> accounter) {
|
|
return ByteBufCodecs.tagCodec(accounter).apply(ByteBufCodecs.fromCodec(NbtOps.INSTANCE, codec));
|
|
}
|
|
|
|
public static <T> StreamCodec<RegistryFriendlyByteBuf, T> fromCodecWithRegistriesTrusted(Codec<T> codec) {
|
|
return ByteBufCodecs.fromCodecWithRegistries(codec, NbtAccounter::unlimitedHeap);
|
|
}
|
|
|
|
public static <T> StreamCodec<RegistryFriendlyByteBuf, T> fromCodecWithRegistries(Codec<T> codec) {
|
|
return ByteBufCodecs.fromCodecWithRegistries(codec, () -> NbtAccounter.create(0x200000L));
|
|
}
|
|
|
|
public static <T> StreamCodec<RegistryFriendlyByteBuf, T> fromCodecWithRegistries(final Codec<T> codec, Supplier<NbtAccounter> accounter) {
|
|
final StreamCodec<ByteBuf, Tag> tagCodec = ByteBufCodecs.tagCodec(accounter);
|
|
return new StreamCodec<RegistryFriendlyByteBuf, T>(){
|
|
|
|
@Override
|
|
public T decode(RegistryFriendlyByteBuf input) {
|
|
Tag tag = (Tag)tagCodec.decode(input);
|
|
RegistryOps<Tag> ops = input.registryAccess().createSerializationContext(NbtOps.INSTANCE);
|
|
return codec.parse(ops, (Object)tag).getOrThrow(msg -> new DecoderException("Failed to decode: " + msg + " " + String.valueOf(tag)));
|
|
}
|
|
|
|
@Override
|
|
public void encode(RegistryFriendlyByteBuf output, T value) {
|
|
RegistryOps<Tag> ops = output.registryAccess().createSerializationContext(NbtOps.INSTANCE);
|
|
Tag tag = (Tag)codec.encodeStart(ops, value).getOrThrow(msg -> new EncoderException("Failed to encode: " + msg + " " + String.valueOf(value)));
|
|
tagCodec.encode(output, tag);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <B extends ByteBuf, V> StreamCodec<B, Optional<V>> optional(final StreamCodec<? super B, V> original) {
|
|
return new StreamCodec<B, Optional<V>>(){
|
|
|
|
@Override
|
|
public Optional<V> decode(B input) {
|
|
if (input.readBoolean()) {
|
|
return Optional.of(original.decode(input));
|
|
}
|
|
return Optional.empty();
|
|
}
|
|
|
|
@Override
|
|
public void encode(B output, Optional<V> value) {
|
|
if (value.isPresent()) {
|
|
output.writeBoolean(true);
|
|
original.encode(output, value.get());
|
|
} else {
|
|
output.writeBoolean(false);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
public static int readCount(ByteBuf input, int maxSize) {
|
|
int count = VarInt.read(input);
|
|
if (count > maxSize) {
|
|
throw new DecoderException(count + " elements exceeded max size of: " + maxSize);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public static void writeCount(ByteBuf output, int count, int maxSize) {
|
|
if (count > maxSize) {
|
|
throw new EncoderException(count + " elements exceeded max size of: " + maxSize);
|
|
}
|
|
VarInt.write(output, count);
|
|
}
|
|
|
|
public static <B extends ByteBuf, V, C extends Collection<V>> StreamCodec<B, C> collection(IntFunction<C> constructor, StreamCodec<? super B, V> elementCodec) {
|
|
return ByteBufCodecs.collection(constructor, elementCodec, Integer.MAX_VALUE);
|
|
}
|
|
|
|
public static <B extends ByteBuf, V, C extends Collection<V>> StreamCodec<B, C> collection(final IntFunction<C> constructor, final StreamCodec<? super B, V> elementCodec, final int maxSize) {
|
|
return new StreamCodec<B, C>(){
|
|
|
|
@Override
|
|
public C decode(B input) {
|
|
int count = ByteBufCodecs.readCount(input, maxSize);
|
|
Collection result = (Collection)constructor.apply(Math.min(count, 65536));
|
|
for (int i = 0; i < count; ++i) {
|
|
result.add(elementCodec.decode(input));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public void encode(B output, C value) {
|
|
ByteBufCodecs.writeCount(output, value.size(), maxSize);
|
|
for (Object element : value) {
|
|
elementCodec.encode(output, element);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <B extends ByteBuf, V, C extends Collection<V>> StreamCodec.CodecOperation<B, V, C> collection(IntFunction<C> constructor) {
|
|
return original -> ByteBufCodecs.collection(constructor, original);
|
|
}
|
|
|
|
public static <B extends ByteBuf, V> StreamCodec.CodecOperation<B, V, List<V>> list() {
|
|
return original -> ByteBufCodecs.collection(ArrayList::new, original);
|
|
}
|
|
|
|
public static <B extends ByteBuf, V> StreamCodec.CodecOperation<B, V, List<V>> list(int maxSize) {
|
|
return original -> ByteBufCodecs.collection(ArrayList::new, original, maxSize);
|
|
}
|
|
|
|
public static <B extends ByteBuf, K, V, M extends Map<K, V>> StreamCodec<B, M> map(IntFunction<? extends M> constructor, StreamCodec<? super B, K> keyCodec, StreamCodec<? super B, V> valueCodec) {
|
|
return ByteBufCodecs.map(constructor, keyCodec, valueCodec, Integer.MAX_VALUE);
|
|
}
|
|
|
|
public static <B extends ByteBuf, K, V, M extends Map<K, V>> StreamCodec<B, M> map(final IntFunction<? extends M> constructor, final StreamCodec<? super B, K> keyCodec, final StreamCodec<? super B, V> valueCodec, final int maxSize) {
|
|
return new StreamCodec<B, M>(){
|
|
|
|
@Override
|
|
public void encode(B output, M map) {
|
|
ByteBufCodecs.writeCount(output, map.size(), maxSize);
|
|
map.forEach((k, v) -> {
|
|
keyCodec.encode(output, k);
|
|
valueCodec.encode(output, v);
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public M decode(B input) {
|
|
int count = ByteBufCodecs.readCount(input, maxSize);
|
|
Map result = (Map)constructor.apply(Math.min(count, 65536));
|
|
for (int i = 0; i < count; ++i) {
|
|
Object key = keyCodec.decode(input);
|
|
Object value = valueCodec.decode(input);
|
|
result.put(key, value);
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <B extends ByteBuf, L, R> StreamCodec<B, Either<L, R>> either(final StreamCodec<? super B, L> leftCodec, final StreamCodec<? super B, R> rightCodec) {
|
|
return new StreamCodec<B, Either<L, R>>(){
|
|
|
|
@Override
|
|
public Either<L, R> decode(B input) {
|
|
if (input.readBoolean()) {
|
|
return Either.left(leftCodec.decode(input));
|
|
}
|
|
return Either.right(rightCodec.decode(input));
|
|
}
|
|
|
|
@Override
|
|
public void encode(B output, Either<L, R> value) {
|
|
value.ifLeft(left -> {
|
|
output.writeBoolean(true);
|
|
leftCodec.encode(output, left);
|
|
}).ifRight(right -> {
|
|
output.writeBoolean(false);
|
|
rightCodec.encode(output, right);
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <B extends ByteBuf, V> StreamCodec.CodecOperation<B, V, V> lengthPrefixed(final int maxSize, final BiFunction<B, ByteBuf, B> decorator) {
|
|
return original -> new StreamCodec<B, V>(){
|
|
|
|
@Override
|
|
public V decode(B input) {
|
|
int size = VarInt.read(input);
|
|
if (size > maxSize) {
|
|
throw new DecoderException("Buffer size " + size + " is larger than allowed limit of " + maxSize);
|
|
}
|
|
int index = input.readerIndex();
|
|
ByteBuf limitedSlice = (ByteBuf)decorator.apply(input, input.slice(index, size));
|
|
input.readerIndex(index + size);
|
|
return original.decode(limitedSlice);
|
|
}
|
|
|
|
/*
|
|
* WARNING - Removed try catching itself - possible behaviour change.
|
|
*/
|
|
@Override
|
|
public void encode(B output, V value) {
|
|
ByteBuf scratchBuffer = (ByteBuf)decorator.apply(output, output.alloc().buffer());
|
|
try {
|
|
original.encode(scratchBuffer, value);
|
|
int size = scratchBuffer.readableBytes();
|
|
if (size > maxSize) {
|
|
throw new EncoderException("Buffer size " + size + " is larger than allowed limit of " + maxSize);
|
|
}
|
|
VarInt.write(output, size);
|
|
output.writeBytes(scratchBuffer);
|
|
}
|
|
finally {
|
|
scratchBuffer.release();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <V> StreamCodec.CodecOperation<ByteBuf, V, V> lengthPrefixed(int maxSize) {
|
|
return ByteBufCodecs.lengthPrefixed(maxSize, (parent, child) -> child);
|
|
}
|
|
|
|
public static <V> StreamCodec.CodecOperation<RegistryFriendlyByteBuf, V, V> registryFriendlyLengthPrefixed(int maxSize) {
|
|
return ByteBufCodecs.lengthPrefixed(maxSize, (parent, child) -> new RegistryFriendlyByteBuf((ByteBuf)child, parent.registryAccess()));
|
|
}
|
|
|
|
public static <T> StreamCodec<ByteBuf, T> idMapper(final IntFunction<T> byId, final ToIntFunction<T> toId) {
|
|
return new StreamCodec<ByteBuf, T>(){
|
|
|
|
@Override
|
|
public T decode(ByteBuf input) {
|
|
int id = VarInt.read(input);
|
|
return byId.apply(id);
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, T value) {
|
|
int id = toId.applyAsInt(value);
|
|
VarInt.write(output, id);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <T> StreamCodec<ByteBuf, T> idMapper(IdMap<T> mapper) {
|
|
return ByteBufCodecs.idMapper(mapper::byIdOrThrow, mapper::getIdOrThrow);
|
|
}
|
|
|
|
private static <T, R> StreamCodec<RegistryFriendlyByteBuf, R> registry(final ResourceKey<? extends Registry<T>> registryKey, final Function<Registry<T>, IdMap<R>> mapExtractor) {
|
|
return new StreamCodec<RegistryFriendlyByteBuf, R>(){
|
|
|
|
private IdMap<R> getRegistryOrThrow(RegistryFriendlyByteBuf input) {
|
|
return (IdMap)mapExtractor.apply(input.registryAccess().lookupOrThrow(registryKey));
|
|
}
|
|
|
|
@Override
|
|
public R decode(RegistryFriendlyByteBuf input) {
|
|
int id = VarInt.read(input);
|
|
return this.getRegistryOrThrow(input).byIdOrThrow(id);
|
|
}
|
|
|
|
@Override
|
|
public void encode(RegistryFriendlyByteBuf output, R value) {
|
|
int id = this.getRegistryOrThrow(output).getIdOrThrow(value);
|
|
VarInt.write(output, id);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <T> StreamCodec<RegistryFriendlyByteBuf, T> registry(ResourceKey<? extends Registry<T>> registryKey) {
|
|
return ByteBufCodecs.registry(registryKey, r -> r);
|
|
}
|
|
|
|
public static <T> StreamCodec<RegistryFriendlyByteBuf, Holder<T>> holderRegistry(ResourceKey<? extends Registry<T>> registryKey) {
|
|
return ByteBufCodecs.registry(registryKey, Registry::asHolderIdMap);
|
|
}
|
|
|
|
public static <T> StreamCodec<RegistryFriendlyByteBuf, Holder<T>> holder(final ResourceKey<? extends Registry<T>> registryKey, final StreamCodec<? super RegistryFriendlyByteBuf, T> directCodec) {
|
|
return new StreamCodec<RegistryFriendlyByteBuf, Holder<T>>(){
|
|
private static final int DIRECT_HOLDER_ID = 0;
|
|
|
|
private IdMap<Holder<T>> getRegistryOrThrow(RegistryFriendlyByteBuf input) {
|
|
return input.registryAccess().lookupOrThrow(registryKey).asHolderIdMap();
|
|
}
|
|
|
|
@Override
|
|
public Holder<T> decode(RegistryFriendlyByteBuf input) {
|
|
int id = VarInt.read(input);
|
|
if (id == 0) {
|
|
return Holder.direct(directCodec.decode(input));
|
|
}
|
|
return this.getRegistryOrThrow(input).byIdOrThrow(id - 1);
|
|
}
|
|
|
|
@Override
|
|
public void encode(RegistryFriendlyByteBuf output, Holder<T> holder) {
|
|
switch (holder.kind()) {
|
|
case REFERENCE: {
|
|
int id = this.getRegistryOrThrow(output).getIdOrThrow(holder);
|
|
VarInt.write(output, id + 1);
|
|
break;
|
|
}
|
|
case DIRECT: {
|
|
VarInt.write(output, 0);
|
|
directCodec.encode(output, holder.value());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <T> StreamCodec<RegistryFriendlyByteBuf, HolderSet<T>> holderSet(final ResourceKey<? extends Registry<T>> registryKey) {
|
|
return new StreamCodec<RegistryFriendlyByteBuf, HolderSet<T>>(){
|
|
private static final int NAMED_SET = -1;
|
|
private final StreamCodec<RegistryFriendlyByteBuf, Holder<T>> holderCodec;
|
|
{
|
|
this.holderCodec = ByteBufCodecs.holderRegistry(registryKey);
|
|
}
|
|
|
|
@Override
|
|
public HolderSet<T> decode(RegistryFriendlyByteBuf input) {
|
|
int count = VarInt.read(input) - 1;
|
|
if (count == -1) {
|
|
HolderLookup.RegistryLookup registry = input.registryAccess().lookupOrThrow(registryKey);
|
|
return (HolderSet)registry.get(TagKey.create(registryKey, (Identifier)Identifier.STREAM_CODEC.decode(input))).orElseThrow();
|
|
}
|
|
ArrayList<Holder> holders = new ArrayList<Holder>(Math.min(count, 65536));
|
|
for (int i = 0; i < count; ++i) {
|
|
holders.add((Holder)this.holderCodec.decode(input));
|
|
}
|
|
return HolderSet.direct(holders);
|
|
}
|
|
|
|
@Override
|
|
public void encode(RegistryFriendlyByteBuf output, HolderSet<T> value) {
|
|
Optional key = value.unwrapKey();
|
|
if (key.isPresent()) {
|
|
VarInt.write(output, 0);
|
|
Identifier.STREAM_CODEC.encode(output, key.get().location());
|
|
} else {
|
|
VarInt.write(output, value.size() + 1);
|
|
for (Holder holder : value) {
|
|
this.holderCodec.encode(output, holder);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
public static StreamCodec<ByteBuf, JsonElement> lenientJson(final int maxStringLength) {
|
|
return new StreamCodec<ByteBuf, JsonElement>(){
|
|
private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();
|
|
|
|
@Override
|
|
public JsonElement decode(ByteBuf input) {
|
|
String payload = Utf8String.read(input, maxStringLength);
|
|
try {
|
|
return LenientJsonParser.parse(payload);
|
|
}
|
|
catch (JsonSyntaxException e) {
|
|
throw new DecoderException("Failed to parse JSON", (Throwable)e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void encode(ByteBuf output, JsonElement value) {
|
|
String payload = GSON.toJson(value);
|
|
Utf8String.write(output, payload, maxStringLength);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|