730 lines
37 KiB
Java
730 lines
37 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.BiMap
|
|
* com.google.common.collect.HashBiMap
|
|
* com.google.common.collect.ImmutableList
|
|
* com.google.common.collect.ImmutableMap
|
|
* com.google.common.collect.ImmutableMap$Builder
|
|
* com.google.common.collect.ImmutableMultimap
|
|
* com.google.common.collect.ImmutableMultimap$Builder
|
|
* com.google.common.collect.Multimap
|
|
* com.google.common.primitives.UnsignedBytes
|
|
* com.google.gson.JsonElement
|
|
* com.mojang.authlib.GameProfile
|
|
* com.mojang.authlib.properties.Property
|
|
* com.mojang.authlib.properties.PropertyMap
|
|
* com.mojang.datafixers.kinds.App
|
|
* com.mojang.datafixers.kinds.Applicative
|
|
* com.mojang.datafixers.util.Either
|
|
* com.mojang.datafixers.util.Pair
|
|
* com.mojang.serialization.Codec
|
|
* com.mojang.serialization.Codec$ResultFunction
|
|
* com.mojang.serialization.DataResult
|
|
* com.mojang.serialization.DataResult$Error
|
|
* com.mojang.serialization.Decoder
|
|
* com.mojang.serialization.Dynamic
|
|
* com.mojang.serialization.DynamicOps
|
|
* com.mojang.serialization.JavaOps
|
|
* com.mojang.serialization.JsonOps
|
|
* com.mojang.serialization.Lifecycle
|
|
* com.mojang.serialization.MapCodec
|
|
* com.mojang.serialization.MapLike
|
|
* com.mojang.serialization.RecordBuilder
|
|
* com.mojang.serialization.codecs.BaseMapCodec
|
|
* com.mojang.serialization.codecs.RecordCodecBuilder
|
|
* it.unimi.dsi.fastutil.floats.FloatArrayList
|
|
* it.unimi.dsi.fastutil.objects.Object2BooleanMap
|
|
* it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap
|
|
* it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
|
* org.apache.commons.lang3.StringEscapeUtils
|
|
* org.apache.commons.lang3.mutable.MutableObject
|
|
* org.joml.AxisAngle4f
|
|
* org.joml.Matrix4f
|
|
* org.joml.Matrix4fc
|
|
* org.joml.Quaternionf
|
|
* org.joml.Quaternionfc
|
|
* org.joml.Vector2f
|
|
* org.joml.Vector2fc
|
|
* org.joml.Vector3f
|
|
* org.joml.Vector3fc
|
|
* org.joml.Vector3i
|
|
* org.joml.Vector3ic
|
|
* org.joml.Vector4f
|
|
* org.joml.Vector4fc
|
|
* org.jspecify.annotations.Nullable
|
|
*/
|
|
package net.minecraft.util;
|
|
|
|
import com.google.common.collect.BiMap;
|
|
import com.google.common.collect.HashBiMap;
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.ImmutableMap;
|
|
import com.google.common.collect.ImmutableMultimap;
|
|
import com.google.common.collect.Multimap;
|
|
import com.google.common.primitives.UnsignedBytes;
|
|
import com.google.gson.JsonElement;
|
|
import com.mojang.authlib.GameProfile;
|
|
import com.mojang.authlib.properties.Property;
|
|
import com.mojang.authlib.properties.PropertyMap;
|
|
import com.mojang.datafixers.kinds.App;
|
|
import com.mojang.datafixers.kinds.Applicative;
|
|
import com.mojang.datafixers.util.Either;
|
|
import com.mojang.datafixers.util.Pair;
|
|
import com.mojang.serialization.Codec;
|
|
import com.mojang.serialization.DataResult;
|
|
import com.mojang.serialization.Decoder;
|
|
import com.mojang.serialization.Dynamic;
|
|
import com.mojang.serialization.DynamicOps;
|
|
import com.mojang.serialization.JavaOps;
|
|
import com.mojang.serialization.JsonOps;
|
|
import com.mojang.serialization.Lifecycle;
|
|
import com.mojang.serialization.MapCodec;
|
|
import com.mojang.serialization.MapLike;
|
|
import com.mojang.serialization.RecordBuilder;
|
|
import com.mojang.serialization.codecs.BaseMapCodec;
|
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|
import it.unimi.dsi.fastutil.floats.FloatArrayList;
|
|
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
|
|
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
|
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
import java.net.URI;
|
|
import java.net.URISyntaxException;
|
|
import java.time.Instant;
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.time.temporal.TemporalAccessor;
|
|
import java.util.Arrays;
|
|
import java.util.Base64;
|
|
import java.util.BitSet;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HexFormat;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.OptionalLong;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import java.util.function.BiFunction;
|
|
import java.util.function.Function;
|
|
import java.util.function.IntFunction;
|
|
import java.util.function.ToIntFunction;
|
|
import java.util.regex.Pattern;
|
|
import java.util.regex.PatternSyntaxException;
|
|
import java.util.stream.Stream;
|
|
import net.minecraft.core.HolderSet;
|
|
import net.minecraft.core.UUIDUtil;
|
|
import net.minecraft.nbt.NbtOps;
|
|
import net.minecraft.nbt.Tag;
|
|
import net.minecraft.resources.Identifier;
|
|
import net.minecraft.util.ARGB;
|
|
import net.minecraft.util.StringUtil;
|
|
import net.minecraft.util.Util;
|
|
import org.apache.commons.lang3.StringEscapeUtils;
|
|
import org.apache.commons.lang3.mutable.MutableObject;
|
|
import org.joml.AxisAngle4f;
|
|
import org.joml.Matrix4f;
|
|
import org.joml.Matrix4fc;
|
|
import org.joml.Quaternionf;
|
|
import org.joml.Quaternionfc;
|
|
import org.joml.Vector2f;
|
|
import org.joml.Vector2fc;
|
|
import org.joml.Vector3f;
|
|
import org.joml.Vector3fc;
|
|
import org.joml.Vector3i;
|
|
import org.joml.Vector3ic;
|
|
import org.joml.Vector4f;
|
|
import org.joml.Vector4fc;
|
|
import org.jspecify.annotations.Nullable;
|
|
|
|
public class ExtraCodecs {
|
|
public static final Codec<JsonElement> JSON = ExtraCodecs.converter(JsonOps.INSTANCE);
|
|
public static final Codec<Object> JAVA = ExtraCodecs.converter(JavaOps.INSTANCE);
|
|
public static final Codec<Tag> NBT = ExtraCodecs.converter(NbtOps.INSTANCE);
|
|
public static final Codec<Vector2fc> VECTOR2F = Codec.FLOAT.listOf().comapFlatMap(input -> Util.fixedSize(input, 2).map(d -> new Vector2f(((Float)d.get(0)).floatValue(), ((Float)d.get(1)).floatValue())), vec -> List.of(Float.valueOf(vec.x()), Float.valueOf(vec.y())));
|
|
public static final Codec<Vector3fc> VECTOR3F = Codec.FLOAT.listOf().comapFlatMap(input -> Util.fixedSize(input, 3).map(d -> new Vector3f(((Float)d.get(0)).floatValue(), ((Float)d.get(1)).floatValue(), ((Float)d.get(2)).floatValue())), vec -> List.of(Float.valueOf(vec.x()), Float.valueOf(vec.y()), Float.valueOf(vec.z())));
|
|
public static final Codec<Vector3ic> VECTOR3I = Codec.INT.listOf().comapFlatMap(input -> Util.fixedSize(input, 3).map(d -> new Vector3i(((Integer)d.get(0)).intValue(), ((Integer)d.get(1)).intValue(), ((Integer)d.get(2)).intValue())), vec -> List.of(Integer.valueOf(vec.x()), Integer.valueOf(vec.y()), Integer.valueOf(vec.z())));
|
|
public static final Codec<Vector4fc> VECTOR4F = Codec.FLOAT.listOf().comapFlatMap(input -> Util.fixedSize(input, 4).map(d -> new Vector4f(((Float)d.get(0)).floatValue(), ((Float)d.get(1)).floatValue(), ((Float)d.get(2)).floatValue(), ((Float)d.get(3)).floatValue())), vec -> List.of(Float.valueOf(vec.x()), Float.valueOf(vec.y()), Float.valueOf(vec.z()), Float.valueOf(vec.w())));
|
|
public static final Codec<Quaternionfc> QUATERNIONF_COMPONENTS = Codec.FLOAT.listOf().comapFlatMap(input -> Util.fixedSize(input, 4).map(d -> new Quaternionf(((Float)d.get(0)).floatValue(), ((Float)d.get(1)).floatValue(), ((Float)d.get(2)).floatValue(), ((Float)d.get(3)).floatValue()).normalize()), q -> List.of(Float.valueOf(q.x()), Float.valueOf(q.y()), Float.valueOf(q.z()), Float.valueOf(q.w())));
|
|
public static final Codec<AxisAngle4f> AXISANGLE4F = RecordCodecBuilder.create(i -> i.group((App)Codec.FLOAT.fieldOf("angle").forGetter(o -> Float.valueOf(o.angle)), (App)VECTOR3F.fieldOf("axis").forGetter(o -> new Vector3f(o.x, o.y, o.z))).apply((Applicative)i, AxisAngle4f::new));
|
|
public static final Codec<Quaternionfc> QUATERNIONF = Codec.withAlternative(QUATERNIONF_COMPONENTS, (Codec)AXISANGLE4F.xmap(Quaternionf::new, AxisAngle4f::new));
|
|
public static final Codec<Matrix4fc> MATRIX4F = Codec.FLOAT.listOf().comapFlatMap(input -> Util.fixedSize(input, 16).map(l -> {
|
|
Matrix4f result = new Matrix4f();
|
|
for (int i = 0; i < l.size(); ++i) {
|
|
result.setRowColumn(i >> 2, i & 3, ((Float)l.get(i)).floatValue());
|
|
}
|
|
return result.determineProperties();
|
|
}), m -> {
|
|
FloatArrayList output = new FloatArrayList(16);
|
|
for (int i = 0; i < 16; ++i) {
|
|
output.add(m.getRowColumn(i >> 2, i & 3));
|
|
}
|
|
return output;
|
|
});
|
|
private static final String HEX_COLOR_PREFIX = "#";
|
|
public static final Codec<Integer> RGB_COLOR_CODEC = Codec.withAlternative((Codec)Codec.INT, VECTOR3F, v -> ARGB.colorFromFloat(1.0f, v.x(), v.y(), v.z()));
|
|
public static final Codec<Integer> ARGB_COLOR_CODEC = Codec.withAlternative((Codec)Codec.INT, VECTOR4F, v -> ARGB.colorFromFloat(v.w(), v.x(), v.y(), v.z()));
|
|
public static final Codec<Integer> STRING_RGB_COLOR = Codec.withAlternative((Codec)ExtraCodecs.hexColor(6).xmap(ARGB::opaque, ARGB::transparent), RGB_COLOR_CODEC);
|
|
public static final Codec<Integer> STRING_ARGB_COLOR = Codec.withAlternative(ExtraCodecs.hexColor(8), ARGB_COLOR_CODEC);
|
|
public static final Codec<Integer> UNSIGNED_BYTE = Codec.BYTE.flatComapMap(UnsignedBytes::toInt, integer -> {
|
|
if (integer > 255) {
|
|
return DataResult.error(() -> "Unsigned byte was too large: " + integer + " > 255");
|
|
}
|
|
return DataResult.success((Object)integer.byteValue());
|
|
});
|
|
public static final Codec<Integer> NON_NEGATIVE_INT = ExtraCodecs.intRangeWithMessage(0, Integer.MAX_VALUE, n -> "Value must be non-negative: " + n);
|
|
public static final Codec<Integer> POSITIVE_INT = ExtraCodecs.intRangeWithMessage(1, Integer.MAX_VALUE, n -> "Value must be positive: " + n);
|
|
public static final Codec<Long> NON_NEGATIVE_LONG = ExtraCodecs.longRangeWithMessage(0L, Long.MAX_VALUE, n -> "Value must be non-negative: " + n);
|
|
public static final Codec<Long> POSITIVE_LONG = ExtraCodecs.longRangeWithMessage(1L, Long.MAX_VALUE, n -> "Value must be positive: " + n);
|
|
public static final Codec<Float> NON_NEGATIVE_FLOAT = ExtraCodecs.floatRangeMinInclusiveWithMessage(0.0f, Float.MAX_VALUE, n -> "Value must be non-negative: " + n);
|
|
public static final Codec<Float> POSITIVE_FLOAT = ExtraCodecs.floatRangeMinExclusiveWithMessage(0.0f, Float.MAX_VALUE, n -> "Value must be positive: " + n);
|
|
public static final Codec<Pattern> PATTERN = Codec.STRING.comapFlatMap(pattern -> {
|
|
try {
|
|
return DataResult.success((Object)Pattern.compile(pattern));
|
|
}
|
|
catch (PatternSyntaxException e) {
|
|
return DataResult.error(() -> "Invalid regex pattern '" + pattern + "': " + e.getMessage());
|
|
}
|
|
}, Pattern::pattern);
|
|
public static final Codec<Instant> INSTANT_ISO8601 = ExtraCodecs.temporalCodec(DateTimeFormatter.ISO_INSTANT).xmap(Instant::from, Function.identity());
|
|
public static final Codec<byte[]> BASE64_STRING = Codec.STRING.comapFlatMap(string -> {
|
|
try {
|
|
return DataResult.success((Object)Base64.getDecoder().decode((String)string));
|
|
}
|
|
catch (IllegalArgumentException e) {
|
|
return DataResult.error(() -> "Malformed base64 string");
|
|
}
|
|
}, bytes -> Base64.getEncoder().encodeToString((byte[])bytes));
|
|
public static final Codec<String> ESCAPED_STRING = Codec.STRING.comapFlatMap(str -> DataResult.success((Object)StringEscapeUtils.unescapeJava((String)str)), StringEscapeUtils::escapeJava);
|
|
public static final Codec<TagOrElementLocation> TAG_OR_ELEMENT_ID = Codec.STRING.comapFlatMap(name -> name.startsWith(HEX_COLOR_PREFIX) ? Identifier.read(name.substring(1)).map(id -> new TagOrElementLocation((Identifier)id, true)) : Identifier.read(name).map(id -> new TagOrElementLocation((Identifier)id, false)), TagOrElementLocation::decoratedId);
|
|
public static final Function<Optional<Long>, OptionalLong> toOptionalLong = o -> o.map(OptionalLong::of).orElseGet(OptionalLong::empty);
|
|
public static final Function<OptionalLong, Optional<Long>> fromOptionalLong = l -> l.isPresent() ? Optional.of(l.getAsLong()) : Optional.empty();
|
|
public static final Codec<BitSet> BIT_SET = Codec.LONG_STREAM.xmap(longStream -> BitSet.valueOf(longStream.toArray()), bitSet -> Arrays.stream(bitSet.toLongArray()));
|
|
private static final Codec<Property> PROPERTY = RecordCodecBuilder.create(i -> i.group((App)Codec.STRING.fieldOf("name").forGetter(Property::name), (App)Codec.STRING.fieldOf("value").forGetter(Property::value), (App)Codec.STRING.lenientOptionalFieldOf("signature").forGetter(property -> Optional.ofNullable(property.signature()))).apply((Applicative)i, (name, value, signature) -> new Property(name, value, (String)signature.orElse(null))));
|
|
public static final Codec<PropertyMap> PROPERTY_MAP = Codec.either((Codec)Codec.unboundedMap((Codec)Codec.STRING, (Codec)Codec.STRING.listOf()), (Codec)PROPERTY.listOf()).xmap(mapListEither -> {
|
|
ImmutableMultimap.Builder result = ImmutableMultimap.builder();
|
|
mapListEither.ifLeft(s -> s.forEach((name, properties) -> {
|
|
for (String property : properties) {
|
|
result.put(name, (Object)new Property(name, property));
|
|
}
|
|
})).ifRight(properties -> {
|
|
for (Property property : properties) {
|
|
result.put((Object)property.name(), (Object)property);
|
|
}
|
|
});
|
|
return new PropertyMap((Multimap)result.build());
|
|
}, propertyMap -> Either.right(propertyMap.values().stream().toList()));
|
|
public static final Codec<String> PLAYER_NAME = Codec.string((int)0, (int)16).validate(name -> {
|
|
if (StringUtil.isValidPlayerName(name)) {
|
|
return DataResult.success((Object)name);
|
|
}
|
|
return DataResult.error(() -> "Player name contained disallowed characters: '" + name + "'");
|
|
});
|
|
public static final Codec<GameProfile> AUTHLIB_GAME_PROFILE = ExtraCodecs.gameProfileCodec(UUIDUtil.AUTHLIB_CODEC).codec();
|
|
public static final MapCodec<GameProfile> STORED_GAME_PROFILE = ExtraCodecs.gameProfileCodec(UUIDUtil.CODEC);
|
|
public static final Codec<String> NON_EMPTY_STRING = Codec.STRING.validate(value -> value.isEmpty() ? DataResult.error(() -> "Expected non-empty string") : DataResult.success((Object)value));
|
|
public static final Codec<Integer> CODEPOINT = Codec.STRING.comapFlatMap(s -> {
|
|
int[] codepoint = s.codePoints().toArray();
|
|
if (codepoint.length != 1) {
|
|
return DataResult.error(() -> "Expected one codepoint, got: " + s);
|
|
}
|
|
return DataResult.success((Object)codepoint[0]);
|
|
}, Character::toString);
|
|
public static final Codec<String> RESOURCE_PATH_CODEC = Codec.STRING.validate(s -> {
|
|
if (!Identifier.isValidPath(s)) {
|
|
return DataResult.error(() -> "Invalid string to use as a resource path element: " + s);
|
|
}
|
|
return DataResult.success((Object)s);
|
|
});
|
|
public static final Codec<URI> UNTRUSTED_URI = Codec.STRING.comapFlatMap(string -> {
|
|
try {
|
|
return DataResult.success((Object)Util.parseAndValidateUntrustedUri(string));
|
|
}
|
|
catch (URISyntaxException e) {
|
|
return DataResult.error(e::getMessage);
|
|
}
|
|
}, URI::toString);
|
|
public static final Codec<String> CHAT_STRING = Codec.STRING.validate(string -> {
|
|
for (int i = 0; i < string.length(); ++i) {
|
|
char c = string.charAt(i);
|
|
if (StringUtil.isAllowedChatCharacter(c)) continue;
|
|
return DataResult.error(() -> "Disallowed chat character: '" + c + "'");
|
|
}
|
|
return DataResult.success((Object)string);
|
|
});
|
|
|
|
public static <T> Codec<T> converter(DynamicOps<T> ops) {
|
|
return Codec.PASSTHROUGH.xmap(t -> t.convert(ops).getValue(), t -> new Dynamic(ops, t));
|
|
}
|
|
|
|
private static Codec<Integer> hexColor(int expectedDigits) {
|
|
long maxValue = (1L << expectedDigits * 4) - 1L;
|
|
return Codec.STRING.comapFlatMap(string -> {
|
|
if (!string.startsWith(HEX_COLOR_PREFIX)) {
|
|
return DataResult.error(() -> "Hex color must begin with #");
|
|
}
|
|
int digits = string.length() - HEX_COLOR_PREFIX.length();
|
|
if (digits != expectedDigits) {
|
|
return DataResult.error(() -> "Hex color is wrong size, expected " + expectedDigits + " digits but got " + digits);
|
|
}
|
|
try {
|
|
long value = HexFormat.fromHexDigitsToLong(string, HEX_COLOR_PREFIX.length(), string.length());
|
|
if (value < 0L || value > maxValue) {
|
|
return DataResult.error(() -> "Color value out of range: " + string);
|
|
}
|
|
return DataResult.success((Object)((int)value));
|
|
}
|
|
catch (NumberFormatException e) {
|
|
return DataResult.error(() -> "Invalid color value: " + string);
|
|
}
|
|
}, value -> HEX_COLOR_PREFIX + HexFormat.of().toHexDigits(value.intValue(), expectedDigits));
|
|
}
|
|
|
|
public static <P, I> Codec<I> intervalCodec(Codec<P> pointCodec, String lowerBoundName, String upperBoundName, BiFunction<P, P, DataResult<I>> makeInterval, Function<I, P> getMin, Function<I, P> getMax) {
|
|
Codec arrayCodec = Codec.list(pointCodec).comapFlatMap(list -> Util.fixedSize(list, 2).flatMap(l -> {
|
|
Object min = l.get(0);
|
|
Object max = l.get(1);
|
|
return (DataResult)makeInterval.apply(min, max);
|
|
}), p -> ImmutableList.of(getMin.apply(p), getMax.apply(p)));
|
|
Codec objectCodec = RecordCodecBuilder.create(i -> i.group((App)pointCodec.fieldOf(lowerBoundName).forGetter(Pair::getFirst), (App)pointCodec.fieldOf(upperBoundName).forGetter(Pair::getSecond)).apply((Applicative)i, Pair::of)).comapFlatMap(p -> (DataResult)makeInterval.apply(p.getFirst(), p.getSecond()), i -> Pair.of(getMin.apply(i), getMax.apply(i)));
|
|
Codec arrayOrObjectCodec = Codec.withAlternative((Codec)arrayCodec, (Codec)objectCodec);
|
|
return Codec.either(pointCodec, (Codec)arrayOrObjectCodec).comapFlatMap(either -> (DataResult)either.map(min -> (DataResult)makeInterval.apply(min, min), DataResult::success), p -> {
|
|
Object max;
|
|
Object min = getMin.apply(p);
|
|
if (Objects.equals(min, max = getMax.apply(p))) {
|
|
return Either.left(min);
|
|
}
|
|
return Either.right((Object)p);
|
|
});
|
|
}
|
|
|
|
public static <A> Codec.ResultFunction<A> orElsePartial(final A value) {
|
|
return new Codec.ResultFunction<A>(){
|
|
|
|
public <T> DataResult<Pair<A, T>> apply(DynamicOps<T> ops, T input, DataResult<Pair<A, T>> a) {
|
|
MutableObject message = new MutableObject();
|
|
Optional result = a.resultOrPartial(arg_0 -> ((MutableObject)message).setValue(arg_0));
|
|
if (result.isPresent()) {
|
|
return a;
|
|
}
|
|
return DataResult.error(() -> "(" + (String)message.get() + " -> using default)", (Object)Pair.of((Object)value, input));
|
|
}
|
|
|
|
public <T> DataResult<T> coApply(DynamicOps<T> ops, A input, DataResult<T> t) {
|
|
return t;
|
|
}
|
|
|
|
public String toString() {
|
|
return "OrElsePartial[" + String.valueOf(value) + "]";
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <E> Codec<E> idResolverCodec(ToIntFunction<E> toInt, IntFunction<@Nullable E> fromInt, int unknownId) {
|
|
return Codec.INT.flatXmap(id -> Optional.ofNullable(fromInt.apply((int)id)).map(DataResult::success).orElseGet(() -> DataResult.error(() -> "Unknown element id: " + id)), e -> {
|
|
int id = toInt.applyAsInt(e);
|
|
return id == unknownId ? DataResult.error(() -> "Element with unknown id: " + String.valueOf(e)) : DataResult.success((Object)id);
|
|
});
|
|
}
|
|
|
|
public static <I, E> Codec<E> idResolverCodec(Codec<I> value, Function<I, @Nullable E> fromId, Function<E, @Nullable I> toId) {
|
|
return value.flatXmap(id -> {
|
|
Object element = fromId.apply(id);
|
|
return element == null ? DataResult.error(() -> "Unknown element id: " + String.valueOf(id)) : DataResult.success(element);
|
|
}, e -> {
|
|
Object id = toId.apply(e);
|
|
if (id == null) {
|
|
return DataResult.error(() -> "Element with unknown id: " + String.valueOf(e));
|
|
}
|
|
return DataResult.success(id);
|
|
});
|
|
}
|
|
|
|
public static <E> Codec<E> orCompressed(final Codec<E> normal, final Codec<E> compressed) {
|
|
return new Codec<E>(){
|
|
|
|
public <T> DataResult<T> encode(E input, DynamicOps<T> ops, T prefix) {
|
|
if (ops.compressMaps()) {
|
|
return compressed.encode(input, ops, prefix);
|
|
}
|
|
return normal.encode(input, ops, prefix);
|
|
}
|
|
|
|
public <T> DataResult<Pair<E, T>> decode(DynamicOps<T> ops, T input) {
|
|
if (ops.compressMaps()) {
|
|
return compressed.decode(ops, input);
|
|
}
|
|
return normal.decode(ops, input);
|
|
}
|
|
|
|
public String toString() {
|
|
return String.valueOf(normal) + " orCompressed " + String.valueOf(compressed);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <E> MapCodec<E> orCompressed(final MapCodec<E> normal, final MapCodec<E> compressed) {
|
|
return new MapCodec<E>(){
|
|
|
|
public <T> RecordBuilder<T> encode(E input, DynamicOps<T> ops, RecordBuilder<T> prefix) {
|
|
if (ops.compressMaps()) {
|
|
return compressed.encode(input, ops, prefix);
|
|
}
|
|
return normal.encode(input, ops, prefix);
|
|
}
|
|
|
|
public <T> DataResult<E> decode(DynamicOps<T> ops, MapLike<T> input) {
|
|
if (ops.compressMaps()) {
|
|
return compressed.decode(ops, input);
|
|
}
|
|
return normal.decode(ops, input);
|
|
}
|
|
|
|
public <T> Stream<T> keys(DynamicOps<T> ops) {
|
|
return compressed.keys(ops);
|
|
}
|
|
|
|
public String toString() {
|
|
return String.valueOf(normal) + " orCompressed " + String.valueOf(compressed);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <E> Codec<E> overrideLifecycle(Codec<E> codec, final Function<E, Lifecycle> decodeLifecycle, final Function<E, Lifecycle> encodeLifecycle) {
|
|
return codec.mapResult(new Codec.ResultFunction<E>(){
|
|
|
|
public <T> DataResult<Pair<E, T>> apply(DynamicOps<T> ops, T input, DataResult<Pair<E, T>> a) {
|
|
return a.result().map(r -> a.setLifecycle((Lifecycle)decodeLifecycle.apply(r.getFirst()))).orElse(a);
|
|
}
|
|
|
|
public <T> DataResult<T> coApply(DynamicOps<T> ops, E input, DataResult<T> t) {
|
|
return t.setLifecycle((Lifecycle)encodeLifecycle.apply(input));
|
|
}
|
|
|
|
public String toString() {
|
|
return "WithLifecycle[" + String.valueOf(decodeLifecycle) + " " + String.valueOf(encodeLifecycle) + "]";
|
|
}
|
|
});
|
|
}
|
|
|
|
public static <E> Codec<E> overrideLifecycle(Codec<E> codec, Function<E, Lifecycle> lifecycleGetter) {
|
|
return ExtraCodecs.overrideLifecycle(codec, lifecycleGetter, lifecycleGetter);
|
|
}
|
|
|
|
public static <K, V> StrictUnboundedMapCodec<K, V> strictUnboundedMap(Codec<K> keyCodec, Codec<V> elementCodec) {
|
|
return new StrictUnboundedMapCodec<K, V>(keyCodec, elementCodec);
|
|
}
|
|
|
|
public static <E> Codec<List<E>> compactListCodec(Codec<E> elementCodec) {
|
|
return ExtraCodecs.compactListCodec(elementCodec, elementCodec.listOf());
|
|
}
|
|
|
|
public static <E> Codec<List<E>> compactListCodec(Codec<E> elementCodec, Codec<List<E>> listCodec) {
|
|
return Codec.either(listCodec, elementCodec).xmap(e -> (List)e.map(l -> l, List::of), v -> v.size() == 1 ? Either.right(v.getFirst()) : Either.left((Object)v));
|
|
}
|
|
|
|
private static Codec<Integer> intRangeWithMessage(int minInclusive, int maxInclusive, Function<Integer, String> error) {
|
|
return Codec.INT.validate(value -> {
|
|
if (value.compareTo(minInclusive) >= 0 && value.compareTo(maxInclusive) <= 0) {
|
|
return DataResult.success((Object)value);
|
|
}
|
|
return DataResult.error(() -> (String)error.apply((Integer)value));
|
|
});
|
|
}
|
|
|
|
public static Codec<Integer> intRange(int minInclusive, int maxInclusive) {
|
|
return ExtraCodecs.intRangeWithMessage(minInclusive, maxInclusive, n -> "Value must be within range [" + minInclusive + ";" + maxInclusive + "]: " + n);
|
|
}
|
|
|
|
private static Codec<Long> longRangeWithMessage(long minInclusive, long maxInclusive, Function<Long, String> error) {
|
|
return Codec.LONG.validate(value -> {
|
|
if ((long)value.compareTo(minInclusive) >= 0L && (long)value.compareTo(maxInclusive) <= 0L) {
|
|
return DataResult.success((Object)value);
|
|
}
|
|
return DataResult.error(() -> (String)error.apply((Long)value));
|
|
});
|
|
}
|
|
|
|
public static Codec<Long> longRange(int minInclusive, int maxInclusive) {
|
|
return ExtraCodecs.longRangeWithMessage(minInclusive, maxInclusive, n -> "Value must be within range [" + minInclusive + ";" + maxInclusive + "]: " + n);
|
|
}
|
|
|
|
private static Codec<Float> floatRangeMinInclusiveWithMessage(float minInclusive, float maxInclusive, Function<Float, String> error) {
|
|
return Codec.FLOAT.validate(value -> {
|
|
if (value.compareTo(Float.valueOf(minInclusive)) >= 0 && value.compareTo(Float.valueOf(maxInclusive)) <= 0) {
|
|
return DataResult.success((Object)value);
|
|
}
|
|
return DataResult.error(() -> (String)error.apply((Float)value));
|
|
});
|
|
}
|
|
|
|
private static Codec<Float> floatRangeMinExclusiveWithMessage(float minExclusive, float maxInclusive, Function<Float, String> error) {
|
|
return Codec.FLOAT.validate(value -> {
|
|
if (value.compareTo(Float.valueOf(minExclusive)) > 0 && value.compareTo(Float.valueOf(maxInclusive)) <= 0) {
|
|
return DataResult.success((Object)value);
|
|
}
|
|
return DataResult.error(() -> (String)error.apply((Float)value));
|
|
});
|
|
}
|
|
|
|
public static Codec<Float> floatRange(float minInclusive, float maxInclusive) {
|
|
return ExtraCodecs.floatRangeMinInclusiveWithMessage(minInclusive, maxInclusive, n -> "Value must be within range [" + minInclusive + ";" + maxInclusive + "]: " + n);
|
|
}
|
|
|
|
public static <T> Codec<List<T>> nonEmptyList(Codec<List<T>> listCodec) {
|
|
return listCodec.validate(list -> list.isEmpty() ? DataResult.error(() -> "List must have contents") : DataResult.success((Object)list));
|
|
}
|
|
|
|
public static <T> Codec<HolderSet<T>> nonEmptyHolderSet(Codec<HolderSet<T>> listCodec) {
|
|
return listCodec.validate(list -> {
|
|
if (list.unwrap().right().filter(List::isEmpty).isPresent()) {
|
|
return DataResult.error(() -> "List must have contents");
|
|
}
|
|
return DataResult.success((Object)list);
|
|
});
|
|
}
|
|
|
|
public static <M extends Map<?, ?>> Codec<M> nonEmptyMap(Codec<M> mapCodec) {
|
|
return mapCodec.validate(map -> map.isEmpty() ? DataResult.error(() -> "Map must have contents") : DataResult.success((Object)map));
|
|
}
|
|
|
|
public static <E> MapCodec<E> retrieveContext(Function<DynamicOps<?>, DataResult<E>> getter) {
|
|
class ContextRetrievalCodec
|
|
extends MapCodec<E> {
|
|
final /* synthetic */ Function val$getter;
|
|
|
|
ContextRetrievalCodec(Function function) {
|
|
this.val$getter = function;
|
|
}
|
|
|
|
public <T> RecordBuilder<T> encode(E input, DynamicOps<T> ops, RecordBuilder<T> prefix) {
|
|
return prefix;
|
|
}
|
|
|
|
public <T> DataResult<E> decode(DynamicOps<T> ops, MapLike<T> input) {
|
|
return (DataResult)this.val$getter.apply(ops);
|
|
}
|
|
|
|
public String toString() {
|
|
return "ContextRetrievalCodec[" + String.valueOf(this.val$getter) + "]";
|
|
}
|
|
|
|
public <T> Stream<T> keys(DynamicOps<T> ops) {
|
|
return Stream.empty();
|
|
}
|
|
}
|
|
return new ContextRetrievalCodec(getter);
|
|
}
|
|
|
|
public static <E, L extends Collection<E>, T> Function<L, DataResult<L>> ensureHomogenous(Function<E, T> typeGetter) {
|
|
return container -> {
|
|
Iterator it = container.iterator();
|
|
if (it.hasNext()) {
|
|
Object firstType = typeGetter.apply(it.next());
|
|
while (it.hasNext()) {
|
|
Object next = it.next();
|
|
Object nextType = typeGetter.apply(next);
|
|
if (nextType == firstType) continue;
|
|
return DataResult.error(() -> "Mixed type list: element " + String.valueOf(next) + " had type " + String.valueOf(nextType) + ", but list is of type " + String.valueOf(firstType));
|
|
}
|
|
}
|
|
return DataResult.success((Object)container, (Lifecycle)Lifecycle.stable());
|
|
};
|
|
}
|
|
|
|
public static <A> Codec<A> catchDecoderException(final Codec<A> codec) {
|
|
return Codec.of(codec, (Decoder)new Decoder<A>(){
|
|
|
|
public <T> DataResult<Pair<A, T>> decode(DynamicOps<T> ops, T input) {
|
|
try {
|
|
return codec.decode(ops, input);
|
|
}
|
|
catch (Exception e) {
|
|
return DataResult.error(() -> "Caught exception decoding " + String.valueOf(input) + ": " + e.getMessage());
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public static Codec<TemporalAccessor> temporalCodec(DateTimeFormatter formatter) {
|
|
return Codec.STRING.comapFlatMap(s -> {
|
|
try {
|
|
return DataResult.success((Object)formatter.parse((CharSequence)s));
|
|
}
|
|
catch (Exception e) {
|
|
return DataResult.error(e::getMessage);
|
|
}
|
|
}, formatter::format);
|
|
}
|
|
|
|
public static MapCodec<OptionalLong> asOptionalLong(MapCodec<Optional<Long>> fieldCodec) {
|
|
return fieldCodec.xmap(toOptionalLong, fromOptionalLong);
|
|
}
|
|
|
|
private static MapCodec<GameProfile> gameProfileCodec(Codec<UUID> uuidCodec) {
|
|
return RecordCodecBuilder.mapCodec(i -> i.group((App)uuidCodec.fieldOf("id").forGetter(GameProfile::id), (App)PLAYER_NAME.fieldOf("name").forGetter(GameProfile::name), (App)PROPERTY_MAP.optionalFieldOf("properties", (Object)PropertyMap.EMPTY).forGetter(GameProfile::properties)).apply((Applicative)i, GameProfile::new));
|
|
}
|
|
|
|
public static <K, V> Codec<Map<K, V>> sizeLimitedMap(Codec<Map<K, V>> codec, int maxSizeInclusive) {
|
|
return codec.validate(map -> {
|
|
if (map.size() > maxSizeInclusive) {
|
|
return DataResult.error(() -> "Map is too long: " + map.size() + ", expected range [0-" + maxSizeInclusive + "]");
|
|
}
|
|
return DataResult.success((Object)map);
|
|
});
|
|
}
|
|
|
|
public static <T> Codec<Object2BooleanMap<T>> object2BooleanMap(Codec<T> keyCodec) {
|
|
return Codec.unboundedMap(keyCodec, (Codec)Codec.BOOL).xmap(Object2BooleanOpenHashMap::new, Object2ObjectOpenHashMap::new);
|
|
}
|
|
|
|
@Deprecated
|
|
public static <K, V> MapCodec<V> dispatchOptionalValue(final String typeKey, final String valueKey, final Codec<K> typeCodec, final Function<? super V, ? extends K> typeGetter, final Function<? super K, ? extends Codec<? extends V>> valueCodec) {
|
|
return new MapCodec<V>(){
|
|
|
|
public <T> Stream<T> keys(DynamicOps<T> ops) {
|
|
return Stream.of(ops.createString(typeKey), ops.createString(valueKey));
|
|
}
|
|
|
|
public <T> DataResult<V> decode(DynamicOps<T> ops, MapLike<T> input) {
|
|
Object typeName = input.get(typeKey);
|
|
if (typeName == null) {
|
|
return DataResult.error(() -> "Missing \"" + typeKey + "\" in: " + String.valueOf(input));
|
|
}
|
|
return typeCodec.decode(ops, typeName).flatMap(type -> {
|
|
Object value = Objects.requireNonNullElseGet(input.get(valueKey), () -> ((DynamicOps)ops).emptyMap());
|
|
return ((Codec)valueCodec.apply(type.getFirst())).decode(ops, value).map(Pair::getFirst);
|
|
});
|
|
}
|
|
|
|
public <T> RecordBuilder<T> encode(V input, DynamicOps<T> ops, RecordBuilder<T> builder) {
|
|
Object type = typeGetter.apply(input);
|
|
builder.add(typeKey, typeCodec.encodeStart(ops, type));
|
|
DataResult<T> parameters = this.encode((Codec)valueCodec.apply(type), input, ops);
|
|
if (parameters.result().isEmpty() || !Objects.equals(parameters.result().get(), ops.emptyMap())) {
|
|
builder.add(valueKey, parameters);
|
|
}
|
|
return builder;
|
|
}
|
|
|
|
private <T, V2 extends V> DataResult<T> encode(Codec<V2> codec, V input, DynamicOps<T> ops) {
|
|
return codec.encodeStart(ops, input);
|
|
}
|
|
};
|
|
}
|
|
|
|
public static <A> Codec<Optional<A>> optionalEmptyMap(final Codec<A> codec) {
|
|
return new Codec<Optional<A>>(){
|
|
|
|
public <T> DataResult<Pair<Optional<A>, T>> decode(DynamicOps<T> ops, T input) {
|
|
if (7.isEmptyMap(ops, input)) {
|
|
return DataResult.success((Object)Pair.of(Optional.empty(), input));
|
|
}
|
|
return codec.decode(ops, input).map(pair -> pair.mapFirst(Optional::of));
|
|
}
|
|
|
|
private static <T> boolean isEmptyMap(DynamicOps<T> ops, T input) {
|
|
Optional map = ops.getMap(input).result();
|
|
return map.isPresent() && ((MapLike)map.get()).entries().findAny().isEmpty();
|
|
}
|
|
|
|
public <T> DataResult<T> encode(Optional<A> input, DynamicOps<T> ops, T prefix) {
|
|
if (input.isEmpty()) {
|
|
return DataResult.success((Object)ops.emptyMap());
|
|
}
|
|
return codec.encode(input.get(), ops, prefix);
|
|
}
|
|
};
|
|
}
|
|
|
|
@Deprecated
|
|
public static <E extends Enum<E>> Codec<E> legacyEnum(Function<String, E> valueOf) {
|
|
return Codec.STRING.comapFlatMap(key -> {
|
|
try {
|
|
return DataResult.success((Object)((Enum)valueOf.apply((String)key)));
|
|
}
|
|
catch (IllegalArgumentException ignored) {
|
|
return DataResult.error(() -> "No value with id: " + key);
|
|
}
|
|
}, Enum::toString);
|
|
}
|
|
|
|
public record StrictUnboundedMapCodec<K, V>(Codec<K> keyCodec, Codec<V> elementCodec) implements BaseMapCodec<K, V>,
|
|
Codec<Map<K, V>>
|
|
{
|
|
public <T> DataResult<Map<K, V>> decode(DynamicOps<T> ops, MapLike<T> input) {
|
|
ImmutableMap.Builder read = ImmutableMap.builder();
|
|
for (Pair pair : input.entries().toList()) {
|
|
DataResult v;
|
|
DataResult k = this.keyCodec().parse(ops, pair.getFirst());
|
|
DataResult entry = k.apply2stable(Pair::of, v = this.elementCodec().parse(ops, pair.getSecond()));
|
|
Optional error = entry.error();
|
|
if (error.isPresent()) {
|
|
String errorMessage = ((DataResult.Error)error.get()).message();
|
|
return DataResult.error(() -> {
|
|
if (k.result().isPresent()) {
|
|
return "Map entry '" + String.valueOf(k.result().get()) + "' : " + errorMessage;
|
|
}
|
|
return errorMessage;
|
|
});
|
|
}
|
|
if (entry.result().isPresent()) {
|
|
Pair kvPair = (Pair)entry.result().get();
|
|
read.put(kvPair.getFirst(), kvPair.getSecond());
|
|
continue;
|
|
}
|
|
return DataResult.error(() -> "Empty or invalid map contents are not allowed");
|
|
}
|
|
ImmutableMap elements = read.build();
|
|
return DataResult.success((Object)elements);
|
|
}
|
|
|
|
public <T> DataResult<Pair<Map<K, V>, T>> decode(DynamicOps<T> ops, T input) {
|
|
return ops.getMap(input).setLifecycle(Lifecycle.stable()).flatMap(map -> this.decode(ops, (Object)map)).map(r -> Pair.of((Object)r, (Object)input));
|
|
}
|
|
|
|
public <T> DataResult<T> encode(Map<K, V> input, DynamicOps<T> ops, T prefix) {
|
|
return this.encode(input, ops, ops.mapBuilder()).build(prefix);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "StrictUnboundedMapCodec[" + String.valueOf(this.keyCodec) + " -> " + String.valueOf(this.elementCodec) + "]";
|
|
}
|
|
}
|
|
|
|
public record TagOrElementLocation(Identifier id, boolean tag) {
|
|
@Override
|
|
public String toString() {
|
|
return this.decoratedId();
|
|
}
|
|
|
|
private String decoratedId() {
|
|
return this.tag ? ExtraCodecs.HEX_COLOR_PREFIX + String.valueOf(this.id) : this.id.toString();
|
|
}
|
|
}
|
|
|
|
public static class LateBoundIdMapper<I, V> {
|
|
private final BiMap<I, V> idToValue = HashBiMap.create();
|
|
|
|
public Codec<V> codec(Codec<I> idCodec) {
|
|
BiMap valueToId = this.idToValue.inverse();
|
|
return ExtraCodecs.idResolverCodec(idCodec, arg_0 -> this.idToValue.get(arg_0), arg_0 -> valueToId.get(arg_0));
|
|
}
|
|
|
|
public LateBoundIdMapper<I, V> put(I id, V value) {
|
|
Objects.requireNonNull(value, () -> "Value for " + String.valueOf(id) + " is null");
|
|
this.idToValue.put(id, value);
|
|
return this;
|
|
}
|
|
|
|
public Set<V> values() {
|
|
return Collections.unmodifiableSet(this.idToValue.values());
|
|
}
|
|
}
|
|
}
|
|
|