diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/SXItemSource.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/SXItemSource.java index ca7ddbc59..93d820fd0 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/SXItemSource.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/item/SXItemSource.java @@ -13,7 +13,7 @@ public class SXItemSource implements ExternalItemSource { @Override public String plugin() { - return "sxitem"; + return "sx-item"; } @Nullable diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java index a424feb2a..4844f542e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ExternalModifier.java @@ -8,12 +8,13 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import org.jetbrains.annotations.NotNull; -import java.util.Locale; -import java.util.Map; +import java.util.*; public class ExternalModifier implements ItemDataModifier { public static final Factory FACTORY = new Factory<>(); + private static final ThreadLocal> BUILD_STACK = ThreadLocal.withInitial(LinkedHashSet::new); private final String id; private final ExternalItemSource provider; @@ -38,14 +39,36 @@ public class ExternalModifier implements ItemDataModifier { @SuppressWarnings("unchecked") @Override public Item apply(Item item, ItemBuildContext context) { - I another = this.provider.build(id, context); - if (another == null) { - CraftEngine.instance().logger().warn("'" + id + "' could not be found in " + provider.plugin()); + Dependency dependency = new Dependency(provider.plugin(), id); + Set buildStack = BUILD_STACK.get(); + + if (buildStack.contains(dependency)) { + StringJoiner dependencyChain = new StringJoiner(" -> "); + buildStack.forEach(element -> dependencyChain.add(element.asString())); + dependencyChain.add(dependency.asString()); + CraftEngine.instance().logger().warn( + "Failed to build '" + this.id + "' from plugin '" + provider.plugin() + "' due to dependency loop: " + dependencyChain + ); return item; } - Item anotherWrapped = (Item) CraftEngine.instance().itemManager().wrap(another); - item.merge(anotherWrapped); - return item; + + buildStack.add(dependency); + try { + I another = this.provider.build(this.id, context); + if (another == null) { + CraftEngine.instance().logger().warn("'" + this.id + "' could not be found in " + provider.plugin()); + return item; + } + Item anotherWrapped = (Item) CraftEngine.instance().itemManager().wrap(another); + item.merge(anotherWrapped); + return item; + } catch (Throwable e) { + CraftEngine.instance().logger().warn("Failed to build item '" + this.id + "' from plugin '" + provider.plugin() + "'", e); + return item; + } finally { + buildStack.remove(dependency); + BUILD_STACK.remove(); + } } public static class Factory implements ItemDataModifierFactory { @@ -60,4 +83,11 @@ public class ExternalModifier implements ItemDataModifier { return new ExternalModifier<>(id, ResourceConfigUtils.requireNonNullOrThrow(provider, () -> new LocalizedResourceConfigException("warning.config.item.data.external.invalid_source", plugin))); } } + + private record Dependency(String source, String id) { + + public @NotNull String asString() { + return source + "[id=" + id + "]"; + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index ac37372ab..9311afd1e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -24,7 +24,6 @@ import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator; import net.momirealms.craftengine.core.pack.model.rangedisptach.CustomModelDataRangeDispatchProperty; import net.momirealms.craftengine.core.pack.obfuscation.ObfA; -import net.momirealms.craftengine.core.pack.obfuscation.ResourcePackGenerationException; import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.pack.revision.Revisions; import net.momirealms.craftengine.core.plugin.CraftEngine; @@ -248,16 +247,21 @@ public abstract class AbstractPackManager implements PackManager { // magicConstructor.newInstance(resourcePackPath(), resourcePackPath()); Method magicMethod = ReflectionUtils.getMethod(magicClazz, void.class); assert magicMethod != null; - this.zipGenerator = (p1, p2) -> { + final String magicStr1 = StringUtils.fromBytes(new byte[]{5, 50, 36, 56, 34, 37, 52, 50, 7, 54, 52, 60, 16, 50, 57, 50, 37, 54, 35, 62, 56, 57, 18, 47, 52, 50, 39, 35, 62, 56, 57}, 87); + final String magicStr2 = StringUtils.fromBytes(new byte[]{4, 35, 43, 46, 39, 38, 98, 54, 45, 98, 37, 39, 44, 39, 48, 35, 54, 39, 98, 48, 39, 49, 45, 55, 48, 33, 39, 98, 50, 35, 33, 41, 120, 98}, 66); + final String magicStr3 = StringUtils.fromBytes(new byte[]{107, 76, 68, 65, 72, 73, 13, 89, 66, 13, 74, 72, 67, 72, 95, 76, 89, 72, 13, 87, 68, 93, 13, 75, 68, 65, 72, 94, 39}, 45); + ReflectionUtils.getDeclaredField(getClass().getSuperclass(), StringUtils.fromBytes(new byte[]{69, 86, 79, 120, 90, 81, 90, 77, 94, 75, 80, 77}, 63)).set(this, (BiConsumer) (p1, p2) -> { try { Object magicObject = magicConstructor.newInstance(p1, p2); magicMethod.invoke(magicObject); - } catch (ResourcePackGenerationException e) { - this.plugin.logger().warn("Failed to generate resource pack: " + e.getMessage()); - } catch (Exception e) { - this.plugin.logger().warn("Failed to generate zip files\n" + new StringWriter(){{e.printStackTrace(new PrintWriter(this));}}.toString().replaceAll("\\.[Il]{2,}", "").replaceAll("/[Il]{2,}", "")); + } catch (Throwable e) { + if (e.getClass().getSimpleName().equals(magicStr1)) { + this.plugin.logger().warn(magicStr2 + e.getMessage()); + } else { + this.plugin.logger().warn(magicStr3 + new StringWriter(){{e.printStackTrace(new PrintWriter(this));}}.toString().replaceAll("\\.[Il]{2,}", "").replaceAll("/[Il]{2,}", "")); + } } - }; + }); } else { this.plugin.logger().warn("Magic class doesn't exist"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/PackCacheData.java b/core/src/main/java/net/momirealms/craftengine/core/pack/PackCacheData.java index 1b4d1f23e..702bf2163 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/PackCacheData.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/PackCacheData.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import org.jetbrains.annotations.NotNull; @@ -7,7 +8,6 @@ import org.jetbrains.annotations.NotNull; import java.nio.file.Files; import java.nio.file.Path; import java.util.Set; -import java.util.stream.Collectors; public class PackCacheData { private final Set externalZips; @@ -17,13 +17,13 @@ public class PackCacheData { this.externalFolders = Config.foldersToMerge().stream() .map(it -> plugin.dataFolderPath().getParent().resolve(it)) .filter(Files::exists) - .collect(Collectors.toSet()); + .collect(ObjectOpenHashSet.toSet()); this.externalZips = Config.zipsToMerge().stream() .map(it -> plugin.dataFolderPath().getParent().resolve(it)) .filter(Files::exists) .filter(Files::isRegularFile) .filter(file -> file.getFileName().toString().endsWith(".zip")) - .collect(Collectors.toSet()); + .collect(ObjectOpenHashSet.toSet()); } @NotNull diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ListMonitor.java b/core/src/main/java/net/momirealms/craftengine/core/util/ListMonitor.java index bddcd6a34..3e37d53b5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/ListMonitor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ListMonitor.java @@ -13,14 +13,13 @@ public class ListMonitor implements List { private final List list; private final Consumer addConsumer; private final Consumer removeConsumer; - public ListMonitor(List list, Consumer addConsumer, Consumer removeConsumer) { - for (T key : list) { - addConsumer.accept(key); - } this.list = list; this.addConsumer = addConsumer; this.removeConsumer = removeConsumer; + for (T key : list) { + this.addConsumer.accept(key); + } } public List list() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SetMonitor.java b/core/src/main/java/net/momirealms/craftengine/core/util/SetMonitor.java new file mode 100644 index 000000000..39a3fa4ac --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SetMonitor.java @@ -0,0 +1,99 @@ +package net.momirealms.craftengine.core.util; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import java.util.function.Consumer; + +public class SetMonitor implements Set { + private final Set set; + private final Consumer addConsumer; + private final Consumer removeConsumer; + + public SetMonitor(Set set, Consumer addConsumer, Consumer removeConsumer) { + this.set = set; + this.addConsumer = addConsumer; + this.removeConsumer = removeConsumer; + for (E element : set) { + this.addConsumer.accept(element); + } + } + + @Override + public boolean add(E e) { + this.addConsumer.accept(e); + return this.set.add(e); + } + + @Override + public boolean remove(Object o) { + this.removeConsumer.accept(o); + return this.set.remove(o); + } + + @Override + public boolean addAll(@NotNull Collection c) { + for (E element : c) { + this.addConsumer.accept(element); + } + return this.set.addAll(c); + } + + @Override + public boolean removeAll(@NotNull Collection c) { + for (Object o : c) { + this.removeConsumer.accept(o); + } + return this.set.removeAll(c); + } + + @Override + public void clear() { + for (E element : this.set) { + this.removeConsumer.accept(element); + } + this.set.clear(); + } + + @Override + public int size() { + return this.set.size(); + } + + @Override + public boolean isEmpty() { + return this.set.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return this.set.contains(o); + } + + @Override + public @NotNull Iterator iterator() { + return this.set.iterator(); + } + + @Override + public @NotNull Object @NotNull [] toArray() { + return this.set.toArray(); + } + + @Override + public @NotNull T @NotNull [] toArray(@NotNull T[] a) { + return this.set.toArray(a); + } + + @Override + public boolean containsAll(@NotNull Collection c) { + return this.set.containsAll(c); + } + + @Override + public boolean retainAll(@NotNull Collection c) { + return this.set.retainAll(c); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/StringUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/StringUtils.java index 4c44e0054..a399dd63c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/StringUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/StringUtils.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.core.util; +import java.nio.charset.StandardCharsets; + public final class StringUtils { private StringUtils() {} @@ -39,4 +41,12 @@ public final class StringUtils { } return new String(chars); } + + public static String fromBytes(byte[] bytes, int index) { + byte[] decodedBytes = new byte[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + decodedBytes[i] = (byte) (bytes[i] ^ ((byte) index)); + } + return new String(decodedBytes, StandardCharsets.UTF_8); + } }