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

XFEServerManager

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

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

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Stable, declarative extension point for trigger catalog contributions.
 *
 * <p>Implementations describe capabilities only. They do not expose host classes or executable
 * bytecode to trigger documents. Platform code is responsible for binding a descriptor id to a
 * separately registered, isolated implementation.</p>
 */
public interface TriggerExtension {
    String namespace();

    String version();

    default Collection<Descriptor> descriptors() {
        return List.of();
    }

    /** Pure/snapshot handlers keyed by their namespaced VALUE_FUNCTION or CONDITION_FUNCTION id. */
    default Map<String, FunctionHandler> functionHandlers() {
        return Map.of();
    }

    /** Server-thread handlers keyed by their namespaced ACTION descriptor id. */
    default Map<String, ActionHandler> actionHandlers() {
        return Map.of();
    }

    @FunctionalInterface
    interface FunctionHandler {
        Object evaluate(FunctionInvocation invocation) throws Exception;
    }

    @FunctionalInterface
    interface ActionHandler {
        void execute(ActionInvocation invocation) throws Exception;
    }

    /** Contains transport-safe snapshots only; Minecraft/JVM host objects are never supplied. */
    record FunctionInvocation(
            String functionId,
            List<Object> arguments,
            Map<String, Object> event,
            Map<String, Object> variables,
            long executionSeed,
            String executionInstant) {
        public FunctionInvocation {
            functionId = requireId(functionId, "functionId");
            arguments = java.util.Collections.unmodifiableList(new java.util.ArrayList<>(
                    arguments == null ? List.of() : arguments));
            event = immutableMap(event);
            variables = immutableMap(variables);
            executionInstant = requireText(executionInstant, "executionInstant");
        }
    }

    /** Stable action boundary carrying the VM's retry/idempotency identity. */
    record ActionInvocation(
            String actionId,
            Map<String, Object> parameters,
            Map<String, Object> event,
            String executionId,
            String nodeId,
            String idempotencyKey) {
        public ActionInvocation {
            actionId = requireId(actionId, "actionId");
            parameters = immutableMap(parameters);
            event = immutableMap(event);
            executionId = requireText(executionId, "executionId");
            nodeId = requireText(nodeId, "nodeId");
            idempotencyKey = requireText(idempotencyKey, "idempotencyKey");
        }
    }

    enum Kind { EVENT, EVENT_RESPONSE, VALUE_FUNCTION, CONDITION_FUNCTION, ACTION, TYPE }

    enum Purity { PURE, SNAPSHOT_QUERY, WORLD_QUERY, SIDE_EFFECT }

    enum ThreadAffinity { BACKGROUND, SERVER }

    enum Risk {
        SAFE,
        ADMINISTRATOR,
        OWNER;

        /** Extensions may raise an existing risk, but can never lower it. */
        public static Risk atLeast(Risk builtIn, Risk proposed) {
            Objects.requireNonNull(builtIn, "builtIn");
            Objects.requireNonNull(proposed, "proposed");
            return values()[Math.max(builtIn.ordinal(), proposed.ordinal())];
        }
    }

    record Parameter(
            String name,
            String valueType,
            boolean required,
            Object defaultValue,
            String description) {
        public Parameter {
            name = requireId(name, "parameter name");
            valueType = requireText(valueType, "parameter valueType");
            description = description == null ? "" : description.strip();
        }
    }

    record Descriptor(
            String id,
            Kind kind,
            String displayName,
            String description,
            List<Parameter> parameters,
            String returnType,
            Purity purity,
            ThreadAffinity threadAffinity,
            Risk risk,
            Set<String> supportedPlatforms,
            Set<String> applicableEvents,
            Map<String, Object> metadata) {
        public Descriptor {
            id = requireId(id, "descriptor id");
            Objects.requireNonNull(kind, "kind");
            displayName = requireText(displayName, "displayName");
            description = description == null ? "" : description.strip();
            parameters = List.copyOf(parameters == null ? List.of() : parameters);
            returnType = returnType == null ? "void" : requireText(returnType, "returnType");
            Objects.requireNonNull(purity, "purity");
            Objects.requireNonNull(threadAffinity, "threadAffinity");
            Objects.requireNonNull(risk, "risk");
            supportedPlatforms = Set.copyOf(supportedPlatforms == null ? Set.of() : supportedPlatforms);
            applicableEvents = Set.copyOf(applicableEvents == null ? Set.of() : applicableEvents);
            metadata = Map.copyOf(metadata == null ? Map.of() : metadata);
        }
    }

    private static String requireId(String value, String field) {
        String normalized = requireText(value, field).toLowerCase(java.util.Locale.ROOT);
        if (!normalized.matches("[a-z][a-z0-9_.:_-]{0,127}")) {
            throw new IllegalArgumentException(field + " contains unsupported characters");
        }
        return normalized;
    }

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

    private static Map<String, Object> immutableMap(Map<String, Object> values) {
        return java.util.Collections.unmodifiableMap(new java.util.LinkedHashMap<>(
                values == null ? Map.of() : values));
    }
}