OnVehicleModuleSelect
OnVehicleModuleSelect is called when a player clicks a module entry in the loot panel of a ModularCarGarage, right before the game deselects the current loot container and switches to that module's contents.
object OnVehicleModuleSelect(Item vehicleItem, ModularCarGarage modularCarGarage, BasePlayer player)Parameters
| Parameter | Type | Description |
|---|---|---|
vehicleItem | Item | The Item representing the modular car module the player just clicked in the garage loot panel. Use item.info.shortname or item.skin to identify which module type it is. |
modularCarGarage | ModularCarGarage | The ModularCarGarage entity the player is looting, which is the source of the vehicle module list. Use it to reach the car, its position, or its owner. |
player | BasePlayer | The BasePlayer who selected the module entry. Pass its UserIDString to permission or data lookups. |
Return value
Returning null lets the game continue: it removes the current loot container and opens the selected module. Returning any non-null object stops the method before that happens, so the player's loot panel stays on its current container and the module is never opened.
How it works
The hook fires from ModularCarGarage.RPC_SelectedLootItem, the server RPC that runs when a looting player clicks one of the module rows shown in the car garage UI. The game first verifies the player is still looting this garage and that the garage has an occupant, then resolves the clicked item to a vehicle item and calls the hook. After that it removes the existing loot container at slot 3 and opens the selected module (or the car's storage) instead.
vehicleItem is the module item as stored on the car, useful for reading info.shortname and skin to identify the module. modularCarGarage gives you the entity itself, so modularCarGarage.OwnerID or its position can be checked. player is the clicker, so permission checks go against player.UserIDString.
Because the hook stops the game's own selection path when you return a non-null value, you can refuse the switch — for instance blocking access to a module while its permission requirement is unmet. Declaring vehicleItem as Item is required; there is no narrower type to narrow dispatch with here.
Example plugin: Restrict module access until a seat unlock permission is granted
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Vehicle Module Permission Gate", "RustPluginGenerator", "1.0.0")]
[Description("Restricts access to modular car modules based on permission nodes per module shortname")]
public class VehicleModulePermissionGate : RustPlugin
{
private const string PermBypass = "vehiclemodulepermissiongate.bypass";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Module shortname to permission suffix")]
public Dictionary<string, string> ModulePermissions = new Dictionary<string, string>
{
["vehicle.1mod.engine"] = "engine",
["vehicle.2mod.fuel.tank"] = "fueltank",
["vehicle.1mod.flatbed"] = "flatbed"
};
}
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>
{
["NoAccess"] = "You do not have permission to open the {0} module."
}, 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);
foreach (var suffix in config.ModulePermissions.Values)
{
permission.RegisterPermission("vehiclemodulepermissiongate." + suffix, this);
}
}
private object OnVehicleModuleSelect(Item vehicleItem, ModularCarGarage modularCarGarage, BasePlayer player)
{
if (vehicleItem?.info == null || player == null || modularCarGarage == null) return null;
if (player.IsAdmin || permission.UserHasPermission(player.UserIDString, PermBypass)) return null;
string suffix;
if (!config.ModulePermissions.TryGetValue(vehicleItem.info.shortname, out suffix)) return null;
string node = "vehiclemodulepermissiongate." + suffix;
if (permission.UserHasPermission(player.UserIDString, node)) return null;
player.ChatMessage(Msg("NoAccess", player.UserIDString, vehicleItem.info.displayName.english));
return true;
}
}
}The plugin maps module shortnames to permission nodes in its config. When a player clicks a module in a car garage, OnVehicleModuleSelect looks up the module shortname, checks the matching permission, and returns a non-null value if the player lacks it, which stops the game from opening the module. A chat message explains the refusal. Admins and players holding the bypass permission skip the check entirely. Config reload is not needed because the mapping is read on every click, and permissions are registered in Init.
Common use cases
Gate armored or engine modules behind donor permissions
Check the module shortname and the player's permission, then return non-null to keep the module closed for non-donors.
Block looting modules of cars that are not on a parking brake
Read the garage's car state and refuse the switch when the vehicle is unsafe to loot, so players cannot move items while the car is rolling.
Log module access for auditing
Record which player opened which module and when, writing the entries to oxide/data. Return null so the selection always proceeds.
Pitfalls
A non-null return also skips container removal
The game removes the loot container at slot 3 before opening the new module. Refusing the switch leaves the player's current container intact, which is what you want, but do not expect any part of the open path to run.
The hook runs on every click, not once
Players clicking through module rows fire this repeatedly. Keep the handler cheap and never log or print inside it unconditionally.
vehicleItem can be stale
The item is resolved from the car occupant by UID; if the car changes between clicks, always null-check it and re-read
infobefore comparing shortnames.
Build a plugin with OnVehicleModuleSelect
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 OnVehicleModuleSelect
Generate