OnVehicleModuleMove
OnVehicleModuleMove is called when a player moves a vehicle module from an inventory into a modular vehicle. It lets plugins veto the move by returning a CanMoveFromResponse.
object OnVehicleModuleMove(BaseVehicleModule moduleForItem, BaseModularVehicle modularVehicle, BasePlayer player)Parameters
| Parameter | Type | Description |
|---|---|---|
moduleForItem | BaseVehicleModule | The vehicle module (engine, turret, storage, seating, etc.) that the player is trying to place into or move between vehicles. It is the item's vehicle module component, not the Item itself. |
modularVehicle | BaseModularVehicle | The BaseModularVehicle (e.g. a ModularCar) that owns or is receiving the module. Use it to inspect the vehicle type, its current modules and health. |
player | BasePlayer | The BasePlayer moving the module. Can be null in edge cases, so always null-check before reading player data. |
Return value
Return null (or any object that is not a PlayerInventory.CanMoveFromResponse) to keep vanilla behavior, which is the response built from CanBeMovedNow() and CannotBeMovedNowReason(). Return a CanMoveFromResponse to override that result directly: a failure response blocks the move and its reason is shown to the player, a success response allows it regardless of module state.
How it works
This hook fires inside BaseModularVehicle.CanMoveFrom, after the game has confirmed the item really carries a vehicle module (GetModuleForItem(item) is not null) and before the default move check runs. The game uses it for everything that ends in a module being placed on a modular car or other modular vehicle: dragging an engine, turret or storage module from the player's inventory onto the vehicle, and any code path that validates such a move.
Return a PlayerInventory.CanMoveFromResponse to take over the decision. A failure response blocks the move and supplies the reason string shown to the player; a success response allows it. Returning null (or anything that is not a response) lets the game fall through to moduleForItem.CanBeMovedNow() and moduleForItem.CannotBeMovedNowReason(), which is the normal durability/locked check.
All three parameters can be null-checked. There is no way to make Oxide skip this call by declaring a narrower parameter type, since the declared types already match the game's argument types, so validate inside your hook body. Keep the method fast: it runs on every move attempt, so avoid heavy loops and never print to console.
Example plugin: Block moving damaged modules onto vehicles
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Vehicle Module Guard", "RustPluginGenerator", "1.0.0")]
[Description("Blocks moving vehicle modules that are below a minimum condition onto modular vehicles.")]
public class VehicleModuleGuard : RustPlugin
{
private const string PermBypass = "vehiclemoduleguard.bypass";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Minimum module condition percent (0-100)")]
public float MinimumConditionPercent { get; set; } = 25f;
}
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>
{
["TooDamaged"] = "This module is too damaged to be fitted ({0}% condition, minimum {1}%)."
}, 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 OnVehicleModuleMove(BaseVehicleModule moduleForItem, BaseModularVehicle modularVehicle, BasePlayer player)
{
if (moduleForItem == null || modularVehicle == null || player == null) return null;
if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;
Item item = moduleForItem.GetItem();
if (item == null) return null;
if (item.maxCondition <= 0f) return null;
float percent = item.condition / item.maxCondition * 100f;
float required = Mathf.Clamp(config.MinimumConditionPercent, 0f, 100f);
if (percent >= required) return null;
string reason = Msg("TooDamaged", player.UserIDString, UnityEngine.Mathf.RoundToInt(percent), UnityEngine.Mathf.RoundToInt(required));
return PlayerInventory.CanMoveFromResponse.Failure(reason);
}
}
}The plugin reads a minimum module condition percentage from its config and registers the vehiclemoduleguard.bypass permission. When OnVehicleModuleMove fires, it walks the guards: null parameters, the bypass permission, the configured threshold, then moduleForItem.condition against the module's own maxCondition. If the module is too worn, it returns PlayerInventory.CanMoveFromResponse.Failure with a localized message so the player sees why the move was refused. Otherwise it returns null and the game's normal CanBeMovedNow() check decides. Because returning a failure string is player-facing, it is registered in the language file.
Common use cases
Condition gate on modules
Prevent players from fitting modules that have decayed below a configured condition percentage, protecting the server's module economy.
Raid protection windows
During offline hours, block anyone from adding a turret module to a vehicle unless they are the owner or have a bypass permission.
Vehicle-specific limits
Inspect
modularVehicletype and reject specific module categories (e.g. extra seating) on particular vehicle prefabs to enforce server rules.
Pitfalls
Item vs module confusion
The first parameter is the
BaseVehicleModulecomponent, not theItem. UsemoduleForItem.condition,moduleForItem.maxConditionandmoduleForItem.ShortPrefabName; go throughmoduleForItem.GetItem()if you truly need the item.Returning a wrong type
Only a
PlayerInventory.CanMoveFromResponseis honoured. Returning a bool or a string silently falls through to vanilla behavior, so always build a response object.Rejected reason visibility
The failure reason string is surfaced to the player in the item-move feedback. Keep it short and localized through your
langmessages rather than a long sentence.
Build a plugin with OnVehicleModuleMove
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 OnVehicleModuleMove
Generate