CanCraft
CanCraft is called when the game validates whether a craft can be queued on an ItemCrafter, and also from PlayerBlueprints when checking blueprint availability. Returning a bool overrides the game's own answer.
object CanCraft(ItemCrafter itemCrafter, ItemBlueprint bp, int amount, bool free)Parameters
| Parameter | Type | Description |
|---|---|---|
itemCrafter | ItemCrafter | The ItemCrafter component that owns the craft queue, usually the player's own crafter. Null on no realistic path, but check it before touching the player. |
bp | ItemBlueprint | The ItemBlueprint being crafted; bp.targetItem is the resulting ItemDefinition (shortname, itemid, craftingStackable). |
amount | int | How many items the player is trying to queue or craft in this call. |
free | bool | True when the craft is being issued without consuming ingredients (admin or server-initiated crafts). |
Return value
Return false to deny the craft and skip the game's own validation for this call. Return true to allow it and bypass the remaining checks for this call, including the stack limit and ingredient validation, so be careful with true. Return null to run vanilla logic unchanged. Any return value other than a bool is ignored.
How it works
The hook fires inside ItemCrafter.CanCraft, after the crafter has passed some early guards (including the queue limit check) and before the game validates amount against bp.targetItem.craftingStackable and ingredient availability. It also fires from PlayerBlueprints.CanCraft with the signature (PlayerBlueprints, ItemDefinition, int) when the blueprint system checks whether an item can be crafted at all.
Use bp.targetItem.shortname or bp.targetItem.itemid to identify the recipe, amount to limit queue size per craft, and free to let admin or scripted crafts bypass a restriction. Get the player from itemCrafter via itemCrafter.GetComponent<BasePlayer>() or the owner reference you keep, and null-check it.
Declaring ItemBlueprint matches both sites where the argument is a blueprint; the PlayerBlueprints overload passes an ItemDefinition instead, so those calls are skipped by your (ItemCrafter, ItemBlueprint, int, bool) method. Return null to let vanilla rules decide.
Other signatures
Oxide also calls CanCraft from other places in the game with these parameter types. Declare the overload you need, or several:
object CanCraft(PlayerBlueprints playerBlueprints, ItemDefinition itemDefinition, int skinItemId)
Example plugin: Restrict expensive crafts behind a permission
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("CraftPermissionGate", "RustPluginGenerator", "1.0.0")]
[Description("Blocks crafting of configured items for players without the required permission")]
public class CraftPermissionGate : RustPlugin
{
private const string PermGate = "craftpermissiongate.gate";
private PluginConfig config;
private readonly Dictionary<ulong, float> lastNotice = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Required permission")]
public string Permission = PermGate;
[JsonProperty("Gated item shortnames")]
public List<string> GatedItems = new List<string> { "ammo.rocket.basic", "explosive.timed", "explosive.satchel" };
[JsonProperty("Notice cooldown seconds")]
public float NoticeCooldown = 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"] = "You do not have permission to craft {0}."
}, 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(config.Permission, this);
}
private object CanCraft(ItemCrafter itemCrafter, ItemBlueprint bp, int amount, bool free)
{
if (itemCrafter == null || bp == null || bp.targetItem == null || free) return null;
string shortname = bp.targetItem.shortname;
if (string.IsNullOrEmpty(shortname) || !config.GatedItems.Contains(shortname)) return null;
BasePlayer player = itemCrafter.GetComponent<BasePlayer>();
if (player == null || player.IsNpc) return null;
if (permission.UserHasPermission(player.UserIDString, config.Permission)) return null;
float now = UnityEngine.Time.realtimeSinceStartup;
float last;
if (!lastNotice.TryGetValue(player.userID, out last) || now - last >= config.NoticeCooldown)
{
lastNotice[player.userID] = now;
player.ChatMessage(Msg("Blocked", player.UserIDString, bp.targetItem.displayName.english));
}
return false;
}
private void Unload()
{
lastNotice.Clear();
}
}
}The config lists item shortnames that require a permission. CanCraft looks up the blueprint's target item and, if it is gated and the craft is not free, checks the owning player's permission. Players without the permission get a throttled chat reply and the hook returns false, blocking the craft. Timer cleanup in Unload and a simple per-player timestamp dictionary keep chat spam down. The permission prefix is the lowercase class name, and the config falls back to defaults if the file is corrupt.
Common use cases
Permission-gate expensive recipes
Deny crafts like rockets or C4 for players without a rank permission and tell them why.
Cap craft batch size
Return false when amount exceeds a configured limit to stop players queuing huge batches in one action.
Event-only crafting
Allow restricted items only while a flag is set or a player is whitelisted, returning true to bypass vanilla ingredient checks during events.
Pitfalls
Returning true skips vanilla checks
Returning true from the ItemCrafter call lets the craft proceed past stack-limit and ingredient validation, so only do it for server-initiated or admin cases.
The overload has a different signature
PlayerBlueprints.CanCraft passes an ItemDefinition, not an ItemBlueprint, so your method must match one signature. Catching blueprint calls requires the ItemBlueprint parameter.
Fires per queue action
The hook runs each time a craft is validated, including repeated calls while queueing. Avoid logging or heavy work inside it; cache results if needed.
Build a plugin with CanCraft
Describe what should happen and get a compiled plugin that uses this hook.
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 CanCraft
Generate