Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion rule/mcfunction_processor/McfunctionProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ 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<String, List<String>> functionCache = new HashMap<>();
private String currentPackId;
private Path dataPackRoot;
private List<Path> dependencyPaths = new ArrayList<>();
private Path workspaceRoot;
private Map<String, Set<String>> functionCallGraph = new HashMap<>();
private Set<String> detectedCycles = new HashSet<>();

public static void main(String[] args) throws Exception {
new McfunctionProcessor().run();
Expand Down Expand Up @@ -65,7 +68,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;
Expand Down Expand Up @@ -317,6 +320,8 @@ private List<String> loadFunction(String functionName) {
// 对加载的函数内容进行基础处理(注释去除和反斜杠拼接)
List<String> processedContent = processBasicLines(content);
functionCache.put(functionName, processedContent);
// 解析该函数调用的其他函数,构建调用图
parseFunctionCalls(functionName, processedContent);
return processedContent;
} catch (IOException e) {
// 继续尝试下一个路径
Expand Down Expand Up @@ -551,6 +556,69 @@ private List<String> splitSNBTPairs(String content) {
return pairs;
}

private void parseFunctionCalls(String functionName, List<String> functionContent) {
Set<String> calls = new HashSet<>();
for (String line : functionContent) {
String trimmed = line.trim();
Matcher matcher = FUNCTION_CALL_DETECTION_PATTERN.matcher(trimmed);
while (matcher.find()) {
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<String> visited = new HashSet<>();
Set<String> stack = new HashSet<>();
List<String> path = new ArrayList<>();
if (hasCycle(startFunction, visited, stack, path)) {
// 避免重复报告同一个环
String cycleKey = String.join("->", path);
if (!detectedCycles.contains(cycleKey)) {
detectedCycles.add(cycleKey);
throw new RuntimeException("Detected function call cycle: " + cycleKey);
}
}
}

private boolean hasCycle(String function, Set<String> visited, Set<String> stack, List<String> 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<String> 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();

Expand Down
Loading