How to make a Rust auto upgrade on build plugin (grade per player)
Lets 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 inventory. It uses OnEntityBuilt and the BuildingBlock grade system.
Players who always build in stone or metal still have to place twigs first, then walk around with a hammer upgrading. That is slow, and on wipe day it is the main source of tedious clicking. This plugin gives every player a personal preferred grade. When a building block is placed, OnEntityBuilt fires, the plugin looks up that player's saved grade and checks the required resources against their main inventory. If they have enough, the same resources the hammer would consume are taken and the block is upgraded immediately. If they do not, the block stays at its original grade and the player gets a short warning. Everything is stored per player ID in oxide/data, the grade choices survive restarts, and the permission autoupgrade.use decides who may set a grade at all.
The plugin code
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Auto Upgrade", "RustPluginGenerator", "1.0.0")]
[Description("Upgrades newly placed building blocks to each player's chosen grade using inventory resources")]
public class AutoUpgrade : RustPlugin
{
private const string PermUse = "autoupgrade.use";
private const string DataFile = "AutoUpgrade";
private PluginConfig config;
private StoredData data;
private class PluginConfig
{
[JsonProperty("Use upgrade cost")]
public bool UseUpgradeCost { get; set; } = true;
[JsonProperty("Show upgrade effect")]
public bool ShowUpgradeEffect { get; set; } = true;
}
private class StoredData
{
public Dictionary<ulong, int> Grades = new Dictionary<ulong, int>();
}
// Per-step cost of upgrading FROM the given grade to the next one.
private static readonly Dictionary<BuildingGrade.Enum, Dictionary<string, int>> StepCost = new Dictionary<BuildingGrade.Enum, Dictionary<string, int>>
{
[BuildingGrade.Enum.Twigs] = new Dictionary<string, int> { ["wood"] = 10 },
[BuildingGrade.Enum.Wood] = new Dictionary<string, int> { ["wood"] = 50 },
[BuildingGrade.Enum.Stone] = new Dictionary<string, int> { ["stones"] = 100 },
[BuildingGrade.Enum.Metal] = new Dictionary<string, int> { ["metal.fragments"] = 100 },
[BuildingGrade.Enum.TopTier] = new Dictionary<string, int> { ["metal.refined"] = 25 }
};
private static readonly BuildingGrade.Enum[] Ladder =
{
BuildingGrade.Enum.Twigs,
BuildingGrade.Enum.Wood,
BuildingGrade.Enum.Stone,
BuildingGrade.Enum.Metal,
BuildingGrade.Enum.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>
{
["NoPermission"] = "You do not have permission to use auto upgrade.",
["Current"] = "Your auto upgrade grade: <color=#ce422b>{0}</color>. Use /up 1-4 to change it, /up off to disable.",
["NotSet"] = "Auto upgrade is disabled. Use /up 1-4 to pick a grade.",
["Set"] = "Auto upgrade grade set to <color=#ce422b>{0}</color>.",
["Disabled"] = "Auto upgrade disabled.",
["Usage"] = "Usage: /up <1-4> or /up off. 1 = wood, 2 = stone, 3 = metal, 4 = HQM.",
["NotEnough"] = "Not enough resources to auto upgrade this block to {0}.",
["Upgraded"] = "Block upgraded to <color=#ce422b>{0}</color>."
}, 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);
}
private void Loaded()
{
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(DataFile) ?? new StoredData();
}
private void OnServerInitialized()
{
if (data.Grades == null) data.Grades = new Dictionary<ulong, int>();
}
private void Unload()
{
SaveData();
}
private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(DataFile, data);
[ChatCommand("up")]
private void CmdUp(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)
{
int current;
if (data.Grades.TryGetValue(player.userID.Get(), out current))
{
player.ChatMessage(Msg("Current", player.UserIDString, GradeName(current)));
}
else
{
player.ChatMessage(Msg("NotSet", player.UserIDString));
}
return;
}
string arg = args[0].ToLowerInvariant();
if (arg == "off")
{
data.Grades.Remove(player.userID.Get());
SaveData();
player.ChatMessage(Msg("Disabled", player.UserIDString));
return;
}
int choice;
if (!int.TryParse(arg, out choice) || choice < 1 || choice > 4)
{
player.ChatMessage(Msg("Usage", player.UserIDString));
return;
}
data.Grades[player.userID.Get()] = choice;
SaveData();
player.ChatMessage(Msg("Set", player.UserIDString, GradeName(choice)));
}
private object OnEntityBuilt(Planner planner, GameObject gameObject)
{
if (planner == null || gameObject == null) return null;
BasePlayer player = planner.GetOwnerPlayer();
if (player == null || player.IsNpc) return null;
if (!permission.UserHasPermission(player.UserIDString, PermUse)) return null;
int choice;
if (!data.Grades.TryGetValue(player.userID.Get(), out choice)) return null;
BuildingBlock block = gameObject.ToBaseEntity() as BuildingBlock;
if (block == null) return null;
BuildingGrade.Enum target = ChoiceToGrade(choice);
if (target == BuildingGrade.Enum.None || target == block.grade) return null;
string targetName = GradeName(choice);
if (config.UseUpgradeCost)
{
Dictionary<string, int> cost = GetUpgradeCost(block.grade, target);
if (cost == null || !TakeResources(player, cost))
{
player.ChatMessage(Msg("NotEnough", player.UserIDString, targetName));
return null;
}
}
block.SetGrade(target);
block.SetHealthToMax();
block.SendNetworkUpdate();
if (config.ShowUpgradeEffect)
{
Effect.server.Run("assets/bundled/prefabs/fx/build/promote_metal.prefab", block.transform.position);
}
return null;
}
private Dictionary<string, int> GetUpgradeCost(BuildingGrade.Enum from, BuildingGrade.Enum to)
{
int start = Array.IndexOf(Ladder, from);
int end = Array.IndexOf(Ladder, to);
if (start < 0 || end < 0 || end <= start) return null;
Dictionary<string, int> total = new Dictionary<string, int>();
for (int i = start; i < end; i++)
{
Dictionary<string, int> step;
if (!StepCost.TryGetValue(Ladder[i], out step)) continue;
foreach (KeyValuePair<string, int> kv in step)
{
int existing;
total.TryGetValue(kv.Key, out existing);
total[kv.Key] = existing + kv.Value;
}
}
return total;
}
private bool TakeResources(BasePlayer player, Dictionary<string, int> cost)
{
foreach (KeyValuePair<string, int> kv in cost)
{
ItemDefinition def = ItemManager.FindItemDefinition(kv.Key);
if (def == null) return false;
if (player.inventory.GetAmount(def.itemid) < kv.Value) return false;
}
foreach (KeyValuePair<string, int> kv in cost)
{
ItemDefinition def = ItemManager.FindItemDefinition(kv.Key);
player.inventory.Take(null, def.itemid, kv.Value);
}
return true;
}
private static BuildingGrade.Enum ChoiceToGrade(int choice)
{
switch (choice)
{
case 1: return BuildingGrade.Enum.Wood;
case 2: return BuildingGrade.Enum.Stone;
case 3: return BuildingGrade.Enum.Metal;
case 4: return BuildingGrade.Enum.TopTier;
default: return BuildingGrade.Enum.None;
}
}
private static string GradeName(int choice)
{
switch (choice)
{
case 1: return "Wood";
case 2: return "Stone";
case 3: return "Metal";
case 4: return "HQM";
default: return "None";
}
}
}
}Hooks used: OnServerInitialized, OnEntityBuilt
Config file
Created automatically on first load at oxide/config/AutoUpgrade.json.
{
"Use upgrade cost": true,
"Show upgrade effect": true
}Commands and permissions
| Command | What it does | Permission |
|---|---|---|
/up <1-4> | Sets your auto upgrade grade: 1 = wood, 2 = stone, 3 = metal, 4 = HQM. Running /up with no argument prints your current grade. | autoupgrade.use |
/up off | Disables auto upgrade for you, so placed blocks keep their placed grade. | autoupgrade.use |
| Permission | Grants |
|---|---|
autoupgrade.use | Allows the player to set and use an auto upgrade grade. |
How the code works
The config class and default values
PluginConfigholds two[JsonProperty]fields:UseUpgradeCost(defaulttrue) andShowUpgradeEffect(defaulttrue).LoadDefaultConfigassigns a new instance, andLoadConfigreads the JSON file back, falling back to defaults and printing a warning if the file cannot be parsed.SaveConfigsimply writes the object, so the file is regenerated with both keys on first start.Storing a grade per player
Grades live in
Dictionary<ulong, int>inside aStoredDataclass, persisted withInterface.Oxide.DataFileSystem.WriteObjectintooxide/data/AutoUpgrade. Because the key is the player'suserIDconverted with.Get(), the setting survives reconnects, restarts and wipes of the player list. The plugin writes the data file on every change and again inUnload, so a server crash loses at most the last few commands.Parsing /up with ChatCommand
[ChatCommand("up")]handles the argument.args.Length == 0prints the current grade usingMsgand a lang key. The literaloffremoves the player's entry. A number between 1 and 4 is mapped toBuildingGrade.Enum.Wood,Stone,MetalandTopTier, then stored. Anything else replies with the usage line, so players are never left guessing which values are valid.Upgrading inside OnEntityBuilt
OnEntityBuilt(Planner planner, GameObject gameObject)gives you the planner and the newly placed object. The plugin resolves the owner withplanner?.GetOwnerPlayer(), null-checks it, casts the object withgameObject.ToBaseEntity() as BuildingBlockand ignores anything that is not a building block. It also skips when the currentgradealready equals the target or when the player lacks the permission, so twigs stay twigs for players who never ran/up.Charging the same cost as the hammer
GetUpgradeCostwalks the grade ladder from the current grade up to the target and sums the per-step cost in wood, stone, metal fragments and high quality metal.TakeResourceschecksinventory.GetAmountfor each and callsinventory.Takeonly if all of them are available; otherwise it returns false. WithUseUpgradeCostdisabled, the check is skipped and the upgrade is free. This keeps the plugin compatible with normal Rust economics instead of creating free resources.Applying the grade and feedback
After the resources are taken, the plugin sets
block.grade, callsblock.SetGrade(grade), thenblock.SetHealthToMax()andblock.SendNetworkUpdate().block.buildingIDand stability are untouched, so the structure remains part of the same base. WhenShowUpgradeEffectis enabled,Effect.server.Runplays the hammer upgrade effect atblock.transform.positionfor visual feedback. Failures print a short chat line once per placement, not aPrintWarning, so logs stay quiet during raids or wipes.
Install it on your server
Save AutoUpgrade.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/AutoUpgrade.json(Carbon:carbon/configs/AutoUpgrade.json). After editing, reload it:oxide.reload AutoUpgrade c.reload AutoUpgrade # CarbonGrant the permission
Give it to every player, or swapdefaultfor a group such asvip:oxide.grant group default autoupgrade.use c.grant group default autoupgrade.use # Carbon
Variations
Each one opens the generator with a ready prompt you can tweak.
Charge from a virtual economy
Skip material cost and charge money from Economics instead.
Generate this versionTeam-wide default grade
Store one grade per team instead of per player.
Generate this versionAdmin-only whitelist grades
Restrict certain grades behind permissions.
Generate this version
FAQ
Does this work with the hammer's normal upgrade flow?
Yes. The plugin only reacts to OnEntityBuilt, which fires when a building block is placed. Manual hammer upgrades are untouched, and if a player has auto upgrade disabled nothing changes at all.
Why did my block stay twigs after placing it?
Either you do not have autoupgrade.use, you never ran /up, or your main inventory did not hold the full upgrade cost. With UseUpgradeCost enabled, the plugin refuses rather than letting you build with materials you do not own.
Can players exploit the resources?
No. TakeResources calls inventory.GetAmount for every material and only calls inventory.Take when every amount is present, so nothing is removed when the upgrade cannot complete. The block is only upgraded after the take succeeds.
Where are settings stored and how do I clear them?
Grades are saved to oxide/data/AutoUpgrade.json keyed by Steam ID. Deleting the file, or running /up off per player, resets the behavior. The config in oxide/config/AutoUpgrade.json controls cost and effects.
Will it load on Carbon?
Yes. Drop the compiled DLL into oxide/plugins on Oxide or Carbon; both load the same plugin API and the same hook list. Restart the server or use oxide.reload AutoUpgrade after copying the file.
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
- Building grade limitsRestricts building upgrades to a configurable maximum grade, such as stopping at stone or metal, with a permission that lifts the cap.OnStructureUpgrade
Last updated Oct 26, 2026.
Customize auto upgrade on build
Customize