Skip to content
RustPluginGenerator

OnExcavatorGather

OnExcavatorGather is called once per output item while a giant excavator arm produces resources, before the item is placed into an output pile.

Signature
object OnExcavatorGather(ExcavatorArm excavatorArm, Item item)

Parameters

Hook parameters
ParameterTypeDescription
excavatorArmExcavatorArmThe ExcavatorArm instance that is producing resources. Use it to read the arm's position or to identify which excavator is running.
itemItemThe freshly created output item (one pile's share) that the arm is about to place into an output pile. It has not been moved into a container yet at this point.

Return value

Returning a non-null value (for example false) stops the produce cycle for the current item and skips the remaining output piles for this pass, effectively cancelling the drop. Return null to let the item be placed into its output pile as normal. The original method returns void, so the return value only decides whether the code path continues.

How it works

The hook fires inside ExcavatorArm.ProduceResources, which runs on a timer while a player has the excavator arm set to mine a resource. For every output pile the arm creates a new item via ItemManager.Create (amount computed as the mined total divided across the active piles) and calls the hook with the arm and that item. Because the hook fires once per created item rather than once per mining cycle, a single cycle can produce several calls.

item is a fully formed but not yet stored item: read item.info.shortname and item.amount to inspect what is being produced. excavatorArm lets you identify the source; you can compare against a stored reference or read excavatorArm.transform.position.

There is no narrowing trick here beyond the declared types: ExcavatorArm and Item are the exact parameter types. The hook does not fire for quarries, pumpjacks or coaling towers; those have their own hooks.

Example plugin: Skip excavator output while no players are nearby

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

namespace Oxide.Plugins
{
    [Info("Excavator Idle Guard", "RustPluginGenerator", "1.0.0")]
    [Description("Stops giant excavator output while no players are near the arm")]
    public class ExcavatorIdleGuard : RustPlugin
    {
        private const string PermToggle = "excavatoridleguard.toggle";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Guard enabled")]
            public bool Enabled = true;

            [JsonProperty("Player detection radius (meters)")]
            public float Radius = 50f;
        }

        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>
            {
                ["NoPermission"] = "You do not have permission to use this command.",
                ["Toggled"] = "Excavator idle guard is now {0}.",
                ["Enabled"] = "enabled",
                ["Disabled"] = "disabled"
            }, 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(PermToggle, this);
        }

        [ChatCommand("excgather")]
        private void CmdExcGather(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermToggle))
            {
                player.ChatMessage(Msg("NoPermission", player.UserIDString));
                return;
            }

            config.Enabled = !config.Enabled;
            SaveConfig();
            player.ChatMessage(Msg("Toggled", player.UserIDString, Msg(config.Enabled ? "Enabled" : "Disabled", player.UserIDString)));
        }

        private object OnExcavatorGather(ExcavatorArm excavatorArm, Item item)
        {
            if (!config.Enabled || excavatorArm == null || item == null) return null;

            Vector3 origin = excavatorArm.transform.position;
            float radiusSqr = config.Radius * config.Radius;

            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                if (player == null || player.IsNpc || !player.IsConnected) continue;
                if ((player.transform.position - origin).sqrMagnitude <= radiusSqr)
                {
                    return null;
                }
            }

            return false;
        }
    }
}

The plugin keeps an excavator from filling its output piles while nobody is around to collect the ore. On each OnExcavatorGather call it checks the configurable radius around the arm for any connected player. If none is found, it returns false, which makes the game skip placing the item and abandon the rest of the current cycle. When a player is nearby it returns null so mining behaves normally. Admins can toggle the guard with /excgather if they have the excavatoridleguard.toggle permission; the state is stored in the config so it survives restarts.

Common use cases

  • Idle excavator protection

    Stop output when no player is within a set distance of the arm, so piles do not overflow on low-population servers.

  • Custom resource filtering

    Return false for specific shortnames from the config so an excavator never produces a resource you consider unwanted.

  • Mining statistics

    Read item.info.shortname and item.amount, return null, and record totals per shortname for a leaderboard or economy payout.

Pitfalls

  • Cancelling skips the whole loop

    The original code returns out of ProduceResources after a non-null result, so remaining output piles in that pass get nothing. Cancel deliberately, not per-pile.

  • Called for every pile

    A cycle creates one item per output pile, so your hook runs multiple times per cycle. Do not do heavy scans (like looping all players) unless the result is cached.

  • The item is not yet in a container

    item.parent is null at hook time and item.GetOwnerPlayer() returns null. Use the item data only; anything relying on container ownership will not work here.

Plugin recipes that use OnExcavatorGather

Build a plugin with OnExcavatorGather

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

Prompt: Idle excavator protection using the OnExcavatorGather 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 OnExcavatorGather

Generate