package com.xfestudio.xfeservermanager.core.governance.claim;
import java.util.EnumSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
public record Claim(
UUID claimId,
UUID ownerId,
String name,
Set<ChunkKey> chunks,
Map<UUID, ClaimRole> assignments,
Set<ClaimFlag> publicFlags,
long revision
) {
public Claim {
Objects.requireNonNull(claimId, "claimId");
Objects.requireNonNull(ownerId, "ownerId");
if (name == null || name.isBlank()) throw new IllegalArgumentException("name must not be blank");
name = name.trim();
Objects.requireNonNull(chunks, "chunks");
chunks = Set.copyOf(chunks);
if (chunks.isEmpty()) throw new IllegalArgumentException("claim must contain at least one chunk");
String dimension = chunks.iterator().next().dimensionId();
if (chunks.stream().anyMatch(chunk -> !chunk.dimensionId().equals(dimension))) {
throw new IllegalArgumentException("one claim cannot span dimensions");
}
if (!isContiguous(chunks)) throw new IllegalArgumentException("claim chunks must be cardinally contiguous");
Objects.requireNonNull(assignments, "assignments");
var normalized = new java.util.HashMap<>(assignments);
if (normalized.containsValue(ClaimRole.OWNER)
&& !ClaimRole.OWNER.equals(normalized.get(ownerId))) {
throw new IllegalArgumentException("only ownerId can hold the owner role");
}
normalized.entrySet().removeIf(entry -> entry.getValue() == null);
if (normalized.entrySet().stream().anyMatch(entry -> entry.getValue() == ClaimRole.OWNER
&& !entry.getKey().equals(ownerId))) {
throw new IllegalArgumentException("only ownerId can hold the owner role");
}
normalized.put(ownerId, ClaimRole.OWNER);
assignments = Map.copyOf(normalized);
Objects.requireNonNull(publicFlags, "publicFlags");
publicFlags = publicFlags.isEmpty() ? Set.of() : Set.copyOf(EnumSet.copyOf(publicFlags));
if (revision < 0) throw new IllegalArgumentException("revision must be non-negative");
}
public String dimensionId() {
return chunks.iterator().next().dimensionId();
}
public ClaimRole roleOf(UUID playerId) {
return assignments.get(playerId);
}
public Claim withChunks(Set<ChunkKey> next) {
return new Claim(claimId, ownerId, name, next, assignments, publicFlags, revision + 1);
}
public Claim withAssignment(UUID playerId, ClaimRole role) {
Objects.requireNonNull(playerId, "playerId");
Objects.requireNonNull(role, "role");
if (playerId.equals(ownerId) && role != ClaimRole.OWNER) {
throw new IllegalArgumentException("owner role cannot be changed");
}
if (role == ClaimRole.OWNER && !playerId.equals(ownerId)) {
throw new IllegalArgumentException("ownership requires the transfer workflow");
}
var next = new java.util.HashMap<>(assignments);
next.put(playerId, role);
return new Claim(claimId, ownerId, name, chunks, next, publicFlags, revision + 1);
}
public Claim withoutAssignment(UUID playerId) {
Objects.requireNonNull(playerId, "playerId");
if (playerId.equals(ownerId)) throw new IllegalArgumentException("owner cannot be removed");
var next = new java.util.HashMap<>(assignments);
next.remove(playerId);
return new Claim(claimId, ownerId, name, chunks, next, publicFlags, revision + 1);
}
public Claim withFlags(Set<ClaimFlag> flags) {
return new Claim(claimId, ownerId, name, chunks, assignments, flags, revision + 1);
}
public Claim transferTo(UUID newOwner) {
Objects.requireNonNull(newOwner, "newOwner");
if (newOwner.equals(ownerId)) throw new IllegalArgumentException("new owner must differ from current owner");
var next = new java.util.HashMap<>(assignments);
next.put(ownerId, ClaimRole.MANAGER);
next.put(newOwner, ClaimRole.OWNER);
return new Claim(claimId, newOwner, name, chunks, next, publicFlags, revision + 1);
}
private static boolean isContiguous(Set<ChunkKey> chunks) {
var reached = new java.util.HashSet<ChunkKey>();
var queue = new java.util.ArrayDeque<ChunkKey>();
queue.add(chunks.iterator().next());
while (!queue.isEmpty()) {
ChunkKey current = queue.remove();
if (!reached.add(current)) continue;
for (ChunkKey candidate : chunks) {
if (!reached.contains(candidate) && current.cardinallyAdjacent(candidate)) queue.add(candidate);
}
}
return reached.size() == chunks.size();
}
}
package com.xfestudio.xfeservermanager.core.governance.claim;
import java.util.EnumSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
public record Claim(
UUID claimId,
UUID ownerId,
String name,
Set<ChunkKey> chunks,
Map<UUID, ClaimRole> assignments,
Set<ClaimFlag> publicFlags,
long revision
) {
public Claim {
Objects.requireNonNull(claimId, "claimId");
Objects.requireNonNull(ownerId, "ownerId");
if (name == null || name.isBlank()) throw new IllegalArgumentException("name must not be blank");
name = name.trim();
Objects.requireNonNull(chunks, "chunks");
chunks = Set.copyOf(chunks);
if (chunks.isEmpty()) throw new IllegalArgumentException("claim must contain at least one chunk");
String dimension = chunks.iterator().next().dimensionId();
if (chunks.stream().anyMatch(chunk -> !chunk.dimensionId().equals(dimension))) {
throw new IllegalArgumentException("one claim cannot span dimensions");
}
if (!isContiguous(chunks)) throw new IllegalArgumentException("claim chunks must be cardinally contiguous");
Objects.requireNonNull(assignments, "assignments");
var normalized = new java.util.HashMap<>(assignments);
if (normalized.containsValue(ClaimRole.OWNER)
&& !ClaimRole.OWNER.equals(normalized.get(ownerId))) {
throw new IllegalArgumentException("only ownerId can hold the owner role");
}
normalized.entrySet().removeIf(entry -> entry.getValue() == null);
if (normalized.entrySet().stream().anyMatch(entry -> entry.getValue() == ClaimRole.OWNER
&& !entry.getKey().equals(ownerId))) {
throw new IllegalArgumentException("only ownerId can hold the owner role");
}
normalized.put(ownerId, ClaimRole.OWNER);
assignments = Map.copyOf(normalized);
Objects.requireNonNull(publicFlags, "publicFlags");
publicFlags = publicFlags.isEmpty() ? Set.of() : Set.copyOf(EnumSet.copyOf(publicFlags));
if (revision < 0) throw new IllegalArgumentException("revision must be non-negative");
}
public String dimensionId() {
return chunks.iterator().next().dimensionId();
}
public ClaimRole roleOf(UUID playerId) {
return assignments.get(playerId);
}
public Claim withChunks(Set<ChunkKey> next) {
return new Claim(claimId, ownerId, name, next, assignments, publicFlags, revision + 1);
}
public Claim withAssignment(UUID playerId, ClaimRole role) {
Objects.requireNonNull(playerId, "playerId");
Objects.requireNonNull(role, "role");
if (playerId.equals(ownerId) && role != ClaimRole.OWNER) {
throw new IllegalArgumentException("owner role cannot be changed");
}
if (role == ClaimRole.OWNER && !playerId.equals(ownerId)) {
throw new IllegalArgumentException("ownership requires the transfer workflow");
}
var next = new java.util.HashMap<>(assignments);
next.put(playerId, role);
return new Claim(claimId, ownerId, name, chunks, next, publicFlags, revision + 1);
}
public Claim withoutAssignment(UUID playerId) {
Objects.requireNonNull(playerId, "playerId");
if (playerId.equals(ownerId)) throw new IllegalArgumentException("owner cannot be removed");
var next = new java.util.HashMap<>(assignments);
next.remove(playerId);
return new Claim(claimId, ownerId, name, chunks, next, publicFlags, revision + 1);
}
public Claim withFlags(Set<ClaimFlag> flags) {
return new Claim(claimId, ownerId, name, chunks, assignments, flags, revision + 1);
}
public Claim transferTo(UUID newOwner) {
Objects.requireNonNull(newOwner, "newOwner");
if (newOwner.equals(ownerId)) throw new IllegalArgumentException("new owner must differ from current owner");
var next = new java.util.HashMap<>(assignments);
next.put(ownerId, ClaimRole.MANAGER);
next.put(newOwner, ClaimRole.OWNER);
return new Claim(claimId, newOwner, name, chunks, next, publicFlags, revision + 1);
}
private static boolean isContiguous(Set<ChunkKey> chunks) {
var reached = new java.util.HashSet<ChunkKey>();
var queue = new java.util.ArrayDeque<ChunkKey>();
queue.add(chunks.iterator().next());
while (!queue.isEmpty()) {
ChunkKey current = queue.remove();
if (!reached.add(current)) continue;
for (ChunkKey candidate : chunks) {
if (!reached.contains(candidate) && current.cardinallyAdjacent(candidate)) queue.add(candidate);
}
}
return reached.size() == chunks.size();
}
}