From 725aa5c6e85eb6a8cddb579a662c7eca8d58c71d Mon Sep 17 00:00:00 2001 From: Catnies Date: Tue, 24 Jun 2025 05:30:49 +0800 Subject: [PATCH 01/14] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader#parsePrim?= =?UTF-8?q?itive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{SNBTDeserializer.java => SNBTReader.java} | 82 ++++++++++++------- 1 file changed, 52 insertions(+), 30 deletions(-) rename core/src/main/java/net/momirealms/craftengine/core/util/{SNBTDeserializer.java => SNBTReader.java} (80%) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java similarity index 80% rename from core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java rename to core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index 573493ce5..eeaf05a09 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -177,47 +177,43 @@ public final class SNBTDeserializer { // 解析原生值 private Object parsePrimitive() { - skipWhitespace(); - int tokenStart = position; - // 先解析获取值的长度 + int tokenStart = position; while (position < length) { char c = sourceContent[position]; if (c <= ' ' || c == ',' || c == ']' || c == '}') break; position++; } int tokenLength = position - tokenStart; - if (tokenLength == 0) { - throw new IllegalArgumentException("Empty value at position " + tokenStart); - } + if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); // 布尔值快速检查 - if (tokenLength == TRUE_LENGTH && matchesAt(tokenStart, TRUE_LITERAL)) { - return Boolean.TRUE; - } - if (tokenLength == FALSE_LENGTH && matchesAt(tokenStart, FALSE_LITERAL)) { - return Boolean.FALSE; - } + if (tokenLength == TRUE_LENGTH && matchesAt(tokenStart, TRUE_LITERAL)) return Boolean.TRUE; + if (tokenLength == FALSE_LENGTH && matchesAt(tokenStart, FALSE_LITERAL)) return Boolean.FALSE; + + // 无后缀数字处理 + if (isNumber(tokenStart, tokenStart + tokenLength) == 1) return Integer.parseInt(new String(sourceContent, tokenStart, tokenLength)); + if (isNumber(tokenStart, tokenStart + tokenLength) == 2) return Double.parseDouble(new String(sourceContent, tokenStart, tokenLength)); // 带后缀的值处理 - try { - char lastChar = sourceContent[tokenStart + tokenLength - 1]; - if (tokenLength > 1 && isTypeSuffix(lastChar)) { - return switch (lastChar) { - case BYTE_SUFFIX -> parseByte(tokenStart, tokenLength - 1); - case SHORT_SUFFIX -> parseShort(tokenStart, tokenLength - 1); - case LONG_SUFFIX -> parseLong(tokenStart, tokenLength - 1); - case FLOAT_SUFFIX -> Float.parseFloat(new String(sourceContent, tokenStart, tokenLength - 1)); - case DOUBLE_SUFFIX -> Double.parseDouble(new String(sourceContent, tokenStart, tokenLength - 1)); - case BOOLEAN_SUFFIX -> parseBoolean(new String(sourceContent, tokenStart, tokenLength - 1)); - default -> throw new IllegalArgumentException("Invalid suffixed value " + new String(sourceContent, tokenStart, tokenLength - 1)); - }; - } - // 没有后缀就默认为 double 喵 - return Double.parseDouble(new String(sourceContent, tokenStart, tokenLength)); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Invalid number value at position " + tokenStart, e); + char lastChar = sourceContent[tokenStart + tokenLength - 1]; + if (tokenLength > 1 && // 要求: 长度>1 + isTypeSuffix(lastChar) && // 要求: 有效后缀 + isNumber(tokenStart, tokenStart + tokenLength - 1) > 0 // 要求: 除后缀外是合法数字 + ) { + return switch (lastChar) { + case BYTE_SUFFIX -> parseByte(tokenStart, tokenLength - 1); + case SHORT_SUFFIX -> parseShort(tokenStart, tokenLength - 1); + case LONG_SUFFIX -> parseLong(tokenStart, tokenLength - 1); + case FLOAT_SUFFIX -> Float.parseFloat(new String(sourceContent, tokenStart, tokenLength - 1)); + case DOUBLE_SUFFIX -> Double.parseDouble(new String(sourceContent, tokenStart, tokenLength - 1)); + case BOOLEAN_SUFFIX -> parseBoolean(new String(sourceContent, tokenStart, tokenLength - 1)); + default -> new IllegalArgumentException("Parse Error with: " + new String(sourceContent, tokenStart, tokenLength - 1)); // 永远不应进入此 case. + }; } + + // 都无法匹配就默认为 String 喵~ + return new String(sourceContent, tokenStart, tokenLength); } // 工具函数: 快速检查布尔值字符串匹配, 忽略大小写. @@ -317,7 +313,6 @@ public final class SNBTDeserializer { return sourceContent[position]; } - // 跳过空格 private void skipWhitespace() { while (position < length) { @@ -331,4 +326,31 @@ public final class SNBTDeserializer { } } + // 检查是不是合法数字. + // 返回0代表不合法, 1代表整数, 2代表小数. + private int isNumber(int start, int end) { + // 跳过负号 + if (sourceContent[start] == '-') start++; + + // 除负号外第一个字符必须是数字. + char c1 = sourceContent[start]; + if (c1 < '0' || c1 > '9') return 0; + + // 检查剩余的数字, 只能有一个小数点 + boolean hasDecimal = false; + for (; start < end; start++) { + char c = sourceContent[start]; + if (c < '0' || c > '9') { + if (c == '.') { + if (hasDecimal) return 0; // bro 不能有2个小数点. + hasDecimal = true; + } else { + return 0; + } + } + } + + return hasDecimal ? 2 : 1; + } + } From 424d4c3c9aa05b89f1615de423d71b54f5065471 Mon Sep 17 00:00:00 2001 From: Catnies Date: Tue, 24 Jun 2025 05:46:09 +0800 Subject: [PATCH 02/14] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader,=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8D=95=E5=BC=95=E5=8F=B7=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E5=88=86=E9=9A=94=E7=AC=A6=20.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{SNBTReader.java => SNBTDeserializer.java} | 131 +++++++++--------- 1 file changed, 66 insertions(+), 65 deletions(-) rename core/src/main/java/net/momirealms/craftengine/core/util/{SNBTReader.java => SNBTDeserializer.java} (74%) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java similarity index 74% rename from core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java rename to core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java index eeaf05a09..ec34b9bbe 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java @@ -11,6 +11,8 @@ public final class SNBTDeserializer { private static final char LIST_START = '['; private static final char LIST_END = ']'; private static final char STRING_DELIMITER = '"'; + private static final char SINGLE_QUOTES = '\''; + private static final char DOUBLE_QUOTES = '"'; private static final char KEY_VALUE_SEPARATOR = ':'; private static final char ELEMENT_SEPARATOR = ','; private static final char ESCAPE_CHAR = '\\'; @@ -23,15 +25,10 @@ public final class SNBTDeserializer { private static final char DOUBLE_SUFFIX = 'd'; private static final char BOOLEAN_SUFFIX = 'B'; - // 布尔值常量 - private static final String TRUE_LITERAL = "true"; - private static final String FALSE_LITERAL = "false"; - private static final int TRUE_LENGTH = 4; - private static final int FALSE_LENGTH = 5; - + // 使用 char[] 处理获得更高的性能 private final char[] sourceContent; private final int length; - private int position = 0; + private int cursor = 0; private SNBTDeserializer(String content) { this.sourceContent = content.toCharArray(); @@ -44,10 +41,10 @@ public final class SNBTDeserializer { Object result = parser.parseValue(); parser.skipWhitespace(); - if (parser.position != parser.length) { + if (parser.cursor != parser.length) { throw new IllegalArgumentException("Extra content at end: " + - new String(parser.sourceContent, parser.position, - parser.length - parser.position)); + new String(parser.sourceContent, parser.cursor, + parser.length - parser.cursor)); } return result; } @@ -58,92 +55,94 @@ public final class SNBTDeserializer { return switch (peekCurrentChar()) { case COMPOUND_START -> parseCompound(); case LIST_START -> parseList(); - case STRING_DELIMITER -> parseString(); + case DOUBLE_QUOTES -> parseString(DOUBLE_QUOTES); + case SINGLE_QUOTES -> parseString(SINGLE_QUOTES); default -> parsePrimitive(); }; } // 解析包小肠 {} private Map parseCompound() { - position++; // 跳过 '{' + cursor++; // 跳过 '{' skipWhitespace(); Map compoundMap = new LinkedHashMap<>(16); // 避免一次扩容, 应该有一定的性能提升 - if (position < length && sourceContent[position] != COMPOUND_END) { + if (cursor < length && sourceContent[cursor] != COMPOUND_END) { do { String key = parseKey(); - if (position >= length || sourceContent[position] != KEY_VALUE_SEPARATOR) { - throw new IllegalArgumentException("Expected ':' at position " + position); + if (cursor >= length || sourceContent[cursor] != KEY_VALUE_SEPARATOR) { + throw new IllegalArgumentException("Expected ':' at position " + cursor); } - position++; // 跳过 ':' + cursor++; // 跳过 ':' Object value = parseValue(); compoundMap.put(key, value); skipWhitespace(); - } while (position < length && sourceContent[position] == ELEMENT_SEPARATOR && ++position > 0); + } while (cursor < length && sourceContent[cursor] == ELEMENT_SEPARATOR && ++cursor > 0); } - if (position >= length || sourceContent[position] != COMPOUND_END) { - throw new IllegalArgumentException("Expected '}' at position " + position); + if (cursor >= length || sourceContent[cursor] != COMPOUND_END) { + throw new IllegalArgumentException("Expected '}' at position " + cursor); } - position++; // 跳过 '}' + cursor++; // 跳过 '}' return compoundMap; } // 解析列表值 [1, 2, 3] private List parseList() { - position++; // 跳过 '[' + cursor++; // 跳过 '[' skipWhitespace(); List elementList = new ArrayList<>(); - if (position < length && sourceContent[position] != LIST_END) { + if (cursor < length && sourceContent[cursor] != LIST_END) { do { elementList.add(parseValue()); skipWhitespace(); - } while (position < length && sourceContent[position] == ELEMENT_SEPARATOR && ++position > 0); + } while (cursor < length && sourceContent[cursor] == ELEMENT_SEPARATOR && ++cursor > 0); } - if (position >= length || sourceContent[position] != LIST_END) { - throw new IllegalArgumentException("Expected ']' at position " + position); + if (cursor >= length || sourceContent[cursor] != LIST_END) { + throw new IllegalArgumentException("Expected ']' at position " + cursor); } - position++; // 跳过 ']' + cursor++; // 跳过 ']' return elementList; } // 解析字符串 - private String parseString() { - position++; // 跳过开始的引号 - int start = position; + private String parseString(char delimiter) { + cursor++; // 跳过开始的引号 + int start = cursor; // 扫一次字符串, 如果没有发现转义就直接返回, 发现了就再走转义解析. // 这样可以避免创建一次 StringBuilder. - while (position < length) { - char c = sourceContent[position]; - if (c == STRING_DELIMITER) { - String result = new String(sourceContent, start, position - start); - position++; // 跳过结束引号 + while (cursor < length) { + char c = sourceContent[cursor]; + if (c == delimiter) { + String result = new String(sourceContent, start, cursor - start); + cursor++; // 跳过结束引号 return result; // 没有转义直接返回字符串. } - // 如果发现转义字符, + // 如果发现转义字符,进行转义处理. else if (c == ESCAPE_CHAR) { - return parseStringWithEscape(start); + return parseStringWithEscape(start, delimiter); } - position++; + cursor++; } // 没有扫描到结束引号 throw new IllegalArgumentException("Unterminated string at " + start); } // 处理含转义的字符串 - private String parseStringWithEscape(int start) { - StringBuilder sb = new StringBuilder(position - start + 16); - sb.append(sourceContent, start, position - start); + private String parseStringWithEscape(int start, char delimiter) { + // 先把之前遍历没有包含转义的字符保存 + StringBuilder sb = new StringBuilder(cursor - start + 16); + sb.append(sourceContent, start, cursor - start); - while (position < length) { - char c = sourceContent[position++]; - if (c == ESCAPE_CHAR && position < length) { - sb.append(getEscapedChar(sourceContent[position++])); - } else if (c == STRING_DELIMITER) { // 字符 + while (cursor < length) { + char c = sourceContent[cursor++]; + if (c == ESCAPE_CHAR && cursor < length) { + sb.append(getEscapedChar(sourceContent[cursor++])); // 解析转义. + } else if (c == delimiter) { // 发现结束分隔字符, 返回. return sb.toString(); } else { sb.append(c); @@ -157,39 +156,40 @@ public final class SNBTDeserializer { private String parseKey() { skipWhitespace(); // 如果有双引号就委托给string解析处理. - if (position < length && sourceContent[position] == STRING_DELIMITER) { - return parseString(); + if (cursor < length) { + if (sourceContent[cursor] == STRING_DELIMITER) return parseString(STRING_DELIMITER); + if (sourceContent[cursor] == SINGLE_QUOTES) return parseString(SINGLE_QUOTES); } - int start = position; - while (position < length) { - char c = sourceContent[position]; + int start = cursor; + while (cursor < length) { + char c = sourceContent[cursor]; if (Character.isJavaIdentifierPart(c)) { - position++; + cursor++; } else { break; } } skipWhitespace(); - return new String(sourceContent, start, position - start); + return new String(sourceContent, start, cursor - start); } // 解析原生值 private Object parsePrimitive() { // 先解析获取值的长度 - int tokenStart = position; - while (position < length) { - char c = sourceContent[position]; + int tokenStart = cursor; + while (cursor < length) { + char c = sourceContent[cursor]; if (c <= ' ' || c == ',' || c == ']' || c == '}') break; - position++; + cursor++; } - int tokenLength = position - tokenStart; + int tokenLength = cursor - tokenStart; if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); // 布尔值快速检查 - if (tokenLength == TRUE_LENGTH && matchesAt(tokenStart, TRUE_LITERAL)) return Boolean.TRUE; - if (tokenLength == FALSE_LENGTH && matchesAt(tokenStart, FALSE_LITERAL)) return Boolean.FALSE; + if (tokenLength == 4 && matchesAt(tokenStart, "true")) return Boolean.TRUE; + if (tokenLength == 5 && matchesAt(tokenStart, "false")) return Boolean.FALSE; // 无后缀数字处理 if (isNumber(tokenStart, tokenStart + tokenLength) == 1) return Integer.parseInt(new String(sourceContent, tokenStart, tokenLength)); @@ -295,7 +295,8 @@ public final class SNBTDeserializer { // 转义字符处理 private char getEscapedChar(char escapedChar) { return switch (escapedChar) { - case STRING_DELIMITER -> '"'; + case DOUBLE_QUOTES -> '"'; + case SINGLE_QUOTES -> '\''; case ESCAPE_CHAR -> '\\'; case COMPOUND_START -> '{'; case COMPOUND_END -> '}'; @@ -309,17 +310,17 @@ public final class SNBTDeserializer { // 获取当前字符 private char peekCurrentChar() { - if (position >= length) throw new IllegalArgumentException("Unexpected end of input at position " + position); - return sourceContent[position]; + if (cursor >= length) throw new IllegalArgumentException("Unexpected end of input at position " + cursor); + return sourceContent[cursor]; } // 跳过空格 private void skipWhitespace() { - while (position < length) { - char c = sourceContent[position]; + while (cursor < length) { + char c = sourceContent[cursor]; if (c > ' ') { break; } // 大于空格的字符都不是空白字符 if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { - position++; + cursor++; } else { break; } From 113827f62162c637da2d43b7188414e8d1c798df Mon Sep 17 00:00:00 2001 From: Catnies Date: Tue, 24 Jun 2025 07:37:36 +0800 Subject: [PATCH 03/14] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader,=20?= =?UTF-8?q?=E4=BD=BF=E5=85=B6=E7=BB=A7=E6=89=BFDefaultStringReader.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{SNBTDeserializer.java => SNBTReader.java} | 189 +++++------------- 1 file changed, 51 insertions(+), 138 deletions(-) rename core/src/main/java/net/momirealms/craftengine/core/util/{SNBTDeserializer.java => SNBTReader.java} (55%) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java similarity index 55% rename from core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java rename to core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index ec34b9bbe..f7bc00d6e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -5,7 +5,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -public final class SNBTDeserializer { +public final class SNBTReader extends DefaultStringReader { private static final char COMPOUND_START = '{'; private static final char COMPOUND_END = '}'; private static final char LIST_START = '['; @@ -23,36 +23,29 @@ public final class SNBTDeserializer { 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 BOOLEAN_SUFFIX = 'B'; - // 使用 char[] 处理获得更高的性能 - private final char[] sourceContent; private final int length; - private int cursor = 0; - - private SNBTDeserializer(String content) { - this.sourceContent = content.toCharArray(); - this.length = sourceContent.length; + private SNBTReader(String content) { + super(content); + this.length = this.string.length(); } // 入口API public static Object deserializeAsJava(String input) throws IllegalArgumentException { - SNBTDeserializer parser = new SNBTDeserializer(input); + SNBTReader parser = new SNBTReader(input); Object result = parser.parseValue(); parser.skipWhitespace(); - if (parser.cursor != parser.length) { - throw new IllegalArgumentException("Extra content at end: " + - new String(parser.sourceContent, parser.cursor, - parser.length - parser.cursor)); - } + if (parser.cursor != parser.length) + throw new IllegalArgumentException("Extra content at end: " + parser.string.substring(parser.cursor, parser.length - parser.cursor)); + return result; } // 开始解析, 步进字符. private Object parseValue() { skipWhitespace(); - return switch (peekCurrentChar()) { + return switch (peek()) { case COMPOUND_START -> parseCompound(); case LIST_START -> parseList(); case DOUBLE_QUOTES -> parseString(DOUBLE_QUOTES); @@ -68,20 +61,20 @@ public final class SNBTDeserializer { Map compoundMap = new LinkedHashMap<>(16); // 避免一次扩容, 应该有一定的性能提升 - if (cursor < length && sourceContent[cursor] != COMPOUND_END) { + if (cursor < length && peek() != COMPOUND_END) { do { String key = parseKey(); - if (cursor >= length || sourceContent[cursor] != KEY_VALUE_SEPARATOR) { + if (cursor >= length || peek() != KEY_VALUE_SEPARATOR) { throw new IllegalArgumentException("Expected ':' at position " + cursor); } cursor++; // 跳过 ':' Object value = parseValue(); compoundMap.put(key, value); skipWhitespace(); - } while (cursor < length && sourceContent[cursor] == ELEMENT_SEPARATOR && ++cursor > 0); + } while (cursor < length && peek() == ELEMENT_SEPARATOR && ++cursor > 0); } - if (cursor >= length || sourceContent[cursor] != COMPOUND_END) { + if (cursor >= length || peek() != COMPOUND_END) { throw new IllegalArgumentException("Expected '}' at position " + cursor); } cursor++; // 跳过 '}' @@ -94,14 +87,14 @@ public final class SNBTDeserializer { skipWhitespace(); List elementList = new ArrayList<>(); - if (cursor < length && sourceContent[cursor] != LIST_END) { + if (cursor < length && peek() != LIST_END) { do { elementList.add(parseValue()); skipWhitespace(); - } while (cursor < length && sourceContent[cursor] == ELEMENT_SEPARATOR && ++cursor > 0); + } while (cursor < length && peek() == ELEMENT_SEPARATOR && ++cursor > 0); } - if (cursor >= length || sourceContent[cursor] != LIST_END) { + if (cursor >= length || peek() != LIST_END) { throw new IllegalArgumentException("Expected ']' at position " + cursor); } cursor++; // 跳过 ']' @@ -116,9 +109,9 @@ public final class SNBTDeserializer { // 扫一次字符串, 如果没有发现转义就直接返回, 发现了就再走转义解析. // 这样可以避免创建一次 StringBuilder. while (cursor < length) { - char c = sourceContent[cursor]; + char c = peek(); if (c == delimiter) { - String result = new String(sourceContent, start, cursor - start); + String result = string.substring(start, cursor); cursor++; // 跳过结束引号 return result; // 没有转义直接返回字符串. } @@ -136,12 +129,12 @@ public final class SNBTDeserializer { private String parseStringWithEscape(int start, char delimiter) { // 先把之前遍历没有包含转义的字符保存 StringBuilder sb = new StringBuilder(cursor - start + 16); - sb.append(sourceContent, start, cursor - start); + sb.append(string, start, cursor); while (cursor < length) { - char c = sourceContent[cursor++]; + char c = read(); if (c == ESCAPE_CHAR && cursor < length) { - sb.append(getEscapedChar(sourceContent[cursor++])); // 解析转义. + sb.append(getEscapedChar(read())); // 解析转义. } else if (c == delimiter) { // 发现结束分隔字符, 返回. return sb.toString(); } else { @@ -157,22 +150,17 @@ public final class SNBTDeserializer { skipWhitespace(); // 如果有双引号就委托给string解析处理. if (cursor < length) { - if (sourceContent[cursor] == STRING_DELIMITER) return parseString(STRING_DELIMITER); - if (sourceContent[cursor] == SINGLE_QUOTES) return parseString(SINGLE_QUOTES); + if (peek() == STRING_DELIMITER) return parseString(STRING_DELIMITER); + if (peek() == SINGLE_QUOTES) return parseString(SINGLE_QUOTES); } int start = cursor; while (cursor < length) { - char c = sourceContent[cursor]; - if (Character.isJavaIdentifierPart(c)) { - cursor++; - } else { - break; - } + if (Character.isJavaIdentifierPart(peek())) cursor++; else break; } skipWhitespace(); - return new String(sourceContent, start, cursor - start); + return string.substring(start, cursor); } // 解析原生值 @@ -180,46 +168,51 @@ public final class SNBTDeserializer { // 先解析获取值的长度 int tokenStart = cursor; while (cursor < length) { - char c = sourceContent[cursor]; + char c = peek(); if (c <= ' ' || c == ',' || c == ']' || c == '}') break; cursor++; } int tokenLength = cursor - tokenStart; if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); + String fullContent = string.substring(tokenStart, tokenStart + tokenLength); // 布尔值快速检查 - if (tokenLength == 4 && matchesAt(tokenStart, "true")) return Boolean.TRUE; - if (tokenLength == 5 && matchesAt(tokenStart, "false")) return Boolean.FALSE; + if ("1B".equals(fullContent) || (tokenLength == 4 && matchesAt(tokenStart, "true"))) return Boolean.TRUE; + if ("0B".equals(fullContent) || (tokenLength == 5 && matchesAt(tokenStart, "false"))) return Boolean.FALSE; // 无后缀数字处理 - if (isNumber(tokenStart, tokenStart + tokenLength) == 1) return Integer.parseInt(new String(sourceContent, tokenStart, tokenLength)); - if (isNumber(tokenStart, tokenStart + tokenLength) == 2) return Double.parseDouble(new String(sourceContent, tokenStart, tokenLength)); + if (isNumber(tokenStart, tokenStart + tokenLength) == 1) return Integer.parseInt(fullContent); + if (isNumber(tokenStart, tokenStart + tokenLength) == 2) return Double.parseDouble(fullContent); // 带后缀的值处理 - char lastChar = sourceContent[tokenStart + tokenLength - 1]; + char lastChar = string.charAt(tokenStart + tokenLength - 1); if (tokenLength > 1 && // 要求: 长度>1 isTypeSuffix(lastChar) && // 要求: 有效后缀 isNumber(tokenStart, tokenStart + tokenLength - 1) > 0 // 要求: 除后缀外是合法数字 ) { - return switch (lastChar) { - case BYTE_SUFFIX -> parseByte(tokenStart, tokenLength - 1); - case SHORT_SUFFIX -> parseShort(tokenStart, tokenLength - 1); - case LONG_SUFFIX -> parseLong(tokenStart, tokenLength - 1); - case FLOAT_SUFFIX -> Float.parseFloat(new String(sourceContent, tokenStart, tokenLength - 1)); - case DOUBLE_SUFFIX -> Double.parseDouble(new String(sourceContent, tokenStart, tokenLength - 1)); - case BOOLEAN_SUFFIX -> parseBoolean(new String(sourceContent, tokenStart, tokenLength - 1)); - default -> new IllegalArgumentException("Parse Error with: " + new String(sourceContent, tokenStart, tokenLength - 1)); // 永远不应进入此 case. - }; + final String content = string.substring(tokenStart, tokenStart + tokenLength - 1); + try { + return switch (lastChar) { + case BYTE_SUFFIX -> Byte.parseByte(content); + case SHORT_SUFFIX -> Short.parseShort(content); + case LONG_SUFFIX -> Long.parseLong(content); + case FLOAT_SUFFIX -> Float.parseFloat(content); + case DOUBLE_SUFFIX -> Double.parseDouble(content); + default -> new IllegalArgumentException("Parse Error with: " + content); // 永远不应进入此 case. + }; + } catch (NumberFormatException e) { + return fullContent; // 如果有神人写了 128b 一类的超范围值, 就当做字符串返回好了. + } } // 都无法匹配就默认为 String 喵~ - return new String(sourceContent, tokenStart, tokenLength); + return fullContent; } // 工具函数: 快速检查布尔值字符串匹配, 忽略大小写. private boolean matchesAt(int start, String target) { for (int i = 0; i < target.length(); i++) { - char c1 = sourceContent[start + i]; + char c1 = string.charAt(start + i); char c2 = target.charAt(i); if (c1 != c2 && c1 != (c2 ^ 32)) return false; // 忽略大小写比较 } @@ -228,68 +221,7 @@ public final class SNBTDeserializer { // 工具函数: 合法后缀检查 private boolean isTypeSuffix(char c) { - return c == BYTE_SUFFIX || c == SHORT_SUFFIX || c == LONG_SUFFIX || - c == FLOAT_SUFFIX || c == DOUBLE_SUFFIX || c == BOOLEAN_SUFFIX; - } - - - // 手动解析值 - private byte parseByte(int start, int length) { - int value = parseInteger(start, length); - if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) - throw new IllegalArgumentException("Byte value out of range"); - return (byte) value; - } - - private short parseShort(int start, int length) { - int value = parseInteger(start, length); - if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) - throw new NumberFormatException("Short value out of range"); - return (short) value; - } - - private Boolean parseBoolean(String content) { - if ("1".equals(content)) return Boolean.TRUE; - if ("0".equals(content)) return Boolean.FALSE; - throw new NumberFormatException("Invalid boolean value"); - } - - private int parseInteger(int start, int length) { - int result = 0; - boolean negative = false; - int i = 0; - - if (sourceContent[start] == '-') { - negative = true; - i = 1; - } - - for (; i < length; i++) { - char c = sourceContent[start + i]; - if (c < '0' || c > '9') throw new NumberFormatException("Invalid integer"); - result = result * 10 + (c - '0'); - } - - return negative ? -result : result; - } - - private long parseLong(int start, int length) { - long result = 0; - boolean negative = false; - int i = 0; - - if (sourceContent[start] == '-') { - negative = true; - i = 1; - } - - for (; i < length; i++) { - char c = sourceContent[start + i]; - if (c < '0' || c > '9') throw new NumberFormatException("Invalid long"); - result = result * 10 + (c - '0'); - } - - return negative ? -result : result; + return c == BYTE_SUFFIX || c == SHORT_SUFFIX || c == LONG_SUFFIX || c == FLOAT_SUFFIX || c == DOUBLE_SUFFIX; } // 转义字符处理 @@ -308,39 +240,20 @@ public final class SNBTDeserializer { }; } - // 获取当前字符 - private char peekCurrentChar() { - if (cursor >= length) throw new IllegalArgumentException("Unexpected end of input at position " + cursor); - return sourceContent[cursor]; - } - - // 跳过空格 - private void skipWhitespace() { - while (cursor < length) { - char c = sourceContent[cursor]; - if (c > ' ') { break; } // 大于空格的字符都不是空白字符 - if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { - cursor++; - } else { - break; - } - } - } - // 检查是不是合法数字. // 返回0代表不合法, 1代表整数, 2代表小数. private int isNumber(int start, int end) { // 跳过负号 - if (sourceContent[start] == '-') start++; + if (string.charAt(start) == '-') start++; // 除负号外第一个字符必须是数字. - char c1 = sourceContent[start]; + char c1 = string.charAt(start); if (c1 < '0' || c1 > '9') return 0; // 检查剩余的数字, 只能有一个小数点 boolean hasDecimal = false; for (; start < end; start++) { - char c = sourceContent[start]; + char c = string.charAt(start); if (c < '0' || c > '9') { if (c == '.') { if (hasDecimal) return 0; // bro 不能有2个小数点. From 17129163706c2370115737cc44a39d000c36db41 Mon Sep 17 00:00:00 2001 From: Catnies Date: Tue, 24 Jun 2025 18:39:10 +0800 Subject: [PATCH 04/14] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader,=20?= =?UTF-8?q?=E5=BF=BD=E7=95=A5=20key=20=E5=90=8E=E9=9D=A2=E7=9A=84=E7=A9=BA?= =?UTF-8?q?=E6=A0=BC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/core/util/SNBTReader.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 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 f7bc00d6e..17c0086b7 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 @@ -59,7 +59,7 @@ public final class SNBTReader extends DefaultStringReader { cursor++; // 跳过 '{' skipWhitespace(); - Map compoundMap = new LinkedHashMap<>(16); // 避免一次扩容, 应该有一定的性能提升 + Map compoundMap = new LinkedHashMap<>(); if (cursor < length && peek() != COMPOUND_END) { do { @@ -156,11 +156,14 @@ public final class SNBTReader extends DefaultStringReader { int start = cursor; while (cursor < length) { - if (Character.isJavaIdentifierPart(peek())) cursor++; else break; + char c = peek(); + if (c == ' ') break; // 忽略 key 后面的空格, { a :1} 应当解析成 {a:1} + if (Character.isJavaIdentifierPart(c)) cursor++; else break; } - skipWhitespace(); - return string.substring(start, cursor); + String key = string.substring(start, cursor); + skipWhitespace(); // 跳过 key 后面的空格. + return key; } // 解析原生值 @@ -176,7 +179,7 @@ public final class SNBTReader extends DefaultStringReader { if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); String fullContent = string.substring(tokenStart, tokenStart + tokenLength); - // 布尔值快速检查 + // 布尔值检查 if ("1B".equals(fullContent) || (tokenLength == 4 && matchesAt(tokenStart, "true"))) return Boolean.TRUE; if ("0B".equals(fullContent) || (tokenLength == 5 && matchesAt(tokenStart, "false"))) return Boolean.FALSE; @@ -266,5 +269,4 @@ public final class SNBTReader extends DefaultStringReader { return hasDecimal ? 2 : 1; } - } From fba78b7e6dfaa285313d4abff2d19adbfb6185fd Mon Sep 17 00:00:00 2001 From: TamashiiMon <100000541+TamashiiMon@users.noreply.github.com> Date: Tue, 24 Jun 2025 15:54:47 +0200 Subject: [PATCH 05/14] Create de.yml --- .../src/main/resources/translations/de.yml | 382 ++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 common-files/src/main/resources/translations/de.yml diff --git a/common-files/src/main/resources/translations/de.yml b/common-files/src/main/resources/translations/de.yml new file mode 100644 index 000000000..731d82203 --- /dev/null +++ b/common-files/src/main/resources/translations/de.yml @@ -0,0 +1,382 @@ +# Nicht ändern +lang-version: "${lang_version}" + +exception.invalid_syntax: "Ungültige Syntax. Korrekte Syntax: " +exception.invalid_argument: "Ungültiges Argument. Grund: " +exception.invalid_sender: " ist nicht berechtigt, diesen Befehl auszuführen. Muss vom Typ sein" +exception.unexpected: "Beim Versuch, diesen Befehl auszuführen, ist ein interner Fehler aufgetreten" +exception.no_permission: "Es tut mir leid, aber Sie haben keine Berechtigung, diesen Befehl auszuführen" +exception.no_such_command: "Unbekannter Befehl." +argument.entity.notfound.player: "Spieler nicht gefunden: " +argument.entity.notfound.entity: "Entität nicht gefunden: " +argument.parse.failure.time: "'' ist kein gültiges Zeitformat" +argument.parse.failure.material: "'' ist kein gültiger Materialname" +argument.parse.failure.enchantment: "'' ist keine gültige Verzauberung" +argument.parse.failure.offlineplayer: "Kein Spieler für die Eingabe '' gefunden" +argument.parse.failure.player: "Kein Spieler für die Eingabe '' gefunden" +argument.parse.failure.world: "'' ist keine gültige Minecraft-Welt" +argument.parse.failure.location.invalid_format: "'' ist kein gültiger Ort. Erforderliches Format ist ' " +argument.parse.failure.location.mixed_local_absolute: "Lokale und absolute Koordinaten können nicht gemischt werden. (Entweder alle Koordinaten verwenden '^' oder keine)" +argument.parse.failure.namespacedkey.namespace: "Ungültiger Namespace ''. Muss [a-z0-9._-] sein" +argument.parse.failure.namespacedkey.key: "Ungültiger Schlüssel ''. Muss [a-z0-9/._-] sein" +argument.parse.failure.namespacedkey.need_namespace: "Ungültige Eingabe '', erfordert einen expliziten Namespace" +argument.parse.failure.boolean: "Konntest keinen booleschen Wert aus '' parsen" +argument.parse.failure.number: "'' ist keine gültige Zahl im Bereich von bis " +argument.parse.failure.char: "'' ist kein gültiges Zeichen" +argument.parse.failure.string: "'' ist kein gültiger String vom Typ " +argument.parse.failure.uuid: "'' ist keine gültige UUID" +argument.parse.failure.enum: "'' ist keiner der folgenden Werte: " +argument.parse.failure.regex: "'' stimmt nicht mit '' überein" +argument.parse.failure.flag.unknown: "Unbekanntes Flag ''" +argument.parse.failure.flag.duplicate_flag: "Doppeltes Flag ''" +argument.parse.failure.flag.no_flag_started: "Kein Flag gestartet. Weiß nicht, was mit '' zu tun ist" +argument.parse.failure.flag.missing_argument: "Fehlendes Argument für ''" +argument.parse.failure.flag.no_permission: "Sie haben keine Berechtigung, '' zu verwenden" +argument.parse.failure.color: "'' ist keine gültige Farbe" +argument.parse.failure.duration: "'' ist kein gültiges Zeitdauerformat" +argument.parse.failure.aggregate.missing: "Fehlende Komponente ''" +argument.parse.failure.aggregate.failure: "Ungültige Komponente '': " +argument.parse.failure.either: "Konnte oder aus '' nicht auflösen" +argument.parse.failure.namedtextcolor: "'' ist keine benannte Textfarbe" +command.reload.config.success: "Konfigurationen in ms neu geladen. (Async: ms | Sync: ms)" +command.reload.config.failure: "Neuladen der Konfiguration fehlgeschlagen. Überprüfen Sie die Konsolenprotokolle." +command.reload.pack.success: "Ressourcenpaket in ms neu geladen." +command.reload.pack.failure: "Neuladen des Ressourcenpakets fehlgeschlagen. Überprüfen Sie die Konsolenprotokolle." +command.reload.all.success: "Neu laden in ms abgeschlossen. (Async: ms | Sync: ms | Pack: ms)" +command.reload.all.failure: "Neu laden fehlgeschlagen. Überprüfen Sie die Konsolenprotokolle." +command.item.get.success: "Sie haben erhalten" +command.item.get.failure.not_exist: "'>" +command.item.give.success.single: "':'':''>" +command.item.give.success.multiple: "':'':''>" +command.item.give.failure.not_exist: "'>" +command.search_recipe.not_found: "Kein Rezept für diesen Gegenstand gefunden" +command.search_usage.not_found: "Keine Verwendung für diesen Gegenstand gefunden" +command.search_recipe.no_item: "Bitte halten Sie einen Gegenstand, bevor Sie diesen Befehl ausführen" +command.search_usage.no_item: "Bitte halten Sie einen Gegenstand, bevor Sie diesen Befehl ausführen" +command.totem_animation.failure.not_totem: "Gegenstand '' ist kein minecraft:totem_of_undying" +command.resource.enable.success: "Ressource aktiviert. Führen Sie /ce reload all aus, um die Änderungen anzuwenden" +command.resource.enable.failure.unknown: "Unbekannte Ressource " +command.resource.disable.success: "Ressource deaktiviert. Führen Sie /ce reload all aus, um die Änderungen anzuwenden" +command.resource.disable.failure.unknown: "Unbekannte Ressource " +command.resource.list: "Aktivierte Ressourcen(): Deaktivierte Ressourcen(): " +command.upload.failure.not_supported: "Die aktuelle Hosting-Methode '' unterstützt das Hochladen von Ressourcenpaketen nicht." +command.upload.on_progress: "Upload-Vorgang gestartet. Überprüfen Sie die Konsole für weitere Informationen." +command.send_resource_pack.success.single: "Ressourcenpaket an gesendet." +command.send_resource_pack.success.multiple: "Ressourcenpakete an Spieler gesendet." +warning.config.pack.duplicated_files: "Duplizierte Dateien gefunden. Bitte beheben Sie diese im Abschnitt 'resource-pack.duplicated-files-handler' in config.yml." +warning.config.yaml.duplicated_key: "Problem in Datei gefunden - Duplizierter Schlüssel '' in Zeile gefunden, dies kann zu unerwarteten Ergebnissen führen." +warning.config.type.int: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Integer-Typ für Option '' umwandeln." +warning.config.type.float: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Float-Typ für Option '' umwandeln." +warning.config.type.double: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Double-Typ für Option '' umwandeln." +warning.config.type.quaternionf: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Quaternionf-Typ für Option '' umwandeln." +warning.config.type.vector3f: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Vector3f-Typ für Option '' umwandeln." +warning.config.number.missing_type: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'type'-Argument für das Zahlenargument." +warning.config.number.invalid_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen Zahlargumenttyp ''." +warning.config.number.missing_argument: "Problem in Datei gefunden - Der Konfiguration '' fehlt das Argument für 'number'." +warning.config.number.invalid_format: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges Zahlenformat ''." +warning.config.number.fixed.missing_value: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value'-Argument für die 'constant'-Zahl." +warning.config.number.fixed.invalid_value: "Problem in Datei gefunden - Die Konfiguration '' verwendet das ungültige 'value'-Argument '' für die 'constant'-Zahl." +warning.config.number.expression.missing_expression: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'expression'-Argument für die 'expression'-Zahl." +warning.config.number.uniform.missing_min: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'min'-Argument für die 'uniform'-Zahl." +warning.config.number.uniform.missing_max: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'max'-Argument für die 'uniform'-Zahl." +warning.config.condition.all_of.missing_terms: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'terms'-Argument für die 'all_of'-Bedingung." +warning.config.condition.all_of.invalid_terms_type: "Problem in Datei gefunden - Die Konfiguration '' hat eine falsch konfigurierte 'all_of'-Bedingung, 'terms' sollte eine Kartenliste sein, aktueller Typ: ''." +warning.config.condition.any_of.missing_terms: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'terms'-Argument für die 'any_of'-Bedingung." +warning.config.condition.any_of.invalid_terms_type: "Problem in Datei gefunden - Die Konfiguration '' hat eine falsch konfigurierte 'any_of'-Bedingung, 'terms' sollte eine Kartenliste sein, aktueller Typ: ''." +warning.config.condition.inverted.missing_term: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'term'-Argument für die 'inverted'-Bedingung." +warning.config.condition.inverted.invalid_term_type: "Problem in Datei gefunden - Die Konfiguration '' hat eine falsch konfigurierte 'inverted'-Bedingung, 'term' sollte ein Konfigurationsabschnitt sein, aktueller Typ: ''." +warning.config.condition.enchantment.missing_predicate: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'predicate'-Argument für die 'enchantment'-Bedingung." +warning.config.condition.enchantment.invalid_predicate: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges Verzauberungs-'predicate'-Argument ''." +warning.config.condition.match_block_property.missing_properties: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'properties'-Argument für die 'match_block_property'-Bedingung." +warning.config.condition.match_item.missing_id: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'id'-Argument für die 'match_item'-Bedingung." +warning.config.condition.table_bonus.missing_enchantment: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'enchantment'-Argument für die 'table_bonus'-Bedingung." +warning.config.condition.table_bonus.missing_chances: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'chances'-Argument für die 'table_bonus'-Bedingung." +warning.config.condition.permission.missing_permission: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'permission'-Argument für die 'permission'-Bedingung." +warning.config.condition.string_equals.missing_value1: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value1'-Argument für die 'string_equals'-Bedingung." +warning.config.condition.string_equals.missing_value2: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value2'-Argument für die 'string_equals'-Bedingung." +warning.config.condition.string_contains.missing_value1: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value1'-Argument für die 'string_contains'-Bedingung." +warning.config.condition.string_contains.missing_value2: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value2'-Argument für die 'string_contains'-Bedingung." +warning.config.condition.string_regex.missing_value: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'value'-Argument für die 'string_regex'-Bedingung." +warning.config.condition.string_regex.missing_regex: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'regex'-Argument für die 'string_regex'-Bedingung." +warning.config.condition.expression.missing_expression: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'expression'-Argument für die 'expression'-Bedingung." +warning.config.condition.is_null.missing_argument: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'argument'-Argument für die 'is_null'-Bedingung." +warning.config.condition.hand.missing_hand: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'hand'-Argument für die 'hand'-Bedingung." +warning.config.condition.hand.invalid_hand: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges 'hand'-Argument '' für die 'hand'-Bedingung. Erlaubte Handtypen: []" +warning.config.condition.on_cooldown.missing_id: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'id'-Argument für die 'on_cooldown'-Bedingung." +warning.config.structure.not_section: "Problem in Datei gefunden - Die Konfiguration '' wird als Konfigurationsabschnitt erwartet, ist aber tatsächlich ein(e) ''." +warning.config.image.duplicate: "Problem in Datei gefunden - Dupliziertes Bild ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.image.missing_height: "Problem in Datei gefunden - Dem Bild '' fehlt das erforderliche 'height'-Argument." +warning.config.image.height_ascent_conflict: "Problem in Datei gefunden - Das Bild '' verletzt die Bitmap-Bildregel: 'height'-Argument '' sollte nicht niedriger als 'ascent'-Argument '' sein." +warning.config.image.missing_file: "Problem in Datei gefunden - Dem Bild '' fehlt das erforderliche 'file'-Argument." +warning.config.image.invalid_file_chars: "Problem in Datei gefunden - Das Bild '' hat ein 'file'-Argument '', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.image.invalid_font_chars: "Problem in Datei gefunden - Das Bild '' hat ein 'font'-Argument '', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.image.missing_char: "Problem in Datei gefunden - Dem Bild '' fehlt das erforderliche 'char'-Argument." +warning.config.image.codepoint_conflict: "Problem in Datei gefunden - Das Bild '' verwendet ein Zeichen '()' in Schriftart , das von einem anderen Bild '' verwendet wurde." +warning.config.image.invalid_codepoint_grid: "Problem in Datei gefunden - Bild '' hat ein ungültiges 'chars'-Codepunktgitter." +warning.config.image.invalid_char: "Problem in Datei gefunden - Bild '' hat einen Zeichenparameter, der kombinierende Zeichen enthält, was zu einer Bildaufteilung führen kann." +warning.config.image.invalid_hex_value: "Problem in Datei gefunden - Das Bild '' verwendet ein Unicode-Zeichen '', das kein gültiger Hexadezimalwert (Basis 16) ist." +warning.config.recipe.duplicate: "Problem in Datei gefunden - Dupliziertes Rezept ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.recipe.missing_type: "Problem in Datei gefunden - Dem Rezept '' fehlt das erforderliche 'type'-Argument." +warning.config.recipe.invalid_type: "Problem in Datei gefunden - Das Rezept '' verwendet einen ungültigen Rezepttyp ''." +warning.config.recipe.invalid_item: "Problem in Datei gefunden - Das Rezept '' verwendet einen ungültigen Gegenstand ''." +warning.config.recipe.missing_ingredient: "Problem in Datei gefunden - Dem Kochrezept '' fehlt das erforderliche 'ingredient'-Argument." +warning.config.recipe.missing_result: "Problem in Datei gefunden - Dem Rezept '' fehlt das erforderliche 'result'-Argument." +warning.config.recipe.result.missing_id: "Problem in Datei gefunden - Dem Rezept '' fehlt das erforderliche Argument 'id' für das Rezeptresultat." +warning.config.recipe.crafting.invalid_category: "Problem in Datei gefunden - Das Herstellungsrezept '' verwendet eine ungültige Kategorie ''. Erlaubte Kategorien: []." +warning.config.recipe.cooking.invalid_category: "Problem in Datei gefunden - Das Kochrezept '' verwendet eine ungültige Kategorie ''. Erlaubte Kategorien: []." +warning.config.recipe.shaped.missing_pattern: "Problem in Datei gefunden - Dem geformten Rezept '' fehlt das erforderliche Argument 'pattern'." +warning.config.recipe.shaped.invalid_pattern: "Problem in Datei gefunden - Das geformte Rezept '' verwendet ein ungültiges Muster ''." +warning.config.recipe.shaped.invalid_symbol: "Problem in Datei gefunden - Das geformte Rezept '' verwendet ein ungültiges Symbol '' im Muster." +warning.config.recipe.smithing_transform.post_processor.missing_type: "Problem in Datei gefunden - Dem Schmiedetransformationsrezept '' fehlt das erforderliche Argument 'type' für einen der Post-Prozessoren." +warning.config.recipe.smithing_transform.post_processor.invalid_type: "Problem in Datei gefunden - Das Schmiedetransformationsrezept '' verwendet einen ungültigen Post-Prozessor-Typ ''." +warning.config.recipe.smithing_transform.post_processor.keep_component.missing_components: "Problem in Datei gefunden - Dem Schmiedetransformationsrezept '' fehlt das erforderliche Argument 'components' für die Post-Prozessoren 'keep_components'." +warning.config.recipe.smithing_transform.post_processor.keep_component.missing_tags: "Problem in Datei gefunden - Dem Schmiedetransformationsrezept '' fehlt das erforderliche Argument 'tags' für die Post-Prozessoren 'keep_tags'." +warning.config.i18n.unknown_locale: "Problem in Datei gefunden - Unbekanntes Gebietsschema ''." +warning.config.template.duplicate: "Problem in Datei gefunden - Dupliziertes Template ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.template.invalid: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges Template ''." +warning.config.template.argument.self_increase_int.invalid_range: "Problem in Datei gefunden - Das Template '' verwendet einen 'from'-Wert '', der größer ist als der 'to'-Wert '' im 'self_increase_int'-Argument." +warning.config.template.argument.list.invalid_type: "Problem in Datei gefunden - Das Template '' verwendet ein 'list'-Argument, das eine 'List' als Argument erwartet, während das Eingabeargument ein(e) '' ist." +warning.config.template.argument.default_value.invalid_syntax: "Problem in Datei gefunden - Das Template '' verwendet einen ungültigen Standardwert '' für das Argument ''." +warning.config.vanilla_loot.missing_type: "Problem in Datei gefunden - Dem Vanilla-Beute '' fehlt das erforderliche 'type'-Argument." +warning.config.vanilla_loot.invalid_type: "Problem in Datei gefunden - Der Vanilla-Beute '' verwendet einen ungültigen Typ ''. Erlaubte Typen: []." +warning.config.vanilla_loot.block.invalid_target: "Problem in Datei gefunden - Ungültiges Blockziel '' in Vanilla-Beute ''." +warning.config.sound.duplicate: "Problem in Datei gefunden - Duplizierter Sound ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.sound.missing_sounds: "Problem in Datei gefunden - Dem Sound '' fehlt das erforderliche 'sounds'-Argument." +warning.config.sound.missing_name: "Problem in Datei gefunden - Dem Sound '' fehlt das erforderliche 'name'-Argument." +warning.config.jukebox_song.duplicate: "Problem in Datei gefunden - Dupliziertes Jukebox-Lied ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.jukebox_song.missing_sound: "Problem in Datei gefunden - Dem Jukebox-Lied '' fehlt das erforderliche 'sound'-Argument." +warning.config.furniture.duplicate: "Problem in Datei gefunden - Dupliziertes Möbelstück ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.furniture.missing_placement: "Problem in Datei gefunden - Dem Möbelstück '' fehlt das erforderliche 'placement'-Argument." +warning.config.furniture.element.missing_item: "Problem in Datei gefunden - Dem Möbelstück '' fehlt das erforderliche 'item'-Argument für eines seiner Elemente." +warning.config.furniture.settings.unknown: "Problem in Datei gefunden - Das Möbelstück '' verwendet einen unbekannten Einstellungstyp ''." +warning.config.furniture.hitbox.invalid_type: "Problem in Datei gefunden - Das Möbelstück '' verwendet einen ungültigen Hitbox-Typ ''." +warning.config.furniture.hitbox.custom.invalid_entity: "Problem in Datei gefunden - Das Möbelstück '' verwendet eine benutzerdefinierte Hitbox mit ungültigem Entitätstyp ''." +warning.config.item.duplicate: "Problem in Datei gefunden - Duplizierter Gegenstand ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.item.settings.unknown: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen unbekannten Einstellungstyp ''." +warning.config.item.settings.invulnerable.invalid_damage_source: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine unbekannte Schadensquelle ''. Erlaubte Quellen: []." +warning.config.item.missing_material: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'material'-Argument." +warning.config.item.invalid_material: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Materialtyp ''." +warning.config.item.invalid_custom_model_data: "Problem in Datei gefunden - Der Gegenstand '' verwendet negative benutzerdefinierte Modelldaten '', die ungültig sind." +warning.config.item.bad_custom_model_data: "Problem in Datei gefunden - Der Gegenstand '' verwendet benutzerdefinierte Modelldaten '', die zu groß sind. Es wird empfohlen, einen Wert unter 16.777.216 zu verwenden." +warning.config.item.custom_model_data_conflict: "Problem in Datei gefunden - Der Gegenstand '' verwendet benutzerdefinierte Modelldaten '', die bereits von Gegenstand '' belegt sind." +warning.config.item.invalid_component: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen nicht existierenden Komponententyp ''." +warning.config.item.missing_model_id: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'custom-model-data' oder 'item-model'-Argument." +warning.config.item.missing_model: "Problem in Datei gefunden - Dem Gegenstand '' fehlt der erforderliche 'model'-Abschnitt für die Unterstützung von 1.21.4+-Ressourcenpaketen." +warning.config.item.behavior.missing_type: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'type'-Argument für sein Gegenstandsverhalten." +warning.config.item.behavior.invalid_type: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Gegenstandsverhaltenstyp ''." +warning.config.item.behavior.block.missing_block: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'block'-Argument für das 'block_item'-Verhalten." +warning.config.item.behavior.furniture.missing_furniture: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'furniture'-Argument für das 'furniture_item'-Verhalten." +warning.config.item.behavior.liquid_collision.missing_block: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'block'-Argument für das 'liquid_collision_block_item'-Verhalten." +warning.config.item.legacy_model.missing_path: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'path'-Argument für das Legacy-Modell." +warning.config.item.legacy_model.overrides.missing_path: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'path'-Argument für Legacy-Modell-Überschreibungen." +warning.config.item.legacy_model.overrides.missing_predicate: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'predicate'-Argument für Legacy-Modell-Überschreibungen." +warning.config.item.legacy_model.cannot_convert: "Problem in Datei gefunden - Kann 1.21.4+-Gegenstände für Gegenstand '' nicht in das Legacy-Format konvertieren. Bitte erstellen Sie den Abschnitt 'legacy-model' für diesen Gegenstand manuell." +warning.config.item.model.invalid_type: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Modelltyp ''." +warning.config.item.model.tint.missing_type: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'type'-Argument für die Tönung." +warning.config.item.model.tint.invalid_type: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Tönungstyp ''." +warning.config.item.model.tint.constant.missing_value: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'value'-Argument für die konstante Tönung." +warning.config.item.model.tint.grass.invalid_temp: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine ungültige Temperatur '' für die Grastönung, die zwischen 0 und 1 liegen sollte." +warning.config.item.model.tint.grass.invalid_downfall: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Niederschlag '' für die Grastönung, der zwischen 0 und 1 liegen sollte." +warning.config.item.model.tint.invalid_value: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine ungültige Tönung ''." +warning.config.item.model.base.missing_path: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:model'." +warning.config.item.model.base.invalid_path: "Problem in Datei gefunden - Der Gegenstand '' hat ein ungültiges 'path'-Argument '' für das Modell 'minecraft:model', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.item.model.condition.missing_property: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:condition'." +warning.config.item.model.condition.invalid_property: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:condition'." +warning.config.item.model.condition.missing_on_true: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'on-true'-Argument für das Modell 'minecraft:condition'." +warning.config.item.model.condition.missing_on_false: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'on-false'-Argument für das Modell 'minecraft:condition'." +warning.config.item.model.condition.keybind.missing: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'keybind'-Argument für die Eigenschaft 'minecraft:keybind_down'." +warning.config.item.model.condition.has_component.missing_component: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:has_component'." +warning.config.item.model.composite.missing_models: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'models'-Argument für das Modell 'minecraft:composite'." +warning.config.item.model.range_dispatch.missing_property: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:range_dispatch'." +warning.config.item.model.range_dispatch.invalid_property: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:range_dispatch'." +warning.config.item.model.range_dispatch.missing_entries: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'entries'-Argument für das Modell 'minecraft:composite'." +warning.config.item.model.range_dispatch.entry.missing_model: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'model'-Argument für einen der Einträge im Modell 'minecraft:composite'." +warning.config.item.model.range_dispatch.compass.missing_target: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'target'-Argument für die Eigenschaft 'minecraft:compass'." +warning.config.item.model.range_dispatch.time.missing_source: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'source'-Argument für die Eigenschaft 'minecraft:time'." +warning.config.item.model.select.missing_property: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:select'." +warning.config.item.model.select.invalid_property: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:select'." +warning.config.item.model.select.missing_cases: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'cases'-Argument für das Modell 'minecraft:select'." +warning.config.item.model.select.case.missing_when: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'when'-Argument für einen der Fälle im Modell 'minecraft:select'." +warning.config.item.model.select.case.missing_model: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'model'-Argument für einen der Fälle im Modell 'minecraft:select'." +warning.config.item.model.select.block_state.missing_property: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'block-state-property'-Argument für die Eigenschaft 'minecraft:block_state'." +warning.config.item.model.select.local_time.missing_pattern: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'pattern'-Argument für die Eigenschaft 'minecraft:local_time'." +warning.config.item.model.special.missing_type: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'type'-Argument für das Modell 'minecraft:special'." +warning.config.item.model.special.missing_path: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'path'-Argument für das Modell 'minecraft:special'." +warning.config.item.model.special.invalid_path: "Problem in Datei gefunden - Der Gegenstand '' hat ein ungültiges 'path'-Argument '' für das Modell 'minecraft:special', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.item.model.special.invalid_type: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Typ '' für das Modell 'minecraft:special'." +warning.config.item.model.special.banner.missing_color: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'color'-Argument für das Spezialmodell 'minecraft:banner'." +warning.config.item.model.special.bed.missing_texture: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:bed'." +warning.config.item.model.special.sign.missing_wood_type: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'wood-type'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'." +warning.config.item.model.special.sign.missing_texture: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:hanging_sign'/'minecraft:standing_sign'." +warning.config.item.model.special.chest.missing_texture: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:chest'." +warning.config.item.model.special.chest.invalid_openness: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen 'openness'-Wert '' für das Spezialmodell 'minecraft:chest'. Gültiger Bereich '0~1.'" +warning.config.item.model.special.shulker_box.missing_texture: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'texture'-Argument für das Spezialmodell 'minecraft:shulker_box'." +warning.config.item.model.special.shulker_box.invalid_openness: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen 'openness'-Wert '' für das Spezialmodell 'minecraft:shulker_box'. Gültiger Bereich '0~1.'" +warning.config.item.model.special.head.missing_kind: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'kind'-Argument für das Spezialmodell 'minecraft:head'." +warning.config.block.duplicate: "Problem in Datei gefunden - Duplizierter Block ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.block.missing_state: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'state'-Argument." +warning.config.block.state.property.missing_type: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'type'-Argument für die Eigenschaft ''." +warning.config.block.state.property.invalid_type: "Problem in Datei gefunden - Der Block '' verwendet das ungültige 'type'-Argument '' für die Eigenschaft ''." +warning.config.block.state.property.integer.invalid_range: "Problem in Datei gefunden - Der Block '' verwendet das ungültige 'range'-Argument '' für die Integer-Eigenschaft ''. Korrekte Syntax: 1~2." +warning.config.block.state.property.invalid_format: "Problem in Datei gefunden - Der Block '' verwendet ein ungültiges Blockzustandsformat ''." +warning.config.block.state.missing_real_id: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'id'-Argument für 'state'. 'id' ist die serverseitige Block-ID, die für jeden Blockzustandstyp eindeutig ist. Wenn Sie einen serverseitigen Block mit 'note_block' und ID 30 erstellen, wäre die echte Block-ID 'craftengine:note_block_30'." +warning.config.block.state.missing_state: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'state'-Argument für 'state'." +warning.config.block.state.missing_properties: "Problem in Datei gefunden - Dem Block '' fehlt der erforderliche 'properties'-Abschnitt für 'states'." +warning.config.block.state.missing_appearances: "Problem in Datei gefunden - Dem Block '' fehlt der erforderliche 'appearances'-Abschnitt für 'states'." +warning.config.block.state.missing_variants: "Problem in Datei gefunden - Dem Block '' fehlt der erforderliche 'variants'-Abschnitt für 'states'." +warning.config.block.state.variant.missing_appearance: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'appearance'-Argument für Variante ''." +warning.config.block.state.variant.invalid_appearance: "Problem in Datei gefunden - Der Block '' hat einen Fehler, dass die Variante '' ein nicht existierendes Erscheinungsbild '' verwendet." +warning.config.block.state.invalid_vanilla: "Problem in Datei gefunden - Der Block '' verwendet einen ungültigen Vanilla-Blockzustand ''." +warning.config.block.state.unavailable_vanilla: "Problem in Datei gefunden - Der Block '' verwendet einen nicht verfügbaren Vanilla-Blockzustand ''. Bitte geben Sie diesen Zustand in mappings.yml frei." +warning.config.block.state.invalid_vanilla_id: "Problem in Datei gefunden - Der Block '' verwendet einen Vanilla-Blockzustand '', der den verfügbaren Slot-Bereich '0~' überschreitet." +warning.config.block.state.conflict: "Problem in Datei gefunden - Der Block '' verwendet einen Vanilla-Blockzustand '', der bereits von '' belegt ist." +warning.config.block.state.bind_failed: "Problem in Datei gefunden - Der Block '' konnte den echten Blockzustand für '' nicht binden, da der Zustand von '' belegt ist." +warning.config.block.state.missing_model: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'model' oder 'models'-Argument." +warning.config.block.state.invalid_real_id: "Problem in Datei gefunden - Der Block '' verwendet einen echten Blockzustand '', der den verfügbaren Slot-Bereich '0~' überschreitet. Erwägen Sie, weitere echte Zustände in 'additional-real-blocks.yml' hinzuzufügen, wenn die Slots aufgebraucht sind." +warning.config.block.state.model.missing_path: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'path'-Option für 'model'." +warning.config.block.state.model.invalid_path: "Problem in Datei gefunden - Der Block '' hat ein 'path'-Argument '', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.block.settings.unknown: "Problem in Datei gefunden - Der Block '' verwendet einen unbekannten Einstellungstyp ''." +warning.config.block.behavior.missing_type: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'type'-Argument für sein Blockverhalten." +warning.config.block.behavior.invalid_type: "Problem in Datei gefunden - Der Block '' verwendet einen ungültigen Blockverhaltenstyp ''." +warning.config.block.behavior.concrete.missing_solid: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'solid-block'-Option für das 'concrete_block'-Verhalten." +warning.config.block.behavior.crop.missing_age: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'age'-Eigenschaft für das 'crop_block'-Verhalten." +warning.config.block.behavior.sugar_cane.missing_age: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'age'-Eigenschaft für das 'sugar_cane_block'-Verhalten." +warning.config.block.behavior.leaves.missing_persistent: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'persistent'-Eigenschaft für das 'leaves_block'-Verhalten." +warning.config.block.behavior.leaves.missing_distance: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'distance'-Eigenschaft für das 'leaves_block'-Verhalten." +warning.config.block.behavior.lamp.missing_lit: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'lit'-Eigenschaft für das 'lamp_block'-Verhalten." +warning.config.block.behavior.sapling.missing_stage: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'stage'-Eigenschaft für das 'sapling_block'-Verhalten." +warning.config.block.behavior.sapling.missing_feature: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'feature'-Argument für das 'sapling_block'-Verhalten." +warning.config.block.behavior.strippable.missing_stripped: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'stripped'-Argument für das 'strippable_block'-Verhalten." +warning.config.block.behavior.door.missing_half: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'half'-Eigenschaft für das 'door_block'-Verhalten." +warning.config.block.behavior.door.missing_facing: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'facing'-Eigenschaft für das 'door_block'-Verhalten." +warning.config.block.behavior.door.missing_hinge: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'hinge'-Eigenschaft für das 'door_block'-Verhalten." +warning.config.block.behavior.door.missing_open: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'open'-Eigenschaft für das 'door_block'-Verhalten." +warning.config.block.behavior.door.missing_powered: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'powered'-Eigenschaft für das 'door_block'-Verhalten." +warning.config.block.behavior.trapdoor.missing_half: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'half'-Eigenschaft für das 'trapdoor_block'-Verhalten." +warning.config.block.behavior.trapdoor.missing_facing: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'facing'-Eigenschaft für das 'trapdoor_block'-Verhalten." +warning.config.block.behavior.trapdoor.missing_open: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'open'-Eigenschaft für das 'trapdoor_block'-Verhalten." +warning.config.block.behavior.trapdoor.missing_powered: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'powered'-Eigenschaft für das 'trapdoor_block'-Verhalten." +warning.config.block.behavior.stackable.missing_property: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche ''-Eigenschaft für das 'stackable_block'-Verhalten." +warning.config.block.behavior.stackable.missing_items: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'items'-Argument für das 'stackable_block'-Verhalten." +warning.config.block.behavior.fence_gate.missing_facing: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'facing'-Argument für das 'fence_gate_block'-Verhalten." +warning.config.block.behavior.fence_gate.missing_in_wall: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'in_wall'-Argument für das 'fence_gate_block'-Verhalten." +warning.config.block.behavior.fence_gate.missing_open: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'powered'-Argument für das 'fence_gate_block'-Verhalten." +warning.config.block.behavior.fence_gate.missing_powered: "Problem in Datei gefunden - Dem Block '' fehlt das erforderliche 'open'-Argument für das 'fence_gate_block'-Verhalten." +warning.config.block.behavior.trapdoor.missing_type: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'type'-Eigenschaft für das 'slab_block'-Verhalten." +warning.config.block.behavior.stairs.missing_facing: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'facing'-Eigenschaft für das 'stairs_block'-Verhalten." +warning.config.block.behavior.stairs.missing_half: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'half'-Eigenschaft für das 'stairs_block'-Verhalten." +warning.config.block.behavior.stairs.missing_shape: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'shape'-Eigenschaft für das 'stairs_block'-Verhalten." +warning.config.block.behavior.pressure_plate.missing_powered: "Problem in Datei gefunden - Dem Block '' fehlt die erforderliche 'powered'-Eigenschaft für das 'pressure_plate_block'-Verhalten." +warning.config.model.generation.missing_parent: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'parent'-Argument im Abschnitt 'generation'." +warning.config.model.generation.invalid_display_position: "Problem in Datei gefunden - Die Konfiguration '' verwendet eine ungültige Anzeigeposition '' im Abschnitt 'generation.display'. Erlaubte Anzeigepositionen: []" +warning.config.model.generation.invalid_gui_light: "Problem in Datei gefunden - Die Konfiguration '' verwendet eine ungültige GUI-Lichtoption '' im Abschnitt 'generation'. Erlaubte GUI-Lichtoptionen: []" +warning.config.model.generation.conflict: "Problem in Datei gefunden - Fehler beim Generieren des Modells für '', da zwei oder mehr Konfigurationen versuchen, verschiedene JSON-Modelle mit demselben Pfad zu generieren: ''." +warning.config.model.generation.texture.invalid: "Problem in Datei gefunden - Die Konfiguration '' hat eine Textur '' mit Pfad '', die illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.model.generation.parent.invalid: "Problem in Datei gefunden - Die Konfiguration '' hat ein 'parent'-Argument '', das illegale Zeichen enthält. Bitte lesen Sie https://minecraft.wiki/w/Resource_location#Legal_characters." +warning.config.emoji.missing_keywords: "Problem in Datei gefunden - Dem Emoji '' fehlt das erforderliche 'keywords'-Argument." +warning.config.emoji.duplicate: "Problem in Datei gefunden - Dupliziertes Emoji ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.emoji.invalid_image: "Problem in Datei gefunden - Das Emoji '' hat ein ungültiges 'image'-Argument ''." +warning.config.advancement.duplicate: "Problem in Datei gefunden - Duplizierter Fortschritt ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." +warning.config.loot_table.missing_pools: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, der das erforderliche 'pools'-Argument fehlt." +warning.config.loot_table.invalid_pools_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, 'pools' sollte eine String-/Kartenliste sein, aktueller Typ: ''." +warning.config.loot_table.invalid_conditions_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, 'conditions' sollte eine Kartenliste sein, aktueller Typ: ''." +warning.config.loot_table.invalid_functions_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, 'functions' sollte eine Kartenliste sein, aktueller Typ: ''." +warning.config.loot_table.invalid_entries_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, 'entries' sollte eine Kartenliste sein, aktueller Typ: ''." +warning.config.loot_table.function.missing_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einer der Funktionen fehlt das erforderliche 'type'-Argument." +warning.config.loot_table.function.invalid_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einer der Funktionen verwendet einen ungültigen Funktionstyp ''." +warning.config.loot_table.function.apply_bonus.missing_enchantment: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, der Funktion 'apply_bonus' fehlt das erforderliche 'enchantment'-Argument." +warning.config.loot_table.function.apply_bonus.missing_formula: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, der Funktion 'apply_bonus' fehlt das erforderliche 'formula'-Argument." +warning.config.loot_table.function.drop_exp.missing_count: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, der Funktion 'drop_exp' fehlt das erforderliche 'count'-Argument." +warning.config.loot_table.function.set_count.missing_count: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, der Funktion 'set_count' fehlt das erforderliche 'count'-Argument." +warning.config.loot_table.entry.missing_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einem der Einträge fehlt das erforderliche 'type'-Argument." +warning.config.loot_table.entry.invalid_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einem der Einträge verwendet einen ungültigen Eintragstyp ''." +warning.config.loot_table.entry.exp.missing_count: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, dem Eintrag 'exp' fehlt das erforderliche 'count'-Argument." +warning.config.loot_table.entry.item.missing_item: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, dem Eintrag 'item' fehlt das erforderliche 'item'-Argument." +warning.config.loot_table.condition.missing_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einer der Bedingungen fehlt das erforderliche 'type'-Argument." +warning.config.loot_table.condition.invalid_type: "Problem in Datei gefunden - '' hat eine falsch konfigurierte Beutetabelle, einer der Bedingungen verwendet einen ungültigen Bedingungstyp ''." +warning.config.host.missing_type: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'type'-Argument für den Host." +warning.config.host.invalid_type: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Host-Typ '' ist ungültig. Bitte lesen Sie https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/resource-pack/host." +warning.config.host.external.missing_url: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'url'-Argument für den externen Host." +warning.config.host.alist.missing_api_url: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'api-url'-Argument für den AList-Host." +warning.config.host.alist.missing_username: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'username'-Argument oder Umgebungsvariable 'CE_ALIST_USERNAME' für den AList-Host." +warning.config.host.alist.missing_password: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'password'-Argument oder Umgebungsvariable 'CE_ALIST_PASSWORD' für den AList-Host." +warning.config.host.alist.missing_upload_path: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für den AList-Host." +warning.config.host.dropbox.missing_app_key: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'app-key'-Argument oder Umgebungsvariable 'CE_DROPBOX_APP_KEY' für den Dropbox-Host." +warning.config.host.dropbox.missing_app_secret: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'app-secret'-Argument oder Umgebungsvariable 'CE_DROPBOX_APP_SECRET' für den Dropbox-Host." +warning.config.host.dropbox.missing_refresh_token: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'refresh-token'-Argument oder Umgebungsvariable 'CE_DROPBOX_REFRESH_TOKEN' für den Dropbox-Host." +warning.config.host.dropbox.missing_upload_path: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für den Dropbox-Host." +warning.config.host.lobfile.missing_api_key: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'api-key'-Argument für den Lobfile-Host." +warning.config.host.onedrive.missing_client_id: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'client-id'-Argument oder Umgebungsvariable 'CE_ONEDRIVE_CLIENT_ID' für den OneDrive-Host." +warning.config.host.onedrive.missing_client_secret: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'client-secret'-Argument oder Umgebungsvariable 'CE_ONEDRIVE_CLIENT_SECRET' für den OneDrive-Host." +warning.config.host.onedrive.missing_refresh_token: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'refresh-token'-Argument oder Umgebungsvariable 'CE_ONEDRIVE_REFRESH_TOKEN' für den OneDrive-Host." +warning.config.host.onedrive.missing_upload_path: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für den OneDrive-Host." +warning.config.host.s3.missing_endpoint: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'endpoint'-Argument für den S3-Host." +warning.config.host.s3.missing_bucket: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'bucket'-Argument für den S3-Host." +warning.config.host.s3.missing_access_key: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'access-key-id'-Argument oder Umgebungsvariable 'CE_S3_ACCESS_KEY_ID' für den S3-Host." +warning.config.host.s3.missing_secret: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'access-key-secret'-Argument oder Umgebungsvariable 'CE_S3_ACCESS_KEY_SECRET' für den S3-Host." +warning.config.host.s3.missing_upload_path: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'upload-path'-Argument für den S3-Host." +warning.config.host.self.missing_ip: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'ip'-Argument für den Selbst-Host." +warning.config.host.self.invalid_port: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Ungültiger Port '' für den Selbst-Host." +warning.config.host.self.invalid_url: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Ungültige URL '' für den Selbst-Host." +warning.config.host.gitlab.missing_url: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'gitlab-url'-Argument für den GitLab-Host." +warning.config.host.gitlab.missing_token: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'access-token'-Argument für den GitLab-Host." +warning.config.host.gitlab.missing_project: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'project-id'-Argument für den GitLab-Host." +warning.config.host.proxy.missing_host: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'host'-Argument für den Proxy." +warning.config.host.proxy.missing_port: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'port'-Argument für den Proxy." +warning.config.host.proxy.missing_scheme: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Fehlendes erforderliches 'scheme'-Argument für den Proxy." +warning.config.host.proxy.invalid: "Problem in config.yml im Abschnitt 'resource-pack.delivery.hosting' gefunden - Ungültiger Proxy ''." +warning.config.conflict_matcher.missing_type: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'type'-Argument für einen der Handler." +warning.config.conflict_matcher.invalid_type: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Einer der Begriffe verwendet den ungültigen Typ ''." +warning.config.conflict_matcher.exact.missing_path: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'path'-Argument für den 'exact'-Matcher." +warning.config.conflict_matcher.contains.missing_path: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'path'-Argument für den 'contains'-Matcher." +warning.config.conflict_matcher.filename.missing_name: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'path'-Argument für den 'filename'-Matcher." +warning.config.conflict_matcher.pattern.missing_pattern: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'pattern'-Argument für den 'pattern'-Matcher." +warning.config.conflict_matcher.parent_prefix.missing_prefix: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'prefix'-Argument für den 'parent_path_prefix'-Matcher." +warning.config.conflict_matcher.parent_suffix.missing_suffix: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'suffix'-Argument für den 'parent_path_suffix'-Matcher." +warning.config.conflict_matcher.inverted.missing_term: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'term'-Argument für den 'inverted'-Matcher." +warning.config.conflict_matcher.all_of.missing_terms: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'terms'-Argument für den 'all_of'-Matcher." +warning.config.conflict_matcher.any_of.missing_terms: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'terms'-Argument für den 'any_of'-Matcher." +warning.config.conflict_resolution.missing_type: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Fehlendes erforderliches 'type'-Argument für eine der Auflösungen." +warning.config.conflict_resolution.invalid_type: "Problem in config.yml im Abschnitt 'resource-pack.duplicated-files-handler' gefunden - Eine der Auflösungen verwendet den ungültigen Typ ''." +warning.config.event.missing_trigger: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'on'-Argument für Ereignisauslöser." +warning.config.event.invalid_trigger: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen Ereignisauslöser ''." +warning.config.event.condition.missing_type: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'type'-Argument für die Ereignisbedingung." +warning.config.event.condition.invalid_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges 'type'-Argument '' für die Ereignisbedingung." +warning.config.function.missing_type: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'type'-Argument für die Funktion." +warning.config.function.invalid_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen Funktionstyp ''." +warning.config.function.command.missing_command: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'command'-Argument für die 'command'-Funktion." +warning.config.function.actionbar.missing_actionbar: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'actionbar'-Argument für die 'actionbar'-Funktion." +warning.config.function.message.missing_message: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'message'-Argument für die 'message'-Funktion." +warning.config.function.open_window.missing_gui_type: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'gui-type'-Argument für die 'open_window'-Funktion." +warning.config.function.open_window.invalid_gui_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen GUI-Typ für die 'open_window'-Funktion. Erlaubte Typen: []." +warning.config.function.run.missing_functions: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'functions'-Argument für die 'run'-Funktion." +warning.config.function.place_block.missing_block_state: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'block-state'-Argument für die 'place_block'-Funktion." +warning.config.function.set_food.missing_food: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'food'-Argument für die 'set_food'-Funktion." +warning.config.function.set_saturation.missing_saturation: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'saturation'-Argument für die 'set_saturation'-Funktion." +warning.config.function.play_sound.missing_sound: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'sound'-Argument für die 'play_sound'-Funktion." +warning.config.function.particle.missing_particle: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'particle'-Argument für die 'particle'-Funktion." +warning.config.function.particle.missing_color: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'color'-Argument für die 'particle'-Funktion." +warning.config.function.particle.missing_from: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'from'-Argument für die 'particle'-Funktion." +warning.config.function.particle.missing_to: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'to'-Argument für die 'particle'-Funktion." +warning.config.function.particle.missing_item: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'item'-Argument für die 'particle'-Funktion." +warning.config.function.particle.missing_block_state: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'block-state'-Argument für die 'particle'-Funktion." +warning.config.function.leveler_exp.missing_count: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'count'-Argument für die 'leveler_exp'-Funktion." +warning.config.function.leveler_exp.missing_leveler: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'leveler'-Argument für die 'leveler_exp'-Funktion." +warning.config.function.leveler_exp.missing_plugin: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'plugin'-Argument für die 'leveler_exp'-Funktion." +warning.config.function.remove_potion_effect.missing_potion_effect: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'potion-effect'-Argument für die 'remove_potion_effect'-Funktion." +warning.config.function.potion_effect.missing_potion_effect: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'potion-effect'-Argument für die 'potion_effect'-Funktion." +warning.config.function.set_cooldown.missing_time: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'time'-Argument für die 'set_cooldown'-Funktion." +warning.config.function.set_cooldown.missing_id: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'id'-Argument für die 'set_cooldown'-Funktion." +warning.config.function.remove_cooldown.missing_id: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'id'-Argument für die 'remove_cooldown'-Funktion." +warning.config.selector.missing_type: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'type'-Argument für den Selektor." +warning.config.selector.invalid_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen Selektortyp ''." +warning.config.selector.invalid_target: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges Selektorziel ''." +warning.config.resource_pack.item_model.conflict.vanilla: "Fehler beim Generieren des Gegenstandsmodells für '', da dieses Gegenstandsmodell von einem Vanilla-Gegenstand belegt wurde." +warning.config.resource_pack.item_model.already_exist: "Fehler beim Generieren des Gegenstandsmodells für '', da die Datei '' bereits existiert." +warning.config.resource_pack.model.generation.already_exist: "Fehler beim Generieren des Modells, da die Modelldatei '' bereits existiert." +warning.config.resource_pack.generation.missing_font_texture: "Schriftart '' fehlt die erforderliche Textur: ''" +warning.config.resource_pack.generation.missing_model_texture: "Modell '' fehlt Textur ''" +warning.config.resource_pack.generation.missing_item_model: "Gegenstand '' fehlt Modelldatei: ''" +warning.config.resource_pack.generation.missing_block_model: "Block '' fehlt Modelldatei: ''" +warning.config.resource_pack.generation.missing_parent_model: "Modell '' kann das Elternmodell nicht finden: ''" +warning.config.resource_pack.generation.malformatted_json: "Json-Datei '' ist fehlerhaft formatiert." From c717b16162a7f8f6e5a61ec3fe426b1d909330f4 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 25 Jun 2025 00:11:16 +0800 Subject: [PATCH 06/14] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A4=B4=E9=A2=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../behavior/SturdyBaseBlockBehavior.java | 2 - .../plugin/command/feature/ReloadCommand.java | 3 +- common-files/src/main/resources/config.yml | 2 +- .../resources/default/configuration/items.yml | 6 +- .../default/resourcepack/pack.mcmeta | 2 +- .../shaders/core/rendertype_entity_solid.fsh | 0 .../resourcepack/pack.mcmeta | 8 +- .../src/main/resources/translations/en.yml | 5 +- .../src/main/resources/translations/es.yml | 2 +- .../src/main/resources/translations/tr.yml | 2 +- .../src/main/resources/translations/zh_cn.yml | 5 +- .../core/item/AbstractItemManager.java | 8 +- .../core/item/modifier/ComponentModifier.java | 1 - .../core/pack/AbstractPackManager.java | 208 +++++++++++++----- .../core/pack/model/BaseItemModel.java | 43 +++- .../pack/model/BundleSelectedItemModel.java | 52 +++++ .../core/pack/model/CompositeItemModel.java | 41 +++- .../core/pack/model/ConditionItemModel.java | 48 +++- .../core/pack/model/EmptyItemModel.java | 18 +- .../core/pack/model/ItemModel.java | 8 +- .../core/pack/model/ItemModelReader.java | 8 + .../core/pack/model/ItemModels.java | 43 +++- .../core/pack/model/ModernItemModel.java | 31 +++ .../pack/model/RangeDispatchItemModel.java | 68 ++++-- .../core/pack/model/SelectItemModel.java | 75 ++++++- .../core/pack/model/SpecialItemModel.java | 28 ++- .../condition/BrokenConditionProperty.java | 11 +- .../condition/ComponentConditionProperty.java | 49 +++++ .../model/condition/ConditionProperties.java | 58 +++-- .../condition/ConditionPropertyReader.java | 8 + .../CustomModelDataConditionProperty.java | 14 +- .../condition/DamagedConditionProperty.java | 11 +- .../HasComponentConditionProperty.java | 10 + .../KeyBindDownConditionProperty.java | 12 +- .../condition/RodCastConditionProperty.java | 11 +- .../condition/SimpleConditionProperty.java | 8 + .../condition/UsingItemConditionProperty.java | 11 +- .../CompassRangeDispatchProperty.java | 23 +- .../CrossBowPullingRangeDispatchProperty.java | 12 +- .../CustomModelDataRangeDispatchProperty.java | 10 +- .../DamageRangeDispatchProperty.java | 10 +- .../NormalizeRangeDispatchProperty.java | 11 +- .../RangeDispatchProperties.java | 49 ++++- .../RangeDispatchPropertyReader.java | 8 + .../SimpleRangeDispatchProperty.java | 10 +- .../TimeRangeDispatchProperty.java | 11 +- .../UseCycleRangeDispatchProperty.java | 16 +- .../UseDurationRangeDispatchProperty.java | 10 +- .../select/BlockStateSelectProperty.java | 10 +- .../select/ChargeTypeSelectProperty.java | 12 +- .../model/select/ComponentSelectProperty.java | 44 ++++ .../select/CustomModelDataSelectProperty.java | 10 +- .../model/select/LocalTimeSelectProperty.java | 22 +- .../model/select/MainHandSelectProperty.java | 12 +- .../pack/model/select/SelectProperties.java | 49 ++++- .../model/select/SelectPropertyReader.java | 8 + .../model/select/SimpleSelectProperty.java | 10 +- .../select/TrimMaterialSelectProperty.java | 12 +- .../model/special/BannerSpecialModel.java | 22 +- .../pack/model/special/BedSpecialModel.java | 24 +- .../pack/model/special/ChestSpecialModel.java | 20 +- .../pack/model/special/HeadSpecialModel.java | 38 +++- .../model/special/PlayerHeadSpecialModel.java | 56 +++++ .../model/special/ShulkerBoxSpecialModel.java | 22 +- .../pack/model/special/SignSpecialModel.java | 22 +- .../model/special/SimpleSpecialModel.java | 20 +- .../core/pack/model/special/SpecialModel.java | 9 +- .../model/special/SpecialModelReader.java | 8 + .../pack/model/special/SpecialModels.java | 54 ++++- .../core/pack/model/tint/ConstantTint.java | 11 +- .../pack/model/tint/CustomModelDataTint.java | 18 +- .../core/pack/model/tint/GrassTint.java | 11 +- .../pack/model/tint/SimpleDefaultTint.java | 22 +- .../core/pack/model/tint/Tint.java | 1 + .../core/pack/model/tint/TintReader.java | 30 +++ .../core/pack/model/tint/Tints.java | 43 +++- .../core/pack/revision/Revision.java | 139 ++++++++++++ .../core/pack/revision/Revisions.java | 9 + .../core/plugin/config/Config.java | 23 +- .../core/plugin/gui/GuiElement.java | 4 +- .../core/registry/BuiltInRegistries.java | 12 + .../craftengine/core/registry/Registries.java | 12 + .../core/util/MinecraftVersion.java | 83 +++++++ .../core/util/MinecraftVersions.java | 21 ++ .../craftengine/core/util/VersionHelper.java | 4 + gradle.properties | 4 +- 86 files changed, 1736 insertions(+), 285 deletions(-) rename common-files/src/main/resources/resources/remove_shulker_head/resourcepack/{1_20_5_assets => 1_20_5_remove_shulker_head_overlay}/minecraft/shaders/core/rendertype_entity_solid.fsh (100%) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/model/BundleSelectedItemModel.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModelReader.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ComponentConditionProperty.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionPropertyReader.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchPropertyReader.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/model/select/ComponentSelectProperty.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectPropertyReader.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/model/special/PlayerHeadSpecialModel.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModelReader.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintReader.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revisions.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SturdyBaseBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SturdyBaseBlockBehavior.java index 03852aac9..e835b702a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SturdyBaseBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SturdyBaseBlockBehavior.java @@ -16,8 +16,6 @@ import net.momirealms.craftengine.core.util.ResourceConfigUtils; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; public class SturdyBaseBlockBehavior extends AbstractCanSurviveBlockBehavior { public static final Factory FACTORY = new Factory(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ReloadCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ReloadCommand.java index c3ab26bd0..a71aadc6a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ReloadCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ReloadCommand.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.locale.MessageConstants; +import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.command.CommandSender; import org.incendo.cloud.Command; import org.incendo.cloud.parser.standard.EnumParser; @@ -75,7 +76,7 @@ public class ReloadCommand extends BukkitCommandFeature { } else if (argument == ReloadArgument.ALL) { RELOAD_PACK_FLAG = true; try { - plugin().reloadPlugin(plugin().scheduler().async(), r -> plugin().scheduler().sync().run(r), true).thenAcceptAsync(reloadResult -> { + plugin().reloadPlugin(plugin().scheduler().async(), r -> plugin().scheduler().sync().run(r), !VersionHelper.isFolia()).thenAcceptAsync(reloadResult -> { try { long time1 = System.currentTimeMillis(); plugin().packManager().generateResourcePack(); diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index c552b653d..9ea8b709b 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -58,7 +58,7 @@ resource-pack: validate: enable: true # Define the name of the overlay folders - overlay-format: "overlay_{version}" + overlay-format: "ce_overlay_{version}" supported-version: min: "1.20" max: LATEST diff --git a/common-files/src/main/resources/resources/default/configuration/items.yml b/common-files/src/main/resources/resources/default/configuration/items.yml index 764547c3d..6018cc3c2 100644 --- a/common-files/src/main/resources/resources/default/configuration/items.yml +++ b/common-files/src/main/resources/resources/default/configuration/items.yml @@ -13,8 +13,7 @@ items#gui_head: translation: 0,8,0 scale: 2,2,2 model: - type: minecraft:head - kind: player + type: minecraft:player_head default:gui_head_size_4: material: player_head custom-model-data: 1001 @@ -29,8 +28,7 @@ items#gui_head: translation: 9,7,0 scale: 4,4,4 model: - type: minecraft:head - kind: player + type: minecraft:player_head items#topaz_gears: default:topaz_rod: material: fishing_rod diff --git a/common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta b/common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta index 8a2bb301a..0ae3705e1 100644 --- a/common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta +++ b/common-files/src/main/resources/resources/default/resourcepack/pack.mcmeta @@ -4,7 +4,7 @@ "description":"CraftEngine", "supported_formats": { "min_inclusive": 15, - "max_inclusive": 71 + "max_inclusive": 1000 } } } \ No newline at end of file diff --git a/common-files/src/main/resources/resources/remove_shulker_head/resourcepack/1_20_5_assets/minecraft/shaders/core/rendertype_entity_solid.fsh b/common-files/src/main/resources/resources/remove_shulker_head/resourcepack/1_20_5_remove_shulker_head_overlay/minecraft/shaders/core/rendertype_entity_solid.fsh similarity index 100% rename from common-files/src/main/resources/resources/remove_shulker_head/resourcepack/1_20_5_assets/minecraft/shaders/core/rendertype_entity_solid.fsh rename to common-files/src/main/resources/resources/remove_shulker_head/resourcepack/1_20_5_remove_shulker_head_overlay/minecraft/shaders/core/rendertype_entity_solid.fsh diff --git a/common-files/src/main/resources/resources/remove_shulker_head/resourcepack/pack.mcmeta b/common-files/src/main/resources/resources/remove_shulker_head/resourcepack/pack.mcmeta index b1ea29ab9..bac482aad 100644 --- a/common-files/src/main/resources/resources/remove_shulker_head/resourcepack/pack.mcmeta +++ b/common-files/src/main/resources/resources/remove_shulker_head/resourcepack/pack.mcmeta @@ -1,10 +1,10 @@ { "pack": { "pack_format": 15, - "description": "CraftEngine Remove Shulker Head for Some Versions", + "description": "Remove Shulker Head for Some Versions", "supported_formats": { "min_inclusive": 15, - "max_inclusive": 99 + "max_inclusive": 1000 } }, "overlays": { @@ -12,9 +12,9 @@ { "formats": { "min_inclusive": 24, - "max_inclusive": 99 + "max_inclusive": 1000 }, - "directory": "1_20_5_assets" + "directory": "1_20_5_remove_shulker_head_overlay" } ] } diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 6e7658833..8e9d2319f 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -186,8 +186,10 @@ warning.config.item.model.condition.missing_property: "Issue found in fi warning.config.item.model.condition.invalid_property: "Issue found in file - The item '' is using an invalid property '' for model 'minecraft:condition'." warning.config.item.model.condition.missing_on_true: "Issue found in file - The item '' is missing the required 'on-true' argument for model 'minecraft:condition'." warning.config.item.model.condition.missing_on_false: "Issue found in file - The item '' is missing the required 'on-false' argument for model 'minecraft:condition'." -warning.config.item.model.condition.keybind.missing: "Issue found in file - The item '' is missing the required 'keybind' argument for property 'minecraft:keybind_down'." +warning.config.item.model.condition.keybind.missing_keybind: "Issue found in file - The item '' is missing the required 'keybind' argument for property 'minecraft:keybind_down'." warning.config.item.model.condition.has_component.missing_component: "Issue found in file - The item '' is missing the required 'component' argument for property 'minecraft:has_component'." +warning.config.item.model.condition.component.missing_predicate: "Issue found in file - The item '' is missing the required 'predicate' argument for property 'minecraft:component'." +warning.config.item.model.condition.component.missing_value: "Issue found in file - The item '' is missing the required 'value' argument for property 'minecraft:component'." warning.config.item.model.composite.missing_models: "Issue found in file - The item '' is missing the required 'models' argument for 'minecraft:composite' model." warning.config.item.model.range_dispatch.missing_property: "Issue found in file - The item '' is missing the required 'property' argument for model 'minecraft:range_dispatch'." warning.config.item.model.range_dispatch.invalid_property: "Issue found in file - The item '' is using an invalid property '' for model 'minecraft:range_dispatch'." @@ -202,6 +204,7 @@ warning.config.item.model.select.case.missing_when: "Issue found in file warning.config.item.model.select.case.missing_model: "Issue found in file - The item '' is missing the required 'model' argument for one of the cases in model 'minecraft:select'." warning.config.item.model.select.block_state.missing_property: "Issue found in file - The item '' is missing the required 'block-state-property' argument for property 'minecraft:block_state'." warning.config.item.model.select.local_time.missing_pattern: "Issue found in file - The item '' is missing the required 'pattern' argument for property 'minecraft:local_time'." +warning.config.item.model.select.component.missing_component: "Issue found in file - The item '' is missing the required 'component' argument for property 'minecraft:component'." warning.config.item.model.special.missing_type: "Issue found in file - The item '' is missing the required 'type' argument for model 'minecraft:special'." warning.config.item.model.special.missing_path: "Issue found in file - The item '' is missing the required 'path' argument for model 'minecraft:special'." warning.config.item.model.special.invalid_path: "Issue found in file - The item '' has an invalid 'path' argument '' for model 'minecraft:special' which contains illegal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters." diff --git a/common-files/src/main/resources/translations/es.yml b/common-files/src/main/resources/translations/es.yml index a30f3805b..6e489ea9e 100644 --- a/common-files/src/main/resources/translations/es.yml +++ b/common-files/src/main/resources/translations/es.yml @@ -140,7 +140,7 @@ warning.config.item.model.condition.missing_property: "Problema encontra warning.config.item.model.condition.invalid_property: "Problema encontrado en el archivo - El objeto '' está usando una propiedad inválida '' para el modelo 'minecraft:condition'." warning.config.item.model.condition.missing_on_true: "Problema encontrado en el archivo - El objeto '' carece del argumento requerido 'on-true' para el modelo 'minecraft:condition'." warning.config.item.model.condition.missing_on_false: "Problema encontrado en el archivo - El objeto '' carece del argumento requerido 'on-false' para el modelo 'minecraft:condition'." -warning.config.item.model.condition.keybind.missing: "Problema encontrado en el archivo - El objeto '' carece del argumento requerido 'keybind' para la propiedad 'minecraft:keybind_down'." +warning.config.item.model.condition.keybind.missing_keybind: "Problema encontrado en el archivo - El objeto '' carece del argumento requerido 'keybind' para la propiedad 'minecraft:keybind_down'." warning.config.item.model.condition.has_component.missing_component: "Problema encontrado en el archivo - El objeto '' carece del argumento requerido 'component' para la propiedad 'minecraft:has_component'." warning.config.item.model.composite.missing_models: "Problema encontrado en el archivo - El objeto '' carece del argumento requerido 'models' para el modelo 'minecraft:composite'." warning.config.item.model.range_dispatch.missing_property: "Problema encontrado en el archivo - El objeto '' carece del argumento requerido 'property' para el modelo 'minecraft:range_dispatch'." diff --git a/common-files/src/main/resources/translations/tr.yml b/common-files/src/main/resources/translations/tr.yml index 64f30f644..8cdcd859c 100644 --- a/common-files/src/main/resources/translations/tr.yml +++ b/common-files/src/main/resources/translations/tr.yml @@ -139,7 +139,7 @@ warning.config.item.model.condition.missing_property: " dosyasın warning.config.item.model.condition.invalid_property: " dosyasında sorun bulundu - '' eşyası, 'minecraft:condition' modeli için geçersiz bir özellik '' kullanıyor." warning.config.item.model.condition.missing_on_true: " dosyasında sorun bulundu - '' eşyası, 'minecraft:condition' modeli için gerekli 'on-true' argümanı eksik." warning.config.item.model.condition.missing_on_false: " dosyasında sorun bulundu - '' eşyası, 'minecraft:condition' modeli için gerekli 'on-false' argümanı eksik." -warning.config.item.model.condition.keybind.missing: " dosyasında sorun bulundu - '' eşyası, 'minecraft:keybind_down' özelliği için gerekli 'keybind' argümanı eksik." +warning.config.item.model.condition.keybind.missing_keybind: " dosyasında sorun bulundu - '' eşyası, 'minecraft:keybind_down' özelliği için gerekli 'keybind' argümanı eksik." warning.config.item.model.condition.has_component.missing_component: " dosyasında sorun bulundu - '' eşyası, 'minecraft:has_component' özelliği için gerekli 'component' argümanı eksik." warning.config.item.model.composite.missing_models: " dosyasında sorun bulundu - '' eşyası, 'minecraft:composite' modeli için gerekli 'models' argümanı eksik." warning.config.item.model.range_dispatch.missing_property: " dosyasında sorun bulundu - '' eşyası, 'minecraft:range_dispatch' modeli için gerekli 'property' argümanı eksik." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 10962adf2..2edc1009c 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -186,8 +186,10 @@ warning.config.item.model.condition.missing_property: "在文件 warning.config.item.model.condition.invalid_property: "在文件 发现问题 - 物品 '' 的 'minecraft:condition' 模型使用了无效属性 ''" warning.config.item.model.condition.missing_on_true: "在文件 发现问题 - 物品 '' 的 'minecraft:condition' 模型缺少必需的 'on-true' 参数" warning.config.item.model.condition.missing_on_false: "在文件 发现问题 - 物品 '' 的 'minecraft:condition' 模型缺少必需的 'on-false' 参数" -warning.config.item.model.condition.keybind.missing: "在文件 发现问题 - 物品 '' 的 'minecraft:keybind_down' 属性缺少必需的 'keybind' 参数" +warning.config.item.model.condition.keybind.missing_keybind: "在文件 发现问题 - 物品 '' 的 'minecraft:keybind_down' 属性缺少必需的 'keybind' 参数" warning.config.item.model.condition.has_component.missing_component: "在文件 发现问题 - 物品 '' 的 'minecraft:has_component' 属性缺少必需的 'component' 参数" +warning.config.item.model.condition.component.missing_predicate: "在文件 发现问题 - 物品 '' 的 'minecraft:component' 属性缺少必需的 'predicate' 参数." +warning.config.item.model.condition.component.missing_value: "在文件 发现问题 - 物品 '' 的 'minecraft:component' 属性缺少必需的 'value' 参数." warning.config.item.model.composite.missing_models: "在文件 发现问题 - 物品 '' 的 'minecraft:composite' 模型缺少必需的 'models' 参数" warning.config.item.model.range_dispatch.missing_property: "在文件 发现问题 - 物品 '' 的 'minecraft:range_dispatch' 模型缺少必需的 'property' 参数" warning.config.item.model.range_dispatch.invalid_property: "在文件 发现问题 - 物品 '' 的 'minecraft:range_dispatch' 模型使用了无效属性 ''" @@ -202,6 +204,7 @@ warning.config.item.model.select.case.missing_when: "在文件 warning.config.item.model.select.case.missing_model: "在文件 发现问题 - 物品 '' 的 'minecraft:select' 模型的某个 case 缺少必需的 'model' 参数" warning.config.item.model.select.block_state.missing_property: "在文件 发现问题 - 物品 '' 的 'minecraft:block_state' 属性缺少必需的 'block-state-property' 参数" warning.config.item.model.select.local_time.missing_pattern: "在文件 发现问题 - 物品 '' 的 'minecraft:local_time' 属性缺少必需的 'pattern' 参数" +warning.config.item.model.select.component.missing_component: "在文件 发现问题 - 物品 '' 的 'minecraft:component' 属性缺少必需的 'component' 参数" warning.config.item.model.special.missing_type: "在文件 发现问题 - 物品 '' 的 'minecraft:special' 模型缺少必需的 'type' 参数" warning.config.item.model.special.missing_path: "在文件 发现问题 - 物品 '' 的 'minecraft:special' 模型缺少必需的模型 'path' 参数" warning.config.item.model.special.invalid_path: "在文件 发现问题 - 物品 '' 的 'minecraft:special' 模型路径 '' 包含非法字符 请参考 https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6" diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index 6ee017481..928cfa75e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java @@ -263,15 +263,13 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl public class ItemParser implements ConfigParser { public static final String[] CONFIG_SECTION_NAME = new String[] {"items", "item"}; - private static final float VERSION_1_21_2 = 21.2f; - private static final float VERSION_1_21_4 = 21.4f; private boolean isModernFormatRequired() { - return Config.packMaxVersion() >= VERSION_1_21_4; + return Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_4); } private boolean needsLegacyCompatibility() { - return Config.packMinVersion() < VERSION_1_21_4; + return Config.packMinVersion().isBelow(MinecraftVersions.V1_21_4); } @Override @@ -433,7 +431,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl ResourceConfigUtils.getAsBoolean(section.getOrDefault("hand-animation-on-swap", true), "hand-animation-on-swap") )); } - if (Config.packMaxVersion() >= VERSION_1_21_2 && needsLegacyCompatibility() && legacyOverridesModels != null && !legacyOverridesModels.isEmpty()) { + if (Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2) && needsLegacyCompatibility() && legacyOverridesModels != null && !legacyOverridesModels.isEmpty()) { TreeSet lom = AbstractItemManager.this.modernItemModels1_21_2.computeIfAbsent(itemModelKey, k -> new TreeSet<>()); lom.addAll(legacyOverridesModels); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentModifier.java b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentModifier.java index 2406e10eb..e9c333e69 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentModifier.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/modifier/ComponentModifier.java @@ -1,7 +1,6 @@ package net.momirealms.craftengine.core.item.modifier; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index dc7b84e14..412f27fe5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -17,11 +17,15 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHost; import net.momirealms.craftengine.core.pack.host.ResourcePackHosts; import net.momirealms.craftengine.core.pack.host.impl.NoneHost; import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration; +import net.momirealms.craftengine.core.pack.model.ItemModel; import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel; import net.momirealms.craftengine.core.pack.model.ModernItemModel; +import net.momirealms.craftengine.core.pack.model.RangeDispatchItemModel; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator; +import net.momirealms.craftengine.core.pack.model.rangedisptach.CustomModelDataRangeDispatchProperty; import net.momirealms.craftengine.core.pack.obfuscation.ObfA; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.ConfigParser; @@ -54,11 +58,12 @@ import java.util.function.Predicate; import static net.momirealms.craftengine.core.util.MiscUtils.castToMap; +@SuppressWarnings("DuplicatedCode") public abstract class AbstractPackManager implements PackManager { public static final Map PRESET_MODERN_MODELS_ITEM = new HashMap<>(); public static final Map PRESET_LEGACY_MODELS_ITEM = new HashMap<>(); public static final Map PRESET_MODELS_BLOCK = new HashMap<>(); - public static final Map PRESET_ITEMS = new HashMap<>(); + public static final Map PRESET_ITEMS = new HashMap<>(); public static final Set VANILLA_TEXTURES = new HashSet<>(); public static final Set VANILLA_MODELS = new HashSet<>(); private static final byte[] EMPTY_IMAGE; @@ -103,7 +108,7 @@ public abstract class AbstractPackManager implements PackManager { loadInternalData("internal/models/item/legacy/_all.json", PRESET_LEGACY_MODELS_ITEM::put); loadInternalData("internal/models/item/_all.json", PRESET_MODERN_MODELS_ITEM::put); loadInternalData("internal/models/block/_all.json", PRESET_MODELS_BLOCK::put); - loadInternalData("internal/items/_all.json", PRESET_ITEMS::put); + loadModernItemModel("internal/items/_all.json", PRESET_ITEMS::put); loadInternalList("textures", "block/", VANILLA_TEXTURES::add); loadInternalList("textures", "item/", VANILLA_TEXTURES::add); @@ -122,6 +127,21 @@ public abstract class AbstractPackManager implements PackManager { } } + private void loadModernItemModel(String path, BiConsumer callback) { + try (InputStream inputStream = this.plugin.resourceStream(path)) { + if (inputStream != null) { + JsonObject allModelsItems = JsonParser.parseReader(new InputStreamReader(inputStream)).getAsJsonObject(); + for (Map.Entry entry : allModelsItems.entrySet()) { + if (entry.getValue() instanceof JsonObject modelJson) { + callback.accept(Key.of(entry.getKey()), ModernItemModel.fromJson(modelJson)); + } + } + } + } catch (IOException e) { + this.plugin.logger().warn("Failed to load " + path, e); + } + } + private void loadInternalData(String path, BiConsumer callback) { try (InputStream inputStream = this.plugin.resourceStream(path)) { if (inputStream != null) { @@ -311,7 +331,7 @@ public abstract class AbstractPackManager implements PackManager { // internal plugin.saveResource("resources/remove_shulker_head/resourcepack/pack.mcmeta"); plugin.saveResource("resources/remove_shulker_head/resourcepack/assets/minecraft/shaders/core/rendertype_entity_solid.fsh"); - plugin.saveResource("resources/remove_shulker_head/resourcepack/1_20_5_assets/minecraft/shaders/core/rendertype_entity_solid.fsh"); + plugin.saveResource("resources/remove_shulker_head/resourcepack/1_20_5_remove_shulker_head_overlay/minecraft/shaders/core/rendertype_entity_solid.fsh"); plugin.saveResource("resources/remove_shulker_head/resourcepack/assets/minecraft/textures/entity/shulker/shulker_white.png"); plugin.saveResource("resources/remove_shulker_head/pack.yml"); // internal @@ -602,19 +622,21 @@ public abstract class AbstractPackManager implements PackManager { } } + HashSet revisions = new HashSet<>(); this.generateFonts(generatedPackPath); this.generateItemModels(generatedPackPath, this.plugin.itemManager()); this.generateItemModels(generatedPackPath, this.plugin.blockManager()); this.generateBlockOverrides(generatedPackPath); this.generateLegacyItemOverrides(generatedPackPath); - this.generateModernItemOverrides(generatedPackPath); + this.generateModernItemOverrides(generatedPackPath, revisions::add); this.generateModernItemModels1_21_2(generatedPackPath); - this.generateModernItemModels1_21_4(generatedPackPath); + this.generateModernItemModels1_21_4(generatedPackPath, revisions::add); this.generateOverrideSounds(generatedPackPath); this.generateCustomSounds(generatedPackPath); this.generateClientLang(generatedPackPath); this.generateEquipments(generatedPackPath); this.generateParticle(generatedPackPath); + this.generatePackMetadata(generatedPackPath.resolve("pack.mcmeta"), revisions); if (Config.excludeShaders()) { this.removeAllShaders(generatedPackPath); } @@ -634,6 +656,57 @@ public abstract class AbstractPackManager implements PackManager { } } + private void generatePackMetadata(Path path, Set revisions) throws IOException { + JsonObject rawMeta; + boolean changed = false; + if (!Files.exists(path)) { + rawMeta = new JsonObject(); + changed = true; + } else { + rawMeta = GsonHelper.readJsonFile(path).getAsJsonObject(); + } + if (!rawMeta.has("pack")) { + JsonObject pack = new JsonObject(); + rawMeta.add("pack", pack); + pack.addProperty("pack_format", Config.packMinVersion().packFormat()); + JsonObject supportedFormats = new JsonObject(); + supportedFormats.addProperty("min_inclusive", Config.packMinVersion().packFormat()); + supportedFormats.addProperty("max_inclusive", Config.packMaxVersion().packFormat()); + pack.add("supported_formats", supportedFormats); + changed = true; + } + if (revisions.isEmpty()) { + if (changed) { + GsonHelper.writeJsonFile(rawMeta, path); + } + return; + } + JsonObject overlays; + if (rawMeta.has("overlays")) { + overlays = rawMeta.get("overlays").getAsJsonObject(); + } else { + overlays = new JsonObject(); + rawMeta.add("overlays", overlays); + } + JsonArray entries; + if (overlays.has("entries")) { + entries = overlays.get("entries").getAsJsonArray(); + } else { + entries = new JsonArray(); + overlays.add("entries", entries); + } + for (Revision revision : revisions) { + JsonObject entry = new JsonObject(); + JsonObject formats = new JsonObject(); + entry.add("formats", formats); + formats.addProperty("min_inclusive", revision.minPackVersion()); + formats.addProperty("max_inclusive", revision.maxPackVersion()); + entry.addProperty("directory", Config.createOverlayFolderName(revision.versionString())); + entries.add(entry); + } + GsonHelper.writeJsonFile(rawMeta, path); + } + private void removeAllShaders(Path path) { List rootPaths; try { @@ -945,7 +1018,7 @@ public abstract class AbstractPackManager implements PackManager { private void generateParticle(Path generatedPackPath) { if (!Config.removeTintedLeavesParticle()) return; - if (Config.packMaxVersion() < 21.49f) return; + if (Config.packMaxVersion().isBelow(MinecraftVersions.V1_21_5)) return; JsonObject particleJson = new JsonObject(); JsonArray textures = new JsonArray(); textures.add("empty"); @@ -979,7 +1052,7 @@ public abstract class AbstractPackManager implements PackManager { private void generateEquipments(Path generatedPackPath) { for (EquipmentGeneration generator : this.plugin.itemManager().equipmentsToGenerate()) { EquipmentData equipmentData = generator.modernData(); - if (equipmentData != null && Config.packMaxVersion() >= 21.4f) { + if (equipmentData != null && Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_4)) { Path equipmentPath = generatedPackPath .resolve("assets") .resolve(equipmentData.assetId().namespace()) @@ -1012,7 +1085,7 @@ public abstract class AbstractPackManager implements PackManager { this.plugin.logger().severe("Error writing equipment file", e); } } - if (equipmentData != null && Config.packMaxVersion() >= 21.2f && Config.packMinVersion() < 21.4f) { + if (equipmentData != null && Config.packMaxVersion().isAtOrAbove(MinecraftVersions.V1_21_2) && Config.packMinVersion().isBelow(MinecraftVersions.V1_21_4)) { Path equipmentPath = generatedPackPath .resolve("assets") .resolve(equipmentData.assetId().namespace()) @@ -1257,8 +1330,8 @@ public abstract class AbstractPackManager implements PackManager { } private void generateModernItemModels1_21_2(Path generatedPackPath) { - if (Config.packMaxVersion() < 21.19f) return; - if (Config.packMinVersion() > 21.39f) return; + if (Config.packMaxVersion().isBelow(MinecraftVersions.V1_21_2)) return; + if (Config.packMinVersion().isAtOrAbove(MinecraftVersions.V1_21_4)) return; // 此段代码生成1.21.2专用的item model文件,情况非常复杂! for (Map.Entry> entry : this.plugin.itemManager().modernItemModels1_21_2().entrySet()) { @@ -1323,8 +1396,8 @@ public abstract class AbstractPackManager implements PackManager { } } - private void generateModernItemModels1_21_4(Path generatedPackPath) { - if (Config.packMaxVersion() < 21.39f) return; + private void generateModernItemModels1_21_4(Path generatedPackPath, Consumer callback) { + if (Config.packMaxVersion().isBelow(MinecraftVersions.V1_21_4)) return; for (Map.Entry entry : this.plugin.itemManager().modernItemModels1_21_4().entrySet()) { Key key = entry.getKey(); Path itemPath = generatedPackPath @@ -1347,25 +1420,43 @@ public abstract class AbstractPackManager implements PackManager { this.plugin.logger().severe("Error creating " + itemPath.toAbsolutePath(), e); continue; } - JsonObject model = new JsonObject(); ModernItemModel modernItemModel = entry.getValue(); - model.add("model", modernItemModel.itemModel().get()); - if (!modernItemModel.handAnimationOnSwap()) { - model.addProperty("hand_animation_on_swap", false); - } - if (modernItemModel.oversizedInGui()) { - model.addProperty("oversized_in_gui", true); - } try (BufferedWriter writer = Files.newBufferedWriter(itemPath)) { - GsonHelper.get().toJson(model, writer); + GsonHelper.get().toJson(modernItemModel.toJson(Config.packMinVersion()), writer); } catch (IOException e) { this.plugin.logger().warn("Failed to save item model for " + key, e); } + + List revisions = modernItemModel.revisions(); + if (!revisions.isEmpty()) { + for (Revision revision : revisions) { + if (revision.matches(Config.packMinVersion(), Config.packMaxVersion())) { + Path overlayItemPath = generatedPackPath + .resolve(Config.createOverlayFolderName(revision.versionString())) + .resolve("assets") + .resolve(key.namespace()) + .resolve("items") + .resolve(key.value() + ".json"); + try { + Files.createDirectories(overlayItemPath.getParent()); + } catch (IOException e) { + this.plugin.logger().severe("Error creating " + overlayItemPath.toAbsolutePath(), e); + continue; + } + try (BufferedWriter writer = Files.newBufferedWriter(overlayItemPath)) { + GsonHelper.get().toJson(modernItemModel.toJson(revision.minVersion()), writer); + callback.accept(revision); + } catch (IOException e) { + this.plugin.logger().warn("Failed to save item model for " + key, e); + } + } + } + } } } - private void generateModernItemOverrides(Path generatedPackPath) { - if (Config.packMaxVersion() < 21.39f) return; + private void generateModernItemOverrides(Path generatedPackPath, Consumer callback) { + if (Config.packMaxVersion().isBelow(MinecraftVersions.V1_21_4)) return; for (Map.Entry> entry : this.plugin.itemManager().modernItemOverrides().entrySet()) { Key vanillaItemModel = entry.getKey(); Path overridedItemPath = generatedPackPath @@ -1374,10 +1465,10 @@ public abstract class AbstractPackManager implements PackManager { .resolve("items") .resolve(vanillaItemModel.value() + ".json"); - JsonObject originalItemModel; + ModernItemModel originalItemModel; if (Files.exists(overridedItemPath)) { try { - originalItemModel = GsonHelper.readJsonFile(overridedItemPath).getAsJsonObject(); + originalItemModel = ModernItemModel.fromJson(GsonHelper.readJsonFile(overridedItemPath).getAsJsonObject()); } catch (IOException e) { this.plugin.logger().warn("Failed to load existing item model (modern)", e); continue; @@ -1390,24 +1481,13 @@ public abstract class AbstractPackManager implements PackManager { } } - boolean handAnimationOnSwap = Optional.ofNullable(originalItemModel.get("hand_animation_on_swap")).map(JsonElement::getAsBoolean).orElse(true); - boolean oversizedInGui = Optional.ofNullable(originalItemModel.get("oversized_in_gui")).map(JsonElement::getAsBoolean).orElse(false); - JsonObject fallbackModel = originalItemModel.get("model").getAsJsonObject(); - JsonObject newJson = new JsonObject(); - JsonObject model = new JsonObject(); - newJson.add("model", model); - model.addProperty("type", "minecraft:range_dispatch"); - model.addProperty("property", "minecraft:custom_model_data"); - // 将原有的json读成fallback - model.add("fallback", fallbackModel); - JsonArray entries = new JsonArray(); - model.add("entries", entries); + boolean handAnimationOnSwap = originalItemModel.handAnimationOnSwap(); + boolean oversizedInGui = originalItemModel.oversizedInGui(); + + Map entries = new HashMap<>(); for (Map.Entry modelWithDataEntry : entry.getValue().entrySet()) { - JsonObject entryObject = new JsonObject(); ModernItemModel modernItemModel = modelWithDataEntry.getValue(); - entryObject.addProperty("threshold", modelWithDataEntry.getKey()); - entryObject.add("model", modernItemModel.itemModel().get()); - entries.add(entryObject); + entries.put(modelWithDataEntry.getKey().floatValue(), modernItemModel.itemModel()); if (modernItemModel.handAnimationOnSwap()) { handAnimationOnSwap = true; } @@ -1415,28 +1495,58 @@ public abstract class AbstractPackManager implements PackManager { oversizedInGui = true; } } - if (!handAnimationOnSwap) { - newJson.addProperty("hand_animation_on_swap", false); - } - if (oversizedInGui) { - newJson.addProperty("oversized_in_gui", true); - } + + RangeDispatchItemModel rangeDispatch = new RangeDispatchItemModel( + new CustomModelDataRangeDispatchProperty(0), + 1f, + originalItemModel.itemModel(), + entries + ); + + ModernItemModel newItemModel = new ModernItemModel(rangeDispatch, handAnimationOnSwap, oversizedInGui); try { Files.createDirectories(overridedItemPath.getParent()); } catch (IOException e) { this.plugin.logger().severe("Error creating " + overridedItemPath.toAbsolutePath(), e); continue; } + try (BufferedWriter writer = Files.newBufferedWriter(overridedItemPath)) { - GsonHelper.get().toJson(newJson, writer); + GsonHelper.get().toJson(newItemModel.toJson(Config.packMinVersion()), writer); } catch (IOException e) { this.plugin.logger().warn("Failed to save item model for " + vanillaItemModel, e); } + + List revisions = newItemModel.revisions(); + if (!revisions.isEmpty()) { + for (Revision revision : revisions) { + if (revision.matches(Config.packMinVersion(), Config.packMaxVersion())) { + Path overlayItemPath = generatedPackPath + .resolve(Config.createOverlayFolderName(revision.versionString())) + .resolve("assets") + .resolve(vanillaItemModel.namespace()) + .resolve("items") + .resolve(vanillaItemModel.value() + ".json"); + try { + Files.createDirectories(overlayItemPath.getParent()); + } catch (IOException e) { + this.plugin.logger().severe("Error creating " + overlayItemPath.toAbsolutePath(), e); + continue; + } + try (BufferedWriter writer = Files.newBufferedWriter(overlayItemPath)) { + GsonHelper.get().toJson(newItemModel.toJson(revision.minVersion()), writer); + callback.accept(revision); + } catch (IOException e) { + this.plugin.logger().warn("Failed to save item model for " + vanillaItemModel, e); + } + } + } + } } } private void generateLegacyItemOverrides(Path generatedPackPath) { - if (Config.packMinVersion() > 21.39f) return; + if (Config.packMinVersion().isAtOrAbove(MinecraftVersions.V1_21_4)) return; for (Map.Entry> entry : this.plugin.itemManager().legacyItemOverrides().entrySet()) { Key vanillaLegacyModel = entry.getKey(); Path overridedItemPath = generatedPackPath diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java index 1756b6b14..66eed8371 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/BaseItemModel.java @@ -1,13 +1,16 @@ package net.momirealms.craftengine.core.pack.model; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.ResourceLocation; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.tint.Tint; import net.momirealms.craftengine.core.pack.model.tint.Tints; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.Nullable; @@ -18,6 +21,7 @@ import java.util.Map; public class BaseItemModel implements ItemModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String path; private final List tints; private final ModelGeneration modelGeneration; @@ -29,21 +33,21 @@ public class BaseItemModel implements ItemModel { } public List tints() { - return tints; + return this.tints; } public String path() { - return path; + return this.path; } @Override - public JsonObject get() { + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); - json.addProperty("model", path); - if (!tints.isEmpty()) { + json.addProperty("model", this.path); + if (!this.tints.isEmpty()) { JsonArray array = new JsonArray(); - for (Tint tint : tints) { + for (Tint tint : this.tints) { array.add(tint.get()); } json.add("tints", array); @@ -65,6 +69,11 @@ public class BaseItemModel implements ItemModel { } } + @Override + public List revisions() { + return List.of(); + } + public static class Factory implements ItemModelFactory { @SuppressWarnings("unchecked") @@ -91,4 +100,26 @@ public class BaseItemModel implements ItemModel { } } } + + public static class Reader implements ItemModelReader { + + @Override + public ItemModel read(JsonObject json) { + String model = json.get("model").getAsString(); + if (json.has("tints")) { + JsonArray array = json.getAsJsonArray("tints"); + List tints = new ArrayList<>(array.size()); + for (JsonElement element : array) { + if (element instanceof JsonObject jo) { + tints.add(Tints.fromJson(jo)); + } else { + throw new IllegalArgumentException("tint is expected to be a json object"); + } + } + return new BaseItemModel(model, tints, null); + } else { + return new BaseItemModel(model, List.of(), null); + } + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/BundleSelectedItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/BundleSelectedItemModel.java new file mode 100644 index 000000000..28d1969e5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/BundleSelectedItemModel.java @@ -0,0 +1,52 @@ +package net.momirealms.craftengine.core.pack.model; + +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; +import net.momirealms.craftengine.core.pack.revision.Revision; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; + +import java.util.List; +import java.util.Map; + +public class BundleSelectedItemModel implements ItemModel { + public static final BundleSelectedItemModel INSTANCE = new BundleSelectedItemModel(); + public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + + @Override + public Key type() { + return ItemModels.BUNDLE_SELECTED_ITEM; + } + + @Override + public List modelsToGenerate() { + return List.of(); + } + + @Override + public List revisions() { + return List.of(); + } + + @Override + public JsonObject apply(MinecraftVersion version) { + JsonObject json = new JsonObject(); + json.addProperty("type", type().toString()); + return json; + } + + public static class Factory implements ItemModelFactory { + @Override + public ItemModel create(Map arguments) { + return INSTANCE; + } + } + + public static class Reader implements ItemModelReader { + @Override + public ItemModel read(JsonObject json) { + return INSTANCE; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java index 7d9d97af0..f17124ae8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java @@ -1,10 +1,13 @@ package net.momirealms.craftengine.core.pack.model; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.MiscUtils; import java.util.ArrayList; @@ -13,6 +16,7 @@ import java.util.Map; public class CompositeItemModel implements ItemModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final List models; public CompositeItemModel(List models) { @@ -20,16 +24,16 @@ public class CompositeItemModel implements ItemModel { } public List models() { - return models; + return this.models; } @Override - public JsonObject get() { + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); JsonArray array = new JsonArray(); - for (ItemModel model : models) { - array.add(model.get()); + for (ItemModel model : this.models) { + array.add(model.apply(version)); } json.add("models", array); return json; @@ -40,6 +44,15 @@ public class CompositeItemModel implements ItemModel { return ItemModels.COMPOSITE; } + @Override + public List revisions() { + List versions = new ArrayList<>(); + for (ItemModel model : this.models) { + versions.addAll(model.revisions()); + } + return versions; + } + @Override public List modelsToGenerate() { List models = new ArrayList<>(4); @@ -72,4 +85,24 @@ public class CompositeItemModel implements ItemModel { } } } + + public static class Reader implements ItemModelReader { + + @Override + public ItemModel read(JsonObject json) { + JsonArray models = json.getAsJsonArray("models"); + if (models == null) { + throw new IllegalArgumentException("models is expected to be a JsonArray"); + } + List modelList = new ArrayList<>(); + for (JsonElement model : models) { + if (model instanceof JsonObject jo) { + modelList.add(ItemModels.fromJson(jo)); + } else { + throw new IllegalArgumentException("model is expected to be a JsonObject"); + } + } + return new CompositeItemModel(modelList); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java index cb51e65a8..f55910045 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java @@ -4,8 +4,10 @@ import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.model.condition.ConditionProperties; import net.momirealms.craftengine.core.pack.model.condition.ConditionProperty; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.MiscUtils; import java.util.ArrayList; @@ -14,6 +16,7 @@ import java.util.Map; public class ConditionItemModel implements ItemModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final ConditionProperty property; private final ItemModel onTrue; private final ItemModel onFalse; @@ -25,15 +28,15 @@ public class ConditionItemModel implements ItemModel { } public ConditionProperty property() { - return property; + return this.property; } public ItemModel onTrue() { - return onTrue; + return this.onTrue; } public ItemModel onFalse() { - return onFalse; + return this.onFalse; } @Override @@ -41,21 +44,35 @@ public class ConditionItemModel implements ItemModel { return ItemModels.CONDITION; } + @Override + public List revisions() { + List onTrueVersions = this.onTrue.revisions(); + List onFalseVersions = this.onFalse.revisions(); + if (onTrueVersions.isEmpty() && onFalseVersions.isEmpty()) return List.of(); + List versions = new ArrayList<>(onTrueVersions.size() + onFalseVersions.size()); + versions.addAll(onTrueVersions); + versions.addAll(onFalseVersions); + return versions; + } + @Override public List modelsToGenerate() { - List models = new ArrayList<>(4); - models.addAll(onTrue.modelsToGenerate()); - models.addAll(onFalse.modelsToGenerate()); + List onTrueModels = this.onTrue.modelsToGenerate(); + List onFalseModels = this.onFalse.modelsToGenerate(); + if (onTrueModels.isEmpty() && onFalseModels.isEmpty()) return List.of(); + List models = new ArrayList<>(onTrueModels.size() + onFalseModels.size()); + models.addAll(onTrueModels); + models.addAll(onFalseModels); return models; } @Override - public JsonObject get() { + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); - json.add("on_true", onTrue.get()); - json.add("on_false", onFalse.get()); - property.accept(json); + json.add("on_true", this.onTrue.apply(version)); + json.add("on_false", this.onFalse.apply(version)); + this.property.accept(json); return json; } @@ -79,4 +96,15 @@ public class ConditionItemModel implements ItemModel { return new ConditionItemModel(property, onTrue, onFalse); } } + + public static class Reader implements ItemModelReader { + + @Override + public ItemModel read(JsonObject json) { + ConditionProperty property = ConditionProperties.fromJson(json); + ItemModel onTrue = ItemModels.fromJson(json.getAsJsonObject("on_true")); + ItemModel onFalse = ItemModels.fromJson(json.getAsJsonObject("on_false")); + return new ConditionItemModel(property, onTrue, onFalse); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/EmptyItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/EmptyItemModel.java index 0e60b5ca8..48013160f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/EmptyItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/EmptyItemModel.java @@ -2,17 +2,20 @@ package net.momirealms.craftengine.core.pack.model; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import java.util.List; import java.util.Map; public class EmptyItemModel implements ItemModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private static final EmptyItemModel INSTANCE = new EmptyItemModel(); @Override - public JsonObject get() { + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); return json; @@ -28,11 +31,22 @@ public class EmptyItemModel implements ItemModel { return List.of(); } - public static class Factory implements ItemModelFactory { + @Override + public List revisions() { + return List.of(); + } + public static class Factory implements ItemModelFactory { @Override public ItemModel create(Map arguments) { return INSTANCE; } } + + public static class Reader implements ItemModelReader { + @Override + public ItemModel read(JsonObject json) { + return INSTANCE; + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModel.java index 09d763d22..8b6636bf3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModel.java @@ -2,14 +2,18 @@ package net.momirealms.craftengine.core.pack.model; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import java.util.List; -import java.util.function.Supplier; +import java.util.function.Function; -public interface ItemModel extends Supplier { +public interface ItemModel extends Function { Key type(); List modelsToGenerate(); + + List revisions(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModelReader.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModelReader.java new file mode 100644 index 000000000..8da23e303 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModelReader.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.pack.model; + +import com.google.gson.JsonObject; + +public interface ItemModelReader { + + ItemModel read(JsonObject json); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java index b96282128..c4e5618c7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ItemModels.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack.model; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -18,23 +19,39 @@ public class ItemModels { public static final Key RANGE_DISPATCH = Key.of("minecraft:range_dispatch"); public static final Key SELECT = Key.of("minecraft:select"); public static final Key SPECIAL = Key.of("minecraft:special"); + public static final Key BUNDLE_SELECTED_ITEM = Key.of("minecraft:bundle/selected_item"); static { - register(EMPTY, EmptyItemModel.FACTORY); - register(COMPOSITE, CompositeItemModel.FACTORY); - register(MODEL, BaseItemModel.FACTORY); - register(CONDITION, ConditionItemModel.FACTORY); - register(SPECIAL, SpecialItemModel.FACTORY); - register(RANGE_DISPATCH, RangeDispatchItemModel.FACTORY); - register(SELECT, SelectItemModel.FACTORY); + registerFactory(EMPTY, EmptyItemModel.FACTORY); + registerReader(EMPTY, EmptyItemModel.READER); + registerFactory(COMPOSITE, CompositeItemModel.FACTORY); + registerReader(COMPOSITE, CompositeItemModel.READER); + registerFactory(MODEL, BaseItemModel.FACTORY); + registerReader(MODEL, BaseItemModel.READER); + registerFactory(CONDITION, ConditionItemModel.FACTORY); + registerReader(CONDITION, ConditionItemModel.READER); + registerFactory(SPECIAL, SpecialItemModel.FACTORY); + registerReader(SPECIAL, SpecialItemModel.READER); + registerFactory(RANGE_DISPATCH, RangeDispatchItemModel.FACTORY); + registerReader(RANGE_DISPATCH, RangeDispatchItemModel.READER); + registerFactory(SELECT, SelectItemModel.FACTORY); + registerReader(SELECT, SelectItemModel.READER); + registerFactory(BUNDLE_SELECTED_ITEM, BundleSelectedItemModel.FACTORY); + registerReader(BUNDLE_SELECTED_ITEM, BundleSelectedItemModel.READER); } - public static void register(Key key, ItemModelFactory factory) { + public static void registerFactory(Key key, ItemModelFactory factory) { Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.ITEM_MODEL_FACTORY) .registerForHolder(new ResourceKey<>(Registries.ITEM_MODEL_FACTORY.location(), key)); holder.bindValue(factory); } + public static void registerReader(Key key, ItemModelReader reader) { + Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.ITEM_MODEL_READER) + .registerForHolder(new ResourceKey<>(Registries.ITEM_MODEL_READER.location(), key)); + holder.bindValue(reader); + } + public static ItemModel fromMap(Map map) { String type = map.getOrDefault("type", "minecraft:model").toString(); Key key = Key.withDefaultNamespace(type, "minecraft"); @@ -44,4 +61,14 @@ public class ItemModels { } return factory.create(map); } + + public static ItemModel fromJson(JsonObject json) { + String type = json.get("type").getAsString(); + Key key = Key.withDefaultNamespace(type, "minecraft"); + ItemModelReader reader = BuiltInRegistries.ITEM_MODEL_READER.getValue(key); + if (reader == null) { + throw new IllegalArgumentException("Invalid item model type: " + key); + } + return reader.read(json); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ModernItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ModernItemModel.java index 17225c4ec..e20445618 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ModernItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ModernItemModel.java @@ -1,5 +1,11 @@ package net.momirealms.craftengine.core.pack.model; +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.revision.Revision; +import net.momirealms.craftengine.core.util.MinecraftVersion; + +import java.util.List; + public class ModernItemModel { private final ItemModel itemModel; private final boolean oversizedInGui; @@ -11,6 +17,31 @@ public class ModernItemModel { this.oversizedInGui = oversizedInGui; } + public static ModernItemModel fromJson(JsonObject json) { + ItemModel model = ItemModels.fromJson(json.getAsJsonObject("model")); + return new ModernItemModel( + model, + !json.has("hand_animation_on_swap") || json.get("hand_animation_on_swap").getAsBoolean(), + json.has("oversized_in_gui") && json.get("oversized_in_gui").getAsBoolean() + ); + } + + public JsonObject toJson(MinecraftVersion version) { + JsonObject json = new JsonObject(); + if (this.oversizedInGui) { + json.addProperty("oversized_in_gui", true); + } + if (!this.handAnimationOnSwap) { + json.addProperty("hand_animation_on_swap", false); + } + json.add("model", this.itemModel.apply(version)); + return json; + } + + public List revisions() { + return this.itemModel.revisions().stream().distinct().toList(); + } + public boolean handAnimationOnSwap() { return handAnimationOnSwap; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java index 90372bd90..5df6a5eda 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java @@ -1,12 +1,15 @@ package net.momirealms.craftengine.core.pack.model; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.rangedisptach.RangeDispatchProperties; import net.momirealms.craftengine.core.pack.model.rangedisptach.RangeDispatchProperty; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.NotNull; @@ -19,6 +22,7 @@ import java.util.Map; public class RangeDispatchItemModel implements ItemModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final RangeDispatchProperty property; private final float scale; private final ItemModel fallBack; @@ -35,42 +39,42 @@ public class RangeDispatchItemModel implements ItemModel { } public RangeDispatchProperty property() { - return property; + return this.property; } public float scale() { - return scale; + return this.scale; } @Nullable public ItemModel fallBack() { - return fallBack; + return this.fallBack; } public Map entries() { - return entries; + return this.entries; } @Override - public JsonObject get() { + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); - property.accept(json); + this.property.accept(json); JsonArray array = new JsonArray(); for (Map.Entry entry : entries.entrySet()) { float threshold = entry.getKey(); ItemModel model = entry.getValue(); JsonObject jo = new JsonObject(); jo.addProperty("threshold", threshold); - jo.add("model", model.get()); + jo.add("model", model.apply(version)); array.add(jo); } json.add("entries", array); - if (scale != 1) { - json.addProperty("scale", scale); + if (this.scale != 1) { + json.addProperty("scale", this.scale); } - if (fallBack != null) { - json.add("fallback", fallBack.get()); + if (this.fallBack != null) { + json.add("fallback", this.fallBack.apply(version)); } return json; } @@ -80,13 +84,25 @@ public class RangeDispatchItemModel implements ItemModel { return ItemModels.RANGE_DISPATCH; } + @Override + public List revisions() { + List versions = new ArrayList<>(4); + if (this.fallBack != null) { + versions.addAll(this.fallBack.revisions()); + } + for (ItemModel model : this.entries.values()) { + versions.addAll(model.revisions()); + } + return versions; + } + @Override public List modelsToGenerate() { List models = new ArrayList<>(4); - if (fallBack != null) { - models.addAll(fallBack.modelsToGenerate()); + if (this.fallBack != null) { + models.addAll(this.fallBack.modelsToGenerate()); } - for (ItemModel model : entries.values()) { + for (ItemModel model : this.entries.values()) { models.addAll(model.modelsToGenerate()); } return models; @@ -122,4 +138,28 @@ public class RangeDispatchItemModel implements ItemModel { } } } + + public static class Reader implements ItemModelReader { + + @Override + public ItemModel read(JsonObject json) { + JsonArray entriesObj = json.getAsJsonArray("entries"); + if (entriesObj == null) { + throw new IllegalArgumentException("entries is expected to be a JsonArray"); + } + Map entries = new HashMap<>(); + for (JsonElement entry : entriesObj) { + if (entry instanceof JsonObject entryObj) { + float threshold = entryObj.getAsJsonPrimitive("threshold").getAsFloat(); + ItemModel model = ItemModels.fromJson(entryObj.getAsJsonObject("model")); + entries.put(threshold, model); + } + } + return new RangeDispatchItemModel(RangeDispatchProperties.fromJson(json), + json.has("scale") ? json.get("scale").getAsFloat() : 1f, + json.has("fallback") ? ItemModels.fromJson(json.getAsJsonObject("fallback")) : null, + entries + ); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java index f57f30efb..ac9596f0b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java @@ -1,12 +1,16 @@ package net.momirealms.craftengine.core.pack.model; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.select.SelectProperties; import net.momirealms.craftengine.core.pack.model.select.SelectProperty; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.MiscUtils; import org.incendo.cloud.type.Either; import org.jetbrains.annotations.NotNull; @@ -19,6 +23,7 @@ import java.util.Map; public class SelectItemModel implements ItemModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final SelectProperty property; private final Map>, ItemModel> whenMap; private final ItemModel fallBack; @@ -32,28 +37,28 @@ public class SelectItemModel implements ItemModel { } public SelectProperty property() { - return property; + return this.property; } public Map>, ItemModel> whenMap() { - return whenMap; + return this.whenMap; } public ItemModel fallBack() { - return fallBack; + return this.fallBack; } @Override - public JsonObject get() { + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); - property.accept(json); + this.property.accept(json); JsonArray array = new JsonArray(); json.add("cases", array); - for (Map.Entry>, ItemModel> entry : whenMap.entrySet()) { + for (Map.Entry>, ItemModel> entry : this.whenMap.entrySet()) { JsonObject item = new JsonObject(); ItemModel itemModel = entry.getValue(); - item.add("model", itemModel.get()); + item.add("model", itemModel.apply(version)); Either> either = entry.getKey(); if (either.primary().isPresent()) { item.addProperty("when", either.primary().get()); @@ -67,8 +72,8 @@ public class SelectItemModel implements ItemModel { } array.add(item); } - if (fallBack != null) { - json.add("fallback", fallBack.get()); + if (this.fallBack != null) { + json.add("fallback", this.fallBack.apply(version)); } return json; } @@ -78,13 +83,25 @@ public class SelectItemModel implements ItemModel { return ItemModels.SELECT; } + @Override + public List revisions() { + List versions = new ArrayList<>(); + if (this.fallBack != null) { + versions.addAll(this.fallBack.revisions()); + } + for (ItemModel itemModel : this.whenMap.values()) { + versions.addAll(itemModel.revisions()); + } + return versions; + } + @Override public List modelsToGenerate() { List models = new ArrayList<>(4); - if (fallBack != null) { - models.addAll(fallBack.modelsToGenerate()); + if (this.fallBack != null) { + models.addAll(this.fallBack.modelsToGenerate()); } - for (ItemModel itemModel : whenMap.values()) { + for (ItemModel itemModel : this.whenMap.values()) { models.addAll(itemModel.modelsToGenerate()); } return models; @@ -132,4 +149,38 @@ public class SelectItemModel implements ItemModel { } } } + + public static class Reader implements ItemModelReader { + + @Override + public ItemModel read(JsonObject json) { + JsonArray cases = json.getAsJsonArray("cases"); + if (cases == null) { + throw new IllegalArgumentException("cases is expected to be a JsonArray"); + } + Map>, ItemModel> whenMap = new HashMap<>(cases.size()); + for (JsonElement e : cases) { + if (e instanceof JsonObject caseObj) { + ItemModel model = ItemModels.fromJson(caseObj.getAsJsonObject("model")); + JsonElement whenObj = caseObj.get("when"); + Either> either; + if (whenObj instanceof JsonArray array) { + List whens = new ArrayList<>(array.size()); + for (JsonElement o : array) { + whens.add(o.toString()); + } + either = Either.ofFallback(whens); + } else if (whenObj instanceof JsonPrimitive primitive) { + either = Either.ofPrimary(primitive.toString()); + } else { + throw new IllegalArgumentException("when is expected to be either JsonPrimitive or JsonArray"); + } + whenMap.put(either, model); + } else { + throw new IllegalArgumentException("case is expected to be a JsonObject"); + } + } + return new SelectItemModel(SelectProperties.fromJson(json), whenMap, json.has("fallback") ? ItemModels.fromJson(json.getAsJsonObject("fallback")) : null); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/SpecialItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/SpecialItemModel.java index 50052ce85..cb7409f03 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/SpecialItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/SpecialItemModel.java @@ -5,8 +5,10 @@ import net.momirealms.craftengine.core.pack.ResourceLocation; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.special.SpecialModel; import net.momirealms.craftengine.core.pack.model.special.SpecialModels; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.Nullable; @@ -16,6 +18,7 @@ import java.util.Map; public class SpecialItemModel implements ItemModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final SpecialModel specialModel; private final String base; private final ModelGeneration modelGeneration; @@ -27,19 +30,19 @@ public class SpecialItemModel implements ItemModel { } public SpecialModel specialModel() { - return specialModel; + return this.specialModel; } public String base() { - return base; + return this.base; } @Override - public JsonObject get() { + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); - json.add("model", specialModel.get()); - json.addProperty("base", base); + json.add("model", this.specialModel.apply(version)); + json.addProperty("base", this.base); return json; } @@ -57,6 +60,11 @@ public class SpecialItemModel implements ItemModel { } } + @Override + public List revisions() { + return this.specialModel.revisions(); + } + public static class Factory implements ItemModelFactory { @Override @@ -74,4 +82,14 @@ public class SpecialItemModel implements ItemModel { return new SpecialItemModel(SpecialModels.fromMap(model), base, modelGeneration); } } + + public static class Reader implements ItemModelReader { + + @Override + public ItemModel read(JsonObject json) { + String base = json.get("base").getAsString(); + SpecialModel sm = SpecialModels.fromJson(json.getAsJsonObject("model")); + return new SpecialItemModel(sm, base, null); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/BrokenConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/BrokenConditionProperty.java index 3cc36665a..cf08f10c0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/BrokenConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/BrokenConditionProperty.java @@ -9,6 +9,8 @@ import java.util.Map; public class BrokenConditionProperty implements ConditionProperty, LegacyModelPredicate { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + public static final BrokenConditionProperty INSTANCE = new BrokenConditionProperty(); @Override public Key type() { @@ -34,7 +36,14 @@ public class BrokenConditionProperty implements ConditionProperty, LegacyModelPr public static class Factory implements ConditionPropertyFactory { @Override public ConditionProperty create(Map arguments) { - return new BrokenConditionProperty(); + return INSTANCE; + } + } + + public static class Reader implements ConditionPropertyReader { + @Override + public ConditionProperty read(JsonObject json) { + return INSTANCE; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ComponentConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ComponentConditionProperty.java new file mode 100644 index 000000000..379ffab71 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ComponentConditionProperty.java @@ -0,0 +1,49 @@ +package net.momirealms.craftengine.core.pack.model.condition; + +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; + +public class ComponentConditionProperty implements ConditionProperty { + public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + private final String predicate; + private final String value; + + public ComponentConditionProperty(String predicate, String value) { + this.predicate = predicate; + this.value = value; + } + + @Override + public Key type() { + return ConditionProperties.COMPONENT; + } + + @Override + public void accept(JsonObject jsonObject) { + jsonObject.addProperty("property", type().toString()); + jsonObject.addProperty("predicate", this.predicate); + jsonObject.addProperty("value", this.value); + } + + public static class Factory implements ConditionPropertyFactory { + @Override + public ConditionProperty create(Map arguments) { + String predicate = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("predicate"), "warning.config.item.model.condition.component.missing_predicate"); + String value = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("value"), "warning.config.item.model.condition.component.missing_value"); + return new ComponentConditionProperty(predicate, value); + } + } + + public static class Reader implements ConditionPropertyReader { + @Override + public ConditionProperty read(JsonObject json) { + String predicate = json.get("predicate").getAsString(); + String value = json.get("value").getAsString(); + return new ComponentConditionProperty(predicate, value); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java index ccb36b529..8c3c990b1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionProperties.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack.model.condition; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -21,31 +22,52 @@ public class ConditionProperties { public static final Key FISHING_ROD_CAST = Key.of("minecraft:fishing_rod/cast"); public static final Key HAS_COMPONENT = Key.of("minecraft:has_component"); public static final Key KEYBIND_DOWN = Key.of("minecraft:keybind_down"); + public static final Key COMPONENT = Key.of("minecraft:component"); public static final Key SELECTED = Key.of("minecraft:selected"); public static final Key USING_ITEM = Key.of("minecraft:using_item"); public static final Key VIEW_ENTITY = Key.of("minecraft:view_entity"); static { - register(BROKEN, BrokenConditionProperty.FACTORY); - register(BUNDLE_HAS_SELECTED_ITEM, SimpleConditionProperty.FACTORY); - register(CARRIED, SimpleConditionProperty.FACTORY); - register(DAMAGED, DamagedConditionProperty.FACTORY); - register(EXTENDED_VIEW, SimpleConditionProperty.FACTORY); - register(FISHING_ROD_CAST, RodCastConditionProperty.FACTORY); - register(SELECTED, SimpleConditionProperty.FACTORY); - register(USING_ITEM, UsingItemConditionProperty.FACTORY); - register(VIEW_ENTITY, SimpleConditionProperty.FACTORY); - register(CUSTOM_MODEL_DATA, CustomModelDataConditionProperty.FACTORY); - register(HAS_COMPONENT, HasComponentConditionProperty.FACTORY); - register(KEYBIND_DOWN, KeyBindDownConditionProperty.FACTORY); + registerFactory(BROKEN, BrokenConditionProperty.FACTORY); + registerReader(BROKEN, BrokenConditionProperty.READER); + registerFactory(BUNDLE_HAS_SELECTED_ITEM, SimpleConditionProperty.FACTORY); + registerReader(BUNDLE_HAS_SELECTED_ITEM, SimpleConditionProperty.READER); + registerFactory(CARRIED, SimpleConditionProperty.FACTORY); + registerReader(CARRIED, SimpleConditionProperty.READER); + registerFactory(DAMAGED, DamagedConditionProperty.FACTORY); + registerReader(DAMAGED, DamagedConditionProperty.READER); + registerFactory(EXTENDED_VIEW, SimpleConditionProperty.FACTORY); + registerReader(EXTENDED_VIEW, SimpleConditionProperty.READER); + registerFactory(FISHING_ROD_CAST, RodCastConditionProperty.FACTORY); + registerReader(FISHING_ROD_CAST, RodCastConditionProperty.READER); + registerFactory(SELECTED, SimpleConditionProperty.FACTORY); + registerReader(SELECTED, SimpleConditionProperty.READER); + registerFactory(USING_ITEM, UsingItemConditionProperty.FACTORY); + registerReader(USING_ITEM, UsingItemConditionProperty.READER); + registerFactory(VIEW_ENTITY, SimpleConditionProperty.FACTORY); + registerReader(VIEW_ENTITY, SimpleConditionProperty.READER); + registerFactory(CUSTOM_MODEL_DATA, CustomModelDataConditionProperty.FACTORY); + registerReader(CUSTOM_MODEL_DATA, CustomModelDataConditionProperty.READER); + registerFactory(HAS_COMPONENT, HasComponentConditionProperty.FACTORY); + registerReader(HAS_COMPONENT, HasComponentConditionProperty.READER); + registerFactory(KEYBIND_DOWN, KeyBindDownConditionProperty.FACTORY); + registerReader(KEYBIND_DOWN, KeyBindDownConditionProperty.READER); + registerFactory(COMPONENT, ComponentConditionProperty.FACTORY); + registerReader(COMPONENT, ComponentConditionProperty.READER); } - public static void register(Key key, ConditionPropertyFactory factory) { + public static void registerFactory(Key key, ConditionPropertyFactory factory) { Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.CONDITION_PROPERTY_FACTORY) .registerForHolder(new ResourceKey<>(Registries.CONDITION_PROPERTY_FACTORY.location(), key)); holder.bindValue(factory); } + public static void registerReader(Key key, ConditionPropertyReader reader) { + Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.CONDITION_PROPERTY_READER) + .registerForHolder(new ResourceKey<>(Registries.CONDITION_PROPERTY_READER.location(), key)); + holder.bindValue(reader); + } + public static ConditionProperty fromMap(Map map) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("property"), "warning.config.item.model.condition.missing_property"); Key key = Key.withDefaultNamespace(type, "minecraft"); @@ -55,4 +77,14 @@ public class ConditionProperties { } return factory.create(map); } + + public static ConditionProperty fromJson(JsonObject json) { + String type = json.get("property").getAsString(); + Key key = Key.withDefaultNamespace(type, "minecraft"); + ConditionPropertyReader reader = BuiltInRegistries.CONDITION_PROPERTY_READER.getValue(key); + if (reader == null) { + throw new IllegalArgumentException("Invalid condition property type: " + key); + } + return reader.read(json); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionPropertyReader.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionPropertyReader.java new file mode 100644 index 000000000..5b11bebd3 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/ConditionPropertyReader.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.pack.model.condition; + +import com.google.gson.JsonObject; + +public interface ConditionPropertyReader { + + ConditionProperty read(JsonObject json); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/CustomModelDataConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/CustomModelDataConditionProperty.java index 205301496..0e413e2dd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/CustomModelDataConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/CustomModelDataConditionProperty.java @@ -8,6 +8,7 @@ import java.util.Map; public class CustomModelDataConditionProperty implements ConditionProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final int index; public CustomModelDataConditionProperty(int index) { @@ -22,16 +23,23 @@ public class CustomModelDataConditionProperty implements ConditionProperty { @Override public void accept(JsonObject jsonObject) { jsonObject.addProperty("property", type().toString()); - if (index != 0) - jsonObject.addProperty("index", index); + if (this.index != 0) + jsonObject.addProperty("index", this.index); } public static class Factory implements ConditionPropertyFactory { - @Override public ConditionProperty create(Map arguments) { int index = ResourceConfigUtils.getAsInt(arguments.getOrDefault("index", 0), "index"); return new CustomModelDataConditionProperty(index); } } + + public static class Reader implements ConditionPropertyReader { + @Override + public ConditionProperty read(JsonObject json) { + int index = json.has("index") ? json.get("index").getAsInt() : 0; + return new CustomModelDataConditionProperty(index); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/DamagedConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/DamagedConditionProperty.java index c6ace3972..2955ee8be 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/DamagedConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/DamagedConditionProperty.java @@ -8,6 +8,8 @@ import java.util.Map; public class DamagedConditionProperty implements ConditionProperty, LegacyModelPredicate { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + public static final DamagedConditionProperty INSTANCE = new DamagedConditionProperty(); @Override public Key type() { @@ -32,7 +34,14 @@ public class DamagedConditionProperty implements ConditionProperty, LegacyModelP public static class Factory implements ConditionPropertyFactory { @Override public ConditionProperty create(Map arguments) { - return new DamagedConditionProperty(); + return INSTANCE; + } + } + + public static class Reader implements ConditionPropertyReader { + @Override + public ConditionProperty read(JsonObject json) { + return INSTANCE; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/HasComponentConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/HasComponentConditionProperty.java index 85ab57e9c..0c4fb5846 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/HasComponentConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/HasComponentConditionProperty.java @@ -8,6 +8,7 @@ import java.util.Map; public class HasComponentConditionProperty implements ConditionProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String component; private final boolean ignoreDefault; @@ -47,4 +48,13 @@ public class HasComponentConditionProperty implements ConditionProperty { return new HasComponentConditionProperty(componentObj, ignoreDefault); } } + + public static class Reader implements ConditionPropertyReader { + @Override + public ConditionProperty read(JsonObject json) { + String component = json.get("component").getAsString(); + boolean ignoreDefault = json.has("ignore_default") && json.get("ignore_default").getAsBoolean(); + return new HasComponentConditionProperty(component, ignoreDefault); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/KeyBindDownConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/KeyBindDownConditionProperty.java index 12b099843..acf925aee 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/KeyBindDownConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/KeyBindDownConditionProperty.java @@ -8,6 +8,7 @@ import java.util.Map; public class KeyBindDownConditionProperty implements ConditionProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String keybind; public KeyBindDownConditionProperty(String keybind) { @@ -26,11 +27,18 @@ public class KeyBindDownConditionProperty implements ConditionProperty { } public static class Factory implements ConditionPropertyFactory { - @Override public ConditionProperty create(Map arguments) { - String keybindObj = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("keybind"), "warning.config.item.model.condition.keybind.missing"); + String keybindObj = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("keybind"), "warning.config.item.model.condition.keybind.missing_keybind"); return new KeyBindDownConditionProperty(keybindObj); } } + + public static class Reader implements ConditionPropertyReader { + @Override + public ConditionProperty read(JsonObject json) { + String keybind = json.get("keybind").getAsString(); + return new KeyBindDownConditionProperty(keybind); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/RodCastConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/RodCastConditionProperty.java index d228e33a2..d4faf0d7b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/RodCastConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/RodCastConditionProperty.java @@ -9,6 +9,8 @@ import java.util.Map; public class RodCastConditionProperty implements ConditionProperty, LegacyModelPredicate { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + public static final RodCastConditionProperty INSTANCE = new RodCastConditionProperty(); @Override public Key type() { @@ -34,7 +36,14 @@ public class RodCastConditionProperty implements ConditionProperty, LegacyModelP public static class Factory implements ConditionPropertyFactory { @Override public ConditionProperty create(Map arguments) { - return new RodCastConditionProperty(); + return INSTANCE; + } + } + + public static class Reader implements ConditionPropertyReader { + @Override + public ConditionProperty read(JsonObject json) { + return INSTANCE; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/SimpleConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/SimpleConditionProperty.java index 70d9709e5..879df1828 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/SimpleConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/SimpleConditionProperty.java @@ -7,6 +7,7 @@ import java.util.Map; public class SimpleConditionProperty implements ConditionProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final Key type; public SimpleConditionProperty(Key type) { @@ -30,4 +31,11 @@ public class SimpleConditionProperty implements ConditionProperty { return new SimpleConditionProperty(type); } } + + public static class Reader implements ConditionPropertyReader { + @Override + public ConditionProperty read(JsonObject json) { + return new SimpleConditionProperty(Key.of(json.get("property").toString())); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/UsingItemConditionProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/UsingItemConditionProperty.java index 08cd5ad2f..6a385429d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/UsingItemConditionProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/condition/UsingItemConditionProperty.java @@ -9,6 +9,8 @@ import java.util.Map; public class UsingItemConditionProperty implements ConditionProperty, LegacyModelPredicate { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + public static final UsingItemConditionProperty INSTANCE = new UsingItemConditionProperty(); @Override public Key type() { @@ -37,7 +39,14 @@ public class UsingItemConditionProperty implements ConditionProperty, LegacyMode public static class Factory implements ConditionPropertyFactory { @Override public ConditionProperty create(Map arguments) { - return new UsingItemConditionProperty(); + return INSTANCE; + } + } + + public static class Reader implements ConditionPropertyReader { + @Override + public ConditionProperty read(JsonObject json) { + return INSTANCE; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CompassRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CompassRangeDispatchProperty.java index e1008a576..f3a9b5d06 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CompassRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CompassRangeDispatchProperty.java @@ -8,10 +8,13 @@ import java.util.Map; public class CompassRangeDispatchProperty implements RangeDispatchProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String target; + private final boolean wobble; - public CompassRangeDispatchProperty(String target) { + public CompassRangeDispatchProperty(String target, boolean wobble) { this.target = target; + this.wobble = wobble; } @Override @@ -22,15 +25,27 @@ public class CompassRangeDispatchProperty implements RangeDispatchProperty { @Override public void accept(JsonObject jsonObject) { jsonObject.addProperty("property", type().toString()); - jsonObject.addProperty("target", target); + jsonObject.addProperty("target", this.target); + if (!this.wobble) { + jsonObject.addProperty("wobble", false); + } } public static class Factory implements RangeDispatchPropertyFactory { - @Override public RangeDispatchProperty create(Map arguments) { String targetObj = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("target"), "warning.config.item.model.range_dispatch.compass.missing_target"); - return new CompassRangeDispatchProperty(targetObj); + boolean wobble = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("wobble", true), "wobble"); + return new CompassRangeDispatchProperty(targetObj, wobble); + } + } + + public static class Reader implements RangeDispatchPropertyReader { + @Override + public RangeDispatchProperty read(JsonObject json) { + String target = json.get("target").getAsString(); + boolean wobble = !json.has("wobble") || json.get("wobble").getAsBoolean(); + return new CompassRangeDispatchProperty(target, wobble); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CrossBowPullingRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CrossBowPullingRangeDispatchProperty.java index 74c3d336c..ab6acc9b4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CrossBowPullingRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CrossBowPullingRangeDispatchProperty.java @@ -9,6 +9,8 @@ import java.util.Map; public class CrossBowPullingRangeDispatchProperty implements RangeDispatchProperty, LegacyModelPredicate { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + public static final CrossBowPullingRangeDispatchProperty INSTANCE = new CrossBowPullingRangeDispatchProperty(); @Override public void accept(JsonObject jsonObject) { @@ -32,10 +34,16 @@ public class CrossBowPullingRangeDispatchProperty implements RangeDispatchProper } public static class Factory implements RangeDispatchPropertyFactory { - @Override public RangeDispatchProperty create(Map arguments) { - return new CrossBowPullingRangeDispatchProperty(); + return INSTANCE; + } + } + + public static class Reader implements RangeDispatchPropertyReader { + @Override + public RangeDispatchProperty read(JsonObject json) { + return INSTANCE; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CustomModelDataRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CustomModelDataRangeDispatchProperty.java index 7c7b51031..b31ac63f0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CustomModelDataRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/CustomModelDataRangeDispatchProperty.java @@ -9,6 +9,7 @@ import java.util.Map; public class CustomModelDataRangeDispatchProperty implements RangeDispatchProperty, LegacyModelPredicate { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final int index; public CustomModelDataRangeDispatchProperty(int index) { @@ -37,11 +38,18 @@ public class CustomModelDataRangeDispatchProperty implements RangeDispatchProper } public static class Factory implements RangeDispatchPropertyFactory { - @Override public RangeDispatchProperty create(Map arguments) { int index = ResourceConfigUtils.getAsInt(arguments.getOrDefault("index", 0), "index"); return new CustomModelDataRangeDispatchProperty(index); } } + + public static class Reader implements RangeDispatchPropertyReader { + @Override + public RangeDispatchProperty read(JsonObject json) { + int index = json.has("index") ? json.get("index").getAsInt() : 0; + return new CustomModelDataRangeDispatchProperty(index); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/DamageRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/DamageRangeDispatchProperty.java index ddb47b314..4ae98cfda 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/DamageRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/DamageRangeDispatchProperty.java @@ -9,6 +9,7 @@ import java.util.Map; public class DamageRangeDispatchProperty implements RangeDispatchProperty, LegacyModelPredicate { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final boolean normalize; public DamageRangeDispatchProperty(boolean normalize) { @@ -41,11 +42,18 @@ public class DamageRangeDispatchProperty implements RangeDispatchProperty, Legac } public static class Factory implements RangeDispatchPropertyFactory { - @Override public RangeDispatchProperty create(Map arguments) { boolean normalize = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("normalize", true), "normalize"); return new DamageRangeDispatchProperty(normalize); } } + + public static class Reader implements RangeDispatchPropertyReader { + @Override + public RangeDispatchProperty read(JsonObject json) { + boolean normalize = !json.has("normalize") || json.get("normalize").getAsBoolean(); + return new DamageRangeDispatchProperty(normalize); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/NormalizeRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/NormalizeRangeDispatchProperty.java index 8150147f1..24f4802ea 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/NormalizeRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/NormalizeRangeDispatchProperty.java @@ -8,6 +8,7 @@ import java.util.Map; public class NormalizeRangeDispatchProperty implements RangeDispatchProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final Key type; private final boolean normalize; @@ -30,7 +31,6 @@ public class NormalizeRangeDispatchProperty implements RangeDispatchProperty { } public static class Factory implements RangeDispatchPropertyFactory { - @Override public RangeDispatchProperty create(Map arguments) { Key type = Key.of(arguments.get("property").toString()); @@ -38,4 +38,13 @@ public class NormalizeRangeDispatchProperty implements RangeDispatchProperty { return new NormalizeRangeDispatchProperty(type, normalize); } } + + public static class Reader implements RangeDispatchPropertyReader { + @Override + public RangeDispatchProperty read(JsonObject json) { + Key type = Key.of(json.get("property").toString()); + boolean normalize = !json.has("normalize") || json.get("normalize").getAsBoolean(); + return new NormalizeRangeDispatchProperty(type, normalize); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java index cafb43f05..5b6033ce4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchProperties.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack.model.rangedisptach; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -24,24 +25,40 @@ public class RangeDispatchProperties { public static final Key USE_DURATION = Key.of("minecraft:use_duration"); static { - register(BUNDLE_FULLNESS, SimpleRangeDispatchProperty.FACTORY); - register(COOLDOWN, SimpleRangeDispatchProperty.FACTORY); - register(CROSSBOW_PULL, CrossBowPullingRangeDispatchProperty.FACTORY); - register(COMPASS, CompassRangeDispatchProperty.FACTORY); - register(COUNT, NormalizeRangeDispatchProperty.FACTORY); - register(DAMAGE, DamageRangeDispatchProperty.FACTORY); - register(CUSTOM_MODEL_DATA, CustomModelDataRangeDispatchProperty.FACTORY); - register(TIME, TimeRangeDispatchProperty.FACTORY); - register(USE_CYCLE, UseCycleRangeDispatchProperty.FACTORY); - register(USE_DURATION, UseDurationRangeDispatchProperty.FACTORY); + registerFactory(BUNDLE_FULLNESS, SimpleRangeDispatchProperty.FACTORY); + registerReader(BUNDLE_FULLNESS, SimpleRangeDispatchProperty.READER); + registerFactory(COOLDOWN, SimpleRangeDispatchProperty.FACTORY); + registerReader(COOLDOWN, SimpleRangeDispatchProperty.READER); + registerFactory(CROSSBOW_PULL, CrossBowPullingRangeDispatchProperty.FACTORY); + registerReader(CROSSBOW_PULL, CrossBowPullingRangeDispatchProperty.READER); + registerFactory(COMPASS, CompassRangeDispatchProperty.FACTORY); + registerReader(COMPASS, CompassRangeDispatchProperty.READER); + registerFactory(COUNT, NormalizeRangeDispatchProperty.FACTORY); + registerReader(COUNT, NormalizeRangeDispatchProperty.READER); + registerFactory(DAMAGE, DamageRangeDispatchProperty.FACTORY); + registerReader(DAMAGE, DamageRangeDispatchProperty.READER); + registerFactory(CUSTOM_MODEL_DATA, CustomModelDataRangeDispatchProperty.FACTORY); + registerReader(CUSTOM_MODEL_DATA, CustomModelDataRangeDispatchProperty.READER); + registerFactory(TIME, TimeRangeDispatchProperty.FACTORY); + registerReader(TIME, TimeRangeDispatchProperty.READER); + registerFactory(USE_CYCLE, UseCycleRangeDispatchProperty.FACTORY); + registerReader(USE_CYCLE, UseCycleRangeDispatchProperty.READER); + registerFactory(USE_DURATION, UseDurationRangeDispatchProperty.FACTORY); + registerReader(USE_DURATION, UseDurationRangeDispatchProperty.READER); } - public static void register(Key key, RangeDispatchPropertyFactory factory) { + public static void registerFactory(Key key, RangeDispatchPropertyFactory factory) { Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.RANGE_DISPATCH_PROPERTY_FACTORY) .registerForHolder(new ResourceKey<>(Registries.RANGE_DISPATCH_PROPERTY_FACTORY.location(), key)); holder.bindValue(factory); } + public static void registerReader(Key key, RangeDispatchPropertyReader reader) { + Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.RANGE_DISPATCH_PROPERTY_READER) + .registerForHolder(new ResourceKey<>(Registries.RANGE_DISPATCH_PROPERTY_READER.location(), key)); + holder.bindValue(reader); + } + public static RangeDispatchProperty fromMap(Map map) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("property"), "warning.config.item.model.range_dispatch.missing_property"); Key key = Key.withDefaultNamespace(type, "minecraft"); @@ -51,4 +68,14 @@ public class RangeDispatchProperties { } return factory.create(map); } + + public static RangeDispatchProperty fromJson(JsonObject json) { + String type = json.get("property").getAsString(); + Key key = Key.withDefaultNamespace(type, "minecraft"); + RangeDispatchPropertyReader reader = BuiltInRegistries.RANGE_DISPATCH_PROPERTY_READER.getValue(key); + if (reader == null) { + throw new IllegalArgumentException("Invalid range dispatch property type: " + key); + } + return reader.read(json); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchPropertyReader.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchPropertyReader.java new file mode 100644 index 000000000..adcd719d0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/RangeDispatchPropertyReader.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.pack.model.rangedisptach; + +import com.google.gson.JsonObject; + +public interface RangeDispatchPropertyReader { + + RangeDispatchProperty read(JsonObject json); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/SimpleRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/SimpleRangeDispatchProperty.java index e354b683c..9e30fb5a8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/SimpleRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/SimpleRangeDispatchProperty.java @@ -7,6 +7,7 @@ import java.util.Map; public class SimpleRangeDispatchProperty implements RangeDispatchProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final Key type; public SimpleRangeDispatchProperty(Key type) { @@ -24,11 +25,18 @@ public class SimpleRangeDispatchProperty implements RangeDispatchProperty { } public static class Factory implements RangeDispatchPropertyFactory { - @Override public RangeDispatchProperty create(Map arguments) { Key type = Key.of(arguments.get("property").toString()); return new SimpleRangeDispatchProperty(type); } } + + public static class Reader implements RangeDispatchPropertyReader { + @Override + public RangeDispatchProperty read(JsonObject json) { + Key type = Key.of(json.get("property").toString()); + return new SimpleRangeDispatchProperty(type); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/TimeRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/TimeRangeDispatchProperty.java index 253011f67..bebe9a895 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/TimeRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/TimeRangeDispatchProperty.java @@ -8,6 +8,7 @@ import java.util.Map; public class TimeRangeDispatchProperty implements RangeDispatchProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String source; private final boolean wobble; @@ -31,7 +32,6 @@ public class TimeRangeDispatchProperty implements RangeDispatchProperty { } public static class Factory implements RangeDispatchPropertyFactory { - @Override public RangeDispatchProperty create(Map arguments) { String sourceObj = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("source"), "warning.config.item.model.range_dispatch.time.missing_source"); @@ -39,4 +39,13 @@ public class TimeRangeDispatchProperty implements RangeDispatchProperty { return new TimeRangeDispatchProperty(sourceObj, wobble); } } + + public static class Reader implements RangeDispatchPropertyReader { + @Override + public RangeDispatchProperty read(JsonObject json) { + String source = json.get("source").getAsString(); + boolean wobble = !json.has("wobble") || json.get("wobble").getAsBoolean(); + return new TimeRangeDispatchProperty(source, wobble); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseCycleRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseCycleRangeDispatchProperty.java index 972bcbbed..b585a7633 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseCycleRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseCycleRangeDispatchProperty.java @@ -8,9 +8,10 @@ import java.util.Map; public class UseCycleRangeDispatchProperty implements RangeDispatchProperty { public static final Factory FACTORY = new Factory(); - private final int period; + public static final Reader READER = new Reader(); + private final float period; - public UseCycleRangeDispatchProperty(int period) { + public UseCycleRangeDispatchProperty(float period) { this.period = period; } @@ -26,10 +27,17 @@ public class UseCycleRangeDispatchProperty implements RangeDispatchProperty { } public static class Factory implements RangeDispatchPropertyFactory { - @Override public RangeDispatchProperty create(Map arguments) { - int period = ResourceConfigUtils.getAsInt(arguments.getOrDefault("period", 0), "period"); + float period = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("period", 0), "period"); + return new UseCycleRangeDispatchProperty(period); + } + } + + public static class Reader implements RangeDispatchPropertyReader { + @Override + public RangeDispatchProperty read(JsonObject json) { + float period = json.has("period") ? json.get("period").getAsFloat() : 1.0f; return new UseCycleRangeDispatchProperty(period); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseDurationRangeDispatchProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseDurationRangeDispatchProperty.java index 39d7aff35..65f8b8b53 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseDurationRangeDispatchProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/rangedisptach/UseDurationRangeDispatchProperty.java @@ -10,6 +10,7 @@ import java.util.Map; public class UseDurationRangeDispatchProperty implements RangeDispatchProperty, LegacyModelPredicate { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final boolean remaining; public UseDurationRangeDispatchProperty(boolean remaining) { @@ -41,11 +42,18 @@ public class UseDurationRangeDispatchProperty implements RangeDispatchProperty, } public static class Factory implements RangeDispatchPropertyFactory { - @Override public RangeDispatchProperty create(Map arguments) { boolean remaining = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("remaining", false), "remaining"); return new UseDurationRangeDispatchProperty(remaining); } } + + public static class Reader implements RangeDispatchPropertyReader { + @Override + public RangeDispatchProperty read(JsonObject json) { + boolean remaining = json.has("remaining") && json.get("remaining").getAsBoolean(); + return new UseDurationRangeDispatchProperty(remaining); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/BlockStateSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/BlockStateSelectProperty.java index bb89446eb..c894e9c59 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/BlockStateSelectProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/BlockStateSelectProperty.java @@ -8,6 +8,7 @@ import java.util.Map; public class BlockStateSelectProperty implements SelectProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String blockStateProperty; public BlockStateSelectProperty(String blockStateProperty) { @@ -26,11 +27,18 @@ public class BlockStateSelectProperty implements SelectProperty { } public static class Factory implements SelectPropertyFactory { - @Override public SelectProperty create(Map arguments) { String property = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("block-state-property"), "warning.config.item.model.select.block_state.missing_property"); return new BlockStateSelectProperty(property); } } + + public static class Reader implements SelectPropertyReader { + @Override + public SelectProperty read(JsonObject json) { + String property = json.get("block_state_property").getAsString(); + return new BlockStateSelectProperty(property); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/ChargeTypeSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/ChargeTypeSelectProperty.java index 028b7db04..ce324f274 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/ChargeTypeSelectProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/ChargeTypeSelectProperty.java @@ -9,6 +9,8 @@ import java.util.Map; public class ChargeTypeSelectProperty implements SelectProperty, LegacyModelPredicate { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + public static final ChargeTypeSelectProperty INSTANCE = new ChargeTypeSelectProperty(); @Override public Key type() { @@ -33,10 +35,16 @@ public class ChargeTypeSelectProperty implements SelectProperty, LegacyModelPred } public static class Factory implements SelectPropertyFactory { - @Override public SelectProperty create(Map arguments) { - return new ChargeTypeSelectProperty(); + return INSTANCE; + } + } + + public static class Reader implements SelectPropertyReader { + @Override + public SelectProperty read(JsonObject json) { + return INSTANCE; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/ComponentSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/ComponentSelectProperty.java new file mode 100644 index 000000000..3448a3f4d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/ComponentSelectProperty.java @@ -0,0 +1,44 @@ +package net.momirealms.craftengine.core.pack.model.select; + +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.Map; + +public class ComponentSelectProperty implements SelectProperty { + public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + private final String component; + + public ComponentSelectProperty(String component) { + this.component = component; + } + + @Override + public Key type() { + return SelectProperties.COMPONENT; + } + + @Override + public void accept(JsonObject jsonObject) { + jsonObject.addProperty("property", type().toString()); + jsonObject.addProperty("component", this.component); + } + + public static class Factory implements SelectPropertyFactory { + @Override + public SelectProperty create(Map arguments) { + String component = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("component"), "warning.config.item.model.select.component.missing_component"); + return new ComponentSelectProperty(component); + } + } + + public static class Reader implements SelectPropertyReader { + @Override + public SelectProperty read(JsonObject json) { + String component = json.get("component").getAsString(); + return new ComponentSelectProperty(component); + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/CustomModelDataSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/CustomModelDataSelectProperty.java index cafd0e00a..bf318036d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/CustomModelDataSelectProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/CustomModelDataSelectProperty.java @@ -8,6 +8,7 @@ import java.util.Map; public class CustomModelDataSelectProperty implements SelectProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final int index; public CustomModelDataSelectProperty(int index) { @@ -26,11 +27,18 @@ public class CustomModelDataSelectProperty implements SelectProperty { } public static class Factory implements SelectPropertyFactory { - @Override public SelectProperty create(Map arguments) { int index = ResourceConfigUtils.getAsInt(arguments.getOrDefault("index", 0), "index"); return new CustomModelDataSelectProperty(index); } } + + public static class Reader implements SelectPropertyReader { + @Override + public SelectProperty read(JsonObject json) { + int index = json.has("index") ? json.get("index").getAsInt() : 0; + return new CustomModelDataSelectProperty(index); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/LocalTimeSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/LocalTimeSelectProperty.java index 7de594b79..3009ab33d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/LocalTimeSelectProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/LocalTimeSelectProperty.java @@ -10,6 +10,7 @@ import java.util.Map; public class LocalTimeSelectProperty implements SelectProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String pattern; private final String locale; private final String timeZone; @@ -30,17 +31,16 @@ public class LocalTimeSelectProperty implements SelectProperty { @Override public void accept(JsonObject jsonObject) { jsonObject.addProperty("property", type().toString()); - jsonObject.addProperty("pattern", pattern); - if (locale != null) { - jsonObject.addProperty("locale", locale); + jsonObject.addProperty("pattern", this.pattern); + if (this.locale != null) { + jsonObject.addProperty("locale", this.locale); } - if (timeZone != null) { - jsonObject.addProperty("time_zone", timeZone); + if (this.timeZone != null) { + jsonObject.addProperty("time_zone", this.timeZone); } } public static class Factory implements SelectPropertyFactory { - @Override public SelectProperty create(Map arguments) { String pattern = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("pattern"), "warning.config.item.model.select.local_time.missing_pattern"); @@ -49,4 +49,14 @@ public class LocalTimeSelectProperty implements SelectProperty { return new LocalTimeSelectProperty(pattern, locale, timeZone); } } + + public static class Reader implements SelectPropertyReader { + @Override + public SelectProperty read(JsonObject json) { + String pattern = json.get("pattern").getAsString(); + String locale = json.has("locale") ? json.get("locale").getAsString() : null; + String timeZone = json.has("time_zone") ? json.get("time_zone").getAsString() : null; + return new LocalTimeSelectProperty(pattern, locale, timeZone); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/MainHandSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/MainHandSelectProperty.java index 0a8912bfb..ca0bdb6b1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/MainHandSelectProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/MainHandSelectProperty.java @@ -8,6 +8,8 @@ import java.util.Map; public class MainHandSelectProperty implements SelectProperty, LegacyModelPredicate { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + public static final MainHandSelectProperty INSTANCE = new MainHandSelectProperty(); @Override public Key type() { @@ -31,10 +33,16 @@ public class MainHandSelectProperty implements SelectProperty, LegacyModelPredic } public static class Factory implements SelectPropertyFactory { - @Override public SelectProperty create(Map arguments) { - return new MainHandSelectProperty(); + return INSTANCE; + } + } + + public static class Reader implements SelectPropertyReader { + @Override + public SelectProperty read(JsonObject json) { + return INSTANCE; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java index ee01af5d5..f1cd14106 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectProperties.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack.model.select; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -21,25 +22,43 @@ public class SelectProperties { public static final Key LOCAL_TIME = Key.of("minecraft:local_time"); public static final Key MAIN_HAND = Key.of("minecraft:main_hand"); public static final Key TRIM_MATERIAL = Key.of("minecraft:trim_material"); + public static final Key COMPONENT = Key.of("minecraft:component"); static { - register(CHARGE_TYPE, ChargeTypeSelectProperty.FACTORY); - register(CONTEXT_DIMENSION, SimpleSelectProperty.FACTORY); - register(CONTEXT_ENTITY_TYPE, SimpleSelectProperty.FACTORY); - register(DISPLAY_CONTEXT, SimpleSelectProperty.FACTORY); - register(MAIN_HAND, MainHandSelectProperty.FACTORY); - register(TRIM_MATERIAL, TrimMaterialSelectProperty.FACTORY); - register(BLOCK_STATE, BlockStateSelectProperty.FACTORY); - register(CUSTOM_MODEL_DATA, CustomModelDataSelectProperty.FACTORY); - register(LOCAL_TIME, LocalTimeSelectProperty.FACTORY); + registerFactory(CHARGE_TYPE, ChargeTypeSelectProperty.FACTORY); + registerReader(CHARGE_TYPE, ChargeTypeSelectProperty.READER); + registerFactory(CONTEXT_DIMENSION, SimpleSelectProperty.FACTORY); + registerReader(CONTEXT_DIMENSION, SimpleSelectProperty.READER); + registerFactory(CONTEXT_ENTITY_TYPE, SimpleSelectProperty.FACTORY); + registerReader(CONTEXT_ENTITY_TYPE, SimpleSelectProperty.READER); + registerFactory(DISPLAY_CONTEXT, SimpleSelectProperty.FACTORY); + registerReader(DISPLAY_CONTEXT, SimpleSelectProperty.READER); + registerFactory(MAIN_HAND, MainHandSelectProperty.FACTORY); + registerReader(MAIN_HAND, MainHandSelectProperty.READER); + registerFactory(TRIM_MATERIAL, TrimMaterialSelectProperty.FACTORY); + registerReader(TRIM_MATERIAL, TrimMaterialSelectProperty.READER); + registerFactory(BLOCK_STATE, BlockStateSelectProperty.FACTORY); + registerReader(BLOCK_STATE, BlockStateSelectProperty.READER); + registerFactory(CUSTOM_MODEL_DATA, CustomModelDataSelectProperty.FACTORY); + registerReader(CUSTOM_MODEL_DATA, CustomModelDataSelectProperty.READER); + registerFactory(LOCAL_TIME, LocalTimeSelectProperty.FACTORY); + registerReader(LOCAL_TIME, LocalTimeSelectProperty.READER); + registerFactory(COMPONENT, ComponentSelectProperty.FACTORY); + registerReader(COMPONENT, ComponentSelectProperty.READER); } - public static void register(Key key, SelectPropertyFactory factory) { + public static void registerFactory(Key key, SelectPropertyFactory factory) { Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.SELECT_PROPERTY_FACTORY) .registerForHolder(new ResourceKey<>(Registries.SELECT_PROPERTY_FACTORY.location(), key)); holder.bindValue(factory); } + public static void registerReader(Key key, SelectPropertyReader reader) { + Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.SELECT_PROPERTY_READER) + .registerForHolder(new ResourceKey<>(Registries.SELECT_PROPERTY_READER.location(), key)); + holder.bindValue(reader); + } + public static SelectProperty fromMap(Map map) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("property"), "warning.config.item.model.select.missing_property"); Key key = Key.withDefaultNamespace(type, "minecraft"); @@ -49,4 +68,14 @@ public class SelectProperties { } return factory.create(map); } + + public static SelectProperty fromJson(JsonObject json) { + String type = json.get("property").getAsString(); + Key key = Key.withDefaultNamespace(type, "minecraft"); + SelectPropertyReader reader = BuiltInRegistries.SELECT_PROPERTY_READER.getValue(key); + if (reader == null) { + throw new IllegalArgumentException("Invalid select property type: " + key); + } + return reader.read(json); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectPropertyReader.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectPropertyReader.java new file mode 100644 index 000000000..f270388c8 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SelectPropertyReader.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.pack.model.select; + +import com.google.gson.JsonObject; + +public interface SelectPropertyReader { + + SelectProperty read(JsonObject json); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SimpleSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SimpleSelectProperty.java index d40592bb3..dbb000fa4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SimpleSelectProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/SimpleSelectProperty.java @@ -7,6 +7,7 @@ import java.util.Map; public class SimpleSelectProperty implements SelectProperty { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final Key type; public SimpleSelectProperty(Key type) { @@ -24,11 +25,18 @@ public class SimpleSelectProperty implements SelectProperty { } public static class Factory implements SelectPropertyFactory { - @Override public SelectProperty create(Map arguments) { Key type = Key.of(arguments.get("property").toString()); return new SimpleSelectProperty(type); } } + + public static class Reader implements SelectPropertyReader { + @Override + public SelectProperty read(JsonObject json) { + Key type = Key.of(json.get("property").toString()); + return new SimpleSelectProperty(type); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/TrimMaterialSelectProperty.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/TrimMaterialSelectProperty.java index ea17d87f9..434346bcd 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/TrimMaterialSelectProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/select/TrimMaterialSelectProperty.java @@ -9,6 +9,8 @@ import java.util.Map; public class TrimMaterialSelectProperty implements SelectProperty, LegacyModelPredicate { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + public static final TrimMaterialSelectProperty INSTANCE = new TrimMaterialSelectProperty(); private static final Map LEGACY_TRIM_DATA = new HashMap<>(); static { LEGACY_TRIM_DATA.put("minecraft:quartz", 0.1f); @@ -58,10 +60,16 @@ public class TrimMaterialSelectProperty implements SelectProperty, LegacyModelPr } public static class Factory implements SelectPropertyFactory { - @Override public SelectProperty create(Map arguments) { - return new TrimMaterialSelectProperty(); + return INSTANCE; + } + } + + public static class Reader implements SelectPropertyReader { + @Override + public SelectProperty read(JsonObject json) { + return INSTANCE; } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java index d8f27dc8c..64734b778 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BannerSpecialModel.java @@ -1,13 +1,17 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import java.util.List; import java.util.Map; public class BannerSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String color; public BannerSpecialModel(String color) { @@ -20,19 +24,31 @@ public class BannerSpecialModel implements SpecialModel { } @Override - public JsonObject get() { + public List revisions() { + return List.of(); + } + + @Override + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); - json.addProperty("color", color); + json.addProperty("color", this.color); return json; } public static class Factory implements SpecialModelFactory { - @Override public SpecialModel create(Map arguments) { String color = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("color"), "warning.config.item.model.special.banner.missing_color"); return new BannerSpecialModel(color); } } + + public static class Reader implements SpecialModelReader { + @Override + public SpecialModel read(JsonObject json) { + String color = json.get("color").getAsString(); + return new BannerSpecialModel(color); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BedSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BedSpecialModel.java index 4b2e60004..accd8abff 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BedSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/BedSpecialModel.java @@ -1,13 +1,17 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import java.util.List; import java.util.Map; public class BedSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String texture; public BedSpecialModel(String texture) { @@ -20,19 +24,31 @@ public class BedSpecialModel implements SpecialModel { } @Override - public JsonObject get() { + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); json.addProperty("texture", texture); return json; } - public static class Factory implements SpecialModelFactory { + @Override + public List revisions() { + return List.of(); + } + public static class Factory implements SpecialModelFactory { @Override public SpecialModel create(Map arguments) { - String color = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.bed.missing_texture"); - return new BedSpecialModel(color); + String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.bed.missing_texture"); + return new BedSpecialModel(texture); + } + } + + public static class Reader implements SpecialModelReader { + @Override + public SpecialModel read(JsonObject json) { + String texture = json.get("texture").getAsString(); + return new BedSpecialModel(texture); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java index a4cdce121..e7a7b4dfe 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ChestSpecialModel.java @@ -1,14 +1,18 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import java.util.List; import java.util.Map; public class ChestSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String texture; private final float openness; @@ -23,7 +27,12 @@ public class ChestSpecialModel implements SpecialModel { } @Override - public JsonObject get() { + public List revisions() { + return List.of(); + } + + @Override + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); json.addProperty("texture", texture); @@ -34,7 +43,6 @@ public class ChestSpecialModel implements SpecialModel { } public static class Factory implements SpecialModelFactory { - @Override public SpecialModel create(Map arguments) { float openness = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("openness", 0), "openness"); @@ -45,4 +53,12 @@ public class ChestSpecialModel implements SpecialModel { return new ChestSpecialModel(texture, openness); } } + + public static class Reader implements SpecialModelReader { + @Override + public SpecialModel read(JsonObject json) { + float openness = json.has("openness") ? json.get("openness").getAsFloat() : 0; + return new ChestSpecialModel(json.get("texture").getAsString(), openness); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java index 7ee58315d..35e43ac08 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/HeadSpecialModel.java @@ -1,19 +1,23 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import java.util.List; import java.util.Map; import java.util.Optional; public class HeadSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String kind; private final String texture; - private final int animation; + private final float animation; - public HeadSpecialModel(String kind, String texture, int animation) { + public HeadSpecialModel(String kind, String texture, float animation) { this.kind = kind; this.texture = texture; this.animation = animation; @@ -25,26 +29,40 @@ public class HeadSpecialModel implements SpecialModel { } @Override - public JsonObject get() { + public List revisions() { + return List.of(); + } + + @Override + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); - json.addProperty("kind", kind); - if (texture != null) { - json.addProperty("texture", texture); + json.addProperty("kind", this.kind); + if (this.texture != null) { + json.addProperty("texture", this.texture); } - if (animation != 0) { - json.addProperty("animation", animation); + if (this.animation != 0) { + json.addProperty("animation", this.animation); } return json; } public static class Factory implements SpecialModelFactory { - @Override public SpecialModel create(Map arguments) { String kind = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("kind"), "warning.config.item.model.special.head.missing_kind"); String texture = Optional.ofNullable(arguments.get("texture")).map(String::valueOf).orElse(null); - int animation = ResourceConfigUtils.getAsInt(arguments.getOrDefault("animation", 0), "animation"); + float animation = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("animation", 0), "animation"); + return new HeadSpecialModel(kind, texture, animation); + } + } + + public static class Reader implements SpecialModelReader { + @Override + public SpecialModel read(JsonObject json) { + String kind = json.get("kind").getAsString(); + String texture = json.has("texture") ? json.get("texture").getAsString() : null; + float animation = json.has("animation") ? json.get("animation").getAsFloat() : 0f; return new HeadSpecialModel(kind, texture, animation); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/PlayerHeadSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/PlayerHeadSpecialModel.java new file mode 100644 index 000000000..a106a009a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/PlayerHeadSpecialModel.java @@ -0,0 +1,56 @@ +package net.momirealms.craftengine.core.pack.model.special; + +import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.revision.Revision; +import net.momirealms.craftengine.core.pack.revision.Revisions; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; +import net.momirealms.craftengine.core.util.MinecraftVersions; + +import java.util.List; +import java.util.Map; + +public class PlayerHeadSpecialModel implements SpecialModel { + public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); + public static final PlayerHeadSpecialModel INSTANCE = new PlayerHeadSpecialModel(); + + public PlayerHeadSpecialModel() { + } + + @Override + public Key type() { + return SpecialModels.PLAYER_HEAD; + } + + @Override + public List revisions() { + return List.of(Revisions.SINCE_1_21_6); + } + + @Override + public JsonObject apply(MinecraftVersion version) { + JsonObject json = new JsonObject(); + if (version.isAtOrAbove(MinecraftVersions.V1_21_6)) { + json.addProperty("type", type().toString()); + } else { + json.addProperty("type", SpecialModels.HEAD.toString()); + json.addProperty("kind", "player"); + } + return json; + } + + public static class Factory implements SpecialModelFactory { + @Override + public SpecialModel create(Map arguments) { + return INSTANCE; + } + } + + public static class Reader implements SpecialModelReader { + @Override + public SpecialModel read(JsonObject json) { + return INSTANCE; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java index 917474670..7df3f9449 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/ShulkerBoxSpecialModel.java @@ -1,18 +1,22 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import org.jetbrains.annotations.Nullable; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; public class ShulkerBoxSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final String texture; private final float openness; private final Direction orientation; @@ -23,13 +27,18 @@ public class ShulkerBoxSpecialModel implements SpecialModel { this.orientation = orientation; } + @Override + public List revisions() { + return List.of(); + } + @Override public Key type() { return SpecialModels.SHULKER_BOX; } @Override - public JsonObject get() { + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); json.addProperty("texture", texture); @@ -41,7 +50,6 @@ public class ShulkerBoxSpecialModel implements SpecialModel { } public static class Factory implements SpecialModelFactory { - @Override public SpecialModel create(Map arguments) { float openness = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("openness", 0), "openness"); @@ -53,4 +61,14 @@ public class ShulkerBoxSpecialModel implements SpecialModel { return new ShulkerBoxSpecialModel(texture, openness, orientation); } } + + public static class Reader implements SpecialModelReader { + @Override + public SpecialModel read(JsonObject json) { + float openness = json.has("openness") ? json.get("openness").getAsFloat() : 0f; + Direction orientation = json.has("orientation") ? Direction.valueOf(json.get("orientation").getAsString().toUpperCase(Locale.ENGLISH)) : null; + String texture = json.get("texture").getAsString(); + return new ShulkerBoxSpecialModel(texture, openness, orientation); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java index 9f64bab5e..b6a83032c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SignSpecialModel.java @@ -1,13 +1,17 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import java.util.List; import java.util.Map; public class SignSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final Key type; private final String woodType; private final String texture; @@ -24,7 +28,12 @@ public class SignSpecialModel implements SpecialModel { } @Override - public JsonObject get() { + public List revisions() { + return List.of(); + } + + @Override + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); json.addProperty("wood_type", woodType); @@ -33,7 +42,6 @@ public class SignSpecialModel implements SpecialModel { } public static class Factory implements SpecialModelFactory { - @Override public SpecialModel create(Map arguments) { Key type = Key.of(arguments.get("type").toString()); @@ -42,4 +50,14 @@ public class SignSpecialModel implements SpecialModel { return new SignSpecialModel(type, woodType, texture); } } + + public static class Reader implements SpecialModelReader { + @Override + public SpecialModel read(JsonObject json) { + Key type = Key.of(json.get("type").toString()); + String woodType = json.get("wood_type").getAsString(); + String texture = json.get("texture").getAsString(); + return new SignSpecialModel(type, woodType, texture); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SimpleSpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SimpleSpecialModel.java index c971e102c..9b7fa9d43 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SimpleSpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SimpleSpecialModel.java @@ -1,12 +1,16 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; +import java.util.List; import java.util.Map; public class SimpleSpecialModel implements SpecialModel { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final Key type; public SimpleSpecialModel(Key type) { @@ -19,18 +23,30 @@ public class SimpleSpecialModel implements SpecialModel { } @Override - public JsonObject get() { + public JsonObject apply(MinecraftVersion version) { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); return json; } - public static class Factory implements SpecialModelFactory { + @Override + public List revisions() { + return List.of(); + } + public static class Factory implements SpecialModelFactory { @Override public SpecialModel create(Map arguments) { Key type = Key.of(arguments.get("type").toString()); return new SimpleSpecialModel(type); } } + + public static class Reader implements SpecialModelReader { + @Override + public SpecialModel read(JsonObject json) { + Key type = Key.of(json.get("type").toString()); + return new SimpleSpecialModel(type); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModel.java index 173f6c916..c5cfaac2a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModel.java @@ -1,11 +1,16 @@ package net.momirealms.craftengine.core.pack.model.special; import com.google.gson.JsonObject; +import net.momirealms.craftengine.core.pack.revision.Revision; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; -import java.util.function.Supplier; +import java.util.List; +import java.util.function.Function; -public interface SpecialModel extends Supplier { +public interface SpecialModel extends Function { Key type(); + + List revisions(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModelReader.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModelReader.java new file mode 100644 index 000000000..e15039a3f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModelReader.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.pack.model.special; + +import com.google.gson.JsonObject; + +public interface SpecialModelReader { + + SpecialModel read(JsonObject json); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java index 61f4278fa..16256d869 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/special/SpecialModels.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack.model.special; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -26,25 +27,44 @@ public class SpecialModels { public static final Key PLAYER_HEAD = Key.of("minecraft:player_head"); static { - register(TRIDENT, SimpleSpecialModel.FACTORY); - register(DECORATED_POT, SimpleSpecialModel.FACTORY); - register(CONDUIT, SimpleSpecialModel.FACTORY); - register(SHIELD, SimpleSpecialModel.FACTORY); - register(HANGING_SIGN, SignSpecialModel.FACTORY); - register(STANDING_SIGN, SignSpecialModel.FACTORY); - register(PLAYER_HEAD, SignSpecialModel.FACTORY); - register(CHEST, ChestSpecialModel.FACTORY); - register(BANNER, BannerSpecialModel.FACTORY); - register(BED, BedSpecialModel.FACTORY); - register(HEAD, HeadSpecialModel.FACTORY); + registerFactory(TRIDENT, SimpleSpecialModel.FACTORY); + registerReader(TRIDENT, SimpleSpecialModel.READER); + registerFactory(DECORATED_POT, SimpleSpecialModel.FACTORY); + registerReader(DECORATED_POT, SimpleSpecialModel.READER); + registerFactory(CONDUIT, SimpleSpecialModel.FACTORY); + registerReader(CONDUIT, SimpleSpecialModel.READER); + registerFactory(SHIELD, SimpleSpecialModel.FACTORY); + registerReader(SHIELD, SimpleSpecialModel.READER); + registerFactory(HANGING_SIGN, SignSpecialModel.FACTORY); + registerReader(HANGING_SIGN, SignSpecialModel.READER); + registerFactory(STANDING_SIGN, SignSpecialModel.FACTORY); + registerReader(STANDING_SIGN, SignSpecialModel.READER); + registerFactory(PLAYER_HEAD, PlayerHeadSpecialModel.FACTORY); + registerReader(PLAYER_HEAD, PlayerHeadSpecialModel.READER); + registerFactory(CHEST, ChestSpecialModel.FACTORY); + registerReader(CHEST, ChestSpecialModel.READER); + registerFactory(BANNER, BannerSpecialModel.FACTORY); + registerReader(BANNER, BannerSpecialModel.READER); + registerFactory(BED, BedSpecialModel.FACTORY); + registerReader(BED, BedSpecialModel.READER); + registerFactory(HEAD, HeadSpecialModel.FACTORY); + registerReader(HEAD, HeadSpecialModel.READER); + registerFactory(SHULKER_BOX, ShulkerBoxSpecialModel.FACTORY); + registerReader(SHULKER_BOX, ShulkerBoxSpecialModel.READER); } - public static void register(Key key, SpecialModelFactory factory) { + public static void registerFactory(Key key, SpecialModelFactory factory) { Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.SPECIAL_MODEL_FACTORY) .registerForHolder(new ResourceKey<>(Registries.SPECIAL_MODEL_FACTORY.location(), key)); holder.bindValue(factory); } + public static void registerReader(Key key, SpecialModelReader factory) { + Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.SPECIAL_MODEL_READER) + .registerForHolder(new ResourceKey<>(Registries.SPECIAL_MODEL_READER.location(), key)); + holder.bindValue(factory); + } + public static SpecialModel fromMap(Map map) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.model.special.missing_type"); Key key = Key.withDefaultNamespace(type, "minecraft"); @@ -54,4 +74,14 @@ public class SpecialModels { } return factory.create(map); } + + public static SpecialModel fromJson(JsonObject json) { + String type = json.get("type").getAsString(); + Key key = Key.withDefaultNamespace(type, "minecraft"); + SpecialModelReader reader = BuiltInRegistries.SPECIAL_MODEL_READER.getValue(key); + if (reader == null) { + throw new IllegalArgumentException("Invalid special model type: " + key); + } + return reader.read(json); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java index 54c6d40c2..4faf39430 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/ConstantTint.java @@ -10,6 +10,7 @@ import java.util.Map; public class ConstantTint implements Tint { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final Either> value; public ConstantTint(Either> value) { @@ -30,11 +31,17 @@ public class ConstantTint implements Tint { } public static class Factory implements TintFactory { - @Override public Tint create(Map arguments) { - Object value = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("value"), "warning.config.item.model.tint.constant.missing_value"); + Object value = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(arguments, "value", "default"), "warning.config.item.model.tint.constant.missing_value"); return new ConstantTint(parseTintValue(value)); } } + + public static class Reader implements TintReader { + @Override + public Tint read(JsonObject json) { + return new ConstantTint(parseTintValue(json.get("value"))); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java index 0ed576384..88e9b8266 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/CustomModelDataTint.java @@ -10,6 +10,7 @@ import java.util.Map; public class CustomModelDataTint implements Tint { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final Either> value; private final int index; @@ -28,18 +29,27 @@ public class CustomModelDataTint implements Tint { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); if (index != 0) - json.addProperty("index", index); - applyAnyTint(json, value, "default"); + json.addProperty("index", this.index); + applyAnyTint(json, this.value, "default"); return json; } public static class Factory implements TintFactory { - @Override public Tint create(Map arguments) { - Object value = arguments.getOrDefault("default", 0); + Object value = arguments.containsKey("default") ? arguments.getOrDefault("default", 0) : arguments.getOrDefault("value", 0); int index = ResourceConfigUtils.getAsInt(arguments.getOrDefault("index", 0), "index"); return new CustomModelDataTint(parseTintValue(value), index); } } + + public static class Reader implements TintReader { + @Override + public Tint read(JsonObject json) { + return new CustomModelDataTint( + parseTintValue(json.get("default")), + json.has("index") ? json.get("index").getAsInt() : 0 + ); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/GrassTint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/GrassTint.java index d579c0c43..105f606a3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/GrassTint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/GrassTint.java @@ -9,6 +9,7 @@ import java.util.Map; public class GrassTint implements Tint { public static final Factory FACTORY = new Factory(); + public static final Reader READER = new Reader(); private final float temperature; private final float downfall; @@ -32,7 +33,6 @@ public class GrassTint implements Tint { } public static class Factory implements TintFactory { - @Override public Tint create(Map arguments) { float temperature = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("temperature", 0), "temperature"); @@ -46,4 +46,13 @@ public class GrassTint implements Tint { return new GrassTint(temperature, downfall); } } + + public static class Reader implements TintReader { + @Override + public Tint read(JsonObject json) { + float temperature = json.has("temperature") ? json.get("temperature").getAsFloat() : 0; + float downfall = json.has("downfall") ? json.get("downfall").getAsFloat() : 0; + return new GrassTint(temperature, downfall); + } + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java index a543bcfe9..570af530c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/SimpleDefaultTint.java @@ -3,17 +3,19 @@ package net.momirealms.craftengine.core.pack.model.tint; import com.google.gson.JsonObject; import net.momirealms.craftengine.core.util.Key; import org.incendo.cloud.type.Either; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; public class SimpleDefaultTint implements Tint { public static final Factory FACTORY = new Factory(); - private final Either> value; + public static final Reader READER = new Reader(); + private final Either> defaultValue; private final Key type; - public SimpleDefaultTint(Either> value, Key type) { - this.value = value; + public SimpleDefaultTint(Key type, @Nullable Either> defaultValue) { + this.defaultValue = defaultValue; this.type = type; } @@ -26,17 +28,23 @@ public class SimpleDefaultTint implements Tint { public JsonObject get() { JsonObject json = new JsonObject(); json.addProperty("type", type().toString()); - applyAnyTint(json, value, "default"); + applyAnyTint(json, this.defaultValue, "default"); return json; } public static class Factory implements TintFactory { - @Override public Tint create(Map arguments) { - Object value = arguments.getOrDefault("default", 0); + Object value = arguments.containsKey("default") ? arguments.getOrDefault("default", 0) : arguments.getOrDefault("value", 0); Key type = Key.of(arguments.get("type").toString()); - return new SimpleDefaultTint(parseTintValue(value), type); + return new SimpleDefaultTint(type, parseTintValue(value)); + } + } + + public static class Reader implements TintReader { + @Override + public Tint read(JsonObject json) { + return new SimpleDefaultTint(Key.of(json.get("type").getAsString()), parseTintValue(json.get("default"))); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tint.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tint.java index b0be23502..80a16fdca 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tint.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tint.java @@ -13,6 +13,7 @@ public interface Tint extends Supplier { Key type(); default void applyAnyTint(JsonObject json, Either> value, String key) { + if (value == null) return; if (value.primary().isPresent()) { json.addProperty(key, value.primary().get()); } else { diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintReader.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintReader.java new file mode 100644 index 000000000..5c6167ae0 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/TintReader.java @@ -0,0 +1,30 @@ +package net.momirealms.craftengine.core.pack.model.tint; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import org.incendo.cloud.type.Either; + +import java.util.ArrayList; +import java.util.List; + +public interface TintReader { + + Tint read(JsonObject json); + + default Either> parseTintValue(JsonElement element) { + if (element instanceof JsonPrimitive jsonPrimitive) { + return Either.ofPrimary(jsonPrimitive.getAsInt()); + } else if (element instanceof JsonArray array) { + List result = new ArrayList<>(); + for (JsonElement jsonElement : array) { + result.add(jsonElement.getAsFloat()); + } + return Either.ofFallback(result); + } else if (element instanceof JsonObject object) { + throw new IllegalArgumentException("Can't parse tint value: " + object); + } + return null; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java index 07f9172dc..2aa81c197 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/tint/Tints.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.pack.model.tint; +import com.google.gson.JsonObject; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -22,22 +23,36 @@ public class Tints { public static final Key TEAM = Key.of("minecraft:team"); static { - register(CONSTANT, ConstantTint.FACTORY); - register(CUSTOM_MODEL_DATA, CustomModelDataTint.FACTORY); - register(GRASS, GrassTint.FACTORY); - register(DYE, SimpleDefaultTint.FACTORY); - register(FIREWORK, SimpleDefaultTint.FACTORY); - register(MAP_COLOR, SimpleDefaultTint.FACTORY); - register(POTION, SimpleDefaultTint.FACTORY); - register(TEAM, SimpleDefaultTint.FACTORY); + registerFactory(CONSTANT, ConstantTint.FACTORY); + registerReader(CONSTANT, ConstantTint.READER); + registerFactory(CUSTOM_MODEL_DATA, CustomModelDataTint.FACTORY); + registerReader(CUSTOM_MODEL_DATA, CustomModelDataTint.READER); + registerFactory(GRASS, GrassTint.FACTORY); + registerReader(GRASS, GrassTint.READER); + registerFactory(DYE, SimpleDefaultTint.FACTORY); + registerReader(DYE, SimpleDefaultTint.READER); + registerFactory(FIREWORK, SimpleDefaultTint.FACTORY); + registerReader(FIREWORK, SimpleDefaultTint.READER); + registerFactory(MAP_COLOR, SimpleDefaultTint.FACTORY); + registerReader(MAP_COLOR, SimpleDefaultTint.READER); + registerFactory(POTION, SimpleDefaultTint.FACTORY); + registerReader(POTION, SimpleDefaultTint.READER); + registerFactory(TEAM, SimpleDefaultTint.FACTORY); + registerReader(TEAM, SimpleDefaultTint.READER); } - public static void register(Key key, TintFactory factory) { + public static void registerFactory(Key key, TintFactory factory) { Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.TINT_FACTORY) .registerForHolder(new ResourceKey<>(Registries.TINT_FACTORY.location(), key)); holder.bindValue(factory); } + public static void registerReader(Key key, TintReader reader) { + Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.TINT_READER) + .registerForHolder(new ResourceKey<>(Registries.TINT_READER.location(), key)); + holder.bindValue(reader); + } + public static Tint fromMap(Map map) { String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.model.tint.missing_type"); Key key = Key.withDefaultNamespace(type, "minecraft"); @@ -47,4 +62,14 @@ public class Tints { } return factory.create(map); } + + public static Tint fromJson(JsonObject json) { + String type = json.get("type").getAsString(); + Key key = Key.withDefaultNamespace(type, "minecraft"); + TintReader reader = BuiltInRegistries.TINT_READER.getValue(key); + if (reader == null) { + throw new IllegalArgumentException("Invalid tint type: " + type); + } + return reader.read(json); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java b/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java new file mode 100644 index 000000000..849123084 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revision.java @@ -0,0 +1,139 @@ +package net.momirealms.craftengine.core.pack.revision; + +import net.momirealms.craftengine.core.util.MinecraftVersion; +import net.momirealms.craftengine.core.util.MinecraftVersions; + +import java.util.Objects; + +public interface Revision { + + int minPackVersion(); + + int maxPackVersion(); + + String versionString(); + + boolean matches(MinecraftVersion min, MinecraftVersion max); + + MinecraftVersion minVersion(); + + MinecraftVersion maxVersion(); + + static Revision since(MinecraftVersion minecraftVersion) { + return new Since(minecraftVersion); + } + + static Revision fromTo(MinecraftVersion from, MinecraftVersion to) { + return new FromTo(from, to); + } + + class Since implements Revision { + private final MinecraftVersion minVersion; + private String versionString; + + public Since(MinecraftVersion minVersion) { + this.minVersion = minVersion; + } + + @Override + public MinecraftVersion maxVersion() { + return MinecraftVersions.FUTURE; + } + + @Override + public MinecraftVersion minVersion() { + return this.minVersion; + } + + @Override + public String versionString() { + if (this.versionString == null) { + this.versionString = this.minVersion.version().replace(".", "_"); + } + return this.versionString; + } + + @Override + public boolean matches(MinecraftVersion min, MinecraftVersion max) { + return this.minVersion.isAtOrBelow(max); + } + + @Override + public int maxPackVersion() { + return MinecraftVersions.FUTURE.packFormat(); + } + + @Override + public int minPackVersion() { + return this.minVersion.packFormat(); + } + + @Override + public final boolean equals(Object object) { + if (!(object instanceof Since since)) return false; + return this.minVersion.equals(since.minVersion); + } + + @Override + public int hashCode() { + return this.minVersion.hashCode(); + } + } + + class FromTo implements Revision { + private final MinecraftVersion minVersion; + private final MinecraftVersion maxVersion; + private String versionString; + + public FromTo(MinecraftVersion minVersion, MinecraftVersion maxVersion) { + this.minVersion = minVersion; + this.maxVersion = maxVersion; + } + + @Override + public MinecraftVersion maxVersion() { + return this.maxVersion; + } + + @Override + public MinecraftVersion minVersion() { + return this.minVersion; + } + + @Override + public boolean matches(MinecraftVersion min, MinecraftVersion max) { + return !min.isAbove(this.maxVersion) || !max.isBelow(this.minVersion); + } + + @Override + public int minPackVersion() { + return this.minVersion.packFormat(); + } + + @Override + public int maxPackVersion() { + return this.maxVersion.packFormat(); + } + + @Override + public String versionString() { + if (this.versionString == null) { + this.versionString = this.minVersion.version().replace(".", "_") + "-" + this.maxVersion.version().replace(".", "_"); + } + return this.versionString; + } + + @Override + public final boolean equals(Object object) { + if (!(object instanceof FromTo fromTo)) return false; + return Objects.equals(minVersion, fromTo.minVersion) && Objects.equals(maxVersion, fromTo.maxVersion); + } + + @Override + public int hashCode() { + int result = Objects.hashCode(minVersion); + result = 31 * result + Objects.hashCode(maxVersion); + return result; + } + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revisions.java b/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revisions.java new file mode 100644 index 000000000..82f434c2f --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/revision/Revisions.java @@ -0,0 +1,9 @@ +package net.momirealms.craftengine.core.pack.revision; + +import net.momirealms.craftengine.core.util.MinecraftVersions; + +public final class Revisions { + private Revisions() {} + + public static final Revision SINCE_1_21_6 = Revision.since(MinecraftVersions.V1_21_6); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index 16bf6f3f4..65c05e6c4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.plugin.locale.TranslationManager; import net.momirealms.craftengine.core.plugin.logger.filter.DisconnectLogFilter; import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MinecraftVersion; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.InjectionTarget; import net.momirealms.craftengine.core.world.chunk.storage.CompressionMethod; @@ -80,8 +81,8 @@ public class Config { protected List resource_pack$protection$obfuscation$resource_location$bypass_sounds; protected List resource_pack$protection$obfuscation$resource_location$bypass_equipments; - protected float resource_pack$supported_version$min; - protected float resource_pack$supported_version$max; + protected MinecraftVersion resource_pack$supported_version$min; + protected MinecraftVersion resource_pack$supported_version$max; protected String resource_pack$overlay_format; protected boolean resource_pack$delivery$kick_if_declined; @@ -368,15 +369,11 @@ public class Config { firstTime = false; } - private static float getVersion(String version) { + private static MinecraftVersion getVersion(String version) { if (version.equalsIgnoreCase("LATEST")) { - version = PluginProperties.getValue("latest-version"); + return new MinecraftVersion(PluginProperties.getValue("latest-version")); } - String[] split = version.split("\\.", 2); - if (split.length != 2) { - throw new IllegalArgumentException("Invalid version: " + version); - } - return Float.parseFloat(split[1]); + return MinecraftVersion.parse(version); } public static String configVersion() { @@ -427,11 +424,11 @@ public class Config { return instance.light_system$enable; } - public static float packMinVersion() { + public static MinecraftVersion packMinVersion() { return instance.resource_pack$supported_version$min; } - public static float packMaxVersion() { + public static MinecraftVersion packMaxVersion() { return instance.resource_pack$supported_version$max; } @@ -747,6 +744,10 @@ public class Config { return instance.resource_pack$exclude_core_shaders; } + public static String createOverlayFolderName(String version) { + return instance.resource_pack$overlay_format.replace("{version}", version); + } + public YamlDocument loadOrCreateYamlData(String fileName) { Path path = this.plugin.dataFolderPath().resolve(fileName); if (!Files.exists(path)) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/GuiElement.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/GuiElement.java index cbecc4ba5..36a7ea28d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/GuiElement.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/gui/GuiElement.java @@ -129,7 +129,9 @@ public interface GuiElement { @Override public Item item() { - return gui().itemAt(this.index).item(); + ItemWithAction item = gui().itemAt(this.index); + if (item == null) return null; + return item.item(); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index a28c42fe9..46c6061b6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -15,11 +15,17 @@ import net.momirealms.craftengine.core.pack.conflict.PathContext; import net.momirealms.craftengine.core.pack.conflict.resolution.ResolutionFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.model.ItemModelFactory; +import net.momirealms.craftengine.core.pack.model.ItemModelReader; import net.momirealms.craftengine.core.pack.model.condition.ConditionPropertyFactory; +import net.momirealms.craftengine.core.pack.model.condition.ConditionPropertyReader; import net.momirealms.craftengine.core.pack.model.rangedisptach.RangeDispatchPropertyFactory; +import net.momirealms.craftengine.core.pack.model.rangedisptach.RangeDispatchPropertyReader; import net.momirealms.craftengine.core.pack.model.select.SelectPropertyFactory; +import net.momirealms.craftengine.core.pack.model.select.SelectPropertyReader; import net.momirealms.craftengine.core.pack.model.special.SpecialModelFactory; +import net.momirealms.craftengine.core.pack.model.special.SpecialModelReader; import net.momirealms.craftengine.core.pack.model.tint.TintFactory; +import net.momirealms.craftengine.core.pack.model.tint.TintReader; import net.momirealms.craftengine.core.plugin.config.template.TemplateArgumentFactory; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory; @@ -41,11 +47,17 @@ public class BuiltInRegistries { public static final Registry NUMBER_PROVIDER_FACTORY = createRegistry(Registries.NUMBER_PROVIDER_FACTORY); public static final Registry TEMPLATE_ARGUMENT_FACTORY = createRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY); public static final Registry ITEM_MODEL_FACTORY = createRegistry(Registries.ITEM_MODEL_FACTORY); + public static final Registry ITEM_MODEL_READER = createRegistry(Registries.ITEM_MODEL_READER); public static final Registry TINT_FACTORY = createRegistry(Registries.TINT_FACTORY); + public static final Registry TINT_READER = createRegistry(Registries.TINT_READER); public static final Registry SPECIAL_MODEL_FACTORY = createRegistry(Registries.SPECIAL_MODEL_FACTORY); + public static final Registry SPECIAL_MODEL_READER = createRegistry(Registries.SPECIAL_MODEL_READER); public static final Registry RANGE_DISPATCH_PROPERTY_FACTORY = createRegistry(Registries.RANGE_DISPATCH_PROPERTY_FACTORY); + public static final Registry RANGE_DISPATCH_PROPERTY_READER = createRegistry(Registries.RANGE_DISPATCH_PROPERTY_READER); public static final Registry CONDITION_PROPERTY_FACTORY = createRegistry(Registries.CONDITION_PROPERTY_FACTORY); + public static final Registry CONDITION_PROPERTY_READER = createRegistry(Registries.CONDITION_PROPERTY_READER); public static final Registry SELECT_PROPERTY_FACTORY = createRegistry(Registries.SELECT_PROPERTY_FACTORY); + public static final Registry SELECT_PROPERTY_READER = createRegistry(Registries.SELECT_PROPERTY_READER); public static final Registry> RECIPE_FACTORY = createRegistry(Registries.RECIPE_FACTORY); public static final Registry FORMULA_FACTORY = createRegistry(Registries.FORMULA_FACTORY); public static final Registry> PATH_MATCHER_FACTORY = createRegistry(Registries.PATH_MATCHER_FACTORY); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index 5d8ed2868..c050b9642 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -15,11 +15,17 @@ import net.momirealms.craftengine.core.pack.conflict.PathContext; import net.momirealms.craftengine.core.pack.conflict.resolution.ResolutionFactory; import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory; import net.momirealms.craftengine.core.pack.model.ItemModelFactory; +import net.momirealms.craftengine.core.pack.model.ItemModelReader; import net.momirealms.craftengine.core.pack.model.condition.ConditionPropertyFactory; +import net.momirealms.craftengine.core.pack.model.condition.ConditionPropertyReader; import net.momirealms.craftengine.core.pack.model.rangedisptach.RangeDispatchPropertyFactory; +import net.momirealms.craftengine.core.pack.model.rangedisptach.RangeDispatchPropertyReader; import net.momirealms.craftengine.core.pack.model.select.SelectPropertyFactory; +import net.momirealms.craftengine.core.pack.model.select.SelectPropertyReader; import net.momirealms.craftengine.core.pack.model.special.SpecialModelFactory; +import net.momirealms.craftengine.core.pack.model.special.SpecialModelReader; import net.momirealms.craftengine.core.pack.model.tint.TintFactory; +import net.momirealms.craftengine.core.pack.model.tint.TintReader; import net.momirealms.craftengine.core.plugin.config.template.TemplateArgumentFactory; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory; @@ -42,11 +48,17 @@ public class Registries { public static final ResourceKey> NUMBER_PROVIDER_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("number_provider_factory")); public static final ResourceKey> TEMPLATE_ARGUMENT_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("template_argument_factory")); public static final ResourceKey> ITEM_MODEL_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("item_model_factory")); + public static final ResourceKey> ITEM_MODEL_READER = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("item_model_reader")); public static final ResourceKey> TINT_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("tint_factory")); + public static final ResourceKey> TINT_READER = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("tint_reader")); public static final ResourceKey> SPECIAL_MODEL_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("special_model_factory")); + public static final ResourceKey> SPECIAL_MODEL_READER = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("special_model_reader")); public static final ResourceKey> RANGE_DISPATCH_PROPERTY_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("range_dispatch_property_factory")); + public static final ResourceKey> RANGE_DISPATCH_PROPERTY_READER = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("range_dispatch_property_reader")); public static final ResourceKey> CONDITION_PROPERTY_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("condition_property_factory")); + public static final ResourceKey> CONDITION_PROPERTY_READER = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("condition_property_reader")); public static final ResourceKey> SELECT_PROPERTY_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("select_property_factory")); + public static final ResourceKey> SELECT_PROPERTY_READER = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("select_property_reader")); public static final ResourceKey>> RECIPE_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_factory")); public static final ResourceKey> FORMULA_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("formula_factory")); public static final ResourceKey>> PATH_MATCHER_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("path_matcher_factory")); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java b/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java new file mode 100644 index 000000000..5aae1211c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersion.java @@ -0,0 +1,83 @@ +package net.momirealms.craftengine.core.util; + +import java.util.HashMap; +import java.util.Map; + +public final class MinecraftVersion implements Comparable { + public static final Map PACK_FORMATS = new HashMap<>(); + static { + PACK_FORMATS.put(1_20_00, 15); + PACK_FORMATS.put(1_20_01, 15); + PACK_FORMATS.put(1_20_02, 18); + PACK_FORMATS.put(1_20_03, 22); + PACK_FORMATS.put(1_20_04, 22); + PACK_FORMATS.put(1_20_05, 32); + PACK_FORMATS.put(1_20_06, 32); + PACK_FORMATS.put(1_21_00, 34); + PACK_FORMATS.put(1_21_01, 34); + PACK_FORMATS.put(1_21_02, 42); + PACK_FORMATS.put(1_21_03, 42); + PACK_FORMATS.put(1_21_04, 46); + PACK_FORMATS.put(1_21_05, 55); + PACK_FORMATS.put(1_21_06, 63); + PACK_FORMATS.put(1_99_99, 1000); + } + + private final int version; + private final String versionString; + private final int packFormat; + + public static MinecraftVersion parse(final String version) { + return new MinecraftVersion(version); + } + + public String version() { + return versionString; + } + + public int packFormat() { + return packFormat; + } + + public MinecraftVersion(String version) { + this.version = VersionHelper.parseVersionToInteger(version); + this.versionString = version; + this.packFormat = PACK_FORMATS.get(this.version); + } + + public boolean isAtOrAbove(MinecraftVersion other) { + return version >= other.version; + } + + public boolean isAtOrBelow(MinecraftVersion other) { + return version <= other.version; + } + + public boolean isAt(MinecraftVersion other) { + return version == other.version; + } + + public boolean isBelow(MinecraftVersion other) { + return version < other.version; + } + + public boolean isAbove(MinecraftVersion other) { + return version > other.version; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof MinecraftVersion that)) return false; + return version == that.version; + } + + @Override + public int hashCode() { + return version; + } + + @Override + public int compareTo(MinecraftVersion other) { + return Integer.compare(this.version, other.version); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java b/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java new file mode 100644 index 000000000..dd66400dc --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MinecraftVersions.java @@ -0,0 +1,21 @@ +package net.momirealms.craftengine.core.util; + +public final class MinecraftVersions { + private MinecraftVersions() {} + + public static final MinecraftVersion V1_20 = new MinecraftVersion("1.20"); + public static final MinecraftVersion V1_20_1 = new MinecraftVersion("1.20.1"); + public static final MinecraftVersion V1_20_2 = new MinecraftVersion("1.20.2"); + public static final MinecraftVersion V1_20_3 = new MinecraftVersion("1.20.3"); + public static final MinecraftVersion V1_20_4 = new MinecraftVersion("1.20.4"); + public static final MinecraftVersion V1_20_5 = new MinecraftVersion("1.20.5"); + public static final MinecraftVersion V1_20_6 = new MinecraftVersion("1.20.6"); + public static final MinecraftVersion V1_21 = new MinecraftVersion("1.21"); + public static final MinecraftVersion V1_21_1 = new MinecraftVersion("1.21.1"); + public static final MinecraftVersion V1_21_2 = new MinecraftVersion("1.21.2"); + public static final MinecraftVersion V1_21_3 = new MinecraftVersion("1.21.3"); + public static final MinecraftVersion V1_21_4 = new MinecraftVersion("1.21.4"); + public static final MinecraftVersion V1_21_5 = new MinecraftVersion("1.21.5"); + public static final MinecraftVersion V1_21_6 = new MinecraftVersion("1.21.6"); + public static final MinecraftVersion FUTURE = new MinecraftVersion("1.99.99"); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java b/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java index f8558822e..4fceb5606 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/VersionHelper.java @@ -7,6 +7,7 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; public class VersionHelper { + public static final MinecraftVersion MINECRAFT_VERSION; private static final int version; private static final int majorVersion; private static final int minorVersion; @@ -36,6 +37,9 @@ public class VersionHelper { } JsonObject json = GsonHelper.parseJsonToJsonObject(new String(inputStream.readAllBytes(), StandardCharsets.UTF_8)); String versionString = json.getAsJsonPrimitive("id").getAsString(); + + MINECRAFT_VERSION = new MinecraftVersion(versionString); + String[] split = versionString.split("\\."); int major = Integer.parseInt(split[1]); int minor = split.length == 3 ? Integer.parseInt(split[2].split("-", 2)[0]) : 0; diff --git a/gradle.properties b/gradle.properties index 7f01a5cd2..eb5bb7438 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,8 +3,8 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] project_version=0.0.58 -config_version=38 -lang_version=19 +config_version=39 +lang_version=20 project_group=net.momirealms latest_supported_version=1.21.6 From 4a8488f535903c2b358127bae830bd5bfab3eea6 Mon Sep 17 00:00:00 2001 From: TamashiiMon <100000541+TamashiiMon@users.noreply.github.com> Date: Tue, 24 Jun 2025 18:45:54 +0200 Subject: [PATCH 07/14] Update de.yml --- .../src/main/resources/translations/de.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/common-files/src/main/resources/translations/de.yml b/common-files/src/main/resources/translations/de.yml index 731d82203..2af081c0e 100644 --- a/common-files/src/main/resources/translations/de.yml +++ b/common-files/src/main/resources/translations/de.yml @@ -3,7 +3,7 @@ lang-version: "${lang_version}" exception.invalid_syntax: "Ungültige Syntax. Korrekte Syntax: " exception.invalid_argument: "Ungültiges Argument. Grund: " -exception.invalid_sender: " ist nicht berechtigt, diesen Befehl auszuführen. Muss vom Typ sein" +exception.invalid_sender: " ist nicht berechtigt, diesen Befehl auszuführen. Muss vom Typ " exception.unexpected: "Beim Versuch, diesen Befehl auszuführen, ist ein interner Fehler aufgetreten" exception.no_permission: "Es tut mir leid, aber Sie haben keine Berechtigung, diesen Befehl auszuführen" exception.no_such_command: "Unbekannter Befehl." @@ -70,6 +70,8 @@ warning.config.type.float: "Problem in Datei gefunden - Laden vo warning.config.type.double: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Double-Typ für Option '' umwandeln." warning.config.type.quaternionf: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Quaternionf-Typ für Option '' umwandeln." warning.config.type.vector3f: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Vector3f-Typ für Option '' umwandeln." +warning.config.type.boolean: "Problem in Datei gefunden - Laden von '' fehlgeschlagen: Kann '' nicht in den Booleschen Typ für Option '' umwandeln." +warning.config.type.snbt.invalid_syntax: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen SNBT-Syntax ''." warning.config.number.missing_type: "Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'type'-Argument für das Zahlenargument." warning.config.number.invalid_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen Zahlargumenttyp ''." warning.config.number.missing_argument: "Problem in Datei gefunden - Der Konfiguration '' fehlt das Argument für 'number'." @@ -154,6 +156,7 @@ warning.config.furniture.hitbox.custom.invalid_entity: "Problem in Datei warning.config.item.duplicate: "Problem in Datei gefunden - Duplizierter Gegenstand ''. Bitte überprüfen Sie, ob dieselbe Konfiguration in anderen Dateien vorhanden ist." warning.config.item.settings.unknown: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen unbekannten Einstellungstyp ''." warning.config.item.settings.invulnerable.invalid_damage_source: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine unbekannte Schadensquelle ''. Erlaubte Quellen: []." +warning.config.item.settings.equippable.missing_slot: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'slot'-Argument für die 'equippable'-Einstellung." warning.config.item.missing_material: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'material'-Argument." warning.config.item.invalid_material: "Problem in Datei gefunden - Der Gegenstand '' verwendet einen ungültigen Materialtyp ''." warning.config.item.invalid_custom_model_data: "Problem in Datei gefunden - Der Gegenstand '' verwendet negative benutzerdefinierte Modelldaten '', die ungültig sind." @@ -184,7 +187,9 @@ warning.config.item.model.condition.missing_property: "Problem in Datei warning.config.item.model.condition.invalid_property: "Problem in Datei gefunden - Der Gegenstand '' verwendet eine ungültige Eigenschaft '' für das Modell 'minecraft:condition'." warning.config.item.model.condition.missing_on_true: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'on-true'-Argument für das Modell 'minecraft:condition'." warning.config.item.model.condition.missing_on_false: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'on-false'-Argument für das Modell 'minecraft:condition'." -warning.config.item.model.condition.keybind.missing: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'keybind'-Argument für die Eigenschaft 'minecraft:keybind_down'." +warning.config.item.model.condition.keybind.missing_keybind: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'keybind'-Argument für die Eigenschaft 'minecraft:keybind_down'." +warning.config.item.model.condition.component.missing_predicate: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'predicate'-Argument für die Eigenschaft 'minecraft:has_component'." +warning.config.item.model.condition.component.missing_value: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'value'-Argument für die Eigenschaft 'minecraft:has_component'." warning.config.item.model.condition.has_component.missing_component: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:has_component'." warning.config.item.model.composite.missing_models: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'models'-Argument für das Modell 'minecraft:composite'." warning.config.item.model.range_dispatch.missing_property: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'property'-Argument für das Modell 'minecraft:range_dispatch'." @@ -198,6 +203,7 @@ warning.config.item.model.select.invalid_property: "Problem in Datei Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'cases'-Argument für das Modell 'minecraft:select'." warning.config.item.model.select.case.missing_when: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'when'-Argument für einen der Fälle im Modell 'minecraft:select'." warning.config.item.model.select.case.missing_model: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'model'-Argument für einen der Fälle im Modell 'minecraft:select'." +warning.config.item.model.select.component.missing_component: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'component'-Argument für die Eigenschaft 'minecraft:component'." warning.config.item.model.select.block_state.missing_property: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'block-state-property'-Argument für die Eigenschaft 'minecraft:block_state'." warning.config.item.model.select.local_time.missing_pattern: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'pattern'-Argument für die Eigenschaft 'minecraft:local_time'." warning.config.item.model.special.missing_type: "Problem in Datei gefunden - Dem Gegenstand '' fehlt das erforderliche 'type'-Argument für das Modell 'minecraft:special'." @@ -371,8 +377,8 @@ warning.config.function.remove_cooldown.missing_id: "Problem in Datei Problem in Datei gefunden - Der Konfiguration '' fehlt das erforderliche 'type'-Argument für den Selektor." warning.config.selector.invalid_type: "Problem in Datei gefunden - Die Konfiguration '' verwendet einen ungültigen Selektortyp ''." warning.config.selector.invalid_target: "Problem in Datei gefunden - Die Konfiguration '' verwendet ein ungültiges Selektorziel ''." -warning.config.resource_pack.item_model.conflict.vanilla: "Fehler beim Generieren des Gegenstandsmodells für '', da dieses Gegenstandsmodell von einem Vanilla-Gegenstand belegt wurde." -warning.config.resource_pack.item_model.already_exist: "Fehler beim Generieren des Gegenstandsmodells für '', da die Datei '' bereits existiert." +warning.config.resource_pack.item_model.conflict.vanilla: "Fehler beim Generieren des Gegenstandsmodells für '' da dieses Gegenstandsmodell von einem Vanilla-Gegenstand belegt wurde." +warning.config.resource_pack.item_model.already_exist: "Fehler beim Generieren des Gegenstandsmodells für '' da die Datei '' bereits existiert." warning.config.resource_pack.model.generation.already_exist: "Fehler beim Generieren des Modells, da die Modelldatei '' bereits existiert." warning.config.resource_pack.generation.missing_font_texture: "Schriftart '' fehlt die erforderliche Textur: ''" warning.config.resource_pack.generation.missing_model_texture: "Modell '' fehlt Textur ''" @@ -380,3 +386,4 @@ warning.config.resource_pack.generation.missing_item_model: "Gegenstand warning.config.resource_pack.generation.missing_block_model: "Block '' fehlt Modelldatei: ''" warning.config.resource_pack.generation.missing_parent_model: "Modell '' kann das Elternmodell nicht finden: ''" warning.config.resource_pack.generation.malformatted_json: "Json-Datei '' ist fehlerhaft formatiert." +warning.config.resource_pack.invalid_overlay_format: "Problem in config.yml gefunden - Ungültiges Overlay-Format '' im Abschnitt 'resource-pack'. Unterstützte Overlays: []" From 4979d9473b7961fce360c22573ff9bb60617ab7d Mon Sep 17 00:00:00 2001 From: Catnies Date: Wed, 25 Jun 2025 01:32:59 +0800 Subject: [PATCH 08/14] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader,=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=97=A0=E5=BC=95=E5=8F=B7=E5=8C=85=E8=A3=B9?= =?UTF-8?q?=E4=B8=94=E5=B8=A6=E7=A9=BA=E6=A0=BC=E7=9A=84=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/momirealms/craftengine/core/util/SNBTReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 17c0086b7..d6a333645 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 @@ -172,7 +172,7 @@ public final class SNBTReader extends DefaultStringReader { int tokenStart = cursor; while (cursor < length) { char c = peek(); - if (c <= ' ' || c == ',' || c == ']' || c == '}') break; + if (c == ',' || c == ']' || c == '}') break; cursor++; } int tokenLength = cursor - tokenStart; From 4e9903b63a3c24ccce03f367f9a6d19a7a894265 Mon Sep 17 00:00:00 2001 From: Catnies Date: Wed, 25 Jun 2025 01:48:15 +0800 Subject: [PATCH 09/14] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader,=20?= =?UTF-8?q?=E5=8E=BB=E9=99=A4=E8=A7=A3=E6=9E=90=E7=9A=84=E6=97=A0=E5=BC=95?= =?UTF-8?q?=E5=8F=B7=E5=AD=97=E7=AC=A6=E4=B8=B2=E6=9C=AB=E5=B0=BE=E7=9A=84?= =?UTF-8?q?=E7=A9=BA=E6=A0=BC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/momirealms/craftengine/core/util/SNBTReader.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 d6a333645..c4904715b 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 @@ -170,12 +170,18 @@ public final class SNBTReader extends DefaultStringReader { private Object parsePrimitive() { // 先解析获取值的长度 int tokenStart = cursor; + int lastWhitespace = 0; // 记录值末尾的空格数量,{a:炒鸡 大保健} 和 {a: 炒鸡 大保健 } 都应解析成 "炒鸡 大保健". while (cursor < length) { char c = peek(); if (c == ',' || c == ']' || c == '}') break; cursor++; + if (c == ' ') { + lastWhitespace++; // 遇到空格先增加值, 代表值尾部空格数量. + continue; + } + lastWhitespace = 0; // 遇到正常字符时清空记录的尾部空格数. } - int tokenLength = cursor - tokenStart; + int tokenLength = cursor - tokenStart - lastWhitespace; // 计算值长度需要再减去尾部空格. if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); String fullContent = string.substring(tokenStart, tokenStart + tokenLength); From 8c61451201bdc977e6601e08a8641514c5d0cd26 Mon Sep 17 00:00:00 2001 From: Catnies Date: Wed, 25 Jun 2025 02:01:12 +0800 Subject: [PATCH 10/14] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader#parsePrim?= =?UTF-8?q?itive,=20=E9=80=82=E5=BD=93=E6=97=B6=E6=9C=BA=E5=8F=AF=E6=8F=90?= =?UTF-8?q?=E5=89=8D=E8=BF=94=E5=9B=9E.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/momirealms/craftengine/core/util/SNBTReader.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 c4904715b..e90ff7d67 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 @@ -171,6 +171,7 @@ public final class SNBTReader extends DefaultStringReader { // 先解析获取值的长度 int tokenStart = cursor; int lastWhitespace = 0; // 记录值末尾的空格数量,{a:炒鸡 大保健} 和 {a: 炒鸡 大保健 } 都应解析成 "炒鸡 大保健". + boolean contentHasWhitespace = false; // 记录值中有没有空格. while (cursor < length) { char c = peek(); if (c == ',' || c == ']' || c == '}') break; @@ -179,11 +180,15 @@ public final class SNBTReader extends DefaultStringReader { lastWhitespace++; // 遇到空格先增加值, 代表值尾部空格数量. continue; } - lastWhitespace = 0; // 遇到正常字符时清空记录的尾部空格数. + if (lastWhitespace > 0) { + lastWhitespace = 0; // 遇到正常字符时清空记录的尾部空格数. + contentHasWhitespace = true; + } } int tokenLength = cursor - tokenStart - lastWhitespace; // 计算值长度需要再减去尾部空格. if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); String fullContent = string.substring(tokenStart, tokenStart + tokenLength); + if (contentHasWhitespace) return fullContent; // 如果值的中间有空格, 一定是字符串, 可直接返回. // 布尔值检查 if ("1B".equals(fullContent) || (tokenLength == 4 && matchesAt(tokenStart, "true"))) return Boolean.TRUE; From ad6e2a6ffff21148a1e44f023147d9e5ac4b5e04 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 25 Jun 2025 02:25:25 +0800 Subject: [PATCH 11/14] 0.0.58.1 --- .../core/plugin/config/template/TemplateManager.java | 4 +++- gradle.properties | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java index 2f6fb429a..26fbbf310 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.plugin.Manageable; import net.momirealms.craftengine.core.plugin.config.ConfigParser; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.SNBTReader; import java.util.ArrayList; import java.util.List; @@ -76,7 +77,8 @@ public interface TemplateManager extends Manageable { this.placeholder = placeholderContent.substring(0, separatorIndex); String defaultValueString = placeholderContent.substring(separatorIndex + 2); try { - this.defaultValue = CraftEngine.instance().platform().snbtToJava(defaultValueString); + // TODO 改进报错检测 + this.defaultValue = SNBTReader.deserializeAsJava(defaultValueString); } catch (LocalizedResourceConfigException e) { e.appendTailArgument(this.placeholder); throw e; diff --git a/gradle.properties b/gradle.properties index eb5bb7438..276665e20 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.58 +project_version=0.0.58.1 config_version=39 lang_version=20 project_group=net.momirealms From 737766c01c5ba652550e42e1b5c8b2a029fa7bfa Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 25 Jun 2025 02:26:36 +0800 Subject: [PATCH 12/14] Update SNBTReader.java --- .../momirealms/craftengine/core/util/SNBTReader.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 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 e90ff7d67..10963176f 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 @@ -194,15 +194,15 @@ public final class SNBTReader extends DefaultStringReader { if ("1B".equals(fullContent) || (tokenLength == 4 && matchesAt(tokenStart, "true"))) return Boolean.TRUE; if ("0B".equals(fullContent) || (tokenLength == 5 && matchesAt(tokenStart, "false"))) return Boolean.FALSE; - // 无后缀数字处理 - if (isNumber(tokenStart, tokenStart + tokenLength) == 1) return Integer.parseInt(fullContent); - if (isNumber(tokenStart, tokenStart + tokenLength) == 2) return Double.parseDouble(fullContent); + int numberType = isNumber(tokenStart, tokenStart + tokenLength); // 返回0代表不是合法数字, 1代表整数, 2代表小数. + if (numberType == 1) return Integer.parseInt(fullContent); + if (numberType == 2) return Double.parseDouble(fullContent); // 带后缀的值处理 char lastChar = string.charAt(tokenStart + tokenLength - 1); if (tokenLength > 1 && // 要求: 长度>1 - isTypeSuffix(lastChar) && // 要求: 有效后缀 - isNumber(tokenStart, tokenStart + tokenLength - 1) > 0 // 要求: 除后缀外是合法数字 + isTypeSuffix(lastChar) && // 要求: 有效后缀 + numberType > 0 // 要求: 除后缀外是合法数字 ) { final String content = string.substring(tokenStart, tokenStart + tokenLength - 1); try { From cd823a7bce05c91dee9abb2496e626d3594c1704 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 25 Jun 2025 04:08:04 +0800 Subject: [PATCH 13/14] =?UTF-8?q?=E6=94=B9=E5=AE=8C=E4=BA=86=E5=96=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/template/TemplateManager.java | 2 +- .../core/util/DefaultStringReader.java | 10 + .../craftengine/core/util/SNBTReader.java | 260 +++++++----------- .../craftengine/core/util/StringReader.java | 4 + 4 files changed, 112 insertions(+), 164 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java index 26fbbf310..a69d39c67 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManager.java @@ -78,7 +78,7 @@ public interface TemplateManager extends Manageable { String defaultValueString = placeholderContent.substring(separatorIndex + 2); try { // TODO 改进报错检测 - this.defaultValue = SNBTReader.deserializeAsJava(defaultValueString); + this.defaultValue = new SNBTReader(defaultValueString).deserializeAsJava(); } catch (LocalizedResourceConfigException e) { e.appendTailArgument(this.placeholder); throw e; diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/DefaultStringReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/DefaultStringReader.java index 18a83e636..26099643f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/DefaultStringReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/DefaultStringReader.java @@ -112,6 +112,16 @@ public class DefaultStringReader implements StringReader { this.cursor += count; } + @Override + public char charAt(int index) { + return this.string.charAt(index); + } + + @Override + public String substring(int start, int end) { + return this.string.substring(start, end); + } + public static boolean isAllowedNumber(char c) { return c >= '0' && c <= '9' || c == '.' || c == '-'; } 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 10963176f..c42e99e8b 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 @@ -15,7 +15,6 @@ public final class SNBTReader extends DefaultStringReader { private static final char DOUBLE_QUOTES = '"'; private static final char KEY_VALUE_SEPARATOR = ':'; private static final char ELEMENT_SEPARATOR = ','; - private static final char ESCAPE_CHAR = '\\'; // 数字类型后缀 private static final char BYTE_SUFFIX = 'b'; @@ -24,21 +23,15 @@ public final class SNBTReader extends DefaultStringReader { private static final char FLOAT_SUFFIX = 'f'; private static final char DOUBLE_SUFFIX = 'd'; - private final int length; - private SNBTReader(String content) { + public SNBTReader(String content) { super(content); - this.length = this.string.length(); } - // 入口API - public static Object deserializeAsJava(String input) throws IllegalArgumentException { - SNBTReader parser = new SNBTReader(input); - Object result = parser.parseValue(); - parser.skipWhitespace(); - - if (parser.cursor != parser.length) - throw new IllegalArgumentException("Extra content at end: " + parser.string.substring(parser.cursor, parser.length - parser.cursor)); - + public Object deserializeAsJava() { + Object result = this.parseValue(); + this.skipWhitespace(); + if (getCursor() != getTotalLength()) + throw new IllegalArgumentException("Extra content at end: " + substring(getCursor(), getTotalLength())); return result; } @@ -48,120 +41,90 @@ public final class SNBTReader extends DefaultStringReader { return switch (peek()) { case COMPOUND_START -> parseCompound(); case LIST_START -> parseList(); - case DOUBLE_QUOTES -> parseString(DOUBLE_QUOTES); - case SINGLE_QUOTES -> parseString(SINGLE_QUOTES); + case DOUBLE_QUOTES -> { + skip(); + yield readStringUntil(DOUBLE_QUOTES); + } + case SINGLE_QUOTES -> { + skip(); + yield readStringUntil(SINGLE_QUOTES); + } default -> parsePrimitive(); }; } // 解析包小肠 {} private Map parseCompound() { - cursor++; // 跳过 '{' + skip(); // 跳过 '{' skipWhitespace(); Map compoundMap = new LinkedHashMap<>(); - if (cursor < length && peek() != COMPOUND_END) { + if (canRead() && peek() != COMPOUND_END) { do { String key = parseKey(); - if (cursor >= length || peek() != KEY_VALUE_SEPARATOR) { - throw new IllegalArgumentException("Expected ':' at position " + cursor); + if (!canRead() || peek() != KEY_VALUE_SEPARATOR) { + throw new IllegalArgumentException("Expected ':' at position " + getCursor()); } - cursor++; // 跳过 ':' + skip(); // 跳过 ':' Object value = parseValue(); compoundMap.put(key, value); skipWhitespace(); - } while (cursor < length && peek() == ELEMENT_SEPARATOR && ++cursor > 0); + } while (canRead() && peek() == ELEMENT_SEPARATOR && ++super.cursor > 0 /* 跳过 ',' */); } - if (cursor >= length || peek() != COMPOUND_END) { - throw new IllegalArgumentException("Expected '}' at position " + cursor); + if (!canRead() || peek() != COMPOUND_END) { + throw new IllegalArgumentException("Expected '}' at position " + getCursor()); } - cursor++; // 跳过 '}' + skip(); // 跳过 '}' return compoundMap; } // 解析列表值 [1, 2, 3] private List parseList() { - cursor++; // 跳过 '[' + skip(); // 跳过 '[' skipWhitespace(); List elementList = new ArrayList<>(); - if (cursor < length && peek() != LIST_END) { + if (canRead() && peek() != LIST_END) { do { elementList.add(parseValue()); skipWhitespace(); - } while (cursor < length && peek() == ELEMENT_SEPARATOR && ++cursor > 0); + } while (canRead() && peek() == ELEMENT_SEPARATOR && ++super.cursor > 0 /* 跳过 ',' */); } - if (cursor >= length || peek() != LIST_END) { - throw new IllegalArgumentException("Expected ']' at position " + cursor); + if (!canRead() || peek() != LIST_END) { + throw new IllegalArgumentException("Expected ']' at position " + getCursor()); } - cursor++; // 跳过 ']' + skip(); // 跳过 ']' return elementList; } - // 解析字符串 - private String parseString(char delimiter) { - cursor++; // 跳过开始的引号 - int start = cursor; - - // 扫一次字符串, 如果没有发现转义就直接返回, 发现了就再走转义解析. - // 这样可以避免创建一次 StringBuilder. - while (cursor < length) { - char c = peek(); - if (c == delimiter) { - String result = string.substring(start, cursor); - cursor++; // 跳过结束引号 - return result; // 没有转义直接返回字符串. - } - // 如果发现转义字符,进行转义处理. - else if (c == ESCAPE_CHAR) { - return parseStringWithEscape(start, delimiter); - } - cursor++; - } - // 没有扫描到结束引号 - throw new IllegalArgumentException("Unterminated string at " + start); - } - - // 处理含转义的字符串 - private String parseStringWithEscape(int start, char delimiter) { - // 先把之前遍历没有包含转义的字符保存 - StringBuilder sb = new StringBuilder(cursor - start + 16); - sb.append(string, start, cursor); - - while (cursor < length) { - char c = read(); - if (c == ESCAPE_CHAR && cursor < length) { - sb.append(getEscapedChar(read())); // 解析转义. - } else if (c == delimiter) { // 发现结束分隔字符, 返回. - return sb.toString(); - } else { - sb.append(c); - } - } - // 没有扫描到结束引号 - throw new IllegalArgumentException("Unterminated string at " + start); - } - // 解析Key值 private String parseKey() { skipWhitespace(); - // 如果有双引号就委托给string解析处理. - if (cursor < length) { - if (peek() == STRING_DELIMITER) return parseString(STRING_DELIMITER); - if (peek() == SINGLE_QUOTES) return parseString(SINGLE_QUOTES); + if (!canRead()) { + throw new IllegalArgumentException("Unterminated key at " + getCursor()); } - int start = cursor; - while (cursor < length) { + // 如果有双引号就委托给string解析处理. + char peek = peek(); + if (peek == STRING_DELIMITER) { + skip(); + return readStringUntil(STRING_DELIMITER); + } else if (peek == SINGLE_QUOTES) { + skip(); + return readStringUntil(SINGLE_QUOTES); + } + + int start = getCursor(); + while (canRead()) { char c = peek(); if (c == ' ') break; // 忽略 key 后面的空格, { a :1} 应当解析成 {a:1} - if (Character.isJavaIdentifierPart(c)) cursor++; else break; + if (Character.isJavaIdentifierPart(c)) skip(); else break; } - String key = string.substring(start, cursor); + String key = substring(start, getCursor()); skipWhitespace(); // 跳过 key 后面的空格. return key; } @@ -169,13 +132,13 @@ public final class SNBTReader extends DefaultStringReader { // 解析原生值 private Object parsePrimitive() { // 先解析获取值的长度 - int tokenStart = cursor; + int tokenStart = getCursor(); int lastWhitespace = 0; // 记录值末尾的空格数量,{a:炒鸡 大保健} 和 {a: 炒鸡 大保健 } 都应解析成 "炒鸡 大保健". boolean contentHasWhitespace = false; // 记录值中有没有空格. - while (cursor < length) { + while (canRead()) { char c = peek(); if (c == ',' || c == ']' || c == '}') break; - cursor++; + skip(); if (c == ' ') { lastWhitespace++; // 遇到空格先增加值, 代表值尾部空格数量. continue; @@ -185,99 +148,70 @@ public final class SNBTReader extends DefaultStringReader { contentHasWhitespace = true; } } - int tokenLength = cursor - tokenStart - lastWhitespace; // 计算值长度需要再减去尾部空格. + int tokenLength = getCursor() - tokenStart - lastWhitespace; // 计算值长度需要再减去尾部空格. if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); - String fullContent = string.substring(tokenStart, tokenStart + tokenLength); - if (contentHasWhitespace) return fullContent; // 如果值的中间有空格, 一定是字符串, 可直接返回. + if (contentHasWhitespace) return substring(tokenStart, tokenStart + tokenLength); // 如果值的中间有空格, 一定是字符串, 可直接返回. // 布尔值检查 - if ("1B".equals(fullContent) || (tokenLength == 4 && matchesAt(tokenStart, "true"))) return Boolean.TRUE; - if ("0B".equals(fullContent) || (tokenLength == 5 && matchesAt(tokenStart, "false"))) return Boolean.FALSE; - - int numberType = isNumber(tokenStart, tokenStart + tokenLength); // 返回0代表不是合法数字, 1代表整数, 2代表小数. - if (numberType == 1) return Integer.parseInt(fullContent); - if (numberType == 2) return Double.parseDouble(fullContent); - - // 带后缀的值处理 - char lastChar = string.charAt(tokenStart + tokenLength - 1); - if (tokenLength > 1 && // 要求: 长度>1 - isTypeSuffix(lastChar) && // 要求: 有效后缀 - numberType > 0 // 要求: 除后缀外是合法数字 - ) { - final String content = string.substring(tokenStart, tokenStart + tokenLength - 1); + if (tokenLength == 4) { + if (matchesAt(tokenStart, "true")) return Boolean.TRUE; + } else if (tokenLength == 5) { + if (matchesAt(tokenStart, "false")) return Boolean.FALSE; + } + if (tokenLength > 1) { + // 至少有1个字符,给了后缀的可能性 + char lastChar = charAt(tokenStart + tokenLength - 1); try { - return switch (lastChar) { - case BYTE_SUFFIX -> Byte.parseByte(content); - case SHORT_SUFFIX -> Short.parseShort(content); - case LONG_SUFFIX -> Long.parseLong(content); - case FLOAT_SUFFIX -> Float.parseFloat(content); - case DOUBLE_SUFFIX -> Double.parseDouble(content); - default -> new IllegalArgumentException("Parse Error with: " + content); // 永远不应进入此 case. - }; + switch (lastChar) { + case BYTE_SUFFIX -> { + return Byte.parseByte(substring(tokenStart, tokenStart + tokenLength - 1)); + } + case SHORT_SUFFIX -> { + return Short.parseShort(substring(tokenStart, tokenStart + tokenLength - 1)); + } + case LONG_SUFFIX -> { + return Long.parseLong(substring(tokenStart, tokenStart + tokenLength - 1)); + } + case FLOAT_SUFFIX -> { + return Float.parseFloat(substring(tokenStart, tokenStart + tokenLength)); + } + case DOUBLE_SUFFIX -> { + return Double.parseDouble(substring(tokenStart, tokenStart + tokenLength)); + } + default -> { + String fullString = substring(tokenStart, tokenStart + tokenLength); + try { + double d = Double.parseDouble(fullString); + if (d % 1 != 0 || fullString.contains(".") || fullString.contains("e")) { + return d; + } else { + return (int) d; + } + } catch (NumberFormatException e) { + return fullString; + } + } + } } catch (NumberFormatException e) { - return fullContent; // 如果有神人写了 128b 一类的超范围值, 就当做字符串返回好了. + return substring(tokenStart, tokenStart + tokenLength); + } + } else { + char onlyChar = charAt(tokenStart); + if (isNumber(onlyChar)) { + return onlyChar - '0'; + } else { + return String.valueOf(onlyChar); } } - - // 都无法匹配就默认为 String 喵~ - return fullContent; } // 工具函数: 快速检查布尔值字符串匹配, 忽略大小写. private boolean matchesAt(int start, String target) { for (int i = 0; i < target.length(); i++) { - char c1 = string.charAt(start + i); + char c1 = charAt(start + i); char c2 = target.charAt(i); if (c1 != c2 && c1 != (c2 ^ 32)) return false; // 忽略大小写比较 } return true; } - - // 工具函数: 合法后缀检查 - private boolean isTypeSuffix(char c) { - return c == BYTE_SUFFIX || c == SHORT_SUFFIX || c == LONG_SUFFIX || c == FLOAT_SUFFIX || c == DOUBLE_SUFFIX; - } - - // 转义字符处理 - private char getEscapedChar(char escapedChar) { - return switch (escapedChar) { - case DOUBLE_QUOTES -> '"'; - case SINGLE_QUOTES -> '\''; - case ESCAPE_CHAR -> '\\'; - case COMPOUND_START -> '{'; - case COMPOUND_END -> '}'; - case LIST_START -> '['; - case LIST_END -> ']'; - case KEY_VALUE_SEPARATOR -> ':'; - case ELEMENT_SEPARATOR -> ','; - default -> escapedChar; - }; - } - - // 检查是不是合法数字. - // 返回0代表不合法, 1代表整数, 2代表小数. - private int isNumber(int start, int end) { - // 跳过负号 - if (string.charAt(start) == '-') start++; - - // 除负号外第一个字符必须是数字. - char c1 = string.charAt(start); - if (c1 < '0' || c1 > '9') return 0; - - // 检查剩余的数字, 只能有一个小数点 - boolean hasDecimal = false; - for (; start < end; start++) { - char c = string.charAt(start); - if (c < '0' || c > '9') { - if (c == '.') { - if (hasDecimal) return 0; // bro 不能有2个小数点. - hasDecimal = true; - } else { - return 0; - } - } - } - - return hasDecimal ? 2 : 1; - } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/StringReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/StringReader.java index 2202bed2c..71dd4568a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/StringReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/StringReader.java @@ -40,6 +40,10 @@ public interface StringReader { void skip(int count); + char charAt(int index); + + String substring(int start, int end); + void skipWhitespace(); void skipWhitespaceAndComment(); From 5f42dfd4530f5ecb8ba0783023438cc11e071bed Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 25 Jun 2025 04:52:26 +0800 Subject: [PATCH 14/14] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20::=20=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E5=88=86=E9=9A=94=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/config/StringKeyConstructor.java | 40 +++++++++++++++++-- gradle.properties | 2 +- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java index cd199e3e8..bcca9168c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java @@ -16,6 +16,7 @@ import java.util.Map; public class StringKeyConstructor extends SafeConstructor { private final Path path; private static final String VERSION_PREFIX = "$$"; + private static final String DEEP_KEY_SEPARATOR = "::"; public StringKeyConstructor(Path path, LoaderOptions loaderOptions) { super(loaderOptions); @@ -60,8 +61,9 @@ public class StringKeyConstructor extends SafeConstructor { } /** - * 场景A (块合并): 构造一个Map,同时处理其中的版本化块合并。 + * 场景A (块合并与路径展开): 构造一个Map,同时处理其中的版本化块合并和 `::` 分隔的深层键。 */ + @SuppressWarnings("unchecked") @Override protected Map constructMapping(MappingNode node) { Map map = new LinkedHashMap<>(); @@ -70,19 +72,51 @@ public class StringKeyConstructor extends SafeConstructor { if (!(keyNode instanceof ScalarNode)) continue; String key = constructScalar((ScalarNode) keyNode); + Node valueNode = tuple.getValueNode(); if (key.startsWith(VERSION_PREFIX)) { + // 处理版本化块合并 String versionSpec = key.substring(VERSION_PREFIX.length()); if (isVersionMatch(versionSpec)) { - Node valueNode = tuple.getValueNode(); if (valueNode instanceof MappingNode) { + // 将版本匹配的map内容合并到当前map map.putAll(constructMapping((MappingNode) valueNode)); } else { logWarning("versioned_key_not_a_map", key, valueNode); } } + } else if (key.contains(DEEP_KEY_SEPARATOR)) { + // 处理 '::' 分隔的深层键 + String[] parts = key.split(DEEP_KEY_SEPARATOR); + Object value = constructObject(valueNode); + Map currentMap = map; + + // 遍历除最后一个部分外的所有路径,创建嵌套的map + for (int i = 0; i < parts.length - 1; i++) { + String part = parts[i]; + Object nextObject = currentMap.get(part); + if (nextObject instanceof Map) { + currentMap = (Map) nextObject; + } else { + // 如果路径中存在一个非map的值,发出警告并覆盖它 + if (nextObject != null) { + logWarning("key_path_conflict", part, keyNode); + } + Map newMap = new LinkedHashMap<>(); + currentMap.put(part, newMap); + currentMap = newMap; + } + } + + // 在最深层的map中设置最终的键值对 + String finalKey = parts[parts.length - 1]; + Object previous = currentMap.put(finalKey, value); + if (previous != null) { + // 使用完整的原始键来报告重复键,更清晰 + logWarning("duplicated_key", key, keyNode); + } } else { - Node valueNode = tuple.getValueNode(); + // 原始逻辑:处理普通键 Object value = constructObject(valueNode); Object previous = map.put(key, value); if (previous != null) { diff --git a/gradle.properties b/gradle.properties index 276665e20..41c4e936d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.58.1 +project_version=0.0.58.2 config_version=39 lang_version=20 project_group=net.momirealms