How to make a Rust building grade limits plugin (Oxide)
Restricts building upgrades to a configurable maximum grade, such as stopping at stone or metal, with a permission that lifts the cap. It uses the OnStructureUpgrade hook to veto blocked upgrades and sends the player a clear chat message.
Servers that want to slow progression often want twig-only raiding or a metal ceiling without wiping HQM bases, but Rust gives every player the full grade list regardless of group or rank. There is no vanilla setting for a maximum building tier, so the only practical place to intervene is the moment a player runs an upgrade with the hammer. The OnStructureUpgrade hook fires before the upgrade is applied and returns an object, so returning a non-null value tells the game to abort the change.
This plugin stores a maximum grade per permission group in its config, checks the upgrading player's permissions, and compares the requested grade against the highest grade that player is allowed to reach. Blocked attempts are cancelled and answered with a chat message that names the limit. Players with the bypass permission, or admins if you enable that option, can build to top tier. The plugin also logs blocked attempts behind a config toggle so admins can see who is trying to go past the cap.
The plugin code
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Building Grade Limits", "RustPluginGenerator", "1.0.0")]
[Description("Blocks building upgrades above a configurable maximum grade, with per-grade permissions.")]
public class BuildingGradeLimits : RustPlugin
{
private const string PermUse = "buildinggradelimits.use";
private const string PermBypass = "buildinggradelimits.bypass";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Default Max Grade")]
public string DefaultMaxGrade { get; set; } = "Wood";
[JsonProperty("Ignore Admins")]
public bool IgnoreAdmins { get; set; } = false;
[JsonProperty("Announce Blocked Upgrades")]
public bool AnnounceBlocked { get; set; } = true;
[JsonProperty("Show Grade In Message")]
public bool ShowGradeInMessage { get; set; } = true;
[JsonProperty("Grade Permissions")]
public Dictionary<string, string> GradePermissions { get; set; } = new Dictionary<string, string>
{
["buildinggradelimits.wood"] = "Wood",
["buildinggradelimits.stone"] = "Stone",
["buildinggradelimits.metal"] = "Metal",
["buildinggradelimits.top"] = "TopTier"
};
}
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>
{
["Blocked"] = "You cannot upgrade past this grade on this server.",
["BlockedWithGrade"] = "You cannot upgrade to {0}. Your maximum building grade is {1}.",
["YourLimit"] = "Your maximum building grade is {0}.",
["NoLimit"] = "You have no building grade limit."
}, 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);
permission.RegisterPermission(PermBypass, this);
foreach (var perm in config.GradePermissions.Keys)
permission.RegisterPermission(perm, this);
}
private object OnStructureUpgrade(BuildingBlock buildingBlock, BasePlayer player, BuildingGrade.Enum type, ulong skin)
{
if (buildingBlock == null || player == null) return null;
if (config.IgnoreAdmins && player.IsAdmin) return null;
if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;
var max = GetMaxGrade(player.UserIDString);
if (type <= max) return null;
var msg = config.ShowGradeInMessage
? Msg("BlockedWithGrade", player.UserIDString, FormatGrade(type), FormatGrade(max))
: Msg("Blocked", player.UserIDString);
SendReply(player, msg);
if (config.AnnounceBlocked)
Puts($"{player.displayName} ({player.UserIDString}) blocked from upgrading to {type} (max {max})");
return false;
}
[ChatCommand("grade")]
private void CmdGrade(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse))
{
SendReply(player, "You do not have permission to use this command.");
return;
}
if (permission.UserHasPermission(player.UserIDString, PermBypass) || (config.IgnoreAdmins && player.IsAdmin))
{
SendReply(player, Msg("NoLimit", player.UserIDString));
return;
}
SendReply(player, Msg("YourLimit", player.UserIDString, FormatGrade(GetMaxGrade(player.UserIDString))));
}
private BuildingGrade.Enum GetMaxGrade(string userId)
{
var best = ParseGrade(config.DefaultMaxGrade, BuildingGrade.Enum.Wood);
foreach (var entry in config.GradePermissions)
{
if (!permission.UserHasPermission(userId, entry.Key)) continue;
var grade = ParseGrade(entry.Value, BuildingGrade.Enum.Twigs);
if (grade > best) best = grade;
}
return best;
}
private static BuildingGrade.Enum ParseGrade(string value, BuildingGrade.Enum fallback)
{
BuildingGrade.Enum grade;
if (!string.IsNullOrEmpty(value) && Enum.TryParse(value, true, out grade))
return grade;
return fallback;
}
private static string FormatGrade(BuildingGrade.Enum grade)
{
switch (grade)
{
case BuildingGrade.Enum.Twigs: return "twig";
case BuildingGrade.Enum.Wood: return "wood";
case BuildingGrade.Enum.Stone: return "stone";
case BuildingGrade.Enum.Metal: return "metal";
case BuildingGrade.Enum.TopTier: return "armored";
default: return grade.ToString();
}
}
}
}Hooks used: OnStructureUpgrade
Config file
Created automatically on first load at oxide/config/BuildingGradeLimits.json.
{
"Default Max Grade": "Wood",
"Ignore Admins": false,
"Announce Blocked Upgrades": true,
"Show Grade In Message": true,
"Grade Permissions": {
"buildinggradelimits.wood": "Wood",
"buildinggradelimits.stone": "Stone",
"buildinggradelimits.metal": "Metal",
"buildinggradelimits.top": "TopTier"
}
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/grade | Shows your current maximum allowed building grade and which permission grants it. | buildinggradelimits.use |
| Permission | Grants |
|---|---|
buildinggradelimits.use | Lets the player run /grade to check their current allowed maximum. |
buildinggradelimits.wood | Raises the player's maximum building grade to wood. |
buildinggradelimits.stone | Raises the player's maximum building grade to stone. |
buildinggradelimits.metal | Raises the player's maximum building grade to metal. |
buildinggradelimits.top | Raises the player's maximum building grade to armored (top tier). |
buildinggradelimits.bypass | Ignores all grade limits for this player. |
How the code works
Set the caps in the config
The nested
PluginConfigclass holdsDefault Max Gradeas a string (Twigs, Wood, Stone, Metal or TopTier) and aGrade Permissionsdictionary mapping each permission name to a maximum grade. The dictionary is ordered from the lowest tier up, so the code can take the highest tier a player holds.Ignore AdminsandAnnounce Blocked Upgradesare simple booleans, andShow Grade In Messagedecides whether the rejection names the limit.Hook into the upgrade
OnStructureUpgradereceives theBuildingBlock, the upgradingBasePlayer, the requestedBuildingGrade.Enumand the skin. It fires before the grade is applied. Returning anything other than null cancels the upgrade; returning null lets vanilla continue. The method first null-checks the building and player, then reads the highest allowed grade from the config for that player's user id.Resolve the player's limit
GetMaxGradecallspermission.UserHasPermissiononce per entry in the configured dictionary and keeps the highestBuildingGrade.Enumthat matches. If the player holds none of the listed permissions, the config'sDefault Max Gradestring is parsed withEnum.TryParseand used instead. The bypass permission short-circuits the whole check, and admins are skipped whenIgnore Adminsis enabled.Compare and block
Since
BuildingGrade.Enumis ordered (Twigs, Wood, Stone, Metal, TopTier), a plain numeric comparison decides whether the requestedtypeis above the maximum. When it is, the hook sends a formatted lang message to the player viaSendReply, optionally names the blocked grade, and returnsfalseso the building keeps its current grade and the player keeps the resources.Messages and the /grade command
All player text lives in
LoadDefaultMessagesunder keys likeBlocked,BlockedWithGrade,NoLimitandYourLimit. The helperMsgformats the selected string with the player's user id so per-player language from other plugins keeps working./gradeis registered with[ChatCommand("grade")]and gated by thebuildinggradelimits.usepermission; it reports the player's effective cap.Admin logging and defaults
When
Announce Blocked Upgradesis true the plugin callsPutswith the player's name and the blocked grade. That method is only reached from an occasional player action, not a per-tick path.LoadConfigandLoadDefaultConfigfollow the standard pattern so a missing or corrupt file falls back to the defaults and rewrites it. OnUnloadno timers or UI exist to clean up, so the method is empty aside from a comment.
Install it on your server
Save BuildingGradeLimits.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/BuildingGradeLimits.json(Carbon:carbon/configs/BuildingGradeLimits.json). After editing, reload it:oxide.reload BuildingGradeLimits c.reload BuildingGradeLimits # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default buildinggradelimits.use c.grant group default buildinggradelimits.use # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
Count-based cap
Limit how many armor blocks each player can own instead of capping the grade per player.
Generate this versionTeam-wide limits
Apply the highest limit held by any team member to the whole team.
Generate this versionGrade change cost
Charge scrap from the player's inventory before allowing an upgrade past the cap.
Generate this version
FAQ
Does blocking an upgrade destroy or refund the materials already spent?
No. OnStructureUpgrade runs before the upgrade is applied, so returning a non-null value stops the change entirely. Vanilla never deducts the upgrade cost, and the building keeps the grade and health it already had.
Can I allow one group to reach metal but keep everyone else at wood?
Yes. The Grade Permissions dictionary maps each permission to a maximum grade, and the plugin uses the highest one a player holds. Grant buildinggradelimits.metal to that group and leave everyone else on the default cap from Default Max Grade.
Why does the code compare grades with a number?
BuildingGrade.Enum is declared in ascending order: Twigs, Wood, Stone, Metal, TopTier. Casting to int gives a reliable ordering, so a single > check decides whether the requested grade is above the player's maximum without spelling out every pair.
How do I install and test it on Oxide or Carbon?
Drop BuildingGradeLimits.cs into oxide/plugins, let it compile, then edit oxide/config/BuildingGradeLimits.json and reload. Carbon loads the same file from its plugin folder. Use /grade with your account to confirm your cap, then try upgrading a wall to a higher tier.
Does this affect upgrades done by server tools or admins in vanish?
The hook fires the same way for any upgrade call that goes through the game's upgrade path, but your own tooling may apply grades through other methods. Set Ignore Admins to true if you want admins to bypass the cap while testing.
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 building grade limits
Customize