Skip to content
RustPluginGenerator

OnPlayerRecover

OnPlayerRecover is called when a player is about to recover from the wounded (crawling) state, before the game assigns their post-recovery health.

Signature
object OnPlayerRecover(BasePlayer player)

Parameters

Hook parameters
ParameterTypeDescription
playerBasePlayerThe BasePlayer who is recovering from the wounded (crawling) state. Check IsConnected or IsNpc before applying player-specific logic.

Return value

Returning null (or nothing) lets the game continue and roll the normal recovery health. Returning any non-null value (a common Oxidism is returning true from a plugin intended to block) stops the original recovery logic, so the game skips its own state change; you are then responsible for any health or state handling you want.

How it works

The hook fires inside BasePlayer.RecoverFromWounded, the private method the server runs when a wounded player finishes recovering instead of dying. That path is reached when the player's wound timer expires or they are otherwise revived out of the crawling state. The only argument is the BasePlayer being recovered.

Because the call happens before the game writes the recovery health (the crawling case rolls Random.Range(2f, 6f) plus any healing accumulated while crawling), this is the place to adjust what happens to that player at the moment they stand back up: heal them further, strip items, teleport them, or notify them. You cannot narrow the hook by parameter type in a useful way here — there is only one signature and one player argument.

Returning a non-null value blocks the game's own recovery handling for that call, so the player leaves the crawling state without the normal health assignment. Only do that when you set health yourself, otherwise the player can end up with unexpected or stale health values. The hook does not fire for players who die from their wounds.

Example plugin: Grant a short damage-resistance window after recovering

RecoverShield.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Recover Shield", "RustPluginGenerator", "1.0.0")]
    [Description("Gives players a short damage-reduction window after they recover from the wounded state")]
    public class RecoverShield : RustPlugin
    {
        private const string PermUse = "recovershield.use";

        private PluginConfig config;
        private readonly Dictionary<ulong, float> shieldUntil = new Dictionary<ulong, float>();
        private readonly Dictionary<ulong, Timer> shieldTimers = new Dictionary<ulong, Timer>();

        private class PluginConfig
        {
            [JsonProperty("Shield duration (seconds)")]
            public float Duration { get; set; } = 8f;

            [JsonProperty("Damage multiplier while shielded")]
            public float DamageMultiplier { get; set; } = 0.25f;

            [JsonProperty("Give free bandage on recover")]
            public bool GiveBandage { get; set; } = true;
        }

        protected override void LoadDefaultConfig() => config = new PluginConfig();

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config == null) throw new Exception();
            }
            catch
            {
                PrintWarning("Configuration file is invalid, using defaults");
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(config);

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["Shielded"] = "You are shielded for {0} seconds after recovering.",
                ["ShieldEnded"] = "Your recovery shield has expired."
            }, this);
        }

        private string Msg(string key, string userId = null, params object[] args)
            => string.Format(lang.GetMessage(key, this, userId), args);

        private void Init()
        {
            permission.RegisterPermission(PermUse, this);
        }

        private void OnPlayerRecover(BasePlayer player)
        {
            if (player == null || player.IsNpc || !permission.UserHasPermission(player.UserIDString, PermUse))
                return;

            if (shieldTimers.TryGetValue(player.userID.Get(), out Timer oldTimer) && oldTimer != null)
                oldTimer.Destroy();

            shieldUntil[player.userID.Get()] = UnityEngine.Time.realtimeSinceStartup + config.Duration;

            if (config.GiveBandage)
            {
                Item kit = ItemManager.CreateByName("bandage", 3, 0);
                if (kit != null)
                    player.GiveItem(kit);
            }

            player.ChatMessage(Msg("Shielded", player.UserIDString, config.Duration));

            shieldTimers[player.userID.Get()] = timer.Once(config.Duration, () =>
            {
                shieldUntil.Remove(player.userID.Get());
                shieldTimers.Remove(player.userID.Get());
                if (player != null && player.IsConnected)
                    player.ChatMessage(Msg("ShieldEnded", player.UserIDString));
            });
        }

        private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
        {
            BasePlayer player = entity as BasePlayer;
            if (player == null || info == null)
                return;

            if (!shieldUntil.TryGetValue(player.userID.Get(), out float until))
                return;

            if (UnityEngine.Time.realtimeSinceStartup > until)
                return;

            if (info.damageTypes != null)
                info.damageTypes.ScaleAll(config.DamageMultiplier);
        }

        private void Unload()
        {
            foreach (Timer t in shieldTimers.Values)
            {
                if (t != null) t.Destroy();
            }
            shieldTimers.Clear();
            shieldUntil.Clear();
        }
    }
}

The plugin listens for OnPlayerRecover and, if the shield is enabled in config, sends the player a chat notice and places a physical shield kit in their belt. A per-player expiry timestamp is stored in a dictionary, and OnEntityTakeDamage scales incoming damage down for anyone still inside the window. Timers strip the kit back out when it ends. The shield duration and damage multiplier live in a nested config class with [JsonProperty] fields, and the chat text is routed through the language file so it can be edited without recompiling. The hook never blocks recovery because blocking would skip the game's own health assignment.

Common use cases

  • Spawn protection after recovery

    Give players a few seconds of reduced incoming damage after they pull themselves out of the crawling state, so they are not instantly downed again by the attacker standing over them.

  • Recovery notifications

    Send the player a chat message or sound effect confirming they recovered, which is useful on servers with custom near-death gameplay.

  • Penalty on recover

    Apply a small item or hunger penalty when a player survives a wound, for example removing one bandage or reducing calories, to make wounding a meaningful consequence.

Pitfalls

  • Returning a value skips the game's health roll

    A non-null return stops the original recovery code from running. If you block it and do not set health yourself, the player leaves the crawling state with whatever health they had, which can look like a bug.

  • Only fires for survivors

    Players who bleed out or are finished while wounded go through the death path instead, so OnPlayerRecover never runs for them. Use OnPlayerDeath for anything that must cover both outcomes.

  • Do not log here

    Recovery can happen often enough on busy servers that console output becomes noise. Send player-facing text through the language file instead of Puts.

Build a plugin with OnPlayerRecover

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Spawn protection after recovery using the OnPlayerRecover hook, with a config file and a permission
Generate this plugin

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.

Build a plugin with OnPlayerRecover

Generate