Skip to content
RustPluginGenerator

OnEngineStartFinished

OnEngineStartFinished is called when a vehicle engine controller finishes its start sequence, after the engine's On flag has been set true and the starting flag cleared. It fires once per successful engine start, not on every tick.

Signature
void OnEngineStartFinished(BaseMountable owner)

Parameters

Hook parameters
ParameterTypeDescription
ownerBaseMountableThe BaseMountable vehicle whose engine controller just completed the start sequence. Usually a ModularCar, MiniCopter, ScrapHelicopter, RHIB or similar drivable entity; null-check before casting.

Return value

The hook return value is ignored. Nothing you return changes whether the engine starts or how the controller behaves; the flags have already been set before the hook is called.

How it works

The hook fires inside VehicleEngineController.FinishStartingEngine, immediately after the controller flips the vehicle's On flag on and clears its internal starting flag. That method is the tail end of the engine start routine, so the hook runs once the engine is actually running, not when the player first presses the start button. The only parameter is the controller's owner, a BaseMountable — in practice the vehicle entity, so you can cast it to ModularCar, MiniCopter, ScrapTransportHelicopter, RHIB or read net.ID.Value, OwnerID and position directly off it. Declaring the parameter as a more specific type such as ModularCar makes Oxide skip the call for helicopters and boats, which is useful if you only care about cars; declaring BaseMountable (as in the signature) receives every vehicle engine start. Because it does not fire often, it is safe to send chat feedback, play effects, log to a data file or start a timer here. Pair it with OnEngineStarted and OnEngineStopped to observe the full engine lifecycle.

Example plugin: Announce and log vehicle engine starts

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

namespace Oxide.Plugins
{
    [Info("EngineStartAnnouncer", "RustPluginGenerator", "1.0.0")]
    [Description("Announces and logs vehicle engine starts with a per-player counter and cooldown")]
    public class EngineStartAnnouncer : RustPlugin
    {
        private const string PermUse = "enginestartannouncer.use";

        private PluginConfig config;
        private StoredData data;
        private Timer saveTimer;
        private readonly Dictionary<ulong, float> lastStart = new Dictionary<ulong, float>();

        private class PluginConfig
        {
            [JsonProperty("Chat message to driver")]
            public bool MessageDriver = true;

            [JsonProperty("Broadcast radius (meters)")]
            public float BroadcastRadius = 60f;

            [JsonProperty("Cooldown per player (seconds)")]
            public float Cooldown = 30f;
        }

        private class StoredData
        {
            [JsonProperty("Starts per player id")]
            public Dictionary<string, int> Starts = new Dictionary<string, int>();
        }

        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>
            {
                ["Started"] = "Engine started. Total starts: {0}",
                ["Nearby"] = "{0} just started a {1} nearby",
                ["NoPermission"] = "You are not allowed to start engines here"
            }, 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(PermUse, this);
            data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
            saveTimer = timer.Every(60f, SaveData);
        }

        private void Unload()
        {
            if (saveTimer != null) saveTimer.Destroy();
            SaveData();
        }

        private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, data);

        private void OnEngineStartFinished(BaseMountable owner)
        {
            if (owner == null) return;

            var vehicle = owner as BaseVehicle;
            if (vehicle == null) return;

            var driver = vehicle.GetDriver();
            if (driver == null || driver.IsNpc || !driver.IsConnected) return;
            if (vehicle.OwnerID != 0 && vehicle.OwnerID != driver.userID.Get()) return;

            float now = Time.realtimeSinceStartup;
            float last;
            if (lastStart.TryGetValue(driver.userID.Get(), out last) && now - last < config.Cooldown) return;
            lastStart[driver.userID.Get()] = now;

            string id = driver.UserIDString;
            if (!data.Starts.ContainsKey(id)) data.Starts[id] = 0;
            data.Starts[id]++;

            if (config.MessageDriver)
            {
                driver.ChatMessage(Msg("Started", id, data.Starts[id]));
            }

            Vector3 pos = vehicle.transform.position;
            foreach (var player in BasePlayer.activePlayerList)
            {
                if (player == null || player == driver) continue;
                if (Vector3.Distance(player.transform.position, pos) > config.BroadcastRadius) continue;
                player.ChatMessage(Msg("Nearby", player.UserIDString, driver.displayName, vehicle.ShortPrefabName));
            }
        }
    }
}

The plugin keeps a per-player start counter in oxide/data and a configurable cooldown, plus chat messages in the language file. OnEngineStartFinished receives the BaseMountable owner, resolves the driver via GetParentEntity() being a BaseVehicle and its GetDriver(), and bails out for NPC, unowned or cooldown-blocked vehicles. On a valid start it increments the counter, sends a chat message to the driver and broadcasts a short announcement to nearby players using distances computed from transform.position. Counters are flushed in Unload and saved on a short timer so frequent restarts do not hammer the disk. No chat spam occurs because the hook only fires once the engine has fully started, and the cooldown suppresses repeated toggles.

Common use cases

  • Charge a fee per engine start

    Call Economics from the hook and deduct a configurable amount from the driver when the engine finishes starting, refunding or blocking through a separate check if the balance is too low.

  • Warn players about noisy engines

    Broadcast a chat notice to nearby players when a vehicle reaches an engine start, so neighbours on a PvP server know a car or helicopter just came online.

  • Track vehicle usage stats

    Increment per-player or per-vehicle counters in a data file to build leaderboards of who drives the most, without polling every tick.

Pitfalls

  • Fires after the engine is already on

    By the time the hook runs the vehicle's On flag is set and the starting flag cleared, so you cannot cancel the start here. Use OnEngineStarted or an input hook if you need to prevent it.

  • owner may not be the vehicle you expect

    The parameter is a BaseMountable, which covers cars, helicopters, boats and other engine controllers. Cast with as and null-check, or narrow the parameter type to the vehicle class you care about.

  • No driver during remote starts

    Engines can be started without a mounted driver in some cases, and GetDriver() may return null. Guard against that before sending player messages.

Build a plugin with OnEngineStartFinished

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

Prompt: Charge a fee per engine start using the OnEngineStartFinished 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 OnEngineStartFinished

Generate