From ec36f7ce2058cdaf1b227ad58d922178e608a2dc Mon Sep 17 00:00:00 2001 From: ApliNi Date: Sun, 21 Jan 2024 13:15:57 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=8F=92=E4=BB=B6,=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=80=A7=E8=83=BD,=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/aplini/usetranslatednames/Cli.java | 24 --- .../aplini/usetranslatednames/Enum/Cli.java | 55 +++++++ .../usetranslatednames/Enum/TransVar.java | 6 + .../aplini/usetranslatednames/Enum/Word.java | 25 +++ .../UseTranslatedNames.java | 153 ++++++++++++------ .../java/aplini/usetranslatednames/Util.java | 36 ++--- src/main/resources/config.yml | 30 +++- src/main/resources/words.yml | 15 ++ 8 files changed, 241 insertions(+), 103 deletions(-) delete mode 100644 src/main/java/aplini/usetranslatednames/Cli.java create mode 100644 src/main/java/aplini/usetranslatednames/Enum/Cli.java create mode 100644 src/main/java/aplini/usetranslatednames/Enum/TransVar.java create mode 100644 src/main/java/aplini/usetranslatednames/Enum/Word.java create mode 100644 src/main/resources/words.yml diff --git a/src/main/java/aplini/usetranslatednames/Cli.java b/src/main/java/aplini/usetranslatednames/Cli.java deleted file mode 100644 index 91d846e..0000000 --- a/src/main/java/aplini/usetranslatednames/Cli.java +++ /dev/null @@ -1,24 +0,0 @@ -package aplini.usetranslatednames; - -import java.util.Map; - -import static aplini.usetranslatednames.Util.SEL; - -public class Cli { - public int inspectLength = 9999999; - public String get; - public String set; - public String displayPlace; - - // 从 getConfig 中转换数据 - public Cli setConfig(Map li){ - // 必选变量 - this.inspectLength = (int) li.get("inspectLength"); - this.get = (String) li.get("get"); - this.set = (String) li.get("set"); - // 可选变量 - this.displayPlace = (String) SEL(li.get("displayPlace"), ""); - - return this; - } -} diff --git a/src/main/java/aplini/usetranslatednames/Enum/Cli.java b/src/main/java/aplini/usetranslatednames/Enum/Cli.java new file mode 100644 index 0000000..a686e47 --- /dev/null +++ b/src/main/java/aplini/usetranslatednames/Enum/Cli.java @@ -0,0 +1,55 @@ +package aplini.usetranslatednames.Enum; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static aplini.usetranslatednames.Util.SEL; + +public class Cli { + // 必选变量 + public int inspectLength; + public String get; + public String set; + // 可选变量 + public String permission; + public String displayPlace; + + + // 这个配置启用了哪些功能 + public boolean enTransVar = false; // 翻译功能 + public Matcher dataTransVar; + + public boolean enWordReplace = false; // 词替换 + public Matcher dataWordReplace; + + public boolean enRegExpReplace = false; // 正则表达式替换 + + + // 从 getConfig 中转换数据 + public Cli setConfig(Map li){ + // 必选变量 + this.inspectLength = (int) li.get("inspectLength"); + this.get = (String) li.get("get"); + this.set = (String) li.get("set"); + // 可选变量 + this.permission = (String) SEL(li.get("permission"), ""); + this.displayPlace = (String) SEL(li.get("displayPlace"), ""); + + + // 这个配置启用了哪些功能 + + // 翻译功能 + this.dataTransVar = Pattern.compile("_\\$(\\d+):(TranslatedName|ItemType)_").matcher(this.set); + this.enTransVar = this.dataTransVar.find(); + + // 词替换 + this.dataWordReplace = Pattern.compile("_\\$(\\d+):Words:([^_]+)_").matcher(this.set); + this.enWordReplace = this.dataWordReplace.find(); + + // 正则表达式替换 + this.enRegExpReplace = Pattern.compile("_\\$\\d+_").matcher(this.set).find(); + + return this; + } +} diff --git a/src/main/java/aplini/usetranslatednames/Enum/TransVar.java b/src/main/java/aplini/usetranslatednames/Enum/TransVar.java new file mode 100644 index 0000000..ba48230 --- /dev/null +++ b/src/main/java/aplini/usetranslatednames/Enum/TransVar.java @@ -0,0 +1,6 @@ +package aplini.usetranslatednames.Enum; + +public class TransVar { + public String transName; + public String itemType; +} diff --git a/src/main/java/aplini/usetranslatednames/Enum/Word.java b/src/main/java/aplini/usetranslatednames/Enum/Word.java new file mode 100644 index 0000000..83830d2 --- /dev/null +++ b/src/main/java/aplini/usetranslatednames/Enum/Word.java @@ -0,0 +1,25 @@ +package aplini.usetranslatednames.Enum; + +import java.util.Map; + +import static aplini.usetranslatednames.Util.SEL; + +public class Word { + // 必选变量 + public String get; + public String set; + // 可选变量 + public String lang; + + + // 从 Map 中转换数据 + public Word setConfig(Map li){ + // 必选变量 + this.get = (String) li.get("get"); + this.set = (String) li.get("set"); + // 可选变量 + this.lang = (String) SEL(li.get("lang"), ""); + + return this; + } +} diff --git a/src/main/java/aplini/usetranslatednames/UseTranslatedNames.java b/src/main/java/aplini/usetranslatednames/UseTranslatedNames.java index baea5d7..74aceea 100644 --- a/src/main/java/aplini/usetranslatednames/UseTranslatedNames.java +++ b/src/main/java/aplini/usetranslatednames/UseTranslatedNames.java @@ -1,5 +1,7 @@ package aplini.usetranslatednames; +import aplini.usetranslatednames.Enum.Cli; +import aplini.usetranslatednames.Enum.Word; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.events.ListenerPriority; @@ -11,29 +13,32 @@ import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.server.ServerLoadEvent; import org.bukkit.plugin.java.JavaPlugin; +import java.io.File; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static aplini.usetranslatednames.Util.SEL; import static aplini.usetranslatednames.Util.toTranslatedName; public final class UseTranslatedNames extends JavaPlugin implements CommandExecutor, TabExecutor, Listener { // 调试模式 private boolean _debug = false; + // 监听器模式 + boolean listeningMode = true; + // 词配置 Map<组名.词 or 组名.语言.词, 词配置> + HashMap words; // 配置文件 List list = new ArrayList<>(); @Override public void onEnable() { - saveDefaultConfig(); - getConfig(); loadConfig(); Util.load(this); @@ -53,16 +58,23 @@ public void onEnable() { ){ @Override public void onPacketSending(PacketEvent event){ + + Player player = event.getPlayer(); + // 获取消息 JSON - // String json = event.getPacket().getStrings().read(0); // 1.20.4 - - String json = event.getPacket().getChatComponents().read(0).getJson(); // 1.20.4 + + String json; + if(listeningMode){ + // 1.20.4 + + json = event.getPacket().getChatComponents().read(0).getJson(); + }else{ + // 1.20.4 - + json = event.getPacket().getStrings().read(0); + } if(json == null) return; - // 处理重复的消息 - if(_debug){ - getLogger().info("[DEBUG] [Player: "+ event.getPlayer().getName() +"] [Length: "+ json.length() +"]: -------"); + getLogger().info("[DEBUG] [Player: "+ event.getPlayer().getName() +" (Lang: "+ player.getLocale() +")] [Length: "+ json.length() +"]: -------"); getLogger().info(" - [get]: "+ json); } @@ -70,6 +82,11 @@ public void onPacketSending(PacketEvent event){ for(Cli cli : list){ boolean ok = false; + // 如果权限不为空 且 玩家没有该权限, 则不进行替换 + if(!cli.permission.isEmpty() && !player.hasPermission(cli.permission)){ + continue; + } + // 防止处理过长的消息 if(json.length() > cli.inspectLength){ continue; @@ -84,24 +101,41 @@ public void onPacketSending(PacketEvent event){ // 匹配到的完整字符串 String jsonFrame = cli.set; if(jsonFrame.isEmpty()){ // 如果为空则仅取消发送它 - continue; + ok = true; + break; } String oldJson = matcher.group(0); // 处理翻译变量 _$1:ItemType_, _$1:TranslatedName_ - Matcher matcher2 = Pattern.compile("_\\$(\\d+):(TranslatedName|ItemType)_").matcher(jsonFrame); - while(matcher2.find()){ - String var = matcher.group(Integer.parseInt(matcher2.group(1))); - if(matcher2.group(2).equals("TranslatedName")){ - jsonFrame = jsonFrame.replace(matcher2.group(), toTranslatedName(var)[1]); - }else{ - jsonFrame = jsonFrame.replace(matcher2.group(), toTranslatedName(var)[0]); + if(cli.enTransVar){ + cli.dataTransVar.reset(); + while(cli.dataTransVar.find()){ + String var = matcher.group(Integer.parseInt(cli.dataTransVar.group(1))); + if(cli.dataTransVar.group(2).equals("TranslatedName")){ + jsonFrame = jsonFrame.replace(cli.dataTransVar.group(), toTranslatedName(var).transName); + }else{ + jsonFrame = jsonFrame.replace(cli.dataTransVar.group(), toTranslatedName(var).itemType); + } } } + // 处理词替换 _$1:Words:xxx_ + if(cli.enWordReplace){ + cli.dataWordReplace.reset(); + while(cli.dataWordReplace.find()){ + String var = matcher.group(Integer.parseInt(cli.dataWordReplace.group(1))); + Word word = (Word) SEL( + words.get(cli.dataWordReplace.group(2) +"."+ player.getLocale() +"."+ var), + words.get(cli.dataWordReplace.group(2) +".."+ var)); + if(word != null){ + jsonFrame = jsonFrame.replace(cli.dataWordReplace.group(), word.set); + } + } + } + + // 处理正则变量 _$1_ - Matcher matcher3 = Pattern.compile("_\\$\\d+_").matcher(jsonFrame); - if(matcher3.find()){ + if(cli.enRegExpReplace){ int matcherLength = matcher.groupCount(); for(int i = 1; i <= matcherLength; i++){ jsonFrame = jsonFrame.replace("_$" + i + "_", matcher.group(i)); @@ -111,11 +145,9 @@ public void onPacketSending(PacketEvent event){ // 替换原文本中的旧 JSON, 重新发送给玩家 jsonFrame = json.replace(oldJson, jsonFrame); - jsonFrame = jsonFrame.replaceAll("^\\{", "{\"utn\":\"utn\","); if(_debug){ getLogger().info(" - [set]: "+ jsonFrame); } - Player player = event.getPlayer(); // 处理显示位置 if(cli.displayPlace.equals("ACTION_BAR")){ player.spigot().sendMessage(ChatMessageType.ACTION_BAR, ComponentSerializer.parse(jsonFrame)); @@ -132,30 +164,59 @@ public void onPacketSending(PacketEvent event){ getLogger().info("UseTranslatedNames 已启动"); } - public void loadConfig(){ - list = new ArrayList<>(); - for(Map li : getConfig().getMapList("list")){ - list.add(new Cli().setConfig(li)); - } - } + // 加载配置文件 + public long loadConfig(){ + long _startTime = System.nanoTime(); // 记录运行时间 - // 检查配置版本 - public void CheckConfigVersion(){ + saveResource("config.yml", false); + saveResource("words.yml", false); + reloadConfig(); + + // 检查配置版本 if(getConfig().getInt("configVersion") != 3){ getLogger().warning("配置版本不匹配, 可能无法正常运行, 请更新或重建配置"); getLogger().warning("配置版本不匹配, 可能无法正常运行, 请更新或重建配置"); getLogger().warning("配置版本不匹配, 可能无法正常运行, 请更新或重建配置"); } - } - @EventHandler // 服务器启动完成事件 - public void onServerLoad(ServerLoadEvent event) { - // 检查配置版本 - CheckConfigVersion(); + // 监听器模式 + listeningMode = getConfig().getBoolean("dev.listeningMode"); + + // 处理词替换配置 + words = new HashMap<>(); + Map wordsConfig = Objects.requireNonNull( + YamlConfiguration.loadConfiguration(new File(getDataFolder(), "words.yml")) + .getConfigurationSection("words")).getValues(false); + for(String groupName : wordsConfig.keySet()){ + List wordList = (List) wordsConfig.get(groupName); + for(Object _word : wordList){ + Word word = new Word().setConfig((Map) _word); + // 'groupName.zh_cn.word' or 'groupName..word' + words.put(groupName +"."+ word.lang +"."+ word.get, word); + } + } + + // 处理替换配置表 + list = new ArrayList<>(); + for(Map li : getConfig().getMapList("list")){ + list.add(new Cli().setConfig(li)); + } + + return Math.round((System.nanoTime() - _startTime) / 1_000_000.0); } - // 执行指令 - @Override + + @Override // 指令补全 + public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { + if(args.length == 1){ + List list = new ArrayList<>(); + list.add("reload"); // 重载配置 + list.add("debug"); // 调试模式 + return list; + } + return null; + } + @Override // 执行指令 public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { // 默认输出插件信息 @@ -169,33 +230,19 @@ public boolean onCommand(CommandSender sender, Command command, String label, St // 重载配置 else if(args[0].equals("reload")){ - reloadConfig(); - loadConfig(); - sender.sendMessage("UseTranslatedNames 已完成重载"); - CheckConfigVersion(); + long time = loadConfig(); + sender.sendMessage("[UTN] 已完成重载, 耗时: "+ time +" 毫秒"); return true; } // 调试模式 else if(args[0].equals("debug")){ _debug = ! _debug; - sender.sendMessage("UseTranslatedNames 调试模式: "+ _debug); + sender.sendMessage("[UTN] 调试模式: "+ _debug); return true; } // 返回 false 时, 玩家将收到命令不存在的错误 return false; } - - // 指令补全 - @Override - public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { - if(args.length == 1){ - List list = new ArrayList<>(); - list.add("reload"); // 重载配置 - list.add("debug"); // 调试模式 - return list; - } - return null; - } } diff --git a/src/main/java/aplini/usetranslatednames/Util.java b/src/main/java/aplini/usetranslatednames/Util.java index bb56909..868f604 100644 --- a/src/main/java/aplini/usetranslatednames/Util.java +++ b/src/main/java/aplini/usetranslatednames/Util.java @@ -1,57 +1,58 @@ package aplini.usetranslatednames; +import aplini.usetranslatednames.Enum.TransVar; import org.bukkit.Material; import org.bukkit.entity.EntityType; import java.util.HashMap; -import java.util.Map; public class Util { // 遍历所有实体和物品 - private static final Map enumEntity = new HashMap<>(); - private static final Map enumBlock = new HashMap<>(); + private static final HashMap enumEntity = new HashMap<>(); + private static final HashMap enumBlock = new HashMap<>(); static void load(UseTranslatedNames plugin) { - plugin.getLogger().info("加载物品列表..."); - for (EntityType value : EntityType.values()) { + // 遍历服务器中的物品和实体 + for(EntityType value : EntityType.values()){ if(value.isAlive()){ enumEntity.put(String.valueOf(value.getKey()), value); } } - for (Material value : Material.values()) { + for(Material value : Material.values()){ if(value.isBlock()){ enumBlock.put(String.valueOf(value.getKey()), value); } } + plugin.getLogger().info("已加载 "+ enumEntity.size() +" 个实体和 "+ enumBlock.size() +" 个物品"); } // 将实体/物品/方块名转换为翻译使用的键 - public static String[] toTranslatedName(String itemName){ - String[] arr = new String[2]; - // String[0] = 匹配到的类型 entity, item(包括block). 用于 JSON hoverEvent - // String[1] = 用于 JSON translate 的名称 + public static TransVar toTranslatedName(String itemName){ + TransVar transVar = new TransVar(); + // itemType = 匹配到的类型 entity, item(包括block). 用于 JSON hoverEvent + // transName = 用于 JSON translate 的名称 // 实体列表 if(enumEntity.containsKey("minecraft:"+ itemName)){ - arr[0] = "show_entity"; - arr[1] = "entity.minecraft."+ itemName; + transVar.itemType = "show_entity"; + transVar.transName = "entity.minecraft."+ itemName; } // 方块列表 else if(enumBlock.containsKey("minecraft:"+ itemName)){ - arr[0] = "show_item"; - arr[1] = "block.minecraft."+ itemName; + transVar.itemType = "show_item"; + transVar.transName = "block.minecraft."+ itemName; } // 物品列表 else{ - arr[0] = "show_item"; - arr[1] = "item.minecraft."+ itemName; + transVar.itemType = "show_item"; + transVar.transName = "item.minecraft."+ itemName; } - return arr; + return transVar; } // 如果 in1 为空则选择 in2, 否则选择 in1 @@ -61,7 +62,6 @@ public static Object SEL(Object in1, Object in2) { } return in1; } - } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 5043105..bb0b3ee 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,11 @@ configVersion: 3 -list: + +dev: + # true = 适用于 1.20.4 版本的方法 (需要 ProtocolLib 5.2.0 + # false = 适用于更低版本的旧方法 + listeningMode: true + +list: # 替换列表 # 翻译实体名 :: 24.03/m 前 #**苦力怕** 破坏 草方块 - inspectLength: 1024 @@ -26,14 +32,22 @@ list: ],"color":"#31b0e8","text":""} - # 所有可用配置 - - inspectLength: 1024 # 检查消息长度是否小于此值 - # 使用正则表达式匹配 - get: '^\{"text":"","extra":\["Missing required argument (\d)"\]\}$' + # [示例] 所有可用配置 + # [可选, 默认所有玩家] 对拥有该权限的玩家处理这条消息 + - permission: 'minecraft.command' + # [必选] 检查消息长度是否小于此值 + inspectLength: 64 + # [必选] 使用正则表达式匹配 + get: '^\{"text":"","extra":\["Missing required argument (\d+)"\]\}$' + # [必选] 将消息替换为 # _$1_ = 正则匹配到的变量 1, 也可以是 `_$2_` (第 2 个变量)... # _$1:ItemType_ = 将 _$1_ 用于获取物品类型, 提供给 JSON hoverEvent 使用的物品类型 show_entity, show_item(block) - # _$1:TranslatedName_ = 将 _$1_ 用于名称翻译, 返回与语言路径对应的 KEY, 例如 `entity.minecraft.allay` - set: '{"text":"§bIpacEL §f> §b此指令需要至少 §a_$1_ §b个参数"}' + # _$1:TranslatedName_ = 将 _$1_ 用于名称翻译, 返回与语言路径对应的 KEY, 例如 `entity.minecraft.allay` + # _$1:Words:组名_ = 将 _$1_ 用于词替换, 需要配置 words.yml + # 其他示例: # set: >- # YAML 语法中使用 `>-` 可以编写换行的文本, 效果如上 # set: '' # 如果为空, 则取消发送这条消息 - displayPlace: 'ACTION_BAR' # 将消息显示在操作栏 (物品栏上面) + set: '{"text":"§bIpacEL §f> §b此指令需要至少§a_$1:Words:中文数字_个参数"}' + # [可选, 默认聊天栏] 将消息显示在操作栏 (物品栏上面) + displayPlace: 'ACTION_BAR' + diff --git a/src/main/resources/words.yml b/src/main/resources/words.yml new file mode 100644 index 0000000..82732b2 --- /dev/null +++ b/src/main/resources/words.yml @@ -0,0 +1,15 @@ +# 词替换配置文件 +words: + # 创建一个组, 组名不应包含下划线 "_" + 中文数字: + # 添加一个词替换 + # 如果多个词的配置重复, 将会被忽略 + - get: '1' # [必选] 需要匹配的词 + set: '一' # [必选] 替换为 + lang: 'zh_cn' # [可选, 默认直接替换] 当客户端语言与其匹配时进行替换 + - get: '2' + set: '二' + lang: 'zh_cn' + - get: '3' + set: '三' + lang: 'zh_cn' \ No newline at end of file