diff --git a/README.md b/README.md index ae929fa..12b43db 100644 --- a/README.md +++ b/README.md @@ -94,15 +94,15 @@ | `/back` | - | `/back` | `OpenTools.default` | 所有人 | | `/tpaaccept` | - | `/tpaaccept <玩家ID>` | `OpenTools.default` | 所有人 | | `/tpadefuse` | - | `/tpadefuse <玩家ID>` | `OpenTools.default` | 所有人 | +| `/tpaback` | - | `/tpaback` | `OpenTools.default` | 所有人 | | `/PlayerTitle` | `/plt` | `/PlayerTitle help` | `OpenTools.default` | 所有人 | | `/testGUI` | - | `/testGUI KickGUI` | `OpenTools.testGUI` | OP | -| `/OSTReload` | - | `/OSTReload` | `OpenTools.reload` | OP | +| `/OSTReload` | - | `/OSTReload` | `OpenTools.ostreload` | OP | | `/stop` | - | `/stop [提示语]` | `OpenTools.stop` | OP | | `/gamemode` | `/gm` | `/gamemode <模式> [玩家ID]` | `OpenTools.gamemode` | OP | | `/sun` | - | `/sun` | `OpenTools.sun` | OP | | `/setspawn` | - | `/setspawn` | `OpenTools.setspawn` | OP | -| `/day` | - | `/day` | `OpenTools.day` | OP | -| `/vanish` | `/v` | `/vanish [玩家ID]` | `OpenTools.vanish` | OP | +| `/day` | - | `/day` | `OpenTools.day` | OP || `/vanish` | `/v` | `/vanish [玩家ID]` | `OpenTools.vanish` | OP | | `/fly` | - | `/fly [玩家ID]` | `OpenTools.fly` | OP | | `/mute` | - | `/mute <玩家ID>` | `OpenTools.mute` | OP | | `/unmute` | - | `/unmute <玩家ID>` | `OpenTools.unmute` | OP | @@ -138,6 +138,7 @@ | `OpenTools.stop` | OP | /stop | | `OpenTools.gamemode` | OP | /gamemode | | `OpenTools.setspawn` | OP | /setspawn | +| `OpenTools.day` | OP | /day | | `OpenTools.list` | OP | /list | | `OpenTools.testGUI` | OP | /testGUI | @@ -249,6 +250,7 @@ powershell -NoProfile -ExecutionPolicy Bypass -File tools/smoke-test/start-test- | 版本 | 日期 | 变更 | |---|---|---| +| `dev-mc-1.21.11`(分支) | 2026-11-09 | 修复 10 项已知代码问题(禁言生效、/heal 血量恢复、/kick 自我检查、控制台解禁、注册 /tpaback、NPE 防护、屏蔽词包含匹配、PAPI 缺失优雅退出、权限节点统一) | | `2.1.0` | 2026-11-09 | 升级 Bukkit API 至 **1.21.11**;`api-version` 提升至 `1.21`;声明 `depend: [PlaceholderAPI]` 保证加载顺序;补充自动化冒烟测试设施 | | `2.0` | 2025 | 初版(Spigot API 1.20) | @@ -256,18 +258,23 @@ powershell -NoProfile -ExecutionPolicy Bypass -File tools/smoke-test/start-test- ## 已知问题与限制 -> 以下问题为代码现状记录,不影响插件基本使用,欢迎提交 PR 修复。 +以下问题已在 **`dev-mc-1.21.11` 分支修复**: -1. **禁言不生效**:`/mute` 仅将玩家写入配置 `MutePlayerList`,聊天事件未检查该列表。 -2. **`/heal` 实际恢复的是饱食度**(与 `/feed` 相同),且配置键大小写不匹配(`healCommandSetting` vs `HealCommandSetting`),导致默认被禁用。 -3. **`/kick` 无法阻止踢出自己**:自我检查逻辑失效(`player.getName().equals(player.getName())` 恒为真)。 -4. **控制台执行 `/unmute` 无效**:解禁分支误用 `add` 而非 `remove`。 -5. **`/tpaback` 未注册**:类存在但未在 plugin.yml 与主类中注册。 -6. **空指针风险**:玩家未死亡时使用 `/back` 或重生事件、未使用过 TPA 时执行 `/tpaback`、跨世界飞行检查,均可能抛出 NPE。 -7. **状态仅存内存**:TPA 请求、聊天冷却、死亡点、隐身/飞行状态存于静态 HashMap,服务器重启即丢失。 -8. **权限声明不一致**:`/OSTReload` 使用 `OpenTools.reload` 而权限节定义为 `OpenTools.ostreload`;`/day` 的 `OpenTools.day` 未在权限节声明。 -9. **硬依赖退出方式激进**:缺失 PlaceholderAPI 时插件会 `System.exit(0)` 直接结束整个 JVM(现已通过 `depend` 声明保证加载顺序规避)。 -10. **屏蔽词精确匹配**:仅拦截与关键词完全相同的整句消息。 +1. ✅ 禁言不生效 → 聊天事件现在检查 `MutePlayerList`,被禁言玩家无法发言。 +2. ✅ `/heal` 恢复饱食度 + 配置键大小写不匹配 → 现在恢复生命值并使用正确的 `HealCommandSetting` 配置键。 +3. ✅ `/kick` 无法阻止踢出自己 → 自我检查改为与命令发送者比较。 +4. ✅ 控制台 `/unmute` 无效 → 解禁分支由 `add` 修正为 `remove`。 +5. ✅ `/tpaback` 未注册 → 已注册命令与权限(返回 TPA 传送前位置)。 +6. ✅ 多处空指针风险(重生事件、/tpaback、跨世界飞行检查)→ 已加空值保护。 +7. ✅ 跨世界飞行权限节点错误(`OpenSourceTools.FLY`)→ 改为正确的 `OpenTools.fly`。 +8. ✅ 屏蔽词仅整句精确匹配 → 改为包含匹配(忽略大小写)。 +9. ✅ 缺失 PlaceholderAPI 时 `System.exit(0)` 强杀整个 JVM → 改为优雅禁用插件。 +10. ✅ 权限声明不一致(OSTReload/day)→ 已统一权限节点。 + +### 仍存在的限制 + +- **状态仅存内存**:TPA 请求、聊天冷却、死亡点、隐身/飞行状态保存在内存中,服务器重启后丢失(如需持久化请提 Issue 讨论方案)。 +- `/kick` 踢人 GUI 与命令的自我踢出/管理员保护逻辑按现有设计保留。 --- diff --git a/src/main/java/top/gtb520/tools/main/PlayerChangedWorld_Event.java b/src/main/java/top/gtb520/tools/main/PlayerChangedWorld_Event.java index 7bd39a1..e629d7f 100644 --- a/src/main/java/top/gtb520/tools/main/PlayerChangedWorld_Event.java +++ b/src/main/java/top/gtb520/tools/main/PlayerChangedWorld_Event.java @@ -9,8 +9,9 @@ public class PlayerChangedWorld_Event implements Listener { @EventHandler public void On_Event(PlayerChangedWorldEvent event) { Player player = event.getPlayer(); - if (player.hasPermission("OpenSourceTools.FLY")) { - if (unity.getStringTemp(player.getName() + "_Fly").equals("已开启")) { + if (player.hasPermission("OpenTools.fly")) { + String Fly = unity.getStringTemp(player.getName() + "_Fly"); + if (Fly != null && Fly.equals("已开启")) { player.setAllowFlight(true); } } diff --git a/src/main/java/top/gtb520/tools/main/PlayerChat_Event.java b/src/main/java/top/gtb520/tools/main/PlayerChat_Event.java index 18065db..f09f029 100644 --- a/src/main/java/top/gtb520/tools/main/PlayerChat_Event.java +++ b/src/main/java/top/gtb520/tools/main/PlayerChat_Event.java @@ -16,6 +16,15 @@ public class PlayerChat_Event implements Listener { public void PlayerChat(AsyncPlayerChatEvent event) { Player player = event.getPlayer(); String Message = event.getMessage(); + + // 禁言检查 + List MutePlayerList = main.instance.getConfig().getStringList("MutePlayerList"); + if (MutePlayerList.contains(player.getName())) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', "&c你已被禁言,无法发送消息!")); + event.setCancelled(true); + return; + } + if (player.isOp()) { if (main.instance.getConfig().getBoolean("ColorChatSetting.AllowAdminUsedColorChat")) { event.setMessage(unity.ColorMessage(Message)); @@ -34,7 +43,7 @@ public class PlayerChat_Event implements Listener { List keywords = new ArrayList<>(main.instance.getConfig().getStringList("AntiKeyWords")); for (String keyword : keywords) { - if (Message.toUpperCase().contentEquals(keyword)) { + if (Message.toUpperCase().contains(keyword.toUpperCase())) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', "&d&l检测到屏蔽词,已阻止发送\n屏蔽词内容:" + keyword)); event.setCancelled(true); for (Player player1 : Bukkit.getOnlinePlayers()) { diff --git a/src/main/java/top/gtb520/tools/main/PlayerReSpawn_Event.java b/src/main/java/top/gtb520/tools/main/PlayerReSpawn_Event.java index 0166e67..aa6fdb2 100644 --- a/src/main/java/top/gtb520/tools/main/PlayerReSpawn_Event.java +++ b/src/main/java/top/gtb520/tools/main/PlayerReSpawn_Event.java @@ -11,6 +11,10 @@ public class PlayerReSpawn_Event implements Listener { public void On_Event(PlayerRespawnEvent event) { Player player = event.getPlayer(); Location DiedLocation = unity.getLocationTempTemp(player.getName() + "_Back"); - player.sendMessage(unity.ColorMessage("&a&l您的死亡位置是X:" + DiedLocation.getBlockX() + " Y:" + DiedLocation.getBlockY() + "_DiedY" + " Z:" + DiedLocation.getBlockZ())); + if (DiedLocation == null) { + player.sendMessage(unity.ColorMessage("&a&l你还没有死亡记录")); + return; + } + player.sendMessage(unity.ColorMessage("&a&l您的死亡位置是 X:" + DiedLocation.getBlockX() + " Y:" + DiedLocation.getBlockY() + " Z:" + DiedLocation.getBlockZ())); } } diff --git a/src/main/java/top/gtb520/tools/main/SuperKick_Command.java b/src/main/java/top/gtb520/tools/main/SuperKick_Command.java index 8fe756e..ae9153d 100644 --- a/src/main/java/top/gtb520/tools/main/SuperKick_Command.java +++ b/src/main/java/top/gtb520/tools/main/SuperKick_Command.java @@ -22,7 +22,7 @@ public class SuperKick_Command implements CommandExecutor { if (Objects.requireNonNull(player).isOnline()) { if (sender instanceof Player) { if (!player.isOp()) { - if (!player.getName().equals(player.getName())) { + if (!player.getName().equals(sender.getName())) { player.kickPlayer(ChatColor.translateAlternateColorCodes('&', "&d你被踢出服务器")); } else { sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&d你不能踢出自己")); @@ -48,7 +48,7 @@ public class SuperKick_Command implements CommandExecutor { if (Objects.requireNonNull(player).isOnline()) { if (sender instanceof Player) { if (!player.isOp()) { - if (!player.getName().equals(player.getName())) { + if (!player.getName().equals(sender.getName())) { player.kickPlayer(ChatColor.translateAlternateColorCodes('&', message)); } else { sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&d你不能踢出自己")); diff --git a/src/main/java/top/gtb520/tools/main/heal_Command.java b/src/main/java/top/gtb520/tools/main/heal_Command.java index 05fb488..96dfcb4 100644 --- a/src/main/java/top/gtb520/tools/main/heal_Command.java +++ b/src/main/java/top/gtb520/tools/main/heal_Command.java @@ -14,7 +14,7 @@ public class heal_Command implements CommandExecutor { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (sender instanceof Player) { Player player = (Player) sender; - String CommandName = "heal"; + String CommandName = "Heal"; if (player.isOp()) { if (!instance.getConfig().getBoolean(CommandName + "CommandSetting.AllowAdminUsed" + CommandName + "Command")) { sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&d这个服务器禁止管理员使用heal命令!")); @@ -26,8 +26,7 @@ public class heal_Command implements CommandExecutor { return false; } } - player.setSaturation(20); - player.setFoodLevel(20); + player.setHealth(player.getMaxHealth()); sender.sendMessage(ChatColor.translateAlternateColorCodes('&',"&a你的血量被神奇的恢复了")); }else { sender.sendMessage(ChatColor.translateAlternateColorCodes('&',"&d这个命令只有玩家才能执行")); diff --git a/src/main/java/top/gtb520/tools/main/main.java b/src/main/java/top/gtb520/tools/main/main.java index e1fd457..7c1c2c4 100644 --- a/src/main/java/top/gtb520/tools/main/main.java +++ b/src/main/java/top/gtb520/tools/main/main.java @@ -40,14 +40,9 @@ public final class main extends JavaPlugin implements Listener { if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") == null) { unity.GetLoggerPlus("&d找不到PlaceholderAPI,请安装PlaceholderAPI后才能使用本插件"); - Bukkit.getPluginManager().disablePlugins(); - try { - Thread.sleep(2300); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - System.exit(0); - } else if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + Bukkit.getPluginManager().disablePlugin(this); + return; + } else { unity.GetLoggerPlus("&2已检测到PlaceholderAPI前置"); } @@ -98,6 +93,7 @@ public final class main extends JavaPlugin implements Listener { Objects.requireNonNull(getCommand("tpa")).setExecutor(new Tpa_Command()); Objects.requireNonNull(getCommand("tpaaccept")).setExecutor(new tpaAccept_Command()); Objects.requireNonNull(getCommand("tpadefuse")).setExecutor(new tpaDefuse_Command()); + Objects.requireNonNull(getCommand("tpaback")).setExecutor(new tpaback_Command()); Objects.requireNonNull(getCommand("vanish")).setExecutor(new vanish_Command()); Objects.requireNonNull(getCommand("fly")).setExecutor(new fly_Command()); Objects.requireNonNull(getCommand("mute")).setExecutor(new mute_Command()); diff --git a/src/main/java/top/gtb520/tools/main/tpaback_Command.java b/src/main/java/top/gtb520/tools/main/tpaback_Command.java index 78f1522..9322a39 100644 --- a/src/main/java/top/gtb520/tools/main/tpaback_Command.java +++ b/src/main/java/top/gtb520/tools/main/tpaback_Command.java @@ -13,6 +13,10 @@ public class tpaback_Command implements CommandExecutor { if (sender instanceof Player) { Player player = (Player) sender; Location BackLocation = unity.getLocationTempTemp(player.getName() + "_Tpa"); + if (BackLocation == null) { + player.sendMessage(unity.ColorMessage("&d你还没有传送记录,无法返回!")); + return false; + } player.teleport(BackLocation); player.sendMessage(unity.ColorMessage("&a传送成功!")); } diff --git a/src/main/java/top/gtb520/tools/main/unmute_Command.java b/src/main/java/top/gtb520/tools/main/unmute_Command.java index 99a13da..a02ae74 100644 --- a/src/main/java/top/gtb520/tools/main/unmute_Command.java +++ b/src/main/java/top/gtb520/tools/main/unmute_Command.java @@ -56,10 +56,10 @@ public class unmute_Command implements CommandExecutor { for (String MutePlayers : MutePlayerList) { if (MutePlayers.equals(Objects.requireNonNull(player).getName())) { - MutePlayerList.add(Objects.requireNonNull(player).getName()); + MutePlayerList.remove(Objects.requireNonNull(player).getName()); main.instance.getConfig().set("MutePlayerList", MutePlayerList); unity.SaveAndReloadConfig(); - sender.sendMessage(ChatColor.GREEN + "你解除了 控制台 的禁言"); + sender.sendMessage(ChatColor.GREEN + "你解除了 " + player.getName() + " 的禁言"); player.sendMessage(ChatColor.RED + "你被 控制台 解除禁言了!"); return false; } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index dea2579..c9a71ae 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -35,13 +35,17 @@ commands: description: 拒绝传送请求 permission: OpenTools.default permission-message: §c§l权限不足 + tpaback: + description: 返回传送前位置 + permission: OpenTools.default + permission-message: §c§l权限不足 testGUI: description: 测试GUI命令 permission: OpenTools.testGUI permission-message: §c§l权限不足 OSTReload: description: 重载插件 - permission: OpenTools.reload + permission: OpenTools.ostreload permission-message: §c§l权限不足 stop: description: 关闭服务器 @@ -112,7 +116,7 @@ permissions: description: fly命令的权限 default: op OpenTools.vanish: - description: unmute命令的权限 + description: vanish命令的权限 default: op OpenTools.ostreload: description: ostreload命令的权限 @@ -132,6 +136,9 @@ permissions: OpenTools.list: description: list命令的权限 default: op + OpenTools.day: + description: day命令的权限 + default: op OpenTools.testGUI: description: testGUI命令的权限 default: op \ No newline at end of file