Map: game servers send tiles, players and overlay; BlueMap and LiveMapSync removed from the Java side
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DjMbLQujBHunCCu5GpsHaT
This commit is contained in:
31 files changed
+268
-596
No files matched your search
@@ -20,7 +20,8 @@ public final class Integration implements AutoCloseable {
|
||||
private final AtomicBoolean syncing = new AtomicBoolean();
|
||||
private volatile boolean closed;
|
||||
private volatile Consumer<JsonArray> notificationHandler;
|
||||
private volatile LiveMapSync liveMap;
|
||||
private volatile net.scopenet.worldmap.WorldMapSync map;
|
||||
private volatile java.util.function.Supplier<String> mapOverlay;
|
||||
private boolean registered;
|
||||
private JsonObject pending;
|
||||
private long nextSync;
|
||||
@@ -144,17 +145,34 @@ public final class Integration implements AutoCloseable {
|
||||
this.notificationHandler = handler;
|
||||
}
|
||||
|
||||
/** Start sending player positions for the in-app BlueMap territory view. */
|
||||
public void enableLiveMap() {
|
||||
if (closed || liveMap != null) return;
|
||||
liveMap = new LiveMapSync(client);
|
||||
liveMap.start();
|
||||
/**
|
||||
* Start the SCOPENET Map: render this world's region files to tiles and send them, player positions and the claims
|
||||
* overlay to the panel. {@code worldRoot} is the level folder (e.g. {@code world}); {@code cacheDir} holds the rendered tiles.
|
||||
*/
|
||||
public void enableMap(java.nio.file.Path worldRoot, java.nio.file.Path cacheDir) {
|
||||
if (closed || map != null) return;
|
||||
map = new net.scopenet.worldmap.WorldMapSync(client, worldRoot, cacheDir, log);
|
||||
if (mapOverlay != null) map.setOverlay(mapOverlay);
|
||||
map.start();
|
||||
}
|
||||
|
||||
/** Where the map gets its claims and pins (JSON text); may be called before or after {@link #enableMap}. */
|
||||
public void setMapOverlay(java.util.function.Supplier<String> overlay) {
|
||||
mapOverlay = overlay;
|
||||
net.scopenet.worldmap.WorldMapSync current = map;
|
||||
if (current != null) current.setOverlay(overlay);
|
||||
}
|
||||
|
||||
/** Hand over a snapshot of player positions; call about once a second on the server thread. */
|
||||
public void liveMapPlayers(java.util.List<LiveMapSync.Player> players) {
|
||||
LiveMapSync map = liveMap;
|
||||
if (map != null) map.offerPlayers(players);
|
||||
public void mapPlayers(java.util.List<net.scopenet.worldmap.WorldMapSync.Player> players) {
|
||||
net.scopenet.worldmap.WorldMapSync current = map;
|
||||
if (current != null) current.offerPlayers(players);
|
||||
}
|
||||
|
||||
/** The world was just saved: pick up its changed regions soon. */
|
||||
public void mapSaved() {
|
||||
net.scopenet.worldmap.WorldMapSync current = map;
|
||||
if (current != null) current.requestScan();
|
||||
}
|
||||
|
||||
public ChunkCheckResult checkChunk(String dimension, int chunkX, int chunkZ, UUID uuid) {
|
||||
@@ -173,8 +191,8 @@ public final class Integration implements AutoCloseable {
|
||||
|
||||
@Override public void close() {
|
||||
closed = true;
|
||||
LiveMapSync map = liveMap;
|
||||
if (map != null) map.close();
|
||||
net.scopenet.worldmap.WorldMapSync current = map;
|
||||
if (current != null) current.close();
|
||||
client.close();
|
||||
loginWorker.shutdownNow();
|
||||
activity.leaveAll();
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
package net.scopenet.integration;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** Sends live player positions to the panel for the in-app BlueMap territory view. */
|
||||
public final class LiveMapSync implements AutoCloseable {
|
||||
public record Player(String uuid, String name, String dimension, double x, double y, double z, float yaw, float pitch) {}
|
||||
|
||||
private final PanelClient client;
|
||||
private final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor(task -> {
|
||||
Thread thread = new Thread(task, "scopenet-positions");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
private volatile List<Player> players = List.of();
|
||||
private volatile boolean changed;
|
||||
private volatile boolean closed;
|
||||
private long lastSent;
|
||||
|
||||
public LiveMapSync(PanelClient client) { this.client = client; }
|
||||
|
||||
public void start() { worker.scheduleWithFixedDelay(this::sendSafely, 1, 1, TimeUnit.SECONDS); }
|
||||
|
||||
public void offerPlayers(List<Player> next) {
|
||||
if (!next.equals(players)) {
|
||||
players = List.copyOf(next);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void sendSafely() {
|
||||
if (closed) return;
|
||||
long now = System.nanoTime();
|
||||
if (!changed && now - lastSent < TimeUnit.SECONDS.toNanos(10)) return;
|
||||
JsonArray list = new JsonArray();
|
||||
for (Player p : players) {
|
||||
JsonObject entry = new JsonObject();
|
||||
entry.addProperty("uuid", p.uuid());
|
||||
entry.addProperty("name", p.name());
|
||||
entry.addProperty("dimension", p.dimension());
|
||||
entry.addProperty("x", p.x());
|
||||
entry.addProperty("y", p.y());
|
||||
entry.addProperty("z", p.z());
|
||||
entry.addProperty("yaw", p.yaw());
|
||||
entry.addProperty("pitch", p.pitch());
|
||||
list.add(entry);
|
||||
}
|
||||
JsonObject body = new JsonObject();
|
||||
body.add("players", list);
|
||||
try {
|
||||
client.post("livemap/players", body);
|
||||
changed = false;
|
||||
lastSent = now;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception ignored) {
|
||||
// Keep the latest snapshot and retry on the next interval.
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void close() {
|
||||
closed = true;
|
||||
worker.shutdownNow();
|
||||
}
|
||||
}
|
||||
@@ -107,12 +107,12 @@ public record Settings(
|
||||
);
|
||||
}
|
||||
|
||||
/** Local opt-out for the central live map (the panel also has a per-server switch). */
|
||||
public static boolean liveMapEnabled(Path path) {
|
||||
/** Local opt-out for the SCOPENET Map (the panel also has a per-server switch). */
|
||||
public static boolean mapEnabled(Path path) {
|
||||
try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
|
||||
Properties properties = new Properties();
|
||||
properties.load(reader);
|
||||
return Boolean.parseBoolean(properties.getProperty("livemap.enabled", "true"));
|
||||
return Boolean.parseBoolean(properties.getProperty("map.enabled", properties.getProperty("livemap.enabled", "true")));
|
||||
} catch (IOException e) {
|
||||
return true;
|
||||
}
|
||||
@@ -139,9 +139,9 @@ public record Settings(
|
||||
essentials.enabled=true
|
||||
economy.enabled=true
|
||||
|
||||
# Send world chunks and player positions to the panel's central Live Map.
|
||||
# It only runs when Live Map is also enabled for this server in the panel.
|
||||
livemap.enabled=true
|
||||
# Draw this world on the SCOPENET Map: tiles, player positions, claims and pins go to the panel.
|
||||
# It only runs when the map is also enabled for this server in the panel.
|
||||
map.enabled=true
|
||||
""";
|
||||
Files.writeString(path, defaultProps, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user