From b0e1305427f79f251ecc226b35e328f693db0e0c Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 7 Jun 2025 16:11:37 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=A8=E6=80=81lore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../factory/ComponentItemFactory1_20_5.java | 120 +++++++++++++++++- .../handler/CommonItemPacketHandler.java | 4 +- .../core/font/AbstractFontManager.java | 68 ++++------ .../craftengine/core/font/Font.java | 2 +- .../core/item/AbstractItemManager.java | 17 ++- .../item/modifier/CustomNameModifier.java | 3 +- .../item/modifier/DynamicLoreModifier.java | 58 +++++++++ .../core/item/modifier/ItemNameModifier.java | 3 +- .../core/item/modifier/LoreModifier.java | 6 +- .../core/pack/AbstractPackManager.java | 3 +- .../core/plugin/config/Config.java | 2 +- .../config/template/TemplateManagerImpl.java | 30 +---- .../craftengine/core/util/CharacterUtils.java | 19 ++- .../craftengine/core/util/MiscUtils.java | 24 ++++ gradle.properties | 2 +- 15 files changed, 256 insertions(+), 105 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/item/modifier/DynamicLoreModifier.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java index f79b48f36..9029a35a8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java @@ -4,11 +4,14 @@ import com.google.gson.JsonElement; import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper; import net.momirealms.craftengine.bukkit.item.ComponentTypes; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps; import net.momirealms.craftengine.bukkit.util.EnchantmentUtils; import net.momirealms.craftengine.core.item.Enchantment; import net.momirealms.craftengine.core.item.Trim; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.Tag; import org.bukkit.inventory.ItemStack; @@ -38,29 +41,136 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory rootMap = (Map) item.getJavaComponent(ComponentTypes.CUSTOM_DATA).orElse(null); + if (rootMap == null) return null; + Object currentObj = rootMap; + for (int i = 0; i < path.length; i++) { + Object pathSegment = path[i]; + if (pathSegment == null) return null; + String key = pathSegment.toString(); + currentObj = ((Map) currentObj).get(key); + if (currentObj == null) return null; + if (i == path.length - 1) { + return currentObj; + } + if (!(currentObj instanceof Map)) { + return null; + } + } + return currentObj; } @Override protected Tag getNBTTag(ComponentItemWrapper item, Object... path) { - throw new UnsupportedOperationException("This feature is not available on 1.20.5+"); + CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA).orElse(null); + if (rootTag == null) return null; + Tag currentTag = rootTag; + for (int i = 0; i < path.length; i++) { + Object pathSegment = path[i]; + if (pathSegment == null) return null; + CompoundTag t = (CompoundTag) currentTag; + String key = pathSegment.toString(); + currentTag = t.get(key); + if (currentTag == null) return null; + if (i == path.length - 1) { + return currentTag; + } + if (!(currentTag instanceof CompoundTag)) { + return null; + } + } + return currentTag; } @Override protected void setTag(ComponentItemWrapper item, Object value, Object... path) { - throw new UnsupportedOperationException("This feature is not available on 1.20.5+"); + Tag valueTag; + if (value instanceof Tag tag) { + valueTag = tag; + } else if (value instanceof JsonElement je) { + valueTag = MRegistryOps.JSON.convertTo(MRegistryOps.SPARROW_NBT, je); + } else if (CoreReflections.clazz$Tag.isInstance(value)) { + valueTag = MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, value); + } else { + assert MRegistryOps.JAVA != null; + valueTag = MRegistryOps.JAVA.convertTo(MRegistryOps.SPARROW_NBT, value); + } + + CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA).orElse(new CompoundTag()); + + if (path == null || path.length == 0) { + if (valueTag instanceof CompoundTag) { + rootTag = (CompoundTag) valueTag; + } else { + throw new IllegalArgumentException("Cannot set non-CompoundTag as root without path"); + } + } else { + CompoundTag currentTag = rootTag; + for (int i = 0; i < path.length - 1; i++) { + Object pathSegment = path[i]; + if (pathSegment == null) throw new NullPointerException("Path segment cannot be null"); + + String key = pathSegment.toString(); + Tag nextTag = currentTag.get(key); + + if (!(nextTag instanceof CompoundTag)) { + nextTag = new CompoundTag(); + currentTag.put(key, nextTag); + } + currentTag = (CompoundTag) nextTag; + } + + String finalKey = path[path.length - 1].toString(); + currentTag.put(finalKey, valueTag); + } + + item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag); } @Override protected boolean hasTag(ComponentItemWrapper item, Object... path) { - throw new UnsupportedOperationException("This feature is not available on 1.20.5+"); + return getNBTTag(item, path) != null; } @Override protected boolean removeTag(ComponentItemWrapper item, Object... path) { - throw new UnsupportedOperationException("This feature is not available on 1.20.5+"); + CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA).orElse(null); + if (rootTag == null || path == null || path.length == 0) return false; + + if (path.length == 1) { + String key = path[0].toString(); + if (rootTag.containsKey(key)) { + rootTag.remove(key); + item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag); + return true; + } + return false; + } + + CompoundTag parentTag = rootTag; + for (int i = 0; i < path.length - 1; i++) { + Object pathSegment = path[i]; + if (pathSegment == null) return false; + + String key = pathSegment.toString(); + Tag childTag = parentTag.get(key); + + if (!(childTag instanceof CompoundTag)) { + return false; + } + parentTag = (CompoundTag) childTag; + } + + String finalKey = path[path.length - 1].toString(); + if (parentTag.containsKey(finalKey)) { + parentTag.remove(finalKey); + item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag); + return true; + } + return false; } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java index 52359827c..9563b71f5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.plugin.network.handler; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.EntityDataUtils; import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; @@ -25,7 +26,8 @@ public class CommonItemPacketHandler implements EntityPacketHandler { for (int i = 0; i < packedItems.size(); i++) { Object packedItem = packedItems.get(i); int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); - if (entityDataId == EntityDataUtils.ITEM_DATA_ID) { + // TODO 检查为什么会导致问题,难道是其他插件乱发entity id? + if (entityDataId == EntityDataUtils.ITEM_DATA_ID && CoreReflections.clazz$ItemStack.isInstance(packedItem)) { Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); Optional optional = BukkitItemManager.instance().s2c(itemStack, (BukkitServerPlayer) user); diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java index cce4c9e69..4b62d8f22 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/AbstractFontManager.java @@ -10,7 +10,6 @@ import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.context.ContextHolder; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; import org.ahocorasick.trie.Token; import org.ahocorasick.trie.Trie; @@ -44,10 +43,8 @@ public abstract class AbstractFontManager implements FontManager { protected Trie emojiKeywordTrie; protected Map tagMapper; protected Map emojiMapper; - protected List emojiList; protected List allEmojiSuggestions; - protected Set existingImagePaths = new HashSet<>(); public AbstractFontManager(CraftEngine plugin) { this.plugin = plugin; @@ -68,7 +65,6 @@ public abstract class AbstractFontManager implements FontManager { this.images.clear(); this.illegalChars.clear(); this.emojis.clear(); - this.existingImagePaths.clear(); } @Override @@ -472,14 +468,11 @@ public abstract class AbstractFontManager implements FontManager { if (character.startsWith("\\u")) { chars = List.of(CharacterUtils.decodeUnicodeToChars(character)); } else { - if (CharacterUtils.containsCombinedCharacter(character)) { - TranslationManager.instance().log("warning.config.image.invalid_char", path.toString(), id.toString()); - } - StringBuilder stringBuilder = new StringBuilder(); - for (char c : character.toCharArray()) { - stringBuilder.append(String.format("\\u%04x", (int) c)); - } - chars = List.of(CharacterUtils.decodeUnicodeToChars(stringBuilder.toString())); + // ??? TODO 需要测试特殊字符集 +// if (CharacterUtils.containsCombinedCharacter(character)) { +// TranslationManager.instance().log("warning.config.image.invalid_char", path.toString(), id.toString()); +// } + chars = List.of(character.toCharArray()); } } } @@ -510,30 +503,26 @@ public abstract class AbstractFontManager implements FontManager { } Object heightObj = section.get("height"); - if (!resourceLocation.endsWith(".png")) resourceLocation += ".png"; - Key namespacedPath = Key.of(resourceLocation); - Path targetImagePath = pack.resourcePackFolder() - .resolve("assets") - .resolve(namespacedPath.namespace()) - .resolve("textures") - .resolve(namespacedPath.value()); - - if (!doesImageFileExist(targetImagePath)) { -// TranslationManager.instance().log("warning.config.image.file_not_found", path.toString(), id.toString(), targetImagePath.toString()); - // DO NOT RETURN, JUST GIVE WARNINGS - } else if (heightObj == null) { - try (InputStream in = Files.newInputStream(targetImagePath)) { - BufferedImage image = ImageIO.read(in); - heightObj = image.getHeight() / codepointGrid.length; - } catch (IOException e) { - plugin.logger().warn("Failed to load image " + targetImagePath, e); - return; - } - } if (heightObj == null) { - throw new LocalizedResourceConfigException("warning.config.image.missing_height", path, id); + Key namespacedPath = Key.of(resourceLocation); + Path targetImagePath = pack.resourcePackFolder() + .resolve("assets") + .resolve(namespacedPath.namespace()) + .resolve("textures") + .resolve(namespacedPath.value()); + if (Files.exists(targetImagePath)) { + try (InputStream in = Files.newInputStream(targetImagePath)) { + BufferedImage image = ImageIO.read(in); + heightObj = image.getHeight() / codepointGrid.length; + } catch (IOException e) { + plugin.logger().warn("Failed to load image " + targetImagePath, e); + return; + } + } else { + throw new LocalizedResourceConfigException("warning.config.image.missing_height", path, id); + } } int height = ResourceConfigUtils.getAsInt(heightObj, "height"); @@ -545,22 +534,11 @@ public abstract class AbstractFontManager implements FontManager { BitmapImage bitmapImage = new BitmapImage(id, fontKey, height, ascent, resourceLocation, codepointGrid); for (int[] y : codepointGrid) { for (int x : y) { - font.addBitMapImage(x, bitmapImage); + font.addBitmapImage(x, bitmapImage); } } images.put(id, bitmapImage); } - - private boolean doesImageFileExist(Path path) { - if (existingImagePaths.contains(path)) { - return true; - } - boolean exist = Files.exists(path); - if (exist) { - existingImagePaths.add(path); - } - return exist; - } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/Font.java b/core/src/main/java/net/momirealms/craftengine/core/font/Font.java index cfc1cfab9..87ce3b98d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/Font.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/Font.java @@ -28,7 +28,7 @@ public class Font { return this.idToCodepoint.get(codepoint); } - public void addBitMapImage(int codepoint, BitmapImage image) { + public void addBitmapImage(int codepoint, BitmapImage image) { this.idToCodepoint.put(codepoint, image); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index 1615d4fcb..729fd56b8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java @@ -439,16 +439,25 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl }, "external"); registerDataFunction((obj) -> { String name = obj.toString(); - return new CustomNameModifier<>(name); + return new CustomNameModifier<>(Config.nonItalic() ? "" + name : name); }, "custom-name"); registerDataFunction((obj) -> { String name = obj.toString(); - return new ItemNameModifier<>(name); + return new ItemNameModifier<>(Config.nonItalic() ? "" + name : name); }, "item-name", "display-name"); registerDataFunction((obj) -> { - List name = MiscUtils.getAsStringList(obj); - return new LoreModifier<>(name); + List lore = MiscUtils.getAsStringList(obj).stream().map(it -> "" + it).toList(); + return new LoreModifier<>(lore); }, "lore", "display-lore", "description"); + registerDataFunction((obj) -> { + Map> dynamicLore = new LinkedHashMap<>(); + if (obj instanceof Map map) { + for (Map.Entry entry : map.entrySet()) { + dynamicLore.put(entry.getKey().toString(), MiscUtils.getAsStringList(entry.getValue())); + } + } + return new DynamicLoreModifier<>(dynamicLore); + }, "dynamic-lore"); registerDataFunction((obj) -> { Map data = MiscUtils.castToMap(obj, false); return new TagsModifier<>(data); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java index d16b5281b..6d72273dd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/CustomNameModifier.java @@ -4,7 +4,6 @@ import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.sparrow.nbt.CompoundTag; @@ -14,7 +13,7 @@ public class CustomNameModifier implements ItemDataModifier { private final String argument; public CustomNameModifier(String argument) { - this.argument = Config.nonItalic() ? "" + argument : argument; + this.argument = argument; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DynamicLoreModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DynamicLoreModifier.java new file mode 100644 index 000000000..60c57efb0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/DynamicLoreModifier.java @@ -0,0 +1,58 @@ +package net.momirealms.craftengine.core.item.modifier; + +import net.momirealms.craftengine.core.item.ComponentKeys; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.item.NetworkItemHandler; +import net.momirealms.craftengine.core.util.AdventureHelper; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.Tag; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class DynamicLoreModifier implements ItemDataModifier { + private final Map> displayContexts; + private final String defaultContext; + + public DynamicLoreModifier(Map> displayContexts) { + this.defaultContext = displayContexts.keySet().iterator().next(); + this.displayContexts = displayContexts; + } + + @Override + public String name() { + return "dynamic-lore"; + } + + @Override + public void apply(Item item, ItemBuildContext context) { + String displayContext = Optional.ofNullable(item.getJavaTag("craftengine:display_context")).orElse(this.defaultContext).toString(); + List lore = this.displayContexts.get(displayContext); + if (lore == null) { + lore = this.displayContexts.get(this.defaultContext); + } + item.loreComponent(lore.stream().map(it -> AdventureHelper.miniMessage().deserialize(it, context.tagResolvers())).toList()); + } + + @Override + public void prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) { + if (VersionHelper.isOrAbove1_20_5()) { + Tag previous = item.getNBTComponent(ComponentKeys.LORE); + if (previous != null) { + networkData.put(ComponentKeys.LORE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); + } else { + networkData.put(ComponentKeys.LORE.asString(), NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + } + } else { + Tag previous = item.getNBTTag("display", "Lore"); + if (previous != null) { + networkData.put("display.Lore", NetworkItemHandler.pack(NetworkItemHandler.Operation.ADD, previous)); + } else { + networkData.put("display.Lore", NetworkItemHandler.pack(NetworkItemHandler.Operation.REMOVE)); + } + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java index f59c44abb..081d11a75 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ItemNameModifier.java @@ -4,7 +4,6 @@ import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.sparrow.nbt.CompoundTag; @@ -14,7 +13,7 @@ public class ItemNameModifier implements ItemDataModifier { private final String argument; public ItemNameModifier(String argument) { - this.argument = Config.nonItalic() ? "" + argument : argument; + this.argument = argument; } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/LoreModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/LoreModifier.java index 6328fe5c8..faeed738c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/LoreModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/LoreModifier.java @@ -4,7 +4,6 @@ import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.NetworkItemHandler; -import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.sparrow.nbt.CompoundTag; @@ -16,7 +15,7 @@ public class LoreModifier implements ItemDataModifier { private final List argument; public LoreModifier(List argument) { - this.argument = Config.nonItalic() ? argument.stream().map(it -> "" + it).toList() : argument; + this.argument = argument; } @Override @@ -26,8 +25,7 @@ public class LoreModifier implements ItemDataModifier { @Override public void apply(Item item, ItemBuildContext context) { - item.loreComponent(this.argument.stream().map(it -> AdventureHelper.miniMessage().deserialize( - it, context.tagResolvers())).toList()); + item.loreComponent(this.argument.stream().map(it -> AdventureHelper.miniMessage().deserialize(it, context.tagResolvers())).toList()); } @Override 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 d9befc7a4..c2abd2c50 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 @@ -488,6 +488,7 @@ public abstract class AbstractPackManager implements PackManager { this.plugin.logger().info("Loaded packs. Took " + String.format("%.2f", ((o2 - o1) / 1_000_000.0)) + " ms"); for (Map.Entry> entry : cachedConfigs.entrySet()) { ConfigParser parser = entry.getKey(); + if (!predicate.test(parser)) continue; long t1 = System.nanoTime(); for (CachedConfigSection cached : entry.getValue()) { for (Map.Entry configEntry : cached.config().entrySet()) { @@ -497,7 +498,7 @@ public abstract class AbstractPackManager implements PackManager { if (parser.supportsParsingObject()) { // do not apply templates parser.parseObject(cached.pack(), cached.filePath(), id, configEntry.getValue()); - } else if (predicate.test(parser)) { + } else { if (configEntry.getValue() instanceof Map configSection0) { Map config = castToMap(configSection0, false); if ((boolean) config.getOrDefault("enable", true)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index 4eaecf1f9..6db94681c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -160,7 +160,7 @@ public class Config { BasicFileAttributes attributes = Files.readAttributes(this.configFilePath, BasicFileAttributes.class); long lastModified = attributes.lastModifiedTime().toMillis(); long size = attributes.size(); - if (lastModified != this.lastModified || size != this.size) { + if (lastModified != this.lastModified || size != this.size || this.config == null) { byte[] configFileBytes = Files.readAllBytes(this.configFilePath); try (InputStream inputStream = new ByteArrayInputStream(configFileBytes)) { this.config = YamlDocument.create(inputStream); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index 57ef4f95c..398af24f7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -114,14 +114,14 @@ public class TemplateManagerImpl implements TemplateManager { Map results = new LinkedHashMap<>(); for (Object processedTemplate : processedTemplates) { if (processedTemplate instanceof Map map) { - deepMergeMaps(results, MiscUtils.castToMap(map, false)); + MiscUtils.deepMergeMaps(results, MiscUtils.castToMap(map, false)); } } if (processingResult.overrides() instanceof Map overrides) { results.putAll(MiscUtils.castToMap(overrides, false)); } if (processingResult.merges() instanceof Map merges) { - deepMergeMaps(results, MiscUtils.castToMap(merges, false)); + MiscUtils.deepMergeMaps(results, MiscUtils.castToMap(merges, false)); } return results; } else if (firstTemplate instanceof List) { @@ -155,7 +155,7 @@ public class TemplateManagerImpl implements TemplateManager { if (processingResult.overrides() instanceof Map overrides) { Map output = new LinkedHashMap<>(MiscUtils.castToMap(overrides, false)); if (processingResult.merges() instanceof Map merges) { - deepMergeMaps(output, MiscUtils.castToMap(merges, false)); + MiscUtils.deepMergeMaps(output, MiscUtils.castToMap(merges, false)); } return output; } else if (processingResult.overrides() instanceof List overrides) { @@ -329,28 +329,4 @@ public class TemplateManagerImpl implements TemplateManager { Object merges, Map arguments ) {} - - @SuppressWarnings("unchecked") - private void deepMergeMaps(Map baseMap, Map mapToMerge) { - for (Map.Entry entry : mapToMerge.entrySet()) { - String key = entry.getKey(); - Object value = entry.getValue(); - if (baseMap.containsKey(key)) { - Object existingValue = baseMap.get(key); - if (existingValue instanceof Map && value instanceof Map) { - Map existingMap = (Map) existingValue; - Map newMap = (Map) value; - deepMergeMaps(existingMap, newMap); - } else if (existingValue instanceof List && value instanceof List) { - List existingList = (List) existingValue; - List newList = (List) value; - existingList.addAll(newList); - } else { - baseMap.put(key, value); - } - } else { - baseMap.put(key, value); - } - } - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java index 208625927..90abef1e1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/CharacterUtils.java @@ -11,19 +11,16 @@ public class CharacterUtils { private CharacterUtils() {} public static char[] decodeUnicodeToChars(String unicodeString) { - String processedString = unicodeString.replace("\\u", ""); - int length = processedString.length() / 4; - char[] chars = new char[length]; - for (int i = 0; i < length; i++) { - String hex = processedString.substring(i * 4, i * 4 + 4); + int count = unicodeString.length() / 6; + if (unicodeString.length() % 6 != 0) { + throw new LocalizedResourceConfigException("warning.config.image.invalid_unicode_string_length"); + } + char[] chars = new char[count]; + for (int i = 0, j = 0; j < count; i += 6, j++) { + String hex = unicodeString.substring(i + 2, i + 6); try { int codePoint = Integer.parseInt(hex, 16); - if (Character.isSupplementaryCodePoint(codePoint)) { - chars[i] = Character.highSurrogate(codePoint); - chars[++i] = Character.lowSurrogate(codePoint); - } else { - chars[i] = (char) codePoint; - } + chars[j] = (char) codePoint; } catch (NumberFormatException e) { throw new LocalizedResourceConfigException("warning.config.image.invalid_hex_value", e, hex); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java index bf59ac48f..3cafd6416 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java @@ -99,4 +99,28 @@ public class MiscUtils { } } } + + @SuppressWarnings("unchecked") + public static void deepMergeMaps(Map baseMap, Map mapToMerge) { + for (Map.Entry entry : mapToMerge.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (baseMap.containsKey(key)) { + Object existingValue = baseMap.get(key); + if (existingValue instanceof Map && value instanceof Map) { + Map existingMap = (Map) existingValue; + Map newMap = (Map) value; + deepMergeMaps(existingMap, newMap); + } else if (existingValue instanceof List && value instanceof List) { + List existingList = (List) existingValue; + List newList = (List) value; + existingList.addAll(newList); + } else { + baseMap.put(key, value); + } + } else { + baseMap.put(key, value); + } + } + } } diff --git a/gradle.properties b/gradle.properties index 747826baf..33a71d065 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.56.1 +project_version=0.0.56.2 config_version=34 lang_version=15 project_group=net.momirealms From 26edc960a221d4394947bd1d15b1e2402947536c Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 7 Jun 2025 19:49:47 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=86=94=E7=82=89?= =?UTF-8?q?=E6=80=A7=E8=83=BD=EF=BC=8C=E4=BF=9D=E8=AF=81=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E5=AE=89=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/BukkitBlockManager.java | 4 +- .../bukkit/block/BukkitCustomBlock.java | 2 +- .../block/behavior/SaplingBlockBehavior.java | 2 +- .../bukkit/item/BukkitItemManager.java | 2 +- .../item/recipe/BukkitRecipeManager.java | 45 +- .../item/recipe/RecipeEventListener.java | 22 +- .../plugin/injector/InjectedCacheCheck.java | 4 + .../plugin/injector/RecipeInjector.java | 395 ++++++++---------- .../reflection/minecraft/MRegistries.java | 62 +-- .../bukkit/sound/BukkitSoundManager.java | 2 +- .../craftengine/bukkit/util/BlockTags.java | 2 +- .../craftengine/bukkit/util/FeatureUtils.java | 2 +- .../craftengine/bukkit/util/ItemTags.java | 2 +- .../bukkit/util/RegistryUtils.java | 2 +- .../item/recipe/AbstractRecipeManager.java | 11 +- gradle.properties | 2 +- 16 files changed, 276 insertions(+), 285 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java index 17e26e141..6e5c77ea3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java @@ -160,7 +160,7 @@ public class BukkitBlockManager extends AbstractBlockManager { for (Map.Entry> entry : this.clientBoundTags.entrySet()) { list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue())); } - Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.instance$Registries$BLOCK, list)); + Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.BLOCK, list)); for (Player player : Bukkit.getOnlinePlayers()) { this.plugin.networkManager().sendPacket(this.plugin.adapt(player), packet); } @@ -762,7 +762,7 @@ public class BukkitBlockManager extends AbstractBlockManager { private Object createBlockProperties(Key realBlockKey) throws Exception { Object blockProperties = CoreReflections.method$BlockBehaviour$Properties$of.invoke(null); Object realBlockResourceLocation = createResourceLocation(realBlockKey); - Object realBlockResourceKey = CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.instance$Registries$BLOCK, realBlockResourceLocation); + Object realBlockResourceKey = CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.BLOCK, realBlockResourceLocation); if (CoreReflections.field$BlockBehaviour$Properties$id != null) { CoreReflections.field$BlockBehaviour$Properties$id.set(blockProperties, realBlockResourceKey); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java index dfac86a24..93469a576 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlock.java @@ -151,7 +151,7 @@ public class BukkitCustomBlock extends AbstractCustomBlock { Object holder = BukkitCraftEngine.instance().blockManager().getMinecraftBlockHolder(state.customBlockState().registryId()); Set tags = new HashSet<>(); for (Key tag : settings.tags()) { - tags.add(CoreReflections.method$TagKey$create.invoke(null, MRegistries.instance$Registries$BLOCK, KeyUtils.toResourceLocation(tag))); + tags.add(CoreReflections.method$TagKey$create.invoke(null, MRegistries.BLOCK, KeyUtils.toResourceLocation(tag))); } CoreReflections.field$Holder$Reference$tags.set(holder, tags); // set burning properties diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java index acee6a957..8569e6ca9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java @@ -77,7 +77,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior { } private void generateTree(Object world, Object blockPos, Object blockState, Object randomSource) throws Exception { - Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.instance$Registries$CONFIGURED_FEATURE); + Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.CONFIGURED_FEATURE); if (registry == null) return; @SuppressWarnings("unchecked") Optional holder = (Optional) CoreReflections.method$Registry$getHolder1.invoke(registry, FeatureUtils.createFeatureKey(treeFeature())); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java index 632869c87..d909239f3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java @@ -214,7 +214,7 @@ public class BukkitItemManager extends AbstractItemManager { .orElseGet(() -> ((WritableRegistry) BuiltInRegistries.OPTIMIZED_ITEM_ID) .register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id)); Object resourceLocation = KeyUtils.toResourceLocation(id.namespace(), id.value()); - Object mcHolder = ((Optional) CoreReflections.method$Registry$getHolder1.invoke(MBuiltInRegistries.ITEM, CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.instance$Registries$ITEM, resourceLocation))).get(); + Object mcHolder = ((Optional) CoreReflections.method$Registry$getHolder1.invoke(MBuiltInRegistries.ITEM, CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.ITEM, resourceLocation))).get(); Set tags = (Set) CoreReflections.field$Holder$Reference$tags.get(mcHolder); for (Object tag : tags) { Key tagId = Key.of(CoreReflections.field$TagKey$location.get(tag).toString()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java index 05b6873eb..f37e0118c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/BukkitRecipeManager.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.MaterialUtils; import net.momirealms.craftengine.bukkit.util.RecipeUtils; @@ -45,9 +46,9 @@ public class BukkitRecipeManager extends AbstractRecipeManager { // 将自定义配方转为“广义”配方,接受更加宽容的输入 // 部分过程借助bukkit完成,部分直接通过nms方法注册 private static final Map>> MIXED_RECIPE_CONVERTORS = new HashMap<>(); - private static Object nmsRecipeManager; private static final List injectedIngredients = new ArrayList<>(); - private static final IdentityHashMap, Object> recipeToMcRecipeHolder = new IdentityHashMap<>(); + private static final IdentityHashMap, Object> CE_RECIPE_2_NMS_HOLDER = new IdentityHashMap<>(); + private static Object nmsRecipeManager; private static void registerNMSSmithingRecipe(Object recipe) { try { @@ -265,7 +266,8 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } public Object nmsRecipeHolderByRecipe(Recipe recipe) { - return recipeToMcRecipeHolder.get(recipe); + if (super.isReloading) return null; + return CE_RECIPE_2_NMS_HOLDER.get(recipe); } public static Object nmsRecipeManager() { @@ -287,6 +289,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { @Override public void load() { if (!Config.enableRecipeSystem()) return; + super.isReloading = true; if (VersionHelper.isOrAbove1_21_2()) { try { this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(nmsRecipeManager); @@ -308,7 +311,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager { } catch (ReflectiveOperationException e) { this.plugin.logger().warn("Failed to unregister recipes", e); } - recipeToMcRecipeHolder.clear(); } @Override @@ -320,6 +322,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { @Override public void disable() { unload(); + CE_RECIPE_2_NMS_HOLDER.clear(); HandlerList.unregisterAll(this.recipeEventListener); if (this.crafterEventListener != null) { HandlerList.unregisterAll(this.crafterEventListener); @@ -473,6 +476,15 @@ public class BukkitRecipeManager extends AbstractRecipeManager { // clear cache injectedIngredients.clear(); + + CE_RECIPE_2_NMS_HOLDER.clear(); + // create mappings + for (Map.Entry> entry : this.byId.entrySet()) { + Optional nmsRecipe = getOptionalNMSRecipe(entry.getKey()); + nmsRecipe.ifPresent(o -> CE_RECIPE_2_NMS_HOLDER.put(entry.getValue(), o)); + } + + super.isReloading = false; } catch (Exception e) { this.plugin.logger().warn("Failed to run delayed recipe tasks", e); } @@ -691,7 +703,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { return optionalItem.map(itemStackCustomItem -> MaterialUtils.getMaterial(itemStackCustomItem.material())).orElse(null); } - private static List getIngredientLooks(List> holders) throws ReflectiveOperationException { + private static List getIngredientLooks(List> holders) { List itemStacks = new ArrayList<>(); for (Holder holder : holders) { ItemStack itemStack = BukkitItemManager.instance().getBuildableItem(holder.value()).get().buildItemStack(ItemBuildContext.EMPTY, 1); @@ -710,8 +722,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { .map(Optional::get) .toList(); - Object shapedRecipe = getNMSRecipe(id); - recipeToMcRecipeHolder.put(recipe, shapedRecipe); + Object shapedRecipe = getOptionalNMSRecipe(id).get(); if (VersionHelper.isOrAbove1_20_2()) { shapedRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapedRecipe); } @@ -731,8 +742,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { try { List> actualIngredients = recipe.ingredientsInUse(); - Object shapelessRecipe = getNMSRecipe(id); - recipeToMcRecipeHolder.put(recipe, shapelessRecipe); + Object shapelessRecipe = getOptionalNMSRecipe(id).get(); if (VersionHelper.isOrAbove1_20_2()) { shapelessRecipe = CoreReflections.field$RecipeHolder$recipe.get(shapelessRecipe); } @@ -751,8 +761,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { private static void injectCookingRecipe(Key id, CustomCookingRecipe recipe) { try { Ingredient actualIngredient = recipe.ingredient(); - Object smeltingRecipe = getNMSRecipe(id); - recipeToMcRecipeHolder.put(recipe, smeltingRecipe); + Object smeltingRecipe = getOptionalNMSRecipe(id).get(); if (VersionHelper.isOrAbove1_20_2()) { smeltingRecipe = CoreReflections.field$RecipeHolder$recipe.get(smeltingRecipe); } @@ -771,23 +780,17 @@ public class BukkitRecipeManager extends AbstractRecipeManager { // 获取nms配方,请注意1.20.1获取配方本身,而1.20.2+获取的是配方的holder // recipe on 1.20.1 and holder on 1.20.2+ - private static Object getNMSRecipe(Key id) throws ReflectiveOperationException { + private static Optional getOptionalNMSRecipe(Key id) throws ReflectiveOperationException { if (VersionHelper.isOrAbove1_21_2()) { - Object resourceKey = CraftBukkitReflections.method$CraftRecipe$toMinecraft.invoke(null, new NamespacedKey(id.namespace(), id.value())); + Object resourceKey = FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id)); @SuppressWarnings("unchecked") Optional optional = (Optional) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceKey); - if (optional.isEmpty()) { - throw new IllegalArgumentException("Recipe " + id + " not found"); - } - return optional.get(); + return optional; } else { Object resourceLocation = KeyUtils.toResourceLocation(id); @SuppressWarnings("unchecked") Optional optional = (Optional) CoreReflections.method$RecipeManager$byKey.invoke(nmsRecipeManager, resourceLocation); - if (optional.isEmpty()) { - throw new IllegalArgumentException("Recipe " + id + " not found"); - } - return optional.get(); + return optional; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java index 3e059ee13..91a6938b7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java @@ -843,7 +843,7 @@ public class RecipeEventListener implements Listener { try { player = (Player) CraftBukkitReflections.method$InventoryView$getPlayer.invoke(event.getView()); } catch (ReflectiveOperationException e) { - plugin.logger().warn("Failed to get inventory viewer", e); + this.plugin.logger().warn("Failed to get inventory viewer", e); return; } @@ -854,14 +854,18 @@ public class RecipeEventListener implements Listener { if (ceRecipe != null) { inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); serverPlayer.setLastUsedRecipe(ceRecipe.id()); - correctCraftingRecipeUsed(inventory, ceRecipe); + if (!ceRecipe.id().equals(recipeId)) { + correctCraftingRecipeUsed(inventory, ceRecipe); + } return; } ceRecipe = this.recipeManager.recipeByInput(RecipeTypes.SHAPED, input, lastRecipe); if (ceRecipe != null) { inventory.setResult(ceRecipe.result(new ItemBuildContext(serverPlayer, ContextHolder.EMPTY))); serverPlayer.setLastUsedRecipe(ceRecipe.id()); - correctCraftingRecipeUsed(inventory, ceRecipe); + if (!ceRecipe.id().equals(recipeId)) { + correctCraftingRecipeUsed(inventory, ceRecipe); + } return; } // clear result if not met @@ -869,9 +873,8 @@ public class RecipeEventListener implements Listener { } private void correctCraftingRecipeUsed(CraftingInventory inventory, Recipe recipe) { - Object holderOrRecipe = recipeManager.nmsRecipeHolderByRecipe(recipe); + Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe); if (holderOrRecipe == null) { - // it's a vanilla recipe but not injected return; } try { @@ -922,20 +925,21 @@ public class RecipeEventListener implements Listener { CustomSmithingTransformRecipe transformRecipe = (CustomSmithingTransformRecipe) ceRecipe; ItemStack processed = transformRecipe.assemble(new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY), this.itemManager.wrap(base)); event.setResult(processed); - correctSmithingRecipeUsed(inventory, ceRecipe); + if (!ceRecipe.id().equals(recipeId)) { + correctSmithingRecipeUsed(inventory, ceRecipe); + } } private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe recipe) { - Object holderOrRecipe = recipeManager.nmsRecipeHolderByRecipe(recipe); + Object holderOrRecipe = this.recipeManager.nmsRecipeHolderByRecipe(recipe); if (holderOrRecipe == null) { - // it's a vanilla recipe but not injected return; } try { Object resultInventory = CraftBukkitReflections.field$CraftResultInventory$resultInventory.get(inventory); CoreReflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe); } catch (ReflectiveOperationException e) { - plugin.logger().warn("Failed to correct used recipe", e); + this.plugin.logger().warn("Failed to correct used recipe", e); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/InjectedCacheCheck.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/InjectedCacheCheck.java index 728547596..f8fc33654 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/InjectedCacheCheck.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/InjectedCacheCheck.java @@ -8,6 +8,10 @@ public interface InjectedCacheCheck { void recipeType(Object recipeType); + Key customRecipeType(); + + void customRecipeType(Key customRecipeType); + Object lastRecipe(); void lastRecipe(Object lastRecipe); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java index 820b9f556..a928ad74c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java @@ -16,6 +16,8 @@ import net.momirealms.craftengine.bukkit.item.recipe.BukkitRecipeManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRecipeTypes; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; +import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.recipe.CustomCookingRecipe; import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem; @@ -28,7 +30,6 @@ import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.inventory.ItemStack; -import java.util.List; import java.util.Optional; public class RecipeInjector { @@ -41,17 +42,23 @@ public class RecipeInjector { .name("net.momirealms.craftengine.bukkit.entity.InjectedCacheChecker") .implement(CoreReflections.clazz$RecipeManager$CachedCheck) .implement(InjectedCacheCheck.class) + .defineField("recipeType", Object.class, Visibility.PUBLIC) .method(ElementMatchers.named("recipeType")) .intercept(FieldAccessor.ofField("recipeType")) + + .defineField("customRecipeType", Key.class, Visibility.PUBLIC) + .method(ElementMatchers.named("customRecipeType")) + .intercept(FieldAccessor.ofField("customRecipeType")) + .defineField("lastRecipe", Object.class, Visibility.PUBLIC) .method(ElementMatchers.named("lastRecipe")) .intercept(FieldAccessor.ofField("lastRecipe")) - .method(ElementMatchers.named("setLastRecipe")) - .intercept(FieldAccessor.ofField("lastRecipe")) + .defineField("lastCustomRecipe", Key.class, Visibility.PUBLIC) .method(ElementMatchers.named("lastCustomRecipe")) .intercept(FieldAccessor.ofField("lastCustomRecipe")) + .method(ElementMatchers.named("getRecipeFor").or(ElementMatchers.named("a"))) .intercept(MethodDelegation.to( VersionHelper.isOrAbove1_21_2() ? @@ -73,269 +80,227 @@ public class RecipeInjector { if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected Object recipeType = FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$recipeType(entity); InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); - injectedChecker.recipeType(recipeType); + if (recipeType == MRecipeTypes.SMELTING) { + injectedChecker.customRecipeType(RecipeTypes.SMELTING); + injectedChecker.recipeType(MRecipeTypes.SMELTING); + } else if (recipeType == MRecipeTypes.BLASTING) { + injectedChecker.customRecipeType(RecipeTypes.BLASTING); + injectedChecker.recipeType(MRecipeTypes.BLASTING); + } else if (recipeType == MRecipeTypes.SMOKING) { + injectedChecker.customRecipeType(RecipeTypes.SMOKING); + injectedChecker.recipeType(MRecipeTypes.SMOKING); + } else { + throw new IllegalStateException("RecipeType " + recipeType + " not supported"); + } CoreReflections.field$AbstractFurnaceBlockEntity$quickCheck.set(entity, injectedChecker); } else if (!VersionHelper.isOrAbove1_21_2() && CoreReflections.clazz$CampfireBlockEntity.isInstance(entity)) { Object quickCheck = CoreReflections.field$CampfireBlockEntity$quickCheck.get(entity); if (clazz$InjectedCacheChecker.isInstance(quickCheck)) return; // already injected InjectedCacheCheck injectedChecker = (InjectedCacheCheck) ReflectionUtils.UNSAFE.allocateInstance(clazz$InjectedCacheChecker); + injectedChecker.customRecipeType(RecipeTypes.CAMPFIRE_COOKING); injectedChecker.recipeType(MRecipeTypes.CAMPFIRE_COOKING); CoreReflections.field$CampfireBlockEntity$quickCheck.set(entity, injectedChecker); } } + @SuppressWarnings("DuplicatedCode") public static class GetRecipeForMethodInterceptor1_20 { public static final GetRecipeForMethodInterceptor1_20 INSTANCE = new GetRecipeForMethodInterceptor1_20(); @SuppressWarnings("unchecked") @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { - Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); + public Object intercept(@This Object thisObj, @AllArguments Object[] args) { InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object type = injectedCacheCheck.recipeType(); - Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); - if (optionalRecipe.isPresent()) { - Pair pair = optionalRecipe.get(); - Object resourceLocation = pair.getFirst(); - Key recipeId = Key.of(resourceLocation.toString()); - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - - ItemStack itemStack; - List items; - if (type == MRecipeTypes.CAMPFIRE_COOKING) { - items = (List) CoreReflections.field$SimpleContainer$items.get(args[0]); - } else { - items = (List) CoreReflections.field$AbstractFurnaceBlockEntity$items.get(args[0]); - } - itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(items.get(0)); - - // it's a recipe from other plugins - boolean isCustom = recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - injectedCacheCheck.lastRecipe(resourceLocation); - return Optional.of(pair.getSecond()); - } - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); - if (idHolder.isEmpty()) { - return Optional.empty(); - } - - SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); - CustomCookingRecipe ceRecipe; - Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); - if (type == MRecipeTypes.SMELTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.BLASTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.SMOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.CAMPFIRE_COOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe); - } else { - return Optional.empty(); - } - if (ceRecipe == null) { - return Optional.empty(); - } - - // Cache recipes, it might be incorrect on reloading - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - // It doesn't matter at all - injectedCacheCheck.lastRecipe(resourceLocation); - return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(pair.getSecond())); - } else { + Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe(); + Optional> optionalRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); + if (optionalRecipe.isEmpty()) { return Optional.empty(); } + + Pair resourceLocationAndRecipe = optionalRecipe.get(); + Object rawRecipeResourceLocation = resourceLocationAndRecipe.getFirst(); + Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); + BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); + + boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); + if (!isCustom) { + injectedCacheCheck.lastRecipe(rawRecipeResourceLocation); + return Optional.of(resourceLocationAndRecipe.getSecond()); + } + + ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror( + injectedCacheCheck.recipeType() == MRecipeTypes.CAMPFIRE_COOKING ? + FastNMS.INSTANCE.field$SimpleContainer$items(args[0]).getFirst() : + FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$getItem(args[0], 0) + ); + + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); + if (idHolder.isEmpty()) { + return Optional.empty(); + } + + SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); + CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); + if (ceRecipe == null) { + return Optional.empty(); + } + + injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); + if (!ceRecipe.id().equals(rawRecipeKey)) { + injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id())); + } + return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(resourceLocationAndRecipe.getSecond()); } } + @SuppressWarnings("DuplicatedCode") public static class GetRecipeForMethodInterceptor1_20_5 { public static final GetRecipeForMethodInterceptor1_20_5 INSTANCE = new GetRecipeForMethodInterceptor1_20_5(); @SuppressWarnings("unchecked") @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { - Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); + public Object intercept(@This Object thisObj, @AllArguments Object[] args) { InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object type = injectedCacheCheck.recipeType(); - Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); - if (optionalRecipe.isPresent()) { - Object holder = optionalRecipe.get(); - Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder); - Key recipeId = Key.of(id.toString()); - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - - ItemStack itemStack; - List items; - if (type == MRecipeTypes.CAMPFIRE_COOKING) { - items = (List) CoreReflections.field$SimpleContainer$items.get(args[0]); - } else { - items = (List) CoreReflections.field$AbstractFurnaceBlockEntity$items.get(args[0]); - } - itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(items.get(0)); - - // it's a recipe from other plugins - boolean isCustom = recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - injectedCacheCheck.lastRecipe(id); - return optionalRecipe; - } - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); - if (idHolder.isEmpty()) { - return Optional.empty(); - } - - SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); - CustomCookingRecipe ceRecipe; - Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); - if (type == MRecipeTypes.SMELTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.BLASTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.SMOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.CAMPFIRE_COOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe); - } else { - return Optional.empty(); - } - if (ceRecipe == null) { - return Optional.empty(); - } - - // Cache recipes, it might be incorrect on reloading - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - // It doesn't matter at all - injectedCacheCheck.lastRecipe(id); - return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder)); - } else { + Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe(); + Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); + if (optionalRecipe.isEmpty()) { return Optional.empty(); } + + Object rawRecipeHolder = optionalRecipe.get(); + Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$RecipeHolder$id(rawRecipeHolder); + Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); + + BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); + ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror( + injectedCacheCheck.recipeType() == MRecipeTypes.CAMPFIRE_COOKING ? + FastNMS.INSTANCE.field$SimpleContainer$items(args[0]).getFirst() : + FastNMS.INSTANCE.field$AbstractFurnaceBlockEntity$getItem(args[0], 0) + ); + + boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); + if (!isCustom) { + injectedCacheCheck.lastRecipe(rawRecipeResourceLocation); + return optionalRecipe; + } + + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); + if (idHolder.isEmpty()) { + return Optional.empty(); + } + + SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); + CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); + if (ceRecipe == null) { + return Optional.empty(); + } + + injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); + if (!ceRecipe.id().equals(rawRecipeKey)) { + injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id())); + } + return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); } } + @SuppressWarnings("DuplicatedCode") public static class GetRecipeForMethodInterceptor1_21 { public static final GetRecipeForMethodInterceptor1_21 INSTANCE = new GetRecipeForMethodInterceptor1_21(); @SuppressWarnings("unchecked") @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { - Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); + public Object intercept(@This Object thisObj, @AllArguments Object[] args) { InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object type = injectedCacheCheck.recipeType(); - Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); - if (optionalRecipe.isPresent()) { - Object holder = optionalRecipe.get(); - Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder); - Key recipeId = Key.of(id.toString()); - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.field$SingleRecipeInput$item.get(args[0])); - - // it's a recipe from other plugins - boolean isCustom = recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - injectedCacheCheck.lastRecipe(id); - return optionalRecipe; - } - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); - if (idHolder.isEmpty()) { - return Optional.empty(); - } - - SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); - CustomCookingRecipe ceRecipe; - Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); - if (type == MRecipeTypes.SMELTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.BLASTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.SMOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.CAMPFIRE_COOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.CAMPFIRE_COOKING, input, lastCustomRecipe); - } else { - return Optional.empty(); - } - if (ceRecipe == null) { - return Optional.empty(); - } - - // Cache recipes, it might be incorrect on reloading - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - // It doesn't matter at all - injectedCacheCheck.lastRecipe(id); - return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder)); - } else { + Object lastRecipeResourceLocation = injectedCacheCheck.lastRecipe(); + Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceLocation); + if (optionalRecipe.isEmpty()) { return Optional.empty(); } + + Object rawRecipeHolder = optionalRecipe.get(); + Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$RecipeHolder$id(rawRecipeHolder); + Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); + + BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); + boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); + if (!isCustom) { + injectedCacheCheck.lastRecipe(rawRecipeResourceLocation); + return optionalRecipe; + } + + ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0])); + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); + if (idHolder.isEmpty()) { + return Optional.empty(); + } + + SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); + CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); + if (ceRecipe == null) { + return Optional.empty(); + } + + injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); + if (!ceRecipe.id().equals(rawRecipeKey)) { + injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id())); + } + return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); } } + @SuppressWarnings("DuplicatedCode") public static class GetRecipeForMethodInterceptor1_21_2 { public static final GetRecipeForMethodInterceptor1_21_2 INSTANCE = new GetRecipeForMethodInterceptor1_21_2(); @SuppressWarnings("unchecked") @RuntimeType - public Object intercept(@This Object thisObj, @AllArguments Object[] args) throws Exception { - Object mcRecipeManager = BukkitRecipeManager.nmsRecipeManager(); + public Object intercept(@This Object thisObj, @AllArguments Object[] args) { InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; - Object type = injectedCacheCheck.recipeType(); - Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(mcRecipeManager, type, args[0], args[1], lastRecipe); - if (optionalRecipe.isPresent()) { - Object holder = optionalRecipe.get(); - Object id = FastNMS.INSTANCE.field$RecipeHolder$id(holder); - Object resourceLocation = FastNMS.INSTANCE.field$ResourceKey$location(id); - Key recipeId = Key.of(resourceLocation.toString()); - BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); - ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.field$SingleRecipeInput$item.get(args[0])); - - // it's a recipe from other plugins - boolean isCustom = recipeManager.isCustomRecipe(recipeId); - if (!isCustom) { - injectedCacheCheck.lastRecipe(id); - return optionalRecipe; - } - - Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); - Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); - if (idHolder.isEmpty()) { - return Optional.empty(); - } - - SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); - CustomCookingRecipe ceRecipe; - Key lastCustomRecipe = injectedCacheCheck.lastCustomRecipe(); - if (type == MRecipeTypes.SMELTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMELTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.BLASTING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.BLASTING, input, lastCustomRecipe); - } else if (type == MRecipeTypes.SMOKING) { - ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(RecipeTypes.SMOKING, input, lastCustomRecipe); - } else { - return Optional.empty(); - } - if (ceRecipe == null) { - return Optional.empty(); - } - - // Cache recipes, it might be incorrect on reloading - injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); - // It doesn't matter at all - injectedCacheCheck.lastRecipe(id); - return Optional.of(Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(holder)); - } else { + Object lastRecipeResourceKey = injectedCacheCheck.lastRecipe(); + Optional optionalRecipe = (Optional) FastNMS.INSTANCE.method$RecipeManager$getRecipeFor(BukkitRecipeManager.nmsRecipeManager(), injectedCacheCheck.recipeType(), args[0], args[1], lastRecipeResourceKey); + if (optionalRecipe.isEmpty()) { return Optional.empty(); } + + // 获取配方的基础信息 + Object recipeHolder = optionalRecipe.get(); + Object rawRecipeResourceKey = FastNMS.INSTANCE.field$RecipeHolder$id(recipeHolder); + Object rawRecipeResourceLocation = FastNMS.INSTANCE.field$ResourceKey$location(rawRecipeResourceKey); + Key rawRecipeKey = Key.of(rawRecipeResourceLocation.toString()); + + BukkitRecipeManager recipeManager = BukkitRecipeManager.instance(); + // 来自其他插件注册的自定义配方 + boolean isCustom = recipeManager.isCustomRecipe(rawRecipeKey); + if (!isCustom) { + injectedCacheCheck.lastRecipe(rawRecipeResourceKey); + return optionalRecipe; + } + + // 获取唯一内存地址id + ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$SingleRecipeInput$item(args[0])); + Item wrappedItem = BukkitItemManager.instance().wrap(itemStack); + Optional> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id()); + if (idHolder.isEmpty()) { + return Optional.empty(); + } + + SingleItemInput input = new SingleItemInput<>(new OptimizedIDItem<>(idHolder.get(), itemStack)); + CustomCookingRecipe ceRecipe = (CustomCookingRecipe) recipeManager.recipeByInput(injectedCacheCheck.customRecipeType(), input, injectedCacheCheck.lastCustomRecipe()); + // 这个ce配方并不存在,那么应该返回空 + if (ceRecipe == null) { + return Optional.empty(); + } + + // 记录上一次使用的配方(ce) + injectedCacheCheck.lastCustomRecipe(ceRecipe.id()); + // 更新上一次使用的配方(nms) + if (!ceRecipe.id().equals(rawRecipeKey)) { + injectedCacheCheck.lastRecipe(FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(ceRecipe.id()))); + } + return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MRegistries.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MRegistries.java index 04a754e1b..244d26904 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MRegistries.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MRegistries.java @@ -10,21 +10,23 @@ import java.lang.reflect.Type; import static java.util.Objects.requireNonNull; public final class MRegistries { - public static final Object instance$Registries$BLOCK; - public static final Object instance$Registries$ITEM; - public static final Object instance$Registries$ATTRIBUTE; - public static final Object instance$Registries$BIOME; - public static final Object instance$Registries$MOB_EFFECT; - public static final Object instance$Registries$SOUND_EVENT; - public static final Object instance$Registries$PARTICLE_TYPE; - public static final Object instance$Registries$ENTITY_TYPE; - public static final Object instance$Registries$FLUID; - public static final Object instance$Registries$RECIPE_TYPE; - public static final Object instance$Registries$DIMENSION_TYPE; - public static final Object instance$Registries$CONFIGURED_FEATURE; - public static final Object instance$Registries$PLACED_FEATURE; + public static final Object BLOCK; + public static final Object ITEM; + public static final Object ATTRIBUTE; + public static final Object BIOME; + public static final Object MOB_EFFECT; + public static final Object SOUND_EVENT; + public static final Object PARTICLE_TYPE; + public static final Object ENTITY_TYPE; + public static final Object FLUID; + public static final Object RECIPE_TYPE; + public static final Object DIMENSION_TYPE; + public static final Object CONFIGURED_FEATURE; + public static final Object PLACED_FEATURE; @Nullable // 1.21+ - public static final Object instance$Registries$JUKEBOX_SONG; + public static final Object JUKEBOX_SONG; + @Nullable // 1.21+ + public static final Object RECIPE; static { Field[] fields = CoreReflections.clazz$Registries.getDeclaredFields(); @@ -43,6 +45,7 @@ public final class MRegistries { Object registries$ConfiguredFeature = null; Object registries$PlacedFeature = null; Object registries$JukeboxSong = null; + Object registries$Recipe = null; for (Field field : fields) { Type fieldType = field.getGenericType(); if (fieldType instanceof ParameterizedType paramType) { @@ -60,6 +63,8 @@ public final class MRegistries { registries$RecipeType = field.get(null); } else if (rawType == CoreReflections.clazz$ConfiguredFeature) { registries$ConfiguredFeature = field.get(null); + } else if (rawType == CoreReflections.clazz$Recipe) { + registries$Recipe = field.get(null); } } else { if (type == CoreReflections.clazz$Block) { @@ -88,20 +93,21 @@ public final class MRegistries { } } } - instance$Registries$BLOCK = requireNonNull(registries$Block); - instance$Registries$ITEM = requireNonNull(registries$Item); - instance$Registries$ATTRIBUTE = requireNonNull(registries$Attribute); - instance$Registries$BIOME = requireNonNull(registries$Biome); - instance$Registries$MOB_EFFECT = requireNonNull(registries$MobEffect); - instance$Registries$SOUND_EVENT = requireNonNull(registries$SoundEvent); - instance$Registries$DIMENSION_TYPE = requireNonNull(registries$DimensionType); - instance$Registries$PARTICLE_TYPE = requireNonNull(registries$ParticleType); - instance$Registries$ENTITY_TYPE = requireNonNull(registries$EntityType); - instance$Registries$FLUID = requireNonNull(registries$Fluid); - instance$Registries$RECIPE_TYPE = requireNonNull(registries$RecipeType); - instance$Registries$CONFIGURED_FEATURE = requireNonNull(registries$ConfiguredFeature); - instance$Registries$PLACED_FEATURE = requireNonNull(registries$PlacedFeature); - instance$Registries$JUKEBOX_SONG = registries$JukeboxSong; + BLOCK = requireNonNull(registries$Block); + ITEM = requireNonNull(registries$Item); + ATTRIBUTE = requireNonNull(registries$Attribute); + BIOME = requireNonNull(registries$Biome); + MOB_EFFECT = requireNonNull(registries$MobEffect); + SOUND_EVENT = requireNonNull(registries$SoundEvent); + DIMENSION_TYPE = requireNonNull(registries$DimensionType); + PARTICLE_TYPE = requireNonNull(registries$ParticleType); + ENTITY_TYPE = requireNonNull(registries$EntityType); + FLUID = requireNonNull(registries$Fluid); + RECIPE_TYPE = requireNonNull(registries$RecipeType); + CONFIGURED_FEATURE = requireNonNull(registries$ConfiguredFeature); + PLACED_FEATURE = requireNonNull(registries$PlacedFeature); + JUKEBOX_SONG = registries$JukeboxSong; + RECIPE = registries$Recipe; } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java index 50dfb6b68..4500128e3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/sound/BukkitSoundManager.java @@ -26,7 +26,7 @@ public class BukkitSoundManager extends AbstractSoundManager { protected void registerSongs(Map songs) { if (songs.isEmpty()) return; try { - Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.instance$Registries$JUKEBOX_SONG);; + Object registry = CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.JUKEBOX_SONG);; unfreezeRegistry(registry); for (Map.Entry entry : songs.entrySet()) { Key id = entry.getKey(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockTags.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockTags.java index 75728c940..a83a91c27 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockTags.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockTags.java @@ -16,7 +16,7 @@ public class BlockTags { Object value = CACHE.get(key); if (value == null) { try { - value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.instance$Registries$BLOCK, KeyUtils.toResourceLocation(key)); + value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.BLOCK, KeyUtils.toResourceLocation(key)); CACHE.put(key, value); return value; } catch (Exception e) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/FeatureUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/FeatureUtils.java index 800c203aa..e985bf7d6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/FeatureUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/FeatureUtils.java @@ -10,7 +10,7 @@ public class FeatureUtils { public static Object createFeatureKey(Key id) { try { - return CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.instance$Registries$CONFIGURED_FEATURE, KeyUtils.toResourceLocation(id)); + return CoreReflections.method$ResourceKey$create.invoke(null, MRegistries.CONFIGURED_FEATURE, KeyUtils.toResourceLocation(id)); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java index fc87dbc53..2e972673e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemTags.java @@ -19,7 +19,7 @@ public class ItemTags { Object value = CACHE.get(key); if (value == null) { try { - value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.instance$Registries$ITEM, KeyUtils.toResourceLocation(key)); + value = CoreReflections.method$TagKey$create.invoke(null, MRegistries.ITEM, KeyUtils.toResourceLocation(key)); CACHE.put(key, value); return value; } catch (Exception e) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RegistryUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RegistryUtils.java index d5754abde..f6e8bf219 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RegistryUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/RegistryUtils.java @@ -19,7 +19,7 @@ public class RegistryUtils { public static int currentBiomeRegistrySize() { try { - Object idMap = CoreReflections.method$Registry$asHolderIdMap.invoke(CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.instance$Registries$BIOME)); + Object idMap = CoreReflections.method$Registry$asHolderIdMap.invoke(CoreReflections.method$RegistryAccess$registryOrThrow.invoke(FastNMS.INSTANCE.registryAccess(), MRegistries.BIOME)); return (int) CoreReflections.method$IdMap$size.invoke(idMap); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java index f84e496ec..5b4a9d4ed 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeManager.java @@ -28,6 +28,7 @@ public abstract class AbstractRecipeManager implements RecipeManager { protected final Set dataPackRecipes = new HashSet<>(); protected final Set customRecipes = new HashSet<>(); private final RecipeParser recipeParser; + protected boolean isReloading; public AbstractRecipeManager() { this.recipeReader = initVanillaRecipeReader(); @@ -72,37 +73,44 @@ public abstract class AbstractRecipeManager implements RecipeManager { @Override public boolean isDataPackRecipe(Key key) { + if (this.isReloading) return false; return this.dataPackRecipes.contains(key); } @Override public boolean isCustomRecipe(Key key) { + if (this.isReloading) return false; return this.byId.containsKey(key); } @Override public Optional> recipeById(Key key) { + if (this.isReloading) return Optional.empty(); return Optional.ofNullable(this.byId.get(key)); } @Override public List> recipesByType(Key type) { + if (this.isReloading) return List.of(); return this.byType.getOrDefault(type, List.of()); } @Override public List> recipeByResult(Key result) { + if (this.isReloading) return List.of(); return this.byResult.getOrDefault(result, List.of()); } @Override public List> recipeByIngredient(Key ingredient) { + if (this.isReloading) return List.of(); return this.byIngredient.getOrDefault(ingredient, List.of()); } @Nullable @Override public Recipe recipeByInput(Key type, RecipeInput input) { + if (this.isReloading) return null; List> recipes = this.byType.get(type); if (recipes == null) return null; for (Recipe recipe : recipes) { @@ -116,8 +124,9 @@ public abstract class AbstractRecipeManager implements RecipeManager { @Nullable @Override public Recipe recipeByInput(Key type, RecipeInput input, Key lastRecipe) { + if (this.isReloading) return null; if (lastRecipe != null) { - Recipe last = byId.get(lastRecipe); + Recipe last = this.byId.get(lastRecipe); if (last != null && last.matches(input)) { return last; } diff --git a/gradle.properties b/gradle.properties index 33a71d065..e1b0975d8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -51,7 +51,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.17 -nms_helper_version=0.66.12 +nms_helper_version=0.66.15 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23 From db6f7056901e722decb236c90753133af4336367 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 7 Jun 2025 19:56:47 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8D1.20.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/plugin/injector/RecipeInjector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java index a928ad74c..c05455611 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/RecipeInjector.java @@ -150,7 +150,7 @@ public class RecipeInjector { if (!ceRecipe.id().equals(rawRecipeKey)) { injectedCacheCheck.lastRecipe(KeyUtils.toResourceLocation(ceRecipe.id())); } - return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)).orElse(resourceLocationAndRecipe.getSecond()); + return Optional.ofNullable(recipeManager.nmsRecipeHolderByRecipe(ceRecipe)); } }