OnHorseUnhitch
OnHorseUnhitch is called from HitchTrough.UnHitch just before the game clears a hitch spot and detaches the animal from the trough. Returning a non-null value cancels the unhitch and leaves the animal tied.
object OnHorseUnhitch(HitchTrough.IHitchable hitchable, HitchTrough.HitchSpot hitchSpot)Parameters
| Parameter | Type | Description |
|---|---|---|
hitchable | HitchTrough.IHitchable | The HitchTrough.IHitchable being released from its hitch spot. In practice this is the ridable animal (e.g. a horse) that is currently tied to the trough; use it to identify the animal and cast it to a BaseEntity when you need prefab, position or owner data. |
hitchSpot | HitchTrough.HitchSpot | The HitchTrough.HitchSpot the animal currently occupies. It is the specific slot being cleared, and it exposes the trough context and its own occupied state so you can tell exactly which spot is being released. |
Return value
Returning null lets the game continue: the spot is cleared and the animal is detached. Returning any non-null value stops the original unhitch for that spot, so the animal stays tied and the loop breaks; the hook is cancelled for the rest of that call. The original method returns void, so the return value is only used as a cancel signal.
How it works
The hook fires inside HitchTrough.UnHitch for each hitch spot on the trough, when a spot's occupant matches the animal being released. This happens when a player unhitches an animal from a hitch trough through the game's normal interaction, and the method is also the common path used whenever the game programmatically detaches an animal from a trough. It fires once per matching spot, before the game calls SetOccupiedBy(null) and SetHitch(null, null).
hitchable is the animal being released; cast it to BaseEntity (or a concrete type such as RidableAnimal) to read its position, owner and network identity. hitchSpot identifies which slot is being freed, which is useful if a trough has several spots and you care about the specific one.
Because the animal argument is an interface type, declaring a more specific parameter type narrows when Oxide calls your method. Use that only if you want to ignore other hitchable entities. For most admin features, keep the interface signature and inspect the object yourself. The hook fires on the server only and does not run for players, so keep per-call work cheap.
Example plugin: Protect horses from unhitching during a raid cooldown
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Horse Hitch Protection", "RustPluginGenerator", "1.0.0")]
[Description("Blocks unhitching of horses while their owner is in a protection window.")]
public class HorseHitchProtection : RustPlugin
{
private const string PermBypass = "horsehitchprotection.bypass";
private PluginConfig config;
private StoredData data;
private class PluginConfig
{
[JsonProperty("Protection duration (seconds)")]
public double ProtectionSeconds = 600;
}
private class StoredData
{
[JsonProperty("Protected player ids")]
public Dictionary<string, double> Protected = new Dictionary<string, double>();
}
private class PluginData
{
public StoredData Value;
}
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);
private void Init()
{
permission.RegisterPermission(PermBypass, this);
LoadData();
}
private void Unload()
{
SaveData();
}
private void LoadData()
{
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
if (data == null || data.Protected == null) data = new StoredData();
}
private void SaveData()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
private bool IsProtected(BaseEntity entity)
{
if (entity == null) return false;
string id = entity.OwnerID.ToString();
if (!data.Protected.TryGetValue(id, out double until)) return false;
if (UnityEngine.Time.realtimeSinceStartup > until)
{
data.Protected.Remove(id);
return false;
}
return true;
}
private object OnHorseUnhitch(HitchTrough.IHitchable hitchable, HitchTrough.HitchSpot hitchSpot)
{
if (hitchable == null || hitchSpot == null) return null;
BaseEntity entity = hitchable as BaseEntity;
if (entity == null) return null;
string ownerId = entity.OwnerID.ToString();
if (permission.UserHasPermission(ownerId, PermBypass)) return null;
if (IsProtected(entity))
{
return true;
}
return null;
}
[ChatCommand("hitchprotect")]
private void CmdProtect(BasePlayer player, string command, string[] args)
{
if (player == null || !player.IsAdmin) return;
double seconds = config.ProtectionSeconds;
if (args != null && args.Length > 0 && double.TryParse(args[0], out double parsed) && parsed > 0)
{
seconds = parsed;
}
string targetId = player.UserIDString;
if (args != null && args.Length > 1)
{
BasePlayer target = BasePlayer.FindAwakeOrSleeping(args[1]);
if (target != null) targetId = target.UserIDString;
}
data.Protected[targetId] = UnityEngine.Time.realtimeSinceStartup + seconds;
SaveData();
player.ChatMessage($"Hitch protection enabled for {targetId} for {seconds:0} seconds.");
}
}
}The plugin blocks unhitching of horses while their owner is in an active raid window, which is useful on servers that want raiders to commit stolen mounts to a trough instead of instantly running off. It registers a permission so admins can bypass the lock, and a config entry controls how long the protection lasts after a player is flagged.
On OnHorseUnhitch, the plugin casts the hitchable to BaseEntity and reads its OwnerID. If the owner lacks the bypass permission and the protection window is active, it returns a non-null value, which cancels the unhitch and keeps the animal tied. Otherwise it returns null and the game detaches the horse normally. A chat command lets admins start the protection window for a given player.
Common use cases
Raid-scenario horse locking
Prevent players from unhitching horses during a raid or event window so mounts stay committed to the trough. Return a non-null value for owners in the locked state.
Logging mount transfers
Record which player's horse was unhitched, from which trough position, to audit suspicious movement of animals between teams. Return null and write to a data file.
Owner-only unhitch rules
Allow only the animal's owner or their teammates to release it, returning non-null for everyone else. Combine with team lookups via RelationshipManager for shared mounts.
Pitfalls
Fires for every matching spot
The hook is inside a loop over the trough's hitch spots and only fires for the spot whose occupant matches the animal. Avoid assuming it fires for every spot on the trough.
No built-in player context
The signature gives you no player. If you need the acting player, resolve it from the hitchable's owner or from your own command or input hooks; do not expect a BasePlayer parameter to be populated by Oxide.
Over-broad interface type
Declaring the hitchable parameter as a narrower type such as RidableAnimal changes when Oxide calls your method, silently skipping other hitchable entities. Only narrow if that filtering is exactly what you want.
Build a plugin with OnHorseUnhitch
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 OnHorseUnhitch
Generate