OnPayForUpgrade
OnPayForUpgrade is called when a player confirms an upgrade on a building block and the server is about to charge the upgrade cost. Returning a non-null value skips the payment and the upgrade entirely.
object OnPayForUpgrade(BasePlayer player, BuildingBlock buildingBlock, ConstructionGrade g)Parameters
| Parameter | Type | Description |
|---|---|---|
player | BasePlayer | The player who triggered the upgrade from the building block's upgrade menu. Never null when the hook fires, but check it anyway if you rely on inventory or permission data. |
buildingBlock | BuildingBlock | The BuildingBlock entity being upgraded. Use its buildingID, grade and GetBuildingPrivilege() to inspect ownership or authorization. |
g | ConstructionGrade | The ConstructionGrade describing the target grade, including grade and skin. Useful for reading the destination building grade and its cost. |
Return value
Returning null lets the original code continue, so the normal upgrade cost is charged and the upgrade proceeds. Returning any non-null object (a false works fine) short-circuits the method: no resources are taken and the building block is not upgraded, but no message is shown to the player, so send your own feedback before returning.
How it works
The hook fires inside BuildingBlock.PayForUpgrade, right before the server builds the list of required ItemAmounts from g.CostToBuild(grade) and takes the resources from the player's inventory. It runs after the player picks a grade in the hammer upgrade menu and before any items are consumed.
player is the one paying and receiving the upgrade. buildingBlock is the structure being modified, so buildingBlock.buildingID, buildingBlock.grade and buildingBlock.GetBuildingPrivilege() give you the ownership context. g is the target ConstructionGrade, so g.grade tells you the destination grade (BuildingGrade.Enum) and g.skin the skin that will be applied.
Declaring player as BasePlayer is already the exact type; narrowing is not useful here. If you only care about one grade, compare g.grade early and return null immediately. The hook is not called for free-build creative placement, because the creative check happens in the same if after your hook runs.
Use it for upgrade taxes, grade restrictions, per-grade permission gates, or charging alternate currency through a referenced economy plugin.
Example plugin: Charge an economy tax for upgrades and block disallowed grades
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
namespace Oxide.Plugins
{
[Info("Upgrade Tax", "RustPluginGenerator", "1.0.0")]
[Description("Charges an economy fee for building upgrades and blocks grades the player is not allowed to use.")]
public class UpgradeTax : RustPlugin
{
private const string PermUse = "upgradetax.use";
private const string PermMetal = "upgradetax.metal";
private const string PermTopTier = "upgradetax.toptier";
[PluginReference] private Plugin Economics;
private class PluginConfig
{
[JsonProperty("Enable economy fee")]
public bool EnableFee = true;
[JsonProperty("Fee for Wood")]
public double FeeWood = 5.0;
[JsonProperty("Fee for Stone")]
public double FeeStone = 10.0;
[JsonProperty("Fee for Metal")]
public double FeeMetal = 25.0;
[JsonProperty("Fee for TopTier")]
public double FeeTopTier = 50.0;
[JsonProperty("Require permission for Metal")]
public bool RequireMetalPerm = false;
[JsonProperty("Require permission for TopTier")]
public bool RequireTopTierPerm = false;
}
private PluginConfig config;
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>
{
["NoPermissionGrade"] = "You are not allowed to upgrade to this grade.",
["CannotAfford"] = "You cannot afford the upgrade fee of {0}.",
["NoEconomy"] = "Upgrade fees are not available right now."
}, 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(PermMetal, this);
permission.RegisterPermission(PermTopTier, this);
}
private object OnPayForUpgrade(BasePlayer player, BuildingBlock buildingBlock, ConstructionGrade g)
{
if (player == null || buildingBlock == null || g == null || g.gradeBase == null) return null;
if (player.IsAdmin) return null;
if (!permission.UserHasPermission(player.UserIDString, PermUse)) return null;
BuildingGrade.Enum grade = g.gradeBase.type;
if (grade == BuildingGrade.Enum.Metal && config.RequireMetalPerm && !permission.UserHasPermission(player.UserIDString, PermMetal))
{
player.ChatMessage(Msg("NoPermissionGrade", player.UserIDString));
return false;
}
if (grade == BuildingGrade.Enum.TopTier && config.RequireTopTierPerm && !permission.UserHasPermission(player.UserIDString, PermTopTier))
{
player.ChatMessage(Msg("NoPermissionGrade", player.UserIDString));
return false;
}
if (!config.EnableFee) return null;
double fee = FeeForGrade(grade);
if (fee <= 0.0) return null;
if (Economics == null)
{
player.ChatMessage(Msg("NoEconomy", player.UserIDString));
return null;
}
double balance = Economics.Call<double>("Balance", player.UserIDString);
if (balance < fee)
{
player.ChatMessage(Msg("CannotAfford", player.UserIDString, fee));
return false;
}
Economics.Call("Withdraw", player.UserIDString, fee);
return null;
}
private double FeeForGrade(BuildingGrade.Enum grade)
{
switch (grade)
{
case BuildingGrade.Enum.Wood: return config.FeeWood;
case BuildingGrade.Enum.Stone: return config.FeeStone;
case BuildingGrade.Enum.Metal: return config.FeeMetal;
case BuildingGrade.Enum.TopTier: return config.FeeTopTier;
default: return 0.0;
}
}
}
}The plugin intercepts OnPayForUpgrade before vanilla charges the build cost. Admins and players without upgradetax.use are passed through untouched. It reads the target grade from g.gradeBase.type and, when the config requires it, blocks Metal upgrades for players without upgradetax.metal and TopTier upgrades for players without upgradetax.toptier, sending the NoPermissionGrade message and returning false. When the fee is enabled, it looks up the per-grade fee and checks the player's Economics balance. If Economics is missing it sends the NoEconomy message and lets the upgrade proceed. If the balance is below the fee it sends CannotAfford with the amount and returns false, cancelling the upgrade before vanilla resources are taken. Otherwise it withdraws the fee through Economics and returns null, so the upgrade proceeds with vanilla cost handling.
Common use cases
Per-grade permission gates
Block upgrades to Metal or TopTier unless the player has a specific permission node, returning false for unauthorized attempts.
Economy-based upgrade taxes
Charge a currency amount through Economics on top of the vanilla build cost so hard farming gives a money sink to the server economy.
Raid-time upgrade limitations
Prevent upgrading blocks during configured raid windows or inside areas controlled by a specific group by inspecting the building privilege.
Pitfalls
No automatic feedback on cancel
Returning a non-null value silently stops the upgrade. Always message the player through lang before returning, otherwise they see nothing happen after clicking a grade.
Creative mode is checked separately
The free-build creative check runs after your hook, so do not rely on it to save admins. Add your own exemption if you want admins or creative mode to skip taxes.
Missing Economics plugin
If you gate upgrades on a referenced plugin, handle the case where it is null or the balance check fails, otherwise every upgrade on the server can be blocked.
Build a plugin with OnPayForUpgrade
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnPayForUpgrade
Generate