Skip to main content

Player Death System Troubleshooting Guide

Comprehensive troubleshooting guide for the player death system (overhauled in PR #1094, March 26, 2026).

Quick Diagnosis

Symptom: Player stuck in death animation, never respawns

Likely Causes:
  1. Death lock not cleared after respawn
  2. Database transaction deadlock (pre-PR #1094)
  3. Respawn timer not firing
  4. Death state desync between client and server
Quick Fix:
Permanent Fix: Update to latest version (PR #1094 or later).

Symptom: Equipment duplicates on death

Likely Causes:
  1. Post-transaction DB persist failed (equipment not cleared)
  2. Death lock not preventing reconnect inventory load
  3. Gravestone loot not properly cleared
Diagnosis:
Fix: PR #1094 added persist retry queue and death lock guards. Update to latest version.

Symptom: Items lost on death (not in gravestone or inventory)

Likely Causes:
  1. Gravestone spawned but entity destroyed prematurely
  2. Ground items despawned before player could loot
  3. Death lock cleared before items recovered
Diagnosis:
Recovery:

System Architecture

Death Flow (Safe Zone)

Two-Phase Persist Pattern

Why? SQLite deadlocks on nested transactions. The death transaction calls clearEquipmentAndReturn() and clearInventoryImmediate(), which each try to open their own transactions. Solution:
  1. Inside transaction: Clear in-memory state, skip DB persist
  2. After transaction: Persist to DB with retry queue
Crash Recovery: If server crashes between steps 1 and 2, death lock prevents reconnect inventory load. Items are not restored to player.

Persist Retry Queue

Purpose: Handle transient DB failures during post-transaction persist. Behavior:
  • Single retry per failure (no infinite loops)
  • Bounded to 100 entries (prevents unbounded growth)
  • Drained once per tick in processPendingRespawns()
  • Emits AUDIT_LOG event on retry failure
Monitoring:

Common Issues

Issue: Player respawns but kept items not returned

Diagnosis:
Causes:
  1. itemsKeptOnDeath Map cleared before respawn
  2. Death lock keptItems field empty
  3. addItemDirect() failed (inventory full, DB error)
Fix:

Issue: Gravestone shows duplicate items after looting

Diagnosis:
Causes:
  1. CORPSE_EMPTY event not firing (event lost)
  2. Gravestone entity not destroyed after looting
  3. lootItems not synced to client via modify()
Fix (PR #1094):
  • HeadstoneEntity.modify() now syncs lootItems from network data
  • PlayerDeathSystem.handleCorpseEmpty() destroys entity via EntityManager
  • Tick-based expiration fallback if CORPSE_EMPTY is lost

Issue: Death lock persists after respawn

Diagnosis:
Causes:
  1. clearDeathLock() not called after respawn
  2. CORPSE_EMPTY event never fired
  3. Server crashed before lock cleared
Fix:
Automatic Cleanup: Death locks older than 1 hour are auto-cleared on reconnect.

Issue: Player respawns during active duel

Diagnosis:
Causes:
  1. Duel respawn guard not active (pre-PR #1094)
  2. isPlayerInActiveDuel() returning false incorrectly
Fix (PR #1094):
  • handleRespawnRequest() blocks respawn during active duels
  • initiateRespawn() has defense-in-depth guard

Issue: Gravestone loot visible to all players

Diagnosis:
Causes:
  1. HeadstoneEntity.getNetworkData() includes lootItems (pre-PR #1094)
  2. Loot sent via broadcast instead of targeted packet
Fix (PR #1094):
  • lootItems stripped from getNetworkData() and serialize()
  • Only lootItemCount is broadcast
  • Full loot data sent via targeted corpseLoot packet on interaction

Monitoring & Alerting

Key Metrics

Death Lock Age:
Persist Retry Failures:
Reconnect with Active Death Lock:

AUDIT_LOG Events

The death system emits AUDIT_LOG events for ops visibility: Event Types:
  • DEATH_LOCK_RECONNECT_BLOCK - Player reconnected with active death lock (crash recovery)
  • DEATH_PERSIST_DESYNC - Equipment/inventory persist retry failed (possible item duplication)
  • DEATH_PERSIST_RETRY_QUEUE_FULL - Retry queue full (DB persistently unavailable)
Query:

Configuration

Death Constants

Tuning Parameters

Respawn Timing:
Gravestone TTL:
Death Lock Cleanup:

Testing

Unit Tests

DeathUtils.test.ts (51 tests):
  • sanitizeKilledBy() - XSS, Unicode, injection, edge cases
  • splitItemsForSafeDeath() - OSRS keep-3, stack handling, OOM regression
  • validatePosition() - Validation, clamping, invalid inputs
  • isPositionInBounds() - Bounds checking
  • isValidPositionNumber() - Finite number validation
  • getItemValue() - Manifest lookup
PlayerDeathFlow.test.ts (10 tests):
  • Duel guard blocks respawn
  • Death processing race guard
  • Tick-based respawn timing
  • Persist retry queue drain
  • PLAYER_DIEDPLAYER_SET_DEAD migration

Integration Tests

PvPDeath.integration.test.ts:
  • Full death flow with real server
  • Gravestone spawning and looting
  • Kept items returned on respawn
  • Death lock cleanup
SafeAreaDeathHandler.test.ts:
  • Gravestone TTL expiration
  • Tick-based cleanup
  • Item drop to ground after gravestone expires
WildernessDeathHandler.test.ts:
  • Immediate ground item drop
  • No gravestone in wilderness
  • All items dropped (no keep-3)

Recovery Procedures

Stuck Death Lock

Symptoms: Player can’t log in, or inventory is empty on login. Diagnosis:
Recovery:

Duplicate Equipment

Symptoms: Player has duplicate items after death. Diagnosis:
Recovery:
Prevention: PR #1094 added persist retry queue. Update to latest version.

Orphaned Gravestone

Symptoms: Gravestone persists after looting, shows stale items. Diagnosis:
Recovery:
Prevention: PR #1094 fixed gravestone cleanup via EntityManager.destroyEntity(). Update to latest version.

Database Schema

death_locks Table

Fields:
  • player_id: Player character ID (primary key)
  • gravestone_id: Gravestone entity ID (empty until spawned)
  • position_x/y/z: Death position
  • zone_type: “safe_area” or “wilderness”
  • item_count: Number of dropped items
  • items: Dropped items (for gravestone)
  • kept_items: Kept items (for respawn) - NEW in PR #1094
  • killed_by: Killer name (sanitized)
  • timestamp: Death timestamp (milliseconds)

Migration (PR #1094)

Event Reference

Deprecated Events

PLAYER_DIED (deprecated in PR #1094):
Migration:

New Events (PR #1094)

PLAYER_SET_DEAD:
DEATH_RECOVERED:
AUDIT_LOG:

Performance Tuning

Tick-Based Respawn

Advantages:
  • Deterministic timing (no setTimeout drift)
  • Server-authoritative (client can’t manipulate)
  • Efficient (single tick handler for all players)
Configuration:
Fallback: If TickSystem not available (client-side), uses setTimeout.

Persist Retry Queue

Tuning:
Monitoring:

Gravestone Cleanup

Tick-Based Expiration:
Event-Based Cleanup:

Security Considerations

Duel Escape Prevention

Exploit: Player could respawn during duel to escape with staked items. Fix (PR #1094):
Guards:
  • handleRespawnRequest() - Blocks manual respawn button
  • initiateRespawn() - Defense-in-depth guard

Position Validation

Exploit: Malicious client sends extreme position to teleport on death. Fix:

Killer Name Sanitization

Exploit: Malicious killer name with XSS/injection payload. Fix:

Changelog

March 26, 2026 (PR #1094)

  • Complete rewrite of death pipeline
  • Two-phase persist pattern (fixes SQLite deadlock)
  • OSRS keep-3 system for safe zone deaths
  • Gravestone privacy (loot hidden from broadcast)
  • Death lock crash recovery with kept items
  • Persist retry queue (bounded to 100 entries)
  • Duel respawn guard (prevents escape exploit)
  • Death processing guard (prevents respawn race)
  • Event migration (PLAYER_DIEDPLAYER_SET_DEAD/ENTITY_DEATH)
  • 61 new tests (DeathUtils + PlayerDeathFlow)
  • 23 files changed, 2,574 additions, 566 deletions