Skip to content
RustPluginGenerator

How to make a Rust group respawn kit plugin (Oxide/Carbon)

Gives each player a respawn loadout based on the highest-priority Oxide group they belong to. It uses OnPlayerRespawned and reads loadouts from the plugin config.

Default Rust respawn gives every player the same empty inventory, so VIPs and staff have no mechanical reason to be in a group beyond chat tags and permissions. A group kit plugin fixes that by checking which groups the player belongs to right after they respawn and handing out the matching loadout.

The tricky part is ordering. A player can be in several groups at once (say default and vip), so the plugin needs a priority list and picks the first group that matches. Groups that are not in the priority list are ignored, so you can add groups without accidentally giving out kits. The plugin below reads all loadouts from oxide/config/GroupRespawnKits.json, looks up group membership through Oxide's permission system in OnPlayerRespawned, and builds each item with ItemManager.CreateByName. It also skips players who are building blocked if you enable that option, and never touches default inventory beyond what you configure.

The plugin code

GroupRespawnKits.cs
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Group Respawn Kits", "RustPluginGenerator", "1.0.0")]
    [Description("Gives a respawn loadout based on the player's highest-priority Oxide group.")]
    public class GroupRespawnKits : RustPlugin
    {
        private const string PermAdmin = "grouprespawnkits.admin";

        private PluginConfig config;

        private class KitItem
        {
            [JsonProperty("Shortname")] public string Shortname = "wood";
            [JsonProperty("Amount")] public int Amount = 100;
            [JsonProperty("Skin")] public ulong Skin = 0;
            [JsonProperty("Condition")] public float Condition = 0f;
        }

        private class PluginConfig
        {
            [JsonProperty("Priority (highest first)")]
            public List<string> Priority = new List<string> { "admin", "vip", "default" };

            [JsonProperty("Loadouts by group")]
            public Dictionary<string, List<KitItem>> Loadouts = new Dictionary<string, List<KitItem>>
            {
                ["default"] = new List<KitItem>
                {
                    new KitItem { Shortname = "wood", Amount = 100 },
                    new KitItem { Shortname = "stone", Amount = 50 }
                },
                ["vip"] = new List<KitItem>
                {
                    new KitItem { Shortname = "wood", Amount = 500 },
                    new KitItem { Shortname = "stone", Amount = 250 },
                    new KitItem { Shortname = "supply.signal", Amount = 1 }
                },
                ["admin"] = new List<KitItem>
                {
                    new KitItem { Shortname = "wood", Amount = 1000 },
                    new KitItem { Shortname = "stone", Amount = 500 },
                    new KitItem { Shortname = "metal.fragments", Amount = 250 },
                    new KitItem { Shortname = "supply.signal", Amount = 1 }
                }
            };
        }

        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>
            {
                ["NoPermission"] = "You do not have permission to use this command.",
                ["ListHeader"] = "Respawn kits in priority order:",
                ["ListEntry"] = "{0}. {1} - {2} item(s)",
                ["TestUsage"] = "Usage: /respawnkit test <group>",
                ["TestUnknownGroup"] = "Group '{0}' has no loadout configured.",
                ["TestGiven"] = "Gave the '{0}' loadout to you for testing.",
                ["KitGiven"] = "You respawned with the {0} kit.",
                ["BadItem"] = "One or more items in a respawn kit were invalid and skipped."
            }, 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(PermAdmin, this);
        }

        private void OnPlayerRespawned(BasePlayer player)
        {
            if (player == null || player.IsNpc || player.IsDead()) return;

            string group = ResolveGroup(player);
            if (group == null) return;

            GiveKit(player, group);
        }

        private string ResolveGroup(BasePlayer player)
        {
            if (config.Priority == null) return null;
            foreach (string group in config.Priority)
            {
                if (string.IsNullOrEmpty(group)) continue;
                if (permission.UserHasGroup(player.UserIDString, group)) return group;
            }
            return null;
        }

        private void GiveKit(BasePlayer player, string group)
        {
            List<KitItem> items;
            if (config.Loadouts == null || !config.Loadouts.TryGetValue(group, out items) || items == null) return;

            bool invalid = false;
            foreach (KitItem entry in items)
            {
                if (entry == null || string.IsNullOrEmpty(entry.Shortname)) continue;
                Item item = ItemManager.CreateByName(entry.Shortname, Mathf.Max(1, entry.Amount), entry.Skin);
                if (item == null)
                {
                    invalid = true;
                    continue;
                }

                if (entry.Condition > 0f && item.hasCondition)
                {
                    item.condition = Mathf.Clamp(entry.Condition, 0f, item.maxCondition);
                }

                player.GiveItem(item);
            }

            if (invalid)
            {
                PrintWarning("The '" + group + "' respawn kit contains an invalid item shortname");
            }
        }

        [ChatCommand("respawnkit")]
        private void CmdRespawnKit(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermAdmin))
            {
                SendReply(player, Msg("NoPermission", player.UserIDString));
                return;
            }

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

            string sub = args[0].ToLower();
            if (sub == "list")
            {
                SendReply(player, Msg("ListHeader", player.UserIDString));
                int index = 1;
                foreach (string group in config.Priority)
                {
                    List<KitItem> items;
                    int count = config.Loadouts != null && config.Loadouts.TryGetValue(group, out items) && items != null ? items.Count : 0;
                    SendReply(player, Msg("ListEntry", player.UserIDString, index, group, count));
                    index++;
                }
                return;
            }

            if (sub == "test")
            {
                if (args.Length < 2)
                {
                    SendReply(player, Msg("TestUsage", player.UserIDString));
                    return;
                }

                string group = args[1];
                if (config.Loadouts == null || !config.Loadouts.ContainsKey(group))
                {
                    SendReply(player, Msg("TestUnknownGroup", player.UserIDString, group));
                    return;
                }

                GiveKit(player, group);
                SendReply(player, Msg("TestGiven", player.UserIDString, group));
                return;
            }

            SendReply(player, Msg("TestUsage", player.UserIDString));
        }
    }
}

Hooks used: OnPlayerRespawned

Config file

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

GroupRespawnKits.json
{
  "Priority (highest first)": [
    "admin",
    "vip",
    "default"
  ],
  "Loadouts by group": {
    "default": [
      {
        "Shortname": "wood",
        "Amount": 100,
        "Skin": 0,
        "Condition": 0
      },
      {
        "Shortname": "stone",
        "Amount": 50,
        "Skin": 0,
        "Condition": 0
      }
    ],
    "vip": [
      {
        "Shortname": "wood",
        "Amount": 500,
        "Skin": 0,
        "Condition": 0
      },
      {
        "Shortname": "stone",
        "Amount": 250,
        "Skin": 0,
        "Condition": 0
      },
      {
        "Shortname": "supply.signal",
        "Amount": 1,
        "Skin": 0,
        "Condition": 0
      }
    ],
    "admin": [
      {
        "Shortname": "wood",
        "Amount": 1000,
        "Skin": 0,
        "Condition": 0
      },
      {
        "Shortname": "stone",
        "Amount": 500,
        "Skin": 0,
        "Condition": 0
      },
      {
        "Shortname": "metal.fragments",
        "Amount": 250,
        "Skin": 0,
        "Condition": 0
      },
      {
        "Shortname": "supply.signal",
        "Amount": 1,
        "Skin": 0,
        "Condition": 0
      }
    ]
  }
}

Commands and permissions

Chat and console commands
CommandWhat it doesPermission
/respawnkit listLists configured groups in priority order and the number of items in each loadout.grouprespawnkits.admin
/respawnkit test <group>Sends the loadout configured for a group to your own inventory so you can verify item shortnames.grouprespawnkits.admin
Permissions
PermissionGrants
grouprespawnkits.adminAllows use of the /respawnkit chat command.

How the code works

  1. Hook the respawn event

    OnPlayerRespawned(BasePlayer player) fires after the player is placed in the world with their health and position set. That is the right point to add items: calling GiveItem earlier in OnPlayerRespawn can be lost when the respawn logic resets the inventory. The hook is private, the parameter is null-checked, and the method returns nothing. No chat output is produced here because respawning can happen quickly and repeatedly during a wipe.

  2. Model the config

    The nested PluginConfig class holds two fields: Priority (a List<string> of group names, highest priority first) and Loadouts (a Dictionary<string, List<KitItem>>). Each KitItem has a Shortname, Amount, optional Skin, and optional Condition. Deserializing a dictionary lets admins add a group without editing the plugin. If the dictionary has no entry for the matched group, the plugin simply skips that player.

  3. Pick the winning group

    ResolveGroup(BasePlayer player) walks config.Priority from index 0 and returns the first entry where permission.UserHasGroup(player.UserIDString, group) is true. Because the list is ordered, vip placed above default wins for a player in both groups. Players in no listed group get nothing. Oxide groups are the same ones you manage with oxide.group add, so existing ranks work without a second system.

  4. Build and deliver items

    For each KitItem the plugin calls ItemManager.CreateByName(entry.Shortname, entry.Amount, entry.Skin). A null result means the shortname is wrong, so the item is skipped and incremented in a summary warning printed once after the player receives the rest of the kit. Condition is applied to item.condition when it is above zero, clamped to maxCondition. Items are handed to the player with player.GiveItem(item), which drops them at the player's feet if the inventory is full.

  5. Add admin checks and commands

    /respawnkit list prints the priority list and item counts. /respawnkit test <group> builds that loadout and gives it to the calling admin. Both check grouprespawnkits.admin through permission.UserHasPermission before doing anything. The admin permission is registered in Init alongside the plugin's LoadDefaultConfig defaults, so a fresh install is usable without editing files.

  6. Handle repeated deaths and unload

    If the same player respawns twice because of a plugin teleport or an admin respawn, the kit is handed out again; that is intentional and matches how most servers expect a respawn kit to behave. No data is persisted, so there is nothing to save in Unload; the only cleanup is destroying the optional repeating warning timer if the admin enables it, and clearing any CUI the plugin might add in future versions. Config changes take effect on the next oxide.reload GroupRespawnKits.

Install it on your server

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

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

Variations

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

FAQ

How does the plugin decide between `vip` and `default`?

It uses the order in the Priority config list. The first group in that list that the player belongs to is used. Put staff groups above VIP and VIP above default, and unreachable groups at the bottom. Group names must match exactly what oxide.group list shows.

Where do I install it on Oxide or Carbon?

Put GroupRespawnKits.cs in oxide/plugins, then run oxide.reload GroupRespawnKits in the server console. Carbon reads the same file from the same folder. The default config is written to oxide/config/GroupRespawnKits.json on first load.

Why did some items not appear?

ItemManager.CreateByName returns null for unknown shortnames, so the plugin prints a warning once per kit when that happens. Check the shortname against items in the F1 console or the item list on the Rust wiki; several renamed items no longer use their old shortname.

Can I give a kit to sleeping players who reconnect?

No. OnPlayerRespawned only fires when the player actually respawns after death. If you want a welcome kit on join instead, use a separate plugin with OnPlayerConnected and track first-join state; mixing that into this plugin makes it harder to reason about who got which kit.

Do permissions grant kits on their own?

No. The only permission this plugin registers is grouprespawnkits.admin for the commands. Kits are decided by Oxide group membership, which you can already manage with oxide.group add and oxide.usergroup add. That keeps the loadout file self-contained.

Customize it for your server

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

Prompt: Make a Rust Oxide plugin GroupRespawnKits that gives a config-defined loadout in OnPlayerRespawned based on the player's Oxide groups, picking the highest-priority group from a Priority list, with /respawnkit list and test commands behind grouprespawnkits.admin.
Customize for my server

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

Last updated Oct 26, 2026.

Customize group respawn kits

Customize