XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEServerManager

【Java】我的世界XFE服务器管理器

公开
关注 0 Fork 0 Star 0
UTF-8
package com.xfestudio.xfeservermanager.core.trigger;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/** Declarative .xfelib payload. It intentionally has no class/bytecode/script entry point. */
public record TriggerLibraryPackage(
        Manifest manifest,
        Map<String, TriggerProgramV2> triggers,
        Map<String, Object> variables,
        Map<String, Object> functions,
        Map<String, Object> templates) {
    public TriggerLibraryPackage {
        Objects.requireNonNull(manifest, "manifest");
        triggers = checkedPrograms(triggers);
        variables = immutable(variables);
        functions = immutable(functions);
        templates = immutable(templates);
        if (triggers.size() > 256 || templates.size() > 256) {
            throw new IllegalArgumentException("library contains too many triggers or templates");
        }
    }

    public record Manifest(String namespace, String version, String engineRequirement,
                           Map<String, String> dependencies, String displayName,
                           String description) {
        public Manifest {
            namespace = text(namespace, "namespace").toLowerCase(java.util.Locale.ROOT);
            if (!namespace.matches("[a-z][a-z0-9_.-]{1,63}")) {
                throw new IllegalArgumentException("library namespace is invalid");
            }
            version = text(version, "version");
            if (!version.matches("(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?")) {
                throw new IllegalArgumentException("library version must be semantic versioning");
            }
            engineRequirement = engineRequirement == null || engineRequirement.isBlank()
                    ? ">=2.0.0" : engineRequirement.strip();
            if (!validRequirement(engineRequirement)) {
                throw new IllegalArgumentException("library engine requirement is invalid");
            }
            dependencies = Map.copyOf(dependencies == null ? Map.of() : dependencies);
            if (dependencies.size() > 64 || dependencies.containsKey(namespace)) {
                throw new IllegalArgumentException("library dependencies are invalid");
            }
            dependencies.forEach((name, required) -> {
                if (!name.matches("[a-z][a-z0-9_.-]{1,63}") || !validRequirement(required)) {
                    throw new IllegalArgumentException("invalid library dependency: " + name);
                }
            });
            displayName = bounded(displayName, namespace, 120);
            description = bounded(description, "", 500);
        }
    }

    public static boolean satisfies(String actual, String requirement) {
        int comparison = compareVersions(actual, requirement.replaceFirst("^(?:>=|<=|>|<|=|\\^|~)\\s*", ""));
        String prefix = requirement.strip();
        if (prefix.startsWith(">=")) return comparison >= 0;
        if (prefix.startsWith("<=")) return comparison <= 0;
        if (prefix.startsWith(">")) return comparison > 0;
        if (prefix.startsWith("<")) return comparison < 0;
        if (prefix.startsWith("^") || prefix.startsWith("~")) {
            String[] have = numeric(actual);
            String[] need = numeric(prefix.substring(1));
            return comparison >= 0 && (prefix.startsWith("^")
                    ? have[0].equals(need[0]) : have[0].equals(need[0]) && have[1].equals(need[1]));
        }
        return comparison == 0;
    }

    private static boolean validRequirement(String value) {
        return value != null && value.strip().matches("(?:>=|<=|>|<|=|\\^|~)?\\s*"
                + "(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?");
    }

    private static int compareVersions(String left, String right) {
        String[] a = numeric(left);
        String[] b = numeric(right);
        for (int index = 0; index < 3; index++) {
            int comparison = Long.compare(Long.parseLong(a[index]), Long.parseLong(b[index]));
            if (comparison != 0) return comparison;
        }
        return 0;
    }

    private static String[] numeric(String version) {
        String normalized = version.strip().replaceFirst("^(?:>=|<=|>|<|=|\\^|~)\\s*", "")
                .split("-", 2)[0];
        String[] parts = normalized.split("\\.");
        if (parts.length != 3) throw new IllegalArgumentException("invalid semantic version: " + version);
        return parts;
    }

    private static Map<String, TriggerProgramV2> checkedPrograms(Map<String, TriggerProgramV2> values) {
        Map<String, TriggerProgramV2> copy = new LinkedHashMap<>();
        if (values != null) values.forEach((name, program) -> {
            if (name == null || !name.matches("[A-Za-z_][A-Za-z0-9_.-]{0,127}") || program == null) {
                throw new IllegalArgumentException("library trigger entry is invalid");
            }
            TriggerProgramTypeChecker.validate(program);
            copy.put(name, program);
        });
        return Map.copyOf(copy);
    }

    private static Map<String, Object> immutable(Map<String, ?> values) {
        return Map.copyOf(values == null ? Map.of() : values);
    }

    private static String text(String value, String field) {
        if (value == null || value.isBlank()) throw new IllegalArgumentException(field + " is required");
        return value.strip();
    }

    private static String bounded(String value, String fallback, int maximum) {
        String result = value == null || value.isBlank() ? fallback : value.strip();
        if (result.length() > maximum) throw new IllegalArgumentException("library text is too long");
        return result;
    }
}