Skip to content
RustPluginGenerator

How to make a Rust barrel scrap bonus plugin (Oxide/Carbon)

Awards bonus scrap when a player destroys a loot barrel, with a configurable chance and a VIP multiplier permission. It uses the OnEntityDeath hook to detect barrel deaths and the killing player.

Loot barrels are a steady early-wipe scrap source, but on many servers they feel identical to break and the payout is flat. Admins who want to steer players toward barrels (or reward VIPs) need a way to add extra scrap on top of the normal loot without touching the loot tables themselves.

This plugin hooks OnEntityDeath, checks that the entity is a LootContainer whose ShortPrefabName matches a configured fragment list, confirms a real player landed the killing blow through info.InitiatorPlayer, and then rolls a chance from config. On success it creates scrap with ItemManager.CreateByName and hands it to the player with GiveItem, applying a multiplier if the player has the VIP permission. A chat command lets you inspect the current rates or reload the config.

The plugin code

BarrelScrapBonus.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Barrel Scrap Bonus", "RustPluginGenerator", "1.0.0")]
    [Description("Awards configurable bonus scrap when a player destroys a loot barrel, with a VIP multiplier.")]
    public class BarrelScrapBonus : RustPlugin
    {
        private const string PermUse = "barrelscrapbonus.use";
        private const string PermAdmin = "barrelscrapbonus.admin";
        private const string PermVip = "barrelscrapbonus.vip";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Bonus Chance Percent")]
            public float BonusChance { get; set; } = 15f;

            [JsonProperty("Min Scrap")]
            public int MinScrap { get; set; } = 10;

            [JsonProperty("Max Scrap")]
            public int MaxScrap { get; set; } = 25;

            [JsonProperty("VIP Multiplier")]
            public float VipMultiplier { get; set; } = 2f;

            [JsonProperty("Notify Player")]
            public bool NotifyPlayer { get; set; } = true;

            [JsonProperty("Barrel Name Fragments")]
            public List<string> BarrelFragments { get; set; } = new List<string>
            {
                "loot-barrel",
                "loot-barrel-1",
                "loot-barrel-2",
                "loot_barrel"
            };
        }

        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>
            {
                ["Bonus"] = "You found {0} bonus scrap in the barrel.",
                ["Status"] = "Chance: {0}%, Scrap: {1}-{2}, VIP multiplier: x{3}",
                ["Reloaded"] = "Barrel scrap bonus configuration reloaded.",
                ["NoPermission"] = "You do not have permission to use this command.",
                ["Usage"] = "Usage: /barrelscrap <status|reload>"
            }, 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(PermUse, this);
            permission.RegisterPermission(PermAdmin, this);
            permission.RegisterPermission(PermVip, this);
        }

        private void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
        {
            if (entity == null || info == null) return;

            var container = entity as LootContainer;
            if (container == null) return;

            var player = info.InitiatorPlayer;
            if (player == null || player.IsNpc || !player.IsConnected) return;

            var prefab = container.ShortPrefabName ?? string.Empty;
            if (!IsBarrel(prefab)) return;

            if (UnityEngine.Random.Range(0f, 100f) > config.BonusChance) return;

            int rolled = UnityEngine.Random.Range(config.MinScrap, config.MaxScrap + 1);
            if (rolled <= 0) return;

            if (permission.UserHasPermission(player.UserIDString, PermVip) && config.VipMultiplier > 0f)
            {
                rolled = Mathf.FloorToInt(rolled * config.VipMultiplier);
            }

            if (rolled <= 0) return;

            var scrap = ItemManager.CreateByName("scrap", rolled);
            if (scrap == null) return;

            player.GiveItem(scrap);

            if (config.NotifyPlayer)
            {
                player.ChatMessage(Msg("Bonus", player.UserIDString, rolled));
            }
        }

        private bool IsBarrel(string prefabName)
        {
            if (string.IsNullOrEmpty(prefabName) || config.BarrelFragments == null) return false;
            foreach (var fragment in config.BarrelFragments)
            {
                if (string.IsNullOrEmpty(fragment)) continue;
                if (prefabName.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) >= 0) return true;
            }
            return false;
        }

        [ChatCommand("barrelscrap")]
        private void CmdBarrelScrap(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;

            if (args == null || args.Length == 0)
            {
                player.ChatMessage(Msg("Usage", player.UserIDString));
                return;
            }

            var sub = args[0].ToLowerInvariant();

            if (sub == "status")
            {
                if (!permission.UserHasPermission(player.UserIDString, PermUse))
                {
                    player.ChatMessage(Msg("NoPermission", player.UserIDString));
                    return;
                }

                player.ChatMessage(Msg("Status", player.UserIDString,
                    config.BonusChance, config.MinScrap, config.MaxScrap, config.VipMultiplier));
                return;
            }

            if (sub == "reload")
            {
                if (!permission.UserHasPermission(player.UserIDString, PermAdmin))
                {
                    player.ChatMessage(Msg("NoPermission", player.UserIDString));
                    return;
                }

                LoadConfig();
                player.ChatMessage(Msg("Reloaded", player.UserIDString));
                return;
            }

            player.ChatMessage(Msg("Usage", player.UserIDString));
        }

        private void Unload()
        {
            SaveConfig();
        }
    }
}

Hooks used: OnEntityDeath

Config file

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

BarrelScrapBonus.json
{
  "Bonus Chance Percent": 15,
  "Min Scrap": 10,
  "Max Scrap": 25,
  "VIP Multiplier": 2,
  "Notify Player": true,
  "Barrel Name Fragments": [
    "loot-barrel",
    "loot-barrel-1",
    "loot-barrel-2",
    "loot_barrel"
  ]
}

Commands and permissions

Chat and console commands
CommandWhat it doesPermission
/barrelscrap statusPrints the current bonus chance, scrap amount range and VIP multiplier to chat.barrelscrapbonus.use
/barrelscrap reloadReloads the plugin configuration from disk.barrelscrapbonus.admin
Permissions
PermissionGrants
barrelscrapbonus.useAllows the player to run /barrelscrap status.
barrelscrapbonus.adminAllows reloading the config with /barrelscrap reload.
barrelscrapbonus.vipApplies the configured VIP multiplier to bonus scrap amounts.

How the code works

  1. Detecting barrels in OnEntityDeath

    The OnEntityDeath(BaseCombatEntity entity, HitInfo info) hook fires for every entity death, so the first job is filtering. The plugin rejects null entities, non-LootContainer entities, and anyone who is not a real player by checking info, info.InitiatorPlayer, IsNpc, and IsConnected. ShortPrefabName is then passed to a helper IsBarrel that matches it against the configurable Barrel Name Fragments list using a case-insensitive IndexOf.

  2. Rolling the chance and amount

    Chance is stored as a percentage in Bonus Chance Percent. The plugin compares UnityEngine.Random.Range(0f, 100f) against config.BonusChance, so 15 means a 15 percent roll. On success it picks a random integer with UnityEngine.Random.Range(config.MinScrap, config.MaxScrap + 1), then multiplies by VIP Multiplier when the player holds barrelscrapbonus.vip. The result is rounded down with Mathf.FloorToInt so the scrap amount is a valid integer.

  3. Delivering the scrap

    ItemManager.CreateByName("scrap", rolled) can return null if the shortname is unknown, so the plugin null-checks before continuing. The item is handed to the player with GiveItem. A chat message built through the language system confirms the bonus when Notify Player is enabled.

  4. Config keys you can tune

    The nested PluginConfig class exposes Bonus Chance Percent, Min Scrap, Max Scrap, VIP Multiplier, Notify Player, and Barrel Name Fragments. Loading goes through LoadConfig, which falls back to LoadDefaultConfig and calls PrintWarning once if the JSON cannot be read. SaveConfig writes back with Config.WriteObject. Admins edit oxide/config/BarrelScrapBonus.json and run /barrelscrap reload to apply.

  5. Permissions and commands

    Three permissions are registered in Init: barrelscrapbonus.use, barrelscrapbonus.admin, and barrelscrapbonus.vip. The VIP one is checked on every kill, so granting or revoking it takes effect immediately. /barrelscrap is a ChatCommand handler that inspects the first argument: status requires the use permission and prints the current values, reload requires the admin permission and calls LoadConfig. All player-facing text goes through lang.

  6. Guard rails for servers

    The hook runs often, so no console logging happens inside OnEntityDeath; the only console warning in the plugin is the invalid-config message from LoadConfig. Unload saves the config with SaveConfig so edits made while running are preserved. The default Barrel Name Fragments list is loot-barrel, loot-barrel-1, loot-barrel-2, and loot_barrel, which covers standard and small barrels; add new fragments after a game update if a prefab renames.

Install it on your server

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

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

Variations

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

  • Bonus scrap for crates too

    Apply the same chance to elite crates and hackable crates alongside barrels.

    Generate this version
  • Daily cap per player

    Limit the number of bonus scrap awards a player can earn per day.

    Generate this version
  • Random rare item drops

    Occasionally award a low-tier item instead of scrap for flavor.

    Generate this version

FAQ

Does the plugin interfere with normal barrel loot?

No. It listens for the death of the barrel and then creates bonus scrap separately. The barrel's own loot table still spawns as normal. If you want the bonus to replace part of the loot, use a different plugin that edits loot tables directly.

How do I install it on Oxide or Carbon?

Drop the .cs file into oxide/plugins, run oxide.reload BarrelScrapBonus from the console, then edit oxide/config/BarrelScrapBonus.json for your chance, amounts and multiplier. Carbon loads the same file; no conversion is needed.

Why do some barrels not trigger the bonus?

The helper IsBarrel matches ShortPrefabName against the fragments in Barrel Name Fragments. If a barrel prefab changes after a game update, add a new fragment to the list or replace an existing one. Matching is case-insensitive.

Does the bonus apply when a barrel is destroyed by an NPC or decay?

Only player-caused deaths can award scrap. The hook requires an info.InitiatorPlayer that is not an NPC, so NPC fire, decay and explosions from other sources will not grant bonus scrap to anyone.

Can I make the reward scale with player rank?

Yes. Grant barrelscrapbonus.vip to a rank group and set VIP Multiplier above 1.0 in config. The multiplier is applied to the rolled amount then floored, so a multiplier of 2.0 doubles the scrap on average.

Customize it for your server

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

Prompt: Create an Oxide plugin called BarrelScrapBonus that awards bonus scrap on loot barrel destruction using OnEntityDeath, with a chance percentage, min/max scrap, a VIP multiplier permission, and /barrelscrap status and reload commands.
Customize for my server

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

Last updated Oct 26, 2026.

Customize barrel scrap bonus

Customize