Skip to content
RustPluginGenerator

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

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

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

Chat and console commands
CommandWhat it doesPermission
/gradeShows your current maximum allowed building grade and which permission grants it.buildinggradelimits.use
Permissions
PermissionGrants
buildinggradelimits.useLets the player run /grade to check their current allowed maximum.
buildinggradelimits.woodRaises the player's maximum building grade to wood.
buildinggradelimits.stoneRaises the player's maximum building grade to stone.
buildinggradelimits.metalRaises the player's maximum building grade to metal.
buildinggradelimits.topRaises the player's maximum building grade to armored (top tier).
buildinggradelimits.bypassIgnores all grade limits for this player.

How the code works

  1. Set the caps in the config

    The nested PluginConfig class holds Default Max Grade as a string (Twigs, Wood, Stone, Metal or TopTier) and a Grade Permissions dictionary 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 Admins and Announce Blocked Upgrades are simple booleans, and Show Grade In Message decides whether the rejection names the limit.

  2. Hook into the upgrade

    OnStructureUpgrade receives the BuildingBlock, the upgrading BasePlayer, the requested BuildingGrade.Enum and 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.

  3. Resolve the player's limit

    GetMaxGrade calls permission.UserHasPermission once per entry in the configured dictionary and keeps the highest BuildingGrade.Enum that matches. If the player holds none of the listed permissions, the config's Default Max Grade string is parsed with Enum.TryParse and used instead. The bypass permission short-circuits the whole check, and admins are skipped when Ignore Admins is enabled.

  4. Compare and block

    Since BuildingGrade.Enum is ordered (Twigs, Wood, Stone, Metal, TopTier), a plain numeric comparison decides whether the requested type is above the maximum. When it is, the hook sends a formatted lang message to the player via SendReply, optionally names the blocked grade, and returns false so the building keeps its current grade and the player keeps the resources.

  5. Messages and the /grade command

    All player text lives in LoadDefaultMessages under keys like Blocked, BlockedWithGrade, NoLimit and YourLimit. The helper Msg formats the selected string with the player's user id so per-player language from other plugins keeps working. /grade is registered with [ChatCommand("grade")] and gated by the buildinggradelimits.use permission; it reports the player's effective cap.

  6. Admin logging and defaults

    When Announce Blocked Upgrades is true the plugin calls Puts with the player's name and the blocked grade. That method is only reached from an occasional player action, not a per-tick path. LoadConfig and LoadDefaultConfig follow the standard pattern so a missing or corrupt file falls back to the defaults and rewrites it. On Unload no timers or UI exist to clean up, so the method is empty aside from a comment.

Install it on your server

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

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

    Apply the highest limit held by any team member to the whole team.

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

Prompt: Write an Oxide plugin that blocks building upgrades above a configured maximum grade, with per-grade permissions buildinggradelimits.wood through top, a bypass permission, an admin toggle, and a /grade check 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 building grade limits

Customize