6.7 KiB
type, title, description, status
| type | title | description | status |
|---|---|---|---|
| User Story | US-002: Rejoin without a visible identity | Let an unlocked player turn a qualifying invisible disconnect into one identity-concealed session. | done |
US-002: Rejoin without a visible identity
As an unlocked player, I want to disconnect while invisibility from a potion I drank is active so that my identity is concealed throughout my next session.
Acceptance criteria
- An unlocked player becomes eligible for a concealed login only by disconnecting while an invisibility effect from a potion they directly drank remains active.
- A player who has not unlocked stealth cannot prepare a concealed login.
- An ordinary disconnect without an active qualifying effect clears any preparation for the next login.
- On a prepared login, no public join announcement is shown.
- When a concealed player disconnects, no public quit or disconnect announcement is shown.
- Ordinary players' quit messages remain unchanged.
- Concealment is checked before disconnect cleanup so announcement suppression is reliable.
- Throughout the concealed session, the player is absent from every other player's tab list, including administrators' tab lists.
- The multiplayer server list's online-player count excludes currently concealed players.
- Concealed players are excluded from any player-name sample shown for the server-list count, while ordinary players remain represented.
- The public count never becomes negative, and the configured maximum-player count remains unchanged.
- Server-list concealment changes only the public ping response and does not alter actual online-player state or gameplay.
- Server-list filtering remains error-free when Purpur represents player samples with native
NameAndIdvalues rather than MojangGameProfilevalues. - A server-ping compatibility failure leaves the original response usable and does not produce repeated unhandled listener exceptions.
- By default, concealed players are excluded from sleep-percentage calculations.
- When the sleep-count policy is
include, concealed players count normally; ordinary players are never modified by either policy. - A player's previous sleeping-ignore state is restored when concealment ends, the player disconnects or is reset, the policy changes to
include, or the plugin disables. - Policy changes apply immediately to currently concealed players.
- Throughout the concealed session, the overhead name is rendered as scrambled magic text, including for administrators, while the physical character stays visible.
- Concealed usernames are excluded from outgoing command suggestions, including
/msg,/tell, and/w; manually addressing a known username remains possible. - Default chat display names and vanilla private-message and advancement name components are scrambled without changing message content or signed chat bodies.
- Masked message name components do not expose the original identity through hover or click actions.
- Previous display names and ordinary suggestions return when concealment ends; ordinary players remain unaffected.
- The concealed player's physical character remains visible in the world and retains ordinary movement, interaction, combat, and permission behavior.
- The concealed player receives a private message explaining that stealth is active for the session.
- Concealment lasts until the player disconnects and is handled predictably across death and plugin reload or disable events.
- Disconnecting consumes the current concealed session; another concealed login requires another qualifying potion and qualifying disconnect.
- Merely owning the unlock never conceals an ordinary login or carries concealment automatically into a later session.
- Prepared-login state survives a server restart between the qualifying disconnect and the next login.
Validation
Automated tests verify unlocked and locked disconnect transitions, ordinary-disconnect clearing, one-login consumption, concealed join and quit announcement suppression, preservation of ordinary announcements, private activation messaging, ordinary-login presentation, tab removal for existing and new observers, overhead-name suppression, active concealed-session tracking, ProtocolLib-independent native player-sample filtering, guarded public server-list count adjustment, nonnegative counts, unchanged maximum capacity, default sleep-count exclusion, immediate policy changes, restoration of prior sleeping-ignore state, and the absence of entity-hiding calls. ProtocolLib is declared as a required dependency, prepared state round trips through YAML, and ./gradlew clean check jar passes.
Identity masking verification (2026-09-06): ./gradlew --offline clean check jar passed all 65 tests and OKF validation. Tests cover semantic name slots, preserved message content, stripped identifying actions, reflected suggestion entries and chat-bound records, safe packet failure handling, immutable name snapshots, overhead formatting, and display-name restoration after repeated concealment. Presentation tests first failed to compile against the missing snapshot API, then passed after implementation. These are automated unit checks, not a live-client smoke test.
Identity masking scope
Masking applies to active concealed sessions, not every potion effect. Outgoing username suggestions are filtered across commands; entering a known username manually still works. Default Bukkit chat uses a temporary scrambled Anonymous display name. Vanilla translated announcements and chat-type bound names are masked at the packet boundary without editing signed message bodies. The overhead tag uses scoreboard magic formatting on the profile name, not a profile rename; this is visual concealment, not anonymity against modified clients.
Custom chat formats that ignore Bukkit display names, nicknames, custom scoreboards, and third-party messaging plugins may need dedicated integration. Unsupported packet layouts retain the original packet and emit one warning per packet type rather than interrupting chat delivery. Live-server/client compatibility still requires a smoke test, particularly after Minecraft or ProtocolLib upgrades.
Implementation: identity presentation, packet listener, and semantic name masking.