Skip to content
RustPluginGenerator

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.

Signature
void OnStructureUpgraded(BuildingBlock buildingBlock, BasePlayer player, BuildingGrade.Enum type, ulong skin)

Parameters

Hook parameters
ParameterTypeDescription
buildingBlockBuildingBlockThe 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.
playerBasePlayerThe 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.
typeBuildingGrade.EnumThe BuildingGrade.Enum the structure now has: Twigs, Wood, Stone, Metal or TopTier. This is the new grade, not the previous one.
skinulongThe 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

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

Prompt: Upgrade statistics using the OnStructureUpgraded hook, with a config file and a permission
Generate this plugin

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