How to make a Rust kill rewards plugin (scrap per kill, Oxide)
Awards a configurable item (scrap by default) to the killer on every player kill, with a separate headshot bonus and optional permission gate. It hooks OnEntityDeath, reads info.InitiatorPlayer, and skips NPC victims, suicides and team kills.
Most PvP servers want to pay players for kills, but vanilla Rust gives you nothing on a kill beyond loot. This plugin hooks OnEntityDeath, confirms the victim is a real, connected player, and confirms the killer is a different, connected, non-NPC player. It then hands out the configured item directly to the killer's inventory, adds a separate bonus when info.isHeadshot is true, and refuses when the two players share a team. A permission gate lets you restrict rewards to a rank, and every message goes through the language file so the wording is editable. All amounts, item shortnames and messages live in the config, so changing the reward from scrap to something else is a one-key edit plus a reload.
The plugin code
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("KillRewards", "RustPluginGenerator", "1.0.0")]
[Description("Awards a configurable item to the killer on every player kill, with an optional headshot bonus.")]
public class KillRewards : RustPlugin
{
private const string PermUse = "killrewards.use";
private PluginConfig config;
private bool warnedInvalidItem;
private class PluginConfig
{
[JsonProperty("Reward Shortname")]
public string RewardShortname { get; set; } = "scrap";
[JsonProperty("Reward Amount")]
public int RewardAmount { get; set; } = 25;
[JsonProperty("Headshot Bonus Amount")]
public int HeadshotBonusAmount { get; set; } = 25;
[JsonProperty("Require Permission")]
public bool RequirePermission { get; set; } = false;
[JsonProperty("Message Killer")]
public bool MessageKiller { get; set; } = true;
[JsonProperty("Announce To Server")]
public bool AnnounceToServer { 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);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["Reward"] = "You received {0} x {1} for killing {2}.",
["RewardHeadshot"] = "You received {0} x {1} for a headshot kill on {2}.",
["Announce"] = "{0} was killed by {1}.",
["InvalidItem"] = "KillRewards: the reward shortname '{0}' is not a valid item."
}, this);
}
private void Init()
{
permission.RegisterPermission(PermUse, this);
}
private void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
{
if (entity == null || info == null) return;
var victim = entity as BasePlayer;
if (victim == null || victim.IsNpc || !victim.IsConnected) return;
var killer = info.InitiatorPlayer;
if (killer == null || killer.IsNpc || !killer.IsConnected) return;
if (killer.userID == victim.userID) return;
// Team check: same non-zero team id means a team kill.
if (killer.currentTeam != 0 && killer.currentTeam == victim.currentTeam) return;
if (config.RequirePermission && !permission.UserHasPermission(killer.UserIDString, PermUse)) return;
GiveReward(killer, victim, info.isHeadshot);
}
private void GiveReward(BasePlayer killer, BasePlayer victim, bool headshot)
{
int amount = config.RewardAmount;
if (headshot) amount += config.HeadshotBonusAmount;
if (amount <= 0) return;
var item = ItemManager.CreateByName(config.RewardShortname, amount, 0);
if (item == null)
{
if (!warnedInvalidItem)
{
warnedInvalidItem = true;
PrintWarning($"Invalid reward shortname '{config.RewardShortname}', no rewards will be given until it is fixed.");
}
return;
}
killer.GiveItem(item);
if (config.MessageKiller)
{
string key = headshot ? "RewardHeadshot" : "Reward";
SendReply(killer, Msg(key, killer.UserIDString, amount, item.info.displayName.english, victim.displayName));
}
if (config.AnnounceToServer)
{
Server.Broadcast(Msg("Announce", null, victim.displayName, killer.displayName));
}
}
private string Msg(string key, string userId = null, params object[] args) =>
string.Format(lang.GetMessage(key, this, userId), args);
}
}Hooks used: OnEntityDeath
Config file
Created automatically on first load at oxide/config/KillRewards.json.
{
"Reward Shortname": "scrap",
"Reward Amount": 25,
"Headshot Bonus Amount": 25,
"Require Permission": false,
"Message Killer": true,
"Announce To Server": false
}Commands and permissions
No chat commands. The plugin works on its own.
| Permission | Grants |
|---|---|
killrewards.use | Grants the player kill rewards. When Require Permission is enabled, players without it get nothing. |
How the code works
Filtering who gets a reward
OnEntityDeathfires for every entity death, so the first job is narrowing it down. The plugin casts the victim toBasePlayerand rejects anything that is null, an NPC (IsNpc), or disconnected. It then readsinfo.InitiatorPlayer. If that is null (suicide, fall damage, decay, bleeding without a killer), nothing happens. It also stops when victim and killer are the same player, which covers suicide commands and self-damage.Blocking team kills
When both players have a non-zero
currentTeamand the values match, the kill is a team kill and is skipped. This simple check covers vanilla teams without needingRelationshipManager. If you run a separate clans plugin, that system is a different data store and is not consulted here; the FAQ covers how to extend the check if your server needs it.Paying the killer
GiveRewardbuilds the item withItemManager.CreateByName(config.RewardShortname, amount, 0). A null return means the shortname in the config is wrong, and the plugin warns once per kill rather than crashing.player.GiveItem(item)puts the item in the first free slot or drops it at the player's feet when the inventory is full, so the reward is never silently lost. The permission check againstkillrewards.useruns before this point.Headshot bonus
info.isHeadshotis read inOnEntityDeathand passed intoGiveReward, which addsconfig.HeadshotBonusAmounton top of the base amount. Set the bonus to 0 in the config to disable it without touching code. Since the two amounts are summed before the item is created, the killer receives a single stack instead of two separate item creations.Config and messages
PluginConfigholds the item shortname, base amount, headshot bonus, whether the permission is required, whether the killer gets a chat confirmation, and whether the kill is announced to the whole server.LoadConfigfalls back toLoadDefaultConfigwith a warning when the file cannot be parsed, then rewrites it. All player text is registered inLoadDefaultMessagesand fetched through theMsghelper.
Install it on your server
Save KillRewards.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/KillRewards.json(Carbon:carbon/configs/KillRewards.json). After editing, reload it:oxide.reload KillRewards c.reload KillRewards # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default killrewards.use c.grant group default killrewards.use # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
Distance-scaled reward
Pay more for long-range kills and less for point-blank ones.
Generate this versionDaily kill cap
Stop farming by limiting rewards per player per day.
Generate this versionKill streak bonuses
Give escalating rewards for consecutive kills without dying.
Generate this version
FAQ
Does this pay for killing NPCs, animals or other players on the same team?
No. NPC victims are rejected via IsNpc on the victim, suicides are rejected because InitiatorPlayer is the victim or null, and team kills are rejected when both players have the same non-zero currentTeam. Only real players killed by a different, connected player produce a reward.
How do I change the reward item or amount?
Edit oxide/config/KillRewards.json: set Reward Shortname to any valid item shortname such as scrap, economics-free alternatives like rifle.ak (minding stack sizes), then set Reward Amount and Headshot Bonus Amount. Reload the plugin with oxide.reload KillRewards to apply.
Why did my killer not receive scrap?
Check three things: Require Permission is false or the killer has killrewards.use, the shortname in the config exists (a typo logs a warning on kill), and the killer is not on the same team as the victim. If the inventory is full the item is dropped at their feet rather than lost.
How do I install it on Oxide or Carbon?
Drop the compiled KillRewards.cs into oxide/plugins, wait for the server to compile it, then edit oxide/config/KillRewards.json. Carbon loads the same file from its plugins folder. Grant the permission with oxide.grant user <steamid> killrewards.use if you enable the gate.
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 economy plugins
- BountiesA scrap bounty system where players fund a pool on a target and the killer collects it, using the OnEntityDeath hook plus a data file forโฆOnServerInitializedOnEntityDeath
- Chat shopA chat-driven shop where players spend scrap from their inventory to buy configured items.
- Daily rewardsAdds a /daily command that lets each player claim one reward from a configurable weighted list every 24 hours, with a streak bonus that scales whileโฆOnServerInitializedOnServerSave
Last updated Oct 26, 2026.
Customize kill rewards
Customize