From a5cb5afba7b853558a3603ddf0ee10c03ea66035 Mon Sep 17 00:00:00 2001
From: youpaishidifu <1@ypshidifu.cn>
Date: Thu, 22 Oct 2026 13:05:46 +0800
Subject: [PATCH] =?UTF-8?q?=E7=AC=AC41=E6=89=B9=E6=94=B6=E5=B0=BE=EF=BC=9A?=
=?UTF-8?q?=E6=AD=A3=E5=88=99=E5=B1=82=E6=AE=8B=E7=95=99=20Unicode=20?=
=?UTF-8?q?=E8=BD=AC=E4=B9=89=E5=85=A8=E9=83=A8=E5=AD=97=E9=9D=A2=E5=8C=96?=
=?UTF-8?q?=EF=BC=88=E5=8F=AF=E8=A7=81=E5=AD=97=E7=AC=A6=E5=86=99=E5=AD=97?=
=?UTF-8?q?=E9=9D=A2=E9=87=8F/=E4=B8=8D=E5=8F=AF=E8=A7=81=E5=AD=97?=
=?UTF-8?q?=E7=AC=A6=E7=94=A8=E5=85=B7=E5=90=8D=E5=B8=B8=E9=87=8F=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/Sdf1_login/AreaProtection.java | 4 +-
.../java/Sdf1_login/ChatFilterManager.java | 4 +-
src/main/java/Sdf1_login/DurationParser.java | 4 +-
.../java/Sdf1_login/MaintenanceManager.java | 10 +-
src/main/java/Sdf1_login/UnicodeChars.java | 107 +++++++++++-------
src/main/java/Sdf1_login/WebManager.java | 5 +-
6 files changed, 81 insertions(+), 53 deletions(-)
diff --git a/src/main/java/Sdf1_login/AreaProtection.java b/src/main/java/Sdf1_login/AreaProtection.java
index 1589e32..57535e9 100644
--- a/src/main/java/Sdf1_login/AreaProtection.java
+++ b/src/main/java/Sdf1_login/AreaProtection.java
@@ -1301,8 +1301,8 @@ public class AreaProtection implements Listener {
List result = new ArrayList<>();
if (s == null || s.isEmpty()) return result;
String trimmed = s.trim();
- // ★ 支持JSON数组格式(PHP端存储格式),如 ["速度","急迫"] 或 ["\\u8bd5\\u70bc..."]
- // (PHP 端 JSON 存储时中文会被转义成 \\uXXXX 形式,此处展示的是 PHP 侧原样)
+ // ★ 支持JSON数组格式(PHP端存储格式),如 ["速度","急迫"]
+ // (PHP 端 JSON 存储时中文会被转义成 U+XXXX 形式,例如 "试" 对应 U+8BD5,此处展示的是 PHP 侧原样)
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
try {
String inner = trimmed.substring(1, trimmed.length() - 1);
diff --git a/src/main/java/Sdf1_login/ChatFilterManager.java b/src/main/java/Sdf1_login/ChatFilterManager.java
index 60a2642..2e68afd 100644
--- a/src/main/java/Sdf1_login/ChatFilterManager.java
+++ b/src/main/java/Sdf1_login/ChatFilterManager.java
@@ -689,7 +689,9 @@ public class ChatFilterManager {
/** 点周带空白的变形域名:baidu . com(2026-10-08 加强拦截) */
private static final Pattern SPACED_DOT_URL =
Pattern.compile(
- "(?i)(?:[\\p{L}0-9](?:[\\p{L}0-9\\-]*[\\p{L}0-9])?[\\s\\u3000]*\\.)+[\\s\\u3000]*[\\p{L}]{2,}");
+ "(?i)(?:[\\p{L}0-9](?:[\\p{L}0-9\\-]*[\\p{L}0-9])?[\\s"
+ + UnicodeChars.IDEOGRAPHIC_SPACE_TEXT + "]*\\.)+[\\s"
+ + UnicodeChars.IDEOGRAPHIC_SPACE_TEXT + "]*[\\p{L}]{2,}");
private static final Pattern COLOR_CODES =
Pattern.compile(
"§[0-9a-fk-orA-FK-OR]");
diff --git a/src/main/java/Sdf1_login/DurationParser.java b/src/main/java/Sdf1_login/DurationParser.java
index ee3b976..2287d09 100644
--- a/src/main/java/Sdf1_login/DurationParser.java
+++ b/src/main/java/Sdf1_login/DurationParser.java
@@ -181,7 +181,7 @@ public class DurationParser {
/** 中文日期+时间:2026年10月8日 14:43(“日”已界定日期边界,时间可紧贴) */
private static final Pattern DT_CN = Pattern.compile(
- "(\\d{4})年(\\d{1,2})月(\\d{1,2})日(?:[ T\\u3000]+)?"
+ "(\\d{4})年(\\d{1,2})月(\\d{1,2})日(?:[ T" + UnicodeChars.IDEOGRAPHIC_SPACE_TEXT + "]+)?"
+ "(\\d{1,2}):(\\d{2})(?::(\\d{2}))?(?!\\d|[A-Za-z])");
/** 分隔日期+时间:26-10-08 14:43 / 2026-10-08 14:43 / 2026.10.08T14:43:00
@@ -189,7 +189,7 @@ public class DurationParser {
* 防止 2026-1-8 14:43 被粘连误读成「日=81、时=4」 */
private static final Pattern DT_SEP = Pattern.compile(
"(?= 0) return false;
// 剥离各类括号与装饰符号(含全角【】〔〕「」『』()),只留标题正文
- String stripped = s.replaceAll("[\\[\\]\\u3010\\u3011\\u3014\\u3015\\u300c\\u300d\\u300e\\u300f"
- + "\\uff08\\uff09()\\-=_::·*#\\s]+", "");
+ String stripped = s.replaceAll("[\\[\\]【】〔〕「」『』()()\\-=_::·*#\\s]+", "");
String low = stripped.toLowerCase(Locale.ROOT);
// 精确匹配常见写法;再兜底 endsWith 兼容「玩家白名单」「ALLOWLIST」这类变体标题
//(有了上面的 = 门槛,endsWith 已经不会再吞掉控制段键值对)
@@ -781,7 +781,7 @@ public class MaintenanceManager implements Listener {
s = s.substring(0, c).trim();
}
// 列表装饰前缀:- > * · •(合法 MC 名不会以这些字符开头)
- String noDecor = s.replaceFirst("^[-*>\\u00b7\\u2022]\\s+", "");
+ String noDecor = s.replaceFirst("^[-*>·•]\\s+", "");
if (!noDecor.equals(s)) {
warn(log, "白名单条目前缀符号已忽略: [" + s + "] -> [" + noDecor + "]");
s = noDecor;
@@ -801,7 +801,7 @@ public class MaintenanceManager implements Listener {
/** 去掉肉眼看不见却会让匹配失败的字符:零宽/BOM/不间断空格/全角空格 */
private static String stripInvisible(String s) {
if (s == null) return null;
- return s.replaceAll("[\\u200B-\\u200D\\u2060\\uFEFF\\u00A0\\u3000]", "");
+ return s.replaceAll(UnicodeChars.INVISIBLE_STRIP_CLASS, "");
}
/** 行内注释起点(# / # / //),返回 -1 表示没有 */
diff --git a/src/main/java/Sdf1_login/UnicodeChars.java b/src/main/java/Sdf1_login/UnicodeChars.java
index a21cda4..677b72b 100644
--- a/src/main/java/Sdf1_login/UnicodeChars.java
+++ b/src/main/java/Sdf1_login/UnicodeChars.java
@@ -3,8 +3,8 @@ package Sdf1_login;
/**
* Unicode 特殊字符常量表。
*
- * 背景:项目里原先大量使用 {@code \\uXXXX} 形式的 Unicode 转义字面量,
- * 读代码的人必须逐个心算码位才能看懂(例如 {@code \\u00a7} 其实是 MC 颜色码 §)。
+ *
背景:项目里原先大量使用 Unicode 转义字面量(写成 U+XXXX 形式),
+ * 读代码的人必须逐个心算码位才能看懂(例如 U+00A7 其实是 MC 颜色码 §)。
* 尤其不可见字符(零宽空格、BOM、全角空格…)直接写成字面量后
* 在编辑器里根本"看不见",反而更难排查。
*
@@ -16,9 +16,10 @@ package Sdf1_login;
* 统一引用本类的具名常量,既可读又不会因"看不见"而误判。
*
*
- * 注意:正则表达式字符串里的 {@code \\uXXXX} 属于正则层转义
+ *
注意:正则表达式字符串里的 Unicode 转义属于正则层转义
* (Java 字符串里存的是字面反斜杠+u,由正则引擎在运行期解释),
- * 改写会改变正则语义,故保持原样不动。
+ * 改写会改变正则语义。本项目已把正则层里可见的转义全部改写成
+ * 真实字符或本类的具名常量;不可见的转义则通过本类常量参与拼接。
*/
public final class UnicodeChars {
@@ -75,118 +76,118 @@ public final class UnicodeChars {
// ==================== 区间常量(不可见/边界判定用) ====================
/** CJK 基本汉字区起点 U+4E00(一) */
- public static final char CJK_START = '\u4E00';
+ public static final char CJK_START = '一';
/** CJK 基本汉字区终点 U+9FFF */
- public static final char CJK_END = '\u9FFF';
+ public static final char CJK_END = '鿿';
/** CJK 兼容汉字常用终点 U+9FA5(龥) */
- public static final char CJK_COMPAT_END = '\u9FA5';
+ public static final char CJK_COMPAT_END = '龥';
/** CJK 扩展 A 区起点 U+3400(㐀) */
- public static final char CJK_EXT_A_START = '\u3400';
+ public static final char CJK_EXT_A_START = '㐀';
/** CJK 扩展 A 区终点 U+4DBF(䶿) */
- public static final char CJK_EXT_A_END = '\u4DBF';
+ public static final char CJK_EXT_A_END = '䶿';
/** 全角 ASCII 起点 U+FF01(!) */
- public static final char FULLWIDTH_START = '\uFF01';
+ public static final char FULLWIDTH_START = '!';
/** 全角 ASCII 终点 U+FF5E(~) */
- public static final char FULLWIDTH_END = '\uFF5E';
+ public static final char FULLWIDTH_END = '~';
/** 全角数字 0 U+FF10 */
- public static final char FULLWIDTH_ZERO = '\uFF10';
+ public static final char FULLWIDTH_ZERO = '0';
/** 全角数字 9 U+FF19 */
- public static final char FULLWIDTH_NINE = '\uFF19';
+ public static final char FULLWIDTH_NINE = '9';
/** 全角大写 A U+FF21 */
- public static final char FULLWIDTH_A_UPPER = '\uFF21';
+ public static final char FULLWIDTH_A_UPPER = 'A';
/** 全角大写 Z U+FF3A */
- public static final char FULLWIDTH_Z_UPPER = '\uFF3A';
+ public static final char FULLWIDTH_Z_UPPER = 'Z';
/** 全角小写 a U+FF41 */
- public static final char FULLWIDTH_A_LOWER = '\uFF41';
+ public static final char FULLWIDTH_A_LOWER = 'a';
/** 全角小写 z U+FF5A */
- public static final char FULLWIDTH_Z_LOWER = '\uFF5A';
+ public static final char FULLWIDTH_Z_LOWER = 'z';
/** 全角左括号 U+FF08 */
- public static final char FULLWIDTH_LPAREN = '\uFF08';
+ public static final char FULLWIDTH_LPAREN = '(';
/** 全角右括号 U+FF09 */
- public static final char FULLWIDTH_RPAREN = '\uFF09';
+ public static final char FULLWIDTH_RPAREN = ')';
/** 全角井号 U+FF03 */
- public static final char FULLWIDTH_HASH = '\uFF03';
+ public static final char FULLWIDTH_HASH = '#';
/** 全角逗号 U+FF0C */
- public static final char FULLWIDTH_COMMA = '\uFF0C';
+ public static final char FULLWIDTH_COMMA = ',';
/** 全角分号 U+FF1B */
- public static final char FULLWIDTH_SEMICOLON = '\uFF1B';
+ public static final char FULLWIDTH_SEMICOLON = ';';
/** 全角冒号 U+FF1A */
- public static final char FULLWIDTH_COLON = '\uFF1A';
+ public static final char FULLWIDTH_COLON = ':';
/** 全角句点 U+FF0E */
- public static final char FULLWIDTH_STOP = '\uFF0E';
+ public static final char FULLWIDTH_STOP = '.';
/** 全角波浪号 U+FF5E */
- public static final char FULLWIDTH_TILDE = '\uFF5E';
+ public static final char FULLWIDTH_TILDE = '~';
/** 半角片假名中点 U+FF65 */
- public static final char HALFWIDTH_KATAKANA_MIDDLE_DOT = '\uFF65';
+ public static final char HALFWIDTH_KATAKANA_MIDDLE_DOT = '・';
/** 小型句点 U+FE52 */
- public static final char SMALL_FULL_STOP = '\uFE52';
+ public static final char SMALL_FULL_STOP = '﹒';
/** 二点引导符 U+2025 */
- public static final char TWO_DOT_LEADER = '\u2025';
+ public static final char TWO_DOT_LEADER = '‥';
/** 水平省略号 U+2026 */
- public static final char HORIZONTAL_ELLIPSIS = '\u2026';
+ public static final char HORIZONTAL_ELLIPSIS = '…';
/** 中点 U+00B7 */
- public static final char MIDDLE_DOT = '\u00B7';
+ public static final char MIDDLE_DOT = '·';
/** 项目符号 U+2022 */
- public static final char BULLET = '\u2022';
+ public static final char BULLET = '•';
/** 表意句点 U+3002 */
- public static final char IDEOGRAPHIC_STOP = '\u3002';
+ public static final char IDEOGRAPHIC_STOP = '。';
/** 表意逗号 U+3001 */
- public static final char IDEOGRAPHIC_COMMA = '\u3001';
+ public static final char IDEOGRAPHIC_COMMA = '、';
/** 表意数字零 U+3007(〇) */
- public static final char IDEOGRAPHIC_ZERO = '\u3007';
+ public static final char IDEOGRAPHIC_ZERO = '〇';
/** 左黑 lenticular 方括号 U+3010(【) */
- public static final char LEFT_BLACK_LENTICULAR = '\u3010';
+ public static final char LEFT_BLACK_LENTICULAR = '【';
/** 右黑 lenticular 方括号 U+3011(】) */
- public static final char RIGHT_BLACK_LENTICULAR = '\u3011';
+ public static final char RIGHT_BLACK_LENTICULAR = '】';
/** 左 tortoise shell 括号 U+3014(〔) */
- public static final char LEFT_TORTOISE_SHELL = '\u3014';
+ public static final char LEFT_TORTOISE_SHELL = '〔';
/** 右 tortoise shell 括号 U+3015(〕) */
- public static final char RIGHT_TORTOISE_SHELL = '\u3015';
+ public static final char RIGHT_TORTOISE_SHELL = '〕';
/** 左直角引号 U+300C(「) */
- public static final char LEFT_CORNER_BRACKET = '\u300C';
+ public static final char LEFT_CORNER_BRACKET = '「';
/** 右直角引号 U+300D」(」) */
- public static final char RIGHT_CORNER_BRACKET = '\u300D';
+ public static final char RIGHT_CORNER_BRACKET = '」';
/** 左白直角引号 U+300E(『) */
- public static final char LEFT_WHITE_CORNER_BRACKET = '\u300E';
+ public static final char LEFT_WHITE_CORNER_BRACKET = '『';
/** 右白直角引号 U+300F(』) */
- public static final char RIGHT_WHITE_CORNER_BRACKET = '\u300F';
+ public static final char RIGHT_WHITE_CORNER_BRACKET = '』';
// ==================== 常用字符串常量 ====================
@@ -214,4 +215,26 @@ public final class UnicodeChars {
* 含:零宽空格、零宽非连接、零宽连接、字连接符、BOM、软连字符。
*/
public static final String INVISIBLE_CHARS = "\u200B\u200C\u200D\u2060\uFEFF\u00AD";
+
+ /** U+200B 零宽空格的字符串版 */
+ public static final String ZERO_WIDTH_SPACE_TEXT = "\u200B";
+
+ /** U+200D 零宽连接符的字符串版 */
+ public static final String ZERO_WIDTH_JOINER_TEXT = "\u200D";
+
+ /** U+2060 字连接符的字符串版 */
+ public static final String WORD_JOINER_TEXT = "\u2060";
+
+ /** U+00A0 不间断空格的字符串版 */
+ public static final String NBSP_TEXT = "\u00A0";
+
+ /**
+ * 需要剔除的不可见字符(正则字符类形式,净化玩家输入用)。
+ * 含:零宽空格~零宽连接(区间)、字连接符、BOM、不间断空格、全角空格。
+ * 这里用具名常量拼接,而不是转义写法——这样源码里能直接看出
+ * 每个字符的用途,读代码的人不必去心算码位。
+ */
+ public static final String INVISIBLE_STRIP_CLASS =
+ "[" + ZERO_WIDTH_SPACE_TEXT + "-" + ZERO_WIDTH_JOINER_TEXT
+ + WORD_JOINER_TEXT + BOM_TEXT + NBSP_TEXT + IDEOGRAPHIC_SPACE_TEXT + "]";
}
diff --git a/src/main/java/Sdf1_login/WebManager.java b/src/main/java/Sdf1_login/WebManager.java
index 56c104f..7d4d9f4 100644
--- a/src/main/java/Sdf1_login/WebManager.java
+++ b/src/main/java/Sdf1_login/WebManager.java
@@ -3990,6 +3990,8 @@ public class WebManager {
* 解码JSON中的 Unicode转义序列
*/
private static String decodeUnicodeEscapes(String str) {
+ // 这里的 "\\u" 是「反斜杠 + u」两个字符(PHP 端 JSON 的转义标记),
+ // 不是 Java 的 Unicode 转义——探测原始字符串里是否带转义标记。
if (str == null || !str.contains("\\u")) return str;
try {
StringBuilder sb = new StringBuilder(str.length());
@@ -6000,9 +6002,10 @@ public class WebManager {
}
}
- /** 解码JSON中的 Unicode 转义(\\uXXXX 形式,如 \\u00a7 → §) */
+ /** 解码JSON中的 Unicode 转义(U+XXXX 形式,如 U+00A7 即 §) */
private String decodeJsonUnicode(String s) {
if (s == null || s.isEmpty()) return s;
+ // 同上:这里探测的是 JSON 里的反斜杠+u 转义标记,不是 Java Unicode 转义。
if (!s.contains("\\u")) return s;
StringBuilder sb = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {