Skip to content
RustPluginGenerator

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.

Signature
object OnVehicleModuleMove(BaseVehicleModule moduleForItem, BaseModularVehicle modularVehicle, BasePlayer player)

Parameters

Hook parameters
ParameterTypeDescription
moduleForItemBaseVehicleModuleThe 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.
modularVehicleBaseModularVehicleThe BaseModularVehicle (e.g. a ModularCar) that owns or is receiving the module. Use it to inspect the vehicle type, its current modules and health.
playerBasePlayerThe 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

VehicleModuleGuard.cs
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 modularVehicle type 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 BaseVehicleModule component, not the Item. Use moduleForItem.condition, moduleForItem.maxCondition and moduleForItem.ShortPrefabName; go through moduleForItem.GetItem() if you truly need the item.

  • Returning a wrong type

    Only a PlayerInventory.CanMoveFromResponse is 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 lang messages rather than a long sentence.

Build a plugin with OnVehicleModuleMove

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

Prompt: Condition gate on modules using the OnVehicleModuleMove 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 OnVehicleModuleMove

Generate