OnItemDropped
OnItemDropped is called from Item.Drop after the item has been removed from its container and the world entity has been created, but before the drop routine finishes. It fires for every scripted item drop, not for items that simply fall out of a lootable container.
void OnItemDropped(Item item, BaseEntity entity)Parameters
| Parameter | Type | Description |
|---|---|---|
item | Item | The Item instance being dropped. It still holds its amount, skin, condition and item definition at this point, but has already been detached from its parent container before the hook fires. |
entity | BaseEntity | The BaseEntity created by Item.Drop to represent the dropped item, usually a DroppedItem. It is the value the original method returns and is already spawned when the hook runs. |
Return value
The return value is ignored; whatever you return, the item entity remains spawned and Item.Drop returns it normally. To prevent a drop you must block it earlier (for example in CanMoveItem), not here.
How it works
The hook is patched into Item.Drop, which is the code path shared by Item.Drop(Vector3, Vector3), BasePlayer.DropItem and every other scripted drop. It fires once per dropped item after the parent container has been stripped and the world entity (DroppedItem in the normal case) has been spawned and assigned, but before RemoveFromContainer() runs and the entity is returned to the caller.
The item parameter gives you the live Item object: check item.info.shortname, item.skin, item.amount or item.condition. The entity parameter is the spawned BaseEntity, so you can read its net.ID.Value, transform.position or cast it to DroppedItem to inspect further. Because the item has already left its container, moving it back into item.parent does not work here.
This hook does not fire for admins running inventory.give through the admin console, mob loot spawning, or items that are removed rather than dropped (for example when the container is destroyed). It also does not fire when a container is destroyed by decay or when a player's corpse spawns. Declaring the second parameter as DroppedItem narrows nothing at the game side but is useful as a sanity check.
The return value is ignored, so the drop always happens. To actually prevent a drop you must use CanMoveItem or cancel at the player command level.
Example plugin: Announce and log rare item drops to chat
using System.Linq;
using UnityEngine;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Rare Drop Announcer", "RustPluginGenerator", "1.0.0")]
[Description("Announces rare item drops in chat and tracks per-player cooldowns.")]
public class RareDropAnnouncer : RustPlugin
{
private const string PermBroadcast = "raredropannouncer.broadcast";
private PluginConfig config;
private readonly Dictionary<ulong, float> cooldowns = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Announce in global chat")]
public bool GlobalChat { get; set; } = true;
[JsonProperty("Rare item shortnames")]
public List<string> RareItems { get; set; } = new List<string> { "rifle.ak", "lmg.m249", "ammo.rocket.basic", "explosives", "targeting.computer" };
[JsonProperty("Cooldown seconds per player")]
public float CooldownSeconds { get; set; } = 20f;
}
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>
{
["Announce"] = "{0} dropped {1} x{2} at {3}",
["Unknown"] = "Someone"
}, 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(PermBroadcast, this);
}
private void OnItemDropped(Item item, BaseEntity entity)
{
if (item?.info == null || entity == null) return;
if (!config.GlobalChat && !permission.UserHasPermission(entity.OwnerID.ToString(), PermBroadcast)) return;
if (config.RareItems == null || !config.RareItems.Contains(item.info.shortname)) return;
ulong owner = entity.OwnerID;
float now = UnityEngine.Time.realtimeSinceStartup;
if (owner != 0 && cooldowns.TryGetValue(owner, out float last) && now - last < config.CooldownSeconds) return;
cooldowns[owner] = now;
string playerName = owner != 0 ? (BasePlayer.FindByID(owner)?.displayName ?? Msg("Unknown")) : Msg("Unknown");
UnityEngine.Vector3 pos = entity.transform.position;
string location = $"({pos.x:0}, {pos.y:0}, {pos.z:0})";
Server.Broadcast(Msg("Announce", null, playerName, item.info.displayName.english, item.amount, location));
}
private void Unload()
{
cooldowns.Clear();
}
}
}The plugin keeps a configurable list of item shortnames that are considered rare. OnItemDropped checks the shortname and, if it matches, formats a chat message with the item's display name, amount and the dropper's position. The dropper is resolved from entity.OwnerID when the dropped entity carries an owner (set by BasePlayer.DropItem), falling back to "unknown" otherwise. A simple per-player cooldown keyed on OwnerID prevents chat spam when a player drops the same rare item repeatedly within a short window. All text goes through the language file, and the config is loaded and validated on startup.
Common use cases
Track where valuable loot is dropped
Log rare item drops with the world position and the owning player to a data file so admins can investigate theft or duplication reports.
Broadcast rare drops to the server
Send a chat or console broadcast when a player drops a high-value item such as an M249 or rocket, so nearby players know a fight is about to happen.
Skin-based alerting
Filter on
item.skinto flag when a dropped item uses a specific skin, which helps on servers with custom skin stores.
Pitfalls
The item is already out of its container
By the time the hook fires,
RemoveFromContainer()has already been scheduled and the item no longer lives in its parent. Do not try to move it back intoitem.parent; useCanMoveItemif you need to block the drop.Fires on every scripted drop
Every call to
Item.Dropruns this hook, including internal drops from plugins and from vanilla death handling. Keep the handler cheap and avoid spawning effects unless a condition matches.Do not destroy the entity to cancel
entity.Kill()inside the hook removes the world entity but the item itself is already detached from its container, so the stack vanishes into nothing. Use a prevention hook instead.
Plugin recipes that use OnItemDropped
Build a plugin with OnItemDropped
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 OnItemDropped
Generate