第41批:全量转译 \uXXXX 转义为可读字面量/UnicodeChars具名常量 + 修复IDE插坏代码
- 新增 UnicodeChars 常量表:不可见字符(零宽/BOM/全角空格/U+0001)统一用具名常量 - 可见字符直接写字面量(§、:、全角数字、汉字区间等),读代码无需心算码位 - 正则层转义(\\uXXXX)保持原样并补注释说明,避免改变正则语义 - 注释里的 \uXXXX 改双写(javac 预处理期生效,注释躲不掉) - 修复IDE插坏的 ConfigManager/UnicodeChars 括号
This commit is contained in:
@@ -1301,7 +1301,8 @@ public class AreaProtection implements Listener {
|
||||
List<String> result = new ArrayList<>();
|
||||
if (s == null || s.isEmpty()) return result;
|
||||
String trimmed = s.trim();
|
||||
// ★ 支持JSON数组格式(PHP端存储格式),如 ["速度","急迫"] 或 ["\u8bd5\u70bc..."]
|
||||
// ★ 支持JSON数组格式(PHP端存储格式),如 ["速度","急迫"] 或 ["\\u8bd5\\u70bc..."]
|
||||
// (PHP 端 JSON 存储时中文会被转义成 \\uXXXX 形式,此处展示的是 PHP 侧原样)
|
||||
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
||||
try {
|
||||
String inner = trimmed.substring(1, trimmed.length() - 1);
|
||||
@@ -1821,27 +1822,27 @@ public class AreaProtection implements Listener {
|
||||
char c = input.charAt(i);
|
||||
|
||||
// 跳过零宽字符和不可见字符
|
||||
if (c == '\u200B' // 零宽空格
|
||||
|| c == '\u200C' // 零宽非连接
|
||||
|| c == '\u200D' // 零宽连接
|
||||
|| c == '\uFEFF' // BOM
|
||||
|| c == '\u00A0' // 不间断空格
|
||||
|| c == '\u2028' // 行分隔符
|
||||
|| c == '\u2029' // 段分隔符
|
||||
|| c == '\u200E' // LTR标记
|
||||
|| c == '\u200F' // RTL标记
|
||||
|| c == '\u2060' // 字连接符
|
||||
|| (c >= '\u2000' && c <= '\u200A')) { // 各种空格
|
||||
if (c == UnicodeChars.ZERO_WIDTH_SPACE // 零宽空格
|
||||
|| c == UnicodeChars.ZERO_WIDTH_NON_JOINER // 零宽非连接
|
||||
|| c == UnicodeChars.ZERO_WIDTH_JOINER // 零宽连接
|
||||
|| c == UnicodeChars.BOM // BOM
|
||||
|| c == UnicodeChars.NBSP // 不间断空格
|
||||
|| c == UnicodeChars.LINE_SEPARATOR // 行分隔符
|
||||
|| c == UnicodeChars.PARAGRAPH_SEPARATOR // 段分隔符
|
||||
|| c == UnicodeChars.LTR_MARK // LTR标记
|
||||
|| c == UnicodeChars.RTL_MARK // RTL标记
|
||||
|| c == UnicodeChars.WORD_JOINER // 字连接符
|
||||
|| (c >= UnicodeChars.SPACE_U2000 && c <= UnicodeChars.SPACE_U200A)) { // 各种空格
|
||||
continue;
|
||||
}
|
||||
|
||||
// 全角→半角映射
|
||||
if (c >= '\uFF01' && c <= '\uFF5E') {
|
||||
if (c >= '!' && c <= '~') {
|
||||
c = (char) (c - 0xFEE0);
|
||||
}
|
||||
|
||||
// 全角空格→半角空格
|
||||
if (c == '\u3000') {
|
||||
if (c == UnicodeChars.IDEOGRAPHIC_SPACE) {
|
||||
c = ' ';
|
||||
}
|
||||
|
||||
@@ -6930,7 +6931,7 @@ public class AreaProtection implements Listener {
|
||||
}
|
||||
String n = name.trim();
|
||||
if (n.length() > 20) return "名字不能超过20个字符";
|
||||
if (!n.matches("^[A-Za-z0-9\u4e00-\u9fa5_-]{1,20}$")) {
|
||||
if (!n.matches("^[A-Za-z0-9一-龥_-]{1,20}$")) {
|
||||
return "名字只能包含中英文、数字、下划线和连字符";
|
||||
}
|
||||
if (n.equalsIgnoreCase("global")) {
|
||||
@@ -11470,7 +11471,7 @@ public class AreaProtection implements Listener {
|
||||
String[] parts = inner.split("\"");
|
||||
java.util.List<String> result = new java.util.ArrayList<>();
|
||||
for (String part : parts) {
|
||||
String trimmed = part.trim().replaceAll("\u00A7[0-9a-fk-or]", "");
|
||||
String trimmed = part.trim().replaceAll("§[0-9a-fk-or]", "");
|
||||
if (!trimmed.isEmpty()) result.add(trimmed);
|
||||
}
|
||||
return result.toArray(new String[0]);
|
||||
|
||||
@@ -681,18 +681,18 @@ public class ChatFilterManager {
|
||||
+ "[_.][\\p{L}]{2,}");
|
||||
private static final Pattern DOT_VARIANTS =
|
||||
Pattern.compile(
|
||||
"[\uff0e\u3002\u2025\u2026\u00b7\uff65\ufe52]");
|
||||
"[.。‥…·・﹒]");
|
||||
/** 零宽/不可见字符:变形URL归一前剔除(如 baidu.<零宽空格>com) */
|
||||
private static final Pattern INVISIBLE =
|
||||
Pattern.compile(
|
||||
"[\u200b\u200c\u200d\u2060\ufeff\u00ad]");
|
||||
UnicodeChars.INVISIBLE_CHARS + "]");
|
||||
/** 点周带空白的变形域名: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,}");
|
||||
private static final Pattern COLOR_CODES =
|
||||
Pattern.compile(
|
||||
"\u00a7[0-9a-fk-orA-FK-OR]");
|
||||
"§[0-9a-fk-orA-FK-OR]");
|
||||
|
||||
private static final Set<String> FILE_EXT;
|
||||
static {
|
||||
@@ -747,7 +747,7 @@ public class ChatFilterManager {
|
||||
String section = "";
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (line.startsWith("\uFEFF"))
|
||||
if (line.startsWith(UnicodeChars.BOM_TEXT))
|
||||
line = line.substring(1);
|
||||
String t = line.trim();
|
||||
|
||||
@@ -1058,8 +1058,8 @@ public class ChatFilterManager {
|
||||
while (m2.find()) {
|
||||
String token = m2.group();
|
||||
if (token.indexOf(' ') < 0 && token.indexOf('\t') < 0
|
||||
&& token.indexOf('\u3000') < 0) continue; // 无空白 = 常规域名,上面已提取
|
||||
String compact = token.replaceAll("[\\s\u3000]+", "").toLowerCase();
|
||||
&& token.indexOf(UnicodeChars.IDEOGRAPHIC_SPACE) < 0) continue; // 无空白 = 常规域名,上面已提取
|
||||
String compact = token.replaceAll("[\\s" + UnicodeChars.IDEOGRAPHIC_SPACE + "]+", "").toLowerCase();
|
||||
int dot = compact.lastIndexOf('.');
|
||||
if (dot < 0) continue;
|
||||
if (!illegalDomainSuffixes.contains(compact.substring(dot + 1))) continue;
|
||||
|
||||
@@ -827,7 +827,7 @@ public class ConfigManager {
|
||||
// 处理UTF-8 BOM头
|
||||
if (firstLine) {
|
||||
firstLine = false;
|
||||
if (line.startsWith("\uFEFF")) {
|
||||
if (line.startsWith(UnicodeChars.BOM_TEXT)) {
|
||||
line = line.substring(1);
|
||||
}
|
||||
}
|
||||
@@ -1601,7 +1601,7 @@ public class ConfigManager {
|
||||
/** 字节数组 → 行列表(UTF-8,去 BOM,去 CR,去文件末空行) */
|
||||
private static List<String> readUtf8Lines(byte[] data) {
|
||||
String s = new String(data, StandardCharsets.UTF_8);
|
||||
if (!s.isEmpty() && s.charAt(0) == '\uFEFF') s = s.substring(1);
|
||||
if (!s.isEmpty() && s.charAt(0) == UnicodeChars.BOM) s = s.substring(1);
|
||||
List<String> L = new ArrayList<>();
|
||||
for (String line : s.split("\n", -1)) {
|
||||
if (line.endsWith("\r")) line = line.substring(0, line.length() - 1);
|
||||
@@ -1623,10 +1623,10 @@ public class ConfigManager {
|
||||
private boolean upgradeJsonMissing(File file, byte[] tplBytes, String label) {
|
||||
try {
|
||||
String tplText = new String(tplBytes, StandardCharsets.UTF_8);
|
||||
if (!tplText.isEmpty() && tplText.charAt(0) == '\uFEFF') tplText = tplText.substring(1);
|
||||
if (!tplText.isEmpty() && tplText.charAt(0) == UnicodeChars.BOM) tplText = tplText.substring(1);
|
||||
byte[] cur = java.nio.file.Files.readAllBytes(file.toPath());
|
||||
String oldText = new String(cur, StandardCharsets.UTF_8);
|
||||
if (!oldText.isEmpty() && oldText.charAt(0) == '\uFEFF') oldText = oldText.substring(1);
|
||||
if (!oldText.isEmpty() && oldText.charAt(0) == UnicodeChars.BOM) oldText = oldText.substring(1);
|
||||
com.google.gson.JsonElement tplEl;
|
||||
try {
|
||||
tplEl = com.google.gson.JsonParser.parseString(tplText);
|
||||
@@ -1796,7 +1796,7 @@ public class ConfigManager {
|
||||
boolean hasChinese = false;
|
||||
for (int ci = 0; ci < testTrimmed.length(); ci++) {
|
||||
char cc = testTrimmed.charAt(ci);
|
||||
if (cc >= '\u4e00' && cc <= '\u9fff') { hasChinese = true; break; }
|
||||
if (cc >= '一' && cc <= '鿿') { hasChinese = true; break; }
|
||||
}
|
||||
if (hasChinese) {
|
||||
int cnResult = parseChineseTime(testTrimmed);
|
||||
@@ -1877,7 +1877,7 @@ public class ConfigManager {
|
||||
private int parseChineseTime(String raw) {
|
||||
// 中文数字映射(含繁体/大写/各种变体)
|
||||
java.util.Map<String, Integer> cnDigit = new java.util.LinkedHashMap<>();
|
||||
cnDigit.put("零", 0); cnDigit.put("\u3007", 0);
|
||||
cnDigit.put("零", 0); cnDigit.put("〇", 0);
|
||||
cnDigit.put("一", 1); cnDigit.put("壹", 1); cnDigit.put("幺", 1);
|
||||
cnDigit.put("二", 2); cnDigit.put("贰", 2); cnDigit.put("貳", 2); cnDigit.put("两", 2); cnDigit.put("弐", 2);
|
||||
cnDigit.put("三", 3); cnDigit.put("叁", 3); cnDigit.put("參", 3);
|
||||
@@ -1929,9 +1929,9 @@ public class ConfigManager {
|
||||
int i = 0;
|
||||
while (i < s.length()) {
|
||||
char c = s.charAt(i);
|
||||
if (c >= '\u4e00' && c <= '\u9fff') {
|
||||
if (c >= '一' && c <= '鿿') {
|
||||
// 尝试2字符匹配(先匹配单位复合词)
|
||||
if (i + 1 < s.length() && s.charAt(i+1) >= '\u4e00' && s.charAt(i+1) <= '\u9fff') {
|
||||
if (i + 1 < s.length() && s.charAt(i+1) >= '一' && s.charAt(i+1) <= '鿿') {
|
||||
String two = s.substring(i, i+2);
|
||||
boolean matchedUnit = false;
|
||||
for (var entry : unitsList) {
|
||||
@@ -1987,7 +1987,7 @@ public class ConfigManager {
|
||||
// 检查是否是多字符中文数字组合(如"十五")
|
||||
boolean isMultiCharNum = false;
|
||||
for (String t : tokens) {
|
||||
if (t.equals(tok) && t.length() > 1 && t.chars().allMatch(c -> (c >= '\u4e00' && c <= '\u9fff') || (c >= '0' && c <= '9'))) {
|
||||
if (t.equals(tok) && t.length() > 1 && t.chars().allMatch(c -> (c >= '一' && c <= '鿿') || (c >= '0' && c <= '9'))) {
|
||||
// 所有字符都是中文数字或乘法器 → 解析中文数字组合
|
||||
int val = evalChineseCompoundNum(tok, cnDigit, multipliers);
|
||||
if (val >= 0) {
|
||||
|
||||
@@ -399,7 +399,7 @@ public class GUIManager implements Listener {
|
||||
public void openSubMenu(
|
||||
Player p, String fileName) {
|
||||
// ★ 剥离MC颜色代码(§f、§7等),防止污染文件名
|
||||
fileName = fileName.replaceAll("\u00A7[0-9a-fk-or]", "").trim();
|
||||
fileName = fileName.replaceAll("§[0-9a-fk-or]", "").trim();
|
||||
String menuTitle =
|
||||
"§6§l" + fileName
|
||||
.replace(".txt", "");
|
||||
@@ -632,7 +632,7 @@ public class GUIManager implements Listener {
|
||||
for (String line : block.split("\n")) {
|
||||
line = line.trim();
|
||||
String v = "";
|
||||
int idx = line.indexOf('\uff1a');
|
||||
int idx = line.indexOf(':');
|
||||
if (idx < 0)
|
||||
idx = line.indexOf(':');
|
||||
if (idx >= 0) {
|
||||
@@ -1766,13 +1766,13 @@ public class GUIManager implements Listener {
|
||||
oldName = "自定义物品";
|
||||
}
|
||||
mm.setDisplayName(
|
||||
"\u00a7e\u00a7l[菜单] \u00a7f"
|
||||
"§e§l[菜单] §f"
|
||||
+ oldName);
|
||||
List<String> lore = mm.hasLore()
|
||||
? new ArrayList<>(mm.getLore())
|
||||
: new ArrayList<>();
|
||||
lore.add(0,
|
||||
"\u00a77右键打开主菜单");
|
||||
"§7右键打开主菜单");
|
||||
mm.setLore(lore);
|
||||
mm.getPersistentDataContainer().set(
|
||||
new org.bukkit.NamespacedKey(
|
||||
@@ -1877,7 +1877,7 @@ public class GUIManager implements Listener {
|
||||
List<String> lore = meta.getLore();
|
||||
if (lore == null) return false;
|
||||
for (String line : lore) {
|
||||
if (line.contains("\u00a70\u00a7k")
|
||||
if (line.contains("§0§k")
|
||||
|| line.contains("CUSTOM")) {
|
||||
return true;
|
||||
}
|
||||
@@ -1986,7 +1986,7 @@ public class GUIManager implements Listener {
|
||||
&& im.getLore() != null) {
|
||||
for (String ln : im.getLore()) {
|
||||
if (ln.contains(
|
||||
"\u00a78")) {
|
||||
"§8")) {
|
||||
p.getInventory()
|
||||
.setItem(i,
|
||||
null);
|
||||
@@ -2010,7 +2010,7 @@ public class GUIManager implements Listener {
|
||||
return true;
|
||||
ItemMeta im = item.getItemMeta();
|
||||
if (im == null) return true;
|
||||
if ("\u00a77默认菜单(雪球)"
|
||||
if ("§7默认菜单(雪球)"
|
||||
.equals(im.getDisplayName()))
|
||||
return true;
|
||||
if (im.hasLore() && im.getLore() != null
|
||||
@@ -2040,7 +2040,7 @@ public class GUIManager implements Listener {
|
||||
&& im.hasLore()
|
||||
&& im.getLore() != null) {
|
||||
for (String ln : im.getLore()) {
|
||||
if (ln.contains("\u00a77右键打开主菜单"))
|
||||
if (ln.contains("§7右键打开主菜单"))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2053,7 +2053,7 @@ public class GUIManager implements Listener {
|
||||
return true;
|
||||
ItemMeta im = item.getItemMeta();
|
||||
if (im == null) return true;
|
||||
if ("\u00a77默认菜单(雪球)"
|
||||
if ("§7默认菜单(雪球)"
|
||||
.equals(im.getDisplayName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class GiftManager {
|
||||
|| line.contains("任务内容")) {
|
||||
int colon = line.indexOf(':');
|
||||
if (colon < 0)
|
||||
colon = line.indexOf('\uff1a');
|
||||
colon = line.indexOf(':');
|
||||
if (colon >= 0) {
|
||||
rewards.add(
|
||||
line.substring(colon + 1)
|
||||
|
||||
@@ -3537,7 +3537,7 @@ public class Main extends JavaPlugin
|
||||
originalName = "自定义物品";
|
||||
}
|
||||
cm.setDisplayName(
|
||||
"\u00a7e\u00a7l[菜单] \u00a7f"
|
||||
"§e§l[菜单] §f"
|
||||
+ originalName);
|
||||
List<String> cmLore =
|
||||
cm.hasLore()
|
||||
@@ -3546,9 +3546,9 @@ public class Main extends JavaPlugin
|
||||
// 避免重复添加
|
||||
if (cmLore.isEmpty()
|
||||
|| !cmLore.get(0).contains(
|
||||
"\u00a77右键打开主菜单")) {
|
||||
"§7右键打开主菜单")) {
|
||||
cmLore.add(0,
|
||||
"\u00a77右键打开主菜单");
|
||||
"§7右键打开主菜单");
|
||||
}
|
||||
cm.setLore(cmLore);
|
||||
customIcon.setItemMeta(cm);
|
||||
@@ -3575,10 +3575,10 @@ public class Main extends JavaPlugin
|
||||
ItemMeta im = snow.getItemMeta();
|
||||
if (im != null) {
|
||||
im.setDisplayName(
|
||||
"\u00a7e\u00a7l[菜单] \u00a7f右键打开主菜单");
|
||||
"§e§l[菜单] §f右键打开主菜单");
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add("\u00a77右键点击打开功能菜单");
|
||||
lore.add("\u00a78" + MENU_SNOWBALL_TAG);
|
||||
lore.add("§7右键点击打开功能菜单");
|
||||
lore.add("§8" + MENU_SNOWBALL_TAG);
|
||||
im.setLore(lore);
|
||||
snow.setItemMeta(im);
|
||||
}
|
||||
@@ -3767,7 +3767,7 @@ public class Main extends JavaPlugin
|
||||
// Sdf1_game宝箱物品标记格式: §0§kCUSTOM|玩家|...
|
||||
// 同时检查两种标记格式
|
||||
if (line.contains("CUSTOM")
|
||||
|| line.contains("\u00a70\u00a7k"))
|
||||
|| line.contains("§0§k"))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -5366,7 +5366,7 @@ public class Main extends JavaPlugin
|
||||
if (raw == null || raw.isEmpty())
|
||||
return Component.text("管理员", NamedTextColor.WHITE);
|
||||
// 先剥掉可能混进来的 § 原生色码,避免与 & 转换叠加出乱码
|
||||
String s = raw.replace("\u00a7", "&");
|
||||
String s = raw.replace("§", "&");
|
||||
// 压掉换行与制表,保证单行
|
||||
s = s.replaceAll("[\\r\\n\\t]+", " ").trim();
|
||||
return LegacyComponentSerializer.legacyAmpersand()
|
||||
@@ -6569,7 +6569,7 @@ public class Main extends JavaPlugin
|
||||
boolean hasChinese = false;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c >= '\u4e00' && c <= '\u9fff') { hasChinese = true; break; }
|
||||
if (c >= '一' && c <= '鿿') { hasChinese = true; break; }
|
||||
}
|
||||
if (hasChinese) return;
|
||||
// 触发口径(35批修订2):整条消息必须包含 ≥3 个英文字母才机翻 ——
|
||||
@@ -7378,7 +7378,7 @@ public class Main extends JavaPlugin
|
||||
String cmd = subLn
|
||||
.replace("§7指令: §f", "")
|
||||
.trim()
|
||||
.replaceAll("\u00A7[0-9a-fk-or]", ""); // 剥离残留MC颜色代码
|
||||
.replaceAll("§[0-9a-fk-or]", ""); // 剥离残留MC颜色代码
|
||||
if (cmd.isEmpty()
|
||||
|| cmd.equals("/")
|
||||
|| cmd.equals("null")) {
|
||||
@@ -7672,7 +7672,7 @@ public class Main extends JavaPlugin
|
||||
if (ln.contains("§7指令: §f")) {
|
||||
String cmd = ln.replace(
|
||||
"§7指令: §f", "").trim()
|
||||
.replaceAll("\u00A7[0-9a-fk-or]", ""); // 剥离残留MC颜色代码
|
||||
.replaceAll("§[0-9a-fk-or]", ""); // 剥离残留MC颜色代码
|
||||
if (cmd.isEmpty()
|
||||
|| cmd.equals("/")
|
||||
|| cmd.equals("null")) {
|
||||
|
||||
@@ -790,9 +790,9 @@ public class MaintenanceManager implements Listener {
|
||||
if (s.isEmpty()) return;
|
||||
if (note != null) warn(log, note + ": " + raw);
|
||||
// 仍坚持「一行一个玩家名」:整行收录,但格式不规范必须能被看见
|
||||
if (s.indexOf(',') >= 0 || s.indexOf('\uFF0C') >= 0 || s.indexOf('\u3001') >= 0
|
||||
|| s.indexOf(';') >= 0 || s.indexOf('\uFF1B') >= 0 || s.indexOf(' ') >= 0
|
||||
|| s.indexOf('\u3000') >= 0 || s.indexOf('\t') >= 0) {
|
||||
if (s.indexOf(',') >= 0 || s.indexOf(',') >= 0 || s.indexOf('、') >= 0
|
||||
|| s.indexOf(';') >= 0 || s.indexOf(';') >= 0 || s.indexOf(' ') >= 0
|
||||
|| s.indexOf(UnicodeChars.IDEOGRAPHIC_SPACE) >= 0 || s.indexOf('\t') >= 0) {
|
||||
warn(log, "白名单条目不符合「一行一个玩家名」,已按整行收录: " + s);
|
||||
}
|
||||
wl.add(s);
|
||||
@@ -807,7 +807,7 @@ public class MaintenanceManager implements Listener {
|
||||
/** 行内注释起点(# / # / //),返回 -1 表示没有 */
|
||||
private static int indexOfInlineComment(String s) {
|
||||
int h = s.indexOf('#');
|
||||
if (h < 0) h = s.indexOf('\uFF03');
|
||||
if (h < 0) h = s.indexOf('#');
|
||||
if (h < 0) h = s.indexOf("//");
|
||||
return h <= 0 ? -1 : h;
|
||||
}
|
||||
|
||||
@@ -1517,6 +1517,6 @@ public class OpWhiteListManager implements Listener {
|
||||
|
||||
/** 日志里去掉颜色符号,避免控制台刷屏乱码 */
|
||||
private static String stripColor(String s) {
|
||||
return s == null ? "" : s.replaceAll("(?i)&[0-9a-k]", "").replaceAll("\u00a7[0-9a-k]", "");
|
||||
return s == null ? "" : s.replaceAll("(?i)&[0-9a-k]", "").replaceAll("§[0-9a-k]", "");
|
||||
}
|
||||
}
|
||||
@@ -1805,7 +1805,7 @@ public class PVPArenaManager implements Listener {
|
||||
killerSession);
|
||||
}
|
||||
|
||||
((Player) killer).sendMessage("\u00a7a\u00a7l击杀 " + vName + "\u00a7a 当前击杀: " + pvpKills.get(kName));
|
||||
((Player) killer).sendMessage("§a§l击杀 " + vName + "§a 当前击杀: " + pvpKills.get(kName));
|
||||
}
|
||||
pvpDeaths.put(player.getName(), pvpDeaths.getOrDefault(player.getName(), 0) + 1);
|
||||
// ★ 非击杀死亡也持久化(如摔死/溺水)
|
||||
|
||||
@@ -2874,19 +2874,19 @@ public class ShopManager implements Listener {
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
// 全角数字→半角
|
||||
if (c >= '\uFF10' && c <= '\uFF19') {
|
||||
if (c >= '0' && c <= '9') {
|
||||
sb.append((char)(c - 0xFF10 + '0'));
|
||||
}
|
||||
// 全角字母→半角
|
||||
else if (c >= '\uFF21' && c <= '\uFF3A') {
|
||||
else if (c >= 'A' && c <= 'Z') {
|
||||
sb.append((char)(c - 0xFF21 + 'A'));
|
||||
}
|
||||
else if (c >= '\uFF41' && c <= '\uFF5A') {
|
||||
else if (c >= 'a' && c <= 'z') {
|
||||
sb.append((char)(c - 0xFF41 + 'a'));
|
||||
}
|
||||
// ★ 保留中文字符(Unicode CJK统一汉字范围)
|
||||
else if ((c >= '\u4E00' && c <= '\u9FFF')
|
||||
|| (c >= '\u3400' && c <= '\u4DBF')) {
|
||||
else if ((c >= '一' && c <= '鿿')
|
||||
|| (c >= '㐀' && c <= '䶿')) {
|
||||
sb.append(c);
|
||||
}
|
||||
// 保留半角字母和数字
|
||||
|
||||
@@ -135,7 +135,7 @@ public class SnManager implements Listener {
|
||||
/** 作废实物拦截提示限流(玩家名 -> 上次提示时间) */
|
||||
private final Map<String, Long> deadMsgAt = new ConcurrentHashMap<>();
|
||||
|
||||
/** PHP 命令执行结果缓冲(id + \u0001 + result),由 tickSync 回传 */
|
||||
/** PHP 命令执行结果缓冲(id + U+0001 分隔符 + result),由 tickSync 回传 */
|
||||
private final List<String> ackBuf = new ArrayList<String>();
|
||||
|
||||
/** 目录快照节流计数(每 3 轮 tickSync 推一次全量) */
|
||||
@@ -1999,7 +1999,7 @@ public class SnManager implements Listener {
|
||||
if (acks != null) {
|
||||
List<Map<String, Object>> rows = new ArrayList<Map<String, Object>>();
|
||||
for (String a : acks) {
|
||||
int p = a.indexOf('\u0001');
|
||||
int p = a.indexOf(UnicodeChars.SEPARATOR);
|
||||
if (p <= 0) continue;
|
||||
Map<String, Object> m = new LinkedHashMap<String, Object>();
|
||||
m.put("id", Long.parseLong(a.substring(0, p)));
|
||||
@@ -2187,7 +2187,7 @@ public class SnManager implements Listener {
|
||||
}
|
||||
if (ackId >= 0 && result != null) {
|
||||
synchronized (ackBuf) {
|
||||
ackBuf.add(ackId + "\u0001" + result);
|
||||
ackBuf.add(ackId + UnicodeChars.SEPARATOR_TEXT + result);
|
||||
}
|
||||
}
|
||||
return result == null ? "" : result;
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
package Sdf1_login;
|
||||
|
||||
/**
|
||||
* Unicode 特殊字符常量表。
|
||||
*
|
||||
* <p>背景:项目里原先大量使用 {@code \\uXXXX} 形式的 Unicode 转义字面量,
|
||||
* 读代码的人必须逐个心算码位才能看懂(例如 {@code \\u00a7} 其实是 MC 颜色码 §)。
|
||||
* 尤其不可见字符(零宽空格、BOM、全角空格…)直接写成字面量后
|
||||
* 在编辑器里根本"看不见",反而更难排查。</p>
|
||||
*
|
||||
* <p>因此本类把两类字符分开处理:</p>
|
||||
* <ul>
|
||||
* <li><b>可见字符</b>(§、:、全角数字、汉字等)→ 直接在业务代码里写字面量,
|
||||
* 一眼可读;</li>
|
||||
* <li><b>不可见字符</b>(零宽空格、BOM、全角空格、U+0001 分隔符等)→
|
||||
* 统一引用本类的具名常量,既可读又不会因"看不见"而误判。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>注意:正则表达式字符串里的 {@code \\uXXXX} 属于<b>正则层</b>转义
|
||||
* (Java 字符串里存的是字面反斜杠+u,由正则引擎在运行期解释),
|
||||
* 改写会改变正则语义,故保持原样不动。</p>
|
||||
*/
|
||||
public final class UnicodeChars {
|
||||
|
||||
private UnicodeChars() {
|
||||
}
|
||||
|
||||
// ==================== 不可见字符(必须用具名常量) ====================
|
||||
|
||||
/** U+0001 SOH —— SnManager 与 PHP 命令执行结果之间的分隔符 */
|
||||
public static final char SEPARATOR = '\u0001';
|
||||
|
||||
/** U+00A0 不间断空格(NO-BREAK SPACE) */
|
||||
public static final char NBSP = '\u00A0';
|
||||
|
||||
/** U+00AD 软连字符(SOFT HYPHEN) */
|
||||
public static final char SOFT_HYPHEN = '\u00AD';
|
||||
|
||||
/** U+2000 EN QUAD —— Unicode 各种空格区起点 */
|
||||
public static final char SPACE_U2000 = '\u2000';
|
||||
|
||||
/** U+200A HAIR SPACE —— Unicode 各种空格区终点 */
|
||||
public static final char SPACE_U200A = '\u200A';
|
||||
|
||||
/** U+200B 零宽空格(ZERO WIDTH SPACE) */
|
||||
public static final char ZERO_WIDTH_SPACE = '\u200B';
|
||||
|
||||
/** U+200C 零宽非连接符(ZERO WIDTH NON-JOINER) */
|
||||
public static final char ZERO_WIDTH_NON_JOINER = '\u200C';
|
||||
|
||||
/** U+200D 零宽连接符(ZERO WIDTH JOINER) */
|
||||
public static final char ZERO_WIDTH_JOINER = '\u200D';
|
||||
|
||||
/** U+200E 从左至右标记(LTR MARK) */
|
||||
public static final char LTR_MARK = '\u200E';
|
||||
|
||||
/** U+200F 从右至左标记(RTL MARK) */
|
||||
public static final char RTL_MARK = '\u200F';
|
||||
|
||||
/** U+2028 行分隔符(LINE SEPARATOR) */
|
||||
public static final char LINE_SEPARATOR = '\u2028';
|
||||
|
||||
/** U+2029 段分隔符(PARAGRAPH SEPARATOR) */
|
||||
public static final char PARAGRAPH_SEPARATOR = '\u2029';
|
||||
|
||||
/** U+2060 字连接符(WORD JOINER) */
|
||||
public static final char WORD_JOINER = '\u2060';
|
||||
|
||||
/** U+3000 表意空格(IDEOGRAPHIC SPACE,即"全角空格") */
|
||||
public static final char IDEOGRAPHIC_SPACE = '\u3000';
|
||||
|
||||
/** U+FEFF 零宽不换行空格 / BOM(ZERO WIDTH NO-BREAK SPACE) */
|
||||
public static final char BOM = '\uFEFF';
|
||||
|
||||
// ==================== 区间常量(不可见/边界判定用) ====================
|
||||
|
||||
/** CJK 基本汉字区起点 U+4E00(一) */
|
||||
public static final char CJK_START = '\u4E00';
|
||||
|
||||
/** CJK 基本汉字区终点 U+9FFF */
|
||||
public static final char CJK_END = '\u9FFF';
|
||||
|
||||
/** CJK 兼容汉字常用终点 U+9FA5(龥) */
|
||||
public static final char CJK_COMPAT_END = '\u9FA5';
|
||||
|
||||
/** CJK 扩展 A 区起点 U+3400(㐀) */
|
||||
public static final char CJK_EXT_A_START = '\u3400';
|
||||
|
||||
/** CJK 扩展 A 区终点 U+4DBF(䶿) */
|
||||
public static final char CJK_EXT_A_END = '\u4DBF';
|
||||
|
||||
/** 全角 ASCII 起点 U+FF01(!) */
|
||||
public static final char FULLWIDTH_START = '\uFF01';
|
||||
|
||||
/** 全角 ASCII 终点 U+FF5E(~) */
|
||||
public static final char FULLWIDTH_END = '\uFF5E';
|
||||
|
||||
/** 全角数字 0 U+FF10 */
|
||||
public static final char FULLWIDTH_ZERO = '\uFF10';
|
||||
|
||||
/** 全角数字 9 U+FF19 */
|
||||
public static final char FULLWIDTH_NINE = '\uFF19';
|
||||
|
||||
/** 全角大写 A U+FF21 */
|
||||
public static final char FULLWIDTH_A_UPPER = '\uFF21';
|
||||
|
||||
/** 全角大写 Z U+FF3A */
|
||||
public static final char FULLWIDTH_Z_UPPER = '\uFF3A';
|
||||
|
||||
/** 全角小写 a U+FF41 */
|
||||
public static final char FULLWIDTH_A_LOWER = '\uFF41';
|
||||
|
||||
/** 全角小写 z U+FF5A */
|
||||
public static final char FULLWIDTH_Z_LOWER = '\uFF5A';
|
||||
|
||||
/** 全角左括号 U+FF08 */
|
||||
public static final char FULLWIDTH_LPAREN = '\uFF08';
|
||||
|
||||
/** 全角右括号 U+FF09 */
|
||||
public static final char FULLWIDTH_RPAREN = '\uFF09';
|
||||
|
||||
/** 全角井号 U+FF03 */
|
||||
public static final char FULLWIDTH_HASH = '\uFF03';
|
||||
|
||||
/** 全角逗号 U+FF0C */
|
||||
public static final char FULLWIDTH_COMMA = '\uFF0C';
|
||||
|
||||
/** 全角分号 U+FF1B */
|
||||
public static final char FULLWIDTH_SEMICOLON = '\uFF1B';
|
||||
|
||||
/** 全角冒号 U+FF1A */
|
||||
public static final char FULLWIDTH_COLON = '\uFF1A';
|
||||
|
||||
/** 全角句点 U+FF0E */
|
||||
public static final char FULLWIDTH_STOP = '\uFF0E';
|
||||
|
||||
/** 全角波浪号 U+FF5E */
|
||||
public static final char FULLWIDTH_TILDE = '\uFF5E';
|
||||
|
||||
/** 半角片假名中点 U+FF65 */
|
||||
public static final char HALFWIDTH_KATAKANA_MIDDLE_DOT = '\uFF65';
|
||||
|
||||
/** 小型句点 U+FE52 */
|
||||
public static final char SMALL_FULL_STOP = '\uFE52';
|
||||
|
||||
/** 二点引导符 U+2025 */
|
||||
public static final char TWO_DOT_LEADER = '\u2025';
|
||||
|
||||
/** 水平省略号 U+2026 */
|
||||
public static final char HORIZONTAL_ELLIPSIS = '\u2026';
|
||||
|
||||
/** 中点 U+00B7 */
|
||||
public static final char MIDDLE_DOT = '\u00B7';
|
||||
|
||||
/** 项目符号 U+2022 */
|
||||
public static final char BULLET = '\u2022';
|
||||
|
||||
/** 表意句点 U+3002 */
|
||||
public static final char IDEOGRAPHIC_STOP = '\u3002';
|
||||
|
||||
/** 表意逗号 U+3001 */
|
||||
public static final char IDEOGRAPHIC_COMMA = '\u3001';
|
||||
|
||||
/** 表意数字零 U+3007(〇) */
|
||||
public static final char IDEOGRAPHIC_ZERO = '\u3007';
|
||||
|
||||
/** 左黑 lenticular 方括号 U+3010(【) */
|
||||
public static final char LEFT_BLACK_LENTICULAR = '\u3010';
|
||||
|
||||
/** 右黑 lenticular 方括号 U+3011(】) */
|
||||
public static final char RIGHT_BLACK_LENTICULAR = '\u3011';
|
||||
|
||||
/** 左 tortoise shell 括号 U+3014(〔) */
|
||||
public static final char LEFT_TORTOISE_SHELL = '\u3014';
|
||||
|
||||
/** 右 tortoise shell 括号 U+3015(〕) */
|
||||
public static final char RIGHT_TORTOISE_SHELL = '\u3015';
|
||||
|
||||
/** 左直角引号 U+300C(「) */
|
||||
public static final char LEFT_CORNER_BRACKET = '\u300C';
|
||||
|
||||
/** 右直角引号 U+300D」(」) */
|
||||
public static final char RIGHT_CORNER_BRACKET = '\u300D';
|
||||
|
||||
/** 左白直角引号 U+300E(『) */
|
||||
public static final char LEFT_WHITE_CORNER_BRACKET = '\u300E';
|
||||
|
||||
/** 右白直角引号 U+300F(』) */
|
||||
public static final char RIGHT_WHITE_CORNER_BRACKET = '\u300F';
|
||||
|
||||
// ==================== 常用字符串常量 ====================
|
||||
|
||||
/** MC 颜色码前缀 § */
|
||||
public static final String COLOR_SIGN = "§";
|
||||
|
||||
/** 全角冒号 ":" */
|
||||
public static final String WIDE_COLON = ":";
|
||||
|
||||
// ==================== 字符串版(用于拼接场景) ====================
|
||||
// 注意:char 常量参与字符串拼接时若两侧都是 char 会被当成数值加法,
|
||||
// 因此需要拼接的不可见字符一律用这里的 String 版常量。
|
||||
|
||||
/** U+FEFF BOM 的字符串版 */
|
||||
public static final String BOM_TEXT = "\uFEFF";
|
||||
|
||||
/** U+0001 分隔符的字符串版 */
|
||||
public static final String SEPARATOR_TEXT = "\u0001";
|
||||
|
||||
/** U+3000 全角空格的字符串版 */
|
||||
public static final String IDEOGRAPHIC_SPACE_TEXT = "\u3000";
|
||||
|
||||
/**
|
||||
* 零宽/不可见字符集合(聊天变形 URL 归一前剔除用)。
|
||||
* 含:零宽空格、零宽非连接、零宽连接、字连接符、BOM、软连字符。
|
||||
*/
|
||||
public static final String INVISIBLE_CHARS = "\u200B\u200C\u200D\u2060\uFEFF\u00AD";
|
||||
}
|
||||
@@ -6000,7 +6000,7 @@ public class WebManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** 解码JSON中的 \\uXXXX Unicode转义(如 \\u00a7 → §) */
|
||||
/** 解码JSON中的 Unicode 转义(\\uXXXX 形式,如 \\u00a7 → §) */
|
||||
private String decodeJsonUnicode(String s) {
|
||||
if (s == null || s.isEmpty()) return s;
|
||||
if (!s.contains("\\u")) return s;
|
||||
|
||||
@@ -41,7 +41,7 @@ public class WelcomeManager {
|
||||
String section = "";
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (line.startsWith("\uFEFF"))
|
||||
if (line.startsWith(UnicodeChars.BOM_TEXT))
|
||||
line = line.substring(1);
|
||||
String t = line.trim();
|
||||
if (t.isEmpty()
|
||||
@@ -135,7 +135,7 @@ public class WelcomeManager {
|
||||
}
|
||||
|
||||
private String colorize(String msg) {
|
||||
return msg.replace("&", "\u00a7");
|
||||
return msg.replace("&", "§");
|
||||
}
|
||||
|
||||
private void writeDefault(File f) {
|
||||
|
||||
Reference in New Issue
Block a user