Skip to content
RustPluginGenerator

OnRemoveDying

OnRemoveDying is called from GrowableEntity.RemoveDying just before the plant in the Dying state is turned back into its removeDyingItem and the removeDyingEffect is played. Returning a non-null value skips the rest of the original method.

Signature
object OnRemoveDying(GrowableEntity growableEntity, BasePlayer receiver)

Parameters

Hook parameters
ParameterTypeDescription
growableEntityGrowableEntityThe GrowableEntity in the Dying state whose RemoveDying method is running. Use it to read its state, prefab name, position and the removeDyingItem of its PlantProperties.
receiverBasePlayerThe BasePlayer passed to RemoveDying, normally the player who is interacting with the plant. It can be null when the method is invoked without a player context, so always null-check it.

Return value

Return null to let the original RemoveDying run: effect plays, the removeDyingItem is processed and the growable is removed. Return any non-null object to stop the rest of the method, leaving the dying growable in place. The returned value itself is not used, only whether it is null.

How it works

The hook fires inside GrowableEntity.RemoveDying(BasePlayer receiver), and only when the entity's State is Dying and its Properties.removeDyingItem is set. In practice this covers a dying plant node that is being cleared: the vanilla method plays the configured remove effect, replaces the growable with the item defined by removeDyingItem and removes the entity. The hook is checked as part of the same condition as the State and removeDyingItem checks, so a non-null return prevents the effect, the item spawn/swap and the cleanup from running at all.

The growableEntity parameter gives you the plant itself: ShortPrefabName, transform.position, the current State, and the Properties object if you need to know which item would be produced. The receiver is the player associated with the call; treat it as optional and check for null before reading UserIDString or currentTeam. Declaring a more specific type is not useful here because the game always passes a GrowableEntity and a BasePlayer. RemoveDying is not a hot per-tick path, but it can run for many entities during a wipe cleanup, so keep the body cheap and avoid chatty logging.

Example plugin: Protect dying plants inside a team member's building

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

namespace Oxide.Plugins
{
    [Info("Dying Plant Protection", "RustPluginGenerator", "1.0.0")]
    [Description("Blocks the removal of dying growables inside bases the interacting player is not authorized on.")]
    public class DyingPlantProtection : RustPlugin
    {
        private const string PermUse = "dyingplantprotection.use";

        private PluginConfig config;
        private readonly Dictionary<ulong, float> lastWarning = new Dictionary<ulong, float>();

        private class PluginConfig
        {
            [JsonProperty("Protect authorized players")]
            public bool ProtectAuthed { get; set; } = true;

            [JsonProperty("Warn on blocked removal")]
            public bool WarnPlayer { get; set; } = true;

            [JsonProperty("Warning cooldown (seconds)")]
            public float WarningCooldown { get; set; } = 5f;
        }

        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>
            {
                ["Blocked"] = "You are not allowed to clear plants in this base."
            }, 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 object OnRemoveDying(GrowableEntity growableEntity, BasePlayer receiver)
        {
            if (growableEntity == null || receiver == null) return null;
            if (!config.ProtectAuthed) return null;
            if (permission.UserHasPermission(receiver.UserIDString, PermUse)) return null;

            var privilege = growableEntity.GetBuildingPrivilege();
            if (privilege == null) return null;
            if (privilege.IsAuthed(receiver)) return null;

            if (IsTeamMember(receiver, privilege.OwnerID)) return null;

            if (config.WarnPlayer) Warn(receiver);
            return true;
        }

        private bool IsTeamMember(BasePlayer player, ulong ownerId)
        {
            if (ownerId == 0 || player.currentTeam == 0) return false;
            var team = RelationshipManager.ServerInstance.FindTeam(player.currentTeam);
            return team != null && team.members.Contains(ownerId);
        }

        private void Warn(BasePlayer player)
        {
            float now = UnityEngine.Time.realtimeSinceStartup;
            float last;
            if (lastWarning.TryGetValue(player.userID, out last) && now - last < config.WarningCooldown) return;
            lastWarning[player.userID] = now;
            player.ChatMessage(Msg("Blocked", player.UserIDString));
        }

        private void Unload()
        {
            lastWarning.Clear();
        }
    }
}

The plugin intercepts RemoveDying and checks whether the plant sits inside a building that the receiver is authorized on or belongs to by team. If the player is authorized (or shares a team with the cupboard owner), the hook returns null and the plant is cleared normally. Otherwise it cancels the removal and optionally sends a short chat warning through the language file. A short cooldown per player keeps the warning from being spammed when several plants are touched in a row. Permission dyingplantprotection.use lets trusted players bypass the protection entirely, which is registered in Init.

Common use cases

  • Protect plants in owned bases

    Cancel removal of dying growables inside a building privilege the receiver is not authorized on, keeping neighbour bases from clearing plants they do not own.

  • Log farm cleanup

    Record which player cleared which plant prefab at what position, so admins can audit unusual farm behaviour without blocking the original method.

  • Replace the vanilla remove item

    Cancel the default removal and hand out a custom reward item via ItemManager instead of the configured removeDyingItem.

Pitfalls

  • Receiver may be null

    RemoveDying is called with a BasePlayer argument that is not guaranteed to be set. Guard the receiver before reading UserIDString or calling permission checks, and fall back to allowing removal.

  • Hook only runs for Dying plants with a remove item

    The call is inside a condition on State == Dying and Properties.removeDyingItem != null. Growables that are not dying, or whose properties define no remove item, never reach the hook, so do not rely on it for general growable cleanup.

  • Blocking leaves the entity in place

    Returning non-null skips the effect and the entity removal, so the dying growable stays in the world. Make sure your plugin or a later call actually cleans it up, otherwise it lingers.

Build a plugin with OnRemoveDying

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

Prompt: Protect plants in owned bases using the OnRemoveDying 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 OnRemoveDying

Generate