Skip to content

fix(presence): release the session when a login never arrives#25

Merged
hbrombeer merged 1 commit into
mainfrom
fix/abandoned-prelogin-session
Jul 21, 2026
Merged

fix(presence): release the session when a login never arrives#25
hbrombeer merged 1 commit into
mainfrom
fix/abandoned-prelogin-session

Conversation

@hbrombeer

Copy link
Copy Markdown
Member

Who decides that a player is online

Velocity's connection list is the source of truth. plugin-player registers the session at PreLoginEvent, heartbeats every online player from proxy.allPlayers, and deletes the row at DisconnectEvent; service-player expires anything unheartbeated after grounds.player.sessions.ttl (90s) with a reaper every 30s.

The pair pre-login → disconnect has a hole: Velocity only fires DisconnectEvent for connections that finished logging in. A client that dies in between — expired Mojang session, crash, pulled cable — leaves its row behind with nothing to remove it.

Live evidence from a workspace proxy:

17:50:20 [plugin-player]: Player session created (playerId=9a122510…, username=dahendriik, ACCEPTED)
17:50:20 [initial connection] /…:38474 has disconnected
17:50:57 [initial connection] /…:46002 has disconnected: You are already online.
17:51:50 [initial connection] /…:41408 has disconnected: You are already online.
17:52:10 [PlayerSessionCleanup]: cleanup completed (removed=1)

Locked out of their own account for 90 seconds, then healed by the TTL. The reaper is doing its job; the session simply should never have outlived the connection.

Change

The proxy that created the row watches it. After a grace period (15s — long enough for auth, config phase and the first server connect), if no Player exists for that id, the session is released.

  • PostLoginEvent hands ownership to the disconnect path, so a player who joined normally is never touched.
  • The pending marker is consumed on release, so a late timer cannot log out someone who reconnected in the meantime.
  • A duplicate login denied at pre-login never becomes pending — that is what stops this from logging out the player who is legitimately online.

The decision is a pure function (shouldReleaseAbandonedLogin) so it is tested without mocking Velocity.

Verified

  • ./gradlew build green
  • four new tests: released when abandoned, kept when the player joined, never released for a session this proxy did not create, and released only once

🤖 Generated with Claude Code

https://claude.ai/code/session_015GE1tqMhu6W7g6Ck5fv8h9

Presence is created at PreLoginEvent and removed at DisconnectEvent, and that
pair has a hole: Velocity only fires DisconnectEvent for connections that
finished logging in. A client that dies in between — an expired Mojang session,
a crash, a pulled cable — leaves its row behind with nothing to remove it, and
every retry is refused with "you are already online" until the 90s TTL reaps it.

Seen in a live workspace: the session was created at 17:50:20, the connection
dropped in the same second, and the next two attempts were kicked at 17:50:57
and 17:51:50 — the player was locked out of their own account for 90 seconds
after doing nothing wrong.

The proxy that created the row now watches it: no player behind that id after a
grace period, no session. PostLogin hands ownership to the disconnect path, so
a player who joined normally is never touched, and the pending marker is
consumed on release so a reconnect in the meantime cannot be logged out by a
late timer. A duplicate login that pre-login denied never becomes pending,
which is what keeps this from kicking the player who is legitimately online.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015GE1tqMhu6W7g6Ck5fv8h9
@hbrombeer
hbrombeer merged commit 1440647 into main Jul 21, 2026
3 checks passed
@hbrombeer
hbrombeer deleted the fix/abandoned-prelogin-session branch July 21, 2026 18:45
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