Skip to content
RustPluginGenerator

OnVehicleModuleDeselected

OnVehicleModuleDeselected is called when a player closes the module selection / loot UI on a ModularCarGarage, right after the game removes the garage container from the player's loot panel.

Signature
void OnVehicleModuleDeselected(ModularCarGarage modularCarGarage, BasePlayer player)

Parameters

Hook parameters
ParameterTypeDescription
modularCarGarageModularCarGarageThe ModularCarGarage entity the player is interacting with. Its inventory holds the module slots that were just being managed; use it to identify the car and read remaining modules.
playerBasePlayerThe BasePlayer who closed the module selection UI. Always check it for null and for IsConnected before sending messages or running per-player logic.

Return value

The hook is called for its side effects only; the game method returns void and the value you return is ignored. Returning null or any other value does not change the loot UI behavior.

How it works

This hook fires inside ModularCarGarage.RPC_DeselectedLootItem, after the game removes the vehicle's loot container slot from the player's loot UI and resends the loot panel. In practice it runs when a player stops looking at the modular car garage panel, typically by closing the loot screen or walking away and letting the loot UI drop the container. That makes it a reliable signal that a player finished a module interaction session.

The two parameters cover everything you normally need. player is the person who left the UI, which is the right key for per-player state such as tracking when they last touched a vehicle, cooldowns, or notification timers. modularCarGarage is the garage entity itself, so you can inspect its modules, read owner information, or compare it against a set of tracked vehicles. Declaring the garage parameter as a more specific type is not useful here because the only injected call always passes a ModularCarGarage; narrowing player to a subtype would make Oxide skip the call in every case.

Because this fires only on UI deselect, not on every frame or every damage tick, it is safe to send a chat message or spawn a short timer from it. Treat it as a lifecycle event, not as a confirmation that a module was actually changed — pair it with the select hooks if you need the full transaction.

Example plugin: Lock a car's modules for a moment after the owner closes the garage

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

namespace Oxide.Plugins
{
    [Info("Vehicle Module Deselect Cooldown", "RustPluginGenerator", "1.0.0")]
    [Description("Enforces a short cooldown between closing a modular car garage and selecting the next module.")]
    public class VehicleModuleDeselectCooldown : RustPlugin
    {
        private const string PermBypass = "vehiclemoduledeselectcooldown.bypass";

        private PluginConfig config;
        private readonly Dictionary<ulong, DeselectRecord> records = new Dictionary<ulong, DeselectRecord>();

        private class PluginConfig
        {
            [JsonProperty("Cooldown seconds between deselect and next module select")]
            public float CooldownSeconds = 3f;
        }

        private class DeselectRecord
        {
            public ulong GarageNetId;
            public float Timestamp;
        }

        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>
            {
                ["Cooldown"] = "Wait {0:0.0}s before changing modules on this vehicle 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(PermBypass, this);
        }

        private void Unload()
        {
            records.Clear();
        }

        private void OnVehicleModuleDeselected(ModularCarGarage modularCarGarage, BasePlayer player)
        {
            if (player == null || modularCarGarage == null || modularCarGarage.net == null) return;
            records[player.userID.Get()] = new DeselectRecord
            {
                GarageNetId = modularCarGarage.net.ID.Value,
                Timestamp = UnityEngine.Time.realtimeSinceStartup
            };
        }

        private object OnVehicleModuleSelect(ModularCarGarage modularCarGarage, BasePlayer player)
        {
            if (player == null || modularCarGarage == null || modularCarGarage.net == null) return null;
            if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;

            DeselectRecord record;
            if (!records.TryGetValue(player.userID.Get(), out record)) return null;
            if (record.GarageNetId != modularCarGarage.net.ID.Value) return null;

            float elapsed = UnityEngine.Time.realtimeSinceStartup - record.Timestamp;
            if (elapsed >= config.CooldownSeconds) return null;

            player.ChatMessage(Msg("Cooldown", player.UserIDString, config.CooldownSeconds - elapsed));
            return true;
        }
    }
}

The plugin records each player who closes a modular car garage UI and stores the car's network id along with a timestamp. On the next OnVehicleModuleSelect, it checks whether the player reopened the same vehicle within the configured cooldown window and, if so, blocks the selection with a localized chat warning. Configuration exposes the cooldown in seconds and an optional permission bypass. All messages go through the language file, state is held in a simple dictionary keyed by user id, and the hooks null-check every argument so a disconnected player or a destroyed garage does not throw.

Common use cases

  • Rate-limit module swapping

    Track when a player leaves the garage UI and reject the next module select if it comes too soon, throttling spam of engine and turret swaps during combat.

  • Log vehicle modification sessions

    Record which player closed which car garage and when, giving admins an audit trail to pair with OnVehicleModuleSelect and OnVehicleModulesAssigned for incident review.

  • Close custom UI panels

    If your plugin opened a custom CUI overlay while the player was tuning the car, destroy it here so the overlay does not linger after the vanilla garage screen is gone.

Pitfalls

  • Not a confirmation of change

    The hook fires whenever the player leaves the UI, even if they never changed a module. Use OnVehicleModulesAssigned or OnVehicleModuleSelected when you need proof an action happened.

  • Player may be disconnecting

    A player can disappear while the loot UI is open. The BasePlayer reference can be null or disconnected, so guard with a null and IsConnected check before calling ChatMessage.

  • Garage entity can already be dead

    The car may have been destroyed or despawned before the player exits the UI. Access modularCarGarage.net only after confirming the object is not null, and avoid assuming the car still exists on the server.

Build a plugin with OnVehicleModuleDeselected

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

Prompt: Rate-limit module swapping using the OnVehicleModuleDeselected 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 OnVehicleModuleDeselected

Generate