OnLivestockAnimalFamiliarityAdded
OnLivestockAnimalFamiliarityAdded is called after a livestock animal has credited familiarity (husbandry progress) to a player. It fires from LivestockAnimal.AddFamiliarity once the value has already been added and the animal's clocks have been refreshed.
void OnLivestockAnimalFamiliarityAdded(Rust.Ai.Gen2.LivestockAnimal livestockAnimal, ulong userId, float seconds, Rust.Ai.Gen2.LivestockAnimal.FamiliarityReason reason)Parameters
| Parameter | Type | Description |
|---|---|---|
livestockAnimal | Rust.Ai.Gen2.LivestockAnimal | The Rust.Ai.Gen2.LivestockAnimal entity (horse, cow, etc.) whose familiarity value was just credited. Use it to read position, OwnerID, network ID or to track the animal. |
userId | ulong | The 64-bit Steam ID of the player who gained familiarity with the animal. |
seconds | float | The amount of familiarity, expressed in seconds, that was added to that player's total for this animal. |
reason | Rust.Ai.Gen2.LivestockAnimal.FamiliarityReason | A Rust.Ai.Gen2.LivestockAnimal.FamiliarityReason value describing how the familiarity was earned, e.g. Handled or Purchased. Match on it to reward only specific kinds of interaction. |
Return value
The original code ignores whatever the hook returns, since the familiarity has already been credited and the leadable-state check follows regardless. Returning null is the safe default; a non-null value has no effect on the game path.
How it works
The hook runs inside Rust.Ai.Gen2.LivestockAnimal.AddFamiliarity, immediately after CreditFamiliarity and RefreshHusbandryClocks have completed. Because it is the post phase, the familiarity value is already stored when your plugin sees the call, so it is the right place to read state, reward players, or persist progress. If the pre-hook OnLivestockAnimalFamiliarityAdd returns a non-null value, the whole block including this hook is skipped, so this hook only fires for familiarity that actually went through.
livestockAnimal is the animal entity; use it for position, net.ID.Value, owner and type checks. userId identifies the player, usually the one who handled, fed or bought the animal. seconds is the size of the credit, useful for throttling rewards or splitting bonuses per unit of familiarity. reason tells you the source (Handled, Purchased, etc.), letting you filter out automated or unrelated gains.
Oxide dispatches by parameter type, so declaring livestockAnimal as BaseEntity or BaseNetworkable is fine and matches every call; keep userId, seconds and reason at their exact types. The hook fires on every familiarity increment, which can be frequent during active husbandry, so keep the body cheap and avoid logging.
Example plugin: Pay players for husbandry familiarity
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Livestock Familiarity Reward", "RustPluginGenerator", "1.0.0")]
[Description("Rewards players with scrap when they earn livestock animal familiarity.")]
public class LivestockFamiliarityReward : RustPlugin
{
private const string PermUse = "livestockfamiliarityreward.use";
private PluginConfig config;
private readonly Dictionary<string, Timer> cooldowns = new Dictionary<string, Timer>();
private class PluginConfig
{
[JsonProperty("Reward Reason Handled")]
public bool RewardHandled = true;
[JsonProperty("Reward Reason Purchased")]
public bool RewardPurchased = true;
[JsonProperty("Scrap Per Familiarity Second")]
public int ScrapPerSecond = 5;
[JsonProperty("Cooldown Seconds")]
public float CooldownSeconds = 30f;
}
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>
{
["Rewarded"] = "You earned {0} scrap for bonding with your livestock.",
["NoPermission"] = "You do not have permission to earn familiarity rewards."
}, 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 bool IsRewarding(Rust.Ai.Gen2.LivestockAnimal.FamiliarityReason reason)
{
switch (reason)
{
case Rust.Ai.Gen2.LivestockAnimal.FamiliarityReason.Handled:
return config.RewardHandled;
case Rust.Ai.Gen2.LivestockAnimal.FamiliarityReason.Purchased:
return config.RewardPurchased;
default:
return false;
}
}
private void OnLivestockAnimalFamiliarityAdded(Rust.Ai.Gen2.LivestockAnimal livestockAnimal, ulong userId, float seconds, Rust.Ai.Gen2.LivestockAnimal.FamiliarityReason reason)
{
if (livestockAnimal == null || livestockAnimal.net == null) return;
if (seconds <= 0f || !IsRewarding(reason)) return;
string userIdString = userId.ToString();
if (!permission.UserHasPermission(userIdString, PermUse)) return;
if (cooldowns.ContainsKey(userIdString)) return;
var player = BasePlayer.FindByID(userId);
if (player == null || !player.IsConnected) return;
int amount = Mathf.Max(1, Mathf.RoundToInt(seconds * config.ScrapPerSecond));
var item = ItemManager.CreateByName("scrap", amount);
if (item == null) return;
player.GiveItem(item);
player.ChatMessage(Msg("Rewarded", userIdString, amount));
cooldowns[userIdString] = timer.Once(config.CooldownSeconds, () => cooldowns.Remove(userIdString));
}
private void Unload()
{
foreach (var timerEntry in cooldowns.Values)
{
timerEntry?.Destroy();
}
cooldowns.Clear();
}
}
}The plugin listens for familiarity credits and, for reasons configured as rewarding, pays the player a fixed amount of scrap scaled by the seconds credited. Only Handled and Purchased count by default, so passive gains are ignored. A per-player cooldown timer limits how often payouts happen, preventing spam during long handling sessions. Config exposes the reward reason flags, the scrap amount and the cooldown, and the chat message goes through the language file. Players without the livestockfamiliarityreward.use permission get no payout, and every parameter is null-checked before use.
Common use cases
Reward husbandry progress
Grant scrap, XP or items to players each time they earn familiarity by handling or purchasing a livestock animal, gated to specific FamiliarityReason values.
Track and display ownership progress
Store accumulated familiarity per player and animal ID so you can show a custom menu or leaderboard of the most bonded riders on the server.
Anti-abuse throttling
Watch for unusual patterns of familiarity credits from the same player in a short window and flag or cap suspicious gains before they affect your economy.
Pitfalls
The pre-hook can cancel this
If another plugin returns a non-null value from OnLivestockAnimalFamiliarityAdd, the credit and this post hook both never happen, so do not assume it always fires when a player interacts with an animal.
High call frequency
Familiarity can be credited repeatedly during active handling sessions. Keep the handler lightweight, cache config values, and gate payouts with timers instead of printing or writing to disk on every call.
Null and disconnected players
Always check
livestockAnimalandlivestockAnimal.netfor null, and resolve the player by ID with a null check since they may have disconnected between the credit and your handler.
Build a plugin with OnLivestockAnimalFamiliarityAdded
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 OnLivestockAnimalFamiliarityAdded
Generate