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.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

/** Canonical schema-version 2 trigger program shared by visual and script front ends. */
public record TriggerProgramV2(
        int schemaVersion,
        List<EventBinding> events,
        List<VariableDeclaration> declarations,
        List<FunctionDeclaration> functions,
        List<Statement> statements) {

    public static final int SCHEMA_VERSION = 2;

    public TriggerProgramV2 {
        if (schemaVersion != SCHEMA_VERSION) {
            throw new IllegalArgumentException("unsupported trigger schemaVersion: " + schemaVersion);
        }
        events = immutable(events, "event");
        declarations = immutable(declarations, "declaration");
        functions = immutable(functions, "function");
        statements = immutable(statements, "statement");
        if (events.isEmpty()) throw new IllegalArgumentException("a V2 program needs at least one event");
        TriggerProgramValidator.validate(events, declarations, functions, statements);
    }

    public record EventBinding(UUID nodeId, String type, Map<String, String> configuration) {
        public EventBinding {
            nodeId = stable(nodeId);
            type = id(type, "event type");
            configuration = Map.copyOf(configuration == null ? Map.of() : configuration);
        }
    }

    public record VariableDeclaration(
            UUID nodeId,
            String name,
            String valueType,
            TriggerExpression initialValue,
            String visibility,
            String storage,
            Lifetime lifetime,
            Long ttlSeconds) {
        public VariableDeclaration {
            nodeId = stable(nodeId);
            name = id(name, "variable name");
            if (!name.matches("[A-Za-z_][A-Za-z0-9_-]{0,47}")) {
                throw new IllegalArgumentException("variable name must be a portable 1-48 character identifier");
            }
            valueType = TriggerValueTypes.normalizeVariableType(text(valueType, "variable type"));
            if (initialValue == null && !valueType.startsWith("optional<")) {
                throw new IllegalArgumentException("non-optional variable requires an initial value");
            }
            visibility = normalize(visibility, "trigger");
            storage = normalize(storage, visibility.equals("global") ? "server" : "trigger");
            lifetime = lifetime == null ? Lifetime.SESSION : lifetime;
            if (lifetime == Lifetime.TTL) {
                if (ttlSeconds == null || ttlSeconds < 1 || ttlSeconds > 31_536_000L) {
                    throw new IllegalArgumentException("ttl variable requires ttlSeconds between 1 and 31536000");
                }
            } else {
                ttlSeconds = null;
            }
        }
    }

    public enum Lifetime { SESSION, TTL, PERSISTENT }

    public record FunctionDeclaration(
            UUID nodeId,
            String name,
            List<Parameter> parameters,
            String returnType,
            List<VariableDeclaration> locals,
            List<Statement> statements) {
        public FunctionDeclaration {
            nodeId = stable(nodeId);
            name = id(name, "function name");
            parameters = immutable(parameters, "function parameter");
            returnType = normalize(returnType, "void");
            if (!returnType.equals("void")) returnType = TriggerValueTypes.normalizeVariableType(returnType);
            locals = immutable(locals, "local variable");
            statements = immutable(statements, "function statement");
        }
    }

    public record Parameter(String name, String valueType, ParameterMode mode) {
        public Parameter {
            name = id(name, "parameter name");
            valueType = TriggerValueTypes.normalizeVariableType(text(valueType, "parameter type"));
            mode = mode == null ? ParameterMode.IN : mode;
        }
    }

    public enum ParameterMode { IN, OUT, INOUT }

    public record Statement(
            UUID nodeId,
            StatementKind kind,
            String name,
            Map<String, TriggerExpression> inputs,
            TriggerExpression expression,
            List<SwitchCase> cases,
            List<Statement> statements,
            List<Statement> elseStatements) {
        public Statement {
            nodeId = stable(nodeId);
            Objects.requireNonNull(kind, "statement kind");
            name = name == null ? "" : name.strip();
            inputs = Map.copyOf(inputs == null ? Map.of() : inputs);
            statements = immutable(statements, "child statement");
            elseStatements = immutable(elseStatements, "else statement");
            cases = immutable(cases, "switch case");
            if ((kind == StatementKind.ACTION || kind == StatementKind.SET
                    || kind == StatementKind.CALL || kind == StatementKind.FOREACH) && name.isBlank()) {
                throw new IllegalArgumentException(kind.name().toLowerCase(Locale.ROOT) + " statement requires a name");
            }
        }
    }

    public enum StatementKind {
        ACTION, SET, IF, SWITCH, REPEAT, WHILE, FOREACH,
        BREAK, CONTINUE, CALL, RETURN, TRY
    }

    public record SwitchCase(UUID nodeId, TriggerExpression match, List<Statement> statements) {
        public SwitchCase {
            nodeId = stable(nodeId);
            Objects.requireNonNull(match, "switch case match");
            statements = immutable(statements, "switch case statement");
        }
    }

    private static UUID stable(UUID value) {
        return value == null ? UUID.randomUUID() : value;
    }

    private static String id(String value, String field) {
        String result = text(value, field);
        if (!result.matches("[A-Za-z_][A-Za-z0-9_.:-]{0,127}")) {
            throw new IllegalArgumentException(field + " contains unsupported characters");
        }
        return result;
    }

    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 normalize(String value, String fallback) {
        return value == null || value.isBlank() ? fallback : value.strip().toLowerCase(Locale.ROOT);
    }

    private static <T> List<T> immutable(List<T> values, String element) {
        List<T> result = List.copyOf(values == null ? List.of() : values);
        if (result.stream().anyMatch(Objects::isNull)) {
            throw new IllegalArgumentException(element + " must not be null");
        }
        return result;
    }
}