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

XFEServerManager

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

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

import java.time.Instant;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/** Immutable dry-run; ordering is newest event first. */
public record RollbackPlan(
        UUID planId,
        long watermark,
        Instant createdAt,
        RollbackDirection direction,
        boolean force,
        boolean loadUnloadedChunks,
        boolean ownerAuthorized,
        List<RollbackPlanItem> items,
        List<JournalGap> gaps
) {
    public RollbackPlan {
        Objects.requireNonNull(planId, "planId");
        if (watermark < 0) throw new IllegalArgumentException("watermark must be non-negative");
        Objects.requireNonNull(createdAt, "createdAt");
        Objects.requireNonNull(direction, "direction");
        if ((force || loadUnloadedChunks) && !ownerAuthorized) {
            throw new SecurityException("force and temporary chunk loading are owner-only");
        }
        items = List.copyOf(items);
        gaps = List.copyOf(gaps);
        var sequences = new java.util.HashSet<Long>();
        var eventIds = new java.util.HashSet<UUID>();
        for (RollbackPlanItem item : items) {
            if (item.entry().sequence() > watermark) throw new IllegalArgumentException("item exceeds watermark");
            if (!sequences.add(item.entry().sequence())) throw new IllegalArgumentException("duplicate plan sequence");
            if (!eventIds.add(item.entry().change().eventId())) throw new IllegalArgumentException("duplicate plan event id");
        }
        for (int index = 1; index < items.size(); index++) {
            long previous = items.get(index - 1).entry().sequence();
            long current = items.get(index).entry().sequence();
            if ((direction == RollbackDirection.ROLLBACK && previous <= current)
                    || (direction == RollbackDirection.REDO && previous >= current)) {
                throw new IllegalArgumentException(direction == RollbackDirection.ROLLBACK
                        ? "rollback items must be in reverse sequence order"
                        : "redo items must be in forward sequence order");
            }
        }
    }

    public boolean completeRecoveryPossible() {
        return gaps.isEmpty() && items.stream().allMatch(item -> item.executable(force));
    }

    public long executableCount() {
        return items.stream().filter(item -> item.executable(force)).count();
    }
}