How to make a Rust dropped item despawn time plugin (global multiplier and per-item overrides)
This plugin changes how long dropped item containers stay on the ground before Rust despawns them, using a global multiplier plus per-shortname overrides. It uses OnItemDropped(Item, BaseEntity) to find the spawned DroppedItemContainer and apply a new despawn time.
By default Rust removes dropped items after a fixed despawn window, and admins who want loot to linger (or disappear faster) have no built-in knob for it. The plugin hooks OnItemDropped, which fires when an item is dropped and its world entity is created, then schedules its own despawn timer that calls Kill() on the entity after the configured duration.
The config controls everything. A global multiplier scales a 300-second baseline for every drop, and a dictionary of shortname to seconds overrides specific items so you can, for example, let loot bags sit for ten minutes while scrap despawns in five. A debug toggle logs each scheduled drop to the server console so you can confirm the values match what you expect.
The plugin code
using UnityEngine;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Item Despawn Time", "RustPluginGenerator", "1.0.0")]
[Description("Scales how long dropped items stay in the world using a global multiplier and per-item overrides.")]
public class ItemDespawnTime : RustPlugin
{
private const string PermAdmin = "itemdespawntime.admin";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Global multiplier")]
public float GlobalMultiplier { get; set; } = 1f;
[JsonProperty("Per-item overrides (shortname: seconds)")]
public Dictionary<string, float> Overrides { get; set; } = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase)
{
["loot-bag"] = 600f,
["scrap"] = 300f
};
[JsonProperty("Debug logging")]
public bool Debug { get; set; } = false;
}
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);
}
private void OnItemDropped(Item item, BaseEntity entity)
{
if (item == null || entity == null || entity.IsDestroyed) return;
if (config == null) return;
BaseEntity target = entity as DroppedItemContainer;
if (target == null) target = entity as DroppedItem;
if (target == null) return;
string shortname = item.info != null ? item.info.shortname : target.ShortPrefabName;
if (string.IsNullOrEmpty(shortname)) return;
float newDuration;
if (config.Overrides != null && config.Overrides.TryGetValue(shortname, out float overrideSeconds))
{
if (overrideSeconds <= 0f) return;
newDuration = overrideSeconds;
}
else
{
// Default dropped item lifetime in Rust is 5 minutes (300 seconds).
const float defaultLifetime = 300f;
newDuration = defaultLifetime * config.GlobalMultiplier;
if (newDuration <= 0f) return;
}
// BaseEntity no longer exposes despawnTime/spawnTime; schedule our own despawn timer instead.
NextTick(() =>
{
if (target == null || target.IsDestroyed) return;
timer.Once(newDuration, () =>
{
if (target == null || target.IsDestroyed) return;
target.Kill();
});
});
if (config.Debug)
{
Puts(string.Format("Scheduled despawn for {0} in {1:0}s", shortname, newDuration));
}
}
[ConsoleCommand("itemdespawn.status")]
private void CmdStatus(ConsoleSystem.Arg arg)
{
if (arg == null) return;
var player = arg.Player();
if (player != null && !permission.UserHasPermission(player.UserIDString, PermAdmin))
{
arg.ReplyWith("You do not have permission to use this command.");
return;
}
int overrideCount = config?.Overrides?.Count ?? 0;
string reply = string.Format("Multiplier: {0:0.##} | Overrides: {1} | Debug: {2}",
config?.GlobalMultiplier ?? 1f, overrideCount, config != null && config.Debug);
arg.ReplyWith(reply);
}
}
}Hooks used: OnItemDropped
Config file
Created automatically on first load at oxide/config/ItemDespawnTime.json.
{
"Global multiplier": 1,
"Per-item overrides (shortname: seconds)": {
"loot-bag": 600,
"scrap": 300
},
"Debug logging": false
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
itemdespawn.status | Prints the current global multiplier, override count and whether debug logging is on. | itemdespawntime.admin |
| Permission | Grants |
|---|---|
itemdespawntime.admin | Allows use of the itemdespawn.status console command. |
How the code works
Find the entity the drop created
OnItemDropped(Item item, BaseEntity entity)fires after the game has spawned the world object for a dropped item, and theentityparameter is that object. The plugin accepts eitherDroppedItemContainerorDroppedItem; anything else is ignored. It keys onitem.info.shortnamewhen available, falling back totarget.ShortPrefabName.Schedule your own despawn timer
The plugin does not read or modify the entity's spawn/despawn fields. Instead it computes a new duration and, on the next tick, calls
timer.Once(newDuration, ...)and thentarget.Kill()when the timer fires. The lambda re-checks that the entity still exists and is not destroyed before killing it, so manually removed items are not touched twice.Compute the duration
If the item shortname is in
Per-item overrides, the override seconds are used directly. Otherwise the duration is300f * Global multiplier. A value of0or below (from either path) causes the plugin to skip that drop and leave the entity alone. Lookup in the override dictionary is case-insensitive viaStringComparer.OrdinalIgnoreCase.Why the hook runs per item, not per player
OnItemDroppedis called once each time an item becomes a world object, so a stack split into several drops produces several calls. The plugin only touches the entity it receives, so performance stays flat even on a busy server. Because the call also fires for NPC and container drops, the same rules apply everywhere. Check the config if a specific drop seems untouched: some plugin-driven drops bypass this hook entirely.Config reload and status output
LoadConfigfalls back toLoadDefaultConfigwhen the JSON is invalid, which keeps the plugin running with sane numbers. The console commanditemdespawn.status(registered with[ConsoleCommand]and gated byitemdespawntime.adminfor in-game callers) reports the active multiplier, the number of overrides and the debug flag, so an admin can confirm a saved config actually took effect without reading the file. The command is safe to run from RCON, where no player is attached and the permission check is skipped.Debug logging without spam
When
Debug loggingis enabled, each adjusted drop writes one line to the server console with the shortname and the new duration. This is off by default because drops can be frequent. Enabling it briefly during setup is the fastest way to prove the numbers are correct, and disabling it afterwards stops the log noise. The plugin writes only to the config file; no data file is used.
Install it on your server
Save ItemDespawnTime.cs
Keep the file name exactly as the class name. Oxide matches them.Upload it to the plugins folder
Oxide:oxide/plugins/. Carbon:carbon/plugins/. The server compiles and loads it within a few seconds, no restart needed.Edit the config if you want
On first load the default config is written tooxide/config/ItemDespawnTime.json(Carbon:carbon/configs/ItemDespawnTime.json). After editing, reload it:oxide.reload ItemDespawnTime c.reload ItemDespawnTime # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default itemdespawntime.admin c.grant group default itemdespawntime.admin # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
Minimum and maximum clamps
Adds hard limits so no drop lasts longer or shorter than the admin allows.
Generate this versionCategory-based rules
Applies despawn rules per item category instead of per shortname.
Generate this versionPlayer dropped items only
Leaves container and NPC drops at the default despawn time.
Generate this version
FAQ
Does this work for both loot bags and plain dropped items?
Yes. The hook receives whichever entity the game spawned, and the plugin handles both DroppedItemContainer and DroppedItem. Loot bags from deaths and single items dropped from the inventory are both covered.
What does the multiplier actually multiply?
It multiplies a hardcoded 300-second baseline, not the entity's remaining lifetime. A multiplier of 2 gives a 600-second despawn; 0.5 gives 150 seconds. Overrides replace that computed value entirely with a fixed number of seconds.
Why is my override not taking effect?
The key must match the item shortname case-insensitively, for example scrap or loot-bag. Confirm the shortname, check Debug logging in the config, and reload the plugin after editing Per-item overrides. A value of 0 or below is treated as skip, not as instant despawn.
Will this affect entities that already exist?
No. The plugin only schedules a despawn for drops at the moment they spawn. Items already on the ground keep the timing they were created with. To apply new settings you need new drops.
How do I install this on Oxide or Carbon?
Place ItemDespawnTime.cs in oxide/plugins, run oxide.reload ItemDespawnTime from the console, then edit oxide/config/ItemDespawnTime.json. Carbon loads the same file from its plugins folder. The default config sets a global multiplier of 1.0 but also ships overrides for loot-bag (600s) and scrap (300s), which take precedence for those items.
Customize it for your server
Change the reward, add a cooldown, restrict it to VIPs. Start from this prompt and edit anything.
Edit the prompt on the next screen. 2 free plugins a month, no card.
More loot plugins
- Barrel scrap bonusAwards bonus scrap when a player destroys a loot barrel, with a configurable chance and a VIP multiplier permission.OnEntityDeath
- First opener crate lockThis plugin gives the first player who opens a loot container a temporary claim on it, enforced through the CanLootEntity hook, so anyone outsideโฆCanLootEntityOnEntityKill
- Custom loot tablesReplaces the contents of configured LootContainer crates, matched by short prefab name, with weighted items that have per-item chances and min/maxโฆOnLootSpawn
Last updated Oct 26, 2026.
Customize dropped item despawn time
Customize