From 439a936bf97010f2318c5c6357e7bbef64929d8c Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Sun, 7 Sep 2025 07:03:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E6=B7=BB=E5=8A=A0=E5=A4=96?= =?UTF-8?q?=E9=83=A8=E6=95=B0=E6=8D=AE=E6=9E=84=E5=BB=BA=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compatibility/item/SXItemSource.java | 2 +- .../core/item/ItemBuildContext.java | 15 +++ .../core/item/modifier/ExternalModifier.java | 38 +++++-- .../core/pack/AbstractPackManager.java | 18 +-- .../craftengine/core/pack/PackCacheData.java | 32 ++++-- .../craftengine/core/util/ListMonitor.java | 11 +- .../craftengine/core/util/SetMonitor.java | 104 ++++++++++++++++++ .../craftengine/core/util/StringUtils.java | 10 ++ 8 files changed, 201 insertions(+), 29 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/SetMonitor.java 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/ItemBuildContext.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java index a8b7dd219..b80c90e22 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemBuildContext.java @@ -7,10 +7,13 @@ import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextPar import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Deque; import java.util.Map; +import java.util.concurrent.ConcurrentLinkedDeque; public class ItemBuildContext extends PlayerOptionalContext { public static final ItemBuildContext EMPTY = new ItemBuildContext(null, ContextHolder.EMPTY); + private Deque externalBuildStack; public ItemBuildContext(@Nullable Player player, @NotNull ContextHolder contexts) { super(player, contexts); @@ -32,4 +35,16 @@ public class ItemBuildContext extends PlayerOptionalContext { if (player == null) return new ItemBuildContext(null, ContextHolder.EMPTY); return new ItemBuildContext(player, new ContextHolder(Map.of(DirectContextParameters.PLAYER, () -> player))); } + + @NotNull + public Deque getExternalBuildStack() { + if (externalBuildStack == null) { + externalBuildStack = new ConcurrentLinkedDeque<>(); + } + return externalBuildStack; + } + + public void clearExternalBuildStack() { + externalBuildStack = null; + } } 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..8a33a867b 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 @@ -9,11 +9,11 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigExce import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -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(ArrayDeque::new); private final String id; private final ExternalItemSource provider; @@ -38,14 +38,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()); + String stackElement = provider.plugin() + "[id=" + id + "]"; + Deque buildStack = BUILD_STACK.get(); + + if (buildStack.contains(stackElement)) { + StringJoiner dependencyChain = new StringJoiner(" -> "); + buildStack.forEach(dependencyChain::add); + dependencyChain.add(stackElement); + CraftEngine.instance().logger().warn("Item '" + item.customId().orElseGet(item::id) + + "' encountered circular dependency while building external item '" + this.id + + "' (from plugin '" + provider.plugin() + "'). Dependency chain: " + dependencyChain + ); return item; } - Item anotherWrapped = (Item) CraftEngine.instance().itemManager().wrap(another); - item.merge(anotherWrapped); - return item; + + buildStack.push(stackElement); + 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; + } finally { + buildStack.pop(); + if (buildStack.isEmpty()) { + BUILD_STACK.remove(); + } + } } public static class Factory implements ItemDataModifierFactory { 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 c4dab03ce..dcb35d111 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..b3c197034 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 @@ -2,6 +2,8 @@ package net.momirealms.craftengine.core.pack; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.util.SetMonitor; import org.jetbrains.annotations.NotNull; import java.nio.file.Files; @@ -14,16 +16,26 @@ public class PackCacheData { private final Set externalFolders; PackCacheData(@NotNull CraftEngine plugin) { - this.externalFolders = Config.foldersToMerge().stream() - .map(it -> plugin.dataFolderPath().getParent().resolve(it)) - .filter(Files::exists) - .collect(Collectors.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()); + this.externalFolders = new SetMonitor<>( + Config.foldersToMerge().stream() + .map(it -> plugin.dataFolderPath().getParent().resolve(it)) + .filter(Files::exists) + .collect(Collectors.toSet()), + add -> Debugger.RESOURCE_PACK.debug(() -> "Adding external folder: " + add), + remove -> Debugger.RESOURCE_PACK.debug(() -> "Removing external folder: " + remove), + true + ); + this.externalZips = new SetMonitor<>( + 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()), + add -> Debugger.RESOURCE_PACK.debug(() -> "Adding external zip: " + add), + remove -> Debugger.RESOURCE_PACK.debug(() -> "Removing external zip: " + remove), + true + ); } @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..aeb2c9350 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 @@ -15,12 +15,17 @@ public class ListMonitor implements List { private final Consumer removeConsumer; public ListMonitor(List list, Consumer addConsumer, Consumer removeConsumer) { - for (T key : list) { - addConsumer.accept(key); - } + this(list, addConsumer, removeConsumer, false); + } + + public ListMonitor(List list, Consumer addConsumer, Consumer removeConsumer, boolean skipInitialNotification) { this.list = list; this.addConsumer = addConsumer; this.removeConsumer = removeConsumer; + if (skipInitialNotification) return; + 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..97b5640b2 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SetMonitor.java @@ -0,0 +1,104 @@ +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, addConsumer, removeConsumer, false); + } + + public SetMonitor(Set set, Consumer addConsumer, Consumer removeConsumer, boolean skipInitialNotification) { + this.set = set; + this.addConsumer = addConsumer; + this.removeConsumer = removeConsumer; + if (skipInitialNotification) return; + 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); + } }