Player Death and Respawn
Player death is one of the most important events in Unturned gameplay. When a player dies, a sequence of events fires: death event, inventory drop, respawn timer, and revival. RocketMod exposes several events along this sequence, allowing plugins to intercept deaths, modify death behavior, manage respawn, and track death statistics.
This article covers the complete death-to-respawn pipeline. It explains each event in the sequence, the order they fire, how to handle inventory on death, how to manage respawn locations and timers, and how to implement death-related gameplay features such as death messages, death penalties, and revive systems.
57 Studios operates multiple Unturned servers with custom death mechanics including death penalties, respawn timers, and custom death messages. The patterns documented here are drawn from production plugin authoring experience.
Prerequisites
- A working RocketMod installation. See RocketMod and OpenMod Plugin Basics.
- Visual Studio with C# development workload.
- Familiarity with RocketMod event subscription patterns.
- Understanding of the damage system (see Player Damage).
What you'll learn
- The complete death-to-respawn sequence and what fires when.
- How to subscribe to death and respawn events.
- How death cause and killer information is provided.
- How inventory drops work during the death sequence.
- How to manage respawn locations and timers.
- How to implement death messages, death penalties, and revive systems.
- Best practices for death handling in production plugins.
Death sequence overview
When a player's health reaches zero, the following sequence occurs:
- Fatal damage event — The player takes damage that reduces health to zero.
- OnPlayerDeath fires — RocketMod's death event fires. The player is still alive at this point, and their inventory is intact.
- Inventory drop — Unturned drops the player's items onto the ground (if the server has drop-on-death enabled).
- OnPlayerDead fires — The player is now fully dead. The death screen appears.
- Respawn timer — The player waits for the configured respawn delay.
- OnPlayerRevive fires — The player is respawned at their bed, home point, or a random spawn location.
- Player is alive — Normal gameplay resumes.
Event order and inventory state
Understanding exactly when inventory drops relative to the death events is critical for plugins that manipulate items on death. The OnPlayerDeath event fires before the player's inventory drops to the ground. The OnPlayerDead event fires after the inventory has been dropped.
This means:
- In
OnPlayerDeath, the player's inventory is still intact. You can read items, remove items, or save item data before they drop. - In
OnPlayerDead, the inventory has already been dropped. The player's inventory is empty. - Adding items to the player's inventory in
OnPlayerDeathmay cause those items to also be dropped when the inventory drop occurs moments later.
Death event fires before inventory drop
The OnPlayerDeath event fires during the death sequence, specifically before the player's inventory items are dropped to the ground. This gives plugins a window to interact with the player's items before they become world pickups. Common use cases include:
- Saving a player's equipped items to a death cache for retrieval.
- Dropping specific items while keeping others in the inventory.
- Logging the player's inventory state at the time of death.
After OnPlayerDeath returns, Unturned proceeds with the standard inventory drop logic.
OnPlayerDeath event
The OnPlayerDeath event fires when the player dies but before the death screen appears and before inventory drops.
Event signature
csharp
public static event OnPlayerDeath OnPlayerDeath;
public delegate void OnPlayerDeath(
UnturnedPlayer player,
EDeathCause cause,
ELimb limb,
SteamPlayer killer
);Event parameters
| Parameter | Type | Description |
|---|---|---|
| First | UnturnedPlayer | The player who died |
| Second | EDeathCause | The cause of death |
| Third | ELimb | The limb that received the fatal hit |
| Fourth | SteamPlayer | The killer (null if environmental death) |
Note the parameter order: the death cause comes before the killer parameter. When implementing the handler, ensure the EDeathCause parameter is in the second position and the SteamPlayer killer parameter is in the fourth position.
csharp
// CORRECT parameter order:
private void OnPlayerDeath(UnturnedPlayer player, EDeathCause cause, ELimb limb, SteamPlayer killer)
{
// player — the dead player
// cause — death cause (second parameter)
// limb — fatal hit location
// killer — the killer player (fourth parameter)
}
// WRONG parameter order — placing killer before cause
// causes the killer SteamPlayer to be interpreted as EDeathCause,
// which produces a compile error due to type mismatch.
private void OnPlayerDeath(UnturnedPlayer player, SteamPlayer killer, EDeathCause cause, ELimb limb)
{
// This does NOT compile — the delegate signature expects
// EDeathCause at position 2 and SteamPlayer at position 4.
}Basic subscription
csharp
using Rocket.Unturned.Events;
protected override void Load()
{
UnturnedPlayerEvents.OnPlayerDeath += OnPlayerDeath;
}
protected override void Unload()
{
UnturnedPlayerEvents.OnPlayerDeath -= OnPlayerDeath;
}Basic implementation
csharp
private void OnPlayerDeath(UnturnedPlayer player, EDeathCause cause, ELimb limb, SteamPlayer killer)
{
string killerName = killer != null ? killer.playerID.playerName : "the environment";
Rocket.Core.Logging.Logger.Log(
$"{player.DisplayName} was killed by {killerName} via {cause}"
);
}OnPlayerDead event
OnPlayerDead fires after the inventory drop. The player is fully dead at this point.
Event signature
csharp
public static event OnPlayerDead OnPlayerDead;
public delegate void OnPlayerDead(
UnturnedPlayer player,
EDeathCause cause,
ELimb limb,
SteamPlayer killer
);The signature is the same as OnPlayerDeath. The difference is the timing within the death sequence.
csharp
private void OnPlayerDead(UnturnedPlayer player, EDeathCause cause, ELimb limb, SteamPlayer killer)
{
// Player is fully dead. Inventory has been dropped.
Rocket.Core.Logging.Logger.Log(
$"{player.DisplayName} is now dead. Cause: {cause}"
);
}OnPlayerRevive event
OnPlayerRevive fires when the player respawns after death.
Event signature
csharp
public static event OnPlayerRevive OnPlayerRevive;
public delegate void OnPlayerRevive(UnturnedPlayer player);csharp
private void OnPlayerRevive(UnturnedPlayer player)
{
player.SendChat("You have respawned.", Color.green);
}Death cause and killer tracking
EDeathCause values
The EDeathCause enum identifies the type of death. Common values:
| Value | Meaning |
|---|---|
Bleeding | Bled out from untreated wounds |
Bones | Fall damage |
Burning | Fire or lava damage |
Food | Starvation |
Gun | Firearm |
Infection | Infection from zombie attacks |
Melee | Melee weapon |
Roadkill | Vehicle impact |
Sentry | Buildable sentry gun |
Shark | Shark attack |
Spawn | Killed via spawn command |
Suicide | /suicide command |
Vehicle | Vehicle explosion |
Water | Drowning |
Zombie | Zombie attack |
Handling killer information
The killer parameter is a SteamPlayer from SDG.Unturned. It is null for environmental deaths (falling, drowning, starvation, zombies).
csharp
private void OnPlayerDeath(UnturnedPlayer player, EDeathCause cause, ELimb limb, SteamPlayer killer)
{
if (killer != null)
{
UnturnedPlayer killerPlayer = UnturnedPlayer.FromSteamPlayer(killer);
Rocket.Core.Logging.Logger.Log(
$"{player.DisplayName} killed by {killerPlayer.DisplayName}"
);
}
else
{
Rocket.Core.Logging.Logger.Log(
$"{player.DisplayName} died from {cause}"
);
}
}Custom death messages
A common plugin feature is custom death messages that replace Unturned's default death notifications:
csharp
private readonly Dictionary<EDeathCause, string[]> _deathMessages =
new Dictionary<EDeathCause, string[]>
{
{ EDeathCause.Gun, new[] {
"{victim} was shot by {killer}.",
"{victim} was gunned down by {killer}.",
"{killer} put a bullet in {victim}."
}},
{ EDeathCause.Melee, new[] {
"{victim} was stabbed by {killer}.",
"{killer} took down {victim} with a melee weapon."
}},
{ EDeathCause.Zombie, new[] {
"{victim} was torn apart by zombies.",
"{victim} succumbed to the infection."
}},
{ EDeathCause.Bones, new[] {
"{victim} fell to their death.",
"{victim} forgot to watch their step."
}},
{ EDeathCause.Burning, new[] {
"{victim} was burnt to a crisp.",
"{victim} played with fire and lost."
}}
};
private static readonly System.Random _random = new System.Random();
private void OnPlayerDeath(UnturnedPlayer player, EDeathCause cause, ELimb limb, SteamPlayer killer)
{
if (!_deathMessages.TryGetValue(cause, out string[] messages))
return;
string template = messages[_random.Next(messages.Length)];
string message = template
.Replace("{victim}", player.DisplayName)
.Replace("{killer}", killer != null ? killer.playerID.playerName : "Unknown");
UnturnedChat.Say(message, Color.magenta);
}Death penalty systems
Plugins can impose penalties on death such as losing currency, losing items, or cooldown timers:
csharp
public class DeathPenaltyComponent : UnturnedPlayerComponent
{
public int ConsecutiveDeaths;
public float LastDeathTime;
protected override void Load()
{
ConsecutiveDeaths = 0;
LastDeathTime = 0f;
}
}
private void OnPlayerDeath(UnturnedPlayer player, EDeathCause cause, ELimb limb, SteamPlayer killer)
{
var penalty = player.GetComponent<DeathPenaltyComponent>();
if (penalty == null)
return;
// Track consecutive deaths within 5 minutes
float now = Time.realtimeSinceStartup;
if (now - penalty.LastDeathTime < 300f)
{
penalty.ConsecutiveDeaths++;
}
else
{
penalty.ConsecutiveDeaths = 1;
}
penalty.LastDeathTime = now;
// Apply penalty based on streak
float penaltyMultiplier = 1f + (penalty.ConsecutiveDeaths * 0.25f);
Rocket.Core.Logging.Logger.Log(
$"{player.DisplayName} death penalty streak: {penalty.ConsecutiveDeaths} " +
$"(multiplier: {penaltyMultiplier})"
);
// If using a currency plugin, deduct coins
// CustomCurrencyPlugin.Instance.DeductCoins(player, (int)(10 * penaltyMultiplier));
}Inventory management on death
Preserving items on death
To prevent items from dropping, the inventory must be modified in the OnPlayerDeath handler before the inventory drop occurs:
csharp
private void OnPlayerDeath(UnturnedPlayer player, EDeathCause cause, ELimb limb, SteamPlayer killer)
{
// In OnPlayerDeath, the inventory is still intact.
// Items can be read, saved, or removed.
// Save the player's inventory to a backup
var inventory = player.Inventory;
SaveDeathCache(player.CSteamID.m_SteamID, inventory);
// Optionally remove items from the inventory
// to prevent them from dropping:
// Clear certain items here before the drop happens.
}Death cache recovery
Give players a way to recover dropped items from a death cache:
csharp
[RocketCommand("recover", "Recover items from your last death.",
AllowedCaller = AllowedCaller.Player)]
public class RecoverCommand : IRocketCommand
{
public void Execute(IRocketPlayer caller, string[] command)
{
var player = (UnturnedPlayer)caller;
ulong steamId = player.CSteamID.m_SteamID;
// Check if there is a death cache entry
if (DeathCache.HasCache(steamId))
{
DeathCache.RestoreToInventory(steamId, player);
player.SendChat("Your items have been recovered.", Color.green);
}
else
{
player.SendChat("No death cache found.", Color.red);
}
}
}Respawn management
Bed respawn
Unturned allows players to claim a bed at a base. On death, they can respawn at their claimed bed. Plugins can interact with this system:
csharp
private void OnPlayerRevive(UnturnedPlayer player)
{
// Check if the player respawned at a bed
// (as opposed to a random spawn point)
// Bed respawn information is in the player's BarricadeData
player.SendChat("You have respawned.", Color.green);
}Custom respawn locations
Plugins can set custom respawn locations by modifying the player's position after revival:
csharp
private void OnPlayerRevive(UnturnedPlayer player)
{
// Check if this player has a custom spawn point
if (_customSpawnPoints.ContainsKey(player.CSteamID.m_SteamID))
{
Vector3 spawnPos = _customSpawnPoints[player.CSteamID.m_SteamID];
player.Player.teleportToLocation(spawnPos, player.Player.transform.rotation.y);
player.SendChat("You have been teleported to your custom spawn point.", Color.cyan);
}
}Respawn timer modification
RocketMod does not expose a direct event for modifying the respawn timer. However, plugins can implement a custom respawn delay by overriding the OnPlayerDeath event and using a coroutine to delay the respawn:
csharp
private IEnumerator DelayedRespawn(UnturnedPlayer player, float delaySeconds)
{
// Show a custom death screen message
player.SendChat($"Respawning in {delaySeconds} seconds...", Color.yellow);
yield return new WaitForSeconds(delaySeconds);
// Force the player to respawn
// Note: This requires access to the player's lifecycle manager
Rocket.Core.Logging.Logger.Log(
$"Delayed respawn completed for {player.DisplayName}"
);
}Complete death handling plugin example
The following example demonstrates death messages, death tracking, inventory caching, and respawn handling in a single plugin.
DeathManagerPlugin.cs:
csharp
using Rocket.API;
using Rocket.Core.Plugins;
using Rocket.Unturned.Chat;
using Rocket.Unturned.Events;
using Rocket.Unturned.Player;
using SDG.Unturned;
using System.Collections.Generic;
using UnityEngine;
namespace DeathManager
{
public class DeathManagerPlugin : RocketPlugin<DeathManagerConfiguration>
{
public static DeathManagerPlugin Instance { get; private set; }
private readonly Dictionary<ulong, DeathRecord> _deathRecords =
new Dictionary<ulong, DeathRecord>();
private readonly Dictionary<EDeathCause, string[]> _deathMessages =
new Dictionary<EDeathCause, string[]>
{
{ EDeathCause.Gun, new[] {
"{victim} was shot by {killer}.",
"{victim} was eliminated by {killer}."
}},
{ EDeathCause.Zombie, new[] {
"{victim} was killed by a zombie.",
"{victim} succumbed to the horde."
}},
{ EDeathCause.Bones, new[] {
"{victim} fell to their death.",
"{victim} forgot to watch their step."
}},
{ EDeathCause.Water, new[] {
"{victim} drowned.",
"{victim} ran out of air."
}},
{ EDeathCause.Suicide, new[] {
"{victim} gave up.",
"{victim} took the easy way out."
}}
};
private static readonly System.Random _random = new System.Random();
protected override void Load()
{
Instance = this;
UnturnedPlayerEvents.OnPlayerDeath += OnPlayerDeath;
UnturnedPlayerEvents.OnPlayerDead += OnPlayerDead;
UnturnedPlayerEvents.OnPlayerRevive += OnPlayerRevive;
Rocket.Core.Logging.Logger.Log("DeathManager loaded.");
}
protected override void Unload()
{
UnturnedPlayerEvents.OnPlayerDeath -= OnPlayerDeath;
UnturnedPlayerEvents.OnPlayerDead -= OnPlayerDead;
UnturnedPlayerEvents.OnPlayerRevive -= OnPlayerRevive;
_deathRecords.Clear();
Instance = null;
Rocket.Core.Logging.Logger.Log("DeathManager unloaded.");
}
private void OnPlayerDeath(UnturnedPlayer player, EDeathCause cause, ELimb limb, SteamPlayer killer)
{
// Record the death
var record = new DeathRecord
{
SteamId = player.CSteamID.m_SteamID,
PlayerName = player.DisplayName,
Cause = cause,
Limb = limb,
KillerId = killer?.m_SteamID.m_SteamID ?? 0,
KillerName = killer?.playerID.playerName ?? "Environment",
Timestamp = DateTime.UtcNow,
Position = player.Player.transform.position
};
_deathRecords[player.CSteamID.m_SteamID] = record;
// Save inventory snapshot before drop
SaveInventorySnapshot(player);
// Broadcast custom death message
if (_deathMessages.TryGetValue(cause, out string[] messages))
{
// Handle PvP deaths with killer name
if (killer != null && cause == EDeathCause.Gun)
{
string template = messages[_random.Next(messages.Length)];
string message = template
.Replace("{victim}", player.DisplayName)
.Replace("{killer}", killer.playerID.playerName);
UnturnedChat.Say(message, Color.magenta);
}
// Handle environmental deaths
else
{
string template = messages[_random.Next(messages.Length)];
string message = template
.Replace("{victim}", player.DisplayName)
.Replace("{killer}", "the environment");
UnturnedChat.Say(message, Color.magenta);
}
}
Rocket.Core.Logging.Logger.Log(
$"DEATH: {player.DisplayName} killed by " +
$"{(killer != null ? killer.playerID.playerName : cause.ToString())}"
);
}
private void OnPlayerDead(UnturnedPlayer player, EDeathCause cause, ELimb limb, SteamPlayer killer)
{
// Player is fully dead, inventory has dropped
Rocket.Core.Logging.Logger.Log(
$"{player.DisplayName} death confirmed. Cause: {cause}"
);
}
private void OnPlayerRevive(UnturnedPlayer player)
{
// Player has respawned
if (_deathRecords.TryGetValue(player.CSteamID.m_SteamID, out DeathRecord record))
{
float elapsedMinutes = (float)(DateTime.UtcNow - record.Timestamp).TotalMinutes;
player.SendChat(
$"You died {elapsedMinutes:F1} minutes ago. " +
$"Cause: {FormatDeathCause(record.Cause)}.",
Color.cyan
);
}
// Grant brief invulnerability on respawn
GrantInvulnerability(player);
}
private void SaveInventorySnapshot(UnturnedPlayer player)
{
// Read the player's inventory before the drop
// and save to a cache file
string cacheDir = $"Rocket/Plugins/DeathManager/deathcache/";
System.IO.Directory.CreateDirectory(cacheDir);
string cacheFile = $"{cacheDir}{player.CSteamID.m_SteamID}.txt";
using (var writer = new System.IO.StreamWriter(cacheFile))
{
writer.WriteLine($"Death time: {DateTime.UtcNow}");
writer.WriteLine($"Position: {player.Player.transform.position}");
// Additional inventory data would be written here
}
}
private void GrantInvulnerability(UnturnedPlayer player)
{
var immunity = player.GetComponent<DamageImmunityComponent>();
if (immunity == null)
{
// Fallback: trigger a warning if component is missing
Rocket.Core.Logging.Logger.LogWarning(
$"DamageImmunityComponent not found for {player.DisplayName}"
);
return;
}
immunity.GrantImmunity(5f);
}
private string FormatDeathCause(EDeathCause cause)
{
switch (cause)
{
case EDeathCause.Gun: return "firearm";
case EDeathCause.Melee: return "melee weapon";
case EDeathCause.Zombie: return "zombie";
case EDeathCause.Bones: return "fall damage";
case EDeathCause.Burning: return "fire";
case EDeathCause.Water: return "drowning";
case EDeathCause.Food: return "starvation";
case EDeathCause.Infection: return "infection";
case EDeathCause.Suicide: return "suicide";
case EDeathCause.Vehicle: return "vehicle explosion";
case EDeathCause.Roadkill: return "vehicle impact";
default: return cause.ToString();
}
}
}
public class DeathRecord
{
public ulong SteamId;
public string PlayerName;
public EDeathCause Cause;
public ELimb Limb;
public ulong KillerId;
public string KillerName;
public System.DateTime Timestamp;
public Vector3 Position;
}
}Death statistics tracking
For leaderboards or gameplay analysis, track death statistics per player:
csharp
public class DeathStatsComponent : UnturnedPlayerComponent
{
public int TotalDeaths;
public int PvPDeaths;
public int PvEDeaths;
public int SuicideCount;
public Dictionary<EDeathCause, int> DeathsByCause;
public string MostCommonKiller;
protected override void Load()
{
TotalDeaths = 0;
PvPDeaths = 0;
PvEDeaths = 0;
SuicideCount = 0;
DeathsByCause = new Dictionary<EDeathCause, int>();
MostCommonKiller = null;
}
}
// In the plugin:
private void OnPlayerDeath(UnturnedPlayer player, EDeathCause cause, ELimb limb, SteamPlayer killer)
{
var stats = player.GetComponent<DeathStatsComponent>();
if (stats == null)
return;
stats.TotalDeaths++;
if (killer != null)
{
stats.PvPDeaths++;
stats.MostCommonKiller = killer.playerID.playerName;
}
else
{
stats.PvEDeaths++;
}
if (cause == EDeathCause.Suicide || cause == EDeathCause.Spawn)
{
stats.SuicideCount++;
}
if (!stats.DeathsByCause.ContainsKey(cause))
stats.DeathsByCause[cause] = 0;
stats.DeathsByCause[cause]++;
}Frequently asked questions
Which event fires first, OnPlayerDeath or OnPlayerDead?
OnPlayerDeath fires first, before the inventory drops. OnPlayerDead fires after the inventory has dropped.
Can I prevent a player from dying after their health reaches zero?
By the time OnPlayerDeath fires, the player's health has already reached zero. The death sequence has started. To prevent death, you must cancel the damage in the OnPlayerDamaged event (see Player Damage), not in the death event.
Can I give a player items after they die?
Yes. In the OnPlayerRevive event, the player is alive and has a clean inventory (items on the character, not in the backpack if drop-on-death is enabled). You can add items at this point.
How do I prevent items from dropping on death?
Items can be removed from the inventory in OnPlayerDeath (before the drop occurs). Alternatively, configure the server to not drop items on death through Unturned's game mode settings.
Is the killer guaranteed to be online when OnPlayerDeath fires?
The killer is the SteamPlayer who dealt the fatal damage. If the killer disconnected between dealing damage and the death occurring (for example, damage-over-time effects), the killer parameter may still reference the player's data, but the player may no longer be on the server. Check killer.player.isOnline if needed.
Can I cancel a respawn?
RocketMod does not provide a cancel mechanism for respawns. To control when a player respawns, manage the respawn timer through the game mode configuration or implement a custom death screen using a UI plugin.
Cross-references
- Player Damage — damage events that precede death.
- Player Chat — chat events for death message integration.
- Player Components — per-player component setup for tracking death state.
- Permissions System — permission-based death feature access.
- Plugin Configuration — configuration serialization for death settings.
Document history
| Version | Date | Author | Notes |
|---|---|---|---|
| 1.0 | 2026-07-27 | 57 Studios | Initial publication. Coverage of death sequence, OnPlayerDeath/OnPlayerDead/OnPlayerRevive events, inventory handling, death messages, respawn management, best practices. |
