Claim protection from a local index: revision-polled panel endpoint, no per-block network checks

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DjMbLQujBHunCCu5GpsHaT
This commit is contained in:
Claude committed 2026-09-30 17:51:05 +00:00
1 parent 78da10364f
commit 5defda1128
10 files changed
+442 -127

No files matched your search

@@ -0,0 +1,93 @@
package net.scopenet.integration;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.sun.net.httpserver.HttpServer;
import org.junit.jupiter.api.*;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.*;
class ClaimIndexTest {
private static final UUID MEMBER = UUID.fromString("b50ad385-829d-3141-a216-7e7d7539ba7f");
private static final UUID OUTSIDER = UUID.randomUUID();
private static final String INDEX = """
{"revision":"7","unchanged":false,
"guilds":[{"id":"g1","name":"Iron Fortress","tag":"IRON"}],
"claims":[["minecraft:overworld",3,-4,0],["minecraft:the_nether",-1,-1,0]],
"members":{"g1":["b50ad385-829d-3141-a216-7e7d7539ba7f","not-a-uuid"]}}""";
private static JsonObject json(String s) { return JsonParser.parseString(s).getAsJsonObject(); }
@Test void answersLocallyAndTreatsWildernessAsOpen() {
ClaimIndex index = new ClaimIndex();
assertFalse(index.loaded());
assertTrue(index.check("minecraft:overworld", 3, -4, OUTSIDER).allowed(), "nothing is enforced before the first load");
assertTrue(index.apply(json(INDEX)));
assertEquals("7", index.revision());
assertEquals(2, index.claimCount());
ChunkCheckResult mine = index.check("minecraft:overworld", 3, -4, MEMBER);
assertTrue(mine.claimed() && mine.allowed());
ChunkCheckResult theirs = index.check("minecraft:overworld", 3, -4, OUTSIDER);
assertTrue(theirs.claimed() && !theirs.allowed());
assertEquals("IRON", theirs.guildTag());
assertFalse(index.check("minecraft:overworld", 3, -3, OUTSIDER).claimed());
assertFalse(index.check("minecraft:the_end", 3, -4, OUTSIDER).claimed(), "claims are per dimension");
assertTrue(index.isClaimed("minecraft:the_nether", -1, -1));
assertFalse(index.isClaimed("minecraft:overworld", -1, -1));
}
@Test void negativeCoordinatesDoNotCollide() {
ClaimIndex index = new ClaimIndex();
index.apply(json("""
{"revision":"1","guilds":[{"id":"g","name":"A","tag":"A"}],
"claims":[["d",-1,0,0],["d",0,-1,0],["d",-1,-1,0]],"members":{}}"""));
assertTrue(index.isClaimed("d", -1, 0));
assertTrue(index.isClaimed("d", 0, -1));
assertTrue(index.isClaimed("d", -1, -1));
assertFalse(index.isClaimed("d", 0, 0));
assertFalse(index.isClaimed("d", 1, -1));
}
@Test void unchangedResponsesKeepTheCurrentCopy() {
ClaimIndex index = new ClaimIndex();
index.apply(json(INDEX));
assertFalse(index.apply(json("{\"revision\":\"7\",\"unchanged\":true}")));
assertEquals(2, index.claimCount());
}
@Test void pollingSendsTheRevisionAndSurvivesPanelOutages() throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
AtomicInteger calls = new AtomicInteger();
StringBuilder lastBody = new StringBuilder();
server.createContext("/api/server/v1/guilds/claim-index", exchange -> {
String body = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8);
lastBody.setLength(0);
lastBody.append(body);
int n = calls.incrementAndGet();
byte[] out = (n == 1 ? INDEX : n == 2 ? "{\"revision\":\"7\",\"unchanged\":true}" : "boom").getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(n >= 3 ? 503 : 200, out.length);
exchange.getResponseBody().write(out);
exchange.close();
});
server.start();
try {
PanelClient client = new PanelClient(Settings.of("http://127.0.0.1:" + server.getAddress().getPort(), "sn_" + "a".repeat(40)));
assertTrue(client.refreshClaims());
assertEquals("{}", lastBody.toString(), "first poll has no revision");
assertFalse(client.refreshClaims());
assertEquals("{\"revision\":\"7\"}", lastBody.toString());
assertThrows(java.io.IOException.class, client::refreshClaims);
// The last good copy still protects land while the panel is down.
assertFalse(client.checkChunk("minecraft:overworld", 3, -4, OUTSIDER).allowed());
assertTrue(client.checkChunk("minecraft:overworld", 3, -4, MEMBER).allowed());
assertTrue(client.isClaimed("minecraft:overworld", 3, -4));
client.close();
} finally {
server.stop(0);
}
}
}