From 539f3ab67b34c3a1acb8bbf95d5421b902628520 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 5 Jun 2025 14:46:59 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=B7=B1=E5=BA=A6?= =?UTF-8?q?=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/template/TemplateManagerImpl.java | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index 82a3727cc..b01302bfe 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -65,17 +65,19 @@ public class TemplateManagerImpl implements TemplateManager { public Map applyTemplates(Key id, Map input) { Objects.requireNonNull(input, "Input must not be null"); Map result = new LinkedHashMap<>(); - processMap(input, Map.of("{__ID__}", PlainStringTemplateArgument.plain(id.value()), - "{__NAMESPACE__}", PlainStringTemplateArgument.plain(id.namespace())), (obj) -> { - // 当前位于根节点下,如果下一级就是模板,则应把模板结果与当前map合并 - // 如果模板结果不是map,则为非法值,因为不可能出现类似于下方的配置 - // items: - // test:invalid: 111 - if (obj instanceof Map mapResult) { - result.putAll(MiscUtils.castToMap(mapResult, false)); - } else { - throw new IllegalArgumentException("Invalid template used. Input: " + GsonHelper.get().toJson(input) + ". Template: " + GsonHelper.get().toJson(obj)); - } + processMap(input, + Map.of("{__ID__}", PlainStringTemplateArgument.plain(id.value()), + "{__NAMESPACE__}", PlainStringTemplateArgument.plain(id.namespace())), + (obj) -> { + // 当前位于根节点下,如果下一级就是模板,则应把模板结果与当前map合并 + // 如果模板结果不是map,则为非法值,因为不可能出现类似于下方的配置 + // items: + // test:invalid: 111 + if (obj instanceof Map mapResult) { + result.putAll(MiscUtils.castToMap(mapResult, false)); + } else { + throw new IllegalArgumentException("Invalid template used. Input: " + GsonHelper.get().toJson(input) + ". Template: " + GsonHelper.get().toJson(obj)); + } }); return result; } @@ -99,13 +101,13 @@ public class TemplateManagerImpl implements TemplateManager { return; } Object firstTemplate = processedTemplates.get(0); - // 对于map和list,应当对多模板合并 + // 如果是map,应当深度合并 if (firstTemplate instanceof Map) { Map results = new LinkedHashMap<>(); - // 仅仅合并list for (Object processedTemplate : processedTemplates) { if (processedTemplate instanceof Map anotherMap) { - results.putAll(MiscUtils.castToMap(anotherMap, false)); + Map castedMap = MiscUtils.castToMap(anotherMap, false); + deepMergeMaps(results, castedMap); } } results.putAll(processingResult.overrides()); @@ -285,4 +287,24 @@ public class TemplateManagerImpl implements TemplateManager { Map overrides, Map arguments ) {} + + @SuppressWarnings("unchecked") + private void deepMergeMaps(Map baseMap, Map mapToMerge) { + for (Map.Entry entry : mapToMerge.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (baseMap.containsKey(key)) { + Object existingValue = baseMap.get(key); + if (existingValue instanceof Map && value instanceof Map) { + Map existingMap = (Map) existingValue; + Map newMap = (Map) value; + deepMergeMaps(existingMap, newMap); + } else { + baseMap.put(key, value); + } + } else { + baseMap.put(key, value); + } + } + } } From 182b5c93d3788febad680747c9be5124eed03262 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 5 Jun 2025 14:48:09 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E6=B7=B1=E5=BA=A6list=E5=80=BC=E5=90=88?= =?UTF-8?q?=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/plugin/config/template/TemplateManagerImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index b01302bfe..2cbba976a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -299,6 +299,10 @@ public class TemplateManagerImpl implements TemplateManager { Map existingMap = (Map) existingValue; Map newMap = (Map) value; deepMergeMaps(existingMap, newMap); + } else if (existingValue instanceof List && value instanceof List) { + List existingList = (List) existingValue; + List newList = (List) value; + existingList.addAll(newList); } else { baseMap.put(key, value); } From 818b61e92e9ff22aa7a7862e4c52cf876710cd5f Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 5 Jun 2025 15:56:15 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E5=A4=9A=E9=87=8D=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configuration/fix_client_visual.yml | 1 - .../config/template/TemplateManager.java | 1 + .../config/template/TemplateManagerImpl.java | 169 +++++++++++++++--- gradle.properties | 2 +- 4 files changed, 145 insertions(+), 28 deletions(-) diff --git a/common-files/src/main/resources/resources/default/configuration/fix_client_visual.yml b/common-files/src/main/resources/resources/default/configuration/fix_client_visual.yml index 9e18aabaf..d4167b5c0 100644 --- a/common-files/src/main/resources/resources/default/configuration/fix_client_visual.yml +++ b/common-files/src/main/resources/resources/default/configuration/fix_client_visual.yml @@ -1,5 +1,4 @@ items: - # client-bound-data requires CraftEngine mod to apply minecraft:string: client-bound-data: components: 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 4cf87fed6..4313066d2 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 @@ -14,6 +14,7 @@ public interface TemplateManager extends Manageable { String TEMPLATE = "template"; String OVERRIDES = "overrides"; String ARGUMENTS = "arguments"; + String MERGES = "merges"; ConfigParser parser(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index 2cbba976a..01c594e03 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -15,9 +15,10 @@ import java.util.function.Consumer; import java.util.function.Supplier; import java.util.regex.Matcher; +@SuppressWarnings("DuplicatedCode") public class TemplateManagerImpl implements TemplateManager { private final Map templates = new HashMap<>(); - private final static Set NON_TEMPLATE_KEY = new HashSet<>(Set.of(TEMPLATE, ARGUMENTS, OVERRIDES)); + private final static Set NON_TEMPLATE_KEY = new HashSet<>(Set.of(TEMPLATE, ARGUMENTS, OVERRIDES, MERGES)); private final TemplateParser templateParser; public TemplateManagerImpl() { @@ -110,7 +111,9 @@ public class TemplateManagerImpl implements TemplateManager { deepMergeMaps(results, castedMap); } } - results.putAll(processingResult.overrides()); + if (processingResult.overrides() instanceof Map overrides) { + results.putAll(MiscUtils.castToMap(overrides, false)); + } processCallBack.accept(results); } else if (firstTemplate instanceof List) { List results = new ArrayList<>(); @@ -120,10 +123,19 @@ public class TemplateManagerImpl implements TemplateManager { results.addAll(anotherList); } } - processCallBack.accept(results); + if (processingResult.overrides() instanceof List overrides) { + processCallBack.accept(overrides); + } else { + processCallBack.accept(results); + } } else { - // 其他情况下应当忽略其他的template - processCallBack.accept(firstTemplate); + Object overrides = processingResult.overrides(); + if (overrides != null) { + processCallBack.accept(overrides); + } else { + // 其他情况下应当忽略其他的template + processCallBack.accept(firstTemplate); + } } } else { // 如果不是模板,则返回值一定是map @@ -182,29 +194,133 @@ public class TemplateManagerImpl implements TemplateManager { MiscUtils.castToMap(input.getOrDefault(ARGUMENTS, Collections.emptyMap()), false), parentArguments ); - // 对overrides参数应用 本节点 + 父节点 参数 - Map overrides = new LinkedHashMap<>(); - processMap(MiscUtils.castToMap(input.getOrDefault(OVERRIDES, Map.of()), false), arguments, (obj) -> { - // 如果overrides的下一级就是一个模板,则模板必须为map类型 - if (obj instanceof Map mapResult) { - overrides.putAll(MiscUtils.castToMap(mapResult, false)); + + Object override = input.get(OVERRIDES); + if (override instanceof Map rawOverrides) { + // 对overrides参数应用 本节点 + 父节点 参数 + Map overrides = new LinkedHashMap<>(); + processMap(MiscUtils.castToMap(rawOverrides, false), arguments, (obj) -> { + // 如果overrides的下一级就是一个模板,则模板必须为map类型 + if (obj instanceof Map mapResult) { + overrides.putAll(MiscUtils.castToMap(mapResult, false)); + } else { + throw new IllegalArgumentException("Invalid template used. Input: " + GsonHelper.get().toJson(input) + ". Template: " + GsonHelper.get().toJson(obj)); + } + }); + if (input.get(MERGES) instanceof Map rawMerges) { + Map merges = new LinkedHashMap<>(); + processMap(MiscUtils.castToMap(rawMerges, false), arguments, (obj) -> { + // 如果merges的下一级就是一个模板,则模板必须为map类型 + if (obj instanceof Map mapResult) { + merges.putAll(MiscUtils.castToMap(mapResult, false)); + } else { + throw new IllegalArgumentException("Invalid template used. Input: " + GsonHelper.get().toJson(input) + ". Template: " + GsonHelper.get().toJson(obj)); + } + }); + // 返回处理结果 + return new TemplateProcessingResult( + templateList, + overrides, + merges, + arguments + ); } else { - throw new IllegalArgumentException("Invalid template used. Input: " + GsonHelper.get().toJson(input) + ". Template: " + GsonHelper.get().toJson(obj)); + return new TemplateProcessingResult( + templateList, + overrides, + null, + arguments + ); + } + } else if (override instanceof List overrides) { + // overrides不为空,且不是map + List processedOverrides = new ArrayList<>(overrides.size()); + for (Object item : overrides) { + processUnknownTypeMember(item, arguments, processedOverrides::add); + } + if (input.get(MERGES) instanceof List rawMerges) { + List merges = new ArrayList<>(rawMerges.size()); + for (Object item : rawMerges) { + processUnknownTypeMember(item, arguments, merges::add); + } + return new TemplateProcessingResult( + templateList, + processedOverrides, + merges, + arguments + ); + } else { + // overrides不为空,且不是map + return new TemplateProcessingResult( + templateList, + processedOverrides, + null, + arguments + ); + } + } else if (override != null) { + // overrides不为空,且不是map。此情况不用再考虑merge了 + return new TemplateProcessingResult( + templateList, + override, + null, + arguments + ); + } else { + // 获取merges + Object merge = input.get(MERGES); + if (merge instanceof Map rawMerges) { + Map merges = new LinkedHashMap<>(); + processMap(MiscUtils.castToMap(rawMerges, false), arguments, (obj) -> { + // 如果merges的下一级就是一个模板,则模板必须为map类型 + if (obj instanceof Map mapResult) { + merges.putAll(MiscUtils.castToMap(mapResult, false)); + } else { + throw new IllegalArgumentException("Invalid template used. Input: " + GsonHelper.get().toJson(input) + ". Template: " + GsonHelper.get().toJson(obj)); + } + }); + Map overrides = new LinkedHashMap<>(); + // 会不会有一种可能,有笨比用户不会使用overrides,把模板和普通配置混合在了一起?再次遍历input后处理 + for (Map.Entry inputEntry : input.entrySet()) { + String inputKey = inputEntry.getKey(); + if (NON_TEMPLATE_KEY.contains(inputKey)) continue; + // 处理那些overrides + processUnknownTypeMember(inputEntry.getValue(), arguments, (processed) -> overrides.put(inputKey, processed)); + } + return new TemplateProcessingResult( + templateList, + overrides.isEmpty() ? null : overrides, + merges, + arguments + ); + } else if (merge instanceof List rawMerges) { + List merges = new ArrayList<>(rawMerges.size()); + for (Object item : rawMerges) { + processUnknownTypeMember(item, arguments, merges::add); + } + return new TemplateProcessingResult( + templateList, + null, + merges, + arguments + ); + } else { + Map overrides = new LinkedHashMap<>(); + // 会不会有一种可能,有笨比用户不会使用overrides,把模板和普通配置混合在了一起?再次遍历input后处理 + for (Map.Entry inputEntry : input.entrySet()) { + String inputKey = inputEntry.getKey(); + if (NON_TEMPLATE_KEY.contains(inputKey)) continue; + // 处理那些overrides + processUnknownTypeMember(inputEntry.getValue(), arguments, (processed) -> overrides.put(inputKey, processed)); + } + return new TemplateProcessingResult( + templateList, + overrides.isEmpty() ? null : overrides, + merge, + arguments + ); } - }); - // 会不会有一种可能,有笨比用户不会使用overrides,把模板和普通配置混合在了一起?再次遍历input后处理 - for (Map.Entry inputEntry : input.entrySet()) { - String inputKey = inputEntry.getKey(); - if (NON_TEMPLATE_KEY.contains(inputKey)) continue; - // 处理那些overrides - processUnknownTypeMember(inputEntry.getValue(), arguments, (processed) -> overrides.put(inputKey, processed)); } - // 返回处理结果 - return new TemplateProcessingResult( - templateList, - overrides, - arguments - ); } // 合并参数 @@ -284,7 +400,8 @@ public class TemplateManagerImpl implements TemplateManager { private record TemplateProcessingResult( List templates, - Map overrides, + Object overrides, + Object merges, Map arguments ) {} diff --git a/gradle.properties b/gradle.properties index 5b9bbed93..9952619bd 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.56 +project_version=0.0.56.1 config_version=34 lang_version=14 project_group=net.momirealms From 54c29cff460b986c0c33a94843ed88ea3c70a3d7 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 5 Jun 2025 16:03:54 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E5=BA=94=E7=94=A8merges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/template/TemplateManagerImpl.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index 01c594e03..4e6e1a650 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -107,13 +107,15 @@ public class TemplateManagerImpl implements TemplateManager { Map results = new LinkedHashMap<>(); for (Object processedTemplate : processedTemplates) { if (processedTemplate instanceof Map anotherMap) { - Map castedMap = MiscUtils.castToMap(anotherMap, false); - deepMergeMaps(results, castedMap); + deepMergeMaps(results, MiscUtils.castToMap(anotherMap, false)); } } if (processingResult.overrides() instanceof Map overrides) { results.putAll(MiscUtils.castToMap(overrides, false)); } + if (processingResult.merges() instanceof Map merges) { + deepMergeMaps(results, MiscUtils.castToMap(merges, false)); + } processCallBack.accept(results); } else if (firstTemplate instanceof List) { List results = new ArrayList<>(); @@ -124,10 +126,13 @@ public class TemplateManagerImpl implements TemplateManager { } } if (processingResult.overrides() instanceof List overrides) { - processCallBack.accept(overrides); - } else { - processCallBack.accept(results); + results.clear(); + results.addAll(overrides); } + if (processingResult.merges() instanceof List merges) { + results.addAll(merges); + } + processCallBack.accept(results); } else { Object overrides = processingResult.overrides(); if (overrides != null) { From ff59cd9db9c6d37249dca1830c13de8228e2f41a Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Thu, 5 Jun 2025 19:46:24 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E5=BC=BA=E5=8C=96=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=9B=BF=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/template/TemplateManagerImpl.java | 123 ++++++++++++++---- 1 file changed, 100 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java index 4e6e1a650..c990cded7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/template/TemplateManagerImpl.java @@ -188,7 +188,7 @@ public class TemplateManagerImpl implements TemplateManager { List templateList = new ArrayList<>(); for (String templateId : templateIds) { // 如果模板id被用了参数,则应先应用参数后再查询模板 - Object actualTemplate = templateId.contains(LEFT_BRACKET) && templateId.contains(RIGHT_BRACKET) ? applyArgument(templateId, parentArguments) : templateId; + Object actualTemplate = applyArgument(templateId, parentArguments); if (actualTemplate == null) continue; // 忽略被null掉的模板 Object template = Optional.ofNullable(templates.get(Key.of(actualTemplate.toString()))) .orElseThrow(() -> new IllegalArgumentException("Template not found: " + actualTemplate)); @@ -337,7 +337,7 @@ public class TemplateManagerImpl implements TemplateManager { // argument_1: "{parent_argument}" for (Map.Entry argumentEntry : rawChildArguments.entrySet()) { // 获取最终的string形式参数 - String placeholder = LEFT_BRACKET + argumentEntry.getKey() + RIGHT_BRACKET; + String placeholder = argumentEntry.getKey(); // 父亲参数最大 if (result.containsKey(placeholder)) continue; Object rawArgument = argumentEntry.getValue(); @@ -378,29 +378,13 @@ public class TemplateManagerImpl implements TemplateManager { // 将某个输入变成最终的结果,可以是string->string,也可以是string->map/list private Object applyArgument(String input, Map arguments) { - StringBuilder result = new StringBuilder(); - Matcher matcher = ARGUMENT_PATTERN.matcher(input); - boolean first = true; - while (matcher.find()) { - String placeholder = matcher.group(); - Supplier replacer = arguments.get(placeholder); - if (replacer == null) { - matcher.appendReplacement(result, placeholder); - continue; - } - if (first) { - first = false; - if (input.length() == placeholder.length()) { - return replacer.get(); - } else { - matcher.appendReplacement(result, replacer.get().toString()); - } - } else { - matcher.appendReplacement(result, replacer.get().toString()); + if (input.charAt(0) == '{' && input.charAt(input.length() - 1) == '}') { + String key = input.substring(1, input.length() - 2); + if (arguments.containsKey(key)) { + return arguments.get(key).get(); } } - matcher.appendTail(result); - return result.toString(); + return replacePlaceholders(input, arguments); } private record TemplateProcessingResult( @@ -433,4 +417,97 @@ public class TemplateManagerImpl implements TemplateManager { } } } + public static String replacePlaceholders(String input, Map replacements) { + if (input == null || input.isEmpty()) { + return input; + } + + StringBuilder finalResult = new StringBuilder(); + int n = input.length(); + int lastAppendPosition = 0; // 追踪上一次追加操作结束的位置 + int i = 0; + + while (i < n) { + // 检查当前字符是否为未转义的 '{' + int backslashes = 0; + int temp_i = i - 1; + while (temp_i >= 0 && input.charAt(temp_i) == '\\') { + backslashes++; + temp_i--; + } + + if (input.charAt(i) == '{' && backslashes % 2 == 0) { + // 发现占位符起点 + int placeholderStartIndex = i; + + // 追加从上一个位置到当前占位符之前的文本 + finalResult.append(input, lastAppendPosition, placeholderStartIndex); + + // --- 开始解析占位符内部 --- + StringBuilder keyBuilder = new StringBuilder(); + int depth = 1; + int j = i + 1; + boolean foundMatch = false; + + while (j < n) { + char c = input.charAt(j); + if (c == '\\') { // 处理转义 + if (j + 1 < n) { + keyBuilder.append(input.charAt(j + 1)); + j += 2; + } else { + keyBuilder.append(c); + j++; + } + } else if (c == '{') { + depth++; + keyBuilder.append(c); + j++; + } else if (c == '}') { + depth--; + if (depth == 0) { // 找到匹配的结束括号 + String key = keyBuilder.toString(); + TemplateArgument value = replacements.get(key); + + if (value != null) { + // 如果在 Map 中找到值,则进行替换 + finalResult.append(value.get()); + } else { + // 否则,保留原始占位符(包括 '{}') + finalResult.append(input, placeholderStartIndex, j + 1); + } + + // 更新位置指针 + i = j + 1; + lastAppendPosition = i; + foundMatch = true; + break; + } + keyBuilder.append(c); // 嵌套的 '}' + j++; + } else { + keyBuilder.append(c); + j++; + } + } + // --- 占位符解析结束 --- + + if (!foundMatch) { + // 如果内层循环结束仍未找到匹配的 '}',则不进行任何特殊处理 + // 外层循环的 i 会自然递增 + i++; + } + + } else { + i++; + } + } + + // 追加最后一个占位符之后的所有剩余文本 + if (lastAppendPosition < n) { + finalResult.append(input, lastAppendPosition, n); + } + + return finalResult.toString(); + } }