OnTrainCarUncouple
OnTrainCarUncouple is called when a player requests to uncouple a train car, before the coupling is actually released. Returning a non-null value cancels the uncouple.
object OnTrainCarUncouple(TrainCar trainCar, BasePlayer player)Parameters
| Parameter | Type | Description |
|---|---|---|
trainCar | TrainCar | The TrainCar the player is trying to uncouple. Use its coupling, position and net.ID to identify the consist. |
player | BasePlayer | The BasePlayer who sent the uncouple request. Already validated as non-null and within 200 units squared of the car when the hook fires. |
Return value
Return null (or nothing) to let the uncouple proceed; the game then reads the front flag and releases the coupling. Return any non-null value to stop the original code path, so the coupling stays attached and no uncouple occurs. The return value itself is ignored beyond being non-null.
How it works
This hook fires from TrainCar.RPC_WantsUncouple, the RPC a client sends when the player interacts with the coupling on a train car. The game has already checked that msg.player is not null and that the player is within roughly 14 meters (200 squared units) of the car before the hook is invoked, so every call you receive is a plausible uncouple attempt. The hook fires before the front/back bit is read and before coupling.Uncouple runs.
trainCar is the car the player clicked; it is a TrainCar, so you can declare the parameter as TrainCar and read its position, net.ID or coupling state. player is the requesting BasePlayer; use UserIDString, IsAdmin or permission checks to decide policy. Because the parameter types are already the most specific ones the game passes, declaring them differently would only cause Oxide to skip calls.
Use it to gate uncoupling for events, faction rules, or to keep a locked consist together. It is not called when the server itself decouples cars, and it does not fire on couple attempts (that is CanTrainCarCouple).
Example plugin: Lock train uncoupling to a permission
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Train Uncouple Control", "RustPluginGenerator", "1.0.0")]
[Description("Restricts who may uncouple train cars using a permission.")]
public class TrainUncoupleControl : RustPlugin
{
private const string PermUse = "trainuncouplecontrol.use";
private PluginConfig config;
private readonly Dictionary<ulong, Timer> denialCooldowns = new Dictionary<ulong, Timer>();
private class PluginConfig
{
[JsonProperty("Admins bypass permission")]
public bool AdminsBypass { get; set; } = true;
[JsonProperty("Cooldown between denial messages (seconds)")]
public float DenialCooldown { get; set; } = 5f;
}
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>
{
["Denied"] = "You do not have permission to uncouple train cars.",
["DeniedCooldown"] = "Wait a moment before trying to uncouple again."
}, 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);
}
private object OnTrainCarUncouple(TrainCar trainCar, BasePlayer player)
{
if (trainCar == null || player == null) return null;
if (config.AdminsBypass && player.IsAdmin) return null;
if (permission.UserHasPermission(player.UserIDString, PermUse)) return null;
NotifyDenied(player);
return true;
}
private void NotifyDenied(BasePlayer player)
{
ulong id = player.userID.Get();
Timer existing;
if (denialCooldowns.TryGetValue(id, out existing) && existing != null) return;
player.ChatMessage(Msg("Denied", player.UserIDString));
denialCooldowns[id] = timer.Once(Mathf.Max(1f, config.DenialCooldown), () =>
{
denialCooldowns.Remove(id);
});
}
private void Unload()
{
foreach (var entry in denialCooldowns)
{
entry.Value?.Destroy();
}
denialCooldowns.Clear();
}
}
}The plugin registers a permission trainuncouplecontrol.use and, in OnTrainCarUncouple, checks the requesting player against it. Players without the permission are denied, the hook returns true so the game stops before coupling.Uncouple runs, and the player gets a chat message pulled from the language file. Admins are always allowed so staff can still rearrange consists during an event. Denial messages are throttled with a short timer per player so a player mashing the coupling does not spam chat. The example is intentionally small: it shows the blocking pattern and the permission wiring without any persistence.
Common use cases
Event consists that must stay coupled
Block uncoupling while a train event is running so participants cannot break apart the consist you spawned. Return non-null for everyone except staff.
Permission-gated train management
Only players with a specific permission can split a train, keeping random players from scattering cars across the map.
Group or team restrictions
Allow only members of the team that owns the car, or players with building auth nearby, to uncouple it.
Pitfalls
Returning false still blocks
The game only continues when the hook returns
null. Returningfalseis a non-null value and cancels the uncouple just liketrue.Not the only uncouple path
Hooks such as this only cover player RPC requests. Server-side logic that calls
coupling.Uncoupledirectly, including other plugins, is not routed through this hook.Range check already happened
The game verifies proximity before calling the hook, so you do not need to re-check distance for fairness. Still null-check
trainCarandplayerdefensively.
Build a plugin with OnTrainCarUncouple
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 OnTrainCarUncouple
Generate