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 d95bce0d7..ec6fbc279 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 @@ -16,6 +16,11 @@ public final class SNBTReader extends DefaultStringReader { private static final char KEY_VALUE_SEPARATOR = ':'; private static final char ELEMENT_SEPARATOR = ','; + 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'; + // 数字类型后缀 private static final char BYTE_SUFFIX = 'b'; private static final char SHORT_SUFFIX = 's'; @@ -81,9 +86,31 @@ public final class SNBTReader extends DefaultStringReader { } // 解析列表值 [1, 2, 3] - private List parseList() { + private Object parseList() { skip(); // 跳过 '[' skipWhitespace(); + + // 检查接下来的2个非空格字符, 确认是否要走数组解析. + if (canRead()) { + setMarker(cursor); // 记录指针, 尝试解析数组. + char typeChar = peek(); + if (typeChar < 'a') typeChar = (char) (typeChar + 32); // 强制转小写进行匹配. + if (typeChar == BYTE_ARRAY || typeChar == INT_ARRAY || typeChar == LONG_ARRAY) { + skip(); + skipWhitespace(); + if (canRead() && peek() == ARRAY_DELIMITER) { // 下一个必须是 ';' + skip(); + return switch (typeChar){ // 解析并返回数组喵 + case BYTE_ARRAY -> parseArray(Byte.class); + case INT_ARRAY -> parseArray(Integer.class); + case LONG_ARRAY -> parseArray(Long.class); + default -> throw new IllegalArgumentException("Unknown typed-array prefix: " + typeChar); + }; + } + } + restore(); // 复原指针. + } + List elementList = new ArrayList<>(); if (canRead() && peek() != LIST_END) { @@ -100,6 +127,55 @@ public final class SNBTReader extends DefaultStringReader { return elementList; } + // 解析数组 [I; 11, 41, 54] + // ArrayType -> B, I, L. + private Object parseArray(Class arrayType) { + 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 (!arrayType.isInstance(element)) + throw new IllegalArgumentException("Error element type at pos " + getCursor()); + + elements.add((Number) element); // 校验通过后加入 + skipWhitespace(); + } while (canRead() && peek() == ELEMENT_SEPARATOR && ++cursor > 0 /* 跳过 ',' */); + } + + if (!canRead() || peek() != LIST_END) + throw new IllegalArgumentException("Expected ']' at position " + getCursor()); + + skip(); // 跳过 ']' + + // 根据数组类型创建并返回对应的数组 + // 构造并返回真正的 Java 数组 + return switch (arrayType.getSimpleName()) { + case "Byte" -> { + byte[] arr = new byte[elements.size()]; + for (int i = 0; i < arr.length; i++) arr[i] = elements.get(i).byteValue(); + yield arr; + } + case "Integer" -> { + int[] arr = new int[elements.size()]; + for (int i = 0; i < arr.length; i++) arr[i] = elements.get(i).intValue(); + yield arr; + } + case "Long" -> { + long[] arr = new long[elements.size()]; + for (int i = 0; i < arr.length; i++) arr[i] = elements.get(i).longValue(); + yield arr; + } + default -> throw new IllegalArgumentException("Unknown typed-array prefix: " + getCursor()); + }; + } + // 解析Key值 private String parseKey() { skipWhitespace();