fix: 修复已知代码问题 - 禁言生效/heal血量恢复/kick自我检查/控制台解禁/注册tpaback/NPE防护/屏蔽词包含匹配/PAPI缺失优雅退出/权限节点统一

This commit is contained in:
database-mysql committed 2026-11-09 08:01:18 +08:00
1 parent 6c7d7fedcb
commit 9d262330d4
10 files changed
+62 -35

No files matched your search

+21 -14
View File
@@ -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 与命令的自我踢出/管理员保护逻辑按现有设计保留。
---
@@ -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);
}
}
@@ -16,6 +16,15 @@ public class PlayerChat_Event implements Listener {
public void PlayerChat(AsyncPlayerChatEvent event) {
Player player = event.getPlayer();
String Message = event.getMessage();
// 禁言检查
List<String> 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<String> 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()) {
@@ -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()));
}
}
@@ -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你不能踢出自己"));
@@ -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这个命令只有玩家才能执行"));
@@ -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());
@@ -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传送成功!"));
}
@@ -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;
}
+9 -2
View File
@@ -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