Skip to content
RustPluginGenerator

OnStructureDemolish

OnStructureDemolish is called when a player requests demolition of a DecayEntity, before the game removes it. Returning a non-null value blocks the demolition.

Signature
object OnStructureDemolish(DecayEntity decayEntity, BasePlayer player, bool flag)

Parameters

Hook parameters
ParameterTypeDescription
decayEntityDecayEntityThe building entity being demolished, typed as DecayEntity because the hook is injected in DecayEntity's demolish RPC handlers. Cast to BuildingBlock or StabilityEntity when you need grade, buildingID or stability data.
playerBasePlayerThe player who sent the demolish request. In DoImmediateDemolish this is always an admin; in DoDemolish it is the owner or a building-authed player who passed CanDemolish.
flagbooltrue when the request came through the admin immediate-demolish RPC path (DoImmediateDemolish), false for the normal demolish path (DoDemolish).

Return value

Return null to let the demolition continue as normal. Return any non-null object to stop the original path: the entity is not removed and the demolish RPC has no further effect. Because the patched methods return void, the returned value is only used as a stop signal.

How it works

The hook fires inside DecayEntity.DoImmediateDemolish and DecayEntity.DoDemolish, which are the RPC handlers behind a player's demolish action on a building entity. Both are guarded by msg.player.CanInteract(); DoDemolish additionally calls CanDemolish(player) first, so by the time the hook runs the game already decided the player is allowed to demolish. DoImmediateDemolish requires IsAdmin on top of that.

decayEntity is the structure being removed. It is usually a BuildingBlock (check grade, buildingID) but can be any DecayEntity. player is the sender of the action, never null at this point because msg.player was dereferenced. flag distinguishes the admin immediate path (true) from the normal path (false), which is useful if you only want to restrict one of them.

Declaring DecayEntity keeps both paths firing. Narrowing to BuildingBlock is safe when you only care about walls and foundations but silently skips other decay entities. Blocking is done by returning any non-null object; the original method returns void, so the value itself is discarded once it stops the demolish.

Example plugin: Block demolishing structures outside a daily window while keeping it logged

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

namespace Oxide.Plugins
{
    [Info("Demolish Window", "RustPluginGenerator", "1.0.0")]
    [Description("Restricts player structure demolition to a configurable UTC time window.")]
    public class DemolishWindow : RustPlugin
    {
        private const string PermBypass = "demolishwindow.bypass";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Start hour UTC (0-23)")]
            public int StartHour = 2;

            [JsonProperty("End hour UTC (0-23)")]
            public int EndHour = 6;

            [JsonProperty("Admins bypass window")]
            public bool AdminBypass = 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>
            {
                ["Blocked"] = "Structures can only be demolished between {0}:00 and {1}:00 UTC."
            }, 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(PermBypass, this);
        }

        private object OnStructureDemolish(DecayEntity decayEntity, BasePlayer player, bool flag)
        {
            if (decayEntity == null || player == null) return null;
            if (player.IsAdmin && config.AdminBypass) return null;
            if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;
            if (InWindow(DateTime.UtcNow.Hour)) return null;

            player.ChatMessage(Msg("Blocked", player.UserIDString, config.StartHour, config.EndHour));
            return true;
        }

        private bool InWindow(int hour)
        {
            int start = Math.Min(Math.Max(config.StartHour, 0), 23);
            int end = Math.Min(Math.Max(config.EndHour, 0), 23);
            if (start == end) return true;
            if (start < end) return hour >= start && hour < end;
            return hour >= start || hour < end;
        }
    }
}

The plugin keeps a config that defines a UTC start and end hour for demolition and an admin bypass toggle. In OnStructureDemolish it resolves the entity's BuildingBlock (if there is one), checks whether the player is an admin that should bypass, and compares DateTime.UtcNow.Hour against the configured window, handling windows that cross midnight. When the request falls outside the window it sends a chat message using the language file and returns true to cancel the demolish. Allowed demolition requests return null so the game continues normally. Demolitions are not logged to the console or file, so frequent calls stay cheap; only the blocked player receives a localized message.

Common use cases

  • Time-gated demolition

    Only allow players to demolish during a posted window so mass rebuilds happen off-peak. Return non-null outside the window and send the player a chat notice.

  • Protect raided or contested bases

    Block demolition of structures whose buildingID belongs to a base tagged as under raid protection, or whose privilege is owned by another player, by checking buildingID and GetBuildingPrivilege().

  • Capture admin demolish separately

    When flag is true the request came from the admin immediate path. You can let admins through while still logging the action to a data file for auditing.

Pitfalls

  • Only fires for player demolish RPCs

    The hook is injected in the two RPC handlers, so decay, entity kill from other systems, and Kill() calls do not trigger it. Use OnEntityDeath or CanDemolish for broader coverage.

  • Parameters may not be null-checked by the game

    msg.player is dereferenced before the hook fires, so player is not null, but decayEntity should still be treated as a live entity and cast defensively before reading buildingID.

  • Returning false does not block

    Only a non-null return cancels the demolish. Returning false is non-null and blocks too, but returning null is the only way to allow it, so do not mix the two idioms.

Build a plugin with OnStructureDemolish

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

Prompt: Time-gated demolition using the OnStructureDemolish 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 OnStructureDemolish

Generate