How to make a Rust tool cupboard auth limit plugin (with VIP tiers)
Caps the number of authorized players on each tool cupboard and blocks extra authorizations with a chat message, using OnCupboardAuthorize. Players with a VIP permission get a higher cap.
By default a tool cupboard (BuildingPrivlidge) accepts an unlimited number of authorized players, which on busy servers turns one base into a shared clan storage and makes it hard to tell who actually holds build rights. This plugin puts a hard cap on the authorized list per cupboard and rejects any authorization attempt past that cap.
The cap comes from config: a base limit for everyone and a separate limit for players holding a VIP permission. The check happens in OnCupboardAuthorize, which Oxide fires right before a player is added to a cupboard's authorization list. Returning false blocks the action, so the plugin returns false with a message when the cupboard is already full. The same hook is also written for VehiclePrivilege so vehicle locks have their own limit from a separate config key. The plugin only counts existing entries and lets Rust handle the list itself, so authorizing, clearing and deauthorizing through the normal UI keep working.
The plugin code
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Cupboard Auth Limit", "RustPluginGenerator", "1.0.0")]
[Description("Caps the number of players authorized on tool cupboards and vehicle privileges, with a VIP tier.")]
public class CupboardAuthLimit : RustPlugin
{
private const string PermVip = "cupboardauthlimit.vip";
private const string PermUse = "cupboardauthlimit.use";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Base auth limit")]
public int BaseLimit { get; set; } = 4;
[JsonProperty("VIP auth limit")]
public int VipLimit { get; set; } = 8;
[JsonProperty("Vehicle privilege limit")]
public int VehicleLimit { get; set; } = 2;
[JsonProperty("Bypass for admins")]
public bool AdminBypass { get; set; } = true;
}
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>
{
["LimitReached"] = "This tool cupboard is full ({0} authorized players maximum).",
["VehicleLimitReached"] = "This vehicle lock is full ({0} authorized players maximum).",
["Status"] = "Base limit: {0} | VIP limit: {1} | Your limit: {2} | Admin bypass: {3}",
["NoPermission"] = "You do not have permission to use this command.",
["Usage"] = "Usage: /cbauthlimit status"
}, this);
}
private void Init()
{
permission.RegisterPermission(PermVip, this);
permission.RegisterPermission(PermUse, this);
}
private string Msg(string key, string userId = null, params object[] args)
=> string.Format(lang.GetMessage(key, this, userId), args);
private int GetLimit(BasePlayer player)
{
if (permission.UserHasPermission(player.UserIDString, PermVip))
return config.VipLimit;
return config.BaseLimit;
}
private int CountAuth(BuildingPrivlidge priv, BasePlayer player)
{
int count = 0;
foreach (var entry in priv.authorizedPlayers)
{
if (player != null && entry == player.userID.Get())
continue; // re-authorizing an existing member is fine
count++;
}
return count;
}
private int CountAuth(VehiclePrivilege priv, BasePlayer player)
{
int count = 0;
foreach (var entry in priv.authorizedPlayers)
{
if (player != null && entry == player.userID.Get())
continue;
count++;
}
return count;
}
private object OnCupboardAuthorize(BuildingPrivlidge buildingPrivlidge, BasePlayer player)
{
if (buildingPrivlidge == null || player == null || player.IsNpc) return null;
if (config.AdminBypass && player.IsAdmin) return null;
int limit = GetLimit(player);
if (limit <= 0) return null;
int current = CountAuth(buildingPrivlidge, player);
if (current >= limit)
{
player.ChatMessage(Msg("LimitReached", player.UserIDString, limit));
return false;
}
return null;
}
private object OnCupboardAuthorize(VehiclePrivilege vehiclePrivilege, BasePlayer player)
{
if (vehiclePrivilege == null || player == null || player.IsNpc) return null;
if (config.AdminBypass && player.IsAdmin) return null;
if (config.VehicleLimit <= 0) return null;
int current = CountAuth(vehiclePrivilege, player);
if (current >= config.VehicleLimit)
{
player.ChatMessage(Msg("VehicleLimitReached", player.UserIDString, config.VehicleLimit));
return false;
}
return null;
}
[ChatCommand("cbauthlimit")]
private void CmdAuthLimit(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 || args[0].ToLowerInvariant() != "status")
{
player.ChatMessage(Msg("Usage", player.UserIDString));
return;
}
player.ChatMessage(Msg("Status", player.UserIDString,
config.BaseLimit, config.VipLimit, GetLimit(player), config.AdminBypass));
}
private void Unload()
{
config = null;
}
}
}Hooks used: OnCupboardAuthorize
Config file
Created automatically on first load at oxide/config/CupboardAuthLimit.json.
{
"Base auth limit": 4,
"VIP auth limit": 8,
"Vehicle privilege limit": 2,
"Bypass for admins": true
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/cbauthlimit status | Shows the base limit, the VIP limit, the caller's effective limit and whether admin bypass is on. | cupboardauthlimit.use |
| Permission | Grants |
|---|---|
cupboardauthlimit.vip | Raises the authorization cap on tool cupboards to the VIP limit. |
cupboardauthlimit.use | Allows the /cbauthlimit status chat command. |
How the code works
Registering permissions and loading the config
LoadDefaultConfigcreates aPluginConfigwith the [JsonProperty] keysBase auth limit,VIP auth limit,Vehicle privilege limitandBypass for admins.LoadConfigreads the file, falls back to defaults and warns if deserialization returns null, then writes the file back.Initregisterscupboardauthlimit.vipandcupboardauthlimit.useso VIP checks and the status command work as soon as the plugin loads.Counting current authorizations
CountAuthwalksauthorizedPlayerson the cupboard and counts each entry, skipping the entry that matches the player being authorized (entry == player.userID.Get()). That skip means re-authorizing someone who is already on the list never gets blocked. The same method is written forBuildingPrivlidgeandVehiclePrivilege. If your Rust build exposes the list differently, the compiler will point at the field name.Blocking extra authorizations in OnCupboardAuthorize
The hook receives the
BuildingPrivlidgeand theBasePlayer. It null-checks both, ignores NPCs and returns null for admins whenBypass for adminsis true.GetLimitreturns the VIP value whenpermission.UserHasPermissionmatchescupboardauthlimit.vip, otherwise the base value. A configured limit of 0 or less means unlimited. When the count is at or above the limit, the player gets theLimitReachedmessage and the hook returnsfalse, which cancels the addition; otherwise it returnsnulland Rust proceeds normally.Handling vehicle privileges with the same hook
OnCupboardAuthorize(VehiclePrivilege vehiclePrivilege, BasePlayer player)fires for the vehicle lock entity, which keeps its ownauthorizedPlayerslist. This overload uses theVehicle privilege limitconfig key instead of the base/VIP values, so vehicle locks get one flat cap. Setting that key to 0 or less disables the vehicle check entirely. The same admin bypass and NPC checks apply, and the blocked player sees theVehicleLimitReachedmessage.Sending feedback through lang
LoadDefaultMessagesregistersLimitReachedandVehicleLimitReached, each with{0}for the limit value, plusStatus,NoPermissionandUsage. TheMsghelper formatslang.GetMessage(key, this, userId), so the text can be edited inoxide/lang/en/CupboardAuthLimit.jsonwithout touching code. Telling the player the exact cap avoids support tickets about why authorization did nothing.The /cbauthlimit status command
[ChatCommand("cbauthlimit")]handles thestatussubcommand and checkscupboardauthlimit.usefirst. It replies with the base limit, VIP limit, the caller's own effective limit fromGetLimitand the current admin bypass setting, so staff can confirm a VIP group is wired to the permission. Anything else prints the usage line. The plugin uses no timers or data files, soUnloadonly clears the config reference.
Install it on your server
Save CupboardAuthLimit.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/CupboardAuthLimit.json(Carbon:carbon/configs/CupboardAuthLimit.json). After editing, reload it:oxide.reload CupboardAuthLimit c.reload CupboardAuthLimit # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default cupboardauthlimit.vip c.grant group default cupboardauthlimit.vip # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
Team-sized limits with auto tiers
Instead of two fixed limits, set the cap per player tier using the server's permission groups.
Generate this versionWarn before the cupboard is full
Tell the owner when the cupboard approaches the cap.
Generate this versionReset the list on wipe
Clear all authorizations on new save for a fresh wipe start.
Generate this version
FAQ
Does returning false in OnCupboardAuthorize actually stop the player from being added?
Yes. OnCupboardAuthorize is a hook that can cancel the action; returning false prevents the player from being authorized, and returning null lets Rust proceed. Pair the cancel with the LimitReached chat message so the player knows why nothing happened.
Will the limit apply to players already authorized before the plugin was installed?
Existing entries are not removed. The count includes everyone currently on the list, so a cupboard that already has more members than the limit simply blocks any new authorization until enough people are deauthorized. Clear cupboards manually before the first wipe if you want a clean start.
How do I give a group the higher limit?
Grant cupboardauthlimit.vip to the group with oxide.grant group vip cupboardauthlimit.vip. GetLimit checks permission.UserHasPermission(player.UserIDString, ...), which resolves group permissions, so team members in that group get the VIP auth limit value automatically.
Does this work on Carbon?
Yes. Carbon loads Oxide plugins, so drop CupboardAuthLimit.cs into the plugins folder and both hooks dispatch normally. Only the standard Oxide API is used: permission, lang, Config and the OnCupboardAuthorize hooks.
What happens to vehicle locks?
A second OnCupboardAuthorize overload handles VehiclePrivilege with the Vehicle privilege limit key. Set that key to 0 to leave vehicle locks unrestricted. This keeps modular car locks from becoming a loophole around the cupboard cap, while the base and VIP limits still apply to tool cupboards only.
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 building plugins
- Auto closing doorsCloses doors a configurable number of seconds after a player opens them, with a per-player /autodoor toggle and a personal delay clamped to serverโฆOnDoorOpened
- Auto code lockAutomatically attaches a code lock with the player's saved personal code to every door or box they place, consuming a code lock from their inventory.OnServerInitializedOnEntityBuilt
- Auto upgrade on buildLets each player pick a building grade with /up 1-4, then upgrades every building block they place to that grade using resources taken from theirโฆOnServerInitializedOnEntityBuilt
Last updated Oct 26, 2026.
Customize tool cupboard auth limit
Customize