Skip to content
RustPluginGenerator

OnApartmentRoomPurchase

OnApartmentRoomPurchase is called on the server when a player rents an apartment room, right after the game finds an unoccupied room and before scrap is taken and ownership is granted.

Signature
object OnApartmentRoomPurchase(ApartmentRoom apartmentRoom, BasePlayer player, ApartmentSize size, ApartmentBuilding apartmentBuilding)

Parameters

Hook parameters
ParameterTypeDescription
apartmentRoomApartmentRoomThe specific ApartmentRoom the game picked as the closest unoccupied room of the requested size. Never null here, since the hook only fires after the room lookup succeeded.
playerBasePlayerThe BasePlayer attempting to rent the room. Use it for permission checks, messages, or account-level lookups.
sizeApartmentSizeThe ApartmentSize enum value requested by the player (small or large room). Useful for pricing rules or size-based restrictions.
apartmentBuildingApartmentBuildingThe ApartmentBuilding entity that owns the room and runs the purchase. Reading its position tells you which building is involved.

Return value

Return null to let the purchase proceed normally. Returning any non-null value aborts the original code path, so no scrap is taken and the room is not assigned. The returned value itself is discarded because PurchaseRoom returns void.

How it works

The hook fires inside ApartmentBuilding.PurchaseRoom, after FetchClosestUnoccupiedRoom returned a room and before the scrap cost is calculated, scrap is removed through player.inventory.Take, the room is handed over via GiveRoomToPlayer and the RENT_APARTMENT achievement is granted.

Use player for permission checks, team restrictions or logging of who rents what. size lets you differentiate rules for small and large rooms. apartmentRoom gives you the actual room object, and apartmentBuilding is useful for proximity checks or per-building configuration keyed by net.ID.Value.

Declaring player as a more specific type is possible but rarely useful here, since the caller always passes a real BasePlayer. Because the hook runs before any inventory mutation, it is the correct place to reject a purchase: returning a non-null value stops the original code path, so the player neither pays scrap nor receives the room. Oxide only calls overloads whose declared parameter types match the arguments, so a mismatched signature is silently skipped.

This is a pre-purchase gate, not a confirmation callback.

Example plugin: Restrict room rentals by permission and building

ApartmentRentalRules.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Apartment Rental Rules", "RustPluginGenerator", "1.0.0")]
    [Description("Limits how many apartment rooms a player can rent and blocks rental for players without permission.")]
    public class ApartmentRentalRules : RustPlugin
    {
        private const string PermBypass = "apartmentrentalrules.bypass";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Maximum rooms per player")]
            public int MaxRoomsPerPlayer = 1;

            [JsonProperty("Require permission to rent")]
            public bool RequirePermission = false;
        }

        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>
            {
                ["LimitReached"] = "You already rent {0} room(s). The limit is {1}.",
                ["NotAllowed"] = "You are not allowed to rent an apartment room."
            }, 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(PermBypass, this);
        }

        private object OnApartmentRoomPurchase(ApartmentRoom apartmentRoom, BasePlayer player, ApartmentSize size, ApartmentBuilding apartmentBuilding)
        {
            if (player == null || player.IsNpc)
                return null;

            if (permission.UserHasPermission(player.UserIDString, PermBypass))
                return null;

            if (config.RequirePermission && !permission.UserHasPermission(player.UserIDString, PermBypass))
                return null;

            int owned = CountOwnedRooms(player.userID.Get());
            if (owned >= config.MaxRoomsPerPlayer)
            {
                player.ChatMessage(Msg("LimitReached", player.UserIDString, owned, config.MaxRoomsPerPlayer));
                return false;
            }

            return null;
        }

        private void OnApartmentRoomPurchased(ApartmentRoom apartmentRoom, BasePlayer player, ApartmentSize size)
        {
            if (player == null)
                return;

            Puts($"{player.displayName} ({player.UserIDString}) rented a {size} apartment room.");
        }

        private int CountOwnedRooms(ulong userId)
        {
            int count = 0;
            foreach (BaseNetworkable networkable in BaseNetworkable.serverEntities)
            {
                ApartmentBuilding building = networkable as ApartmentBuilding;
                if (building == null || building.IsDestroyed)
                    continue;

                // Each building exposes its rooms; count the ones owned by this player.
                foreach (ApartmentRoom room in building.rooms)
                {
                    if (room != null && room.OwnerID == userId)
                        count++;
                }
            }
            return count;
        }
    }
}

The plugin gives admins a apartmentrentalrules.bypass permission and a configurable maximum number of rooms a player may rent per wipe. On OnApartmentRoomPurchase it exits immediately for bypass holders, then counts rooms already owned by the player's user ID across all ApartmentBuilding entities in BaseNetworkable.serverEntities. If the limit is reached, the hook returns false to block the transaction and the player gets a localized chat message. OnApartmentRoomPurchased is used to log the completed rental and clear the player's cached count so the next purchase re-evaluates fresh data.

Common use cases

  • Limit rooms per player

    Count owned rooms and block the purchase when a per-wipe cap is reached, preventing one player or clan from renting every unit in a building.

  • Gate rentals behind a permission or rank

    Check permission.UserHasPermission(player.UserIDString, ...) and return non-null for players who have not been granted access to the apartment system.

  • Restrict by size or building

    Use size and apartmentBuilding.net.ID.Value to allow only small rooms, or to disable rentals in specific buildings listed in the config.

Pitfalls

  • Returning 0 or false is still non-null

    Any non-null return blocks the purchase, including false and 0. If you intend to allow the rental, return null, not a falsy value.

  • Do not charge scrap here

    The game takes the scrap itself after the hook returns null. Taking scrap inside the hook will double charge unless you always block the original purchase afterward.

  • No room object before the hook

    The hook only runs after an unoccupied room was found, so a failed lookup never reaches your code and you cannot use this hook to log such failures.

Build a plugin with OnApartmentRoomPurchase

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Limit rooms per player using the OnApartmentRoomPurchase 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 OnApartmentRoomPurchase

Generate