From 8b147b25315eb5e149804d1e3c30c347fb8746f4 Mon Sep 17 00:00:00 2001 From: youpaishidifu Date: Sat, 4 Jul 2026 23:43:55 +0800 Subject: [PATCH] =?UTF-8?q?@2026-07-04=2023:40=20Java=E4=BF=AE=E5=A4=8D:?= =?UTF-8?q?=201)=20illegal=5Fdomains.txt=20=E7=83=AD=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=81=A2=E5=A4=8D-=E6=AF=8F=E6=AC=A1=E7=8E=A9=E5=AE=B6?= =?UTF-8?q?=E5=8A=A0=E5=85=A5=E6=97=B6=E9=87=8D=E8=AF=BB=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=202)=20N+1=E9=AA=8C=E8=AF=81=E7=A0=81?= =?UTF-8?q?=E6=9C=BA=E5=88=B6-pendingMessages=E6=94=B9=E4=B8=BAList?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E5=88=97=E8=A1=A8,=E7=AD=94=E5=AF=B9?= =?UTF-8?q?=E7=AC=ACN=E9=A2=98=E9=87=8A=E6=94=BEN=E6=9D=A1=E6=B6=88?= =?UTF-8?q?=E6=81=AF=203)=20=E9=9D=9E=E6=B3=95=E5=9F=9F=E5=90=8DKick?= =?UTF-8?q?=E6=8A=91=E5=88=B6join/quit=E6=B6=88=E6=81=AF=204)=20onEnable?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E9=87=8A=E6=94=BEillegal=5Fdomains.txt?= =?UTF-8?q?=E5=88=B0=E6=8F=92=E4=BB=B6=E7=9B=AE=E5=BD=95=205)=20=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E6=89=93=E5=8C=85=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/Sdf1_login/ChatFilterManager.java | 35 +++++++++---- src/main/java/Sdf1_login/Main.java | 50 +++++++++++++------ 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/src/main/java/Sdf1_login/ChatFilterManager.java b/src/main/java/Sdf1_login/ChatFilterManager.java index 5bc7b8a..571d084 100644 --- a/src/main/java/Sdf1_login/ChatFilterManager.java +++ b/src/main/java/Sdf1_login/ChatFilterManager.java @@ -450,18 +450,27 @@ public class ChatFilterManager { return VerificationResult.FAILED; } - // ★ 临时缓存玩家未验证的消息,等验证通过后由插件代为广播 - public final Map pendingMessages = new ConcurrentHashMap<>(); + // ★ 临时缓存玩家未验证的消息(N+1 机制:缓存列表,答对第N题后释放N条) + public final Map> pendingMessages = new ConcurrentHashMap<>(); /** 缓存玩家消息,等验证通过时代为广播 */ public void cachePendingMessage(String playerName, String message) { - pendingMessages.put(playerName, message); + pendingMessages.computeIfAbsent(playerName, k -> new ArrayList<>()).add(message); } - /** 广播缓存的消息给全服 */ - public void broadcastCachedMessage(String playerName, String message) { - pendingMessages.remove(playerName); // 广播后清除缓存 - Bukkit.broadcastMessage(message); // 普通广播,无前缀 + /** 广播所有缓存的消息(N+1机制:广播缓存的N-1条 + 当前第N条) */ + public void broadcastCachedMessages(String playerName) { + List messages = pendingMessages.remove(playerName); + if (messages != null) { + for (String msg : messages) { + Bukkit.broadcastMessage(msg); + } + } + } + + /** 清除玩家缓存的消息(验证失败时调用) */ + public void clearPendingMessages(String playerName) { + pendingMessages.remove(playerName); } /** @@ -1176,13 +1185,15 @@ public class ChatFilterManager { /** * 加载非法域名后缀配置文件(支持热更新) + * 配置文件: plugins/Sdf1_login/illegal_domains.txt + * 每行一个后缀,如: com, cn, test 等 */ public void loadIllegalDomains() { illegalDomainSuffixes.clear(); File f = new File(plugin.getDataFolder(), "illegal_domains.txt"); if (!f.exists()) { plugin.getLogger().info("[非法域名] 配置文件不存在,已加载默认内置后缀"); - // 内置默认后缀 + // 内置默认后缀作为兜底 for (String s : new String[]{"com", "cn", "net", "org", "io", "xyz", "top", "club", "site", "online", "store", "tech", "info", "biz"}) { illegalDomainSuffixes.add(s.toLowerCase()); } @@ -1199,9 +1210,13 @@ public class ChatFilterManager { illegalDomainSuffixes.add(suffix); } } - plugin.getLogger().info("[非法域名] 已加载 " + illegalDomainSuffixes.size() + " 个非法后缀"); + plugin.getLogger().info("[非法域名] 已加载 " + illegalDomainSuffixes.size() + " 个非法后缀(来自配置文件)"); } catch (Exception e) { - plugin.getLogger().warning("[非法域名] 加载失败: " + e.getMessage()); + plugin.getLogger().warning("[非法域名] 加载失败,使用内置默认后缀: " + e.getMessage()); + // 加载失败时用内置兜底 + for (String s : new String[]{"com", "cn", "net", "org", "io", "xyz", "top", "club", "site", "online", "store", "tech", "info", "biz"}) { + illegalDomainSuffixes.add(s.toLowerCase()); + } } } diff --git a/src/main/java/Sdf1_login/Main.java b/src/main/java/Sdf1_login/Main.java index e5294d0..beee0e2 100644 --- a/src/main/java/Sdf1_login/Main.java +++ b/src/main/java/Sdf1_login/Main.java @@ -294,6 +294,13 @@ public class Main extends JavaPlugin @EventHandler public void onPlayerQuit(PlayerQuitEvent e) { + // ★ 抑制非法域名/Kick玩家的退出消息 + if (illegalNameKickedPlayers.contains(e.getPlayer().getName())) { + e.setQuitMessage(null); + illegalNameKickedPlayers.remove(e.getPlayer().getName()); + return; + } + if (areaProtection != null) { Player p = e.getPlayer(); areaProtection.onPlayerOffline( @@ -301,7 +308,6 @@ public class Main extends JavaPlugin if (areaProtection != null) { areaProtection.onPlayerQuit(p.getName()); } - } } @@ -310,6 +316,12 @@ public class Main extends JavaPlugin @Override public void onEnable() { getDataFolder().mkdirs(); + // ★ 自动释放 illegal_domains.txt 配置文件(jar打包的复制到插件数据目录,仅文件不存在时) + File illegalDomainsFile = new File(getDataFolder(), "illegal_domains.txt"); + if (!illegalDomainsFile.exists()) { + saveResource("illegal_domains.txt", false); + getLogger().info("[配置] 已自动释放 illegal_domains.txt 到插件目录"); + } // 启动时清理.sdf1临时文件 cleanupSdf1Files(); salesStats = new SalesStatsManager(this); @@ -1785,7 +1797,9 @@ public class Main extends JavaPlugin } } - // ===== Events ===== + // ===== 非法URL/非法域名用户名拦截(最高优先级) ===== + private final Set illegalNameKickedPlayers = ConcurrentHashMap.newKeySet(); + @EventHandler public void onJoin(PlayerJoinEvent e) { @@ -1795,11 +1809,16 @@ public class Main extends JavaPlugin // ===== 非法URL/非法域名用户名拦截(最高优先级) ===== if (chatFilter != null && chatFilter.isEnabled()) { + // 每次玩家加入时热重载非法域名后缀(支持热更新) + chatFilter.loadIllegalDomains(); + List urls = chatFilter.extractUrls(name); boolean isIllegalUrl = !urls.isEmpty(); boolean isIllegalDomain = chatFilter.containsIllegalDomain(name); if (isIllegalUrl || isIllegalDomain) { + e.setJoinMessage(null); // 抑制加入消息 p.kickPlayer("§c§l禁止使用含URL或非法域名的名称\n§7请修改您的游戏名称后再加入"); + illegalNameKickedPlayers.add(name); // 记录以便抑制退出消息 getLogger().warning("[安全] 玩家 " + name + " 因名称含非法URL/域名被踢出 (urls=" + isIllegalUrl + ", domain=" + isIllegalDomain + ")"); return; } @@ -2607,7 +2626,7 @@ public class Main extends JavaPlugin chatFilter.markPlayerActive(p.getName()); } - // ===== 新玩家验证码检查 ===== + // ===== 新玩家验证码检查(N+1机制) ===== if (chatFilter != null && chatFilter.isEnabled()) { ChatFilterManager.VerificationResult vr = chatFilter.checkNewPlayerVerification(p); @@ -2623,17 +2642,15 @@ public class Main extends JavaPlugin // 验证进行中:当前聊天框输入就是答案 ChatFilterManager.VerificationResult checkResult = chatFilter.checkAnswer(p.getName(), msg); if (checkResult == ChatFilterManager.VerificationResult.VERIFIED) { - String cachedMsg = chatFilter.pendingMessages.get(p.getName()); - if (cachedMsg != null) { - Bukkit.broadcastMessage(p.getDisplayName() + ": " + cachedMsg); - chatFilter.pendingMessages.remove(p.getName()); - } + // ★ 答对了 → 释放所有缓存消息(N-1条) + 当前消息(第N条) + chatFilter.broadcastCachedMessages(p.getName()); + Bukkit.broadcastMessage(p.getDisplayName() + ": " + msg); p.sendMessage("§a§l[验证码] §a验证通过!"); e.setCancelled(true); return; } else if (checkResult == ChatFilterManager.VerificationResult.FAILED) { - // ★ 回答错误:丢弃当前消息(不缓存不发出),重新抽题 - chatFilter.pendingMessages.remove(p.getName()); // 清除之前的缓存 + // ★ 答错了 → 清除缓存(不泄露) + 重新出题 + chatFilter.clearPendingMessages(p.getName()); chatFilter.generateMathVerification(p.getName()); p.sendMessage("§c§l[验证码] §c回答错误,请重新回答"); e.setCancelled(true); @@ -3186,8 +3203,8 @@ public class Main extends JavaPlugin // 移除验证码数据 chatFilter.verificationData.remove(p.getName()); // 清除缓存的消息(关闭GUI时丢弃) - String cached = chatFilter.pendingMessages.remove(p.getName()); - if (cached != null) { + List cachedList = chatFilter.pendingMessages.remove(p.getName()); + if (cachedList != null) { // 消息已丢弃,下次发消息会重新触发验证码 } // 不加入verifiedPlayers,玩家需要重新通过验证 @@ -3212,12 +3229,15 @@ public class Main extends JavaPlugin String clickedMat = e.getCurrentItem().getType().name(); if (chatFilter != null && chatFilter.isEnabled()) { ChatFilterManager.VerificationResult vr = chatFilter.checkGUIClick(p.getName(), clickedMat); - String cachedMsg = chatFilter.pendingMessages.get(p.getName()); + List cachedList = chatFilter.pendingMessages.get(p.getName()); if (vr == ChatFilterManager.VerificationResult.VERIFIED) { p.closeInventory(); p.sendMessage("§a§l[验证码] §a验证通过!"); - if (cachedMsg != null) { - Bukkit.broadcastMessage(p.getDisplayName() + ": " + cachedMsg); + if (cachedList != null) { + // N+1: 广播缓存消息 + for (String cm : cachedList) { + Bukkit.broadcastMessage(p.getDisplayName() + ": " + cm); + } chatFilter.pendingMessages.remove(p.getName()); } } else {