From 4f974888d5990d48e5e9f6eb90ccfd7fceaecee8 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sat, 24 Jan 2026 17:10:31 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=E4=BE=9D=E8=B5=96=E5=BE=AA=E7=8E=AF=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增强 mcfunction_processor 中的函数调用循环检测,防止构建时出现隐式循环依赖。添加调用图构建和环检测算法,当检测到函数调用循环时抛出明确的错误信息。 Fixes #... --- .../McfunctionProcessor.java | 69 ++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/rule/mcfunction_processor/McfunctionProcessor.java b/rule/mcfunction_processor/McfunctionProcessor.java index ec9aafb..660b537 100644 --- a/rule/mcfunction_processor/McfunctionProcessor.java +++ b/rule/mcfunction_processor/McfunctionProcessor.java @@ -18,6 +18,8 @@ public class McfunctionProcessor extends Worker { private Path dataPackRoot; private List dependencyPaths = new ArrayList<>(); private Path workspaceRoot; + private Map> functionCallGraph = new HashMap<>(); + private Set detectedCycles = new HashSet<>(); public static void main(String[] args) throws Exception { new McfunctionProcessor().run(); @@ -65,7 +67,7 @@ protected int handleRequest(WorkRequest request, PrintWriter out) { try { processFile(inputFile, outputFile); - } catch (IOException e) { + } catch (Exception e) { out.println("Error processing file: " + e.getMessage()); e.printStackTrace(out); return 1; @@ -317,6 +319,8 @@ private List loadFunction(String functionName) { // 对加载的函数内容进行基础处理(注释去除和反斜杠拼接) List processedContent = processBasicLines(content); functionCache.put(functionName, processedContent); + // 解析该函数调用的其他函数,构建调用图 + parseFunctionCalls(functionName, processedContent); return processedContent; } catch (IOException e) { // 继续尝试下一个路径 @@ -551,6 +555,69 @@ private List splitSNBTPairs(String content) { return pairs; } + private void parseFunctionCalls(String functionName, List functionContent) { + Set calls = new HashSet<>(); + for (String line : functionContent) { + String trimmed = line.trim(); + Matcher matcher = FUNCTION_CALL_PATTERN.matcher(trimmed); + if (matcher.matches()) { + String calledFunction = matcher.group(1); + // 验证命名空间ID格式 + if (!NAMESPACE_ID_PATTERN.matcher(calledFunction).matches()) { + continue; + } + // 跳过以#开头的函数名 + if (calledFunction.startsWith("#")) { + continue; + } + calls.add(calledFunction); + } + } + functionCallGraph.put(functionName, calls); + // 检测调用图中是否有环 + detectCycles(functionName); + } + + private void detectCycles(String startFunction) { + Set visited = new HashSet<>(); + Set stack = new HashSet<>(); + List path = new ArrayList<>(); + if (hasCycle(startFunction, visited, stack, path)) { + // 避免重复报告同一个环 + String cycleKey = String.join("->", path); + if (!detectedCycles.contains(cycleKey)) { + detectedCycles.add(cycleKey); + throw new RuntimeException("检测到函数调用循环: " + cycleKey); + } + } + } + + private boolean hasCycle(String function, Set visited, Set stack, List path) { + if (stack.contains(function)) { + path.add(function); + return true; + } + if (visited.contains(function)) { + return false; + } + visited.add(function); + stack.add(function); + path.add(function); + + Set calls = functionCallGraph.get(function); + if (calls != null) { + for (String callee : calls) { + if (hasCycle(callee, visited, stack, path)) { + return true; + } + } + } + + stack.remove(function); + path.remove(path.size() - 1); + return false; + } + private String processSNBTValue(String value) { value = value.trim(); From 61e18816c343fde4b7a21a8de0c16b503ede1d0e Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sat, 24 Jan 2026 17:27:32 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=90=9B=20fix:=20standardize=20cycle?= =?UTF-8?q?=20detection=20error=20message=20to=20English?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change error message from Chinese to English for consistency with other error messages in the McfunctionProcessor. This ensures uniform language usage throughout the build tool. The previous Chinese message "检测到函数调用循环: " has been updated to "Detected function call cycle: " to match the existing English error messages like "Error processing file: " and "Usage: ". --- rule/mcfunction_processor/McfunctionProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rule/mcfunction_processor/McfunctionProcessor.java b/rule/mcfunction_processor/McfunctionProcessor.java index 660b537..a15c914 100644 --- a/rule/mcfunction_processor/McfunctionProcessor.java +++ b/rule/mcfunction_processor/McfunctionProcessor.java @@ -587,7 +587,7 @@ private void detectCycles(String startFunction) { String cycleKey = String.join("->", path); if (!detectedCycles.contains(cycleKey)) { detectedCycles.add(cycleKey); - throw new RuntimeException("检测到函数调用循环: " + cycleKey); + throw new RuntimeException("Detected function call cycle: " + cycleKey); } } } From 4be6fd94d491ced667b222ee23086c7b3a419590 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Sat, 24 Jan 2026 17:39:03 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=90=9B=20fix:=20detect=20function=20c?= =?UTF-8?q?alls=20in=20execute=20commands=20for=20cycle=20detection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance the function call detection to capture function calls within execute commands (e.g., "execute as @a run function namespace:path"). This ensures that cycle dependency detection works correctly for all types of function calls, not just those starting with the "function" keyword. The new FUNCTION_CALL_DETECTION_PATTERN uses word boundary matching to find function calls anywhere in the line, while maintaining validation of namespace ID format and skipping commented functions. --- rule/mcfunction_processor/McfunctionProcessor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rule/mcfunction_processor/McfunctionProcessor.java b/rule/mcfunction_processor/McfunctionProcessor.java index a15c914..22f544c 100644 --- a/rule/mcfunction_processor/McfunctionProcessor.java +++ b/rule/mcfunction_processor/McfunctionProcessor.java @@ -12,6 +12,7 @@ public class McfunctionProcessor extends Worker { private static final Pattern FUNCTION_CALL_PATTERN = Pattern.compile("^function\\s+([a-z0-9_.-]+:[a-z0-9_./\\-]+)(?:\\s+(.+))?$"); private static final Pattern FORCE_FUNCTION_PATTERN = Pattern.compile("^#function\\s+([a-z0-9_.-]+:[a-z0-9_./\\-]+)$"); private static final Pattern RETURN_PATTERN = Pattern.compile("^(?:return|execute\\s+.*\\s+run\\s+return)\\b"); + private static final Pattern FUNCTION_CALL_DETECTION_PATTERN = Pattern.compile("\\bfunction\\s+([a-z0-9_.-]+:[a-z0-9_./\\-]+)"); private Map> functionCache = new HashMap<>(); private String currentPackId; @@ -559,8 +560,8 @@ private void parseFunctionCalls(String functionName, List functionConten Set calls = new HashSet<>(); for (String line : functionContent) { String trimmed = line.trim(); - Matcher matcher = FUNCTION_CALL_PATTERN.matcher(trimmed); - if (matcher.matches()) { + Matcher matcher = FUNCTION_CALL_DETECTION_PATTERN.matcher(trimmed); + while (matcher.find()) { String calledFunction = matcher.group(1); // 验证命名空间ID格式 if (!NAMESPACE_ID_PATTERN.matcher(calledFunction).matches()) {