Skip to content
RustPluginGenerator

OnEngineStop

OnEngineStop is called when a player requests that a boat engine be turned off, before the game actually disables it. Returning a non-null value cancels the shutdown.

Signature
object OnEngineStop(SmallEngine smallEngine, BasePlayer player)

Parameters

Hook parameters
ParameterTypeDescription
smallEngineSmallEngineThe SmallEngine component being turned off. Use it to reach the parent boat via GetParentEntity(), read the owner, or compare against tracked engines.
playerBasePlayerThe BasePlayer whose RPC requested the shutdown (the player interacting with the engine control). Can be null in edge cases, so null-check it before use.

Return value

At the SmallEngine.TurnOff site the original method returns void and any non-null return skips the shutdown call, leaving the engine running. At VehicleEngineController.StopEngine a non-null return likewise aborts the stop sequence. Return null (or nothing) to let the engine turn off normally.

How it works

This hook fires from two places. The main one is SmallEngine.TurnOff(RPCMessage), the server-side RPC handler invoked when a player switches a boat engine off from the controls; here smallEngine is the engine and player is the caller. The second is VehicleEngineController.StopEngine, which uses the other overload OnEngineStop(BaseMountable owner) and fires on non-player-driven shutdown paths such as vehicles whose engine is stopped by code.

Because the two calls carry different argument shapes, declaring object OnEngineStop(SmallEngine engine, BasePlayer player) only receives the player-driven case. To also intercept the controller path you must declare the BaseMountable overload as well. Oxide matches parameters by type, so narrowing BasePlayer to a subclass (for example ScientistNPC) would skip ordinary player calls.

Practical use: gate engine shutdown by permission, building privilege, or combat state. Since the return value cancels the original method, the engine stays running, which can be surprising to the player, so send chat feedback from the hook.

Other signatures

Oxide also calls OnEngineStop from other places in the game with these parameter types. Declare the overload you need, or several:

  • object OnEngineStop(BaseMountable owner)

Example plugin: Prevent engine shutdown while the crew is in combat

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

namespace Oxide.Plugins
{
    [Info("Engine Stop Guard", "RustPluginGenerator", "1.0.0")]
    [Description("Blocks boat engine shutdown for a short time after a player or their boat takes damage")]
    public class EngineStopGuard : RustPlugin
    {
        private const string PermBypass = "enginestopguard.bypass";

        private PluginConfig config;
        private readonly Dictionary<ulong, DateTime> combatUntil = new Dictionary<ulong, DateTime>();

        private class PluginConfig
        {
            [JsonProperty("Combat block seconds")]
            public float CombatBlockSeconds = 8f;

            [JsonProperty("Block for boat owner and team")]
            public bool UseTeam = true;
        }

        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>
            {
                ["Blocked"] = "Engine locked in combat. Try again in {0} seconds."
            }, 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 OnEngineStop(SmallEngine engine, BasePlayer player)
        {
            if (engine == null || player == null || !player.IsConnected) return null;
            if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;

            DateTime until;
            if (!combatUntil.TryGetValue(player.userID.Get(), out until) || until <= DateTime.UtcNow) return null;

            double remaining = Math.Ceiling((until - DateTime.UtcNow).TotalSeconds);
            player.ChatMessage(Msg("Blocked", player.UserIDString, remaining));
            return true;
        }

        private object OnEngineStop(BaseMountable owner)
        {
            if (owner == null) return null;
            return null;
        }

        private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
        {
            if (entity == null || info == null || config.CombatBlockSeconds <= 0f) return;

            var player = entity as BasePlayer;
            if (player != null && !player.IsNpc)
            {
                MarkCombat(player.userID.Get());
                if (config.UseTeam && player.currentTeam != 0)
                {
                    var team = RelationshipManager.ServerInstance.FindTeam(player.currentTeam);
                    if (team != null)
                        foreach (var member in team.members)
                            MarkCombat(member);
                }
                return;
            }

            if (entity is BaseBoat)
            {
                ulong ownerId = entity.OwnerID;
                if (ownerId != 0) MarkCombat(ownerId);
            }
        }

        private void MarkCombat(ulong userId)
        {
            if (userId == 0) return;
            combatUntil[userId] = DateTime.UtcNow.AddSeconds(config.CombatBlockSeconds);
        }

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

The plugin registers the enginestopguard.bypass permission and stores the combat hold duration in config. When OnEngineStop fires, it checks that the engine and player are valid, that the caller may bypass, and whether the player or a team member recently took damage (tracked in OnEntityTakeDamage). If combat is active, it returns true to stop the shutdown, sends the player a localized message, and tells them how long until the hold expires. The BaseMountable overload is declared so the controller path is also handled, and combat timestamps are cleared on unload. Timestamps use DateTime.UtcNow.

Common use cases

  • Combat lock for boat crews

    Block engine shutdown for a few seconds after the boat or crew takes damage so players cannot instantly flee or kill the engine mid-fight.

  • Permission-gated engine control

    Only allow turning an engine off for players holding a specific permission, useful on servers with shared public boats.

  • Audit logging of shutdowns

    Record who switched off which boat engine and when by writing the call to a data file, without blocking the shutdown itself.

Pitfalls

  • Two overloads, one hook name

    Declaring only the (SmallEngine, BasePlayer) overload misses the VehicleEngineController.StopEngine path. Add OnEngineStop(BaseMountable owner) if you need to block every shutdown.

  • Null player argument

    The RPC player can be null. Always test player == null (and !player.IsConnected) before reading UserIDString or sending messages.

  • Blocking shutdown silently

    Returning non-null leaves the engine running with no built-in feedback. Send a chat reply from the hook so players understand why controls did nothing.

Build a plugin with OnEngineStop

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

Prompt: Combat lock for boat crews using the OnEngineStop 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 OnEngineStop

Generate