OnItemAddedToContainer
OnItemAddedToContainer is called right after an item has been inserted into an ItemContainer through ItemContainer.Insert, and fires for every successful insertion, including inventory moves, loot transfers and server-side grants.
void OnItemAddedToContainer(ItemContainer itemContainer, Item item, BasePlayer sourcePlayer)Parameters
| Parameter | Type | Description |
|---|---|---|
itemContainer | ItemContainer | The ItemContainer the item was inserted into (the target of ItemContainer.Insert). Compare against known containers such as player inventories or loot boxes to filter events. |
item | Item | The Item instance that was placed into the container. Its info.shortname, amount, skin and condition describe what was added. |
sourcePlayer | BasePlayer | The BasePlayer responsible for the insertion, or null when the item was added by server code, a timer or an entity (for example a furnace output or an admin give). |
Return value
The return value is ignored. The game's ItemContainer.Insert still returns true and the item stays in the container regardless of what your hook method returns.
How it works
The hook fires at the end of ItemContainer.Insert, after the item has already been placed in the container and after the container's internal contents-changed callback has run. It is a notification only: the insertion has already succeeded when your code runs.
Use itemContainer to tell which container received the item (a player's main inventory, belt, wear slots, a lootable corpse, a box, an oven), item to inspect what was added, and sourcePlayer to know who moved it. sourcePlayer is null for server-driven insertions such as furnace output, admin console grants or plugin-created items.
Because the hook fires on every insertion, it runs frequently on populated servers. Keep the body cheap, filter early on itemContainer and item.info.shortname, and never call Puts or PrintWarning. To react only to player-held containers, compare itemContainer.playerOwner or check sourcePlayer != null. The hook cannot cancel the insertion; use CanAcceptItem or CanMoveItem if you need to block it.
Example plugin: Alert when a player picks up a supply signal
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Oxide.Plugins
{
[Info("SupplySignalAlert", "RustPluginGenerator", "1.0.0")]
[Description("Notifies players and staff when a supply signal lands in a player inventory")]
public class SupplySignalAlert : RustPlugin
{
private const string PermNotify = "supplysignalalert.notify";
private const string SignalShortname = "supply.signal";
private PluginConfig config;
private readonly Dictionary<ulong, Timer> cooldowns = new Dictionary<ulong, Timer>();
private class PluginConfig
{
[JsonProperty("Broadcast to staff admins")]
public bool BroadcastToAdmins { get; set; } = true;
[JsonProperty("Per-player cooldown in seconds")]
public float CooldownSeconds { get; set; } = 30f;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new System.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>
{
["HolderNotice"] = "A supply signal is now in your inventory. Use it wisely.",
["AdminNotice"] = "{0} received a supply signal."
}, 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(PermNotify, this);
}
private void Unload()
{
foreach (var entry in cooldowns)
{
entry.Value?.Destroy();
}
cooldowns.Clear();
}
private void OnItemAddedToContainer(ItemContainer itemContainer, Item item, BasePlayer sourcePlayer)
{
if (itemContainer == null || item == null || item.info == null) return;
if (item.info.shortname != SignalShortname) return;
BasePlayer holder = sourcePlayer;
if (holder == null && itemContainer.playerOwner != null)
{
holder = itemContainer.playerOwner;
}
if (holder == null || !holder.IsConnected) return;
ulong userId = holder.userID.Get();
if (cooldowns.ContainsKey(userId)) return;
cooldowns[userId] = timer.Once(config.CooldownSeconds, () => cooldowns.Remove(userId));
holder.ChatMessage(Msg("HolderNotice", holder.UserIDString));
if (!config.BroadcastToAdmins) return;
foreach (BasePlayer admin in BasePlayer.activePlayerList)
{
if (admin == null || admin == holder) continue;
if (!admin.IsAdmin && !permission.UserHasPermission(admin.UserIDString, PermNotify)) continue;
admin.ChatMessage(Msg("AdminNotice", admin.UserIDString, holder.displayName));
}
}
}
}The plugin watches every container insertion and fires a chat alert when a player ends up with a supply signal in one of their own containers. It reads the holder from sourcePlayer or the container's playerOwner, returns early when the item is not a supply signal, and sends a holder notice plus a staff broadcast to active admins and players with the supplysignalalert.notify permission (excluding the holder) when Broadcast to staff admins is enabled. A per-player cooldown timer, configurable via Per-player cooldown in seconds (default 30), prevents a chat flood when the same signal is moved between belt and main inventory.
Common use cases
Track admin-spawned items
When
sourcePlayeris null but the target container belongs to a player, the item was almost certainly added by server code or a plugin. Log these grants for staff auditing.Reward players for finding loot
Detect when a rare item (c4, rocket, targeting computer) lands in a player's inventory and trigger a notification, effect or economy deposit.
Enforce value limits
Keep a live count of high-value items per player by combining insertions with removals, then act with a separate command when a threshold is exceeded.
Pitfalls
It fires for every insertion
Belt swaps, stacking and loot transfers all call
Insert. Guard with a shortname check before doing any work, or the hook becomes a performance problem.sourcePlayer can be null
Server-side insertions such as furnace output or plugin grants do not pass a player. Accessing
sourcePlayer.UserIDStringwithout a null check will throw inside the hook.Re-adding items inside the hook
Giving an item to a player from this hook calls
Insertagain and recursively re-enters the hook. Use aNextTickor timer to defer any further inserts.
Build a plugin with OnItemAddedToContainer
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 OnItemAddedToContainer
Generate