Skip to content
RustPluginGenerator

OnIngredientsCollect

OnIngredientsCollect is called by ItemCrafter.CollectIngredients right before the game builds the list of items to consume for a craft. Returning a non-null value stops the collection, so the craft fails to start.

Signature
object OnIngredientsCollect(ItemCrafter itemCrafter, ItemBlueprint bp, ItemCraftTask task, int amount, BasePlayer player, bool takeBroken)

Parameters

Hook parameters
ParameterTypeDescription
itemCrafterItemCrafterThe ItemCrafter component that is gathering ingredients, normally the player's own crafter (player.inventory.crafting). Use it to inspect the craft queue or the owner.
bpItemBlueprintThe ItemBlueprint being crafted. Use bp.targetItem / bp.GetIngredients() to reason about what the craft needs.
taskItemCraftTaskThe ItemCraftTask currently being collected for. Holds task.owner, task.skinID, task.blueprint and progress data; null-safe access is required.
amountintNumber of craft sets whose ingredients are being collected in this pass. Multiply ingredient amounts by this to know the true cost.
playerBasePlayerThe player the ingredients are collected for. Can be null when the crafter belongs to a non-player entity or when the game calls it without an owner.
takeBrokenboolWhether broken items in the player's inventory may be consumed as ingredients. Matches the game's own parameter for the collection pass.

Return value

Return null (or nothing) and the game continues building the ingredient list and consuming items as normal. Return any non-null object and CollectIngredients returns immediately: no ingredients are taken from the inventory and the craft cannot proceed for this task.

How it works

The hook fires inside ItemCrafter.CollectIngredients, the method that walks the blueprint's ingredient list and moves matching items out of the crafter owner's inventory. The game calls it with the crafter, the blueprint, the active ItemCraftTask, the amount of craft sets being collected, the owning player and the takeBroken flag, then builds its own List<Item> of ingredients.

Because the call happens before the ingredient list is built, this is the only place where you can veto a craft based on what the player is actually about to spend. bp.GetIngredients() gives the ItemAmount list and amount multiplies each entry, so a cost check should use ingredient.amount * amount. player is often the person queuing the craft, but treat it as nullable - the crafter does not always have a player owner.

Declaring player as BasePlayer (as in the signature) already excludes calls with a different owner type, since Oxide only invokes your method when every argument matches the declared parameter type. Declaring a narrower type for the other parameters is not useful here; they are always the exact game types.

The hook does not run per ingredient, only once per collection pass, so it is cheap enough to do dictionary lookups and inventory reads.

Example plugin: Block crafting items that need banned components

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

namespace Oxide.Plugins
{
    [Info("Craft Ingredient Guard", "RustPluginGenerator", "1.0.0")]
    [Description("Blocks crafts that would consume a configured list of restricted ingredient items")]
    public class CraftIngredientGuard : RustPlugin
    {
        private const string PermBypass = "craftingredientguard.bypass";

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

        private class PluginConfig
        {
            [JsonProperty("Restricted ingredient shortnames")]
            public List<string> RestrictedShortnames = new List<string> { "scrap" };

            [JsonProperty("Warn player when blocked")]
            public bool WarnPlayer = true;

            [JsonProperty("Message cooldown in seconds")]
            public float MessageCooldown = 5f;
        }

        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>
            {
                ["Blocked"] = "This craft is blocked: it would consume {0}.",
                ["BlockedGeneric"] = "This craft is blocked because of a restricted ingredient."
            }, 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(PermBypass, this);
        }

        private object OnIngredientsCollect(ItemCrafter itemCrafter, ItemBlueprint bp, ItemCraftTask task, int amount, BasePlayer player, bool takeBroken)
        {
            if (bp == null || config.RestrictedShortnames == null || config.RestrictedShortnames.Count == 0) return null;

            BasePlayer owner = player;
            if (owner == null && itemCrafter != null) owner = itemCrafter.owner;
            if (owner != null && permission.UserHasPermission(owner.UserIDString, PermBypass)) return null;

            if (amount < 1) amount = 1;

            string blocked = null;
            List<ItemAmount> ingredients = bp.GetIngredients();
            if (ingredients == null) return null;

            foreach (ItemAmount ingredient in ingredients)
            {
                if (ingredient?.itemDef == null) continue;
                if (!config.RestrictedShortnames.Contains(ingredient.itemDef.shortname)) continue;
                blocked = ingredient.itemDef.displayName.english;
                break;
            }

            if (blocked == null) return null;

            if (owner != null && config.WarnPlayer) NotifyBlocked(owner, blocked);
            return true;
        }

        private void NotifyBlocked(BasePlayer player, string ingredientName)
        {
            float now = UnityEngine.Time.realtimeSinceStartup;
            float last;
            if (lastMessage.TryGetValue(player.userID, out last) && now - last < config.MessageCooldown) return;
            lastMessage[player.userID] = now;

            player.ChatMessage(Msg("Blocked", player.UserIDString, ingredientName ?? Msg("BlockedGeneric", player.UserIDString)));
        }

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

The plugin keeps a configurable list of item shortnames that are considered restricted components. In OnIngredientsCollect it resolves the acting player (falling back to itemCrafter.owner) and checks the blueprint's ingredient shortnames against the restricted list. If any restricted item would be spent, the collection is stopped by returning true and the player receives a language message naming the blocked component, with a cooldown so repeated craft clicks do not spam chat. If the cost is clean the hook returns null, so the game takes the ingredients and the craft starts normally. Admins bypass the check via the craftingredientguard.bypass permission.

Common use cases

  • Restricted component economy

    Stop crafts that would consume a scarce resource such as scrap or a custom currency item, forcing players to use a different recipe path or a server command.

  • Craft cost auditing

    Log which players attempt expensive crafts and how many ingredient sets they try to collect, using data files rather than chat output.

  • Event-only crafting

    During a special event, block crafts whose ingredients include a normally cheap item so only the event recipe chain remains available.

Pitfalls

  • Forgetting the amount multiplier

    Ingredient amounts on the blueprint are per single craft. If you compare them without multiplying by amount, players crafting in bulk pass your check.

  • Assuming player is never null

    The crafter can be driven without a player owner. Null-check player and fall back to task?.owner before using permissions or sending messages.

  • Expecting a refund

    When you return non-null no ingredients have been taken yet, so there is nothing to refund. Returning non-null also does not cancel the task queue entry itself; the player may simply see nothing happen.

Build a plugin with OnIngredientsCollect

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

Prompt: Restricted component economy using the OnIngredientsCollect 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 OnIngredientsCollect

Generate