OnApartmentRoomUpgrade
OnApartmentRoomUpgrade is called when a player attempts to upgrade their current apartment room to a larger size, before the server picks the destination room and moves the player.
object OnApartmentRoomUpgrade(ApartmentRoom playerApartment, BasePlayer player, ApartmentSize size, ApartmentBuilding apartmentBuilding)Parameters
| Parameter | Type | Description |
|---|---|---|
playerApartment | ApartmentRoom | The ApartmentRoom the player is currently inside and is trying to upgrade to a larger size. |
player | BasePlayer | The BasePlayer requesting the room upgrade. |
size | ApartmentSize | The ApartmentSize the room is being upgraded to. Compare against ApartmentSize enum values to gate specific tiers. |
apartmentBuilding | ApartmentBuilding | The ApartmentBuilding instance that owns the room and is running TryUpgradeRoom. |
Return value
Returning null lets the original method continue, which fetches the closest unoccupied room of the requested size and proceeds with the upgrade. Returning any non-null value makes TryUpgradeRoom return immediately, so the upgrade is cancelled and the player keeps their current room.
How it works
This hook fires inside ApartmentBuilding.TryUpgradeRoom, right after the server confirms the player has a current room and before it calls FetchClosestUnoccupiedRoom(size) to find the target room. It fires only on the upgrade attempt itself, not on the initial purchase (that path uses OnApartmentRoomPurchase).
The parameters cover the whole context of the request: playerApartment is the room being left, player is the requester, size is the requested destination tier, and apartmentBuilding is the parent building. Use player for permission checks and size to allow or deny specific upgrade tiers. Declaring the parameters with these exact apartment types narrows the hook to apartment upgrades only.
Returning any non-null value aborts the upgrade: the game method returns immediately and no new room is fetched, so the player stays in their current room. This is a natural place for an economy charge, cooldown check, or per-tier permission gate. Because the call happens before the destination room is known, you cannot inspect the target room here.
Example plugin: Gate apartment upgrades behind a permission per size
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Apartment Upgrade Gate", "RustPluginGenerator", "1.0.0")]
[Description("Requires a permission per apartment size and rate-limits upgrade attempts.")]
public class ApartmentUpgradeGate : RustPlugin
{
private const string PermSmall = "apartmentupgradegate.small";
private const string PermMedium = "apartmentupgradegate.medium";
private const string PermLarge = "apartmentupgradegate.large";
private PluginConfig config;
private readonly Dictionary<ulong, float> lastAttempt = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Cooldown seconds between attempts")]
public float CooldownSeconds = 5f;
}
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 upgrade to this apartment size.",
["Cooldown"] = "Please wait {0} more seconds before upgrading again."
}, 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(PermSmall, this);
permission.RegisterPermission(PermMedium, this);
permission.RegisterPermission(PermLarge, this);
}
private object OnApartmentRoomUpgrade(ApartmentRoom playerApartment, BasePlayer player, ApartmentSize size, ApartmentBuilding apartmentBuilding)
{
if (player == null || playerApartment == null)
return null;
float now = Time.realtimeSinceStartup;
float last;
if (lastAttempt.TryGetValue(player.userID.Get(), out last))
{
float remaining = config.CooldownSeconds - (now - last);
if (remaining > 0f)
{
player.ChatMessage(Msg("Cooldown", player.UserIDString, Mathf.CeilToInt(remaining)));
return false;
}
}
lastAttempt[player.userID.Get()] = now;
string perm = PermissionForSize(size);
if (perm != null && !permission.UserHasPermission(player.UserIDString, perm))
{
player.ChatMessage(Msg("NoPermission", player.UserIDString));
return false;
}
return null;
}
private static string PermissionForSize(ApartmentSize size)
{
string name = size.ToString().ToLowerInvariant();
if (name.Contains("medium") || name.Contains("middle")) return PermMedium;
if (name.Contains("large") || name.Contains("big")) return PermLarge;
return PermSmall;
}
private void Unload()
{
lastAttempt.Clear();
}
}
}The plugin maps each ApartmentSize to a permission (apartmentupgradegate.medium and similar) and registers them on load. When OnApartmentRoomUpgrade fires, it checks whether the player passes a quick cooldown check and holds the permission for the requested size. A missing permission sends a localized chat message telling the player which permission is required and returns a non-null value, which cancels the upgrade before the server reserves a new room. The cooldown dictionary prevents players from spamming the apartment terminal and is cleared on unload.
Common use cases
Charge an economy balance per upgrade
Call into an Economy plugin with the player's ID and the cost for the requested size, and cancel the upgrade when the balance is too low.
Restrict large apartments to donors
Compare the
sizeparameter against the highest tier and block upgrades unless the player holds a VIP permission.Rate-limit upgrades
Track the last upgrade time per player ID and cancel repeat attempts inside a short window.
Pitfalls
Wrong size means no upgrade
sizeis the requested destination tier, not the current room's size. Compare it againstplayerApartmentonly when you need the delta.No destination room visible yet
The server picks the new room after this hook, so
FetchClosestUnoccupiedRoomhas not run. Do not try to read the target room from the parameters.No resource refund on cancel
Returning non-null simply aborts the method; any cost the game would have applied is skipped, but no cleanup of prior state happens. Handle refunds yourself if you also charge elsewhere.
Build a plugin with OnApartmentRoomUpgrade
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 OnApartmentRoomUpgrade
Generate