Skip to content
RustPluginGenerator

OnDispenserGather

OnDispenserGather is called from ResourceDispenser.GiveResourceFromItem right after the gathered Item is created and before it is applied to the player.

Signature
object OnDispenserGather(ResourceDispenser resourceDispenser, BasePlayer entity, Item item)

Parameters

Hook parameters
ParameterTypeDescription
resourceDispenserResourceDispenserThe ResourceDispenser entity that produced the resource, usually a tree, ore node or corpse with a dispenser component. Use it to read transform.position or net.ID for per-node logic.
entityBasePlayerThe BasePlayer who hit the dispenser. Null-check it and its connection before sending UI or chat; NPCs can also trigger gather logic in some setups.
itemItemThe freshly created Item that is about to be given to the player, already scaled by gather rate and bonus. Changing item.amount or item.skin here affects what the player receives.

Return value

Returning null lets the original code continue: the item is given ownership, analytics fire and OnDispenserGathered is invoked. Returning any non-null object stops that path, so the item is never applied to the player and no OnDispenserGathered follows.

How it works

The hook fires inside ResourceDispenser.GiveResourceFromItem, the method that turns one hit on a tree, ore node or similar dispenser into an item. By the time it runs, the gather amount has already been calculated from the tool, the node's health fraction and gather bonuses, and an Item has been instantiated but not yet handed to the player.

The resourceDispenser parameter identifies the node, useful for per-prefab rules such as different multipliers for stones versus sulfur nodes. entity is the gathering player, and item is the item instance about to be awarded. Adjusting item.amount, item.skin or even swapping item.info-driven values lets you shape the reward without touching the tool or the node.

Declaring entity as a more specific type than BasePlayer narrows dispatch: Oxide only calls your method when the argument matches the declared type, so a too-specific parameter silently skips calls. Returning a non-null value stops the original code path, so the item is never applied to the player and no OnDispenserGathered call follows. That makes the return a veto, not a multiplier.

Example plugin: Boost ore gather amounts by node type

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

namespace Oxide.Plugins
{
    [Info("Dispenser Gather Boost", "RustPluginGenerator", "1.0.0")]
    [Description("Scales the amount of resources awarded from designated dispenser nodes.")]
    public class DispenserGatherBoost : RustPlugin
    {
        private const string PermUse = "dispensergatherboost.use";

        private PluginConfig config;
        private readonly Dictionary<ulong, float> lastNotice = new Dictionary<ulong, float>();

        private class PluginConfig
        {
            [JsonProperty("Gather multiplier by prefab name")]
            public Dictionary<string, float> Multipliers = new Dictionary<string, float>
            {
                ["ore_sulfur"] = 1.5f,
                ["ore_metal"] = 1.25f,
                ["ore_stone"] = 1.25f
            };

            [JsonProperty("Notify player on boosted gather")]
            public bool NotifyPlayer = true;

            [JsonProperty("Notice cooldown seconds")]
            public float NoticeCooldown = 15f;
        }

        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>
            {
                ["Boosted"] = "You gathered {0}x {1} (boosted)."
            }, 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 OnDispenserGather(ResourceDispenser resourceDispenser, BasePlayer entity, Item item)
        {
            if (resourceDispenser == null || entity == null || item == null) return null;
            if (!permission.UserHasPermission(entity.UserIDString, PermUse)) return null;

            BaseEntity baseEntity = resourceDispenser.GetComponent<BaseEntity>();
            string prefab = baseEntity != null ? baseEntity.ShortPrefabName : null;
            if (string.IsNullOrEmpty(prefab) || !config.Multipliers.TryGetValue(prefab, out float multiplier)) return null;
            if (multiplier <= 1f) return null;

            int baseAmount = item.amount;
            int boosted = (int)Math.Ceiling(baseAmount * multiplier);
            if (boosted <= baseAmount) return null;

            item.amount = boosted;

            if (config.NotifyPlayer && entity.IsConnected)
                NotifyIfReady(entity, boosted - baseAmount, item.info.displayName.english);

            return null;
        }

        private void NotifyIfReady(BasePlayer player, int extra, string resourceName)
        {
            float now = UnityEngine.Time.realtimeSinceStartup;
            ulong userId = player.userID;
            if (lastNotice.TryGetValue(userId, out float last) && now - last < config.NoticeCooldown) return;
            lastNotice[userId] = now;
            player.ChatMessage(Msg("Boosted", player.UserIDString, extra, resourceName));
        }

        private void Unload()
        {
            lastNotice.Clear();
        }
    }
}

The plugin defines a config with a multiplier per short prefab name, a per-player notification toggle and a notice cooldown. When OnDispenserGather fires it reads the node's ShortPrefabName, checks the player has the dispensergatherboost.use permission, looks the prefab up in the configured dictionary and scales item.amount (rounded up) before the item is applied. Unknown prefabs, missing permission, or a multiplier of 1 or less return immediately, keeping default behavior. If notifications are enabled and the player is connected, a short chat notice showing the extra yield is sent, throttled per player by the configured cooldown so the chat is not flooded on every swing. Returning null keeps the normal award path intact. Changing the config and reloading the plugin is enough to retune the numbers.

Common use cases

  • Per-node gather multipliers

    Scale item.amount for specific node prefabs to make sulfur or stone farming easier on a PvE server while leaving trees untouched.

  • Event-only bonus resources

    During a timed event, replace the gathered item with a rarer resource by manipulating item.amount and the surrounding reward logic in your own code.

  • Veto gathering for jailed players

    Return a non-null value when the player is in a restricted zone or jail, blocking the item from being awarded while the node still takes damage.

Pitfalls

  • Non-null return blocks the whole award

    Returning a value stops the award path entirely, including ownership and the OnDispenserGathered hook. Use it only when you want to suppress the gather, not to change it.

  • item may be null in edge cases

    Always null-check item and entity before touching their members; NPC or scripted dispenser calls can pass unexpected values.

  • Fires on every swing

    The hook runs for each gather tick, so avoid logging, Puts or chat spam here. Throttle player notices or skip them entirely.

Plugin recipes that use OnDispenserGather

Build a plugin with OnDispenserGather

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

Prompt: Per-node gather multipliers using the OnDispenserGather 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 OnDispenserGather

Generate