Part of the Game Unification lane (EPIC #926/#927). Raised by Joey after the survey slice shipped: cloak/radar must be uncheatable — trust the server only.
The cloak is currently client-side theater. Server-side stealth/radar logic (RadarVisibilityService.CanDetect, RadarContactTracker) is only applied to the radar-contact HUD events. The authoritative entity streams ignore it:
SpaceZone.BroadcastEntityDeltas sends ALL ships + spacecraft to EVERY connection (ComputeBatchDeltaEvent(connectionId, spacecraft, ships, ...) — no filter). A cloaked ship's position/rotation/velocity/health streams to every client at up to 30 Hz; clients merely fade it visually. The Godot client even UPSERTS ships from unknown-id deltas, so a modified client renders every cloaked ship with zero effort.
Join roster leak: BaseZone.AddPlayerAsync broadcasts PlayerJoined (name + exact position) to everyone and sends the full existing-player roster to the joiner — stealth state ignored.
Target command accepts any entity id regardless of whether the observer can detect it (lock-through-cloak).
Not leaking (verified): StealthStateChanged is owner-only; radar contacts are server-computed; scan/survey validation is server-side.
Fix (server-authoritative visibility)
Per-connection visible-entity set in SpaceZone = {own ship + own spacecraft} ∪ {live radar contacts} (reuses RadarContactTracker — radar IS the sensor model; radar off = dark, matching the existing contact-blanking rule).
BroadcastEntityDeltas filters per connection; per-entity delta state cleared on visibility transitions so reappearing entities get a full snapshot.
Visibility transitions emit per-observer PlayerJoined/PlayerLeft (ships) and spacecraft announce/remove equivalents — clients already handle these; ghosts stay on the radar-contact channel (last-known only, no live deltas).
Space join path stops broadcasting/rostering unfiltered; announcements flow from the first visibility tick (~100 ms).
Target (Entity kind) rejected unless the target is in the observer's visible set.
Remaining lower-bandwidth channels to review in a follow-up: DamageDealt/HealthManaUpdate/beam + projectile events referencing entity ids of undetected ships (positions not included; acceptable short-term, listed for completeness).
Part of the **Game Unification** lane (EPIC #926/#927). Raised by Joey after the survey slice shipped: cloak/radar must be uncheatable — **trust the server only**.
## Audit findings (spikersoft-backend master, 2026-08-06)
**The cloak is currently client-side theater.** Server-side stealth/radar logic (`RadarVisibilityService.CanDetect`, `RadarContactTracker`) is only applied to the radar-contact HUD events. The authoritative entity streams ignore it:
1. **`SpaceZone.BroadcastEntityDeltas` sends ALL ships + spacecraft to EVERY connection** (`ComputeBatchDeltaEvent(connectionId, spacecraft, ships, ...)` — no filter). A cloaked ship's position/rotation/velocity/health streams to every client at up to 30 Hz; clients merely fade it visually. The Godot client even UPSERTS ships from unknown-id deltas, so a modified client renders every cloaked ship with zero effort.
2. **Join roster leak**: `BaseZone.AddPlayerAsync` broadcasts `PlayerJoined` (name + exact position) to everyone and sends the full existing-player roster to the joiner — stealth state ignored.
3. `Target` command accepts any entity id regardless of whether the observer can detect it (lock-through-cloak).
Not leaking (verified): `StealthStateChanged` is owner-only; radar contacts are server-computed; scan/survey validation is server-side.
## Fix (server-authoritative visibility)
- Per-connection visible-entity set in SpaceZone = {own ship + own spacecraft} ∪ {live radar contacts} (reuses `RadarContactTracker` — radar IS the sensor model; radar off = dark, matching the existing contact-blanking rule).
- `BroadcastEntityDeltas` filters per connection; per-entity delta state cleared on visibility transitions so reappearing entities get a full snapshot.
- Visibility transitions emit per-observer `PlayerJoined`/`PlayerLeft` (ships) and spacecraft announce/remove equivalents — clients already handle these; ghosts stay on the radar-contact channel (last-known only, no live deltas).
- Space join path stops broadcasting/rostering unfiltered; announcements flow from the first visibility tick (~100 ms).
- `Target` (Entity kind) rejected unless the target is in the observer's visible set.
Remaining lower-bandwidth channels to review in a follow-up: `DamageDealt`/`HealthManaUpdate`/beam + projectile events referencing entity ids of undetected ships (positions not included; acceptable short-term, listed for completeness).
Code:spikersoft-backend@98102023 — the fix is on master via PR #525 (5c04ed99, merge c994553b, "fix(gameserver): server-authoritative visibility — cloaked ships never leave the server (#982)").
All four bullets of the fix section are implemented:
per-connection visible set + filtered broadcast — SpikerSoft.GameServer/Zones/SpaceZone.cs:759-780
(IsEntityVisibleTo at :1005, scratch lists filtered before ComputeBatchDeltaEvent at :781)
per-entity delta state cleared on visibility transitions — SpaceZone.cs:956, :989 → NetworkDeltaCompression.ClearClientEntityState (:238)
no join-roster broadcast in space — SpaceZone.cs:34 overrides BaseZone.RevealPlayersOnJoin to false; honoured at BaseZone.cs:932 (camp/dungeon unchanged)
8 new SpikerSoft.GameServer.Tests/Zones/SpaceZoneVisibilityTests.cs drive the real pipeline
(radar tick → transitions → filtered broadcast)
Live:docker service ps spikersoft-gameserver_spikersoft-gameserver → 1/1 Running on SERVER,
image redeployed 2026-08-07 12:25 UTC, i.e. after the merge (2026-08-06 20:07 UTC). The deployed
build therefore carries the fix.
Not migrated: the ticket's own fix scope is done and live.
The one thing this ticket deferred — "remaining lower-bandwidth channels to review in a follow-up: DamageDealt/HealthManaUpdate/beam + projectile events referencing entity ids of undetected ships" —
has been filed separately as spikerj/spikersoft-backend#547 so it isn't lost when this closes.
— Opus 5 Agent
Verified complete 2026-08-07 — closing.
- **Code:** `spikersoft-backend@98102023` — the fix is on master via PR #525 (`5c04ed99`, merge `c994553b`,
*"fix(gameserver): server-authoritative visibility — cloaked ships never leave the server (#982)"*).
All four bullets of the fix section are implemented:
- per-connection visible set + filtered broadcast — `SpikerSoft.GameServer/Zones/SpaceZone.cs:759-780`
(`IsEntityVisibleTo` at `:1005`, scratch lists filtered before `ComputeBatchDeltaEvent` at `:781`)
- per-entity delta state cleared on visibility transitions — `SpaceZone.cs:956`, `:989` →
`NetworkDeltaCompression.ClearClientEntityState` (`:238`)
- no join-roster broadcast in space — `SpaceZone.cs:34` overrides `BaseZone.RevealPlayersOnJoin` to
`false`; honoured at `BaseZone.cs:932` (camp/dungeon unchanged)
- `Target` (Entity kind) rejected unless visible — `SpaceZone.cs:5164`
- 8 new `SpikerSoft.GameServer.Tests/Zones/SpaceZoneVisibilityTests.cs` drive the real pipeline
(radar tick → transitions → filtered broadcast)
- **Live:** `docker service ps spikersoft-gameserver_spikersoft-gameserver` → 1/1 Running on SERVER,
image redeployed **2026-08-07 12:25 UTC**, i.e. after the merge (2026-08-06 20:07 UTC). The deployed
build therefore carries the fix.
- **Shipped by:** spikerj/spikersoft-backend PR #525 / `5c04ed99` / merge `c994553b`.
Not migrated: the ticket's own fix scope is done and live.
The one thing this ticket deferred — *"remaining lower-bandwidth channels to review in a follow-up:
`DamageDealt`/`HealthManaUpdate`/beam + projectile events referencing entity ids of undetected ships"* —
has been filed separately as **spikerj/spikersoft-backend#547** so it isn't lost when this closes.
— Opus 5 Agent
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part of the Game Unification lane (EPIC #926/#927). Raised by Joey after the survey slice shipped: cloak/radar must be uncheatable — trust the server only.
Audit findings (spikersoft-backend master, 2026-08-06)
The cloak is currently client-side theater. Server-side stealth/radar logic (
RadarVisibilityService.CanDetect,RadarContactTracker) is only applied to the radar-contact HUD events. The authoritative entity streams ignore it:SpaceZone.BroadcastEntityDeltassends ALL ships + spacecraft to EVERY connection (ComputeBatchDeltaEvent(connectionId, spacecraft, ships, ...)— no filter). A cloaked ship's position/rotation/velocity/health streams to every client at up to 30 Hz; clients merely fade it visually. The Godot client even UPSERTS ships from unknown-id deltas, so a modified client renders every cloaked ship with zero effort.BaseZone.AddPlayerAsyncbroadcastsPlayerJoined(name + exact position) to everyone and sends the full existing-player roster to the joiner — stealth state ignored.Targetcommand accepts any entity id regardless of whether the observer can detect it (lock-through-cloak).Not leaking (verified):
StealthStateChangedis owner-only; radar contacts are server-computed; scan/survey validation is server-side.Fix (server-authoritative visibility)
RadarContactTracker— radar IS the sensor model; radar off = dark, matching the existing contact-blanking rule).BroadcastEntityDeltasfilters per connection; per-entity delta state cleared on visibility transitions so reappearing entities get a full snapshot.PlayerJoined/PlayerLeft(ships) and spacecraft announce/remove equivalents — clients already handle these; ghosts stay on the radar-contact channel (last-known only, no live deltas).Target(Entity kind) rejected unless the target is in the observer's visible set.Remaining lower-bandwidth channels to review in a follow-up:
DamageDealt/HealthManaUpdate/beam + projectile events referencing entity ids of undetected ships (positions not included; acceptable short-term, listed for completeness).Verified complete 2026-08-07 — closing.
spikersoft-backend@98102023— the fix is on master via PR #525 (5c04ed99, mergec994553b,"fix(gameserver): server-authoritative visibility — cloaked ships never leave the server (#982)").
All four bullets of the fix section are implemented:
SpikerSoft.GameServer/Zones/SpaceZone.cs:759-780(
IsEntityVisibleToat:1005, scratch lists filtered beforeComputeBatchDeltaEventat:781)SpaceZone.cs:956,:989→NetworkDeltaCompression.ClearClientEntityState(:238)SpaceZone.cs:34overridesBaseZone.RevealPlayersOnJointofalse; honoured atBaseZone.cs:932(camp/dungeon unchanged)Target(Entity kind) rejected unless visible —SpaceZone.cs:5164SpikerSoft.GameServer.Tests/Zones/SpaceZoneVisibilityTests.csdrive the real pipeline(radar tick → transitions → filtered broadcast)
docker service ps spikersoft-gameserver_spikersoft-gameserver→ 1/1 Running on SERVER,image redeployed 2026-08-07 12:25 UTC, i.e. after the merge (2026-08-06 20:07 UTC). The deployed
build therefore carries the fix.
5c04ed99/ mergec994553b.Not migrated: the ticket's own fix scope is done and live.
The one thing this ticket deferred — "remaining lower-bandwidth channels to review in a follow-up:
DamageDealt/HealthManaUpdate/beam + projectile events referencing entity ids of undetected ships" —has been filed separately as spikerj/spikersoft-backend#547 so it isn't lost when this closes.
— Opus 5 Agent