diff --git a/core/build.gradle.kts b/core/build.gradle.kts index b9d95c58f..87c663107 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,4 +1,4 @@ -import io.github.slimjar.func.slimjar +import io.github.slimjar.func.slimjarHelper import io.github.slimjar.resolver.data.Mirror import org.ajoberstar.grgit.Grgit import java.net.URI @@ -69,7 +69,7 @@ dependencies { compileOnly(libs.multiverseCore) // Shaded - implementation(slimjar()) + implementation(slimjarHelper("spigot")) // Dynamically Loaded slim(libs.paralithic) diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 178c2e35f..7838b0c3c 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -435,7 +435,7 @@ public class Iris extends VolmitPlugin implements Listener { public Iris() { instance = this; - SlimJar.load(getDataFolder("cache", "libraries")); + SlimJar.load(); } private void enable() { diff --git a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java index e06fa1f16..c09fc5ca8 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java +++ b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java @@ -39,7 +39,7 @@ import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.parallel.BurstExecutor; 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.J; import lombok.Data; @@ -356,14 +356,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { this.imageLoader = registerLoader(IrisImage.class); this.scriptLoader = registerLoader(IrisScript.class); this.matterObjectLoader = registerLoader(IrisMatterObject.class); - if (OldEnum.exists()) { - builder.registerTypeAdapterFactory(new TypeAdapterFactory() { - @Override - public TypeAdapter create(Gson gson, TypeToken type) { - return (TypeAdapter) OldEnum.create(type.getRawType()); - } - }); - } + builder.registerTypeAdapterFactory(KeyedType::createTypeAdapter); gson = builder.create(); dimensionLoader.streamAll() diff --git a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java index f5e87d05f..1ae05d724 100644 --- a/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java +++ b/core/src/main/java/com/volmit/iris/core/project/SchemaBuilder.java @@ -31,7 +31,7 @@ import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.data.B; import com.volmit.iris.util.json.JSONArray; 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.potion.PotionEffectType; import org.jetbrains.annotations.NotNull; @@ -338,10 +338,10 @@ public class SchemaBuilder { prop.put("$ref", "#/definitions/" + key); 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()) { 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" -> { @@ -506,10 +506,10 @@ public class SchemaBuilder { items.put("$ref", "#/definitions/" + key); prop.put("items", items); 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()) { 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); } } } @@ -552,7 +552,7 @@ public class SchemaBuilder { if (present) d.add(" "); if (value instanceof List) { d.add(SYMBOL_LIMIT__N + " 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(SYMBOL_LIMIT__N + " Default Value is a default object (create this object to see default properties)"); } else { d.add(SYMBOL_LIMIT__N + " Default Value is " + value); @@ -606,7 +606,7 @@ public class SchemaBuilder { } @NotNull - private String addEnumList(JSONObject prop, KList description, ArrayType t, Object[] values, Function function) { + private String addEnumList(JSONObject prop, KList description, ArrayType t, T[] values, Function function) { JSONObject items = new JSONObject(); var s = addEnum(t.type(), items, description, values, function); prop.put("items", items); @@ -615,10 +615,10 @@ public class SchemaBuilder { } @NotNull - private String addEnum(Class type, JSONObject prop, KList description, Object[] values, Function function) { + private String addEnum(Class type, JSONObject prop, KList description, T[] values, Function function) { JSONArray a = new JSONArray(); boolean advanced = type.isAnnotationPresent(Desc.class); - for (Object gg : values) { + for (T gg : values) { if (advanced) { try { JSONObject j = new JSONObject(); @@ -664,7 +664,7 @@ public class SchemaBuilder { 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"; } diff --git a/core/src/main/java/com/volmit/iris/util/data/registry/KeyedRegistry.java b/core/src/main/java/com/volmit/iris/util/data/registry/KeyedRegistry.java new file mode 100644 index 000000000..960ff8ca2 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/data/registry/KeyedRegistry.java @@ -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 { + @NonNull Map map(); + + @Nullable T get(@NonNull NamespacedKey key); + @Nullable NamespacedKey keyOf(@NonNull T value); + + default boolean isEmpty() { + return map().isEmpty(); + } + + @NonNull + default Optional 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 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 KeyedRegistry wrapped(Map map) { + if (map == null) return null; + return new MappedRegistry<>(map); + } + + @Contract(value = "null -> null; !null -> new", pure = true) + static KeyedRegistry wrapped(Registry registry) { + if (registry == null) return null; + return new BukkitRegistry<>(registry); + } + + @Contract(value = "_ -> new", pure = true) + static KeyedRegistry wrapped(@NonNull Collection<@NonNull KeyedRegistry> registries) { + return new CompoundRegistry<>(registries); + } + + + record MappedRegistry(Map map) implements KeyedRegistry { + @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(Registry registry) implements KeyedRegistry { + @Override + public @NonNull Map 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(Collection> registries) implements KeyedRegistry { + @Override + public @NonNull Map map() { + final KMap m = new KMap<>(); + for (final KeyedRegistry registry : registries) { + m.put(registry.map()); + } + return m; + } + + @Override + public @Nullable T get(@NonNull NamespacedKey key) { + for (final KeyedRegistry 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 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); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/util/data/registry/RegistryTypeAdapter.java b/core/src/main/java/com/volmit/iris/util/data/registry/RegistryTypeAdapter.java new file mode 100644 index 000000000..c328781b7 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/data/registry/RegistryTypeAdapter.java @@ -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 extends TypeAdapter { + private final KeyedRegistry registry; + + private RegistryTypeAdapter(KeyedRegistry type) { + this.registry = type; + } + + @Nullable + public static RegistryTypeAdapter of(@NonNull Class 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); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/data/registry/RegistryUtil.java b/core/src/main/java/com/volmit/iris/util/data/registry/RegistryUtil.java index b3377ebe8..6119cbdad 100644 --- a/core/src/main/java/com/volmit/iris/util/data/registry/RegistryUtil.java +++ b/core/src/main/java/com/volmit/iris/util/data/registry/RegistryUtil.java @@ -2,6 +2,8 @@ package com.volmit.iris.util.data.registry; import com.volmit.iris.core.nms.container.Pair; 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 org.bukkit.Bukkit; import org.bukkit.Keyed; @@ -13,116 +15,78 @@ import org.jetbrains.annotations.Nullable; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; +import static com.volmit.iris.util.data.registry.KeyedRegistry.wrapped; @SuppressWarnings("unchecked") public class RegistryUtil { private static final AtomicCache registryLookup = new AtomicCache<>(); - private static final Map, Map> KEYED_REGISTRY = new HashMap<>(); - private static final Map, Map> ENUM_REGISTRY = new HashMap<>(); - private static final Map, Registry> REGISTRY = new HashMap<>(); + private static final KMap, KeyedRegistry> CACHE = new KMap<>(); @NonNull public static T find(@NonNull Class typeClass, @NonNull String... keys) { - return find(typeClass, defaultLookup(), keys); + return find(typeClass, Arrays.stream(keys).map(NamespacedKey::minecraft).toArray(NamespacedKey[]::new)); } @NonNull - public static T find(@NonNull Class typeClass, @Nullable Lookup lookup, @NonNull String... keys) { - return find(typeClass, lookup, Arrays.stream(keys).map(NamespacedKey::minecraft).toArray(NamespacedKey[]::new)); - } - public static T find(@NonNull Class 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 - public static T find(@NonNull Class typeClass, @Nullable Lookup lookup, @NonNull NamespacedKey... keys) { - if (keys.length == 0) throw new IllegalArgumentException("Need at least one key"); - Registry registry = null; - if (Keyed.class.isAssignableFrom(typeClass)) { - registry = getRegistry(typeClass.asSubclass(Keyed.class)); - } - if (registry == null) { - registry = REGISTRY.computeIfAbsent(typeClass, t -> Arrays.stream(Registry.class.getDeclaredFields()) - .filter(field -> Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) - .filter(field -> Registry.class.isAssignableFrom(field.getType())) - .filter(field -> ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].equals(t)) - .map(field -> { - try { - return (Registry) field.get(null); - } catch (IllegalAccessException e) { - return null; - } - }) - .filter(Objects::nonNull) - .findFirst() - .orElse(null)); - } + public static KeyedRegistry lookup(@NonNull Class typeClass) { + return (KeyedRegistry) CACHE.computeIfAbsent(typeClass, $ -> { + final var registries = new KList>(); + if (Keyed.class.isAssignableFrom(typeClass)) { + var bukkit = wrapped(getRegistry(typeClass.asSubclass(Keyed.class))); + if (bukkit == null) { + bukkit = Arrays.stream(Registry.class.getDeclaredFields()) + .filter(field -> Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) + .filter(field -> Registry.class.isAssignableFrom(field.getType())) + .filter(field -> ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].equals(typeClass)) + .map(field -> { + try { + return (Registry) field.get(null); + } catch (IllegalAccessException e) { + return null; + } + }) + .filter(Objects::nonNull) + .findFirst() + .map(KeyedRegistry::wrapped) + .orElse(null); + } - if (registry != null) { - for (NamespacedKey key : keys) { - Keyed value = registry.get(key); - if (value != null) - return (T) value; + registries.addNonNull((KeyedRegistry) (Object) bukkit); } - } + registries.add(getKeyedValues(typeClass)); + registries.add(getEnumValues(typeClass)); - if (lookup != null) - return lookup.find(typeClass, keys); - throw new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys)); + return wrapped(registries); + }); } - @NonNull - public static T findByField(@NonNull Class typeClass, @NonNull NamespacedKey... keys) { - 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 findByEnum(@NonNull Class 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 Lookup defaultLookup() { - return Lookup.combine(RegistryUtil::findByField, RegistryUtil::findByEnum); - } - - private static Map getKeyedValues(@NonNull Class typeClass) { - return Arrays.stream(typeClass.getDeclaredFields()) + private static KeyedRegistry getKeyedValues(@NonNull Class typeClass) { + return wrapped(Arrays.stream(typeClass.getDeclaredFields()) .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) .filter(field -> Keyed.class.isAssignableFrom(field.getType())) .map(field -> { try { - return (Keyed) field.get(null); + final var value = (Keyed) field.get(null); + return new Pair<>(value.getKey(), value); } catch (Throwable e) { return null; } }) .filter(Objects::nonNull) - .collect(Collectors.toMap(Keyed::getKey, Function.identity())); + .collect(Collectors.toMap(Pair::getA, Pair::getB))); } - private static Map getEnumValues(@NonNull Class typeClass) { - return Arrays.stream(typeClass.getDeclaredFields()) + private static KeyedRegistry getEnumValues(@NonNull Class typeClass) { + return wrapped(Arrays.stream(typeClass.getDeclaredFields()) .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) .filter(field -> typeClass.isAssignableFrom(field.getType())) .map(field -> { @@ -133,28 +97,10 @@ public class RegistryUtil { } }) .filter(Objects::nonNull) - .collect(Collectors.toMap(Pair::getA, Pair::getB)); + .collect(Collectors.toMap(Pair::getA, Pair::getB))); } - @FunctionalInterface - public interface Lookup { - @NonNull - T find(@NonNull Class typeClass, @NonNull NamespacedKey... keys); - - static Lookup combine(@NonNull Lookup... lookups) { - if (lookups.length == 0) throw new IllegalArgumentException("Need at least one lookup"); - return (typeClass, keys) -> { - for (Lookup lookup : lookups) { - try { - return lookup.find(typeClass, keys); - } catch (IllegalArgumentException ignored) {} - } - throw new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys)); - }; - } - } - @Nullable private static Registry getRegistry(@NotNull Class type) { RegistryLookup lookup = registryLookup.aquire(() -> { diff --git a/core/src/main/java/com/volmit/iris/util/matter/slices/JigsawStructureMatter.java b/core/src/main/java/com/volmit/iris/util/matter/slices/JigsawStructureMatter.java index 6e9ec2ce0..0e573945e 100644 --- a/core/src/main/java/com/volmit/iris/util/matter/slices/JigsawStructureMatter.java +++ b/core/src/main/java/com/volmit/iris/util/matter/slices/JigsawStructureMatter.java @@ -2,34 +2,34 @@ package com.volmit.iris.util.matter.slices; import com.volmit.iris.util.data.palette.Palette; import com.volmit.iris.util.matter.Sliced; -import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer; +import com.volmit.iris.util.matter.slices.container.JigsawStructureContainer; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; @Sliced -public class JigsawStructureMatter extends RawMatter { +public class JigsawStructureMatter extends RawMatter { public JigsawStructureMatter() { this(1,1,1); } public JigsawStructureMatter(int width, int height, int depth) { - super(width, height, depth, JigsawPieceContainer.class); + super(width, height, depth, JigsawStructureContainer.class); } @Override - public Palette getGlobalPalette() { + public Palette getGlobalPalette() { return null; } @Override - public void writeNode(JigsawPieceContainer b, DataOutputStream dos) throws IOException { + public void writeNode(JigsawStructureContainer b, DataOutputStream dos) throws IOException { dos.writeUTF(b.getLoadKey()); } @Override - public JigsawPieceContainer readNode(DataInputStream din) throws IOException { - return new JigsawPieceContainer(din.readUTF()); + public JigsawStructureContainer readNode(DataInputStream din) throws IOException { + return new JigsawStructureContainer(din.readUTF()); } } diff --git a/core/src/main/java/com/volmit/iris/util/misc/SlimJar.java b/core/src/main/java/com/volmit/iris/util/misc/SlimJar.java index 488000bf9..4cee3df77 100644 --- a/core/src/main/java/com/volmit/iris/util/misc/SlimJar.java +++ b/core/src/main/java/com/volmit/iris/util/misc/SlimJar.java @@ -1,124 +1,69 @@ package com.volmit.iris.util.misc; import com.volmit.iris.Iris; -import com.volmit.iris.core.nms.container.Pair; -import com.volmit.iris.util.collection.KList; import io.github.slimjar.app.builder.ApplicationBuilder; -import io.github.slimjar.injector.loader.IsolatedInjectableClassLoader; +import io.github.slimjar.app.builder.SpigotApplicationBuilder; import io.github.slimjar.injector.loader.factory.InjectableFactory; import io.github.slimjar.logging.ProcessLogger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Path; -import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.logging.Logger; + +import static com.volmit.iris.Iris.instance; public class SlimJar { - private static final String NAME = "Iris"; - private static final Logger LOGGER = Logger.getLogger(NAME); private static final boolean DEBUG = Boolean.getBoolean("iris.debug-slimjar"); private static final boolean DISABLE_REMAPPER = Boolean.getBoolean("iris.disable-remapper"); private static final ReentrantLock lock = new ReentrantLock(); private static final AtomicBoolean loaded = new AtomicBoolean(); - public static void load(@Nullable File localRepository) { + public static void load() { if (loaded.get()) return; lock.lock(); try { if (loaded.getAndSet(true)) return; - if (localRepository == null) { - localRepository = new File(".iris/libraries"); + final var downloadPath = instance.getDataFolder("cache", "libraries").toPath(); + final var logger = instance.getLogger(); + + logger.info("Loading libraries..."); + try { + new SpigotApplicationBuilder(instance) + .downloadDirectoryPath(downloadPath) + .debug(DEBUG) + .remap(!DISABLE_REMAPPER) + .build(); + } catch (Throwable e) { + Iris.warn("Failed to inject the library loader, falling back to application builder"); + ApplicationBuilder.appending(instance.getName()) + .injectableFactory(InjectableFactory.selecting(InjectableFactory.ERROR, InjectableFactory.INJECTABLE, InjectableFactory.WRAPPED, InjectableFactory.UNSAFE)) + .downloadDirectoryPath(downloadPath) + .logger(new ProcessLogger() { + @Override + public void info(@NotNull String message, @Nullable Object... args) { + if (!DEBUG) return; + instance.getLogger().info(message.formatted(args)); + } + + @Override + public void error(@NotNull String message, @Nullable Object... args) { + instance.getLogger().severe(message.formatted(args)); + } + + @Override + public void debug(@NotNull String message, @Nullable Object... args) { + if (!DEBUG) return; + instance.getLogger().info(message.formatted(args)); + } + }) + .build(); } - - LOGGER.info("Loading libraries..."); - load(localRepository.toPath(), new ProcessLogger() { - @Override - public void info(@NotNull String message, @Nullable Object... args) { - if (!DEBUG) return; - LOGGER.info(message.formatted(args)); - } - - @Override - public void error(@NotNull String message, @Nullable Object... args) { - LOGGER.severe(message.formatted(args)); - } - - @Override - public void debug(@NotNull String message, @Nullable Object... args) { - if (!DEBUG) return; - LOGGER.info(message.formatted(args)); - } - }); - LOGGER.info("Libraries loaded successfully!"); + logger.info("Libraries loaded successfully!"); } finally { lock.unlock(); } } - - private static void load(Path downloadPath, ProcessLogger logger) { - try { - loadSpigot(downloadPath, logger); - } catch (Throwable e) { - Iris.warn("Failed to inject the library loader, falling back to application builder"); - ApplicationBuilder.appending(NAME) - .injectableFactory(InjectableFactory.selecting(InjectableFactory.ERROR, InjectableFactory.INJECTABLE, InjectableFactory.WRAPPED, InjectableFactory.UNSAFE)) - .downloadDirectoryPath(downloadPath) - .logger(logger) - .build(); - } - } - - private static void loadSpigot(Path downloadPath, ProcessLogger logger) throws Throwable { - var current = SlimJar.class.getClassLoader(); - var libraryLoaderField = current.getClass().getDeclaredField("libraryLoader"); - libraryLoaderField.setAccessible(true); - if (!ClassLoader.class.isAssignableFrom(libraryLoaderField.getType())) throw new IllegalStateException("Failed to find library loader"); - final var libraryLoader = (ClassLoader) libraryLoaderField.get(current); - - final var pair = findRemapper(); - final var remapper = pair.getA(); - final var factory = pair.getB(); - final var classpath = new KList(); - - ApplicationBuilder.injecting(NAME, classpath::add) - .downloadDirectoryPath(downloadPath) - .logger(logger) - .build(); - - final var urls = remapper.andThen(KList::new) - .apply(classpath.convertNasty(url -> Path.of(url.toURI()))) - .convertNasty(path -> path.toUri().toURL()) - .toArray(URL[]::new); - libraryLoaderField.set(current, factory.apply(urls, libraryLoader == null ? current.getParent() : libraryLoader)); - } - - private static Pair, List>, BiFunction> findRemapper() { - Function, List> mapper = null; - BiFunction factory = null; - if (!DISABLE_REMAPPER) { - try { - var libraryLoader = Class.forName("org.bukkit.plugin.java.LibraryLoader"); - var mapperField = libraryLoader.getDeclaredField("REMAPPER"); - var factoryField = libraryLoader.getDeclaredField("LIBRARY_LOADER_FACTORY"); - mapperField.setAccessible(true); - factoryField.setAccessible(true); - mapper = (Function, List>) mapperField.get(null); - factory = (BiFunction) factoryField.get(null); - } catch (Throwable ignored) {} - } - - if (mapper == null) mapper = Function.identity(); - if (factory == null) factory = (urls, parent) -> new IsolatedInjectableClassLoader(urls, List.of(), parent); - return new Pair<>(mapper, factory); - } } diff --git a/core/src/main/java/com/volmit/iris/util/reflect/KeyedType.java b/core/src/main/java/com/volmit/iris/util/reflect/KeyedType.java new file mode 100644 index 000000000..1316e7c15 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/reflect/KeyedType.java @@ -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 TypeAdapter createTypeAdapter(Gson gson, TypeToken type) { + if (!isKeyed(type.getRawType())) return null; + return (TypeAdapter) (KEYED_ENABLED ? RegistryTypeAdapter.of(type.getRawType()) : OldEnum.create(type.getRawType())); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/reflect/OldEnum.java b/core/src/main/java/com/volmit/iris/util/reflect/OldEnum.java index 24293ad2a..7a7500915 100644 --- a/core/src/main/java/com/volmit/iris/util/reflect/OldEnum.java +++ b/core/src/main/java/com/volmit/iris/util/reflect/OldEnum.java @@ -1,20 +1,22 @@ package com.volmit.iris.util.reflect; import com.google.gson.TypeAdapter; -import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; 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.util.Arrays; import java.util.Objects; +@Deprecated(since = "3.7.1") public class OldEnum { private static final Class oldEnum; - private static final Method name; + private static final MethodHandle name; public static boolean exists() { return oldEnum != null; @@ -25,11 +27,16 @@ public class OldEnum { } public static T valueOf(Class c, String name) { - try { - return (T) c.getDeclaredField(name).get(null); - } catch (Throwable e) { - return null; + return valueOf(c, name, name.replace(".", "_")); + } + + public static T valueOf(Class c, String... names) { + for (final String name : names) { + try { + return (T) c.getDeclaredField(name).get(null); + } catch (Throwable ignored) {} } + return null; } public static String name(Object o) { @@ -40,20 +47,20 @@ public class OldEnum { } } - public static Object[] values(Class clazz) { - if (!isOldEnum(clazz)) return new Object[0]; + public static String[] values(Class clazz) { + if (!isOldEnum(clazz)) return new String[0]; return Arrays.stream(clazz.getDeclaredFields()) .filter(f -> Modifier.isStatic(f.getModifiers())) .filter(f -> Modifier.isFinal(f.getModifiers())) .map(f -> { try { - return f.get(null); + return name(f.get(null)); } catch (Throwable ignored) { return null; } }) .filter(Objects::nonNull) - .toArray(); + .toArray(String[]::new); } public static TypeAdapter create(Class type) { @@ -76,10 +83,10 @@ public class OldEnum { static { Class clazz = null; - Method method = null; + MethodHandle method = null; try { clazz = Class.forName("org.bukkit.util.OldEnum"); - method = clazz.getDeclaredMethod("name"); + method = MethodHandles.lookup().findVirtual(clazz, "name", MethodType.methodType(String.class)); } catch (Throwable ignored) {} if (clazz == null || method == null) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4b12ffb07..1a52c0d2c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ [versions] # Plugins shadow = "9.0.0-rc1" # https://plugins.gradle.org/plugin/com.gradleup.shadow -slimjar = "2.1.4" # https://plugins.gradle.org/plugin/de.crazydev22.slimjar +slimjar = "2.1.5" # https://plugins.gradle.org/plugin/de.crazydev22.slimjar download = "5.6.0" # https://plugins.gradle.org/plugin/de.undercouch.download runPaper = "2.3.1" # https://plugins.gradle.org/plugin/xyz.jpenilla.run-paper sentryPlugin = "5.8.0" # https://github.com/getsentry/sentry-android-gradle-plugin @@ -15,8 +15,8 @@ grgit = "5.3.2" # https://github.com/ajoberstar/grgit lombok = "1.18.38" spigot = "1.20.1-R0.1-SNAPSHOT" # https://hub.spigotmc.org/nexus/repository/snapshots/org/spigotmc/spigot-api/maven-metadata.xml log4j = "2.19.0" # https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api -adventure-api = "4.23.0" # https://github.com/KyoriPowered/adventure -adventure-platform = "4.4.0" # https://github.com/KyoriPowered/adventure-platform +adventure-api = "4.24.0" # https://github.com/KyoriPowered/adventure +adventure-platform = "4.4.1" # https://github.com/KyoriPowered/adventure-platform annotations = "26.0.2" # https://central.sonatype.com/artifact/org.jetbrains/annotations paralithic = "0.8.1" # https://github.com/PolyhedralDev/Paralithic/