Add throttled player-position tracking, mod side (Phase 7b)
Adds PlayerPosition (common/protocol) and BackendConnection.sendPlayerPositions(),
wired into MCMapperMod.java: a new playerPositionIntervalTicks-throttled tick timer
(gated by playerTrackingEnabled) sends the full current overworld online-player
roster to the backend each interval, mirroring the "current state, not a diff"
philosophy of columns/sections — a player logging out just stops appearing next
send, no separate leave message needed.
Wire message: {"type":"player_positions","dimension":0,"players":[...]}.
Backend-side receive/relay + admin visibility toggle land in a follow-up commit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tKdPZt78zbPUZMXWzKEKt
This commit is contained in:
@@ -2,6 +2,7 @@ package com.octoturge.mcmapper.common;
|
||||
|
||||
import com.octoturge.mcmapper.common.protocol.DeltaEvent;
|
||||
import com.octoturge.mcmapper.common.protocol.LinkRequest;
|
||||
import com.octoturge.mcmapper.common.protocol.PlayerPosition;
|
||||
import com.octoturge.mcmapper.common.protocol.SectionData;
|
||||
import com.octoturge.mcmapper.common.protocol.WaypointShare;
|
||||
|
||||
@@ -28,6 +29,14 @@ public interface BackendConnection {
|
||||
/** Forwards one in-game chat message to the backend — see ChatBridge.OutboundSink's javadoc. */
|
||||
void sendChatMessage(String uuid, String username, String message);
|
||||
|
||||
/**
|
||||
* Sends the full current online-player roster for one dimension (Phase 7b) — see
|
||||
* {@link PlayerPosition}'s javadoc for why this is always the whole roster, not a diff. An
|
||||
* empty list is a meaningful, intentional send (everyone logged out), so callers should call
|
||||
* this every throttle tick rather than skipping it when there are no players.
|
||||
*/
|
||||
void sendPlayerPositions(int dimension, List<PlayerPosition> players);
|
||||
|
||||
/** Registers the callback for web-originated chat messages the backend relays back to us. */
|
||||
void setChatListener(ChatListener listener);
|
||||
|
||||
@@ -65,6 +74,10 @@ public interface BackendConnection {
|
||||
public void sendChatMessage(String uuid, String username, String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPlayerPositions(int dimension, List<PlayerPosition> players) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChatListener(ChatListener listener) {
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.octoturge.mcmapper.common;
|
||||
import com.octoturge.mcmapper.common.json.MiniJson;
|
||||
import com.octoturge.mcmapper.common.protocol.DeltaEvent;
|
||||
import com.octoturge.mcmapper.common.protocol.LinkRequest;
|
||||
import com.octoturge.mcmapper.common.protocol.PlayerPosition;
|
||||
import com.octoturge.mcmapper.common.protocol.SectionData;
|
||||
import com.octoturge.mcmapper.common.protocol.WaypointShare;
|
||||
import com.octoturge.mcmapper.common.ws.SimpleWebSocketClient;
|
||||
@@ -32,6 +33,8 @@ import java.util.function.Consumer;
|
||||
* mod -> api {"type":"chat","uuid":"...","username":"...","message":"..."}
|
||||
* api -> mod {"type":"chat","username":"...","message":"..."}
|
||||
* api -> mod {"type":"waypoint_share","name":"...","x":..,"y":..,"z":..,"dimension":..,"color":"#RRGGBB","format":"journeymap"|"xaero"}
|
||||
*
|
||||
* mod -> api {"type":"player_positions","dimension":0,"players":[{"uuid":"...","username":"...","x":..,"y":..,"z":..}]}
|
||||
* </pre>
|
||||
*
|
||||
* Phase 3: {@code link_request} is sent by {@code /mcmapper link}; {@code chat} both directions
|
||||
@@ -58,8 +61,16 @@ import java.util.function.Consumer;
|
||||
* last flush).
|
||||
*
|
||||
* No offline queue: deltas sent while disconnected are dropped rather than buffered — the
|
||||
* periodic reconciliation sweep (not yet built, see plan's Phase 7) is what's meant to catch
|
||||
* whatever a disconnect window missed, so buffering here would be solving the same problem twice.
|
||||
* periodic reconciliation sweep (see {@link com.octoturge.mcmapper.common.ReconciliationScheduler})
|
||||
* is what's meant to catch whatever a disconnect window missed, so buffering here would be
|
||||
* solving the same problem twice.
|
||||
*
|
||||
* Phase 7b: {@code player_positions} carries the full current online-player roster for one
|
||||
* dimension (not a diff), throttled on a separate timer from the delta flush — see
|
||||
* {@link PlayerPosition}'s javadoc. The backend's per-server {@code playerPositionsVisible}
|
||||
* admin toggle decides whether this gets relayed on to web viewers; it's independent of the
|
||||
* mod-local {@code playerTrackingEnabled} config, which decides whether the mod computes/sends
|
||||
* this at all.
|
||||
*
|
||||
* This class avoids a third-party JSON/WS library entirely (see {@link SimpleWebSocketClient}
|
||||
* and {@link MiniJson}'s javadoc) to keep the mod's classpath free of anything that would need
|
||||
@@ -270,6 +281,26 @@ public class DefaultBackendConnection implements BackendConnection {
|
||||
sendRaw("chat", msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPlayerPositions(int dimension, List<PlayerPosition> players) {
|
||||
if (!serverReady) return;
|
||||
List<Object> playerList = new ArrayList<>();
|
||||
for (PlayerPosition p : players) {
|
||||
Map<String, Object> obj = new LinkedHashMap<>();
|
||||
obj.put("uuid", p.uuid);
|
||||
obj.put("username", p.username);
|
||||
obj.put("x", (double) p.x);
|
||||
obj.put("y", (double) p.y);
|
||||
obj.put("z", (double) p.z);
|
||||
playerList.add(obj);
|
||||
}
|
||||
Map<String, Object> msg = new LinkedHashMap<>();
|
||||
msg.put("type", "player_positions");
|
||||
msg.put("dimension", (double) dimension);
|
||||
msg.put("players", playerList);
|
||||
sendRaw("player_positions", msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChatListener(ChatListener listener) {
|
||||
this.chatListener = listener;
|
||||
|
||||
@@ -13,4 +13,5 @@ public class MapperConfig {
|
||||
public int deltaFlushIntervalTicks = 20;
|
||||
public int reconciliationIntervalTicks = 20 * 60 * 5;
|
||||
public int reconciliationChunksPerSweep = 50;
|
||||
public int playerPositionIntervalTicks = 20 * 2;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.octoturge.mcmapper.common.protocol;
|
||||
|
||||
/**
|
||||
* One online player's throttled position (Phase 7b), block-granularity. Sent as the full current
|
||||
* roster on every flush (not a diff) — same "current state, not a diff" philosophy as
|
||||
* {@link DeltaEvent}'s columns — so a player logging out simply stops appearing in the next
|
||||
* roster rather than needing a separate leave message.
|
||||
*/
|
||||
public class PlayerPosition {
|
||||
public final String uuid;
|
||||
public final String username;
|
||||
public final int x;
|
||||
public final int y;
|
||||
public final int z;
|
||||
|
||||
public PlayerPosition(String uuid, String username, int x, int y, int z) {
|
||||
this.uuid = uuid;
|
||||
this.username = username;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user