Skip to content
RustPluginGenerator

OnRackedWeaponUnloaded

OnRackedWeaponUnloaded is called after a player removes a weapon from a WeaponRack and the rack has already unloaded its magazine ammo back into the item. It fires once per weapon removed via WeaponRack.UnloadWeapon.

Signature
void OnRackedWeaponUnloaded(Item slot, BasePlayer player, WeaponRack weaponRack)

Parameters

Hook parameters
ParameterTypeDescription
slotItemThe weapon item that was just removed from the rack and unloaded. Its primaryMagazine (if any) has already been emptied at this point, so the ammo is back in the magazine object's pool rather than in the item.
playerBasePlayerThe BasePlayer who took the weapon off the rack. Can be null in edge cases, so null-check before using it.
weaponRackWeaponRackThe WeaponRack entity the weapon was unloaded from. Use it to read the rack's position, OwnerID or building privilege, or to compare it with another rack.

Return value

The return value is ignored. WeaponRack.UnloadWeapon returns void and the game continues normally regardless of what your hook returns, so return nothing and treat the hook as a pure notification.

How it works

The hook fires at the end of WeaponRack.UnloadWeapon, after the game has detached the weapon from the rack slot, called UnloadAmmo on it, refreshed the slot ammo display, sent the network update and queued the PlayAmmoSound client RPC for the removing player. By the time your code runs, the magazine is empty and the ammo has already been returned, so you cannot use this hook to cancel or alter the unload itself — the game work is done. Use it to react: log who pulled which weapon from which rack, notify a team, apply a cooldown, or hand out a replacement item. slot is the item being taken, player is the remover (null-check it), and weaponRack is the rack entity, which lets you check ownership, position or GetBuildingPrivilege(). Declaring player as BasePlayer and weaponRack as WeaponRack is already the precise signature; narrowing further is not useful here because the game passes those exact types. The hook only fires on a real unload through the rack UI, not when a weapon is destroyed or when a rack is removed with items still inside.

Example plugin: Alert owners when a racked weapon is removed

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

namespace Oxide.Plugins
{
    [Info("Racked Weapon Unload Alert", "RustPluginGenerator", "1.0.0")]
    [Description("Notifies rack owners and admins when a weapon is unloaded from a WeaponRack.")]
    public class RackedWeaponUnloadAlert : RustPlugin
    {
        private const string PermAdmin = "rackedweaponunloadalert.admin";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Notify owner")]
            public bool NotifyOwner { get; set; } = true;

            [JsonProperty("Notify admins")]
            public bool NotifyAdmins { get; set; } = true;

            [JsonProperty("Include grid position")]
            public bool IncludeGrid { get; set; } = 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);

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

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["Owner"] = "{0} ({1}) took your {2} from the rack at {3}.",
                ["Admin"] = "[Rack] {0} unloaded {1} from a rack at {2}."
            }, this);
        }

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

        private void OnRackedWeaponUnloaded(Item slot, BasePlayer player, WeaponRack weaponRack)
        {
            if (slot == null || slot.info == null || player == null || weaponRack == null) return;

            string grid = config.IncludeGrid ? GridRef(weaponRack.transform.position) : "unknown";
            string remover = player.displayName;
            string weapon = slot.info.displayName.english;

            if (config.NotifyOwner && weaponRack.OwnerID != 0 && weaponRack.OwnerID != player.userID.Get())
            {
                var owner = BasePlayer.FindByID(weaponRack.OwnerID);
                if (owner != null && owner.IsConnected)
                {
                    SendReply(owner, Msg("Owner", owner.UserIDString, remover, player.UserIDString, weapon, grid));
                }
            }

            if (config.NotifyAdmins)
            {
                foreach (var admin in BasePlayer.activePlayerList)
                {
                    if (admin == null || !admin.IsConnected) continue;
                    if (admin.userID.Get() == player.userID.Get()) continue;
                    if (!permission.UserHasPermission(admin.UserIDString, PermAdmin)) continue;
                    SendReply(admin, Msg("Admin", admin.UserIDString, remover, weapon, grid));
                }
            }
        }

        private string GridRef(Vector3 position)
        {
            var x = Mathf.FloorToInt((position.x + 1024f) / 146.3f);
            var z = Mathf.FloorToInt((position.z + 1024f) / 146.3f);
            return $"{GridLetter(x)}{z}";
        }

        private string GridLetter(int column)
        {
            const string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            if (column < 0) column = 0;
            if (column >= letters.Length) column = letters.Length - 1;
            return letters[column].ToString();
        }
    }
}

The plugin watches OnRackedWeaponUnloaded and reports weapon removals to the rack owner and to server admins. On each unload it looks up the rack's OwnerID, resolves the owning player with BasePlayer.FindByID, and sends a localized message that names the remover, the weapon's display name and the rack's grid position. Admins with rackedweaponunloadalert.admin also receive a copy so they can spot theft from tool cupboard racks. The plugin ignores unloads where the rack has no owner, and it null-checks the item, its info, the player and the rack before touching them. A single config flag lets you switch off admin notices without unloading the plugin.

Common use cases

  • Audit weapon removal from racks

    Record who unloaded which weapon from which rack and when, so admins can review access to TC-protected gear racks.

  • Notify the rack owner

    Send a chat message or Discord-style notification to the owner whenever a weapon leaves their rack, including the item name and the remover.

  • Apply a per-player cooldown

    Track how often a player pulls weapons from racks and warn or block repeat behaviour with your own command layer, since the unload itself cannot be cancelled.

Pitfalls

  • The unload already happened

    The magazine is empty and the item is detached before the hook fires, so you cannot stop the action, restore the ammo or move the item back into the rack from here.

  • Player can be null

    UnloadWeapon is normally driven by a player via the rack UI, but always null-check player before reading UserIDString or sending chat, otherwise you risk a NullReferenceException in your hook.

  • Racks can outlive their owner

    OwnerID may point to a player who is offline or no longer exists on the server. Use BasePlayer.FindByID and treat a null result as 'no notification target'.

Build a plugin with OnRackedWeaponUnloaded

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

Prompt: Audit weapon removal from racks using the OnRackedWeaponUnloaded 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 OnRackedWeaponUnloaded

Generate