add property to use registry keys for where applicable

disabled by default for now
This commit is contained in:
Julian Krings 2025-08-02 00:00:51 +02:00
parent a5bca0a9bb
commit 5eb25f3977
No known key found for this signature in database
GPG Key ID: 208C6E08C3B718D2
7 changed files with 281 additions and 125 deletions

View File

@ -36,7 +36,7 @@ import com.volmit.iris.util.format.C;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst; import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.reflect.OldEnum; import com.volmit.iris.util.reflect.KeyedType;
import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import lombok.Data; import lombok.Data;
@ -341,14 +341,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
this.imageLoader = registerLoader(IrisImage.class); this.imageLoader = registerLoader(IrisImage.class);
this.scriptLoader = registerLoader(IrisScript.class); this.scriptLoader = registerLoader(IrisScript.class);
this.matterObjectLoader = registerLoader(IrisMatterObject.class); this.matterObjectLoader = registerLoader(IrisMatterObject.class);
if (OldEnum.exists()) { builder.registerTypeAdapterFactory(KeyedType::createTypeAdapter);
builder.registerTypeAdapterFactory(new TypeAdapterFactory() {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return (TypeAdapter<T>) OldEnum.create(type.getRawType());
}
});
}
gson = builder.create(); gson = builder.create();
} }

View File

@ -31,7 +31,7 @@ import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.data.B; import com.volmit.iris.util.data.B;
import com.volmit.iris.util.json.JSONArray; import com.volmit.iris.util.json.JSONArray;
import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.reflect.OldEnum; import com.volmit.iris.util.reflect.KeyedType;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -336,10 +336,10 @@ public class SchemaBuilder {
prop.put("$ref", "#/definitions/" + key); prop.put("$ref", "#/definitions/" + key);
description.add(SYMBOL_TYPE__N + " Must be a valid Potion Effect Type (use ctrl+space for auto complete!)"); description.add(SYMBOL_TYPE__N + " Must be a valid Potion Effect Type (use ctrl+space for auto complete!)");
} else if (KeyedType.isKeyed(k.getType())) {
fancyType = addEnum(k.getType(), prop, description, KeyedType.values(k.getType()), Function.identity());
} else if (k.getType().isEnum()) { } else if (k.getType().isEnum()) {
fancyType = addEnum(k.getType(), prop, description, k.getType().getEnumConstants(), o -> ((Enum<?>) o).name()); fancyType = addEnum(k.getType(), prop, description, k.getType().getEnumConstants(), o -> ((Enum<?>) o).name());
} else if (OldEnum.isOldEnum(k.getType())) {
fancyType = addEnum(k.getType(), prop, description, OldEnum.values(k.getType()), OldEnum::name);
} }
} }
case "object" -> { case "object" -> {
@ -504,10 +504,10 @@ public class SchemaBuilder {
items.put("$ref", "#/definitions/" + key); items.put("$ref", "#/definitions/" + key);
prop.put("items", items); prop.put("items", items);
description.add(SYMBOL_TYPE__N + " Must be a valid Potion Effect Type (use ctrl+space for auto complete!)"); description.add(SYMBOL_TYPE__N + " Must be a valid Potion Effect Type (use ctrl+space for auto complete!)");
} else if (KeyedType.isKeyed(t.type())) {
fancyType = addEnumList(prop, description, t, KeyedType.values(t.type()), Function.identity());
} else if (t.type().isEnum()) { } else if (t.type().isEnum()) {
fancyType = addEnumList(prop, description, t, t.type().getEnumConstants(), o -> ((Enum<?>) o).name()); fancyType = addEnumList(prop, description, t, t.type().getEnumConstants(), o -> ((Enum<?>) o).name());
} else if (OldEnum.isOldEnum(t.type())) {
fancyType = addEnumList(prop, description, t, OldEnum.values(t.type()), OldEnum::name);
} }
} }
} }
@ -548,7 +548,7 @@ public class SchemaBuilder {
if (value instanceof List) { if (value instanceof List) {
d.add(" "); d.add(" ");
d.add("* Default Value is an empty list"); d.add("* Default Value is an empty list");
} else if (!cl.isPrimitive() && !(value instanceof Number) && !(value instanceof String) && !(cl.isEnum()) && !OldEnum.isOldEnum(cl)) { } else if (!cl.isPrimitive() && !(value instanceof Number) && !(value instanceof String) && !(cl.isEnum()) && !KeyedType.isKeyed(cl)) {
d.add(" "); d.add(" ");
d.add("* Default Value is a default object (create this object to see default properties)"); d.add("* Default Value is a default object (create this object to see default properties)");
} else { } else {
@ -596,7 +596,7 @@ public class SchemaBuilder {
} }
@NotNull @NotNull
private String addEnumList(JSONObject prop, KList<String> description, ArrayType t, Object[] values, Function<Object, String> function) { private <T> String addEnumList(JSONObject prop, KList<String> description, ArrayType t, T[] values, Function<T, String> function) {
JSONObject items = new JSONObject(); JSONObject items = new JSONObject();
var s = addEnum(t.type(), items, description, values, function); var s = addEnum(t.type(), items, description, values, function);
prop.put("items", items); prop.put("items", items);
@ -605,10 +605,10 @@ public class SchemaBuilder {
} }
@NotNull @NotNull
private String addEnum(Class<?> type, JSONObject prop, KList<String> description, Object[] values, Function<Object, String> function) { private <T> String addEnum(Class<?> type, JSONObject prop, KList<String> description, T[] values, Function<T, String> function) {
JSONArray a = new JSONArray(); JSONArray a = new JSONArray();
boolean advanced = type.isAnnotationPresent(Desc.class); boolean advanced = type.isAnnotationPresent(Desc.class);
for (Object gg : values) { for (T gg : values) {
if (advanced) { if (advanced) {
try { try {
JSONObject j = new JSONObject(); JSONObject j = new JSONObject();
@ -652,7 +652,7 @@ public class SchemaBuilder {
return "boolean"; return "boolean";
} }
if (c.equals(String.class) || c.isEnum() || OldEnum.isOldEnum(c) || c.equals(Enchantment.class) || c.equals(PotionEffectType.class)) { if (c.equals(String.class) || c.isEnum() || KeyedType.isKeyed(c)) {
return "string"; return "string";
} }

View File

@ -0,0 +1,137 @@
package com.volmit.iris.util.data.registry;
import com.volmit.iris.util.collection.KMap;
import lombok.NonNull;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public interface KeyedRegistry<T> {
@NonNull Map<NamespacedKey, T> map();
@Nullable T get(@NonNull NamespacedKey key);
@Nullable NamespacedKey keyOf(@NonNull T value);
default boolean isEmpty() {
return map().isEmpty();
}
@NonNull
default Optional<T> find(@NonNull String @NonNull ... keys) {
if (keys.length == 0) throw new IllegalArgumentException("Need at least one key");
for (final String key : keys) {
final T t = get(NamespacedKey.minecraft(key));
if (t != null) {
return Optional.of(t);
}
}
return Optional.empty();
}
@NonNull
default Optional<T> find(@NonNull NamespacedKey @NonNull ... keys) {
if (keys.length == 0) throw new IllegalArgumentException("Need at least one key");
for (final NamespacedKey key : keys) {
final T t = get(key);
if (t != null) {
return Optional.of(t);
}
}
return Optional.empty();
}
@Contract(value = "null -> null; !null -> new", pure = true)
static <T> KeyedRegistry<T> wrapped(Map<NamespacedKey, T> map) {
if (map == null) return null;
return new MappedRegistry<>(map);
}
@Contract(value = "null -> null; !null -> new", pure = true)
static <T extends Keyed> KeyedRegistry<T> wrapped(Registry<T> registry) {
if (registry == null) return null;
return new BukkitRegistry<>(registry);
}
@Contract(value = "_ -> new", pure = true)
static <T> KeyedRegistry<T> wrapped(@NonNull Collection<@NonNull KeyedRegistry<T>> registries) {
return new CompoundRegistry<>(registries);
}
record MappedRegistry<T>(Map<NamespacedKey, T> map) implements KeyedRegistry<T> {
@Override
public @Nullable T get(@NonNull NamespacedKey key) {
return map.get(key);
}
@Override
public @Nullable NamespacedKey keyOf(@NonNull T value) {
return map.entrySet().stream()
.filter(e -> e.getValue().equals(value))
.map(Map.Entry::getKey)
.findFirst()
.orElse(null);
}
}
record BukkitRegistry<T extends Keyed>(Registry<T> registry) implements KeyedRegistry<T> {
@Override
public @NonNull Map<NamespacedKey, T> map() {
return registry.stream().collect(Collectors.toMap(Keyed::getKey, Function.identity()));
}
@Override
public @Nullable T get(@NonNull NamespacedKey key) {
return registry.get(key);
}
@Override
public @NonNull NamespacedKey keyOf(@NonNull T value) {
return value.getKey();
}
}
record CompoundRegistry<T>(Collection<KeyedRegistry<T>> registries) implements KeyedRegistry<T> {
@Override
public @NonNull Map<NamespacedKey, T> map() {
final KMap<NamespacedKey, T> m = new KMap<>();
for (final KeyedRegistry<T> registry : registries) {
m.put(registry.map());
}
return m;
}
@Override
public @Nullable T get(@NonNull NamespacedKey key) {
for (final KeyedRegistry<T> registry : registries) {
final T t = registry.get(key);
if (t != null) {
return t;
}
}
return null;
}
@Override
public @Nullable NamespacedKey keyOf(@NonNull T value) {
for (final KeyedRegistry<T> registry : registries) {
final NamespacedKey key = registry.keyOf(value);
if (key != null) {
return key;
}
}
return null;
}
@Override
public boolean isEmpty() {
return registries.isEmpty() || registries.stream().allMatch(KeyedRegistry::isEmpty);
}
}
}

View File

@ -0,0 +1,42 @@
package com.volmit.iris.util.data.registry;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import lombok.NonNull;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
public class RegistryTypeAdapter<T> extends TypeAdapter<T> {
private final KeyedRegistry<T> registry;
private RegistryTypeAdapter(KeyedRegistry<T> type) {
this.registry = type;
}
@Nullable
public static <T> RegistryTypeAdapter<T> of(@NonNull Class<T> type) {
final var registry = RegistryUtil.lookup(type);
return registry.isEmpty() ? null : new RegistryTypeAdapter<>(registry);
}
@Override
public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
final var key = registry.keyOf(value);
if (key == null) out.nullValue();
else out.value(key.toString());
}
@Override
public T read(JsonReader in) throws IOException {
final NamespacedKey key = NamespacedKey.fromString(in.nextString());
return key == null ? null : registry.get(key);
}
}

View File

@ -2,6 +2,8 @@ package com.volmit.iris.util.data.registry;
import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import lombok.NonNull; import lombok.NonNull;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Keyed; import org.bukkit.Keyed;
@ -13,116 +15,78 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Arrays; import java.util.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.volmit.iris.util.data.registry.KeyedRegistry.wrapped;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class RegistryUtil { public class RegistryUtil {
private static final AtomicCache<RegistryLookup> registryLookup = new AtomicCache<>(); private static final AtomicCache<RegistryLookup> registryLookup = new AtomicCache<>();
private static final Map<Class<?>, Map<NamespacedKey, Keyed>> KEYED_REGISTRY = new HashMap<>(); private static final KMap<Class<?>, KeyedRegistry<Object>> CACHE = new KMap<>();
private static final Map<Class<?>, Map<NamespacedKey, Object>> ENUM_REGISTRY = new HashMap<>();
private static final Map<Class<?>, Registry<Keyed>> REGISTRY = new HashMap<>();
@NonNull @NonNull
public static <T> T find(@NonNull Class<T> typeClass, @NonNull String... keys) { public static <T> T find(@NonNull Class<T> typeClass, @NonNull String... keys) {
return find(typeClass, defaultLookup(), keys); return find(typeClass, Arrays.stream(keys).map(NamespacedKey::minecraft).toArray(NamespacedKey[]::new));
} }
@NonNull @NonNull
public static <T> T find(@NonNull Class<T> typeClass, @Nullable Lookup<T> lookup, @NonNull String... keys) {
return find(typeClass, lookup, Arrays.stream(keys).map(NamespacedKey::minecraft).toArray(NamespacedKey[]::new));
}
public static <T> T find(@NonNull Class<T> typeClass, @NonNull NamespacedKey... keys) { public static <T> T find(@NonNull Class<T> typeClass, @NonNull NamespacedKey... keys) {
return find(typeClass, defaultLookup(), keys); return lookup(typeClass).find(keys).orElseThrow(() -> new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys)));
} }
@NonNull @NonNull
public static <T> T find(@NonNull Class<T> typeClass, @Nullable Lookup<T> lookup, @NonNull NamespacedKey... keys) { public static <T> KeyedRegistry<T> lookup(@NonNull Class<T> typeClass) {
if (keys.length == 0) throw new IllegalArgumentException("Need at least one key"); return (KeyedRegistry<T>) CACHE.computeIfAbsent(typeClass, $ -> {
Registry<Keyed> registry = null; final var registries = new KList<KeyedRegistry<Object>>();
if (Keyed.class.isAssignableFrom(typeClass)) { if (Keyed.class.isAssignableFrom(typeClass)) {
registry = getRegistry(typeClass.asSubclass(Keyed.class)); var bukkit = wrapped(getRegistry(typeClass.asSubclass(Keyed.class)));
} if (bukkit == null) {
if (registry == null) { bukkit = Arrays.stream(Registry.class.getDeclaredFields())
registry = REGISTRY.computeIfAbsent(typeClass, t -> Arrays.stream(Registry.class.getDeclaredFields()) .filter(field -> Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers()))
.filter(field -> Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) .filter(field -> Registry.class.isAssignableFrom(field.getType()))
.filter(field -> Registry.class.isAssignableFrom(field.getType())) .filter(field -> ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].equals(typeClass))
.filter(field -> ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].equals(t)) .map(field -> {
.map(field -> { try {
try { return (Registry<Keyed>) field.get(null);
return (Registry<Keyed>) field.get(null); } catch (IllegalAccessException e) {
} catch (IllegalAccessException e) { return null;
return null; }
} })
}) .filter(Objects::nonNull)
.filter(Objects::nonNull) .findFirst()
.findFirst() .map(KeyedRegistry::wrapped)
.orElse(null)); .orElse(null);
} }
if (registry != null) { registries.addNonNull((KeyedRegistry<Object>) (Object) bukkit);
for (NamespacedKey key : keys) {
Keyed value = registry.get(key);
if (value != null)
return (T) value;
} }
} registries.add(getKeyedValues(typeClass));
registries.add(getEnumValues(typeClass));
if (lookup != null) return wrapped(registries);
return lookup.find(typeClass, keys); });
throw new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys));
} }
@NonNull private static KeyedRegistry<Object> getKeyedValues(@NonNull Class<?> typeClass) {
public static <T> T findByField(@NonNull Class<T> typeClass, @NonNull NamespacedKey... keys) { return wrapped(Arrays.stream(typeClass.getDeclaredFields())
var values = KEYED_REGISTRY.computeIfAbsent(typeClass, RegistryUtil::getKeyedValues);
for (NamespacedKey key : keys) {
var value = values.get(key);
if (value != null)
return (T) value;
}
throw new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys));
}
@NonNull
public static <T> T findByEnum(@NonNull Class<T> typeClass, @NonNull NamespacedKey... keys) {
var values = ENUM_REGISTRY.computeIfAbsent(typeClass, RegistryUtil::getEnumValues);
for (NamespacedKey key : keys) {
var value = values.get(key);
if (value != null)
return (T) value;
}
throw new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys));
}
@NonNull
public static <T> Lookup<T> defaultLookup() {
return Lookup.combine(RegistryUtil::findByField, RegistryUtil::findByEnum);
}
private static Map<NamespacedKey, Keyed> getKeyedValues(@NonNull Class<?> typeClass) {
return Arrays.stream(typeClass.getDeclaredFields())
.filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers()))
.filter(field -> Keyed.class.isAssignableFrom(field.getType())) .filter(field -> Keyed.class.isAssignableFrom(field.getType()))
.map(field -> { .map(field -> {
try { try {
return (Keyed) field.get(null); final var value = (Keyed) field.get(null);
return new Pair<>(value.getKey(), value);
} catch (Throwable e) { } catch (Throwable e) {
return null; return null;
} }
}) })
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toMap(Keyed::getKey, Function.identity())); .collect(Collectors.toMap(Pair::getA, Pair::getB)));
} }
private static Map<NamespacedKey, Object> getEnumValues(@NonNull Class<?> typeClass) { private static KeyedRegistry<Object> getEnumValues(@NonNull Class<?> typeClass) {
return Arrays.stream(typeClass.getDeclaredFields()) return wrapped(Arrays.stream(typeClass.getDeclaredFields())
.filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers()))
.filter(field -> typeClass.isAssignableFrom(field.getType())) .filter(field -> typeClass.isAssignableFrom(field.getType()))
.map(field -> { .map(field -> {
@ -133,28 +97,10 @@ public class RegistryUtil {
} }
}) })
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toMap(Pair::getA, Pair::getB)); .collect(Collectors.toMap(Pair::getA, Pair::getB)));
} }
@FunctionalInterface
public interface Lookup<T> {
@NonNull
T find(@NonNull Class<T> typeClass, @NonNull NamespacedKey... keys);
static <T> Lookup<T> combine(@NonNull Lookup<T>... lookups) {
if (lookups.length == 0) throw new IllegalArgumentException("Need at least one lookup");
return (typeClass, keys) -> {
for (Lookup<T> lookup : lookups) {
try {
return lookup.find(typeClass, keys);
} catch (IllegalArgumentException ignored) {}
}
throw new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys));
};
}
}
@Nullable @Nullable
private static <T extends Keyed> Registry<T> getRegistry(@NotNull Class<T> type) { private static <T extends Keyed> Registry<T> getRegistry(@NotNull Class<T> type) {
RegistryLookup lookup = registryLookup.aquire(() -> { RegistryLookup lookup = registryLookup.aquire(() -> {

View File

@ -0,0 +1,37 @@
package com.volmit.iris.util.reflect;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.volmit.iris.util.data.registry.RegistryTypeAdapter;
import com.volmit.iris.util.data.registry.RegistryUtil;
import org.bukkit.Keyed;
public class KeyedType {
private static final boolean KEYED_ENABLED = Boolean.getBoolean("iris.keyed-types");
private static final boolean KEYED_LENIENT = Boolean.getBoolean("iris.keyed-lenient");
public static String[] values(Class<?> type) {
if (!isKeyed(type)) return new String[0];
if (!KEYED_ENABLED) return OldEnum.values(type);
return RegistryUtil.lookup(type)
.map()
.keySet()
.stream()
.map(Object::toString)
.toArray(String[]::new);
}
public static boolean isKeyed(Class<?> type) {
if (KEYED_ENABLED) {
if (KEYED_LENIENT) return !RegistryUtil.lookup(type).isEmpty();
else return Keyed.class.isAssignableFrom(type);
} else return OldEnum.isOldEnum(type);
}
@SuppressWarnings("unchecked")
public static <T> TypeAdapter<T> createTypeAdapter(Gson gson, TypeToken<T> type) {
if (!isKeyed(type.getRawType())) return null;
return (TypeAdapter<T>) (KEYED_ENABLED ? RegistryTypeAdapter.of(type.getRawType()) : OldEnum.create(type.getRawType()));
}
}

View File

@ -1,21 +1,22 @@
package com.volmit.iris.util.reflect; package com.volmit.iris.util.reflect;
import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
//TODO improve this @Deprecated(since = "3.7.1")
public class OldEnum { public class OldEnum {
private static final Class<?> oldEnum; private static final Class<?> oldEnum;
private static final Method name; private static final MethodHandle name;
public static boolean exists() { public static boolean exists() {
return oldEnum != null; return oldEnum != null;
@ -46,20 +47,20 @@ public class OldEnum {
} }
} }
public static Object[] values(Class<?> clazz) { public static String[] values(Class<?> clazz) {
if (!isOldEnum(clazz)) return new Object[0]; if (!isOldEnum(clazz)) return new String[0];
return Arrays.stream(clazz.getDeclaredFields()) return Arrays.stream(clazz.getDeclaredFields())
.filter(f -> Modifier.isStatic(f.getModifiers())) .filter(f -> Modifier.isStatic(f.getModifiers()))
.filter(f -> Modifier.isFinal(f.getModifiers())) .filter(f -> Modifier.isFinal(f.getModifiers()))
.map(f -> { .map(f -> {
try { try {
return f.get(null); return name(f.get(null));
} catch (Throwable ignored) { } catch (Throwable ignored) {
return null; return null;
} }
}) })
.filter(Objects::nonNull) .filter(Objects::nonNull)
.toArray(); .toArray(String[]::new);
} }
public static <T> TypeAdapter<T> create(Class<? extends T> type) { public static <T> TypeAdapter<T> create(Class<? extends T> type) {
@ -82,10 +83,10 @@ public class OldEnum {
static { static {
Class<?> clazz = null; Class<?> clazz = null;
Method method = null; MethodHandle method = null;
try { try {
clazz = Class.forName("org.bukkit.util.OldEnum"); clazz = Class.forName("org.bukkit.util.OldEnum");
method = clazz.getDeclaredMethod("name"); method = MethodHandles.lookup().findVirtual(clazz, "name", MethodType.methodType(String.class));
} catch (Throwable ignored) {} } catch (Throwable ignored) {}
if (clazz == null || method == null) { if (clazz == null || method == null) {