From b7e05a33de7dc728870a70a83cbdb6e6ebe57c06 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 9 Apr 2025 22:34:47 +0800 Subject: [PATCH 01/28] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=81=AB=E9=9F=B3?= =?UTF-8?q?=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/block/BukkitBlockManager.java | 3 +++ .../net/momirealms/craftengine/bukkit/util/Reflections.java | 3 +++ 2 files changed, 6 insertions(+) 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 730920314..9f60f3af1 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 @@ -318,6 +318,9 @@ public class BukkitBlockManager extends AbstractBlockManager { } } + affectedBlocks.remove(Reflections.instance$Blocks$FIRE); + affectedBlocks.remove(Reflections.instance$Blocks$SOUL_FIRE); + this.affectedSoundBlocks = ImmutableSet.copyOf(affectedBlocks); ImmutableMap.Builder soundMapperBuilder = ImmutableMap.builder(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index 7c67dbc05..869b18b27 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -3519,6 +3519,7 @@ public class Reflections { public static final Object instance$Blocks$STONE; public static final Object instance$Blocks$STONE$defaultState; public static final Object instance$Blocks$FIRE; + public static final Object instance$Blocks$SOUL_FIRE; public static final Object instance$Blocks$ICE; static { @@ -3528,6 +3529,8 @@ public class Reflections { instance$Blocks$AIR$defaultState = method$Block$defaultBlockState.invoke(instance$Blocks$AIR); Object fire = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "fire"); instance$Blocks$FIRE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, fire); + Object soulFire = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "soul_fire"); + instance$Blocks$SOUL_FIRE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, soulFire); Object stone = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "stone"); instance$Blocks$STONE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, stone); instance$Blocks$STONE$defaultState = method$Block$defaultBlockState.invoke(instance$Blocks$STONE); From 55b0bee275e4a81dc12a551850ed2d14d93bcae1 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 9 Apr 2025 23:23:17 +0800 Subject: [PATCH 02/28] =?UTF-8?q?=E6=8B=A6=E6=88=AA=E6=8C=87=E4=BB=A4?= =?UTF-8?q?=E5=92=8C=E6=B6=88=E6=81=AF=E7=9A=84tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/loader/src/main/resources/config.yml | 2 +- .../resources/default/configuration/icons.yml | 2 +- .../internal/configuration/offset_chars.yml | 88 +++++++++---------- .../bukkit/font/BukkitFontManager.java | 68 ++++++++------ .../core/font/AbstractFontManager.java | 16 ++++ .../craftengine/core/font/FontManager.java | 2 + gradle.properties | 2 +- 7 files changed, 107 insertions(+), 73 deletions(-) diff --git a/bukkit/loader/src/main/resources/config.yml b/bukkit/loader/src/main/resources/config.yml index 78ff3ec52..242aba376 100644 --- a/bukkit/loader/src/main/resources/config.yml +++ b/bukkit/loader/src/main/resources/config.yml @@ -261,7 +261,7 @@ chunk-system: restore-custom-blocks-on-chunk-load: true offset-characters: - font: minecraft:default + font: minecraft:offset_chars -1: '\uf800' -2: '\uf801' -3: '\uf802' diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/icons.yml b/bukkit/loader/src/main/resources/resources/default/configuration/icons.yml index 6b2db4498..d54812865 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/icons.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/icons.yml @@ -2,7 +2,7 @@ images: default:icons: height: 10 ascent: 9 - font: minecraft:icons # Do not use 'minecraft:default' unless other plugins don't support custom font! + font: minecraft:icons file: minecraft:font/image/icons.png chars: - '\ub000\ub001' diff --git a/bukkit/loader/src/main/resources/resources/internal/configuration/offset_chars.yml b/bukkit/loader/src/main/resources/resources/internal/configuration/offset_chars.yml index cf2175bf7..45b1c925a 100644 --- a/bukkit/loader/src/main/resources/resources/internal/configuration/offset_chars.yml +++ b/bukkit/loader/src/main/resources/resources/internal/configuration/offset_chars.yml @@ -2,264 +2,264 @@ images: internal:neg_1: height: -3 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf800' internal:neg_2: height: -4 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf801' internal:neg_3: height: -5 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf802' internal:neg_4: height: -6 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf803' internal:neg_5: height: -7 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf804' internal:neg_6: height: -8 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf805' internal:neg_7: height: -9 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf806' internal:neg_8: height: -10 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf807' internal:neg_9: height: -11 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf808' internal:neg_10: height: -12 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf809' internal:neg_11: height: -13 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf80a' internal:neg_12: height: -14 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf80b' internal:neg_13: height: -15 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf80c' internal:neg_14: height: -16 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf80d' internal:neg_15: height: -17 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf80e' internal:neg_16: height: -18 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf80f' internal:neg_24: height: -26 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf810' internal:neg_32: height: -34 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf811' internal:neg_48: height: -50 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf812' internal:neg_64: height: -66 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf813' internal:neg_128: height: -130 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf814' internal:neg_256: height: -258 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf815' internal:pos_1: height: -1 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf830' internal:pos_2: height: 1 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf831' internal:pos_3: height: 2 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf832' internal:pos_4: height: 3 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf833' internal:pos_5: height: 4 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf834' internal:pos_6: height: 5 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf835' internal:pos_7: height: 6 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf836' internal:pos_8: height: 7 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf837' internal:pos_9: height: 8 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf838' internal:pos_10: height: 9 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf839' internal:pos_11: height: 10 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf83a' internal:pos_12: height: 11 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf83b' internal:pos_13: height: 12 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf83c' internal:pos_14: height: 13 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf83d' internal:pos_15: height: 14 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf83e' internal:pos_16: height: 15 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf83f' internal:pos_24: height: 23 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf840' internal:pos_32: height: 31 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf841' internal:pos_48: height: 47 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf842' internal:pos_64: height: 63 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf843' internal:pos_128: height: 127 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf844' internal:pos_256: height: 255 ascent: -5000 - font: minecraft:default + font: minecraft:offset_chars file: minecraft:font/offset/space_split.png char: '\uf845' \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 1d32645ec..483bbab9f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -2,6 +2,8 @@ package net.momirealms.craftengine.bukkit.font; import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent; import io.papermc.paper.event.player.AsyncChatDecorateEvent; +import io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent; +import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.Reflections; @@ -15,8 +17,10 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; +import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import java.util.Map; import java.util.function.Consumer; public class BukkitFontManager extends AbstractFontManager implements Listener { @@ -55,7 +59,6 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onCommand(PlayerCommandPreprocessEvent event) { if (!Config.filterCommand()) return; - if (!this.isDefaultFontInUse()) return; if (event.getPlayer().hasPermission(FontManager.BYPASS_COMMAND)) { return; } @@ -66,42 +69,55 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { private void processChatEvent(AsyncChatDecorateEvent event) { Player player = event.player(); if (player == null) return; - if (!this.isDefaultFontInUse()) return; - if (player.hasPermission(FontManager.BYPASS_CHAT)) { - return; - } try { Object originalMessage = Reflections.field$AsyncChatDecorateEvent$originalMessage.get(event); - runIfContainsIllegalCharacter(ComponentUtils.paperAdventureToJson(originalMessage), (json) -> { - Object component = ComponentUtils.jsonToPaperAdventure(json); - try { - Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - }); + String jsonMessage = ComponentUtils.paperAdventureToJson(originalMessage); + if (!player.hasPermission(FontManager.BYPASS_CHAT)) { + runIfContainsIllegalCharacter(jsonMessage, (json) -> { + Object component = ComponentUtils.jsonToPaperAdventure(json); + try { + Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + }); + } } catch (IllegalAccessException e) { throw new RuntimeException(e); } } - private void runIfContainsIllegalCharacter(String string, Consumer callback) { - //noinspection DuplicatedCode - char[] chars = string.toCharArray(); - int[] codepoints = CharacterUtils.charsToCodePoints(chars); - int[] newCodepoints = new int[codepoints.length]; + private void runIfContainsIllegalCharacter(String raw, Consumer callback) { boolean hasIllegal = false; - for (int i = 0; i < codepoints.length; i++) { - int codepoint = codepoints[i]; - if (!isIllegalCodepoint(codepoint)) { - newCodepoints[i] = codepoint; - } else { - newCodepoints[i] = '*'; + // replace illegal image usage + Map tokens = matchTags(raw); + if (!tokens.isEmpty()) { + for (Map.Entry entry : tokens.entrySet()) { + raw = raw.replace(entry.getKey(), "*"); hasIllegal = true; } } - if (hasIllegal) { - callback.accept(new String(newCodepoints, 0, newCodepoints.length)); + + if (this.isDefaultFontInUse()) { + // replace illegal codepoint + char[] chars = raw.toCharArray(); + int[] codepoints = CharacterUtils.charsToCodePoints(chars); + int[] newCodepoints = new int[codepoints.length]; + + for (int i = 0; i < codepoints.length; i++) { + int codepoint = codepoints[i]; + if (!isIllegalCodepoint(codepoint)) { + newCodepoints[i] = codepoint; + } else { + newCodepoints[i] = '*'; + hasIllegal = true; + } + } + if (hasIllegal) { + callback.accept(new String(newCodepoints, 0, newCodepoints.length)); + } + } else if (hasIllegal) { + callback.accept(raw); } } } 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 77d1e3c6c..b8ecc62bf 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 @@ -65,6 +65,22 @@ public abstract class AbstractFontManager implements FontManager { return tags; } + @Override + public String stripTags(String text) { + if (this.trie == null) { + return text; + } + StringBuilder builder = new StringBuilder(); + for (Token token : this.trie.tokenize(text)) { + if (token.isMatch()) { + builder.append("*"); + } else { + builder.append(token.getFragment()); + } + } + return builder.toString(); + } + @Override public ConfigSectionParser[] parsers() { return new ConfigSectionParser[] {this.imageParser, this.emojiParser}; diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index 143bd2858..456947d4e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -19,6 +19,8 @@ public interface FontManager extends Manageable { String BYPASS_COMMAND = "craftengine.filter.bypass.command"; String BYPASS_ANVIL = "craftengine.filter.bypass.anvil"; + String stripTags(String text); + ConfigSectionParser[] parsers(); boolean isDefaultFontInUse(); diff --git a/gradle.properties b/gradle.properties index 47baf8538..a8e68ad88 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.46.2 +project_version=0.0.46.3 config_version=23 lang_version=4 project_group=net.momirealms From e9bddc880eda37c44c296cf44d4ba3120afc9e85 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 10 Apr 2025 02:06:29 +0800 Subject: [PATCH 03/28] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E6=B7=BB=E5=8A=A0emoji?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/default/configuration/emoji.yml | 26 +++++ .../resources/default/configuration/i18n.yml | 8 +- .../src/main/resources/translations/en.yml | 5 +- .../src/main/resources/translations/zh_cn.yml | 5 +- .../bukkit/font/BukkitFontManager.java | 57 ++++++--- .../core/font/AbstractFontManager.java | 109 ++++++++++++++++-- .../craftengine/core/font/Emoji.java | 24 ++-- .../core/font/EmojiParameters.java | 9 ++ .../craftengine/core/font/FontManager.java | 3 + .../core/item/ItemBuildContext.java | 29 +---- .../core/pack/AbstractPackManager.java | 2 + .../gui/category/ItemBrowserManagerImpl.java | 15 +-- .../text/minimessage/NamedArgumentTag.java | 5 +- .../core/util/context/PlayerContext.java | 42 +++++++ 14 files changed, 262 insertions(+), 77 deletions(-) create mode 100644 bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml create mode 100644 core/src/main/java/net/momirealms/craftengine/core/font/EmojiParameters.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/context/PlayerContext.java diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml b/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml new file mode 100644 index 000000000..77f21e382 --- /dev/null +++ b/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml @@ -0,0 +1,26 @@ +templates: + default:emoji/basic: + content: "'>" + default:emoji/addition_info: + content: "'>{text}" + +emoji: + default:emoji_location: + template: "default:emoji/addition_info" + arguments: + text: "" + overrides: + image: "default:icons:0:0" + permission: emoji.location + keywords: + - ":location:" + - ":pos:" + default:emoji_time: + template: "default:emoji/addition_info" + arguments: + text: "" + overrides: + image: "default:icons:0:1" + permission: emoji.time + keywords: + - ":time:" \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml b/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml index 51b178e1b..fe6db7c36 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/i18n.yml @@ -39,6 +39,9 @@ i18n: category.topaz: "Topaz" category.furniture: "Furniture" category.misc: "Misc" + emoji.tip: "Use '' to send the '' emoji" + emoji.time: "Current time: " + emoji.location: "Current coordinates: ,," zh_cn: item.chinese_lantern: "灯笼" item.fairy_flower: "仙灵花" @@ -78,4 +81,7 @@ i18n: category.palm_tree: "棕榈树" category.topaz: "黄玉" category.furniture: "家具" - category.misc: "杂项" \ No newline at end of file + category.misc: "杂项" + emoji.tip: "使用''来发送表情''" + emoji.time: "当前时间: " + emoji.location: "当前坐标: ,," \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/translations/en.yml b/bukkit/loader/src/main/resources/translations/en.yml index d2c95dfb1..007676fe9 100644 --- a/bukkit/loader/src/main/resources/translations/en.yml +++ b/bukkit/loader/src/main/resources/translations/en.yml @@ -102,4 +102,7 @@ warning.config.block.state.model.lack_path: "Issue found in file warning.config.block.state.model.invalid_resource_location: "Issue found in file - The block '' has a 'path' argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" warning.config.model.generation.conflict: "Issue found in file - Failed to generate model for '' as two or more configurations attempt to generate different json models with the same path: ''" warning.config.model.generation.texture.invalid_resource_location: "Issue found in file - The config '' has a '' texture argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" -warning.config.model.generation.parent.invalid_resource_location: "Issue found in file - The config '' has a parent argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" \ No newline at end of file +warning.config.model.generation.parent.invalid_resource_location: "Issue found in file - The config '' has a parent argument [] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters" +warning.config.emoji.lack_keywords: "Issue found in file - The emoji '' is missing the required 'keywords' argument." +warning.config.emoji.duplicated: "Issue found in file - Duplicated emoji ''." +warning.config.emoji.invalid_image: "Issue found in file - The emoji '' has an invalid 'image' argument ''." \ No newline at end of file diff --git a/bukkit/loader/src/main/resources/translations/zh_cn.yml b/bukkit/loader/src/main/resources/translations/zh_cn.yml index 822f32e54..66acc8f30 100644 --- a/bukkit/loader/src/main/resources/translations/zh_cn.yml +++ b/bukkit/loader/src/main/resources/translations/zh_cn.yml @@ -102,4 +102,7 @@ warning.config.block.state.model.lack_path: "在文件 中发现 warning.config.block.state.model.invalid_resource_location: "在文件 中发现问题 - 方块 '' 的 'path' 路径参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" warning.config.model.generation.conflict: "在文件 中发现问题 - 无法为 '' 生成模型,多个配置尝试用相同路径 '' 生成不同的JSON模型" warning.config.model.generation.texture.invalid_resource_location: "在文件 中发现问题 - 配置项 '' 的 '' 纹理参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" -warning.config.model.generation.parent.invalid_resource_location: "在文件 中发现问题 - 配置项 '' 的父模型参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" \ No newline at end of file +warning.config.model.generation.parent.invalid_resource_location: "在文件 中发现问题 - 配置项 '' 的父模型参数 [] 包含非法字符,请参考资源路径规范:https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" +warning.config.emoji.lack_keywords: "在文件 中发现问题 - 表情 '' 缺少必要的 'keywords' 配置" +warning.config.emoji.duplicated: "在文件 中发现问题 - 表情 '' 重复定义" +warning.config.emoji.invalid_image: "在文件 中发现问题 - 表情 '' 使用了无效的 'image' 图片参数 ''." \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 483bbab9f..28ee0c48a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -2,7 +2,6 @@ package net.momirealms.craftengine.bukkit.font; import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent; import io.papermc.paper.event.player.AsyncChatDecorateEvent; -import io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.ComponentUtils; @@ -10,6 +9,7 @@ import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.font.AbstractFontManager; import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.text.minimessage.NamedArgumentTag; import net.momirealms.craftengine.core.util.CharacterUtils; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -17,11 +17,10 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; -import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import java.lang.reflect.InvocationTargetException; import java.util.Map; -import java.util.function.Consumer; public class BukkitFontManager extends AbstractFontManager implements Listener { private final BukkitCraftEngine plugin; @@ -59,10 +58,12 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onCommand(PlayerCommandPreprocessEvent event) { if (!Config.filterCommand()) return; - if (event.getPlayer().hasPermission(FontManager.BYPASS_COMMAND)) { - return; + if (!event.getPlayer().hasPermission(FontManager.BYPASS_COMMAND)) { + IllegalCharacterProcessResult result = processIllegalCharacters(event.getMessage()); + if (result.has()) { + event.setMessage(result.newText()); + } } - runIfContainsIllegalCharacter(event.getMessage(), event::setMessage); } @SuppressWarnings("UnstableApiUsage") @@ -71,23 +72,28 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { if (player == null) return; try { Object originalMessage = Reflections.field$AsyncChatDecorateEvent$originalMessage.get(event); - String jsonMessage = ComponentUtils.paperAdventureToJson(originalMessage); + String rawJsonMessage = ComponentUtils.paperAdventureToJson(originalMessage); + String jsonMessage = replaceEmoji(rawJsonMessage, this.plugin.adapt(player)); + boolean hasChanged = !rawJsonMessage.equals(jsonMessage); if (!player.hasPermission(FontManager.BYPASS_CHAT)) { - runIfContainsIllegalCharacter(jsonMessage, (json) -> { - Object component = ComponentUtils.jsonToPaperAdventure(json); - try { - Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - }); + IllegalCharacterProcessResult result = processIllegalCharacters(jsonMessage); + if (result.has()) { + Object component = ComponentUtils.jsonToPaperAdventure(result.newText()); + Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); + } else if (hasChanged) { + Object component = ComponentUtils.jsonToPaperAdventure(jsonMessage); + Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); + } + } else if (hasChanged) { + Object component = ComponentUtils.jsonToPaperAdventure(jsonMessage); + Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); } - } catch (IllegalAccessException e) { + } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } } - private void runIfContainsIllegalCharacter(String raw, Consumer callback) { + private IllegalCharacterProcessResult processIllegalCharacters(String raw) { boolean hasIllegal = false; // replace illegal image usage Map tokens = matchTags(raw); @@ -113,11 +119,24 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { hasIllegal = true; } } + if (hasIllegal) { - callback.accept(new String(newCodepoints, 0, newCodepoints.length)); + return IllegalCharacterProcessResult.has(new String(newCodepoints, 0, newCodepoints.length)); } } else if (hasIllegal) { - callback.accept(raw); + return IllegalCharacterProcessResult.has(raw); + } + return IllegalCharacterProcessResult.not(); + } + + public record IllegalCharacterProcessResult(boolean has, String newText) { + + public static IllegalCharacterProcessResult has(String newText) { + return new IllegalCharacterProcessResult(true, newText); + } + + public static IllegalCharacterProcessResult not() { + return new IllegalCharacterProcessResult(false, null); } } } 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 b8ecc62bf..53a6f6e1a 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 @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.font; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.ResourceLocation; @@ -8,6 +9,8 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.util.context.ContextHolder; +import net.momirealms.craftengine.core.util.context.PlayerContext; import org.ahocorasick.trie.Token; import org.ahocorasick.trie.Trie; @@ -28,8 +31,10 @@ public abstract class AbstractFontManager implements FontManager { private final EmojiParser emojiParser; private OffsetFont offsetFont; - private Trie trie; + private Trie imageTagTrie; + private Trie emojiKeywordTrie; private Map tagMapper; + private Map emojiMapper; public AbstractFontManager(CraftEngine plugin) { this.plugin = plugin; @@ -49,15 +54,16 @@ public abstract class AbstractFontManager implements FontManager { this.fonts.clear(); this.images.clear(); this.illegalChars.clear(); + this.emojis.clear(); } @Override public Map matchTags(String json) { - if (this.trie == null) { + if (this.imageTagTrie == null) { return Collections.emptyMap(); } Map tags = new HashMap<>(); - for (Token token : this.trie.tokenize(json)) { + for (Token token : this.imageTagTrie.tokenize(json)) { if (token.isMatch()) { tags.put(token.getFragment(), this.tagMapper.get(token.getFragment())); } @@ -67,11 +73,11 @@ public abstract class AbstractFontManager implements FontManager { @Override public String stripTags(String text) { - if (this.trie == null) { + if (this.imageTagTrie == null) { return text; } StringBuilder builder = new StringBuilder(); - for (Token token : this.trie.tokenize(text)) { + for (Token token : this.imageTagTrie.tokenize(text)) { if (token.isMatch()) { builder.append("*"); } else { @@ -81,6 +87,37 @@ public abstract class AbstractFontManager implements FontManager { return builder.toString(); } + @Override + public String replaceEmoji(String jsonText, Player player) { + if (this.emojiKeywordTrie == null) { + return jsonText; + } + Map emojis = new HashMap<>(); + for (Token token : this.emojiKeywordTrie.tokenize(jsonText)) { + if (token.isMatch()) { + emojis.put(token.getFragment(), this.emojiMapper.get(token.getFragment())); + } + } + if (emojis.isEmpty()) return jsonText; + Component component = AdventureHelper.jsonToComponent(jsonText); + for (Map.Entry entry : emojis.entrySet()) { + Emoji emoji = entry.getValue(); + if (player != null && emoji.permission() != null && !player.hasPermission(emoji.permission())) { + continue; + } + component = component.replaceText(builder -> builder.matchLiteral(entry.getKey()) + .replacement( + AdventureHelper.miniMessage().deserialize( + entry.getValue().content(), + PlayerContext.of(player, ContextHolder.builder() + .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .build()).tagResolvers()) + )); + } + return AdventureHelper.componentToJson(component); + } + @Override public ConfigSectionParser[] parsers() { return new ConfigSectionParser[] {this.imageParser, this.emojiParser}; @@ -89,10 +126,24 @@ public abstract class AbstractFontManager implements FontManager { @Override public void delayedLoad() { Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> this.illegalChars.addAll(font.codepointsInUse())); - this.buildTrie(); + this.buildImageTagTrie(); + this.buildEmojiKeywordsTrie(); } - private void buildTrie() { + private void buildEmojiKeywordsTrie() { + this.emojiMapper = new HashMap<>(); + for (Emoji emoji : this.emojis.values()) { + for (String keyword : emoji.keywords()) { + this.emojiMapper.put(keyword, emoji); + } + } + this.emojiKeywordTrie = Trie.builder() + .ignoreOverlaps() + .addKeywords(this.emojiMapper.keySet()) + .build(); + } + + private void buildImageTagTrie() { this.tagMapper = new HashMap<>(); for (BitmapImage image : this.images.values()) { String id = image.id().toString(); @@ -109,7 +160,7 @@ public abstract class AbstractFontManager implements FontManager { this.tagMapper.put("", AdventureHelper.miniMessage().deserialize(this.offsetFont.createOffset(i, FormatUtils::miniMessageFont))); this.tagMapper.put("\\", Component.text("")); } - this.trie = Trie.builder() + this.imageTagTrie = Trie.builder() .ignoreOverlaps() .addKeywords(this.tagMapper.keySet()) .build(); @@ -180,7 +231,47 @@ public abstract class AbstractFontManager implements FontManager { @Override public void parseSection(Pack pack, Path path, Key id, Map section) { - + if (emojis.containsKey(id)) { + TranslationManager.instance().log("warning.config.emoji.duplicated", path.toString(), id.toString()); + return; + } + String permission = (String) section.get("permission"); + Object keywordsRaw = section.get("keywords"); + if (keywordsRaw == null) { + TranslationManager.instance().log("warning.config.emoji.lack_keywords", path.toString(), id.toString()); + return; + } + List keywords = MiscUtils.getAsStringList(keywordsRaw); + String content = section.getOrDefault("content", "").toString(); + String image = null; + if (section.containsKey("image")) { + String rawImage = section.get("image").toString(); + String[] split = rawImage.split(":"); + if (split.length == 2) { + Key imageId = new Key(split[0], split[1]); + Optional bitmapImage = bitmapImageByImageId(imageId); + if (bitmapImage.isPresent()) { + image = bitmapImage.get().miniMessage(0, 0); + } else { + TranslationManager.instance().log("warning.config.emoji.invalid_image", path.toString(), id.toString(), rawImage); + return; + } + } else if (split.length == 4) { + Key imageId = new Key(split[0], split[1]); + Optional bitmapImage = bitmapImageByImageId(imageId); + if (bitmapImage.isPresent()) { + image = bitmapImage.get().miniMessage(Integer.parseInt(split[2]), Integer.parseInt(split[3])); + } else { + TranslationManager.instance().log("warning.config.emoji.invalid_image", path.toString(), id.toString(), rawImage); + return; + } + } else { + TranslationManager.instance().log("warning.config.emoji.invalid_image", path.toString(), id.toString(), rawImage); + return; + } + } + Emoji emoji = new Emoji(content, permission, image, keywords); + emojis.put(id, emoji); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/Emoji.java b/core/src/main/java/net/momirealms/craftengine/core/font/Emoji.java index 3b76f27f7..81a51429b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/Emoji.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/Emoji.java @@ -1,25 +1,27 @@ package net.momirealms.craftengine.core.font; -import net.momirealms.craftengine.core.util.Key; - import javax.annotation.Nullable; +import java.util.List; public class Emoji { - private final Key font; - private final String image; + private final String content; private final String permission; + private final String image; + private final List keywords; - public Emoji(Key font, String image, String permission) { - this.font = font; + public Emoji(String content, String permission, String image, List keywords) { + this.content = content; this.image = image; this.permission = permission; + this.keywords = keywords; } - public Key font() { - return font; + public String content() { + return content; } - public String image() { + @Nullable + public String emojiImage() { return image; } @@ -27,4 +29,8 @@ public class Emoji { public String permission() { return permission; } + + public List keywords() { + return keywords; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/EmojiParameters.java b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiParameters.java new file mode 100644 index 000000000..83c91536d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiParameters.java @@ -0,0 +1,9 @@ +package net.momirealms.craftengine.core.font; + +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.context.ContextKey; + +public class EmojiParameters { + public static final ContextKey KEYWORD = new ContextKey<>(Key.of("keyword")); + public static final ContextKey EMOJI = new ContextKey<>(Key.of("emoji")); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index 456947d4e..bebf1ea77 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.font; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; import net.momirealms.craftengine.core.util.CharacterUtils; @@ -23,6 +24,8 @@ public interface FontManager extends Manageable { ConfigSectionParser[] parsers(); + String replaceEmoji(String jsonText, Player player); + boolean isDefaultFontInUse(); boolean isIllegalCodepoint(int codepoint); 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 2c77dcbd9..5f7ccfd0a 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 @@ -1,43 +1,20 @@ package net.momirealms.craftengine.core.item; -import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.momirealms.craftengine.core.entity.player.Player; -import net.momirealms.craftengine.core.plugin.text.minimessage.*; import net.momirealms.craftengine.core.util.context.ContextHolder; +import net.momirealms.craftengine.core.util.context.PlayerContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class ItemBuildContext implements MiniMessageTextContext { +public class ItemBuildContext extends PlayerContext { public static final ItemBuildContext EMPTY = new ItemBuildContext(null, ContextHolder.EMPTY); - private final Player player; - private final ContextHolder contexts; - private TagResolver[] tagResolvers; public ItemBuildContext(@Nullable Player player, @NotNull ContextHolder contexts) { - this.player = player; - this.contexts = contexts; + super(player, contexts); } @NotNull public static ItemBuildContext of(@Nullable Player player, @NotNull ContextHolder contexts) { return new ItemBuildContext(player, contexts); } - - @Nullable - public Player player() { - return this.player; - } - - @NotNull - public ContextHolder contexts() { - return this.contexts; - } - - @NotNull - public TagResolver[] tagResolvers() { - if (this.tagResolvers == null) { - this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, new PlaceholderTag(this.player), new I18NTag(this), new NamedArgumentTag(this)}; - } - return this.tagResolvers; - } } 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 c99a83be3..a94426c90 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 @@ -300,6 +300,8 @@ public abstract class AbstractPackManager implements PackManager { plugin.saveResource("resources/default/resourcepack/pack.png"); // templates plugin.saveResource("resources/default/configuration/templates.yml"); + // emoji + plugin.saveResource("resources/default/configuration/emoji.yml"); // i18n plugin.saveResource("resources/default/configuration/i18n.yml"); // block_name diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java index 81460d345..aacfc4f47 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/category/ItemBrowserManagerImpl.java @@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.context.ContextHolder; +import net.momirealms.craftengine.core.util.context.PlayerContext; import java.nio.file.Path; import java.util.*; @@ -175,7 +176,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.BROWSER_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.BROWSER_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -290,7 +291,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.CATEGORY_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.CATEGORY_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -351,7 +352,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_NONE_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_NONE_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -576,7 +577,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_SMITHING_TRANSFORM_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_SMITHING_TRANSFORM_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -709,7 +710,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_STONECUTTING_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_STONECUTTING_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -859,7 +860,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(title, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(title, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } @@ -1054,7 +1055,7 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager { } }) .build() - .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_CRAFTING_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers())) + .title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_CRAFTING_TITLE, PlayerContext.of(player, ContextHolder.EMPTY).tagResolvers())) .refresh() .open(player); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java index e63f82acd..67f2b335f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/text/minimessage/NamedArgumentTag.java @@ -28,15 +28,12 @@ public class NamedArgumentTag implements TagResolver { if (!has(name)) { return null; } - String argumentKey = arguments.popOr("No argument key provided").toString(); - ContextKey key = ContextKey.of(Key.of(argumentKey)); if (!this.context.contexts().has(key)) { throw ctx.newException("Invalid argument key", arguments); } - - return Tag.inserting(AdventureHelper.miniMessage().deserialize(this.context.contexts().getOrThrow(key), this.context.tagResolvers())); + return Tag.selfClosingInserting(AdventureHelper.miniMessage().deserialize(this.context.contexts().getOrThrow(key), this.context.tagResolvers())); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/context/PlayerContext.java b/core/src/main/java/net/momirealms/craftengine/core/util/context/PlayerContext.java new file mode 100644 index 000000000..1104abb65 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/context/PlayerContext.java @@ -0,0 +1,42 @@ +package net.momirealms.craftengine.core.util.context; + +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.text.minimessage.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PlayerContext implements MiniMessageTextContext { + public static final PlayerContext EMPTY = new PlayerContext(null, ContextHolder.EMPTY); + private final Player player; + private final ContextHolder contexts; + private TagResolver[] tagResolvers; + + public PlayerContext(@Nullable Player player, @NotNull ContextHolder contexts) { + this.player = player; + this.contexts = contexts; + } + + @NotNull + public static PlayerContext of(@Nullable Player player, @NotNull ContextHolder contexts) { + return new PlayerContext(player, contexts); + } + + @Nullable + public Player player() { + return this.player; + } + + @NotNull + public ContextHolder contexts() { + return this.contexts; + } + + @NotNull + public TagResolver[] tagResolvers() { + if (this.tagResolvers == null) { + this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, new PlaceholderTag(this.player), new I18NTag(this), new NamedArgumentTag(this)}; + } + return this.tagResolvers; + } +} From fe97006318d473cee10e5eb44c67203183e59be1 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 10 Apr 2025 02:29:24 +0800 Subject: [PATCH 04/28] =?UTF-8?q?=E6=B7=BB=E5=8A=A0emoji=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/font/BukkitFontManager.java | 3 +-- .../core/font/AbstractFontManager.java | 25 +++++++++++++++++-- .../craftengine/core/font/FontManager.java | 4 ++- .../core/util/AdventureHelper.java | 4 +++ 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 28ee0c48a..d193d46aa 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -9,7 +9,6 @@ import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.font.AbstractFontManager; import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.plugin.config.Config; -import net.momirealms.craftengine.core.plugin.text.minimessage.NamedArgumentTag; import net.momirealms.craftengine.core.util.CharacterUtils; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -73,7 +72,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { try { Object originalMessage = Reflections.field$AsyncChatDecorateEvent$originalMessage.get(event); String rawJsonMessage = ComponentUtils.paperAdventureToJson(originalMessage); - String jsonMessage = replaceEmoji(rawJsonMessage, this.plugin.adapt(player)); + String jsonMessage = replaceJsonEmoji(rawJsonMessage, this.plugin.adapt(player)); boolean hasChanged = !rawJsonMessage.equals(jsonMessage); if (!player.hasPermission(FontManager.BYPASS_CHAT)) { IllegalCharacterProcessResult result = processIllegalCharacters(jsonMessage); 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 53a6f6e1a..df2e565b8 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 @@ -88,7 +88,28 @@ public abstract class AbstractFontManager implements FontManager { } @Override - public String replaceEmoji(String jsonText, Player player) { + public String replaceMiniMessageEmoji(String miniMessage, Player player) { + if (this.emojiKeywordTrie == null) { + return miniMessage; + } + for (Token token : this.emojiKeywordTrie.tokenize(miniMessage)) { + if (!token.isMatch()) continue; + Emoji emoji = this.emojiMapper.get(token.getFragment()); + if (emoji == null) continue; + Component content = AdventureHelper.miniMessage().deserialize( + emoji.content(), + PlayerContext.of(player, ContextHolder.builder() + .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .build()).tagResolvers() + ); + miniMessage = miniMessage.replace(token.getFragment(), AdventureHelper.componentToMiniMessage(content)); + } + return miniMessage; + } + + @Override + public String replaceJsonEmoji(String jsonText, Player player) { if (this.emojiKeywordTrie == null) { return jsonText; } @@ -108,7 +129,7 @@ public abstract class AbstractFontManager implements FontManager { component = component.replaceText(builder -> builder.matchLiteral(entry.getKey()) .replacement( AdventureHelper.miniMessage().deserialize( - entry.getValue().content(), + emoji.content(), PlayerContext.of(player, ContextHolder.builder() .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index bebf1ea77..7fd53b4d1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -24,7 +24,9 @@ public interface FontManager extends Manageable { ConfigSectionParser[] parsers(); - String replaceEmoji(String jsonText, Player player); + String replaceMiniMessageEmoji(String miniMessage, Player player); + + String replaceJsonEmoji(String jsonText, Player player); boolean isDefaultFontInUse(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java index 2a365d14d..d69c82d01 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java @@ -126,6 +126,10 @@ public class AdventureHelper { return getInstance().miniMessageStrict.serialize(getInstance().gsonComponentSerializer.deserialize(json)); } + public static String componentToMiniMessage(Component component) { + return getInstance().miniMessageStrict.serialize(component); + } + /** * Converts a JSON string to a Component. * From 9318034e99e5cee0684ced59a641c902a024743b Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Thu, 10 Apr 2025 02:40:33 +0800 Subject: [PATCH 05/28] =?UTF-8?q?fix(network):=20=E6=94=B9=E8=BF=9B?= =?UTF-8?q?=E5=A3=B0=E9=9F=B3=E4=BA=8B=E4=BB=B6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/plugin/network/PacketConsumers.java | 4 +--- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 2fce786b1..6fd9b5a9d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -1620,9 +1620,7 @@ public class PacketConsumers { buf.writeVarInt(event.packetID()); buf.writeVarInt(0); Object newId = KeyUtils.toResourceLocation(mapped); - Object newSoundEvent = VersionHelper.isVersionNewerThan1_21_2() ? - Reflections.constructor$SoundEvent.newInstance(newId, Reflections.field$SoundEvent$fixedRange.get(soundEvent)) : - Reflections.constructor$SoundEvent.newInstance(newId, Reflections.field$SoundEvent$range.get(soundEvent), Reflections.field$SoundEvent$newSystem.get(soundEvent)); + Object newSoundEvent = FastNMS.INSTANCE.constructor$SoundEvent(newId, FastNMS.INSTANCE.method$SoundEvent$fixedRange(soundEvent)); FastNMS.INSTANCE.method$SoundEvent$directEncode(buf, newSoundEvent); buf.writeVarInt(source); buf.writeInt(x); diff --git a/gradle.properties b/gradle.properties index a8e68ad88..57c38701c 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.13 -nms_helper_version=0.52 +nms_helper_version=0.53 # Ignite Dependencies mixinextras_version=0.4.1 mixin_version=0.15.2+mixin.0.8.7 From a39e5980cae5c260808219623fdb1510b7ee6649 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 10 Apr 2025 02:46:37 +0800 Subject: [PATCH 06/28] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=93=81=E7=A0=A7?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/font/BukkitFontManager.java | 67 +++++++++++++++++++ .../core/font/AbstractFontManager.java | 4 +- .../core/util/AdventureHelper.java | 11 +++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index d193d46aa..26d33fc0b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -5,21 +5,34 @@ import io.papermc.paper.event.player.AsyncChatDecorateEvent; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.ComponentUtils; +import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils; import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.font.AbstractFontManager; +import net.momirealms.craftengine.core.font.Emoji; +import net.momirealms.craftengine.core.font.EmojiParameters; import net.momirealms.craftengine.core.font.FontManager; +import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.CharacterUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.util.context.ContextHolder; +import net.momirealms.craftengine.core.util.context.PlayerContext; +import org.ahocorasick.trie.Token; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; +import org.bukkit.event.inventory.PrepareAnvilEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.view.AnvilView; import java.lang.reflect.InvocationTargetException; import java.util.Map; +import java.util.Objects; public class BukkitFontManager extends AbstractFontManager implements Listener { private final BukkitCraftEngine plugin; @@ -65,6 +78,60 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } } + @SuppressWarnings("UnstableApiUsage") + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onAnvilRename(PrepareAnvilEvent event) { + if (super.emojiKeywordTrie == null) { + return; + } + ItemStack result = event.getResult(); + if (result == null) return; + Player player; + try { + player = (Player) Reflections.method$InventoryView$getPlayer.invoke(VersionHelper.isVersionNewerThan1_21() ? event.getView() : LegacyInventoryUtils.getView(event)); + } catch (ReflectiveOperationException e) { + this.plugin.logger().warn("Failed to get inventory viewer", e); + return; + } + + String renameText; + if (VersionHelper.isVersionNewerThan1_21_2()) { + AnvilView anvilView = event.getView(); + renameText = anvilView.getRenameText(); + } else { + renameText = LegacyInventoryUtils.getRenameText(event.getInventory()); + } + + if (renameText == null || renameText.isEmpty()) return; + + Component itemName = Component.text(renameText); + boolean replaced = false; + for (Token token : super.emojiKeywordTrie.tokenize(renameText)) { + if (!token.isMatch()) continue; + Emoji emoji = super.emojiMapper.get(token.getFragment()); + if (emoji == null) continue; + if (emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission()))) { + continue; + } + itemName = itemName.replaceText(builder -> { + builder.matchLiteral(token.getFragment()) + .replacement(AdventureHelper.miniMessage().deserialize( + emoji.content(), + PlayerContext.of(plugin.adapt(player), ContextHolder.builder() + .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .build()).tagResolvers() + )) ; + }); + replaced = true; + } + + if (!replaced) return; + Item wrapped = this.plugin.itemManager().wrap(result); + wrapped.customName(AdventureHelper.componentToJson(itemName)); + event.setResult(wrapped.loadCopy()); + } + @SuppressWarnings("UnstableApiUsage") private void processChatEvent(AsyncChatDecorateEvent event) { Player player = event.player(); 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 df2e565b8..767058d65 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 @@ -32,9 +32,9 @@ public abstract class AbstractFontManager implements FontManager { private OffsetFont offsetFont; private Trie imageTagTrie; - private Trie emojiKeywordTrie; + protected Trie emojiKeywordTrie; private Map tagMapper; - private Map emojiMapper; + protected Map emojiMapper; public AbstractFontManager(CraftEngine plugin) { this.plugin = plugin; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java index d69c82d01..95cff2708 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java @@ -6,6 +6,7 @@ import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.json.JSONOptions; import net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer; @@ -19,12 +20,14 @@ public class AdventureHelper { public static final String EMPTY_COMPONENT = componentToJson(Component.empty()); private final MiniMessage miniMessage; private final MiniMessage miniMessageStrict; + private final MiniMessage miniMessageCustom; private final GsonComponentSerializer gsonComponentSerializer; private final NBTComponentSerializer nbtComponentSerializer; private AdventureHelper() { this.miniMessage = MiniMessage.builder().build(); this.miniMessageStrict = MiniMessage.builder().strict(true).build(); + this.miniMessageCustom = MiniMessage.builder().tags(TagResolver.empty()).build(); GsonComponentSerializer.Builder builder = GsonComponentSerializer.builder(); if (!VersionHelper.isVersionNewerThan1_20_5()) { builder.legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get()); @@ -56,6 +59,14 @@ public class AdventureHelper { return getInstance().miniMessage; } + public static MiniMessage customMiniMessage() { + return getInstance().miniMessageCustom; + } + + public static MiniMessage strictMiniMessage() { + return getInstance().miniMessageStrict; + } + /** * Retrieves the GsonComponentSerializer instance. * From af8d0a6fc3970a1c812f813c38c9d07416441a04 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 10 Apr 2025 03:14:36 +0800 Subject: [PATCH 07/28] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E7=8E=A9=E5=AE=B6=E8=83=BD=E5=90=AC=E5=88=B0hit=E5=A3=B0?= =?UTF-8?q?=E9=9F=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/bukkit/plugin/user/BukkitServerPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 721f4b1c8..d1f9487b3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -425,7 +425,7 @@ public class BukkitServerPlayer extends Player { Object soundType = Reflections.field$BlockBehaviour$soundType.get(blockOwner); Object soundEvent = Reflections.field$SoundType$hitSound.get(soundType); Object soundId = Reflections.field$SoundEvent$location.get(soundEvent); - level().playBlockSound(new Vec3d(this.destroyPos.x(), this.destroyPos.y(), this.destroyPos.z()), Key.of(soundId.toString()), 0.5F, 0.5F); + player.playSound(location, soundId.toString(), SoundCategory.BLOCKS, 0.5F, 0.5F); this.lastHitBlockTime = currentTick; } From 4d947fe6b828932b7e53476c9f36f86d4d2f1969 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Thu, 10 Apr 2025 15:07:48 +0800 Subject: [PATCH 08/28] =?UTF-8?q?feat(emoji):=20=E5=A2=9E=E5=8A=A0=20emoji?= =?UTF-8?q?=20=E8=A7=A3=E6=9E=90=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/loader/src/main/resources/config.yml | 2 + .../bukkit/font/BukkitFontManager.java | 122 ++++++++++++++---- .../bukkit/util/ComponentUtils.java | 22 ++-- .../craftengine/bukkit/util/Reflections.java | 37 ++++++ .../core/plugin/config/Config.java | 6 + gradle.properties | 2 +- 6 files changed, 156 insertions(+), 35 deletions(-) diff --git a/bukkit/loader/src/main/resources/config.yml b/bukkit/loader/src/main/resources/config.yml index 242aba376..5efdfb664 100644 --- a/bukkit/loader/src/main/resources/config.yml +++ b/bukkit/loader/src/main/resources/config.yml @@ -237,6 +237,8 @@ gui: performance: # Maximum chain update depth when fixing client visuals max-block-chain-update-limit: 64 + # Maximum emoji parsed each time + max-emoji-parsed-limit: 100 light-system: enable: true diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 26d33fc0b..8ba4f59fd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.bukkit.font; +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent; import io.papermc.paper.event.player.AsyncChatDecorateEvent; import net.kyori.adventure.text.Component; @@ -25,14 +27,17 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; +import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.inventory.PrepareAnvilEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerEditBookEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.view.AnvilView; import java.lang.reflect.InvocationTargetException; -import java.util.Map; -import java.util.Objects; +import java.util.*; +import java.util.function.Consumer; public class BukkitFontManager extends AbstractFontManager implements Listener { private final BukkitCraftEngine plugin; @@ -105,31 +110,104 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { if (renameText == null || renameText.isEmpty()) return; Component itemName = Component.text(renameText); - boolean replaced = false; - for (Token token : super.emojiKeywordTrie.tokenize(renameText)) { + final int[] parsedCount = {0}; + processComponent(itemName, player, parsedCount[0], (text) -> { + if (parsedCount[0]++ >= Config.maxEmojiParsed()) return; + Item wrapped = this.plugin.itemManager().wrap(result); + wrapped.customName(AdventureHelper.componentToJson(text)); + event.setResult(wrapped.loadCopy()); + }, (count) -> parsedCount[0]++); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerEditBook(PlayerEditBookEvent event) { + if (!event.isSigning()) return; + Player player = event.getPlayer(); + BookMeta newBookMeta = event.getNewBookMeta(); + List pages = newBookMeta.pages(); + final boolean[] replacedBookMeta = {false}; + final int[] parsedCount = {0}; + for (int i = 0; i < pages.size(); i++) { + int finalIndex = i; + JsonElement json = ComponentUtils.paperAdventureToJsonElement(pages.get(i)); + if (json instanceof JsonPrimitive primitive) { + if (primitive.isString() && primitive.getAsString().isEmpty()) continue; + } + Component page = AdventureHelper.jsonElementToComponent(json); + processComponent(page, player, parsedCount[0], (text) -> { + try { + replacedBookMeta[0] = true; + Reflections.method$BookMeta$page.invoke( + newBookMeta, finalIndex + 1, + ComponentUtils.jsonElementToPaperAdventure(AdventureHelper.componentToJsonElement(text)) + ); + } catch (IllegalAccessException | InvocationTargetException e) { + this.plugin.logger().warn("Failed to set book page", e); + } + }, (count) -> parsedCount[0]++); + if (parsedCount[0] > Config.maxEmojiParsed()) break; + } + if (replacedBookMeta[0]) { + event.setNewBookMeta(newBookMeta); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onSignChange(SignChangeEvent event) { + Player player = event.getPlayer(); + List lines = event.lines(); + final int[] parsedCount = {0}; + for (int i = 0; i < lines.size(); i++) { + int finalIndex = i; + JsonElement json = ComponentUtils.paperAdventureToJsonElement(lines.get(i)); + if (json.toString().isEmpty()) continue; + Component line = AdventureHelper.jsonElementToComponent(json); + processComponent(line, player, parsedCount[0], (text) -> { + try { + Reflections.method$SignChangeEvent$line.invoke( + event, finalIndex, + ComponentUtils.jsonElementToPaperAdventure(AdventureHelper.componentToJsonElement(text)) + ); + } catch (IllegalAccessException | InvocationTargetException e) { + plugin.logger().warn("Failed to set sign line", e); + } + }, (count) -> parsedCount[0]++); + if (parsedCount[0] > Config.maxEmojiParsed()) break; + } + } + + private void processComponent(Component text, Player player, int parsedCount, Consumer consumer, Consumer parsedCountConsumer) { + if (parsedCount > Config.maxEmojiParsed()) return; + Component textReplaced = text; + Set processedKeywords = new HashSet<>(); + for (Token token : super.emojiKeywordTrie.tokenize(AdventureHelper.componentToJson(text))) { if (!token.isMatch()) continue; - Emoji emoji = super.emojiMapper.get(token.getFragment()); - if (emoji == null) continue; - if (emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission()))) { + String keyword = token.getFragment(); + parsedCountConsumer.accept(parsedCount++); + if (parsedCount > Config.maxEmojiParsed()) return; + if (processedKeywords.contains(keyword)) continue; + Emoji emoji = super.emojiMapper.get(keyword); + if (emoji == null) { + parsedCountConsumer.accept(parsedCount--); continue; } - itemName = itemName.replaceText(builder -> { - builder.matchLiteral(token.getFragment()) - .replacement(AdventureHelper.miniMessage().deserialize( - emoji.content(), - PlayerContext.of(plugin.adapt(player), ContextHolder.builder() - .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) - .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) - .build()).tagResolvers() - )) ; + if (emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission()))) { + parsedCountConsumer.accept(parsedCount--); + continue; + } + textReplaced = textReplaced.replaceText(builder -> { + builder.matchLiteral(keyword) + .replacement(AdventureHelper.miniMessage().deserialize( + emoji.content(), + PlayerContext.of(plugin.adapt(player), ContextHolder.builder() + .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .build()).tagResolvers() + )); }); - replaced = true; + consumer.accept(textReplaced); + processedKeywords.add(keyword); } - - if (!replaced) return; - Item wrapped = this.plugin.itemManager().wrap(result); - wrapped.customName(AdventureHelper.componentToJson(itemName)); - event.setResult(wrapped.loadCopy()); } @SuppressWarnings("UnstableApiUsage") diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ComponentUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ComponentUtils.java index 426e456ff..b9622ed40 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ComponentUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ComponentUtils.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.util; import com.google.gson.JsonElement; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.bukkit.nms.FastNMS; -import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.AdventureHelper; public class ComponentUtils { @@ -27,19 +26,18 @@ public class ComponentUtils { } public static String paperAdventureToJson(Object component) { - try { - return (String) Reflections.method$ComponentSerializer$serialize.invoke(Reflections.instance$GsonComponentSerializer, component); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to serialize paper adventure component " + component, e); - return AdventureHelper.EMPTY_COMPONENT; - } + return Reflections.instance$GsonComponentSerializer$Gson.toJson(component); } public static Object jsonToPaperAdventure(String json) { - try { - return Reflections.method$ComponentSerializer$deserialize.invoke(Reflections.instance$GsonComponentSerializer, json); - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Failed to deserialize paper component from json", e); - } + return Reflections.instance$GsonComponentSerializer$Gson.fromJson(json, Reflections.clazz$AdventureComponent); + } + + public static JsonElement paperAdventureToJsonElement(Object component) { + return Reflections.instance$GsonComponentSerializer$Gson.toJsonTree(component); + } + + public static Object jsonElementToPaperAdventure(JsonElement json) { + return Reflections.instance$GsonComponentSerializer$Gson.fromJson(json, Reflections.clazz$AdventureComponent); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index 869b18b27..a98610732 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.bukkit.util; import com.google.common.collect.ImmutableList; +import com.google.gson.Gson; import com.google.gson.JsonElement; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; @@ -6314,4 +6315,40 @@ public class Reflections { BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutScoreboardObjective") ) ); + + public static final Class clazz$SignChangeEvent = requireNonNull( + ReflectionUtils.getClazz( + "org.bukkit.event.block.SignChangeEvent" + ) + ); + + public static final Method method$SignChangeEvent$line = requireNonNull( + ReflectionUtils.getMethod(clazz$SignChangeEvent, void.class, int.class, clazz$AdventureComponent) + ); + + public static final Class clazz$BookMeta = requireNonNull( + ReflectionUtils.getClazz( + "org.bukkit.inventory.meta.BookMeta" + ) + ); + + public static final Method method$BookMeta$page = requireNonNull( + ReflectionUtils.getMethod(clazz$BookMeta, void.class, int.class, clazz$AdventureComponent) + ); + + public static final Method method$GsonComponentSerializer$serializer = requireNonNull( + ReflectionUtils.getMethod( + clazz$GsonComponentSerializer, Gson.class + ) + ); + + public static final Gson instance$GsonComponentSerializer$Gson; + + static { + try { + instance$GsonComponentSerializer$Gson = (Gson) Reflections.method$GsonComponentSerializer$serializer.invoke(instance$GsonComponentSerializer); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } } 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 fd197c315..f94baf16f 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 @@ -96,6 +96,7 @@ public class Config { protected UUID resource_pack$external_host$uuid; protected int performance$max_block_chain_update_limit; + protected int performance$max_emoji_parsed_limit; protected boolean light_system$force_update_light; protected boolean light_system$enable; @@ -259,6 +260,7 @@ public class Config { // performance performance$max_block_chain_update_limit = config.getInt("performance.max-block-chain-update-limit", 64); + performance$max_emoji_parsed_limit = config.getInt("performance.max-emoji-parsed-limit", 100); // light light_system$force_update_light = config.getBoolean("light-system.force-update-light", false); @@ -352,6 +354,10 @@ public class Config { return instance.performance$max_block_chain_update_limit; } + public static int maxEmojiParsed() { + return instance.performance$max_emoji_parsed_limit; + } + public static boolean removeInvalidFurniture() { return instance.furniture$remove_invalid_furniture_on_chunk_load$enable; } diff --git a/gradle.properties b/gradle.properties index 57c38701c..b90dd8cd5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] project_version=0.0.46.3 -config_version=23 +config_version=24 lang_version=4 project_group=net.momirealms latest_supported_version=1.21.5 From a904b63c402870a0104d23b366987d3b5a3cafbc Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Thu, 10 Apr 2025 19:13:00 +0800 Subject: [PATCH 09/28] =?UTF-8?q?feat(core):=20=E6=B7=BB=E5=8A=A0=E8=A1=A8?= =?UTF-8?q?=E6=83=85=E7=AC=A6=E5=8F=B7=E8=A1=A5=E5=85=A8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/font/BukkitFontManager.java | 19 +++++++++++++++++++ .../core/font/AbstractFontManager.java | 19 +++++++++++++++++++ .../craftengine/core/font/FontManager.java | 5 +++++ gradle.properties | 4 ++-- 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 8ba4f59fd..0151e87e6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -5,6 +5,7 @@ import com.google.gson.JsonPrimitive; import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent; import io.papermc.paper.event.player.AsyncChatDecorateEvent; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils; @@ -31,6 +32,7 @@ import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.inventory.PrepareAnvilEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerEditBookEvent; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.view.AnvilView; @@ -58,6 +60,23 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { HandlerList.unregisterAll(this); } + @Override + public void delayedLoad() { + Map oldCachedEmojiSuggestions = this.oldCachedEmojiSuggestions(); + super.delayedLoad(); + this.oldCachedEmojiSuggestions.putAll(this.cachedEmojiSuggestions()); + Bukkit.getOnlinePlayers().forEach(player -> { + FastNMS.INSTANCE.method$ChatSuggestions$remove(oldCachedEmojiSuggestions.keySet(), player); + FastNMS.INSTANCE.method$ChatSuggestions$add(this.cachedEmojiSuggestions(), player); + }); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + FastNMS.INSTANCE.method$ChatSuggestions$add(this.cachedEmojiSuggestions(), player); + } + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) @SuppressWarnings("UnstableApiUsage") public void onChat(AsyncChatDecorateEvent event) { 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 767058d65..3bdc5c77d 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 @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.font; +import com.google.common.collect.ImmutableMap; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.pack.LoadingSequence; @@ -35,6 +36,9 @@ public abstract class AbstractFontManager implements FontManager { protected Trie emojiKeywordTrie; private Map tagMapper; protected Map emojiMapper; + // tab补全 + protected final Map cachedEmojiSuggestions = new HashMap<>(); + protected final Map oldCachedEmojiSuggestions = new HashMap<>(); public AbstractFontManager(CraftEngine plugin) { this.plugin = plugin; @@ -55,6 +59,17 @@ public abstract class AbstractFontManager implements FontManager { this.images.clear(); this.illegalChars.clear(); this.emojis.clear(); + this.cachedEmojiSuggestions.clear(); + } + + @Override + public Map cachedEmojiSuggestions() { + return ImmutableMap.copyOf(this.cachedEmojiSuggestions); + } + + @Override + public Map oldCachedEmojiSuggestions() { + return ImmutableMap.copyOf(this.oldCachedEmojiSuggestions); } @Override @@ -146,6 +161,7 @@ public abstract class AbstractFontManager implements FontManager { @Override public void delayedLoad() { + this.oldCachedEmojiSuggestions.clear(); Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> this.illegalChars.addAll(font.codepointsInUse())); this.buildImageTagTrie(); this.buildEmojiKeywordsTrie(); @@ -263,6 +279,9 @@ public abstract class AbstractFontManager implements FontManager { return; } List keywords = MiscUtils.getAsStringList(keywordsRaw); + UUID uuid = UUID.randomUUID(); + String keyword = keywords.get(0); + cachedEmojiSuggestions.put(uuid, keyword); String content = section.getOrDefault("content", "").toString(); String image = null; if (section.containsKey("image")) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index 7fd53b4d1..ca001f040 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -11,6 +11,7 @@ import net.momirealms.craftengine.core.util.Key; import java.util.Collection; import java.util.Map; import java.util.Optional; +import java.util.UUID; public interface FontManager extends Manageable { Key DEFAULT_FONT = Key.of("minecraft:default"); @@ -73,4 +74,8 @@ public interface FontManager extends Manageable { } Map matchTags(String json); + + Map cachedEmojiSuggestions(); + + Map oldCachedEmojiSuggestions(); } diff --git a/gradle.properties b/gradle.properties index b90dd8cd5..c2f32fe2c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -41,7 +41,7 @@ geantyref_version=1.3.16 zstd_version=1.5.7-2 commons_io_version=2.18.0 sparrow_nbt_version=0.6.2 -sparrow_util_version=0.37 +sparrow_util_version=0.38 fastutil_version=8.5.15 netty_version=4.1.119.Final joml_version=1.10.8 @@ -51,7 +51,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.13 -nms_helper_version=0.53 +nms_helper_version=0.54 # Ignite Dependencies mixinextras_version=0.4.1 mixin_version=0.15.2+mixin.0.8.7 From 7963288c1d710c27e90ec126e9e081ddf7c183b1 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Thu, 10 Apr 2025 22:00:53 +0800 Subject: [PATCH 10/28] =?UTF-8?q?refactor(bukkit):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=AD=97=E4=BD=93=E7=AE=A1=E7=90=86=E5=99=A8=E4=B8=AD=E8=A1=A8?= =?UTF-8?q?=E6=83=85=E8=A7=A3=E6=9E=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/font/BukkitFontManager.java | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 0151e87e6..b433afe87 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -129,13 +129,13 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { if (renameText == null || renameText.isEmpty()) return; Component itemName = Component.text(renameText); - final int[] parsedCount = {0}; - processComponent(itemName, player, parsedCount[0], (text) -> { - if (parsedCount[0]++ >= Config.maxEmojiParsed()) return; + MutableInt parsedCount = new MutableInt(0); + processComponent(itemName, player, parsedCount, (text) -> { + if (parsedCount.value >= Config.maxEmojiParsed()) return; Item wrapped = this.plugin.itemManager().wrap(result); wrapped.customName(AdventureHelper.componentToJson(text)); event.setResult(wrapped.loadCopy()); - }, (count) -> parsedCount[0]++); + }); } @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @@ -144,8 +144,8 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { Player player = event.getPlayer(); BookMeta newBookMeta = event.getNewBookMeta(); List pages = newBookMeta.pages(); - final boolean[] replacedBookMeta = {false}; - final int[] parsedCount = {0}; + MutableBoolean replacedBookMeta = new MutableBoolean(false); + MutableInt parsedCount = new MutableInt(0); for (int i = 0; i < pages.size(); i++) { int finalIndex = i; JsonElement json = ComponentUtils.paperAdventureToJsonElement(pages.get(i)); @@ -153,9 +153,9 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { if (primitive.isString() && primitive.getAsString().isEmpty()) continue; } Component page = AdventureHelper.jsonElementToComponent(json); - processComponent(page, player, parsedCount[0], (text) -> { + processComponent(page, player, parsedCount, (text) -> { try { - replacedBookMeta[0] = true; + replacedBookMeta.value = true; Reflections.method$BookMeta$page.invoke( newBookMeta, finalIndex + 1, ComponentUtils.jsonElementToPaperAdventure(AdventureHelper.componentToJsonElement(text)) @@ -163,10 +163,10 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } catch (IllegalAccessException | InvocationTargetException e) { this.plugin.logger().warn("Failed to set book page", e); } - }, (count) -> parsedCount[0]++); - if (parsedCount[0] > Config.maxEmojiParsed()) break; + }); + if (parsedCount.value > Config.maxEmojiParsed()) break; } - if (replacedBookMeta[0]) { + if (replacedBookMeta.value) { event.setNewBookMeta(newBookMeta); } } @@ -175,13 +175,13 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { public void onSignChange(SignChangeEvent event) { Player player = event.getPlayer(); List lines = event.lines(); - final int[] parsedCount = {0}; + MutableInt parsedCount = new MutableInt(0); for (int i = 0; i < lines.size(); i++) { int finalIndex = i; JsonElement json = ComponentUtils.paperAdventureToJsonElement(lines.get(i)); if (json.toString().isEmpty()) continue; Component line = AdventureHelper.jsonElementToComponent(json); - processComponent(line, player, parsedCount[0], (text) -> { + processComponent(line, player, parsedCount, (text) -> { try { Reflections.method$SignChangeEvent$line.invoke( event, finalIndex, @@ -190,28 +190,22 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } catch (IllegalAccessException | InvocationTargetException e) { plugin.logger().warn("Failed to set sign line", e); } - }, (count) -> parsedCount[0]++); - if (parsedCount[0] > Config.maxEmojiParsed()) break; + }); + if (parsedCount.value > Config.maxEmojiParsed()) break; } } - private void processComponent(Component text, Player player, int parsedCount, Consumer consumer, Consumer parsedCountConsumer) { - if (parsedCount > Config.maxEmojiParsed()) return; + private void processComponent(Component text, Player player, MutableInt parsedCount, Consumer consumer) { + if (parsedCount.value > Config.maxEmojiParsed()) return; Component textReplaced = text; Set processedKeywords = new HashSet<>(); for (Token token : super.emojiKeywordTrie.tokenize(AdventureHelper.componentToJson(text))) { - if (!token.isMatch()) continue; + if (!token.isMatch() || parsedCount.value > Config.maxEmojiParsed()) continue; String keyword = token.getFragment(); - parsedCountConsumer.accept(parsedCount++); - if (parsedCount > Config.maxEmojiParsed()) return; if (processedKeywords.contains(keyword)) continue; Emoji emoji = super.emojiMapper.get(keyword); - if (emoji == null) { - parsedCountConsumer.accept(parsedCount--); - continue; - } + if (emoji == null) continue; if (emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission()))) { - parsedCountConsumer.accept(parsedCount--); continue; } textReplaced = textReplaced.replaceText(builder -> { @@ -226,9 +220,20 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { }); consumer.accept(textReplaced); processedKeywords.add(keyword); + parsedCount.value++; } } + private static final class MutableInt { + int value; + MutableInt(int value) { this.value = value; } + } + + private static final class MutableBoolean { + boolean value; + MutableBoolean(boolean value) { this.value = value; } + } + @SuppressWarnings("UnstableApiUsage") private void processChatEvent(AsyncChatDecorateEvent event) { Player player = event.player(); From 5d2698a8343267da8415f5ee08609cdc0c4cc13b Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Thu, 10 Apr 2025 22:03:31 +0800 Subject: [PATCH 11/28] =?UTF-8?q?refactor(bukkit):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=AD=97=E4=BD=93=E7=AE=A1=E7=90=86=E5=99=A8=E4=B8=AD=E8=A1=A8?= =?UTF-8?q?=E6=83=85=E8=A7=A3=E6=9E=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../momirealms/craftengine/bukkit/font/BukkitFontManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index b433afe87..6b3a6a440 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -200,7 +200,8 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { Component textReplaced = text; Set processedKeywords = new HashSet<>(); for (Token token : super.emojiKeywordTrie.tokenize(AdventureHelper.componentToJson(text))) { - if (!token.isMatch() || parsedCount.value > Config.maxEmojiParsed()) continue; + if (!token.isMatch()) continue; + if (parsedCount.value++ > Config.maxEmojiParsed()) return; String keyword = token.getFragment(); if (processedKeywords.contains(keyword)) continue; Emoji emoji = super.emojiMapper.get(keyword); @@ -220,7 +221,6 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { }); consumer.accept(textReplaced); processedKeywords.add(keyword); - parsedCount.value++; } } From 1ed5e3b38a18eaf6c52a4ae67cbb15a841ed39bc Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Fri, 11 Apr 2025 20:21:08 +0800 Subject: [PATCH 12/28] =?UTF-8?q?perf(bukkit):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=A1=A8=E6=83=85=E5=BB=BA=E8=AE=AE=E7=9A=84=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/font/BukkitFontManager.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 6b3a6a440..a06665ba7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -67,14 +67,29 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { this.oldCachedEmojiSuggestions.putAll(this.cachedEmojiSuggestions()); Bukkit.getOnlinePlayers().forEach(player -> { FastNMS.INSTANCE.method$ChatSuggestions$remove(oldCachedEmojiSuggestions.keySet(), player); - FastNMS.INSTANCE.method$ChatSuggestions$add(this.cachedEmojiSuggestions(), player); + this.addEmojiSuggestions(player); }); } @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); - FastNMS.INSTANCE.method$ChatSuggestions$add(this.cachedEmojiSuggestions(), player); + this.addEmojiSuggestions(player); + } + + private void addEmojiSuggestions(Player player) { + Map hasPermissions = new HashMap<>(); + Map cachedEmojiSuggestions = this.cachedEmojiSuggestions(); + for (UUID uuid : cachedEmojiSuggestions.keySet()) { + String keyword = cachedEmojiSuggestions.get(uuid); + Emoji emoji = super.emojiMapper.get(keyword); + if (emoji == null) continue; + if (emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission()))) { + continue; + } + hasPermissions.put(uuid, keyword); + } + FastNMS.INSTANCE.method$ChatSuggestions$add(hasPermissions, player); } @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) From 296ba254ac69b2af5a3bab1d8256f880a7a8dac9 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 11 Apr 2025 20:52:26 +0800 Subject: [PATCH 13/28] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/loader/src/main/resources/config.yml | 6 +- .../bukkit/font/BukkitFontManager.java | 178 +++++++++--------- .../core/font/AbstractFontManager.java | 13 +- .../core/plugin/config/Config.java | 8 +- .../core/util/AdventureHelper.java | 24 +++ 5 files changed, 131 insertions(+), 98 deletions(-) diff --git a/bukkit/loader/src/main/resources/config.yml b/bukkit/loader/src/main/resources/config.yml index 5efdfb664..3198c7265 100644 --- a/bukkit/loader/src/main/resources/config.yml +++ b/bukkit/loader/src/main/resources/config.yml @@ -237,8 +237,8 @@ gui: performance: # Maximum chain update depth when fixing client visuals max-block-chain-update-limit: 64 - # Maximum emoji parsed each time - max-emoji-parsed-limit: 100 + # Maximum number of emojis to parse per operation + max-emojis-per-parse: 16 light-system: enable: true @@ -249,7 +249,7 @@ chunk-system: # 1 = NONE | Compression Speed | Decompress Speed | Compression Ratio | Memory Usage | # 2 = DEFLATE | Medium-Slow Medium Moderate Low | # 3 = GZIP | Medium-Slow Medium Moderate Low | - # 4 = LAZ4 | Blazing-Fast Blazing-Fast Low Low | + # 4 = LZ4 | Blazing-Fast Blazing-Fast Low Low | # 5 = ZSTD | Medium-Fast Fast High Medium | compression-method: 4 # Disabling this option prevents the plugin from converting custom blocks to vanilla states when chunks are unloaded. diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 6b3a6a440..0ffafddc4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.bukkit.font; import com.google.gson.JsonElement; -import com.google.gson.JsonPrimitive; +import com.google.gson.JsonObject; import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent; import io.papermc.paper.event.player.AsyncChatDecorateEvent; import net.kyori.adventure.text.Component; @@ -36,10 +36,10 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.view.AnvilView; +import org.jetbrains.annotations.NotNull; import java.lang.reflect.InvocationTargetException; import java.util.*; -import java.util.function.Consumer; public class BukkitFontManager extends AbstractFontManager implements Listener { private final BukkitCraftEngine plugin; @@ -129,111 +129,67 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { if (renameText == null || renameText.isEmpty()) return; Component itemName = Component.text(renameText); - MutableInt parsedCount = new MutableInt(0); - processComponent(itemName, player, parsedCount, (text) -> { - if (parsedCount.value >= Config.maxEmojiParsed()) return; + EmojiReplaceProcessResult replaceProcessResult = processEmojiNoCopy(player, itemName, renameText); + if (replaceProcessResult.changed()) { Item wrapped = this.plugin.itemManager().wrap(result); - wrapped.customName(AdventureHelper.componentToJson(text)); - event.setResult(wrapped.loadCopy()); - }); + wrapped.customName(AdventureHelper.componentToJson(replaceProcessResult.newText())); + event.setResult(wrapped.load()); + } } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onSignChange(SignChangeEvent event) { + Player player = event.getPlayer(); + List lines = event.lines(); + for (int i = 0; i < lines.size(); i++) { + JsonElement json = ComponentUtils.paperAdventureToJsonElement(lines.get(i)); + if (json == null) continue; + Component line = AdventureHelper.jsonElementToComponent(json); + EmojiReplaceProcessResult result = processEmojiNoCopy(player, line); + if (result.changed()) { + try { + Reflections.method$SignChangeEvent$line.invoke(event, i, ComponentUtils.jsonElementToPaperAdventure(AdventureHelper.componentToJsonElement(result.newText()))); + } catch (IllegalAccessException | InvocationTargetException e) { + plugin.logger().warn("Failed to set sign line", e); + } + } else if (AdventureHelper.isPureTextComponent(line)) { + String plainText = AdventureHelper.plainTextContent(line); + try { + JsonObject jo = new JsonObject(); + jo.addProperty("text", plainText); + Reflections.method$SignChangeEvent$line.invoke(event, i, ComponentUtils.jsonElementToPaperAdventure(jo)); + } catch (IllegalAccessException | InvocationTargetException e) { + plugin.logger().warn("Failed to reset sign line", e); + } + } + } + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onPlayerEditBook(PlayerEditBookEvent event) { if (!event.isSigning()) return; Player player = event.getPlayer(); BookMeta newBookMeta = event.getNewBookMeta(); List pages = newBookMeta.pages(); - MutableBoolean replacedBookMeta = new MutableBoolean(false); - MutableInt parsedCount = new MutableInt(0); + boolean changed = false; for (int i = 0; i < pages.size(); i++) { - int finalIndex = i; JsonElement json = ComponentUtils.paperAdventureToJsonElement(pages.get(i)); - if (json instanceof JsonPrimitive primitive) { - if (primitive.isString() && primitive.getAsString().isEmpty()) continue; - } Component page = AdventureHelper.jsonElementToComponent(json); - processComponent(page, player, parsedCount, (text) -> { + EmojiReplaceProcessResult result = processEmojiNoCopy(player, page); + if (result.changed()) { + changed = true; try { - replacedBookMeta.value = true; - Reflections.method$BookMeta$page.invoke( - newBookMeta, finalIndex + 1, - ComponentUtils.jsonElementToPaperAdventure(AdventureHelper.componentToJsonElement(text)) - ); + Reflections.method$BookMeta$page.invoke(newBookMeta, i + 1, ComponentUtils.jsonElementToPaperAdventure(AdventureHelper.componentToJsonElement(result.newText()))); } catch (IllegalAccessException | InvocationTargetException e) { this.plugin.logger().warn("Failed to set book page", e); } - }); - if (parsedCount.value > Config.maxEmojiParsed()) break; + } } - if (replacedBookMeta.value) { + if (changed) { event.setNewBookMeta(newBookMeta); } } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onSignChange(SignChangeEvent event) { - Player player = event.getPlayer(); - List lines = event.lines(); - MutableInt parsedCount = new MutableInt(0); - for (int i = 0; i < lines.size(); i++) { - int finalIndex = i; - JsonElement json = ComponentUtils.paperAdventureToJsonElement(lines.get(i)); - if (json.toString().isEmpty()) continue; - Component line = AdventureHelper.jsonElementToComponent(json); - processComponent(line, player, parsedCount, (text) -> { - try { - Reflections.method$SignChangeEvent$line.invoke( - event, finalIndex, - ComponentUtils.jsonElementToPaperAdventure(AdventureHelper.componentToJsonElement(text)) - ); - } catch (IllegalAccessException | InvocationTargetException e) { - plugin.logger().warn("Failed to set sign line", e); - } - }); - if (parsedCount.value > Config.maxEmojiParsed()) break; - } - } - - private void processComponent(Component text, Player player, MutableInt parsedCount, Consumer consumer) { - if (parsedCount.value > Config.maxEmojiParsed()) return; - Component textReplaced = text; - Set processedKeywords = new HashSet<>(); - for (Token token : super.emojiKeywordTrie.tokenize(AdventureHelper.componentToJson(text))) { - if (!token.isMatch()) continue; - if (parsedCount.value++ > Config.maxEmojiParsed()) return; - String keyword = token.getFragment(); - if (processedKeywords.contains(keyword)) continue; - Emoji emoji = super.emojiMapper.get(keyword); - if (emoji == null) continue; - if (emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission()))) { - continue; - } - textReplaced = textReplaced.replaceText(builder -> { - builder.matchLiteral(keyword) - .replacement(AdventureHelper.miniMessage().deserialize( - emoji.content(), - PlayerContext.of(plugin.adapt(player), ContextHolder.builder() - .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) - .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) - .build()).tagResolvers() - )); - }); - consumer.accept(textReplaced); - processedKeywords.add(keyword); - } - } - - private static final class MutableInt { - int value; - MutableInt(int value) { this.value = value; } - } - - private static final class MutableBoolean { - boolean value; - MutableBoolean(boolean value) { this.value = value; } - } - @SuppressWarnings("UnstableApiUsage") private void processChatEvent(AsyncChatDecorateEvent event) { Player player = event.player(); @@ -261,6 +217,43 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } } + private EmojiReplaceProcessResult processEmojiNoCopy(Player player, Component text) { + return this.processEmojiNoCopy(player, text, AdventureHelper.plainTextContent(text)); + } + + private EmojiReplaceProcessResult processEmojiNoCopy(Player player, Component text, @NotNull String raw) { + boolean replaced = false; + Set processed = new HashSet<>(); + int times = 0; + for (Token token : super.emojiKeywordTrie.tokenize(raw)) { + if (!token.isMatch()) + continue; + String fragment = token.getFragment(); + if (processed.contains(fragment)) + continue; + Emoji emoji = super.emojiMapper.get(token.getFragment()); + if (emoji == null || (emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission())))) + continue; + text = text.replaceText(builder -> builder.matchLiteral(token.getFragment()) + .replacement(AdventureHelper.miniMessage().deserialize( + emoji.content(), + PlayerContext.of(plugin.adapt(player), + ContextHolder.builder() + .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .build() + ).tagResolvers() + ))); + replaced = true; + processed.add(fragment); + if (++times >= Config.maxEmojisPerParse()) { + break; + } + } + if (!replaced) return EmojiReplaceProcessResult.failed(); + return EmojiReplaceProcessResult.success(text); + } + private IllegalCharacterProcessResult processIllegalCharacters(String raw) { boolean hasIllegal = false; // replace illegal image usage @@ -307,4 +300,15 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { return new IllegalCharacterProcessResult(false, null); } } + + public record EmojiReplaceProcessResult(boolean changed, Component newText) { + + public static EmojiReplaceProcessResult success(Component newText) { + return new EmojiReplaceProcessResult(true, newText); + } + + public static EmojiReplaceProcessResult failed() { + return new EmojiReplaceProcessResult(false, null); + } + } } 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 3bdc5c77d..dc3cd1e69 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 @@ -15,6 +15,7 @@ import net.momirealms.craftengine.core.util.context.PlayerContext; import org.ahocorasick.trie.Token; import org.ahocorasick.trie.Trie; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -30,11 +31,11 @@ public abstract class AbstractFontManager implements FontManager { private final Set illegalChars = new HashSet<>(); private final ImageParser imageParser; private final EmojiParser emojiParser; - private OffsetFont offsetFont; - private Trie imageTagTrie; + + protected Trie imageTagTrie; protected Trie emojiKeywordTrie; - private Map tagMapper; + protected Map tagMapper; protected Map emojiMapper; // tab补全 protected final Map cachedEmojiSuggestions = new HashMap<>(); @@ -279,8 +280,12 @@ public abstract class AbstractFontManager implements FontManager { return; } List keywords = MiscUtils.getAsStringList(keywordsRaw); - UUID uuid = UUID.randomUUID(); + if (keywords.isEmpty()) { + TranslationManager.instance().log("warning.config.emoji.lack_keywords", path.toString(), id.toString()); + return; + } String keyword = keywords.get(0); + UUID uuid = UUID.nameUUIDFromBytes(keyword.getBytes(StandardCharsets.UTF_8)); cachedEmojiSuggestions.put(uuid, keyword); String content = section.getOrDefault("content", "").toString(); String image = null; 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 f94baf16f..175a0c02a 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 @@ -96,7 +96,7 @@ public class Config { protected UUID resource_pack$external_host$uuid; protected int performance$max_block_chain_update_limit; - protected int performance$max_emoji_parsed_limit; + protected int performance$max_emojis_per_parse; protected boolean light_system$force_update_light; protected boolean light_system$enable; @@ -260,7 +260,7 @@ public class Config { // performance performance$max_block_chain_update_limit = config.getInt("performance.max-block-chain-update-limit", 64); - performance$max_emoji_parsed_limit = config.getInt("performance.max-emoji-parsed-limit", 100); + performance$max_emojis_per_parse = config.getInt("performance.max-emojis-per-parse", 32); // light light_system$force_update_light = config.getBoolean("light-system.force-update-light", false); @@ -354,8 +354,8 @@ public class Config { return instance.performance$max_block_chain_update_limit; } - public static int maxEmojiParsed() { - return instance.performance$max_emoji_parsed_limit; + public static int maxEmojisPerParse() { + return instance.performance$max_emojis_per_parse; } public static boolean removeInvalidFurniture() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java index 95cff2708..7d32c3fb6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/AdventureHelper.java @@ -5,6 +5,7 @@ import net.kyori.adventure.audience.Audience; import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; @@ -260,4 +261,27 @@ public class AdventureHelper { } return stringBuilder.toString(); } + + public static String plainTextContent(Component component) { + StringBuilder sb = new StringBuilder(); + if (component instanceof TextComponent textComponent) { + sb.append(textComponent.content()); + } + for (Component child : component.children()) { + sb.append(plainTextContent(child)); + } + return sb.toString(); + } + + public static boolean isPureTextComponent(Component component) { + if (!(component instanceof TextComponent textComponent)) { + return false; + } + for (Component child : textComponent.children()) { + if (!isPureTextComponent(child)) { + return false; + } + } + return true; + } } From 182555b70cba1686ec13efe27049f12ab6020abd Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 11 Apr 2025 20:58:41 +0800 Subject: [PATCH 14/28] Update emoji.yml --- .../main/resources/resources/default/configuration/emoji.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml b/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml index 77f21e382..755db9585 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/emoji.yml @@ -1,8 +1,8 @@ templates: default:emoji/basic: - content: "'>" + content: "'>" default:emoji/addition_info: - content: "'>{text}" + content: "'>{text}" emoji: default:emoji_location: From af7713534cd496f51bf905ef2a8723dfde1565fe Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 11 Apr 2025 22:47:48 +0800 Subject: [PATCH 15/28] =?UTF-8?q?=E5=AE=9E=E9=AA=8C=E6=80=A7=E6=94=B9?= =?UTF-8?q?=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/font/BukkitFontManager.java | 81 +++-------- .../core/font/AbstractFontManager.java | 128 +++++++++++++----- .../font/EmojiComponentProcessResult.java | 14 ++ .../core/font/EmojiTextProcessResult.java | 12 ++ .../craftengine/core/font/FontManager.java | 30 +++- 5 files changed, 168 insertions(+), 97 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/font/EmojiComponentProcessResult.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/font/EmojiTextProcessResult.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 0ffafddc4..3d31a83e4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -11,17 +11,14 @@ import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils; import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.font.AbstractFontManager; -import net.momirealms.craftengine.core.font.Emoji; -import net.momirealms.craftengine.core.font.EmojiParameters; +import net.momirealms.craftengine.core.font.EmojiComponentProcessResult; +import net.momirealms.craftengine.core.font.EmojiTextProcessResult; import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.CharacterUtils; import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.util.context.ContextHolder; -import net.momirealms.craftengine.core.util.context.PlayerContext; -import org.ahocorasick.trie.Token; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -36,10 +33,11 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.view.AnvilView; -import org.jetbrains.annotations.NotNull; import java.lang.reflect.InvocationTargetException; -import java.util.*; +import java.util.List; +import java.util.Map; +import java.util.UUID; public class BukkitFontManager extends AbstractFontManager implements Listener { private final BukkitCraftEngine plugin; @@ -127,9 +125,12 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } if (renameText == null || renameText.isEmpty()) return; - + IllegalCharacterProcessResult processResult = processIllegalCharacters(renameText); + if (processResult.has()) { + renameText = processResult.newText(); + } Component itemName = Component.text(renameText); - EmojiReplaceProcessResult replaceProcessResult = processEmojiNoCopy(player, itemName, renameText); + EmojiComponentProcessResult replaceProcessResult = replaceComponentEmoji(itemName, plugin.adapt(player), renameText); if (replaceProcessResult.changed()) { Item wrapped = this.plugin.itemManager().wrap(result); wrapped.customName(AdventureHelper.componentToJson(replaceProcessResult.newText())); @@ -145,7 +146,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { JsonElement json = ComponentUtils.paperAdventureToJsonElement(lines.get(i)); if (json == null) continue; Component line = AdventureHelper.jsonElementToComponent(json); - EmojiReplaceProcessResult result = processEmojiNoCopy(player, line); + EmojiComponentProcessResult result = replaceComponentEmoji(line, plugin.adapt(player)); if (result.changed()) { try { Reflections.method$SignChangeEvent$line.invoke(event, i, ComponentUtils.jsonElementToPaperAdventure(AdventureHelper.componentToJsonElement(result.newText()))); @@ -175,7 +176,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { for (int i = 0; i < pages.size(); i++) { JsonElement json = ComponentUtils.paperAdventureToJsonElement(pages.get(i)); Component page = AdventureHelper.jsonElementToComponent(json); - EmojiReplaceProcessResult result = processEmojiNoCopy(player, page); + EmojiComponentProcessResult result = replaceComponentEmoji(page, plugin.adapt(player)); if (result.changed()) { changed = true; try { @@ -197,19 +198,19 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { try { Object originalMessage = Reflections.field$AsyncChatDecorateEvent$originalMessage.get(event); String rawJsonMessage = ComponentUtils.paperAdventureToJson(originalMessage); - String jsonMessage = replaceJsonEmoji(rawJsonMessage, this.plugin.adapt(player)); - boolean hasChanged = !rawJsonMessage.equals(jsonMessage); + EmojiTextProcessResult processResult = replaceJsonEmoji(rawJsonMessage, this.plugin.adapt(player)); + boolean hasChanged = processResult.replaced(); if (!player.hasPermission(FontManager.BYPASS_CHAT)) { - IllegalCharacterProcessResult result = processIllegalCharacters(jsonMessage); + IllegalCharacterProcessResult result = processIllegalCharacters(processResult.text()); if (result.has()) { Object component = ComponentUtils.jsonToPaperAdventure(result.newText()); Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); } else if (hasChanged) { - Object component = ComponentUtils.jsonToPaperAdventure(jsonMessage); + Object component = ComponentUtils.jsonToPaperAdventure(processResult.text()); Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); } } else if (hasChanged) { - Object component = ComponentUtils.jsonToPaperAdventure(jsonMessage); + Object component = ComponentUtils.jsonToPaperAdventure(processResult.text()); Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); } } catch (IllegalAccessException | InvocationTargetException e) { @@ -217,43 +218,6 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } } - private EmojiReplaceProcessResult processEmojiNoCopy(Player player, Component text) { - return this.processEmojiNoCopy(player, text, AdventureHelper.plainTextContent(text)); - } - - private EmojiReplaceProcessResult processEmojiNoCopy(Player player, Component text, @NotNull String raw) { - boolean replaced = false; - Set processed = new HashSet<>(); - int times = 0; - for (Token token : super.emojiKeywordTrie.tokenize(raw)) { - if (!token.isMatch()) - continue; - String fragment = token.getFragment(); - if (processed.contains(fragment)) - continue; - Emoji emoji = super.emojiMapper.get(token.getFragment()); - if (emoji == null || (emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission())))) - continue; - text = text.replaceText(builder -> builder.matchLiteral(token.getFragment()) - .replacement(AdventureHelper.miniMessage().deserialize( - emoji.content(), - PlayerContext.of(plugin.adapt(player), - ContextHolder.builder() - .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) - .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) - .build() - ).tagResolvers() - ))); - replaced = true; - processed.add(fragment); - if (++times >= Config.maxEmojisPerParse()) { - break; - } - } - if (!replaced) return EmojiReplaceProcessResult.failed(); - return EmojiReplaceProcessResult.success(text); - } - private IllegalCharacterProcessResult processIllegalCharacters(String raw) { boolean hasIllegal = false; // replace illegal image usage @@ -300,15 +264,4 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { return new IllegalCharacterProcessResult(false, null); } } - - public record EmojiReplaceProcessResult(boolean changed, Component newText) { - - public static EmojiReplaceProcessResult success(Component newText) { - return new EmojiReplaceProcessResult(true, newText); - } - - public static EmojiReplaceProcessResult failed() { - return new EmojiReplaceProcessResult(false, null); - } - } } 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 dc3cd1e69..a98476665 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 @@ -14,11 +14,15 @@ import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.util.context.PlayerContext; import org.ahocorasick.trie.Token; import org.ahocorasick.trie.Trie; +import org.jetbrains.annotations.NotNull; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; public abstract class AbstractFontManager implements FontManager { private final CraftEngine plugin; @@ -104,14 +108,20 @@ public abstract class AbstractFontManager implements FontManager { } @Override - public String replaceMiniMessageEmoji(String miniMessage, Player player) { - if (this.emojiKeywordTrie == null) { - return miniMessage; + public EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, Player player, int maxTimes) { + if (this.emojiKeywordTrie == null || maxTimes <= 0) { + return EmojiTextProcessResult.notReplaced(miniMessage); } + Map replacements = new HashMap<>(); for (Token token : this.emojiKeywordTrie.tokenize(miniMessage)) { - if (!token.isMatch()) continue; - Emoji emoji = this.emojiMapper.get(token.getFragment()); - if (emoji == null) continue; + if (!token.isMatch()) + continue; + String fragment = token.getFragment(); + if (replacements.containsKey(fragment)) + continue; + Emoji emoji = this.emojiMapper.get(fragment); + if (emoji == null || (player != null && emoji.permission() != null && !player.hasPermission(emoji.permission()))) + continue; Component content = AdventureHelper.miniMessage().deserialize( emoji.content(), PlayerContext.of(player, ContextHolder.builder() @@ -119,40 +129,96 @@ public abstract class AbstractFontManager implements FontManager { .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) .build()).tagResolvers() ); - miniMessage = miniMessage.replace(token.getFragment(), AdventureHelper.componentToMiniMessage(content)); + replacements.put(fragment, AdventureHelper.componentToMiniMessage(content)); } - return miniMessage; + if (replacements.isEmpty()) return EmojiTextProcessResult.notReplaced(miniMessage); + String regex = replacements.keySet().stream() + .map(Pattern::quote) + .collect(Collectors.joining("|")); + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(miniMessage); + StringBuilder sb = new StringBuilder(); + int count = 0; + while (matcher.find() && count < maxTimes) { + String key = matcher.group(); + String replacement = replacements.get(key); + if (replacement != null) { + matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement)); + count++; + } else { + // should not reach this + matcher.appendReplacement(sb, Matcher.quoteReplacement(matcher.group())); + } + } + matcher.appendTail(sb); + return EmojiTextProcessResult.replaced(sb.toString()); } @Override - public String replaceJsonEmoji(String jsonText, Player player) { + public EmojiTextProcessResult replaceJsonEmoji(@NotNull String jsonText, Player player, int maxTimes) { if (this.emojiKeywordTrie == null) { - return jsonText; + return EmojiTextProcessResult.notReplaced(jsonText); } - Map emojis = new HashMap<>(); + Map emojis = new HashMap<>(); for (Token token : this.emojiKeywordTrie.tokenize(jsonText)) { - if (token.isMatch()) { - emojis.put(token.getFragment(), this.emojiMapper.get(token.getFragment())); - } - } - if (emojis.isEmpty()) return jsonText; - Component component = AdventureHelper.jsonToComponent(jsonText); - for (Map.Entry entry : emojis.entrySet()) { - Emoji emoji = entry.getValue(); - if (player != null && emoji.permission() != null && !player.hasPermission(emoji.permission())) { + if (!token.isMatch()) continue; - } - component = component.replaceText(builder -> builder.matchLiteral(entry.getKey()) - .replacement( - AdventureHelper.miniMessage().deserialize( - emoji.content(), - PlayerContext.of(player, ContextHolder.builder() - .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) - .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) - .build()).tagResolvers()) - )); + String fragment = token.getFragment(); + if (emojis.containsKey(fragment)) continue; + Emoji emoji = this.emojiMapper.get(fragment); + if (emoji == null || (player != null && emoji.permission() != null && !player.hasPermission(emoji.permission()))) + continue; + emojis.put(fragment, AdventureHelper.miniMessage().deserialize( + emoji.content(), + PlayerContext.of(player, ContextHolder.builder() + .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .build()).tagResolvers()) + ); + if (emojis.size() >= maxTimes) break; } - return AdventureHelper.componentToJson(component); + if (emojis.isEmpty()) return EmojiTextProcessResult.notReplaced(jsonText); + Component component = AdventureHelper.jsonToComponent(jsonText); + String patternString = emojis.keySet().stream() + .map(Pattern::quote) + .collect(Collectors.joining("|")); + component = component.replaceText(builder -> builder.times(maxTimes) + .match(Pattern.compile(patternString)) + .replacement((result, b) -> emojis.get(result.group()))); + return EmojiTextProcessResult.replaced(AdventureHelper.componentToJson(component)); + } + + @Override + public EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, Player player, @NotNull String raw, int maxTimes) { + Map emojis = new HashMap<>(); + for (Token token : this.emojiKeywordTrie.tokenize(raw)) { + if (!token.isMatch()) + continue; + String fragment = token.getFragment(); + if (emojis.containsKey(fragment)) + continue; + Emoji emoji = this.emojiMapper.get(token.getFragment()); + if (emoji == null || (player != null && emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission())))) + continue; + emojis.put(fragment, AdventureHelper.miniMessage().deserialize( + emoji.content(), + PlayerContext.of(player, + ContextHolder.builder() + .withOptionalParameter(EmojiParameters.EMOJI, emoji.emojiImage()) + .withParameter(EmojiParameters.KEYWORD, emoji.keywords().get(0)) + .build() + ).tagResolvers() + )); + if (emojis.size() >= maxTimes) break; + } + if (emojis.isEmpty()) return EmojiComponentProcessResult.failed(); + String patternString = emojis.keySet().stream() + .map(Pattern::quote) + .collect(Collectors.joining("|")); + text = text.replaceText(builder -> builder.times(maxTimes) + .match(Pattern.compile(patternString)) + .replacement((result, b) -> emojis.get(result.group()))); + return EmojiComponentProcessResult.success(text); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/EmojiComponentProcessResult.java b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiComponentProcessResult.java new file mode 100644 index 000000000..dbffb0998 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiComponentProcessResult.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.core.font; + +import net.kyori.adventure.text.Component; + +public record EmojiComponentProcessResult(boolean changed, Component newText) { + + public static EmojiComponentProcessResult success(Component newText) { + return new EmojiComponentProcessResult(true, newText); + } + + public static EmojiComponentProcessResult failed() { + return new EmojiComponentProcessResult(false, null); + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/EmojiTextProcessResult.java b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiTextProcessResult.java new file mode 100644 index 000000000..8813cbdf0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/font/EmojiTextProcessResult.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.core.font; + +public record EmojiTextProcessResult(boolean replaced, String text) { + + public static EmojiTextProcessResult replaced(String text) { + return new EmojiTextProcessResult(true, text); + } + + public static EmojiTextProcessResult notReplaced(String text) { + return new EmojiTextProcessResult(false, text); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index ca001f040..62f4eafe6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -3,10 +3,14 @@ package net.momirealms.craftengine.core.font; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.Manageable; +import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser; +import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.CharacterUtils; import net.momirealms.craftengine.core.util.FormatUtils; import net.momirealms.craftengine.core.util.Key; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Map; @@ -23,11 +27,33 @@ public interface FontManager extends Manageable { String stripTags(String text); + default EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player) { + return replaceComponentEmoji(text, player, Config.maxEmojisPerParse()); + } + + default EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player, int maxTimes) { + return replaceComponentEmoji(text, player, AdventureHelper.plainTextContent(text), maxTimes); + } + + default EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player, String raw) { + return replaceComponentEmoji(text, player, raw, Config.maxEmojisPerParse()); + } + + EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player, @NotNull String raw, int maxTimes); + ConfigSectionParser[] parsers(); - String replaceMiniMessageEmoji(String miniMessage, Player player); + default EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, @Nullable Player player) { + return replaceMiniMessageEmoji(miniMessage, player, Config.maxEmojisPerParse()); + } - String replaceJsonEmoji(String jsonText, Player player); + EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, @Nullable Player player, int maxTimes); + + default EmojiTextProcessResult replaceJsonEmoji(@NotNull String json, @Nullable Player player) { + return replaceJsonEmoji(json, player, Config.maxEmojisPerParse()); + } + + EmojiTextProcessResult replaceJsonEmoji(@NotNull String jsonText, @Nullable Player player, int maxTimes); boolean isDefaultFontInUse(); From b1f63d8a316ec72f51abe3b209c6d16972979ad1 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 11 Apr 2025 23:03:53 +0800 Subject: [PATCH 16/28] =?UTF-8?q?=E5=86=8D=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/font/BukkitFontManager.java | 60 +------------------ .../plugin/network/PacketConsumers.java | 49 +++++---------- .../core/font/AbstractFontManager.java | 53 +++++++++++----- .../craftengine/core/font/FontManager.java | 8 ++- .../font/IllegalCharacterProcessResult.java | 12 ++++ 5 files changed, 73 insertions(+), 109 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/font/IllegalCharacterProcessResult.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 3d31a83e4..676e8909f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -10,10 +10,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils; import net.momirealms.craftengine.bukkit.util.Reflections; -import net.momirealms.craftengine.core.font.AbstractFontManager; -import net.momirealms.craftengine.core.font.EmojiComponentProcessResult; -import net.momirealms.craftengine.core.font.EmojiTextProcessResult; -import net.momirealms.craftengine.core.font.FontManager; +import net.momirealms.craftengine.core.font.*; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.AdventureHelper; @@ -95,7 +92,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { if (!event.getPlayer().hasPermission(FontManager.BYPASS_COMMAND)) { IllegalCharacterProcessResult result = processIllegalCharacters(event.getMessage()); if (result.has()) { - event.setMessage(result.newText()); + event.setMessage(result.text()); } } } @@ -125,10 +122,6 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } if (renameText == null || renameText.isEmpty()) return; - IllegalCharacterProcessResult processResult = processIllegalCharacters(renameText); - if (processResult.has()) { - renameText = processResult.newText(); - } Component itemName = Component.text(renameText); EmojiComponentProcessResult replaceProcessResult = replaceComponentEmoji(itemName, plugin.adapt(player), renameText); if (replaceProcessResult.changed()) { @@ -203,7 +196,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { if (!player.hasPermission(FontManager.BYPASS_CHAT)) { IllegalCharacterProcessResult result = processIllegalCharacters(processResult.text()); if (result.has()) { - Object component = ComponentUtils.jsonToPaperAdventure(result.newText()); + Object component = ComponentUtils.jsonToPaperAdventure(result.text()); Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component); } else if (hasChanged) { Object component = ComponentUtils.jsonToPaperAdventure(processResult.text()); @@ -217,51 +210,4 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { throw new RuntimeException(e); } } - - private IllegalCharacterProcessResult processIllegalCharacters(String raw) { - boolean hasIllegal = false; - // replace illegal image usage - Map tokens = matchTags(raw); - if (!tokens.isEmpty()) { - for (Map.Entry entry : tokens.entrySet()) { - raw = raw.replace(entry.getKey(), "*"); - hasIllegal = true; - } - } - - if (this.isDefaultFontInUse()) { - // replace illegal codepoint - char[] chars = raw.toCharArray(); - int[] codepoints = CharacterUtils.charsToCodePoints(chars); - int[] newCodepoints = new int[codepoints.length]; - - for (int i = 0; i < codepoints.length; i++) { - int codepoint = codepoints[i]; - if (!isIllegalCodepoint(codepoint)) { - newCodepoints[i] = codepoint; - } else { - newCodepoints[i] = '*'; - hasIllegal = true; - } - } - - if (hasIllegal) { - return IllegalCharacterProcessResult.has(new String(newCodepoints, 0, newCodepoints.length)); - } - } else if (hasIllegal) { - return IllegalCharacterProcessResult.has(raw); - } - return IllegalCharacterProcessResult.not(); - } - - public record IllegalCharacterProcessResult(boolean has, String newText) { - - public static IllegalCharacterProcessResult has(String newText) { - return new IllegalCharacterProcessResult(true, newText); - } - - public static IllegalCharacterProcessResult not() { - return new IllegalCharacterProcessResult(false, null); - } - } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 6fd9b5a9d..85708dc2a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -20,6 +20,7 @@ import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.font.FontManager; +import net.momirealms.craftengine.core.font.IllegalCharacterProcessResult; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.network.ConnectionState; @@ -1640,21 +1641,21 @@ public class PacketConsumers { public static final TriConsumer RENAME_ITEM = (user, event, packet) -> { try { if (!Config.filterAnvil()) return; + if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_ANVIL)) { + return; + } String message = (String) Reflections.field$ServerboundRenameItemPacket$name.get(packet); if (message != null && !message.isEmpty()) { - FontManager manager = CraftEngine.instance().imageManager(); - if (!manager.isDefaultFontInUse()) return; // check bypass - if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_ANVIL)) { - return; - } - runIfContainsIllegalCharacter(message, manager, (s) -> { + FontManager manager = CraftEngine.instance().imageManager(); + IllegalCharacterProcessResult result = manager.processIllegalCharacters(message); + if (result.has()) { try { - Reflections.field$ServerboundRenameItemPacket$name.set(packet, s); + Reflections.field$ServerboundRenameItemPacket$name.set(packet, result.text()); } catch (ReflectiveOperationException e) { CraftEngine.instance().logger().warn("Failed to replace chat", e); } - }); + } } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ServerboundRenameItemPacket", e); @@ -1665,20 +1666,19 @@ public class PacketConsumers { public static final TriConsumer SIGN_UPDATE = (user, event, packet) -> { try { if (!Config.filterSign()) return; - String[] lines = (String[]) Reflections.field$ServerboundSignUpdatePacket$lines.get(packet); - FontManager manager = CraftEngine.instance().imageManager(); - if (!manager.isDefaultFontInUse()) return; // check bypass if (((BukkitServerPlayer) user).hasPermission(FontManager.BYPASS_SIGN)) { return; } + String[] lines = (String[]) Reflections.field$ServerboundSignUpdatePacket$lines.get(packet); + FontManager manager = CraftEngine.instance().imageManager(); + if (!manager.isDefaultFontInUse()) return; for (int i = 0; i < lines.length; i++) { String line = lines[i]; if (line != null && !line.isEmpty()) { - try { - int lineIndex = i; - runIfContainsIllegalCharacter(line, manager, (s) -> lines[lineIndex] = s); - } catch (Exception ignore) { + IllegalCharacterProcessResult result = manager.processIllegalCharacters(line); + if (result.has()) { + lines[i] = result.text(); } } } @@ -1763,25 +1763,6 @@ public class PacketConsumers { return hasIllegal ? Pair.of(true, new String(newCodepoints, 0, newCodepoints.length)) : Pair.of(false, original); } - private static void runIfContainsIllegalCharacter(String string, FontManager manager, Consumer callback) { - if (string.isEmpty()) return; - int[] codepoints = CharacterUtils.charsToCodePoints(string.toCharArray()); - int[] newCodepoints = new int[codepoints.length]; - boolean hasIllegal = false; - for (int i = 0; i < codepoints.length; i++) { - int codepoint = codepoints[i]; - if (!manager.isIllegalCodepoint(codepoint)) { - newCodepoints[i] = codepoint; - } else { - newCodepoints[i] = '*'; - hasIllegal = true; - } - } - if (hasIllegal) { - callback.accept(new String(newCodepoints, 0, newCodepoints.length)); - } - } - public static final TriConsumer CUSTOM_PAYLOAD = (user, event, packet) -> { try { if (!VersionHelper.isVersionNewerThan1_20_5()) return; 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 a98476665..d2dd94945 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 @@ -91,22 +91,6 @@ public abstract class AbstractFontManager implements FontManager { return tags; } - @Override - public String stripTags(String text) { - if (this.imageTagTrie == null) { - return text; - } - StringBuilder builder = new StringBuilder(); - for (Token token : this.imageTagTrie.tokenize(text)) { - if (token.isMatch()) { - builder.append("*"); - } else { - builder.append(token.getFragment()); - } - } - return builder.toString(); - } - @Override public EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, Player player, int maxTimes) { if (this.emojiKeywordTrie == null || maxTimes <= 0) { @@ -221,6 +205,43 @@ public abstract class AbstractFontManager implements FontManager { return EmojiComponentProcessResult.success(text); } + @Override + public IllegalCharacterProcessResult processIllegalCharacters(String raw, char replacement) { + boolean hasIllegal = false; + // replace illegal image usage + Map tokens = matchTags(raw); + if (!tokens.isEmpty()) { + for (Map.Entry entry : tokens.entrySet()) { + raw = raw.replace(entry.getKey(), String.valueOf(replacement)); + hasIllegal = true; + } + } + + if (this.isDefaultFontInUse()) { + // replace illegal codepoint + char[] chars = raw.toCharArray(); + int[] codepoints = CharacterUtils.charsToCodePoints(chars); + int[] newCodepoints = new int[codepoints.length]; + + for (int i = 0; i < codepoints.length; i++) { + int codepoint = codepoints[i]; + if (!isIllegalCodepoint(codepoint)) { + newCodepoints[i] = codepoint; + } else { + newCodepoints[i] = replacement; + hasIllegal = true; + } + } + + if (hasIllegal) { + return IllegalCharacterProcessResult.has(new String(newCodepoints, 0, newCodepoints.length)); + } + } else if (hasIllegal) { + return IllegalCharacterProcessResult.has(raw); + } + return IllegalCharacterProcessResult.not(); + } + @Override public ConfigSectionParser[] parsers() { return new ConfigSectionParser[] {this.imageParser, this.emojiParser}; diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index 62f4eafe6..8da06423e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -25,8 +25,6 @@ public interface FontManager extends Manageable { String BYPASS_COMMAND = "craftengine.filter.bypass.command"; String BYPASS_ANVIL = "craftengine.filter.bypass.anvil"; - String stripTags(String text); - default EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player) { return replaceComponentEmoji(text, player, Config.maxEmojisPerParse()); } @@ -41,6 +39,12 @@ public interface FontManager extends Manageable { EmojiComponentProcessResult replaceComponentEmoji(@NotNull Component text, @Nullable Player player, @NotNull String raw, int maxTimes); + default IllegalCharacterProcessResult processIllegalCharacters(String raw) { + return processIllegalCharacters(raw, '*'); + } + + IllegalCharacterProcessResult processIllegalCharacters(String raw, char replacement); + ConfigSectionParser[] parsers(); default EmojiTextProcessResult replaceMiniMessageEmoji(@NotNull String miniMessage, @Nullable Player player) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/IllegalCharacterProcessResult.java b/core/src/main/java/net/momirealms/craftengine/core/font/IllegalCharacterProcessResult.java new file mode 100644 index 000000000..f2568de6e --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/font/IllegalCharacterProcessResult.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.core.font; + +public record IllegalCharacterProcessResult(boolean has, String text) { + + public static IllegalCharacterProcessResult has(String text) { + return new IllegalCharacterProcessResult(true, text); + } + + public static IllegalCharacterProcessResult not() { + return new IllegalCharacterProcessResult(false, ""); + } +} \ No newline at end of file From 727ffca8144eb3a0fd66a20061ecb27e5e8c795d Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 12 Apr 2025 00:24:17 +0800 Subject: [PATCH 17/28] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E6=B7=BB=E5=8A=A0playe?= =?UTF-8?q?rinfo=E6=89=80=E9=9C=80=E7=9A=84=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/loader/src/main/resources/config.yml | 12 +++++++----- .../craftengine/bukkit/plugin/network/PacketIds.java | 2 ++ .../bukkit/plugin/network/impl/PacketIds1_20.java | 5 +++++ .../bukkit/plugin/network/impl/PacketIds1_20_5.java | 5 +++++ .../craftengine/bukkit/util/Reflections.java | 6 ++++++ .../craftengine/core/plugin/config/Config.java | 2 ++ 6 files changed, 27 insertions(+), 5 deletions(-) diff --git a/bukkit/loader/src/main/resources/config.yml b/bukkit/loader/src/main/resources/config.yml index 3198c7265..7ba3c04e5 100644 --- a/bukkit/loader/src/main/resources/config.yml +++ b/bukkit/loader/src/main/resources/config.yml @@ -153,18 +153,20 @@ image: command: true sign: true # By intercepting packets, you are allowed to use in other plugins + # Turning off some unused options would help reduce CPU usage on async threads intercept-packets: system-chat: true - tab-list: true + tab-list: true # Tab list header and footer + player-info: true # User list in tab actionbar: true title: true bossbar: true - container: true - team: true + container: true # GUI + team: true # Team prefix, suffix and display name scoreboard: true entity-name: false - armor-stand: true - text-display: true + armor-stand: true # Legacy Holograms + text-display: true # Modern Holograms emoji: {} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java index 060d79728..978507531 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketIds.java @@ -37,4 +37,6 @@ public interface PacketIds { int clientboundSetObjectivePacket(); int clientboundLevelChunkWithLightPacket(); + + int clientboundPlayerInfoUpdatePacket(); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20.java index bbf6bfbaf..4342d7c18 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20.java @@ -94,4 +94,9 @@ public class PacketIds1_20 implements PacketIds { public int clientboundLevelChunkWithLightPacket() { return PacketIdFinder.clientboundByClazz(Reflections.clazz$ClientboundLevelChunkWithLightPacket); } + + @Override + public int clientboundPlayerInfoUpdatePacket() { + return PacketIdFinder.clientboundByClazz(Reflections.clazz$clientboundplayer); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20_5.java index 8acbd3d52..66d8e2319 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20_5.java @@ -93,4 +93,9 @@ public class PacketIds1_20_5 implements PacketIds { public int clientboundLevelChunkWithLightPacket() { return PacketIdFinder.clientboundByName("minecraft:level_chunk_with_light"); } + + @Override + public int clientboundPlayerInfoUpdatePacket() { + return PacketIdFinder.clientboundByName("minecraft:player_info_update"); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index a98610732..ba09578a0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -1745,6 +1745,12 @@ public class Reflections { ) ); + public static final Class clazz$ClientboundPlayerInfoUpdatePacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundPlayerInfoUpdatePacket") + ) + ); + public static final Field field$ClientboundLevelChunkWithLightPacket$chunkData = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ClientboundLevelChunkWithLightPacket, clazz$ClientboundLevelChunkPacketData, 0 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 175a0c02a..409acc738 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 @@ -134,6 +134,7 @@ public class Config { protected boolean image$intercept_packets$entity_name; protected boolean image$intercept_packets$text_display; protected boolean image$intercept_packets$armor_stand; + protected boolean image$intercept_packets$player_info; public Config(CraftEngine plugin) { this.plugin = plugin; @@ -302,6 +303,7 @@ public class Config { image$intercept_packets$entity_name = config.getBoolean("image.intercept-packets.entity-name", false); image$intercept_packets$text_display = config.getBoolean("image.intercept-packets.text-display", true); image$intercept_packets$armor_stand = config.getBoolean("image.intercept-packets.armor-stand", true); + image$intercept_packets$player_info = config.getBoolean("image.intercept-packets.player-info", true); Class modClazz = ReflectionUtils.getClazz(CraftEngine.MOD_CLASS); if (modClazz != null) { From ba2191b6ceb01e0dea7181869d7e957430af9f15 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 12 Apr 2025 00:27:28 +0800 Subject: [PATCH 18/28] Update PacketIds1_20.java --- .../craftengine/bukkit/plugin/network/impl/PacketIds1_20.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20.java index 4342d7c18..f7632f51d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20.java @@ -97,6 +97,6 @@ public class PacketIds1_20 implements PacketIds { @Override public int clientboundPlayerInfoUpdatePacket() { - return PacketIdFinder.clientboundByClazz(Reflections.clazz$clientboundplayer); + return PacketIdFinder.clientboundByClazz(Reflections.clazz$ClientboundPlayerInfoUpdatePacket); } } From 65f704dc1dae6380ee7d61e1f5f024ce85289613 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Sat, 12 Apr 2025 00:46:46 +0800 Subject: [PATCH 19/28] =?UTF-8?q?refactor(nms):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/block/BlockEventListener.java | 8 +- .../bukkit/block/BukkitBlockManager.java | 7 +- .../block/behavior/LeavesBlockBehavior.java | 2 +- .../behavior/SugarCaneBlockBehavior.java | 2 +- .../bukkit/font/BukkitFontManager.java | 21 ++-- .../item/recipe/BukkitRecipeManager.java | 2 +- .../plugin/network/PacketConsumers.java | 9 +- .../plugin/user/BukkitServerPlayer.java | 2 +- .../bukkit/util/BlockStateUtils.java | 2 +- .../craftengine/bukkit/util/Reflections.java | 98 ++++++++++++------- .../core/font/AbstractFontManager.java | 15 ++- .../craftengine/core/font/FontManager.java | 9 +- gradle.properties | 2 +- 13 files changed, 102 insertions(+), 77 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java index 88c6c4617..f8879f586 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BlockEventListener.java @@ -70,7 +70,7 @@ public class BlockEventListener implements Listener { try { Object soundType = Reflections.field$BlockBehaviour$soundType.get(ownerBlock); Object placeSound = Reflections.field$SoundType$placeSound.get(soundType); - player.playSound(block.getLocation(), Reflections.field$SoundEvent$location.get(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); + player.playSound(block.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); } catch (ReflectiveOperationException e) { this.plugin.logger().warn("Failed to get sound type", e); } @@ -87,7 +87,7 @@ public class BlockEventListener implements Listener { Object ownerBlock = BlockStateUtils.getBlockOwner(blockState); Object soundType = Reflections.field$BlockBehaviour$soundType.get(ownerBlock); Object placeSound = Reflections.field$SoundType$placeSound.get(soundType); - player.playSound(block.getLocation(), Reflections.field$SoundEvent$location.get(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); + player.playSound(block.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(placeSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); } catch (ReflectiveOperationException e) { this.plugin.logger().warn("Failed to get sound type", e); } @@ -179,7 +179,7 @@ public class BlockEventListener implements Listener { try { Object soundType = Reflections.field$BlockBehaviour$soundType.get(ownerBlock); Object breakSound = Reflections.field$SoundType$breakSound.get(soundType); - block.getWorld().playSound(block.getLocation(), Reflections.field$SoundEvent$location.get(breakSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); + block.getWorld().playSound(block.getLocation(), FastNMS.INSTANCE.field$SoundEvent$location(breakSound).toString(), SoundCategory.BLOCKS, 1f, 0.8f); } catch (ReflectiveOperationException e) { this.plugin.logger().warn("Failed to get sound type", e); } @@ -251,7 +251,7 @@ public class BlockEventListener implements Listener { try { Object soundType = Reflections.field$BlockBehaviour$soundType.get(ownerBlock); Object stepSound = Reflections.field$SoundType$stepSound.get(soundType); - player.playSound(playerLocation, Reflections.field$SoundEvent$location.get(stepSound).toString(), SoundCategory.BLOCKS, 0.15f, 1f); + player.playSound(playerLocation, FastNMS.INSTANCE.field$SoundEvent$location(stepSound).toString(), SoundCategory.BLOCKS, 0.15f, 1f); } catch (ReflectiveOperationException e) { plugin.logger().warn("Failed to get sound type", e); } 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 9f60f3af1..01b2c7495 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 @@ -8,6 +8,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import dev.dejvokep.boostedyaml.YamlDocument; import net.momirealms.craftengine.bukkit.compatibility.worldedit.WorldEditBlockRegister; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; @@ -327,7 +328,7 @@ public class BukkitBlockManager extends AbstractBlockManager { for (Object soundType : affectedSounds) { for (Field field : List.of(Reflections.field$SoundType$placeSound, Reflections.field$SoundType$fallSound, Reflections.field$SoundType$hitSound, Reflections.field$SoundType$stepSound, Reflections.field$SoundType$breakSound)) { Object soundEvent = field.get(soundType); - Key previousId = Key.of(Reflections.field$SoundEvent$location.get(soundEvent).toString()); + Key previousId = Key.of(FastNMS.INSTANCE.field$SoundEvent$location(soundEvent).toString()); soundMapperBuilder.put(previousId, Key.of(previousId.namespace(), "replaced." + previousId.value())); } } @@ -832,8 +833,8 @@ public class BukkitBlockManager extends AbstractBlockManager { return counter; } - private Object createResourceLocation(Key key) throws Exception { - return Reflections.method$ResourceLocation$fromNamespaceAndPath.invoke(null, key.namespace(), key.value()); + private Object createResourceLocation(Key key) { + return FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath(key.namespace(), key.value()); } private Object getBlockFromRegistry(Object resourceLocation) throws Exception { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java index 99b543466..5c4ec7e41 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/LeavesBlockBehavior.java @@ -117,7 +117,7 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior { if (event.isCancelled()) { return; } - Reflections.method$Level$removeBlock.invoke(level, blockPos, false); + FastNMS.INSTANCE.method$Level$removeBlock(level, blockPos, false); if (isWaterLogged(immutableBlockState)) { bukkitWorld.setBlockData(pos.x(), pos.y(), pos.z(), Material.WATER.createBlockData()); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java index c11a5d511..f819125c8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java @@ -59,7 +59,7 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior { ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(stateId); if (currentState != null && !currentState.isEmpty()) { // break the sugar cane - Reflections.method$Level$removeBlock.invoke(level, blockPos, false); + FastNMS.INSTANCE.method$Level$removeBlock(level, blockPos, false); Vec3d vec3d = Vec3d.atCenterOf(LocationUtils.fromBlockPos(blockPos)); net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); ContextHolder.Builder builder = ContextHolder.builder() diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 866bff3f0..91eb5f378 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -32,9 +32,7 @@ import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.view.AnvilView; import java.lang.reflect.InvocationTargetException; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; public class BukkitFontManager extends AbstractFontManager implements Listener { private final BukkitCraftEngine plugin; @@ -57,11 +55,11 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { @Override public void delayedLoad() { - Map oldCachedEmojiSuggestions = this.oldCachedEmojiSuggestions(); + List oldCachedEmojiSuggestions = this.oldCachedEmojiSuggestions(); super.delayedLoad(); - this.oldCachedEmojiSuggestions.putAll(this.cachedEmojiSuggestions()); + this.oldCachedEmojiSuggestions.addAll(this.cachedEmojiSuggestions()); Bukkit.getOnlinePlayers().forEach(player -> { - FastNMS.INSTANCE.method$ChatSuggestions$remove(oldCachedEmojiSuggestions.keySet(), player); + player.removeCustomChatCompletions(oldCachedEmojiSuggestions); this.addEmojiSuggestions(player); }); } @@ -73,18 +71,17 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } private void addEmojiSuggestions(Player player) { - Map hasPermissions = new HashMap<>(); - Map cachedEmojiSuggestions = this.cachedEmojiSuggestions(); - for (UUID uuid : cachedEmojiSuggestions.keySet()) { - String keyword = cachedEmojiSuggestions.get(uuid); + List hasPermissions = new ArrayList<>(); + List cachedEmojiSuggestions = this.cachedEmojiSuggestions(); + for (String keyword : cachedEmojiSuggestions) { Emoji emoji = super.emojiMapper.get(keyword); if (emoji == null) continue; if (emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission()))) { continue; } - hasPermissions.put(uuid, keyword); + hasPermissions.add(keyword); } - FastNMS.INSTANCE.method$ChatSuggestions$add(hasPermissions, player); + player.addCustomChatCompletions(hasPermissions); } @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) 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 bd548bb67..663ec5371 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 @@ -655,7 +655,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager { jsonObject.add("components", result.components()); Object nmsStack = ItemObject.newItem(TagCompound.newTag(jsonObject.toString())); try { - itemStack = (ItemStack) Reflections.method$CraftItemStack$asCraftMirror.invoke(null, nmsStack); + itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack); } catch (Exception e) { this.plugin.logger().warn("Failed to create ItemStack mirror", e); return new ItemStack(Material.STICK); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 85708dc2a..a618c49df 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -43,7 +43,6 @@ import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.BiConsumer; -import java.util.function.Consumer; public class PacketConsumers { private static int[] mappings; @@ -1118,7 +1117,7 @@ public class PacketConsumers { if (!user.isOnline()) return; BukkitServerPlayer player = (BukkitServerPlayer) user; if (!player.isMiningBlock()) return; - Object hand = Reflections.field$ServerboundSwingPacket$hand.get(packet); + Object hand = FastNMS.INSTANCE.field$ServerboundSwingPacket$hand(packet); if (hand == Reflections.instance$InteractionHand$MAIN_HAND) { player.onSwingHand(); } @@ -1150,7 +1149,7 @@ public class PacketConsumers { Object commonInfo = Reflections.field$ClientboundRespawnPacket$commonPlayerSpawnInfo.get(packet); dimensionKey = Reflections.field$CommonPlayerSpawnInfo$dimension.get(commonInfo); } - Object location = Reflections.field$ResourceKey$location.get(dimensionKey); + Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey); World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString()))); if (world != null) { int sectionCount = (world.getMaxHeight() - world.getMinHeight()) / 16; @@ -1175,7 +1174,7 @@ public class PacketConsumers { Object commonInfo = Reflections.field$ClientboundLoginPacket$commonPlayerSpawnInfo.get(packet); dimensionKey = Reflections.field$CommonPlayerSpawnInfo$dimension.get(commonInfo); } - Object location = Reflections.field$ResourceKey$location.get(dimensionKey); + Object location = FastNMS.INSTANCE.field$ResourceKey$location(dimensionKey); World world = Bukkit.getWorld(Objects.requireNonNull(NamespacedKey.fromString(location.toString()))); if (world != null) { int sectionCount = (world.getMaxHeight() - world.getMinHeight()) / 16; @@ -1219,7 +1218,7 @@ public class PacketConsumers { if (bukkitPlayer.getGameMode() != GameMode.CREATIVE) return; int slot = VersionHelper.isVersionNewerThan1_20_5() ? Reflections.field$ServerboundSetCreativeModeSlotPacket$slotNum.getShort(packet) : Reflections.field$ServerboundSetCreativeModeSlotPacket$slotNum.getInt(packet); if (slot < 36 || slot > 44) return; - ItemStack item = (ItemStack) Reflections.method$CraftItemStack$asCraftMirror.invoke(null, Reflections.field$ServerboundSetCreativeModeSlotPacket$itemStack.get(packet)); + ItemStack item = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(Reflections.field$ServerboundSetCreativeModeSlotPacket$itemStack.get(packet)); if (ItemUtils.isEmpty(item)) return; if (slot - 36 != bukkitPlayer.getInventory().getHeldItemSlot()) { return; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index d1f9487b3..398b5528b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -424,7 +424,7 @@ public class BukkitServerPlayer extends Player { Object blockOwner = Reflections.field$StateHolder$owner.get(this.destroyedState); Object soundType = Reflections.field$BlockBehaviour$soundType.get(blockOwner); Object soundEvent = Reflections.field$SoundType$hitSound.get(soundType); - Object soundId = Reflections.field$SoundEvent$location.get(soundEvent); + Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent); player.playSound(location, soundId.toString(), SoundCategory.BLOCKS, 0.5F, 0.5F); this.lastHitBlockTime = currentTick; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java index 0869b5437..bbce5ef50 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java @@ -128,7 +128,7 @@ public class BlockStateUtils { public static int physicsEventToId(BlockPhysicsEvent event) throws ReflectiveOperationException { Object blockData = Reflections.field$BlockPhysicsEvent$changed.get(event); Object blockState = Reflections.field$CraftBlockData$data.get(blockData); - return (int) Reflections.method$IdMapper$getId.invoke(Reflections.instance$BLOCK_STATE_REGISTRY, blockState); + return FastNMS.INSTANCE.method$IdMapper$getId(Reflections.instance$BLOCK_STATE_REGISTRY, blockState); } public static Object physicsEventToState(BlockPhysicsEvent event) throws ReflectiveOperationException { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index ba09578a0..fdb961d06 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -11,6 +11,7 @@ import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.MessageToByteEncoder; import io.papermc.paper.event.player.AsyncChatDecorateEvent; import net.kyori.adventure.text.Component; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.core.util.ReflectionUtils; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Location; @@ -405,18 +406,21 @@ public class Reflections { BukkitReflectionUtils.assembleMCClass("core.HolderLookup$b") ); + @Deprecated public static final Method method$Component$Serializer$fromJson0 = ReflectionUtils.getMethod( clazz$Component$Serializer, new String[] { "fromJson" }, String.class, clazz$HolderLookup$Provider ); + @Deprecated public static final Method method$Component$Serializer$fromJson1 = ReflectionUtils.getMethod( clazz$Component$Serializer, new String[] { "fromJson" }, JsonElement.class, clazz$HolderLookup$Provider ); + @Deprecated public static final Method method$Component$Serializer$toJson = ReflectionUtils.getMethod( clazz$Component$Serializer, new String[] { "toJson" }, @@ -488,6 +492,7 @@ public class Reflections { ReflectionUtils.getInstanceDeclaredField(clazz$ServerPlayer, clazz$ServerGamePacketListenerImpl, 0) ); + @Deprecated public static final Method method$ServerGamePacketListenerImpl$sendPacket = requireNonNull( ReflectionUtils.getMethods(clazz$ServerGamePacketListenerImpl, void.class, clazz$Packet).get(0) ); @@ -547,12 +552,14 @@ public class Reflections { ) ); + @Deprecated public static final Field field$ClientboundAddEntityPacket$data = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ClientboundAddEntityPacket, int.class, 4 ) ); + @Deprecated public static final Field field$ClientboundAddEntityPacket$type = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ClientboundAddEntityPacket, clazz$EntityType, 0 @@ -577,6 +584,7 @@ public class Reflections { // ) // ); + @Deprecated public static final Field field$ClientboundAddEntityPacket$entityId = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ClientboundAddEntityPacket, int.class, 0 @@ -595,18 +603,21 @@ public class Reflections { ) ); + @Deprecated public static final Field field$Vec3$x = requireNonNull( ReflectionUtils.getInstanceDeclaredField( clazz$Vec3, double.class, 0 ) ); + @Deprecated public static final Field field$Vec3$y = requireNonNull( ReflectionUtils.getInstanceDeclaredField( clazz$Vec3, double.class, 1 ) ); + @Deprecated public static final Field field$Vec3$z = requireNonNull( ReflectionUtils.getInstanceDeclaredField( clazz$Vec3, double.class, 2 @@ -1086,6 +1097,7 @@ public class Reflections { ) ); + @Deprecated public static final Field field$ResourceKey$location = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ResourceKey, clazz$ResourceLocation, 1 @@ -1454,10 +1466,12 @@ public class Reflections { ReflectionUtils.getMethod(clazz$IdMapper, int.class) ); + @Deprecated public static final Method method$IdMapper$getId = requireNonNull( ReflectionUtils.getMethod(clazz$IdMapper, int.class, Object.class) ); + @Deprecated public static final Method method$IdMapper$byId = requireNonNull( ReflectionUtils.getMethod(clazz$IdMapper, Object.class, int.class) ); @@ -1643,6 +1657,7 @@ public class Reflections { BukkitReflectionUtils.assembleMCClass("network.RegistryFriendlyByteBuf") ); + @Deprecated public static final Constructor constructor$RegistryFriendlyByteBuf = Optional.ofNullable(clazz$RegistryFriendlyByteBuf) .map(it -> ReflectionUtils.getConstructor(it, 0)) .orElse(null); @@ -1751,6 +1766,7 @@ public class Reflections { ) ); + @Deprecated public static final Field field$ClientboundLevelChunkWithLightPacket$chunkData = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ClientboundLevelChunkWithLightPacket, clazz$ClientboundLevelChunkPacketData, 0 @@ -1787,6 +1803,7 @@ public class Reflections { ) ); + @Deprecated public static final Field field$CraftChunk$worldServer = requireNonNull( ReflectionUtils.getDeclaredField( clazz$CraftChunk, clazz$ServerLevel, 0 @@ -2165,6 +2182,7 @@ public class Reflections { } } + @Deprecated public static final Method method$ResourceLocation$fromNamespaceAndPath = requireNonNull( ReflectionUtils.getStaticMethod( clazz$ResourceLocation, clazz$ResourceLocation, String.class, String.class @@ -2443,6 +2461,7 @@ public class Reflections { ) ); + @Deprecated public static final Constructor constructor$AABB = requireNonNull( ReflectionUtils.getConstructor( clazz$AABB, double.class, double.class, double.class, double.class, double.class, double.class @@ -2562,12 +2581,14 @@ public class Reflections { ) ); + @Deprecated public static final Field field$ClientboundLevelParticlesPacket$particle = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ClientboundLevelParticlesPacket, clazz$ParticleOptions, 0 ) ); + @Deprecated public static final Field field$BlockParticleOption$blockState = requireNonNull( ReflectionUtils.getDeclaredField( clazz$BlockParticleOption, clazz$BlockState, 0 @@ -2634,6 +2655,7 @@ public class Reflections { ) ); + @Deprecated public static final Field field$SoundEvent$location = requireNonNull( ReflectionUtils.getInstanceDeclaredField( clazz$SoundEvent, clazz$ResourceLocation, 0 @@ -2673,6 +2695,7 @@ public class Reflections { ) ); + @Deprecated public static final Constructor constructor$ClientboundLightUpdatePacket = requireNonNull( ReflectionUtils.getConstructor( clazz$ClientboundLightUpdatePacket, clazz$ChunkPos, clazz$LevelLightEngine, BitSet.class, BitSet.class @@ -2713,6 +2736,7 @@ public class Reflections { ); // 1.20 ~ 1.21.4 moonrise + @Deprecated public static final Method method$ChunkHolder$getPlayers = ReflectionUtils.getMethod( clazz$ChunkHolder, List.class, boolean.class @@ -2792,12 +2816,14 @@ public class Reflections { ) ); + @Deprecated public static final Field field$ServerboundPlayerActionPacket$pos = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ServerboundPlayerActionPacket, clazz$BlockPos, 0 ) ); + @Deprecated public static final Field field$ServerboundPlayerActionPacket$action = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ServerboundPlayerActionPacket, clazz$ServerboundPlayerActionPacket$Action, 0 @@ -2832,17 +2858,17 @@ public class Reflections { static { try { if (VersionHelper.isVersionNewerThan1_20_5()) { - Object block_break_speed = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", VersionHelper.isVersionNewerThan1_21_2() ? "block_break_speed" : "player.block_break_speed"); + Object block_break_speed = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", VersionHelper.isVersionNewerThan1_21_2() ? "block_break_speed" : "player.block_break_speed"); @SuppressWarnings("unchecked") Optional breakSpeedHolder = (Optional) method$Registry$getHolder0.invoke(instance$BuiltInRegistries$ATTRIBUTE, block_break_speed); instance$Holder$Attribute$block_break_speed = breakSpeedHolder.orElse(null); - Object block_interaction_range = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", VersionHelper.isVersionNewerThan1_21_2() ? "block_interaction_range" : "player.block_interaction_range"); + Object block_interaction_range = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", VersionHelper.isVersionNewerThan1_21_2() ? "block_interaction_range" : "player.block_interaction_range"); @SuppressWarnings("unchecked") Optional blockInteractionRangeHolder = (Optional) method$Registry$getHolder0.invoke(instance$BuiltInRegistries$ATTRIBUTE, block_interaction_range); instance$Holder$Attribute$block_interaction_range = blockInteractionRangeHolder.orElse(null); - Object scale = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", VersionHelper.isVersionNewerThan1_21_2() ? "scale" : "generic.scale"); + Object scale = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", VersionHelper.isVersionNewerThan1_21_2() ? "scale" : "generic.scale"); @SuppressWarnings("unchecked") Optional scaleHolder = (Optional) method$Registry$getHolder0.invoke(instance$BuiltInRegistries$ATTRIBUTE, scale); instance$Holder$Attribute$scale = scaleHolder.orElse(null); @@ -2986,6 +3012,7 @@ public class Reflections { ) ); + @Deprecated public static final Field field$ServerboundSwingPacket$hand = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ServerboundSwingPacket, clazz$InteractionHand, 0 @@ -3198,11 +3225,11 @@ public class Reflections { // for 1.20.1-1.20.4 static { try { - Object mining_fatigue = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "mining_fatigue"); + Object mining_fatigue = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "mining_fatigue"); instance$MobEffecr$mining_fatigue = method$Registry$get.invoke(instance$BuiltInRegistries$MOB_EFFECT, mining_fatigue); - Object haste = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "haste"); + Object haste = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "haste"); instance$MobEffecr$haste = method$Registry$get.invoke(instance$BuiltInRegistries$MOB_EFFECT, haste); - Object invisibility = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "invisibility"); + Object invisibility = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "invisibility"); instance$MobEffecr$invisibility = method$Registry$get.invoke(instance$BuiltInRegistries$MOB_EFFECT, invisibility); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); @@ -3289,7 +3316,7 @@ public class Reflections { static { try { - Object key = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "intentionally_empty"); + Object key = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "intentionally_empty"); instance$SoundEvent$EMPTY = method$Registry$get.invoke(instance$BuiltInRegistries$SOUND_EVENT, key); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); @@ -3361,6 +3388,7 @@ public class Reflections { ) ); + @Deprecated public static final Method method$CraftItemStack$asCraftMirror = requireNonNull( ReflectionUtils.getStaticMethod( clazz$CraftItemStack, clazz$CraftItemStack, new String[]{"asCraftMirror"}, clazz$ItemStack @@ -3531,17 +3559,17 @@ public class Reflections { static { try { - Object air = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "air"); + Object air = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "air"); instance$Blocks$AIR = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, air); instance$Blocks$AIR$defaultState = method$Block$defaultBlockState.invoke(instance$Blocks$AIR); - Object fire = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "fire"); + Object fire = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "fire"); instance$Blocks$FIRE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, fire); - Object soulFire = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "soul_fire"); + Object soulFire = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "soul_fire"); instance$Blocks$SOUL_FIRE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, soulFire); - Object stone = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "stone"); + Object stone = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "stone"); instance$Blocks$STONE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, stone); instance$Blocks$STONE$defaultState = method$Block$defaultBlockState.invoke(instance$Blocks$STONE); - Object ice = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "ice"); + Object ice = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "ice"); instance$Blocks$ICE = method$Registry$get.invoke(instance$BuiltInRegistries$BLOCK, ice); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); @@ -3554,6 +3582,7 @@ public class Reflections { ) ); + @Deprecated public static final Method method$Level$removeBlock = requireNonNull( ReflectionUtils.getMethod( clazz$Level, boolean.class, clazz$BlockPos, boolean.class @@ -3723,19 +3752,19 @@ public class Reflections { static { try { - Object textDisplay = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "text_display"); + Object textDisplay = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "text_display"); instance$EntityType$TEXT_DISPLAY = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, textDisplay); - Object itemDisplay = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "item_display"); + Object itemDisplay = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item_display"); instance$EntityType$ITEM_DISPLAY = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, itemDisplay); - Object blockDisplay = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "block_display"); + Object blockDisplay = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "block_display"); instance$EntityType$BLOCK_DISPLAY = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, blockDisplay); - Object fallingBlock = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "falling_block"); + Object fallingBlock = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "falling_block"); instance$EntityType$FALLING_BLOCK = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, fallingBlock); - Object interaction = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "interaction"); + Object interaction = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "interaction"); instance$EntityType$INTERACTION = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, interaction); - Object shulker = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "shulker"); + Object shulker = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "shulker"); instance$EntityType$SHULKER = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, shulker); - Object armorStand = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "armor_stand"); + Object armorStand = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "armor_stand"); instance$EntityType$ARMOR_STAND = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, armorStand); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); @@ -3752,13 +3781,13 @@ public class Reflections { static { try { - instance$RecipeType$CRAFTING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "crafting")); - instance$RecipeType$SMELTING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "smelting")); - instance$RecipeType$BLASTING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "blasting")); - instance$RecipeType$SMOKING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "smoking")); - instance$RecipeType$CAMPFIRE_COOKING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "campfire_cooking")); - instance$RecipeType$STONECUTTING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "stonecutting")); - instance$RecipeType$SMITHING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "smithing")); + instance$RecipeType$CRAFTING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "crafting")); + instance$RecipeType$SMELTING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "smelting")); + instance$RecipeType$BLASTING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "blasting")); + instance$RecipeType$SMOKING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "smoking")); + instance$RecipeType$CAMPFIRE_COOKING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "campfire_cooking")); + instance$RecipeType$STONECUTTING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "stonecutting")); + instance$RecipeType$SMITHING = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$RECIPE_TYPE, FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "smithing")); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -3813,6 +3842,7 @@ public class Reflections { ) ); + @Deprecated public static final Method method$BlockStateBase$onPlace = requireNonNull( ReflectionUtils.getMethod( clazz$BlockStateBase, void.class, clazz$Level, clazz$BlockPos, clazz$BlockState, boolean.class @@ -3858,10 +3888,10 @@ public class Reflections { static { try { - Object air = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "air"); + Object air = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "air"); instance$Items$AIR = method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ITEM, air); instance$ItemStack$Air = constructor$ItemStack.newInstance(instance$Items$AIR); - Object waterBucket = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "water_bucket"); + Object waterBucket = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "water_bucket"); instance$Items$WATER_BUCKET = method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ITEM, waterBucket); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); @@ -4088,15 +4118,15 @@ public class Reflections { static { try { - Object waterId = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "water"); + Object waterId = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "water"); instance$Fluids$WATER = method$Registry$get.invoke(instance$BuiltInRegistries$FLUID, waterId); - Object flowingWaterId = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "flowing_water"); + Object flowingWaterId = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "flowing_water"); instance$Fluids$FLOWING_WATER = method$Registry$get.invoke(instance$BuiltInRegistries$FLUID, flowingWaterId); - Object lavaId = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "lava"); + Object lavaId = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "lava"); instance$Fluids$LAVA = method$Registry$get.invoke(instance$BuiltInRegistries$FLUID, lavaId); - Object flowingLavaId = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "flowing_lava"); + Object flowingLavaId = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "flowing_lava"); instance$Fluids$FLOWING_LAVA = method$Registry$get.invoke(instance$BuiltInRegistries$FLUID, flowingLavaId); - Object emptyId = method$ResourceLocation$fromNamespaceAndPath.invoke(null, "minecraft", "empty"); + Object emptyId = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "empty"); instance$Fluids$EMPTY = method$Registry$get.invoke(instance$BuiltInRegistries$FLUID, emptyId); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); @@ -4509,6 +4539,7 @@ public class Reflections { .map(it -> ReflectionUtils.getDeclaredField(it, 1)) .orElse(null); + @Deprecated public static final Field field$RecipeHolder$id = Optional.ofNullable(clazz$RecipeHolder) .map(it -> ReflectionUtils.getDeclaredField(it, 0)) .orElse(null); @@ -5967,6 +5998,7 @@ public class Reflections { ) ); + @Deprecated public static final Method method$Entity$getId = requireNonNull( VersionHelper.isVersionNewerThan1_20_5() ? ReflectionUtils.getMethod(clazz$Entity, int.class, new String[]{"getId"}) 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 d2dd94945..a8f466d99 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 @@ -42,8 +42,8 @@ public abstract class AbstractFontManager implements FontManager { protected Map tagMapper; protected Map emojiMapper; // tab补全 - protected final Map cachedEmojiSuggestions = new HashMap<>(); - protected final Map oldCachedEmojiSuggestions = new HashMap<>(); + protected final List cachedEmojiSuggestions = new ArrayList<>(); + protected final List oldCachedEmojiSuggestions = new ArrayList<>(); public AbstractFontManager(CraftEngine plugin) { this.plugin = plugin; @@ -68,13 +68,13 @@ public abstract class AbstractFontManager implements FontManager { } @Override - public Map cachedEmojiSuggestions() { - return ImmutableMap.copyOf(this.cachedEmojiSuggestions); + public List cachedEmojiSuggestions() { + return List.copyOf(this.cachedEmojiSuggestions); } @Override - public Map oldCachedEmojiSuggestions() { - return ImmutableMap.copyOf(this.oldCachedEmojiSuggestions); + public List oldCachedEmojiSuggestions() { + return List.copyOf(this.oldCachedEmojiSuggestions); } @Override @@ -372,8 +372,7 @@ public abstract class AbstractFontManager implements FontManager { return; } String keyword = keywords.get(0); - UUID uuid = UUID.nameUUIDFromBytes(keyword.getBytes(StandardCharsets.UTF_8)); - cachedEmojiSuggestions.put(uuid, keyword); + cachedEmojiSuggestions.add(keyword); String content = section.getOrDefault("content", "").toString(); String image = null; if (section.containsKey("image")) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index 8da06423e..e685fc7a3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -12,10 +12,7 @@ import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; +import java.util.*; public interface FontManager extends Manageable { Key DEFAULT_FONT = Key.of("minecraft:default"); @@ -105,7 +102,7 @@ public interface FontManager extends Manageable { Map matchTags(String json); - Map cachedEmojiSuggestions(); + List cachedEmojiSuggestions(); - Map oldCachedEmojiSuggestions(); + List oldCachedEmojiSuggestions(); } diff --git a/gradle.properties b/gradle.properties index c2f32fe2c..4d122a98e 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.13 -nms_helper_version=0.54 +nms_helper_version=0.55 # Ignite Dependencies mixinextras_version=0.4.1 mixin_version=0.15.2+mixin.0.8.7 From 4e4b25c2b83b52b968c93fcf63c5e859754f95e5 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 12 Apr 2025 02:02:49 +0800 Subject: [PATCH 20/28] =?UTF-8?q?=E6=B7=BB=E5=8A=A0player=20info=E6=8B=A6?= =?UTF-8?q?=E6=88=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../item/recipe/RecipeEventListener.java | 3 +- .../plugin/injector/BukkitInjector.java | 8 +-- .../plugin/network/BukkitNetworkManager.java | 1 + .../plugin/network/PacketConsumers.java | 44 ++++++++++++++ .../craftengine/bukkit/util/Reflections.java | 57 +++++++++++++++---- .../core/plugin/config/Config.java | 4 ++ .../core/util/MarkedArrayList.java | 16 ++++++ gradle.properties | 2 +- 8 files changed, 117 insertions(+), 18 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java 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 f67a36256..f0eb1042f 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 @@ -343,8 +343,7 @@ public class RecipeEventListener implements Listener { ItemStack itemStack = event.getItem(); if (ItemUtils.isEmpty(itemStack)) return; try { - @SuppressWarnings("unchecked") - Optional optionalMCRecipe = (Optional) Reflections.method$RecipeManager$getRecipeFor1.invoke( + Optional optionalMCRecipe = FastNMS.INSTANCE.method$RecipeManager$getRecipeFor( BukkitRecipeManager.nmsRecipeManager(), Reflections.instance$RecipeType$CAMPFIRE_COOKING, Reflections.constructor$SingleRecipeInput.newInstance(FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack)), diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java index 641947e72..2386ea39b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/BukkitInjector.java @@ -406,7 +406,7 @@ public class BukkitInjector { InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; Object type = injectedCacheCheck.recipeType(); Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional> optionalRecipe = (Optional>) Reflections.method$RecipeManager$getRecipeFor0.invoke(mcRecipeManager, type, args[0], args[1], 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(); @@ -474,7 +474,7 @@ public class BukkitInjector { InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; Object type = injectedCacheCheck.recipeType(); Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) Reflections.method$RecipeManager$getRecipeFor0.invoke(mcRecipeManager, type, args[0], args[1], 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); @@ -542,7 +542,7 @@ public class BukkitInjector { InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; Object type = injectedCacheCheck.recipeType(); Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) Reflections.method$RecipeManager$getRecipeFor0.invoke(mcRecipeManager, type, args[0], args[1], 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); @@ -602,7 +602,7 @@ public class BukkitInjector { InjectedCacheCheck injectedCacheCheck = (InjectedCacheCheck) thisObj; Object type = injectedCacheCheck.recipeType(); Object lastRecipe = injectedCacheCheck.lastRecipe(); - Optional optionalRecipe = (Optional) Reflections.method$RecipeManager$getRecipeFor1.invoke(mcRecipeManager, type, args[0], args[1], 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); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 4816cd91f..b2c53a8da 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -127,6 +127,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private void registerPacketHandlers() { registerNMSPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, Reflections.clazz$ClientboundLevelChunkWithLightPacket); + registerNMSPacketConsumer(PacketConsumers.PLAYER_INFO_UPDATE, Reflections.clazz$ClientboundPlayerInfoUpdatePacket); registerNMSPacketConsumer(PacketConsumers.PLAYER_ACTION, Reflections.clazz$ServerboundPlayerActionPacket); registerNMSPacketConsumer(PacketConsumers.SWING_HAND, Reflections.clazz$ServerboundSwingPacket); registerNMSPacketConsumer(PacketConsumers.USE_ITEM_ON, Reflections.clazz$ServerboundUseItemOnPacket); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index a618c49df..cd158fe39 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -321,6 +321,50 @@ public class PacketConsumers { } }; + public static final TriConsumer PLAYER_INFO_UPDATE = (user, event, packet) -> { + try { + if (!user.isOnline()) return; + if (!Config.interceptPlayerInfo()) return; + List entries = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$entries(packet); + if (entries instanceof MarkedArrayList) { + return; + } + EnumSet> enums = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$actions(packet); + outer: { + for (Object entry : enums) { + if (entry == Reflections.instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME) { + break outer; + } + } + return; + } + + List newEntries = new MarkedArrayList<>(); + Reflections.field$ClientboundPlayerInfoUpdatePacket$entries.set(packet, newEntries); + for (Object entry : entries) { + Object mcComponent = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$Entry$displayName(entry); + if (mcComponent == null) { + newEntries.add(entry); + continue; + } + String json = ComponentUtils.minecraftToJson(mcComponent); + Map tokens = CraftEngine.instance().imageManager().matchTags(json); + if (tokens.isEmpty()) { + newEntries.add(entry); + continue; + } + Component component = AdventureHelper.jsonToComponent(json); + for (Map.Entry token : tokens.entrySet()) { + component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue())); + } + Object newEntry = FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket$Entry(entry, ComponentUtils.adventureToMinecraft(component)); + newEntries.add(newEntry); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ClientboundPlayerInfoUpdatePacket", e); + } + }; + public static final BiConsumer TEAM_1_20 = (user, event) -> { if (!Config.interceptTeam()) return; try { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index fdb961d06..95c2827ff 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -1766,6 +1766,35 @@ public class Reflections { ) ); + public static final Field field$ClientboundPlayerInfoUpdatePacket$entries = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundPlayerInfoUpdatePacket, List.class, 0 + ) + ); + + public static final Class clazz$ClientboundPlayerInfoUpdatePacket$Action = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action") + ) + ); + + public static final Method method$ClientboundPlayerInfoUpdatePacket$Action$values = requireNonNull( + ReflectionUtils.getStaticMethod( + clazz$ClientboundPlayerInfoUpdatePacket$Action, clazz$ClientboundPlayerInfoUpdatePacket$Action.arrayType() + ) + ); + + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME; + + static { + try { + Object[] values = (Object[]) method$ClientboundPlayerInfoUpdatePacket$Action$values.invoke(null); + instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME = values[5]; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + @Deprecated public static final Field field$ClientboundLevelChunkWithLightPacket$chunkData = requireNonNull( ReflectionUtils.getDeclaredField( @@ -4811,17 +4840,23 @@ public class Reflections { .map(it -> ReflectionUtils.getConstructor(it, clazz$ItemStack)) .orElse(null); - // 1.20.1-1.21.1 - public static final Method method$RecipeManager$getRecipeFor0 = - ReflectionUtils.getMethod( - clazz$RecipeManager, Optional.class, clazz$RecipeType, clazz$Container, clazz$Level, clazz$ResourceLocation - ); - - // 1.21.2+ - public static final Method method$RecipeManager$getRecipeFor1 = - ReflectionUtils.getMethod( - clazz$RecipeManager, Optional.class, clazz$RecipeType, clazz$RecipeInput, clazz$Level, clazz$ResourceKey - ); +// // 1.20.1-1.20.6 +// public static final Method method$RecipeManager$getRecipeFor0 = +// ReflectionUtils.getMethod( +// clazz$RecipeManager, Optional.class, clazz$RecipeType, clazz$Container, clazz$Level, clazz$ResourceLocation +// ); +// +// // 1.21.1 +// public static final Method method$RecipeManager$getRecipeFor2 = +// ReflectionUtils.getMethod( +// clazz$RecipeManager, Optional.class, clazz$RecipeType, clazz$RecipeInput, clazz$Level, clazz$ResourceLocation +// ); +// +// // 1.21.2+ +// public static final Method method$RecipeManager$getRecipeFor1 = +// ReflectionUtils.getMethod( +// clazz$RecipeManager, Optional.class, clazz$RecipeType, clazz$RecipeInput, clazz$Level, clazz$ResourceKey +// ); // 1.21+ public static final Field field$SingleRecipeInput$item = Optional.ofNullable(clazz$SingleRecipeInput) 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 409acc738..8e976ca03 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 @@ -652,6 +652,10 @@ public class Config { return instance.image$intercept_packets$armor_stand; } + public static boolean interceptPlayerInfo() { + return instance.image$intercept_packets$player_info; + } + public YamlDocument loadOrCreateYamlData(String fileName) { File file = new File(this.plugin.dataFolderFile(), fileName); if (!file.exists()) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java b/core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java new file mode 100644 index 000000000..1d1e57d24 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MarkedArrayList.java @@ -0,0 +1,16 @@ +package net.momirealms.craftengine.core.util; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; + +public class MarkedArrayList extends ArrayList { + + public MarkedArrayList() { + } + + public MarkedArrayList(@NotNull Collection c) { + super(c); + } +} diff --git a/gradle.properties b/gradle.properties index 4d122a98e..617b4f248 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.13 -nms_helper_version=0.55 +nms_helper_version=0.56.2 # Ignite Dependencies mixinextras_version=0.4.1 mixin_version=0.15.2+mixin.0.8.7 From f4d8e405461b1e540effb3f979e8bc1de2cde298 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 12 Apr 2025 02:04:53 +0800 Subject: [PATCH 21/28] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8C=85=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/plugin/network/BukkitNetworkManager.java | 4 ++-- .../bukkit/plugin/network/{impl => id}/PacketIdFinder.java | 2 +- .../bukkit/plugin/network/{impl => id}/PacketIds1_20.java | 2 +- .../bukkit/plugin/network/{impl => id}/PacketIds1_20_5.java | 2 +- .../net/momirealms/craftengine/bukkit/util/Reflections.java | 3 ++- 5 files changed, 7 insertions(+), 6 deletions(-) rename bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/{impl => id}/PacketIdFinder.java (97%) rename bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/{impl => id}/PacketIds1_20.java (98%) rename bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/{impl => id}/PacketIds1_20_5.java (97%) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index b2c53a8da..77b5bce77 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -8,8 +8,8 @@ import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.plugin.network.impl.PacketIds1_20; -import net.momirealms.craftengine.bukkit.plugin.network.impl.PacketIds1_20_5; +import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20; +import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20_5; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.plugin.CraftEngine; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIdFinder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIdFinder.java similarity index 97% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIdFinder.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIdFinder.java index 737534fcc..489ea5d01 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIdFinder.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIdFinder.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.bukkit.plugin.network.impl; +package net.momirealms.craftengine.bukkit.plugin.network.id; import com.google.gson.JsonElement; import net.momirealms.craftengine.bukkit.nms.FastNMS; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20.java similarity index 98% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20.java index f7632f51d..a42831d0b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.bukkit.plugin.network.impl; +package net.momirealms.craftengine.bukkit.plugin.network.id; import net.momirealms.craftengine.bukkit.plugin.network.PacketIds; import net.momirealms.craftengine.bukkit.util.Reflections; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20_5.java similarity index 97% rename from bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20_5.java rename to bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20_5.java index 66d8e2319..290f16df7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/impl/PacketIds1_20_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIds1_20_5.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.bukkit.plugin.network.impl; +package net.momirealms.craftengine.bukkit.plugin.network.id; import net.momirealms.craftengine.bukkit.plugin.network.PacketIds; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index 95c2827ff..a163b570d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -1774,7 +1774,8 @@ public class Reflections { public static final Class clazz$ClientboundPlayerInfoUpdatePacket$Action = requireNonNull( ReflectionUtils.getClazz( - BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action") + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action"), + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundPlayerInfoUpdatePacket$a") ) ); From 8e94625f93bf6d21f9efe2108f9f65474078fd35 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Sat, 12 Apr 2025 02:55:07 +0800 Subject: [PATCH 22/28] =?UTF-8?q?feat(bukkit):=20=E6=B7=BB=E5=8A=A0=20Luck?= =?UTF-8?q?Perms=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/compatibility/build.gradle.kts | 2 + .../permission/LuckPermsEventListeners.java | 85 +++++++++++++++++++ .../bukkit/font/BukkitFontManager.java | 58 +++++++++---- 3 files changed, 129 insertions(+), 16 deletions(-) create mode 100644 bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java diff --git a/bukkit/compatibility/build.gradle.kts b/bukkit/compatibility/build.gradle.kts index 05d1dc29f..8a7807cc9 100644 --- a/bukkit/compatibility/build.gradle.kts +++ b/bukkit/compatibility/build.gradle.kts @@ -32,6 +32,8 @@ dependencies { // MMOItems compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT") compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT") + // LuckPerms + compileOnly("net.luckperms:api:5.4") } java { diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java new file mode 100644 index 000000000..b08ce9481 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java @@ -0,0 +1,85 @@ +package net.momirealms.craftengine.bukkit.compatibility.permission; + +import net.luckperms.api.LuckPerms; +import net.luckperms.api.event.EventBus; +import net.luckperms.api.event.EventSubscription; +import net.luckperms.api.event.group.GroupDataRecalculateEvent; +import net.luckperms.api.event.user.UserDataRecalculateEvent; +import net.luckperms.api.model.user.User; +import net.momirealms.craftengine.core.plugin.scheduler.SchedulerAdapter; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.logging.Level; + +public class LuckPermsEventListeners { + private final JavaPlugin plugin; + private final LuckPerms luckPerms; + private final BiConsumer consumer; + private final SchedulerAdapter scheduler; + private final List> subscriptions = new ArrayList<>(); + + public LuckPermsEventListeners(JavaPlugin plugin, BiConsumer consumer, SchedulerAdapter scheduler) { + this.plugin = plugin; + this.consumer = consumer; + this.scheduler = scheduler; + RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class); + if (provider != null) { + this.luckPerms = provider.getProvider(); + EventBus eventBus = this.luckPerms.getEventBus(); + EventSubscription onUserPermissionChangeSubscription = + eventBus.subscribe( + plugin, + UserDataRecalculateEvent.class, + this::onUserPermissionChange + ); + EventSubscription onGroupPermissionChangeSubscription = + eventBus.subscribe( + plugin, + GroupDataRecalculateEvent.class, + this::onGroupPermissionChange + ); + this.subscriptions.add(onUserPermissionChangeSubscription); + this.subscriptions.add(onGroupPermissionChangeSubscription); + } else luckPerms = null; + } + + public void unregisterListeners() { + for (EventSubscription subscription : this.subscriptions) { + try { + subscription.close(); + } catch (Exception e) { + this.plugin.getLogger().log(Level.WARNING,"Failed to close event subscription", e); + } + } + this.subscriptions.clear(); + } + + private void onUserPermissionChange(UserDataRecalculateEvent event) { + this.consumer.accept(event.getUser().getUniqueId(), true); + } + + private void onGroupPermissionChange(GroupDataRecalculateEvent event) { + this.scheduler.asyncLater(() -> { + String groupName = event.getGroup().getName(); + Bukkit.getOnlinePlayers().forEach(player -> { + UUID playerUUID = player.getUniqueId(); + User onlineUser = this.luckPerms.getUserManager().getUser(playerUUID); + if (onlineUser == null) return; + boolean isInGroup = onlineUser.getInheritedGroups(onlineUser.getQueryOptions()) + .parallelStream() + .anyMatch(g -> g.getName().equals(groupName)); + if (isInGroup) { + this.consumer.accept(playerUUID, false); + } + }); + }, 1L, TimeUnit.SECONDS); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 91eb5f378..682084cbb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -5,7 +5,7 @@ import com.google.gson.JsonObject; import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent; import io.papermc.paper.event.player.AsyncChatDecorateEvent; import net.kyori.adventure.text.Component; -import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.compatibility.permission.LuckPermsEventListeners; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils; @@ -14,7 +14,6 @@ import net.momirealms.craftengine.core.font.*; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.util.AdventureHelper; -import net.momirealms.craftengine.core.util.CharacterUtils; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -32,10 +31,13 @@ import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.view.AnvilView; import java.lang.reflect.InvocationTargetException; -import java.util.*; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; public class BukkitFontManager extends AbstractFontManager implements Listener { private final BukkitCraftEngine plugin; + private LuckPermsEventListeners luckPermsEventListeners; public BukkitFontManager(BukkitCraftEngine plugin) { super(plugin); @@ -45,12 +47,20 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { @Override public void delayedInit() { Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap()); + if (this.plugin.isPluginEnabled("LuckPerms")) { + luckPermsEventListeners = new LuckPermsEventListeners( + plugin.bootstrap(), this::refreshEmojiSuggestions, plugin.scheduler() + ); + } } @Override public void disable() { super.disable(); HandlerList.unregisterAll(this); + if (luckPermsEventListeners != null && this.plugin.isPluginEnabled("LuckPerms")) { + luckPermsEventListeners.unregisterListeners(); + } } @Override @@ -64,23 +74,39 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { }); } - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + @EventHandler(priority = EventPriority.LOWEST) public void onPlayerJoin(PlayerJoinEvent event) { - Player player = event.getPlayer(); - this.addEmojiSuggestions(player); + plugin.scheduler().async().execute(() -> { + Player player = event.getPlayer(); + this.addEmojiSuggestions(player); + }); + } + + public void refreshEmojiSuggestions(UUID playerUUID, boolean isAsync) { + if (isAsync) { + plugin.scheduler().async().execute(() -> { + Player player = Bukkit.getPlayer(playerUUID); + if (player == null) return; + player.removeCustomChatCompletions(oldCachedEmojiSuggestions); + this.addEmojiSuggestions(player); + }); + } else { + Player player = Bukkit.getPlayer(playerUUID); + if (player == null) return; + player.removeCustomChatCompletions(oldCachedEmojiSuggestions); + this.addEmojiSuggestions(player); + } } private void addEmojiSuggestions(Player player) { - List hasPermissions = new ArrayList<>(); - List cachedEmojiSuggestions = this.cachedEmojiSuggestions(); - for (String keyword : cachedEmojiSuggestions) { - Emoji emoji = super.emojiMapper.get(keyword); - if (emoji == null) continue; - if (emoji.permission() != null && !player.hasPermission(Objects.requireNonNull(emoji.permission()))) { - continue; - } - hasPermissions.add(keyword); - } + List hasPermissions = cachedEmojiSuggestions().parallelStream() + .filter(keyword -> { + Emoji emoji = super.emojiMapper.get(keyword); + if (emoji == null) return false; + String permission = emoji.permission(); + return permission == null || player.hasPermission(permission); + }) + .collect(Collectors.toList()); player.addCustomChatCompletions(hasPermissions); } From b97fa1ee6a92d189c00c0a08ce04364828f5d999 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Sat, 12 Apr 2025 03:01:48 +0800 Subject: [PATCH 23/28] =?UTF-8?q?feat(bukkit):=20=E6=B7=BB=E5=8A=A0=20Luck?= =?UTF-8?q?Perms=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compatibility/permission/LuckPermsEventListeners.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java index b08ce9481..ee6c10977 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java @@ -56,7 +56,7 @@ public class LuckPermsEventListeners { try { subscription.close(); } catch (Exception e) { - this.plugin.getLogger().log(Level.WARNING,"Failed to close event subscription", e); + this.plugin.getLogger().log(Level.WARNING, "Failed to close event subscription", e); } } this.subscriptions.clear(); From 86f795e7a585c2c4b66a89b941af56a36bcfb61c Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Sat, 12 Apr 2025 03:18:39 +0800 Subject: [PATCH 24/28] =?UTF-8?q?feat(bukkit):=20=E6=B7=BB=E5=8A=A0=20Luck?= =?UTF-8?q?Perms=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../permission/LuckPermsEventListeners.java | 43 ++++++++----------- .../bukkit/font/BukkitFontManager.java | 4 +- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java index ee6c10977..b56a2befe 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java @@ -33,32 +33,26 @@ public class LuckPermsEventListeners { RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class); if (provider != null) { this.luckPerms = provider.getProvider(); - EventBus eventBus = this.luckPerms.getEventBus(); - EventSubscription onUserPermissionChangeSubscription = - eventBus.subscribe( - plugin, - UserDataRecalculateEvent.class, - this::onUserPermissionChange - ); - EventSubscription onGroupPermissionChangeSubscription = - eventBus.subscribe( - plugin, - GroupDataRecalculateEvent.class, - this::onGroupPermissionChange - ); - this.subscriptions.add(onUserPermissionChangeSubscription); - this.subscriptions.add(onGroupPermissionChangeSubscription); - } else luckPerms = null; + this.registerEventListeners(); + } else { + throw new IllegalStateException("Unable to hook into LuckPerms"); + } + } + + private void registerEventListeners() { + EventBus eventBus = this.luckPerms.getEventBus(); + this.subscriptions.add(eventBus.subscribe(this.plugin, UserDataRecalculateEvent.class, this::onUserPermissionChange)); + this.subscriptions.add(eventBus.subscribe(this.plugin, GroupDataRecalculateEvent.class, this::onGroupPermissionChange)); } public void unregisterListeners() { - for (EventSubscription subscription : this.subscriptions) { + this.subscriptions.forEach(subscription -> { try { subscription.close(); } catch (Exception e) { this.plugin.getLogger().log(Level.WARNING, "Failed to close event subscription", e); } - } + }); this.subscriptions.clear(); } @@ -70,14 +64,13 @@ public class LuckPermsEventListeners { this.scheduler.asyncLater(() -> { String groupName = event.getGroup().getName(); Bukkit.getOnlinePlayers().forEach(player -> { - UUID playerUUID = player.getUniqueId(); - User onlineUser = this.luckPerms.getUserManager().getUser(playerUUID); - if (onlineUser == null) return; - boolean isInGroup = onlineUser.getInheritedGroups(onlineUser.getQueryOptions()) - .parallelStream() + UUID uuid = player.getUniqueId(); + User user = luckPerms.getUserManager().getUser(uuid); + if (user == null) return; + boolean inGroup = user.getInheritedGroups(user.getQueryOptions()).stream() .anyMatch(g -> g.getName().equals(groupName)); - if (isInGroup) { - this.consumer.accept(playerUUID, false); + if (inGroup) { + this.consumer.accept(uuid, false); } }); }, 1L, TimeUnit.SECONDS); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 682084cbb..58856a6b0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -46,12 +46,12 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { @Override public void delayedInit() { - Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap()); if (this.plugin.isPluginEnabled("LuckPerms")) { luckPermsEventListeners = new LuckPermsEventListeners( plugin.bootstrap(), this::refreshEmojiSuggestions, plugin.scheduler() ); } + Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap()); } @Override @@ -99,7 +99,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { } private void addEmojiSuggestions(Player player) { - List hasPermissions = cachedEmojiSuggestions().parallelStream() + List hasPermissions = cachedEmojiSuggestions().stream() .filter(keyword -> { Emoji emoji = super.emojiMapper.get(keyword); if (emoji == null) return false; From 4480d450efd4a13d9b1a6338eb907e5bc68f2828 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Sat, 12 Apr 2025 04:51:35 +0800 Subject: [PATCH 25/28] =?UTF-8?q?perf(network):=20=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E5=A4=84=E7=90=86=E5=8C=85=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/network/BukkitNetworkManager.java | 12 ++++++++-- .../bukkit/plugin/network/NMSPacketEvent.java | 17 +++++++++++++ .../plugin/network/PacketConsumers.java | 24 +++++++++---------- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 77b5bce77..16954643e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -443,7 +443,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes NMSPacketEvent event = new NMSPacketEvent(packet); onNMSPacketSend(player, event, packet); if (event.isCancelled()) return; - super.write(context, packet, channelPromise); + if (!event.hasNewPacket()) { + super.write(context, packet, channelPromise); + } else { + super.write(context, event.getNewPacket(), channelPromise); + } channelPromise.addListener((p) -> { for (Runnable task : event.getDelayedTasks()) { task.run(); @@ -460,7 +464,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes NMSPacketEvent event = new NMSPacketEvent(packet); onNMSPacketReceive(player, event, packet); if (event.isCancelled()) return; - super.channelRead(context, packet); + if (!event.hasNewPacket()) { + super.channelRead(context, packet); + } else { + super.channelRead(context, event.getNewPacket()); + } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NMSPacketEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NMSPacketEvent.java index bce61806f..3ac92406f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NMSPacketEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NMSPacketEvent.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.plugin.network; import net.momirealms.craftengine.core.util.Cancellable; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -9,8 +10,10 @@ import java.util.Optional; public class NMSPacketEvent implements Cancellable { private boolean cancelled; + private boolean hasNewPacket; private List delayedTasks = null; private final Object packet; + private Object newPacket; public NMSPacketEvent(Object packet) { this.packet = packet; @@ -20,6 +23,20 @@ public class NMSPacketEvent implements Cancellable { return packet; } + public void setNewPacket(Object newPacket) { + hasNewPacket = true; + this.newPacket = newPacket; + } + + public boolean hasNewPacket() { + return hasNewPacket; + } + + @Nullable + public Object getNewPacket() { + return newPacket; + } + public void addDelayedTask(Runnable task) { if (delayedTasks == null) { delayedTasks = new ArrayList<>(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index cd158fe39..abd371a32 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -339,8 +339,8 @@ public class PacketConsumers { return; } + boolean isChanged = false; List newEntries = new MarkedArrayList<>(); - Reflections.field$ClientboundPlayerInfoUpdatePacket$entries.set(packet, newEntries); for (Object entry : entries) { Object mcComponent = FastNMS.INSTANCE.field$ClientboundPlayerInfoUpdatePacket$Entry$displayName(entry); if (mcComponent == null) { @@ -359,6 +359,10 @@ public class PacketConsumers { } Object newEntry = FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket$Entry(entry, ComponentUtils.adventureToMinecraft(component)); newEntries.add(newEntry); + isChanged = true; + } + if (isChanged) { + event.setNewPacket(FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket(enums, newEntries)); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundPlayerInfoUpdatePacket", e); @@ -1769,18 +1773,12 @@ public class PacketConsumers { } if (changed) { - if (VersionHelper.isVersionNewerThan1_20_5()) { - event.setCancelled(true); - Object newPacket = Reflections.constructor$ServerboundEditBookPacket.newInstance( - Reflections.field$ServerboundEditBookPacket$slot.get(packet), - newPages, - newTitle - ); - user.receivePacket(newPacket); - } else { - Reflections.field$ServerboundEditBookPacket$pages.set(packet, newPages); - Reflections.field$ServerboundEditBookPacket$title.set(packet, newTitle); - } + Object newPacket = Reflections.constructor$ServerboundEditBookPacket.newInstance( + Reflections.field$ServerboundEditBookPacket$slot.get(packet), + newPages, + newTitle + ); + event.setNewPacket(newPacket); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ServerboundEditBookPacket", e); From 6b3f873abdc4c2d1a95e9e951680a93bff12e581 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Sat, 12 Apr 2025 05:51:07 +0800 Subject: [PATCH 26/28] =?UTF-8?q?docs(bukkit):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=B4=A1=E7=8C=AE=E8=80=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bukkit/loader/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/bukkit/loader/build.gradle.kts b/bukkit/loader/build.gradle.kts index ab4e6c6c1..7f2a8a975 100644 --- a/bukkit/loader/build.gradle.kts +++ b/bukkit/loader/build.gradle.kts @@ -48,6 +48,7 @@ bukkit { name = "CraftEngine" apiVersion = "1.20" authors = listOf("XiaoMoMi") + contributors = listOf("jhqwqmc", "iqtesterrr") softDepend = listOf("PlaceholderAPI", "WorldEdit", "FastAsyncWorldEdit") foliaSupported = true } From 1e417cf6b47ca8ee38c4cc89327a6db6c6f20287 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 12 Apr 2025 15:53:35 +0800 Subject: [PATCH 27/28] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../permission/LuckPermsEventListeners.java | 20 +++--- .../bukkit/font/BukkitFontManager.java | 70 +++++++++---------- .../plugin/network/BukkitNetworkManager.java | 12 ++-- .../bukkit/plugin/network/NMSPacketEvent.java | 19 +++-- .../plugin/network/PacketConsumers.java | 4 +- .../core/font/AbstractFontManager.java | 41 +++++------ .../craftengine/core/font/FontManager.java | 4 -- 7 files changed, 77 insertions(+), 93 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java index b56a2befe..91731da59 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java @@ -6,6 +6,7 @@ import net.luckperms.api.event.EventSubscription; import net.luckperms.api.event.group.GroupDataRecalculateEvent; import net.luckperms.api.event.user.UserDataRecalculateEvent; import net.luckperms.api.model.user.User; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.scheduler.SchedulerAdapter; import org.bukkit.Bukkit; import org.bukkit.World; @@ -17,25 +18,24 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.logging.Level; public class LuckPermsEventListeners { private final JavaPlugin plugin; private final LuckPerms luckPerms; - private final BiConsumer consumer; - private final SchedulerAdapter scheduler; + private final Consumer playerCallback; private final List> subscriptions = new ArrayList<>(); - public LuckPermsEventListeners(JavaPlugin plugin, BiConsumer consumer, SchedulerAdapter scheduler) { + public LuckPermsEventListeners(JavaPlugin plugin, Consumer playerCallback) { this.plugin = plugin; - this.consumer = consumer; - this.scheduler = scheduler; + this.playerCallback = playerCallback; RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class); if (provider != null) { this.luckPerms = provider.getProvider(); this.registerEventListeners(); } else { - throw new IllegalStateException("Unable to hook into LuckPerms"); + throw new IllegalStateException("Unable to hook LuckPerms"); } } @@ -57,11 +57,13 @@ public class LuckPermsEventListeners { } private void onUserPermissionChange(UserDataRecalculateEvent event) { - this.consumer.accept(event.getUser().getUniqueId(), true); + CraftEngine.instance().scheduler().async().execute(() -> { + this.playerCallback.accept(event.getUser().getUniqueId()); + }); } private void onGroupPermissionChange(GroupDataRecalculateEvent event) { - this.scheduler.asyncLater(() -> { + CraftEngine.instance().scheduler().asyncLater(() -> { String groupName = event.getGroup().getName(); Bukkit.getOnlinePlayers().forEach(player -> { UUID uuid = player.getUniqueId(); @@ -70,7 +72,7 @@ public class LuckPermsEventListeners { boolean inGroup = user.getInheritedGroups(user.getQueryOptions()).stream() .anyMatch(g -> g.getName().equals(groupName)); if (inGroup) { - this.consumer.accept(uuid, false); + this.playerCallback.accept(uuid); } }); }, 1L, TimeUnit.SECONDS); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 58856a6b0..7ccd995dd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -31,8 +31,7 @@ import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.view.AnvilView; import java.lang.reflect.InvocationTargetException; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; public class BukkitFontManager extends AbstractFontManager implements Listener { @@ -47,9 +46,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { @Override public void delayedInit() { if (this.plugin.isPluginEnabled("LuckPerms")) { - luckPermsEventListeners = new LuckPermsEventListeners( - plugin.bootstrap(), this::refreshEmojiSuggestions, plugin.scheduler() - ); + luckPermsEventListeners = new LuckPermsEventListeners(plugin.bootstrap(), this::refreshEmojiSuggestions); } Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap()); } @@ -65,49 +62,46 @@ public class BukkitFontManager extends AbstractFontManager implements Listener { @Override public void delayedLoad() { - List oldCachedEmojiSuggestions = this.oldCachedEmojiSuggestions(); + Collection players = Bukkit.getOnlinePlayers(); + for (Player player : players) { + removeEmojiSuggestions(player); + } super.delayedLoad(); - this.oldCachedEmojiSuggestions.addAll(this.cachedEmojiSuggestions()); - Bukkit.getOnlinePlayers().forEach(player -> { - player.removeCustomChatCompletions(oldCachedEmojiSuggestions); - this.addEmojiSuggestions(player); - }); + for (Player player : players) { + this.addEmojiSuggestions(player, getEmojiSuggestion(player)); + } } @EventHandler(priority = EventPriority.LOWEST) public void onPlayerJoin(PlayerJoinEvent event) { - plugin.scheduler().async().execute(() -> { - Player player = event.getPlayer(); - this.addEmojiSuggestions(player); - }); + plugin.scheduler().async().execute(() -> this.addEmojiSuggestions(event.getPlayer(), getEmojiSuggestion(event.getPlayer()))); } - public void refreshEmojiSuggestions(UUID playerUUID, boolean isAsync) { - if (isAsync) { - plugin.scheduler().async().execute(() -> { - Player player = Bukkit.getPlayer(playerUUID); - if (player == null) return; - player.removeCustomChatCompletions(oldCachedEmojiSuggestions); - this.addEmojiSuggestions(player); - }); - } else { - Player player = Bukkit.getPlayer(playerUUID); - if (player == null) return; - player.removeCustomChatCompletions(oldCachedEmojiSuggestions); - this.addEmojiSuggestions(player); + private void refreshEmojiSuggestions(UUID uuid) { + Player player = Bukkit.getPlayer(uuid); + if (player == null) return; + removeEmojiSuggestions(player); + addEmojiSuggestions(player, getEmojiSuggestion(player)); + } + + private List getEmojiSuggestion(Player player) { + List suggestions = new ArrayList<>(); + for (Emoji emoji : super.emojiList) { + if (emoji.permission() == null || player.hasPermission(Objects.requireNonNull(emoji.permission()))) { + suggestions.addAll(emoji.keywords()); + } } + return suggestions; } - private void addEmojiSuggestions(Player player) { - List hasPermissions = cachedEmojiSuggestions().stream() - .filter(keyword -> { - Emoji emoji = super.emojiMapper.get(keyword); - if (emoji == null) return false; - String permission = emoji.permission(); - return permission == null || player.hasPermission(permission); - }) - .collect(Collectors.toList()); - player.addCustomChatCompletions(hasPermissions); + private void addEmojiSuggestions(Player player, List suggestions) { + player.addCustomChatCompletions(suggestions); + } + + private void removeEmojiSuggestions(Player player) { + if (super.allEmojiSuggestions != null) { + player.removeCustomChatCompletions(super.allEmojiSuggestions); + } } @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 16954643e..0afcc7e55 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -443,10 +443,10 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes NMSPacketEvent event = new NMSPacketEvent(packet); onNMSPacketSend(player, event, packet); if (event.isCancelled()) return; - if (!event.hasNewPacket()) { - super.write(context, packet, channelPromise); + if (event.isUsingNewPacket()) { + super.write(context, event.optionalNewPacket(), channelPromise); } else { - super.write(context, event.getNewPacket(), channelPromise); + super.write(context, packet, channelPromise); } channelPromise.addListener((p) -> { for (Runnable task : event.getDelayedTasks()) { @@ -464,10 +464,10 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes NMSPacketEvent event = new NMSPacketEvent(packet); onNMSPacketReceive(player, event, packet); if (event.isCancelled()) return; - if (!event.hasNewPacket()) { - super.channelRead(context, packet); + if (event.isUsingNewPacket()) { + super.channelRead(context, event.optionalNewPacket()); } else { - super.channelRead(context, event.getNewPacket()); + super.channelRead(context, packet); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NMSPacketEvent.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NMSPacketEvent.java index 3ac92406f..d753f4815 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NMSPacketEvent.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/NMSPacketEvent.java @@ -1,19 +1,18 @@ package net.momirealms.craftengine.bukkit.plugin.network; import net.momirealms.craftengine.core.util.Cancellable; +import org.jetbrains.annotations.NotNull; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; public class NMSPacketEvent implements Cancellable { - private boolean cancelled; - private boolean hasNewPacket; - private List delayedTasks = null; private final Object packet; - private Object newPacket; + private boolean cancelled; + private List delayedTasks = null; + private Object newPacket = null; public NMSPacketEvent(Object packet) { this.packet = packet; @@ -23,17 +22,15 @@ public class NMSPacketEvent implements Cancellable { return packet; } - public void setNewPacket(Object newPacket) { - hasNewPacket = true; + public void replacePacket(@NotNull Object newPacket) { this.newPacket = newPacket; } - public boolean hasNewPacket() { - return hasNewPacket; + public boolean isUsingNewPacket() { + return newPacket != null; } - @Nullable - public Object getNewPacket() { + public Object optionalNewPacket() { return newPacket; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index abd371a32..0d9f63c9a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -362,7 +362,7 @@ public class PacketConsumers { isChanged = true; } if (isChanged) { - event.setNewPacket(FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket(enums, newEntries)); + event.replacePacket(FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket(enums, newEntries)); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundPlayerInfoUpdatePacket", e); @@ -1778,7 +1778,7 @@ public class PacketConsumers { newPages, newTitle ); - event.setNewPacket(newPacket); + event.replacePacket(newPacket); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ServerboundEditBookPacket", e); 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 a8f466d99..d8fc199d4 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 @@ -41,9 +41,9 @@ public abstract class AbstractFontManager implements FontManager { protected Trie emojiKeywordTrie; protected Map tagMapper; protected Map emojiMapper; - // tab补全 - protected final List cachedEmojiSuggestions = new ArrayList<>(); - protected final List oldCachedEmojiSuggestions = new ArrayList<>(); + + protected List emojiList; + protected List allEmojiSuggestions; public AbstractFontManager(CraftEngine plugin) { this.plugin = plugin; @@ -64,17 +64,27 @@ public abstract class AbstractFontManager implements FontManager { this.images.clear(); this.illegalChars.clear(); this.emojis.clear(); - this.cachedEmojiSuggestions.clear(); } @Override - public List cachedEmojiSuggestions() { - return List.copyOf(this.cachedEmojiSuggestions); + public void disable() { + this.unload(); } @Override - public List oldCachedEmojiSuggestions() { - return List.copyOf(this.oldCachedEmojiSuggestions); + public ConfigSectionParser[] parsers() { + return new ConfigSectionParser[] {this.imageParser, this.emojiParser}; + } + + @Override + public void delayedLoad() { + Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> this.illegalChars.addAll(font.codepointsInUse())); + this.buildImageTagTrie(); + this.buildEmojiKeywordsTrie(); + this.emojiList = new ArrayList<>(this.emojis.values()); + this.allEmojiSuggestions = this.emojis.values().stream() + .flatMap(emoji -> emoji.keywords().stream()) + .collect(Collectors.toList()); } @Override @@ -242,19 +252,6 @@ public abstract class AbstractFontManager implements FontManager { return IllegalCharacterProcessResult.not(); } - @Override - public ConfigSectionParser[] parsers() { - return new ConfigSectionParser[] {this.imageParser, this.emojiParser}; - } - - @Override - public void delayedLoad() { - this.oldCachedEmojiSuggestions.clear(); - Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> this.illegalChars.addAll(font.codepointsInUse())); - this.buildImageTagTrie(); - this.buildEmojiKeywordsTrie(); - } - private void buildEmojiKeywordsTrie() { this.emojiMapper = new HashMap<>(); for (Emoji emoji : this.emojis.values()) { @@ -371,8 +368,6 @@ public abstract class AbstractFontManager implements FontManager { TranslationManager.instance().log("warning.config.emoji.lack_keywords", path.toString(), id.toString()); return; } - String keyword = keywords.get(0); - cachedEmojiSuggestions.add(keyword); String content = section.getOrDefault("content", "").toString(); String image = null; if (section.containsKey("image")) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index e685fc7a3..77babec19 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -101,8 +101,4 @@ public interface FontManager extends Manageable { } Map matchTags(String json); - - List cachedEmojiSuggestions(); - - List oldCachedEmojiSuggestions(); } From a991b22735902dbdda1581fd4e0cdcad669568ca Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Sat, 12 Apr 2025 15:53:47 +0800 Subject: [PATCH 28/28] =?UTF-8?q?=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compatibility/permission/LuckPermsEventListeners.java | 3 --- .../momirealms/craftengine/bukkit/font/BukkitFontManager.java | 1 - .../momirealms/craftengine/core/font/AbstractFontManager.java | 2 -- .../net/momirealms/craftengine/core/font/FontManager.java | 4 +++- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java index 91731da59..ecfbb6983 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/permission/LuckPermsEventListeners.java @@ -7,9 +7,7 @@ import net.luckperms.api.event.group.GroupDataRecalculateEvent; import net.luckperms.api.event.user.UserDataRecalculateEvent; import net.luckperms.api.model.user.User; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.plugin.scheduler.SchedulerAdapter; import org.bukkit.Bukkit; -import org.bukkit.World; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; @@ -17,7 +15,6 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; -import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.logging.Level; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java index 7ccd995dd..40bd35005 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/font/BukkitFontManager.java @@ -32,7 +32,6 @@ import org.bukkit.inventory.view.AnvilView; import java.lang.reflect.InvocationTargetException; import java.util.*; -import java.util.stream.Collectors; public class BukkitFontManager extends AbstractFontManager implements Listener { private final BukkitCraftEngine plugin; 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 d8fc199d4..2ccb72aa8 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 @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.font; -import com.google.common.collect.ImmutableMap; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.pack.LoadingSequence; @@ -16,7 +15,6 @@ import org.ahocorasick.trie.Token; import org.ahocorasick.trie.Trie; import org.jetbrains.annotations.NotNull; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; diff --git a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java index 77babec19..59869b59b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/font/FontManager.java @@ -12,7 +12,9 @@ import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; public interface FontManager extends Manageable { Key DEFAULT_FONT = Key.of("minecraft:default");