Skip to content
RustPluginGenerator

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

ItemDespawnTime.cs
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.

ItemDespawnTime.json
{
  "Global multiplier": 1,
  "Per-item overrides (shortname: seconds)": {
    "loot-bag": 600,
    "scrap": 300
  },
  "Debug logging": false
}

Commands and permissions

Chat and console commands
CommandWhat it doesPermission
itemdespawn.statusPrints the current global multiplier, override count and whether debug logging is on.itemdespawntime.admin
Permissions
PermissionGrants
itemdespawntime.adminAllows use of the itemdespawn.status console command.

How the code works

  1. 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 the entity parameter is that object. The plugin accepts either DroppedItemContainer or DroppedItem; anything else is ignored. It keys on item.info.shortname when available, falling back to target.ShortPrefabName.

  2. 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 then target.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.

  3. Compute the duration

    If the item shortname is in Per-item overrides, the override seconds are used directly. Otherwise the duration is 300f * Global multiplier. A value of 0 or below (from either path) causes the plugin to skip that drop and leave the entity alone. Lookup in the override dictionary is case-insensitive via StringComparer.OrdinalIgnoreCase.

  4. Why the hook runs per item, not per player

    OnItemDropped is 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.

  5. Config reload and status output

    LoadConfig falls back to LoadDefaultConfig when the JSON is invalid, which keeps the plugin running with sane numbers. The console command itemdespawn.status (registered with [ConsoleCommand] and gated by itemdespawntime.admin for 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.

  6. Debug logging without spam

    When Debug logging is 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

  1. Save ItemDespawnTime.cs

    Keep the file name exactly as the class name. Oxide matches them.
  2. 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.
  3. Edit the config if you want

    On first load the default config is written to oxide/config/ItemDespawnTime.json (Carbon: carbon/configs/ItemDespawnTime.json). After editing, reload it:oxide.reload ItemDespawnTime c.reload ItemDespawnTime # Carbon
  4. Grant the permission

    Give it to every player, or swap default for a group such as vip: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 version
  • Category-based rules

    Applies despawn rules per item category instead of per shortname.

    Generate this version
  • Player 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.

Prompt: Make a plugin that scales dropped item despawn time with a global multiplier of 2 and per-item overrides for loot-bag and scrap, with debug logging and an admin status command.
Customize for my server

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026.

Customize dropped item despawn time

Customize