Skip to content
RustPluginGenerator

OnActiveItemChange

OnActiveItemChange is called from BasePlayer.UpdateActiveItem right before the server writes the new active item id to the player, so it fires whenever the held item slot changes.

Signature
object OnActiveItemChange(BasePlayer player, Item activeItem, ItemId itemID)

Parameters

Hook parameters
ParameterTypeDescription
playerBasePlayerThe BasePlayer whose held item is being switched. Never null in practice; the game calls this on the owning connection.
activeItemItemThe item the player currently has in the active/hotbar slot before the change is applied, or null when nothing is held.
itemIDItemIdThe ItemId the game is about to write to the player's active item slot. Compare it against the item definition you look up with ItemManager.FindItemDefinition.

Return value

Returning null lets the game continue and write the new active item id as normal. Returning any non-null object makes UpdateActiveItem return immediately before that write, so the player's active item slot is left unchanged for that refresh.

How it works

The hook fires inside BasePlayer.UpdateActiveItem, which the server runs whenever the player's active item slot needs refreshing: selecting a hotbar slot, cycling with the mouse wheel, equipping after a pickup or deploy, and clearing the slot after dropping, consuming or losing the held item. player is the owner, activeItem is what was equipped before the write (GetActiveItem(), which can be null), and itemID is the id the game is about to store. Because the hook runs on every slot refresh, treat it as a hot path and keep the body cheap. There is no way to identify a specific equipment category from the arguments alone, so look up the definition of itemID and check its shortname or category yourself. Declaring activeItem as a more specific type is not useful here since it is an Item; declaring player as BasePlayer is already exact. Returning a value from the hook prevents the write, which leaves the previous item equipped.

Example plugin: Block restricted items while building-blocked

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

namespace Oxide.Plugins
{
    [Info("ActiveItemWhitelist", "RustPluginGenerator", "1.0.0")]
    [Description("Prevents equipping restricted items while a player stands in a building-blocked zone.")]
    public class ActiveItemWhitelist : RustPlugin
    {
        private const string PermBypass = "activeitemwhitelist.bypass";

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

        private class PluginConfig
        {
            [JsonProperty("Allowed item shortnames in safe zones")]
            public List<string> AllowedItems = new List<string> { "rock", "torch", "hammer", "building.planner" };

            [JsonProperty("Seconds between block messages per player")]
            public float NoticeCooldown = 10f;
        }

        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 cannot equip {0} in this area.",
                ["Unknown"] = "that item"
            }, 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 OnActiveItemChange(BasePlayer player, Item activeItem, ItemId itemID)
        {
            if (player == null || !player.IsConnected || player.IsDead())
                return null;

            int itemId = (int)itemID.Value;

            // Nothing changes on the player, skip the work.
            if (activeItem != null && activeItem.info != null && activeItem.info.itemid == itemId)
                return null;

            if (!player.IsBuildingBlocked() || permission.UserHasPermission(player.UserIDString, PermBypass))
                return null;

            var definition = ItemManager.FindItemDefinition(itemId);
            if (definition == null || config.AllowedItems.Contains(definition.shortname))
                return null;

            NotifyBlocked(player, definition.displayName.english);
            return false;
        }

        private void NotifyBlocked(BasePlayer player, string itemName)
        {
            float now = UnityEngine.Time.realtimeSinceStartup;
            float last;
            if (lastNotice.TryGetValue(player.userID, out last) && now - last < config.NoticeCooldown)
                return;

            lastNotice[player.userID] = now;
            player.ChatMessage(Msg("Blocked", player.UserIDString, string.IsNullOrEmpty(itemName) ? Msg("Unknown", player.UserIDString) : itemName));
        }

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

The plugin keeps a configurable list of item shortnames that may be equipped while the player is building-blocked, plus the activeitemwhitelist.bypass permission that exempts staff. Inside OnActiveItemChange it bails out early when the new itemID matches the item already held, so the body only runs on real switches. It resolves the item from ItemManager.FindItemDefinition(itemID) and, if the shortname is not on the allowed list and the player is building-blocked without the bypass permission, sends a throttled chat message and returns false. Returning false from OnActiveItemChange blocks the active-item change, so the previous item stays equipped.

Common use cases

  • Restrict equipped items in safe zones

    Cancel the switch when the new item is a weapon, explosive or tool and the player is inside a building-blocked area, keeping spawn zones calm without disabling the items entirely.

  • Reward or announce exotic equipment

    Return null and track when a player first equips an item by shortname, then fire a reward, a broadcast or a timer without blocking anything.

  • Fire equipment side effects

    Start or stop a visual effect, buff or UI panel whenever the active item moves to or from a specific shortname, using the previous item and the new item id to detect direction.

Pitfalls

  • Do not spam chat on a hot path

    The hook runs on every active slot refresh, and a blocked switch can retrigger it. Rate-limit any player message with a per-player timestamp or you will flood the chat.

  • The item may be null

    When the player is bare-handed or just dropped their item, activeItem is null and itemID may be the default empty id. Resolve the new item from ItemManager.FindItemDefinition(itemID) rather than assuming activeItem exists.

  • Blocking leaves the old item equipped

    Returning a value skips the write but does not move items in the inventory, so the belt slot still shows the attempted item. Tell the player clearly why the switch was refused.

Build a plugin with OnActiveItemChange

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

Prompt: Restrict equipped items in safe zones using the OnActiveItemChange 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 OnActiveItemChange

Generate