Skip to content
RustPluginGenerator

OnFishCaught

OnFishCaught is called after a player successfully reels in a fish, at the end of BaseFishingRod.CatchProcessBudgeted, right before the catch routine is cancelled.

Signature
void OnFishCaught(ItemDefinition currentFishTarget, BaseFishingRod fishingRod, BasePlayer ownerPlayer)

Parameters

Hook parameters
ParameterTypeDescription
currentFishTargetItemDefinitionThe ItemDefinition of the fish that was just caught, e.g. the definition for `fish.troutsmall`. Use it to read shortname, itemid or displayName.
fishingRodBaseFishingRodThe BaseFishingRod entity the player was using. Useful for reading OwnerID, position and the rod's inventory when you need to grant rewards to the item.
ownerPlayerBasePlayerThe BasePlayer who reeled the fish in. May be null if the rod had no owner at the time the catch finished, so always null-check it.

Return value

The return value is ignored. The game method returns void and the original code path always continues to CancelInvoke(CatchProcess) regardless of what your hook returns, so OnFishCaught cannot block or alter a catch.

How it works

The hook fires once per successful catch, from BaseFishingRod.CatchProcessBudgeted. By the time it runs the game has already signalled the alt-attack animation, scheduled the line reset, killed the bobber and cleared the rod's current bobber reference, so the catch is effectively complete. currentFishTarget is the ItemDefinition of the fish that was awarded; fishingRod is the rod entity; ownerPlayer is the player who reeled it in. Because the hook return value is ignored, OnFishCaught is a notification only: you cannot cancel or swap the fish from here. Use it to react to the catch - award bonus items, count catches for a leaderboard, fire a chat message, trigger CUI or play an effect at the player's position. Declaring ownerPlayer as BasePlayer already matches the game's type; declaring a stricter subclass would make Oxide skip the call for other players, which is rarely what you want here. Guard against a null ownerPlayer and a destroyed rod, since the hook can run while the entity is being torn down.

Example plugin: Reward bonus fish and track catch stats

FishCatchRewards.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Fish Catch Rewards", "RustPluginGenerator", "1.0.0")]
    [Description("Gives bonus items on a fish catch and tracks per-player catch statistics.")]
    public class FishCatchRewards : RustPlugin
    {
        private const string PermBonus = "fishcatchrewards.bonus";

        private PluginConfig config;
        private StoredData data = new StoredData();
        private Timer saveTimer;

        private class PluginConfig
        {
            [JsonProperty("Bonus chance (0-1)")]
            public float BonusChance = 0.25f;

            [JsonProperty("Bonus item shortname")]
            public string BonusItem = "scrap";

            [JsonProperty("Bonus item amount")]
            public int BonusAmount = 5;

            [JsonProperty("Announce every N catches (0 = off)")]
            public int AnnounceEvery = 10;
        }

        private class StoredData
        {
            [JsonProperty("Catches by user id")]
            public Dictionary<string, int> Catches = 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>
            {
                ["Bonus"] = "You pulled in something extra: {0} x{1}",
                ["Milestone"] = "{0} has now caught {1} fish!",
                ["Stats"] = "You have caught {0} fish so far."
            }, 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(PermBonus, this);
            data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
            saveTimer = timer.Every(120f, SaveData);
        }

        private void Unload()
        {
            saveTimer?.Destroy();
            SaveData();
        }

        private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, data);

        private void OnFishCaught(ItemDefinition currentFishTarget, BaseFishingRod fishingRod, BasePlayer ownerPlayer)
        {
            if (ownerPlayer == null || currentFishTarget == null) return;

            string userId = ownerPlayer.UserIDString;
            int count;
            data.Catches.TryGetValue(userId, out count);
            count++;
            data.Catches[userId] = count;

            if (permission.UserHasPermission(userId, PermBonus) && UnityEngine.Random.Range(0f, 1f) < config.BonusChance)
            {
                var bonus = ItemManager.CreateByName(config.BonusItem, config.BonusAmount);
                if (bonus != null)
                {
                    ownerPlayer.GiveItem(bonus);
                    ownerPlayer.ChatMessage(Msg("Bonus", userId, bonus.info.displayName.english, bonus.amount));
                }
            }

            if (config.AnnounceEvery > 0 && count % config.AnnounceEvery == 0)
            {
                Server.Broadcast(Msg("Milestone", null, ownerPlayer.displayName, count));
            }
        }

        [ChatCommand("fishstats")]
        private void CmdFishStats(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            int count;
            data.Catches.TryGetValue(player.UserIDString, out count);
            player.ChatMessage(Msg("Stats", player.UserIDString, count));
        }
    }
}

The plugin listens on OnFishCaught and, after null-checking the player and the fish definition, gives a configurable chance to hand out a bonus item (default: an extra raw fish or scrap) through inventory.GiveItem. Every catch is tracked per player in an oxide/data file, and the plugin announces milestone catches like every tenth fish with a chat line driven from the language file. A permission (fishcatchrewards.bonus) gates the bonus drop, and all messages go through lang, so admins can translate them. Data is written when the plugin unloads and on a periodic timer, keeping disk writes off the hot path.

Common use cases

  • Bonus loot on catch

    Roll a configurable chance to grant scrap, bait or a rare item using ItemManager.CreateByName and player.inventory.GiveItem.

  • Fishing leaderboard

    Increment a per-player counter keyed by UserIDString, persist it to oxide/data, and expose the top fishers through a chat command.

  • Milestone announcements

    Track a player's catch streak and send a server-wide or private message when they reach a threshold you define in config.

Pitfalls

  • The hook cannot change the catch

    Because the return value is ignored, you cannot swap the fish, block it, or replace the reward here. Grant extra items instead of trying to replace currentFishTarget.

  • ownerPlayer can be null

    The rod may have no connected owner when the catch resolves, so guard against null before touching inventory, chat or permissions.

  • Keep the hook cheap

    It runs per catch, but every player fishing adds up; avoid heavy loops, world queries or disk writes inside the hook - defer persistence to a timer.

Plugin recipes that use OnFishCaught

Build a plugin with OnFishCaught

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Bonus loot on catch using the OnFishCaught hook, with a config file and a permission
Generate this plugin

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 OnFishCaught

Generate