Skip to content
RustPluginGenerator

OnApartmentRoomCheckout

OnApartmentRoomCheckout is called when a player tries to check out of an apartment room, right before the game records the checkout and frees the room.

Signature
object OnApartmentRoomCheckout(BasePlayer player, ApartmentRoom playerApartment, ApartmentBuilding apartmentBuilding)

Parameters

Hook parameters
ParameterTypeDescription
playerBasePlayerThe BasePlayer who is checking out of their apartment room. Use it for permission checks, messaging, or data lookups keyed by UserIDString.
playerApartmentApartmentRoomThe ApartmentRoom the player currently holds. Inspect its position, building, or stored state to decide whether the checkout should be allowed.
apartmentBuildingApartmentBuildingThe ApartmentBuilding instance whose TryCheckout method is running. Useful for comparing the room against the building's room list or config.

Return value

Returning any non-null value stops the checkout: TryCheckout returns false immediately and the room is not released. Return null to let the checkout proceed normally.

How it works

This hook fires inside ApartmentBuilding.TryCheckout, after the game has already confirmed the player owns an apartment size and resolved the ApartmentRoom they occupy. It runs before the Azure analytics checkout event and before the room is released, so it is the last point where a plugin can cancel the action.

player is the caller, playerApartment is the room being given up, and apartmentBuilding is the building that owns it. The room argument is the most useful for logic: you can check its size, position, or any state you track yourself (rent, visits, custom limits).

The hook fires on a concrete BasePlayer, so declaring a more specific type is not useful here; declare the exact types. Returning a non-null value aborts the checkout - the method reports failure to the caller and no analytics event is sent.

This is a low-frequency hook tied to player interaction, so light work such as permission checks, data writes, or chat messages is fine. Avoid heavy scans of all apartments on every call.

Example plugin: Charge a checkout fee before releasing the room

ApartmentCheckoutFee.cs
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("Apartment Checkout Fee", "RustPluginGenerator", "1.0.0")]
    [Description("Charges scrap from a player's main inventory before they can check out of an apartment room.")]
    public class ApartmentCheckoutFee : RustPlugin
    {
        private const string PermBypass = "apartmentcheckoutfee.bypass";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Scrap fee")]
            public int Fee = 25;

            [JsonProperty("Item shortname to charge")]
            public string CurrencyShortname = "scrap";
        }

        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>
            {
                ["FeeRequired"] = "Checking out of this apartment costs {0} {1}. You have {2}.",
                ["FeeTaken"] = "A checkout fee of {0} {1} was taken from your inventory."
            }, 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 OnApartmentRoomCheckout(BasePlayer player, ApartmentRoom playerApartment, ApartmentBuilding apartmentBuilding)
        {
            if (player == null || playerApartment == null || apartmentBuilding == null) return null;
            if (config.Fee <= 0) return null;
            if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;

            var definition = ItemManager.FindItemDefinition(config.CurrencyShortname);
            if (definition == null) return null;

            int held = player.inventory.containerMain.GetAmount(definition.itemid, true);
            if (held < config.Fee)
            {
                player.ChatMessage(Msg("FeeRequired", player.UserIDString, config.Fee, definition.displayName.english, held));
                return true;
            }

            var collected = new List<Item>();
            player.inventory.containerMain.Take(collected, definition.itemid, config.Fee);
            if (collected.Count > 0)
            {
                foreach (var item in collected)
                {
                    item.Remove();
                }
            }

            player.ChatMessage(Msg("FeeTaken", player.UserIDString, config.Fee, definition.displayName.english));
            return null;
        }
    }
}

The plugin registers a Scrap checkout fee and gives it back through a refund command, so no economy plugin is required. When OnApartmentRoomCheckout fires it checks the permission that bypasses the fee and the player's main inventory for the configured scrap amount. If the scrap is missing, the player gets a localized chat message and the hook returns true, which makes TryCheckout fail and leaves the room rented. When the scrap is present it is taken from the main container and the hook returns null so the game continues with the checkout. A small config class holds the amount and permission, and messages are registered in the lang file.

Common use cases

  • Checkout cooldown per team

    Deny a checkout if the player's team completed one within the last N minutes, storing the timestamps in oxide/data.

  • Rent settlement

    Award remaining rent or a deposit refund to the player's economy balance at the moment they leave, keyed by UserIDString.

  • Restricted rooms

    Allow checkout only on rooms of a certain size or in a certain area, blocking the action with a chat explanation otherwise.

Pitfalls

  • Hook fires before the room is freed

    The ApartmentRoom is still assigned when the hook runs. Do not assume it is already vacant or write final data that depends on the room being gone.

  • Non-null means cancelled

    Returning false also cancels the checkout, because the check is for any non-null value. Only return null when the action should go through.

  • No paired completion hook guarantee

    The checkout may be denied by another plugin or by the game itself after your hook runs, so do not perform irreversible changes such as removing items unless you are prepared for the action to still fail later.

Build a plugin with OnApartmentRoomCheckout

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

Prompt: Checkout cooldown per team using the OnApartmentRoomCheckout 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 OnApartmentRoomCheckout

Generate