Skip to content
RustPluginGenerator

OnItemCraftFinished

OnItemCraftFinished fires on the server after a single craft task has produced its item and handed it to the crafter, inside ItemCrafter.FinishCrafting. It runs once per completed task, not once per queued item.

Signature
void OnItemCraftFinished(ItemCraftTask task, Item item, ItemCrafter itemCrafter)

Parameters

Hook parameters
ParameterTypeDescription
taskItemCraftTaskThe ItemCraftTask that just completed, holding the blueprint (task.blueprint), the owning entity and the requested amount. The crafted item has already been placed in the crafter's inventory at this point.
itemItemThe finished Item that was produced and handed to the crafter. Its parent is the container it landed in, and it is the item you reward, modify or inspect here.
itemCrafterItemCrafterThe ItemCrafter component that ran the craft, usually the player or the BaseEntity owner. Use itemCrafter.baseEntity to reach the crafting player and its queue.

Return value

The return value is discarded: the original FinishCrafting method returns void and continues normally regardless of what your hook returns. Nothing you return can change the crafted item or cancel the task here.

How it works

The hook is called from ItemCrafter.FinishCrafting, after the task's taken ingredients are trimmed and after the game tells the client the craft completed (note.craft_done). At that moment the produced item already exists and belongs to the crafter, and task.instanceData is applied right after the hook returns.

Use task to read task.blueprint (what was crafted), task.amount (requested quantity) and the task owner. Use item to inspect or adjust the actual crafted stack: skin, amount, condition, or its container (item.parent). Use itemCrafter.baseEntity to get the BasePlayer who crafted so you can message, reward or permission-check them.

Because itemCrafter.baseEntity and task are always present here, narrowing parameters rarely helps; declaring narrow types on this hook does not filter crafts meaningfully since the same types are always passed. Null-check item and item.parent regardless, since rare code paths can leave the parent unset.

The return value is ignored by the game, so this hook is observation and side-effect only. It does not let you cancel the craft; use OnItemCraft or CanCraft for that.

Example plugin: Announce and reward rare crafted items

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

namespace Oxide.Plugins
{
    [Info("Craft Rewards", "RustPluginGenerator", "1.0.0")]
    [Description("Announces and rewards players when configured items are crafted")]
    public class CraftRewards : RustPlugin
    {
        private const string PermReward = "craftrewards.reward";

        private PluginConfig config;

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

            [JsonProperty("Reward amount (scrap)")]
            public int RewardAmount = 10;

            [JsonProperty("Announce to all players")]
            public bool AnnounceToAll = true;

            [JsonProperty("Watched item shortnames")]
            public List<string> WatchedItems = new List<string>
            {
                "explosive.timed",
                "ammo.rifle",
                "rifle.ak"
            };
        }

        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>
            {
                ["Announce"] = "{0} just crafted {1}!",
                ["Reward"] = "You crafted {0} and received {1} scrap.",
                ["NoPerm"] = "You are not allowed to receive craft rewards."
            }, this);
        }

        private string Msg(string key, string userId = null, params object[] args)
        {
            return string.Format(lang.GetMessage(key, this, userId), args);
        }

        private void Init()
        {
            permission.RegisterPermission(PermReward, this);
        }

        private void OnItemCraftFinished(ItemCraftTask task, Item item, ItemCrafter itemCrafter)
        {
            if (task == null || item == null || itemCrafter == null || task.blueprint == null) return;
            if (!config.WatchedItems.Contains(task.blueprint.targetItem.shortname)) return;

            var player = itemCrafter.baseEntity as BasePlayer;
            if (player == null || !player.IsConnected) return;

            var displayName = item.info != null ? item.info.displayName.english : "item";

            if (config.AnnounceToAll)
                Server.Broadcast(Msg("Announce", null, player.displayName, displayName));
            else
                SendReply(player, Msg("Announce", player.UserIDString, player.displayName, displayName));

            if (!config.RewardEnabled) return;
            if (!permission.UserHasPermission(player.UserIDString, PermReward))
            {
                SendReply(player, Msg("NoPerm", player.UserIDString));
                return;
            }

            var scrap = ItemManager.CreateByName("scrap", config.RewardAmount);
            if (scrap == null) return;
            player.GiveItem(scrap);
            SendReply(player, Msg("Reward", player.UserIDString, displayName, config.RewardAmount));
        }
    }
}

The plugin tracks a configurable list of item shortnames that count as rare crafts. When OnItemCraftFinished fires, it resolves the crafting player through itemCrafter.baseEntity, checks task.blueprint against the watched shortnames and confirms the player still has permission. If matched, it sends the configurable chat announcement and optionally grants a scrap bonus via GiveItem. Messages go through the language file, the reward amount lives in the config, and every parameter is null-checked before use, so a craft from a dropped or unusual crafter cannot throw.

Common use cases

  • Announce rare or expensive crafts

    Broadcast to the server or notify the crafter when they finish an item on a watch list, such as explosives or high-tier weapons.

  • Grant craft-based rewards

    Hand out scrap, bonus items or currency-plugin balance when a specific blueprint finishes, optionally gated by permission.

  • Apply per-item tweaks after crafting

    Adjust condition or skin on the freshly produced item, since it already exists in the crafter's inventory at this point.

Pitfalls

  • It fires per task, not per item

    A stack craft still triggers the hook once per completed task; if you want per-item rewards multiply by the produced amount rather than assuming one.

  • Removing the item in the hook

    The crafted item is already in the player's inventory and task.instanceData is applied right after. Removing or moving it can lead to unexpected state; prefer modifying it in place.

  • Crafter may not be a player

    itemCrafter.baseEntity is not guaranteed to be a connected BasePlayer. Null-check it before calling ChatMessage or permission checks.

Plugin recipes that use OnItemCraftFinished

Build a plugin with OnItemCraftFinished

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

Prompt: Announce rare or expensive crafts using the OnItemCraftFinished 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 OnItemCraftFinished

Generate