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.
object OnActiveItemChange(BasePlayer player, Item activeItem, ItemId itemID)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The BasePlayer whose held item is being switched. Never null in practice; the game calls this on the owning connection. |
activeItem | Item | The item the player currently has in the active/hotbar slot before the change is applied, or null when nothing is held. |
itemID | ItemId | The 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
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,
activeItemis null anditemIDmay be the default empty id. Resolve the new item fromItemManager.FindItemDefinition(itemID)rather than assumingactiveItemexists.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.
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