How to make a Rust respawn loadout plugin (save belt and clothing, restore on respawn)
Lets players save their belt and clothing items with /loadout save, then automatically hands that gear back when they respawn. Uses the OnPlayerRespawned hook plus a per-player permission and cooldown.
Every wipe players rebuild the same kit: weapon, ammo, meds, armour. Forcing them to re-craft or run back to a stash after each death slows the server and pushes people to competitors. A saved loadout keeps the fun part of dying (losing your gear) while removing the chore of rebuilding it from scratch. This plugin stores only the shortnames and amounts of items in the belt and clothing slots, so conditions, skins and mods are not cloned. A configurable whitelist limits which shortnames can be saved, the save command has a cooldown, and players need the savedrespawnloadout.use permission to save or be restored. On OnPlayerRespawned the plugin checks the permission, looks up the saved entry and gives each item back with ItemManager.CreateByName, dropping anything that will not fit. Everything persists through the Oxide data file, so saved loadouts survive restarts and are wiped with the file.
The plugin code
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Saved Respawn Loadout", "RustPluginGenerator", "1.0.0")]
[Description("Saves a player's belt and clothing items and restores them on respawn")]
public class SavedRespawnLoadout : RustPlugin
{
private const string PermUse = "savedrespawnloadout.use";
private PluginConfig config;
private StoredData data;
private readonly Dictionary<ulong, DateTime> lastSave = new Dictionary<ulong, DateTime>();
private class PluginConfig
{
[JsonProperty("Whitelisted shortnames")]
public List<string> WhitelistedShortnames = new List<string>
{
"rifle.ak", "rifle.bolt", "smg.mp5", "pistol.semiauto",
"ammo.rifle", "ammo.pistol", "syringe.medical", "bandage",
"hoodie", "pants", "shoes.boots", "metal.facemask"
};
[JsonProperty("Save cooldown (seconds)")]
public double SaveCooldownSeconds = 60.0;
[JsonProperty("Remove loadout after respawn")]
public bool RemoveAfterRespawn = false;
}
private class LoadoutEntry
{
[JsonProperty("Shortname")]
public string Shortname;
[JsonProperty("Amount")]
public int Amount;
}
private class StoredData
{
[JsonProperty("Loadouts")]
public Dictionary<string, List<LoadoutEntry>> Loadouts = new Dictionary<string, List<LoadoutEntry>>();
}
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 loadouts.",
["Saved"] = "Saved {0} item(s) to your respawn loadout.",
["SaveEmpty"] = "Nothing in your belt or clothing could be saved.",
["Cleared"] = "Your saved respawn loadout was deleted.",
["Cooldown"] = "You can save again in {0} second(s).",
["Restored"] = "Your saved loadout was returned to you.",
["Usage"] = "Usage: /loadout save or /loadout clear"
}, 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);
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
}
private void Unload()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
private bool IsWhitelisted(string shortname)
{
return config.WhitelistedShortnames != null && config.WhitelistedShortnames.Contains(shortname);
}
private void AddFromContainer(ItemContainer container, List<LoadoutEntry> result)
{
if (container == null) return;
foreach (var item in container.itemList)
{
if (item?.info == null) continue;
if (!IsWhitelisted(item.info.shortname)) continue;
if (ItemManager.FindItemDefinition(item.info.shortname) == null) continue;
result.Add(new LoadoutEntry { Shortname = item.info.shortname, Amount = item.amount });
}
}
[ChatCommand("loadout")]
private void CmdLoadout(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;
}
switch (args[0].ToLower())
{
case "save":
SaveLoadout(player);
break;
case "clear":
ClearLoadout(player);
break;
default:
player.ChatMessage(Msg("Usage", player.UserIDString));
break;
}
}
private void SaveLoadout(BasePlayer player)
{
var now = DateTime.UtcNow;
DateTime previous;
if (lastSave.TryGetValue(player.userID, out previous))
{
var remaining = config.SaveCooldownSeconds - (now - previous).TotalSeconds;
if (remaining > 0)
{
player.ChatMessage(Msg("Cooldown", player.UserIDString, Math.Ceiling(remaining)));
return;
}
}
var entries = new List<LoadoutEntry>();
AddFromContainer(player.inventory?.containerBelt, entries);
AddFromContainer(player.inventory?.containerWear, entries);
if (entries.Count == 0)
{
player.ChatMessage(Msg("SaveEmpty", player.UserIDString));
return;
}
data.Loadouts[player.UserIDString] = entries;
lastSave[player.userID] = now;
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
player.ChatMessage(Msg("Saved", player.UserIDString, entries.Count));
}
private void ClearLoadout(BasePlayer player)
{
if (data != null && data.Loadouts.Remove(player.UserIDString))
{
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
player.ChatMessage(Msg("Cleared", player.UserIDString));
}
private void OnPlayerRespawned(BasePlayer player)
{
if (player == null || data == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse)) return;
List<LoadoutEntry> entries;
if (!data.Loadouts.TryGetValue(player.UserIDString, out entries) || entries == null || entries.Count == 0) return;
foreach (var entry in entries)
{
if (entry == null || string.IsNullOrEmpty(entry.Shortname)) continue;
var item = ItemManager.CreateByName(entry.Shortname, Mathf.Max(1, entry.Amount));
if (item == null) continue;
player.GiveItem(item);
}
player.ChatMessage(Msg("Restored", player.UserIDString));
if (config.RemoveAfterRespawn)
{
data.Loadouts.Remove(player.UserIDString);
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
}
}
}Hooks used: OnPlayerRespawned
Config file
Created automatically on first load at oxide/config/SavedRespawnLoadout.json.
{
"Whitelisted shortnames": [
"rifle.ak",
"rifle.bolt",
"smg.mp5",
"pistol.semiauto",
"ammo.rifle",
"ammo.pistol",
"syringe.medical",
"bandage",
"hoodie",
"pants",
"shoes.boots",
"metal.facemask"
],
"Save cooldown (seconds)": 60,
"Remove loadout after respawn": false,
"Shortname": null,
"Amount": 0,
"Loadouts": {}
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/loadout save | Saves the shortnames and amounts of your belt and clothing slots. Respects the whitelist and cooldown. | savedrespawnloadout.use |
/loadout clear | Deletes your saved loadout so you respawn with nothing restored. | savedrespawnloadout.use |
| Permission | Grants |
|---|---|
savedrespawnloadout.use | Allows saving a loadout with /loadout save and having it restored on respawn |
How the code works
Saving belt and clothing slots
AddFromContainerwalksplayer.inventory.containerBeltandplayer.inventory.containerWear, iteratingcontainer.itemList. For each entry it skips items whoseinfo.shortnameis not listed inWhitelisted shortnames, and skips shortnames thatItemManager.FindItemDefinitioncannot resolve. Surviving items are copied into aLoadoutEntryholding onlyShortnameandAmount, so skins, conditions and attachments are never stored.Restoring on respawn
OnPlayerRespawned(BasePlayer player)fires after the player is alive again. The plugin confirmspermission.UserHasPermissionagainstPermUse, then looks up theUserIDStringkey indata.Loadouts. For each stored entry it callsItemManager.CreateByName(entry.Shortname, Mathf.Max(1, entry.Amount)); null results are skipped.player.GiveItem(item)places the item in the inventory or drops it at the player's feet when slots are full.Whitelist and cooldown in config
PluginConfigexposesWhitelisted shortnames,Save cooldown (seconds)andRemove loadout after respawn.SaveLoadoutcomparesDateTime.UtcNowagainst the timestamp kept in thelastSavedictionary, and when the remaining time is positive it replies with theCooldownmessage. No timers are involved, so nothing needs tearing down inUnload.Permission and command handling
InitregistersPermUse(savedrespawnloadout.use) withpermission.RegisterPermission. The single[ChatCommand("loadout")]method checks the permission first, then switches onargs[0]to reachSaveLoadoutorClearLoadout. Every reply goes throughMsg, which pulls text fromLoadDefaultMessages, so no chat string is hard-coded.Persistence through the data file
StoredDataholds aLoadoutsdictionary keyed byUserIDStringwithLoadoutEntrylists as values.Initreads it withInterface.Oxide.DataFileSystem.ReadObject<StoredData>(Name), falling back to a new instance when the file is missing. Saving and clearing both callWriteObjectimmediately, andUnloadwrites once more so the state survives a restart.No duplication across respawns
/loadout clearremoves the player's key fromLoadoutsand writes the file. WhenRemove loadout after respawnis true,OnPlayerRespawneddeletes the entry right after restoring it, so a player receives the gear exactly once. This makes the feature safe for one-life events and stops loadouts from stacking up over repeated deaths.
Install it on your server
Save SavedRespawnLoadout.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/SavedRespawnLoadout.json(Carbon:carbon/configs/SavedRespawnLoadout.json). After editing, reload it:oxide.reload SavedRespawnLoadout c.reload SavedRespawnLoadout # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default savedrespawnloadout.use c.grant group default savedrespawnloadout.use # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
Restore only for specific teams
Limit restoration to players on a team so solo players cannot use the feature.
Generate this versionCharge a scrap cost per respawn
Require scrap in the inventory before the saved loadout is handed back.
Generate this versionSave condition and skin
Restore the exact weapon condition and skin rather than a fresh item.
Generate this version
FAQ
Are weapon attachments and skins saved?
No. Only the shortname and amount of each item in the belt and clothing slots is stored. ItemManager.CreateByName gives a fresh item with default skin and condition, which keeps the feature simple and avoids copying a broken modded weapon back into play.
How do I restrict which items can be saved?
Edit Whitelisted shortnames in oxide/config/SavedRespawnLoadout.json. Only shortnames in that list are recorded by /loadout save, and the same list is checked when creating items on respawn. Shortnames that do not resolve through ItemManager.FindItemDefinition are ignored.
Why did my loadout not come back after dying?
Check that you have saved with /loadout save, that you still hold savedrespawnloadout.use, and that your belt and clothing slots were not full when you respawned. Anything that will not fit is dropped at your position by GiveItem.
Does this work on Carbon?
Yes. Carbon loads the same Oxide plugin file. Drop SavedRespawnLoadout.cs into oxide/plugins, run oxide.reload SavedRespawnLoadout, and the config and data files are created on first load. No separate Carbon build or patcher is needed.
Where is the loadout data stored?
In oxide/data/SavedRespawnLoadout.json, written with Interface.Oxide.DataFileSystem. Deleting that file wipes every saved loadout. The file is saved on plugin unload, so give the server a moment before shutting down hard.
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 saved respawn loadout
Customize