From 78d3df877edb22c03d0c2336a5ca6b8bf96ec559 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Fri, 27 Jun 2025 16:36:59 +0800 Subject: [PATCH] Update SNBTReader.java --- .../craftengine/core/util/SNBTReader.java | 104 +++++++++++++++--- 1 file changed, 87 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index c42e99e8b..9c216b98d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -1,9 +1,7 @@ package net.momirealms.craftengine.core.util; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.function.Function; public final class SNBTReader extends DefaultStringReader { private static final char COMPOUND_START = '{'; @@ -16,12 +14,10 @@ public final class SNBTReader extends DefaultStringReader { private static final char KEY_VALUE_SEPARATOR = ':'; private static final char ELEMENT_SEPARATOR = ','; - // 数字类型后缀 - private static final char BYTE_SUFFIX = 'b'; - private static final char SHORT_SUFFIX = 's'; - private static final char LONG_SUFFIX = 'l'; - private static final char FLOAT_SUFFIX = 'f'; - private static final char DOUBLE_SUFFIX = 'd'; + private static final char ARRAY_DELIMITER = ';'; + private static final char BYTE_ARRAY = 'b'; + private static final char INT_ARRAY = 'i'; + private static final char LONG_ARRAY = 'l'; public SNBTReader(String content) { super(content); @@ -81,9 +77,53 @@ public final class SNBTReader extends DefaultStringReader { } // 解析列表值 [1, 2, 3] - private List parseList() { + private Object parseList() { skip(); // 跳过 '[' skipWhitespace(); + + // 检查接下来的2个非空格字符, 确认是否要走数组解析. + if (canRead()) { + setMarker(cursor); // 记录指针, 尝试解析数组. + char typeChar = Character.toLowerCase(peek()); + if (typeChar == BYTE_ARRAY || typeChar == INT_ARRAY || typeChar == LONG_ARRAY) { + skip(); + skipWhitespace(); + if (canRead() && peek() == ARRAY_DELIMITER) { // 下一个必须是 ';' + skip(); + switch (typeChar) { // 解析并返回数组喵 + case BYTE_ARRAY -> { + return parseArray(list -> { + byte[] bytes = new byte[list.size()]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = list.get(i).byteValue(); + } + return bytes; + }); + } + case INT_ARRAY -> { + return parseArray(list -> { + int[] ints = new int[list.size()]; + for (int i = 0; i < ints.length; i++) { + ints[i] = list.get(i).intValue(); + } + return ints; + }); + } + case LONG_ARRAY -> { + return parseArray(list -> { + long[] longs = new long[list.size()]; + for (int i = 0; i < longs.length; i++) { + longs[i] = list.get(i).longValue(); + } + return longs; + }); + } + } + } + } + restore(); // 复原指针. + } + List elementList = new ArrayList<>(); if (canRead() && peek() != LIST_END) { @@ -100,6 +140,34 @@ public final class SNBTReader extends DefaultStringReader { return elementList; } + // 解析数组 [I; 11, 41, 54] + // ArrayType -> B, I, L. + private Object parseArray(Function, Object> convertor) { + skipWhitespace(); + // 用来暂存解析出的数字 + List elements = new ArrayList<>(); + if (canRead() && peek() != LIST_END) { + do { + Object element = parseValue(); + + // 1.21.6的SNBT原版是支持 {key:[B;1,2b,0xFF]} 这种奇葩写法的, 越界部分会被自动舍弃, 如0xff的byte值为-1. + // 如果需要和原版对齐, 那么只需要判断是否是数字就行了. + // if (!(element instanceof Number number)) + // throw new IllegalArgumentException("Error element type at pos " + getCursor()); + if (!(element instanceof Number number)) + throw new IllegalArgumentException("Error parsing number at pos " + getCursor()); + + elements.add(number); // 校验通过后加入 + skipWhitespace(); + } while (canRead() && peek() == ELEMENT_SEPARATOR && ++cursor > 0 /* 跳过 ',' */); + } + + if (!canRead() || peek() != LIST_END) + throw new IllegalArgumentException("Expected ']' at position " + getCursor()); + skip(); // 跳过 ']' + return convertor.apply(elements); + } + // 解析Key值 private String parseKey() { skipWhitespace(); @@ -149,33 +217,35 @@ public final class SNBTReader extends DefaultStringReader { } } int tokenLength = getCursor() - tokenStart - lastWhitespace; // 计算值长度需要再减去尾部空格. - if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); + if (tokenLength == 0) return null; // 如果值长度为0则返回null. if (contentHasWhitespace) return substring(tokenStart, tokenStart + tokenLength); // 如果值的中间有空格, 一定是字符串, 可直接返回. // 布尔值检查 if (tokenLength == 4) { if (matchesAt(tokenStart, "true")) return Boolean.TRUE; + if (matchesAt(tokenStart, "null")) return null; // 支持 {key:null}. } else if (tokenLength == 5) { if (matchesAt(tokenStart, "false")) return Boolean.FALSE; } if (tokenLength > 1) { // 至少有1个字符,给了后缀的可能性 char lastChar = charAt(tokenStart + tokenLength - 1); + lastChar = Character.toLowerCase(lastChar); // 强制转小写进行匹配. try { switch (lastChar) { - case BYTE_SUFFIX -> { + case 'b', 'B' -> { return Byte.parseByte(substring(tokenStart, tokenStart + tokenLength - 1)); } - case SHORT_SUFFIX -> { + case 's', 'S' -> { return Short.parseShort(substring(tokenStart, tokenStart + tokenLength - 1)); } - case LONG_SUFFIX -> { + case 'l', 'L' -> { return Long.parseLong(substring(tokenStart, tokenStart + tokenLength - 1)); } - case FLOAT_SUFFIX -> { + case 'f', 'F' -> { return Float.parseFloat(substring(tokenStart, tokenStart + tokenLength)); } - case DOUBLE_SUFFIX -> { + case 'd', 'D' -> { return Double.parseDouble(substring(tokenStart, tokenStart + tokenLength)); } default -> {