Skip to content
RustPluginGenerator

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

StarterKit.cs
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.

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

Chat and console commands
CommandWhat it doesPermission
/starterkit give <name or steamid>Applies the starter kit to the named online player, replacing their inventory.starterkit.use
/starterkit selfApplies the starter kit to yourself so you can check the configured items.starterkit.use
Permissions
PermissionGrants
starterkit.useGrants access to the /starterkit chat command (give and self).

How the code works

  1. 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 call player.inventory.Strip() to remove every default item before building the kit.

  2. Build items from config

    Each entry in Items holds a shortname, an amount, an optional skin, an optional condition, and a Container name. The plugin reads the shortname with ItemManager.CreateByName(shortname, amount, skin), which returns null for unknown shortnames; those entries are skipped and reported at startup. If Condition is set and the item has a maxCondition, the plugin assigns it and calls MarkDirty() so the durability UI is correct.

  3. Route items to the right container

    Container accepts wear, belt, or main. Clothing shortnames are known to the game as wearables, so wear entries go through inventory.containerWear. Tools usually go to belt for quick access. Everything else lands in inventory.containerMain. If MoveToContainer fails because the container is full, the item is dropped at the player's feet with item.Drop(player.transform.position, Vector3.zero) instead of being lost.

  4. Toggle the behavior per player

    GiveOnRespawn in 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, and Strip() guarantees no leftover items from the previous life carry over.

  5. Manual commands

    Two chat subcommands use [ChatCommand("starterkit")]: self applies the kit to the caller, and give <name or id> looks the target up with BasePlayer.FindAwakeOrSleeping, checks permission.UserHasPermission(target.UserIDString, PermUse) when RequirePermission is enabled, and applies the kit. Both responses go through lang keys, so the wording stays in the language files.

  6. Config defaults and reset

    LoadDefaultConfig fills a PluginConfig with a sensible default kit. LoadConfig reads the file with Config.ReadObject<PluginConfig>(), falls back to defaults on parse failure, and calls SaveConfig so admins see the full schema. /starterkit has no subcommand for config reload; use oxide.reload StarterKit after editing the file. Unload has nothing to clean up because no timers or UI are created.

Install it on your server

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

    Give it to every player, or swap default for a group such as vip: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 version
  • Kit after wipe

    Only hand out the kit during the first N hours after a server wipe.

    Generate this version
  • Random 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.

Prompt: Create an Oxide plugin that replaces default respawn items with a configurable starter kit, giving clothing to the wear container, tools to the belt, and everything else to the main inventory, with a /starterkit give command.
Customize for my server

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

Last updated Oct 26, 2026.

Customize starter kit on spawn

Customize