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
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.
{
"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
| Command | What it does | Permission |
|---|---|---|
/barrelscrap status | Prints the current bonus chance, scrap amount range and VIP multiplier to chat. | barrelscrapbonus.use |
/barrelscrap reload | Reloads the plugin configuration from disk. | barrelscrapbonus.admin |
| Permission | Grants |
|---|---|
barrelscrapbonus.use | Allows the player to run /barrelscrap status. |
barrelscrapbonus.admin | Allows reloading the config with /barrelscrap reload. |
barrelscrapbonus.vip | Applies the configured VIP multiplier to bonus scrap amounts. |
How the code works
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-LootContainerentities, and anyone who is not a real player by checkinginfo,info.InitiatorPlayer,IsNpc, andIsConnected.ShortPrefabNameis then passed to a helperIsBarrelthat matches it against the configurableBarrel Name Fragmentslist using a case-insensitiveIndexOf.Rolling the chance and amount
Chance is stored as a percentage in
Bonus Chance Percent. The plugin comparesUnityEngine.Random.Range(0f, 100f)againstconfig.BonusChance, so15means a 15 percent roll. On success it picks a random integer withUnityEngine.Random.Range(config.MinScrap, config.MaxScrap + 1), then multiplies byVIP Multiplierwhen the player holdsbarrelscrapbonus.vip. The result is rounded down withMathf.FloorToIntso the scrap amount is a valid integer.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 withGiveItem. A chat message built through the language system confirms the bonus whenNotify Playeris enabled.Config keys you can tune
The nested
PluginConfigclass exposesBonus Chance Percent,Min Scrap,Max Scrap,VIP Multiplier,Notify Player, andBarrel Name Fragments. Loading goes throughLoadConfig, which falls back toLoadDefaultConfigand callsPrintWarningonce if the JSON cannot be read.SaveConfigwrites back withConfig.WriteObject. Admins editoxide/config/BarrelScrapBonus.jsonand run/barrelscrap reloadto apply.Permissions and commands
Three permissions are registered in
Init:barrelscrapbonus.use,barrelscrapbonus.admin, andbarrelscrapbonus.vip. The VIP one is checked on every kill, so granting or revoking it takes effect immediately./barrelscrapis aChatCommandhandler that inspects the first argument:statusrequires the use permission and prints the current values,reloadrequires the admin permission and callsLoadConfig. All player-facing text goes throughlang.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 fromLoadConfig.Unloadsaves the config withSaveConfigso edits made while running are preserved. The defaultBarrel Name Fragmentslist isloot-barrel,loot-barrel-1,loot-barrel-2, andloot_barrel, which covers standard and small barrels; add new fragments after a game update if a prefab renames.
Install it on your server
Save BarrelScrapBonus.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/BarrelScrapBonus.json(Carbon:carbon/configs/BarrelScrapBonus.json). After editing, reload it:oxide.reload BarrelScrapBonus c.reload BarrelScrapBonus # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip: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 versionDaily cap per player
Limit the number of bonus scrap awards a player can earn per day.
Generate this versionRandom 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.
Edit the prompt on the next screen. 2 free plugins a month, no card.
More loot plugins
- 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
- Dropped item despawn timeThis plugin changes how long dropped item containers stay on the ground before Rust despawns them, using a global multiplier plus per-shortnameโฆOnItemDropped
Last updated Oct 26, 2026.
Customize barrel scrap bonus
Customize