OnStructureUpgraded
OnStructureUpgraded is called after a building block has finished upgrading to a new grade inside BuildingBlock.DoUpgradeToGrade. It fires with the new grade already applied.
void OnStructureUpgraded(BuildingBlock buildingBlock, BasePlayer player, BuildingGrade.Enum type, ulong skin)Parameters
| Parameter | Type | Description |
|---|---|---|
buildingBlock | BuildingBlock | The BuildingBlock that was just upgraded to its new grade. Use it for its buildingID, OwnerID, position, and GetBuildingPrivilege() when the upgrade needs to be tied to a base or a player. |
player | BasePlayer | The BasePlayer who performed the upgrade. This comes from the upgrade message and can be null if the upgrade was not triggered by a player, so always null-check it. |
type | BuildingGrade.Enum | The BuildingGrade.Enum the structure now has: Twigs, Wood, Stone, Metal or TopTier. This is the new grade, not the previous one. |
skin | ulong | The skin id of the upgrade (ulong, 0 for the default material). Useful if you care about specific cosmetic skins applied through the upgrade path. |
Return value
The return value is ignored. DoUpgradeToGrade returns void and Oxide calls the hook purely for notification, so returning true, false or any object has no effect on the upgrade that already happened.
How it works
The hook fires at the end of BuildingBlock.DoUpgradeToGrade, after the grade change has been applied and the upgrade effect network message has been sent. It does not fire when a player merely right-clicks with the hammer to start an upgrade (that path is what OnStructureUpgrade is for, which is cancellable), and it does not fire for placing new blocks. Declaring the first parameter as BuildingBlock matches the actual argument type, so you can narrow it only if you deliberately want fewer calls; declaring player as BasePlayer already narrows out non-player upgrades, because Oxide skips your method when the argument is null or not an instance of the declared type. Because the hook is not cancellable (the original method returns void and the return value is ignored), use it for reactions such as rewards, statistics, notifications or logging upgrade activity, not for blocking upgrades. It can fire frequently on busy servers during large raids or base builds, so keep the body cheap.
Example plugin: Reward players for upgrading structures to metal or above
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Upgrade Rewards", "RustPluginGenerator", "1.0.0")]
[Description("Gives players a configurable item reward when they upgrade a building block.")]
public class UpgradeRewards : RustPlugin
{
private const string PermUse = "upgraderewards.use";
private class PluginConfig
{
[JsonProperty("Reward cooldown in seconds")]
public float CooldownSeconds = 2f;
[JsonProperty("Reward shortname")]
public string RewardShortname = "scrap";
[JsonProperty("Reward amount for Metal")]
public int MetalAmount = 1;
[JsonProperty("Reward amount for TopTier")]
public int TopTierAmount = 2;
}
private PluginConfig config;
private readonly Dictionary<ulong, float> lastReward = new Dictionary<ulong, float>();
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>
{
["Reward"] = "You received {0}x {1} for upgrading to {2}.",
["NoPermission"] = "You do not have permission to earn upgrade rewards."
}, 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 Unload()
{
lastReward.Clear();
}
private void OnStructureUpgraded(BuildingBlock buildingBlock, BasePlayer player, BuildingGrade.Enum type, ulong skin)
{
if (player == null || !player.IsConnected) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse)) return;
int amount = 0;
if (type == BuildingGrade.Enum.Metal) amount = config.MetalAmount;
else if (type == BuildingGrade.Enum.TopTier) amount = config.TopTierAmount;
if (amount <= 0) return;
float now = Time.realtimeSinceStartup;
float last;
if (lastReward.TryGetValue(player.userID, out last) && now - last < config.CooldownSeconds) return;
lastReward[player.userID] = now;
Item rewards = ItemManager.CreateByName(config.RewardShortname, amount);
if (rewards == null)
{
PrintWarning($"Unknown reward shortname: {config.RewardShortname}");
return;
}
if (!player.inventory.GiveItem(rewards))
{
rewards.Drop(player.transform.position + player.eyes.HeadForward() * 1.5f, Vector3.zero);
}
string rewardName = rewards.info != null ? rewards.info.displayName.english : config.RewardShortname;
player.ChatMessage(Msg("Reward", player.UserIDString, amount, rewardName, type.ToString()));
}
}
}The plugin listens to OnStructureUpgraded and, for every successful upgrade to Metal or TopTier, grants a small item reward directly into the player's main inventory using ItemManager.CreateByName. Rewards are configurable per grade and restricted by permission, so only players with upgraderewards.use earn anything. A short cooldown check based on UnityEngine.Time.realtimeSinceStartup keeps large upgrade bursts from flooding the inventory or spamming chat. The player receives a localized chat message built through the language files, and the reward item is dropped with Drop when the inventory is full so nothing is silently lost. Default config ships a sensible set of rewards for a vanilla-adjusted server.
Common use cases
Upgrade statistics
Count how many blocks each player upgrades per grade and persist the totals through DataFileSystem so admins can rank builders or detect suspicious bulk upgrades.
Discord or chat notifications
Broadcast when a large base finishes a TopTier upgrade so staff and players are aware of big building projects.
Economy payouts
Credit a virtual currency for upgrade work with an optional Economics reference, gated by a permission to prevent farming.
Pitfalls
Player can be null
Some upgrade paths (admin commands, plugin-driven upgrades) may not carry a player. Skip the reward logic when player is null instead of dereferencing it.
Not cancellable
OnStructureUpgraded never blocks an upgrade. Use OnStructureUpgrade or CanChangeGrade if you need to prevent or modify upgrades before they happen.
Fires very often
A base upgrade touches dozens of blocks in rapid succession. Avoid PrintWarning, Puts, file writes, or expensive lookups inside the hook body; batch data or use short cooldowns.
Build a plugin with OnStructureUpgraded
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 OnStructureUpgraded
Generate