Skip to content
RustPluginGenerator

How to make a Rust loot multiplier plugin (Oxide/Carbon)

This plugin multiplies the stack sizes of items generated inside loot containers such as crates and barrels, using a global multiplier, per-item overrides and a blacklist. It hooks OnLootSpawn(LootContainer) to scale the freshly generated inventory before the container opens.

Running a busy server usually means you want loot to feel more rewarding without rewriting the entire loot table. Instead of editing every prefab in the server filesystem, you can intercept the moment Rust fills a loot container and scale up the stacks it just produced.

The tricky part is doing this without touching weapons, tools and attachments that break if you multiply them. This plugin uses the OnLootSpawn(LootContainer lootContainer) hook, which fires after the container's inventory has been generated and before a player can open it. It applies a global multiplier, checks a shortname blacklist, then falls through to per-item overrides so you can keep wood at x5 while boosting scrap to x3. Stack sizes are always clamped to the item definition's stackable limit, and amounts are rounded down so you never create fractional items. A single permission-gated chat command lets admins check the current settings or reload the config file.

The plugin code

LootMultiplier.cs
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("Loot Multiplier", "RustPluginGenerator", "1.0.0")]
    [Description("Multiplies stackable items spawned inside loot containers with overrides and a blacklist.")]
    public class LootMultiplier : RustPlugin
    {
        private const string PermAdmin = "lootmultiplier.admin";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Global Multiplier")]
            public double GlobalMultiplier = 2.0;

            [JsonProperty("Ignored Shortnames")]
            public List<string> IgnoredShortnames = new List<string>
            {
                "rifle.ak",
                "rifle.lr300",
                "rifle.bolt",
                "rifle.semiauto",
                "smg.2",
                "smg.thompson",
                "smg.mp5",
                "pistol.semiauto",
                "pistol.m92",
                "pistol.python",
                "pistol.revolver",
                "shotgun.pump",
                "shotgun.double",
                "shotgun.waterpipe",
                "lmg.m249",
                "rocket.launcher",
                "explosive.timed",
                "explosive.satchel",
                "grenade.f1",
                "tool.camera",
                "hammer",
                "building.planner",
                "box.repair.bench",
                "toolgun"
            };

            [JsonProperty("Item Overrides")]
            public Dictionary<string, double> ItemOverrides = new Dictionary<string, double>
            {
                ["scrap"] = 3.0,
                ["wood"] = 5.0,
                ["metal.fragments"] = 4.0
            };
        }

        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>
            {
                ["NoPermission"] = "You do not have permission to use this command.",
                ["Status"] = "Loot multiplier: global {0}x, overrides: {1}, blacklisted: {2}.",
                ["Reloaded"] = "Loot multiplier configuration reloaded."
            }, 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(PermAdmin, this);
        }

        private object OnLootSpawn(LootContainer lootContainer)
        {
            if (lootContainer == null || lootContainer.inventory == null) return null;
            if (config == null) return null;

            var items = lootContainer.inventory.itemList;
            if (items == null || items.Count == 0) return null;

            foreach (var item in items)
            {
                if (item == null || item.info == null) continue;
                var shortname = item.info.shortname;
                if (string.IsNullOrEmpty(shortname)) continue;
                if (config.IgnoredShortnames != null && config.IgnoredShortnames.Contains(shortname)) continue;

                double multiplier = config.GlobalMultiplier;
                if (config.ItemOverrides != null && config.ItemOverrides.TryGetValue(shortname, out double ov))
                    multiplier = ov;

                if (multiplier <= 1.0) continue;

                int original = item.amount;
                int maxStack = Math.Max(1, item.info.stackable);
                int scaled = (int)Math.Floor(original * multiplier);
                if (scaled <= original) continue;
                if (scaled > maxStack) scaled = maxStack;
                if (scaled == original) continue;

                item.amount = scaled;
                item.MarkDirty();
            }

            return null;
        }

        [ChatCommand("lootmult")]
        private void CmdLootMult(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermAdmin))
            {
                SendReply(player, Msg("NoPermission", player.UserIDString));
                return;
            }

            string sub = args != null && args.Length > 0 ? args[0].ToLowerInvariant() : "status";
            if (sub == "reload")
            {
                LoadConfig();
                SendReply(player, Msg("Reloaded", player.UserIDString));
                return;
            }

            int overrideCount = config?.ItemOverrides?.Count ?? 0;
            int ignoreCount = config?.IgnoredShortnames?.Count ?? 0;
            double global = config?.GlobalMultiplier ?? 1.0;
            SendReply(player, Msg("Status", player.UserIDString, global, overrideCount, ignoreCount));
        }
    }
}

Hooks used: OnLootSpawn

Config file

Created automatically on first load at oxide/config/LootMultiplier.json.

LootMultiplier.json
{
  "Global Multiplier": 2,
  "Ignored Shortnames": [
    "rifle.ak",
    "rifle.lr300",
    "rifle.bolt",
    "rifle.semiauto",
    "smg.2",
    "smg.thompson",
    "smg.mp5",
    "pistol.semiauto",
    "pistol.m92",
    "pistol.python",
    "pistol.revolver",
    "shotgun.pump",
    "shotgun.double",
    "shotgun.waterpipe",
    "lmg.m249",
    "rocket.launcher",
    "explosive.timed",
    "explosive.satchel",
    "grenade.f1",
    "tool.camera",
    "hammer",
    "building.planner",
    "box.repair.bench",
    "toolgun"
  ],
  "Item Overrides": {
    "scrap": 3,
    "wood": 5,
    "metal.fragments": 4
  }
}

Commands and permissions

Chat and console commands
CommandWhat it doesPermission
/lootmult statusPrints the active global multiplier, override count and blacklist count to the caller.lootmultiplier.admin
/lootmult reloadReloads the config file from disk and reapplies the settings.lootmultiplier.admin
Permissions
PermissionGrants
lootmultiplier.adminGrants access to the /lootmult chat command.

How the code works

  1. Hook into loot spawning

    The plugin registers private object OnLootSpawn(LootContainer lootContainer). This hook fires after the container has generated its contents. We return null so Rust continues with its normal behavior and the container opens as usual; only the items are mutated. Every call first null-checks lootContainer and its inventory, then bails out when itemList is empty. The method also bails when config is null. Because this fires for every barrel, crate, food box and lootable corpse, the loop stays cheap: no logging, no allocations beyond what the multiply loop needs.

  2. Apply global multiplier and blacklist

    The handler loops over lootContainer.inventory.itemList and reads each item's shortname from item.info.shortname. Blacklisted shortnames (weapons, tools and attachments by default) are skipped with Contains before any multiplication. Non-blacklisted items get the global Global Multiplier from the config, then a per-item override from Item Overrides when one exists. Because we work on the stack Rust already generated, this composes cleanly with custom loot tables.

  3. Respect the stack limit

    item.info.stackable is the maximum amount the item definition allows in one slot. After multiplying, the amount is rounded down with Math.Floor and clamped to that limit so Rust never leaves the stack in an invalid state. If the scaled amount is not larger than the original, the item is left untouched and MarkDirty is never called. Items that gained quantity are marked dirty so connected clients see the correct stack when they open the container.

  4. Configure overrides and blacklist

    The PluginConfig class exposes Global Multiplier (a double, default 2.0), Ignored Shortnames (a list of strings, prefilled with common weapons and tools) and Item Overrides (a dictionary mapping shortname to a multiplier). The LoadConfig override reads the file, falls back to defaults with a warning if the file is invalid, and calls SaveConfig so the file is always complete. Settings are read into memory once, not parsed on every spawn.

  5. Chat command and permission

    [ChatCommand("lootmult")] handles a status subcommand that reports the current settings and a reload subcommand that calls LoadConfig. With no argument the command defaults to status. Both are gated on the lootmultiplier.admin permission registered in Init(). Each call checks permission.UserHasPermission(player.UserIDString, PermAdmin) first, so regular players cannot inspect or reload the plugin. Player-facing text uses the NoPermission, Status and Reloaded lang keys.

  6. Nothing to clean up on unload

    The plugin holds no active state that needs tearing down, so it does not override Unload. Everything it needs lives in the PluginConfig instance, which is written by SaveConfig during LoadConfig. If you later add a repeating action or a persistent data file, add an Unload override and clear those resources there. For the multiplier alone, Oxide unloading the plugin is enough.

Install it on your server

  1. Save LootMultiplier.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/LootMultiplier.json (Carbon: carbon/configs/LootMultiplier.json). After editing, reload it:oxide.reload LootMultiplier c.reload LootMultiplier # Carbon
  4. Grant the permission

    Give it to every player, or swap default for a group such as vip:oxide.grant group default lootmultiplier.admin c.grant group default lootmultiplier.admin # Carbon

Variations

Each one opens the generator with a ready prompt you can tweak.

  • Per-container multiplier

    Scale the multiplier based on the container's short prefab name so elite crates get a higher boost than food boxes.

    Generate this version
  • Only boost stackables

    Skip items whose stackable limit is 1 so weapons, tools and attachments are never touched.

    Generate this version
  • Loot multiplier by zone

    Apply a different multiplier inside monuments versus the open world using position checks.

    Generate this version

FAQ

Will this inflate loot that's already in a container?

No. OnLootSpawn fires before a container is opened for the first time, so it only affects freshly generated contents. Items players place into containers and items dropped on the ground are not affected, which keeps the multiplier predictable.

How do I keep weapons at x1?

The default Ignored Shortnames list already contains weapons, tools and attachments. Any shortname you add there is skipped before the multiplier is applied. You can find shortnames from the Rust item list or by giving yourself the item with inventory.give.

Can I set a different multiplier for one item?

Yes. Add an entry to Item Overrides in the config using the item's shortname as key and the multiplier as value, for example "scrap": 3.0. Per-item overrides take precedence over the global multiplier, but the blacklist still wins.

Does this work on Carbon as well as Oxide?

Yes. Carbon loads Oxide plugins unchanged, and this plugin only uses hooks and APIs present in both. Drop the .cs file into oxide/plugins/ or Carbon's plugin directory, let it compile, then edit oxide/config/LootMultiplier.json.

What happens if I set the multiplier very high?

Every stack is clamped to item.info.stackable, so a stack of rifle ammo will not exceed its definition limit. The item amount is also rounded down, so a x2.5 multiplier on an amount of 3 yields 7 (7.5 floored), not 8.

Customize it for your server

Change the reward, add a cooldown, restrict it to VIPs. Start from this prompt and edit anything.

Prompt: Generate a Rust Oxide plugin called LootMultiplier that multiplies stackable loot in LootContainer via OnLootSpawn, with a global multiplier of 2, per-item overrides, a blacklist of weapons, and an admin chat command to reload config.
Customize for my server

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

Last updated Oct 26, 2026.

Customize loot multiplier

Customize