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.
void OnEngineStartFinished(BaseMountable owner)Parameters
| Parameter | Type | Description |
|---|---|---|
owner | BaseMountable | The 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
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
asand 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.
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