OnFishingStopped
OnFishingStopped is called when a fishing rod's cast session ends, right after the game records the failure reason in BaseFishingRod.Server_Cancel.
void OnFishingStopped(BaseFishingRod fishingRod, BaseFishingRod.FailReason reason)Parameters
| Parameter | Type | Description |
|---|---|---|
fishingRod | BaseFishingRod | The BaseFishingRod entity that stopped fishing. Use its ownerPlayer, GetOwnerPlayer() or position to identify who was fishing; the rod is still a valid networked entity at this point. |
reason | BaseFishingRod.FailReason | The BaseFishingRod.FailReason value describing why the cast ended (e.g. cancelled, missed, escaped). Compare it against the enum values to distinguish a player cancel from a failed catch. |
Return value
The hook is called from a void method and its return value is ignored. Returning nothing (or any value) has no effect on whether the fishing session ends or how the game handles it.
How it works
The hook fires at the end of BaseFishingRod.Server_Cancel, which the game runs whenever an active fishing attempt is terminated. That method is the shared teardown path: it is reached when the player cancels the cast, when the attempt fails, and in other cases the rod stops fishing, and the passed reason tells you which. Because the call happens after the analytics event is sent, the rod still exists and its owner is still known.
Use fishingRod to resolve the fisher: fishingRod.ownerPlayer or fishingRod.GetOwnerPlayer() gives a BasePlayer (null if they disconnected). fishingRod.transform.position gives the spot for range or zone checks. The reason parameter lets you ignore player-initiated cancels and react only to completed or failed attempts.
There is a single injection site and the hook does not narrow by argument type in a useful way here, so declare the exact BaseFishingRod and BaseFishingRod.FailReason types. The hook fires on every rod stop, so keep the callback cheap and avoid printing to console.
Example plugin: Grant a bonus item when a fishing cast ends without a catch
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Fishing Pity Bonus", "RustPluginGenerator", "1.0.0")]
[Description("Gives a small bonus item when a fishing cast ends without a catch.")]
public class FishingPityBonus : RustPlugin
{
private const string PermUse = "fishingpitybonus.use";
private PluginConfig config;
private readonly Dictionary<ulong, float> lastGrant = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Cooldown in seconds")]
public float CooldownSeconds = 60f;
[JsonProperty("Bonus items (shortname)")]
public List<string> Items = new List<string> { "scrap", "metal.fragments" };
[JsonProperty("Items per grant")]
public int Amount = 1;
}
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>
{
["Bonus"] = "Your cast failed, but you found {0} x{1}.",
["Cooldown"] = "You can receive a bonus again in {0} seconds."
}, 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 void OnFishingStopped(BaseFishingRod fishingRod, BaseFishingRod.FailReason reason)
{
if (fishingRod == null) return;
BasePlayer player = fishingRod.GetOwnerPlayer();
if (player == null || !player.IsConnected) return;
if (!permission.UserHasPermission(player.UserIDString, PermUse)) return;
ulong id = player.userID.Get();
float now = UnityEngine.Time.realtimeSinceStartup;
float last;
if (lastGrant.TryGetValue(id, out last) && now - last < config.CooldownSeconds) return;
if (config.Items == null || config.Items.Count == 0) return;
string shortname = config.Items[UnityEngine.Random.Range(0, config.Items.Count)];
Item item = ItemManager.CreateByName(shortname, config.Amount);
if (item == null) return;
// GiveItem returns void; it drops the item if the inventory is full.
player.GiveItem(item);
lastGrant[id] = now;
player.ChatMessage(Msg("Bonus", player.UserIDString, item.info.displayName.english, config.Amount));
}
private void Unload()
{
lastGrant.Clear();
}
}
}The plugin listens for OnFishingStopped, which fires when a fishing cast ends, and gives the fisher a small compensation item regardless of the fail reason. It reads the player from the rod with GetOwnerPlayer(), checks a per-player cooldown, and picks a random shortname from a configurable list. A permission (fishingpitybonus.use) limits who qualifies, and the granted item is announced through the language file. Cooldown, item list and amount are configurable.
Common use cases
Reward persistence
Give a small consumable or component each time a cast ends without a catch, keeping players engaged during long fishing sessions.
Cancel filtering
Ignore reason values that mean the player deliberately quit the cast so only genuine failed attempts trigger your logic.
Zone restrictions
Check the rod's world position against a fishing zone and apply penalties or messages when fish are stopped outside it.
Pitfalls
Owner may be null
If the player disconnected, GetOwnerPlayer() returns null. Always null-check before granting items or sending chat.
Fires often
The hook runs on every fishing session end across the server. Keep the callback light and do not log to console.
Reason enum values
Do not assume every stop is a failure; compare reason explicitly and document which values your plugin treats as rewards.
Build a plugin with OnFishingStopped
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 OnFishingStopped
Generate