Skip to content

Add player presence reconciliation for proxy servers#9

Open
frederickbaier wants to merge 1 commit intofeat/platformfrom
feat/player-sync
Open

Add player presence reconciliation for proxy servers#9
frederickbaier wants to merge 1 commit intofeat/platformfrom
feat/player-sync

Conversation

@frederickbaier
Copy link

Why

The controller already implements player presence reconciliation — it periodically sends compare requests to each proxy to detect drift between its known player state and reality. This handles two failure modes: lost disconnect events (stale ghost players in the controller) and lost login events (connected players invisible to the network). Until now, the proxy-side counterpart to answer these requests did not exist.

What this PR does

Proxy-side presence responder

Implements the proxy-side handler for the controller's presence reconciliation protocol:

  1. The controller sends a PresenceCompareRequest containing an FNV-32a hash of the players it believes are on a given proxy
  2. The proxy computes the same hash over its actual connected players
  3. If the hashes match, the proxy responds with match: true — no further action needed
  4. If they differ, the proxy responds with the full player snapshot list, allowing the controller to reconcile: re-triggering logins for unknown players and cleaning up stale entries

The hash-first approach minimizes NATS traffic during normal operation — the full player list is only transferred when there's actually a discrepancy.

New components:

  • ProxyPresenceResponder — subscribes to {networkId}.server.{serverId}.presence.compare and handles compare requests. Owned by the proxy plugins (Velocity / BungeeCord) directly, since only proxies need to respond — this avoids every CloudApi.create() caller (lobby plugins, minigame plugins, etc.) creating redundant NATS subscriptions.
  • ProxyPresenceTracker — tracks stable per-player metadata (login timestamp, session ID) that isn't available from the platform API at snapshot time
  • ProxyPresencePlayer / ProxyPresencePlayerProvider — immutable player snapshot and provider interface

Other improvements

  • CloudApi extends AutoCloseableCloudApiImpl.close() properly shuts down cache, event listeners, and the NATS connection, guarded by AtomicBoolean to prevent double-close
  • PlayerIntegration accepts CloudApiImpl directly — removes the runtime type check and cast, since only proxy plugins use it
  • SimpleCloudRuntime utility — centralizes environment variable access (SIMPLECLOUD_UNIQUE_ID, SIMPLECLOUD_GROUP, SIMPLECLOUD_NUMERICAL_ID) replacing scattered System.getenv() calls
  • BungeeCord login fix — was incorrectly passing connection.getListener().getDefaultServer() where the locale parameter was expected

Add ProxyPresenceResponder to answer controller presence-compare
requests, allowing the controller to detect ghost players by comparing
its known player set against the proxy's actual connected players via
hash comparison over NATS. ProxyPresenceTracker maintains stable
per-player metadata (login timestamp, session ID) for accurate snapshots.

The responder is owned by proxy plugins (Velocity/BungeeCord) rather
than CloudApiImpl, ensuring only one NATS subscription per proxy.
CloudApi now extends AutoCloseable with proper shutdown. PlayerIntegration
simplified to accept CloudApiImpl directly.
@frederickbaier frederickbaier changed the base branch from main to feat/platform March 9, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant