How to make a Rust starter kit plugin (on respawn)
Gives every respawning player a configurable starter kit instead of the default rock and torch. Uses OnPlayerRespawned to strip the default inventory, place clothing in the wear container, tools in the belt, and items in the main inventory.
Rust hands every player who respawns a rock and a torch, and nothing else. If you want new spawns to start with clothes, a tool, and a bit of food, you have to hand out those items yourself at the moment of respawn. The hook OnPlayerRespawned fires after the player is placed in the world and the default items exist, so you can remove the defaults and give your own kit without fighting the game.
This plugin strips the player's inventory on respawn, then builds each configured item with ItemManager.CreateByName, routes clothing to the wear container and everything else to the belt or main inventory, and moves anything that does not fit to the ground. Everything lives in one config file, so you can change the kit without recompiling. A chat command re-applies the kit to yourself, and one hands the kit to a named player, both permission-gated.
The plugin code
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Starter Kit", "RustPluginGenerator", "1.0.0")]
[Description("Gives a configurable starter kit to every respawning player instead of the default rock and torch.")]
public class StarterKit : RustPlugin
{
private const string PermUse = "starterkit.use";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Give kit automatically on respawn")]
public bool GiveOnRespawn { get; set; } = true;
[JsonProperty("Require permission for automatic kit")]
public bool RequirePermission { get; set; } = false;
[JsonProperty("Drop overflow items on ground")]
public bool DropOverflow { get; set; } = true;
[JsonProperty("Starter items")]
public List<KitItem> Items { get; set; } = new List<KitItem>
{
new KitItem { Shortname = "hoodie", Amount = 1, Container = "wear" },
new KitItem { Shortname = "pants", Amount = 1, Container = "wear" },
new KitItem { Shortname = "shoes.boots", Amount = 1, Container = "wear" },
new KitItem { Shortname = "rock", Amount = 1, Container = "belt" },
new KitItem { Shortname = "torch", Amount = 1, Container = "belt" },
new KitItem { Shortname = "bandage", Amount = 3, Container = "main" },
new KitItem { Shortname = "apple", Amount = 3, Container = "main" }
};
}
private class KitItem
{
[JsonProperty("Item shortname")]
public string Shortname { get; set; }
[JsonProperty("Amount")]
public int Amount { get; set; } = 1;
[JsonProperty("Skin ID")]
public ulong Skin { get; set; } = 0;
[JsonProperty("Condition (0 = default)")]
public float Condition { get; set; } = 0f;
[JsonProperty("Container (wear, belt, main)")]
public string Container { get; set; } = "main";
}
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>
{
["KitGiven"] = "Your starter kit has been given.",
["KitGivenTarget"] = "You gave {0} the starter kit.",
["PlayerNotFound"] = "No matching player found.",
["NoPermission"] = "You don't have permission to use this command.",
["Usage"] = "Usage: /starterkit give <name or steamid> | /starterkit self",
["TargetNoPermission"] = "{0} does not have the starterkit.use permission."
}, 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);
foreach (var entry in config.Items)
{
if (entry == null || string.IsNullOrEmpty(entry.Shortname)) continue;
if (ItemManager.FindItemDefinition(entry.Shortname) == null)
PrintWarning($"Unknown item shortname '{entry.Shortname}' in config, skipping");
}
}
private void OnPlayerRespawned(BasePlayer player)
{
if (player == null || !config.GiveOnRespawn) return;
if (player.IsNpc || player.IsDead()) return;
if (config.RequirePermission && !permission.UserHasPermission(player.UserIDString, PermUse)) return;
GiveKit(player);
}
[ChatCommand("starterkit")]
private void CmdStarterKit(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse))
{
player.ChatMessage(Msg("NoPermission", player.UserIDString));
return;
}
if (args == null || args.Length == 0)
{
player.ChatMessage(Msg("Usage", player.UserIDString));
return;
}
var sub = args[0].ToLowerInvariant();
if (sub == "self")
{
GiveKit(player);
player.ChatMessage(Msg("KitGiven", player.UserIDString));
return;
}
if (sub == "give" && args.Length >= 2)
{
var target = BasePlayer.FindAwakeOrSleeping(args[1]);
if (target == null)
{
player.ChatMessage(Msg("PlayerNotFound", player.UserIDString));
return;
}
if (config.RequirePermission && !permission.UserHasPermission(target.UserIDString, PermUse))
{
player.ChatMessage(Msg("TargetNoPermission", player.UserIDString, target.displayName));
return;
}
GiveKit(target);
player.ChatMessage(Msg("KitGivenTarget", player.UserIDString, target.displayName));
return;
}
player.ChatMessage(Msg("Usage", player.UserIDString));
}
private void GiveKit(BasePlayer player)
{
if (player?.inventory == null) return;
player.inventory.Strip();
foreach (var entry in config.Items)
{
if (entry == null || string.IsNullOrEmpty(entry.Shortname)) continue;
var item = ItemManager.CreateByName(entry.Shortname, Mathf.Max(1, entry.Amount), entry.Skin);
if (item == null) continue;
if (entry.Condition > 0f && item.maxCondition > 0f)
{
item.condition = Mathf.Min(entry.Condition, item.maxCondition);
}
item.MarkDirty();
var container = ResolveContainer(player, entry.Container);
if (container == null || item.MoveToContainer(container))
{
if (config.DropOverflow) item.Drop(player.transform.position + Vector3.up, Vector3.zero);
else item.Remove();
}
}
player.SendNetworkUpdate();
}
private ItemContainer ResolveContainer(BasePlayer player, string name)
{
var inv = player.inventory;
if (inv == null) return null;
switch ((name ?? "main").ToLowerInvariant())
{
case "wear": return inv.containerWear;
case "belt": return inv.containerBelt;
default: return inv.containerMain;
}
}
}
}Hooks used: OnPlayerRespawned
Config file
Created automatically on first load at oxide/config/StarterKit.json.
{
"Give kit automatically on respawn": true,
"Require permission for automatic kit": false,
"Drop overflow items on ground": true,
"Starter items": [
{
"Item shortname": "hoodie",
"Amount": 1,
"Skin ID": 0,
"Condition (0 = default)": 0,
"Container (wear, belt, main)": "wear"
},
{
"Item shortname": "pants",
"Amount": 1,
"Skin ID": 0,
"Condition (0 = default)": 0,
"Container (wear, belt, main)": "wear"
},
{
"Item shortname": "shoes.boots",
"Amount": 1,
"Skin ID": 0,
"Condition (0 = default)": 0,
"Container (wear, belt, main)": "wear"
},
{
"Item shortname": "rock",
"Amount": 1,
"Skin ID": 0,
"Condition (0 = default)": 0,
"Container (wear, belt, main)": "belt"
},
{
"Item shortname": "torch",
"Amount": 1,
"Skin ID": 0,
"Condition (0 = default)": 0,
"Container (wear, belt, main)": "belt"
},
{
"Item shortname": "bandage",
"Amount": 3,
"Skin ID": 0,
"Condition (0 = default)": 0,
"Container (wear, belt, main)": "main"
},
{
"Item shortname": "apple",
"Amount": 3,
"Skin ID": 0,
"Condition (0 = default)": 0,
"Container (wear, belt, main)": "main"
}
]
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/starterkit give <name or steamid> | Applies the starter kit to the named online player, replacing their inventory. | starterkit.use |
/starterkit self | Applies the starter kit to yourself so you can check the configured items. | starterkit.use |
| Permission | Grants |
|---|---|
starterkit.use | Grants access to the /starterkit chat command (give and self). |
How the code works
Detect the respawn
The plugin implements
private void OnPlayerRespawned(BasePlayer player)and does its work there. The hook fires after the player is back in the world, so the default rock and torch already exist. The first thing the handler does is null-check the player, skip NPCs and dead or sleeping players, then callplayer.inventory.Strip()to remove every default item before building the kit.Build items from config
Each entry in
Itemsholds a shortname, an amount, an optional skin, an optional condition, and aContainername. The plugin reads the shortname withItemManager.CreateByName(shortname, amount, skin), which returnsnullfor unknown shortnames; those entries are skipped and reported at startup. IfConditionis set and the item has amaxCondition, the plugin assigns it and callsMarkDirty()so the durability UI is correct.Route items to the right container
Containeracceptswear,belt, ormain. Clothing shortnames are known to the game as wearables, sowearentries go throughinventory.containerWear. Tools usually go tobeltfor quick access. Everything else lands ininventory.containerMain. IfMoveToContainerfails because the container is full, the item is dropped at the player's feet withitem.Drop(player.transform.position, Vector3.zero)instead of being lost.Toggle the behavior per player
GiveOnRespawnin the config switches the automatic handout on or off. When it is true, the hook path is the only requirement. Because the hook fires on every respawn, the inventory is reset every time the player respawns, including after a death — the item list is the source of truth, andStrip()guarantees no leftover items from the previous life carry over.Manual commands
Two chat subcommands use
[ChatCommand("starterkit")]:selfapplies the kit to the caller, andgive <name or id>looks the target up withBasePlayer.FindAwakeOrSleeping, checkspermission.UserHasPermission(target.UserIDString, PermUse)whenRequirePermissionis enabled, and applies the kit. Both responses go throughlangkeys, so the wording stays in the language files.Config defaults and reset
LoadDefaultConfigfills aPluginConfigwith a sensible default kit.LoadConfigreads the file withConfig.ReadObject<PluginConfig>(), falls back to defaults on parse failure, and callsSaveConfigso admins see the full schema./starterkithas no subcommand for config reload; useoxide.reload StarterKitafter editing the file.Unloadhas nothing to clean up because no timers or UI are created.
Install it on your server
Save StarterKit.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/StarterKit.json(Carbon:carbon/configs/StarterKit.json). After editing, reload it:oxide.reload StarterKit c.reload StarterKit # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default starterkit.use c.grant group default starterkit.use # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
First spawn only
Give the kit once per player, not on every respawn, using a data file of playing IDs.
Generate this versionKit after wipe
Only hand out the kit during the first N hours after a server wipe.
Generate this versionRandom starter kit
Pick one of several configured kits at random on each respawn.
Generate this version
FAQ
Does the kit replace the rock and torch, or stack on top?
It replaces them. The plugin calls player.inventory.Strip() before building the configured items, so the default rock and torch are removed. If you want to keep them, add rock and torch shortnames to the Items array in the config.
Why does my helmet end up in the main inventory?
The Container field controls where an item goes. Wearables must use wear so they land in inventory.containerWear. Unknown container names fall back to main, which is why a misplaced helmet shows up in the backpack slot list.
How do I install this on Oxide or Carbon?
Drop StarterKit.cs into oxide/plugins/, let the server compile it, then edit oxide/config/StarterKit.json and reload with oxide.reload StarterKit. Carbon loads the same file from the same folder.
Can I give the kit to a player who is offline?
No. The give subcommand uses BasePlayer.FindAwakeOrSleeping, which only resolves players the server has loaded. For full offline delivery you would need to queue the kit and apply it on the next OnPlayerRespawned or OnPlayerConnected for that ID.
Does the kit apply after a player is revived or respawned in a bag?
Yes. OnPlayerRespawned fires for bag, bed, and beach respawns alike, because the handler runs after the player entity is placed in the world.
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 kits plugins
- Give kit commandThis plugin adds an admin /givekit command plus a matching console command that hands a config-defined kit to any online or sleeping player,…OnServerInitialized
- Group respawn kitsGives each player a respawn loadout based on the highest-priority Oxide group they belong to.OnPlayerRespawned
- Kits with cooldownsAdds permission-aware kits with per-kit cooldowns and per-wipe use limits.OnServerInitializedOnNewSave
Last updated Oct 26, 2026.
Customize starter kit on spawn
Customize