OnRackedWeaponSwap
OnRackedWeaponSwap is called when a player takes a weapon off a weapon rack and the game is about to move that item from the rack back into the player's hands.
object OnRackedWeaponSwap(Item item, WeaponRackSlot weaponAtIndex, BasePlayer player, WeaponRack weaponRack)Parameters
| Parameter | Type | Description |
|---|---|---|
item | Item | The WeaponRackItem (Item) being taken off the rack and handed back to the player. Its parent container is the weapon rack inventory slot. |
weaponAtIndex | WeaponRackSlot | The WeaponRackSlot the weapon currently occupies. Useful for checking which mount point is being freed. |
player | BasePlayer | The BasePlayer performing the swap. Check permissions, team, or cooldowns against this player. |
weaponRack | WeaponRack | The WeaponRack entity the weapon is being removed from. Use it for OwnerID checks, building privilege lookups and position. |
Return value
Returning null (or nothing) lets the swap continue: the item is removed from the rack container, handed to the player and mounted in the chosen cell. Returning any non-null value stops the original code path, so the weapon stays on the rack and the player receives nothing.
How it works
The hook fires inside WeaponRack.SwapPlayerWeapon, right after the server has picked a free cell on the rack for the item's replacement (if any) and before the rack item is removed from its container, given to the player and mounted. It runs once per swap attempt, only for the player who triggered the interaction.
Use item to inspect shortname, skin, condition or damage state of the weapon leaving the rack. weaponAtIndex tells you which slot is being vacated, player is the actor, and weaponRack gives you OwnerID, the entity position and GetBuildingPrivilege() for authorization checks. There is no more specific type to narrow to; all four parameters are exact object types.
Because the hook runs before the removal and the GivePlayerWeapon call, blocking it leaves both the rack and the player's hands untouched, so the weapon simply stays mounted. This is the right place for per-player limits such as a cooldown, a permission gate or a combat lockout, rather than for post-swap logic like logging the new loadout.
Example plugin: Block weapon rack swaps while a player is in combat or raiding
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Rack Swap Combat Block", "RustPluginGenerator", "1.0.0")]
[Description("Blocks taking weapons off weapon racks while a player is in combat or lacks building privilege.")]
public class RackSwapCombatBlock : RustPlugin
{
private const string PermBypass = "rackswapcombatblock.bypass";
private PluginConfig config;
private readonly Dictionary<ulong, float> lastCombat = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Combat block seconds")]
public float CombatBlockSeconds { get; set; } = 15f;
[JsonProperty("Require building privilege")]
public bool RequirePrivilege { 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);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["BlockedCombat"] = "You cannot swap racked weapons during combat. Wait a few seconds.",
["BlockedPrivilege"] = "You need building privilege to take this weapon off the rack."
}, this);
}
private string Msg(string key, string userId = null) => lang.GetMessage(key, this, userId);
private void Init()
{
permission.RegisterPermission(PermBypass, this);
}
private void Unload()
{
lastCombat.Clear();
}
private object OnRackedWeaponSwap(Item item, WeaponRackSlot weaponAtIndex, BasePlayer player, WeaponRack weaponRack)
{
if (item?.info == null || player == null || weaponRack == null) return null;
if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;
if (config.RequirePrivilege && !player.IsAdmin)
{
var privilege = weaponRack.GetBuildingPrivilege();
if (privilege != null && !privilege.IsAuthed(player) && weaponRack.OwnerID != player.userID.Get())
{
player.ChatMessage(Msg("BlockedPrivilege", player.UserIDString));
return true;
}
}
float stamp;
if (lastCombat.TryGetValue(player.userID.Get(), out stamp))
{
if (UnityEngine.Time.realtimeSinceStartup - stamp < config.CombatBlockSeconds)
{
player.ChatMessage(Msg("BlockedCombat", player.UserIDString));
return true;
}
}
return null;
}
private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
{
if (entity == null || info == null) return;
float now = UnityEngine.Time.realtimeSinceStartup;
var victim = entity as BasePlayer;
if (victim != null && !victim.IsNpc)
{
lastCombat[victim.userID.Get()] = now;
}
var attacker = info.InitiatorPlayer;
if (attacker != null && !attacker.IsNpc)
{
lastCombat[attacker.userID.Get()] = now;
}
}
}
}The plugin tracks when each player last dealt or took damage through OnEntityTakeDamage, storing the timestamp in a dictionary keyed by user ID. In OnRackedWeaponSwap it checks that timestamp: if the player has been in combat within the configured window, or if the rack sits in a building without privilege for that player, the hook returns a non-null value and the swap is cancelled. Otherwise it returns null and the vanilla behaviour proceeds. Only the cooldown duration and the privilege check are configurable, and a brief chat message explains the refusal using the language file.
Common use cases
Combat lockout on rack swaps
Prevent players from hot-swapping weapons during a raid or fight by blocking the swap for a few seconds after their last damage event.
Rack access permissions
Allow only players with a VIP permission to remove weapons from racks they do not own, and cancel the swap for everyone else.
Weapon condition logging
Record the durability of a weapon as it leaves the rack, so you can audit condition loss or refund broken items after the fact. Return null so the swap still happens.
Pitfalls
Blocking does not message the player automatically
The game silently does nothing when you return non-null. Send your own chat or console message, otherwise the player just sees the weapon stay on the rack.
The item is still in the rack container
At this point
item.parentis the rack inventory slot, not the player. Do not assume the weapon is already in the player's inventory, and avoid callingMoveToContaineron it here.OwnerID is not the same as building privilege
A rack's OwnerID only reflects who placed it. Players on a team or a shared TC can still use it, so check
GetBuildingPrivilege().IsAuthed(player)when you need real access control.
Build a plugin with OnRackedWeaponSwap
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 OnRackedWeaponSwap
Generate