Skip to content
RustPluginGenerator

OnPlayerKeepAlive

OnPlayerKeepAlive is called when a client sends the keep-alive RPC to a wounded player. Returning a non-null value stops the wound timer from being prolonged.

Signature
object OnPlayerKeepAlive(BasePlayer player, BasePlayer player2)

Parameters

Hook parameters
ParameterTypeDescription
playerBasePlayerThe wounded player who receives the keep-alive RPC and whose wound timer would be extended.
player2BasePlayerThe connected player whose client sent the keep-alive RPC (msg.player); this is the player interacting with the wounded one.

Return value

Returning null lets the original code run, so ProlongWounding(10f) extends the wounded player's timer. Returning any non-null object blocks that call for this RPC, so the wound timer is not prolonged.

How it works

The hook fires inside BasePlayer.RPC_KeepAlive, which the server reads when a client sends its keep-alive message. In vanilla RPC_KeepAlive only acts when three conditions are true: the sending player can interact, the sender is not the wounded player itself, and the target player IsWounded(). When those hold, the server calls the hook before running ProlongWounding(10f), which pushes the wounded player's bleed-out timer forward by ten seconds.

player is the wounded player being kept alive. player2 is the sender of the RPC — the other player involved. Since player is already known to be wounded by the time the hook fires, you do not need to re-check IsWounded() on it, but do null-check both references since network state can change between packets. Because both parameters are BasePlayer, you cannot narrow them by declaring a subtype to skip calls; the hook fires only for player targets anyway.

The hook runs frequently while players are wounded, so keep the body short and avoid logging, file access, or heavy allocations. It is the correct place to enforce a rule such as a revive cooldown or a limit on how long players can be kept in the wounded state.

Example plugin: Cap how long a player can be kept wounded

WoundTimerCap.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("Wound Timer Cap", "RustPluginGenerator", "1.0.0")]
    [Description("Stops players from keeping a wounded player's bleed-out timer alive past a configured limit.")]
    public class WoundTimerCap : RustPlugin
    {
        private const string PermNotify = "woundtimercap.notify";

        private class PluginConfig
        {
            [JsonProperty("Max wound seconds")]
            public float MaxWoundSeconds { get; set; } = 60f;

            [JsonProperty("Warn keep-alive sender at cap")]
            public bool WarnSender { get; set; } = true;
        }

        private PluginConfig config;
        private readonly Dictionary<ulong, DateTime> woundStart = new Dictionary<ulong, DateTime>();

        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>
            {
                ["CannotKeepAlive"] = "This player has been wounded too long to keep alive.",
                ["SenderWarning"] = "{0} has been wounded for {1} seconds, keep-alive no longer works."
            }, this);
        }

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

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

        private void OnPlayerWound(BasePlayer player)
        {
            if (player == null) return;
            woundStart[player.userID] = DateTime.UtcNow;
        }

        private void OnPlayerRecovered(BasePlayer player)
        {
            if (player == null) return;
            woundStart.Remove(player.userID);
        }

        private void OnPlayerDeath(BasePlayer player, HitInfo info)
        {
            if (player == null) return;
            woundStart.Remove(player.userID);
        }

        private object OnPlayerKeepAlive(BasePlayer player, BasePlayer player2)
        {
            if (player == null || player2 == null) return null;

            DateTime started;
            if (!woundStart.TryGetValue(player.userID, out started)) return null;

            float elapsed = (float)(DateTime.UtcNow - started).TotalSeconds;
            if (elapsed < config.MaxWoundSeconds) return null;

            if (config.WarnSender && permission.UserHasPermission(player2.UserIDString, PermNotify))
            {
                player2.ChatMessage(Msg("SenderWarning", player2.UserIDString, player.displayName, (int)elapsed));
            }

            return false;
        }
    }
}

WoundTimerCap records when each player first enters the wounded state and blocks the keep-alive hook once a configured maximum has elapsed. OnPlayerWound stores the entry timestamp in a dictionary keyed by userID. OnPlayerKeepAlive looks up that timestamp for the wounded player; if the elapsed seconds exceed MaxWoundSeconds, it returns false (non-null) so ProlongWounding is skipped and the timer cannot be extended. OnPlayerRecovered and OnPlayerDeath clear the entry so the next wound starts fresh. The config exposes the cap and an optional warning message, sent to the keep-alive sender when the limit is hit. State is in-memory only, so a restart simply resets the counters.

Common use cases

  • Limit keep-alive time

    Stop a wounded player's bleed-out timer from being extended indefinitely by well-wishers spamming the keep-alive RPC.

  • Restrict who can keep alive

    Block keep-alive RPCs when the sender is not on the wounded player's team by returning a non-null value.

  • Log late-wound attempts

    Track how many keep-alive packets arrive after a wound threshold to spot players abusing the mechanic.

Pitfalls

  • Do not treat the return as a value

    The hook is patched into a void method, so any non-null return merely skips ProlongWounding for that packet. Returning true does not mean yes-prolong; it is the same as returning false.

  • Full-blocking makes players bleed out

    Always blocking keep-alive when a player is wounded removes the mechanic entirely. Gate the return on a configurable condition so vanilla behavior survives for normal cases.

  • Sender is not the wounded player

    player2 is the RPC sender, not the target. If you mutate inventory, health, or messages on the wrong parameter, you will affect the wrong player.

Build a plugin with OnPlayerKeepAlive

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

Prompt: Limit keep-alive time using the OnPlayerKeepAlive 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 OnPlayerKeepAlive

Generate