Skip to content

📦 Crafting — Routing

The Routing folder contains runtime implementations of ICraftingOutputRouter. Routers decide where each crafted output is delivered by resolving a destination container name per output add.

Routing is consulted during space preflight and delivery, so the same routing rules apply consistently across:

  • normal crafts
  • instant crafts (duration == 0)
  • delayed crafts (duration > 0)
  • offline completion / restore

Truth-pass note: This README documents behaviour enforced by the current CraftingServiceCore implementation. Where behaviour is conditional, that dependency is stated explicitly.


🎯 Purpose

By default, crafted outputs are delivered to the default logical container.

Routers allow you to override this destination on a per-output basis to support:

  • category-specific containers ("Bank", "Materials", "QuestRewards")
  • station- or recipe-aware routing
  • per-item destination rules
  • server-authoritative inventory layouts

Routing is invoked:

  • During preflight — for space checks (binary search; no RNG rolls)
  • During delivery — immediate, delayed, or offline

Routers do not modify quantities or timing — only the destination container.


🔧 Resolution order (exact runtime behaviour)

For each output add, the service resolves the destination container using this order:

  1. ICraftingOutputRouter component (optional)
  2. A single router component assigned explicitly on CraftingServiceCore (Inspector)
  3. Receives:
    • the effective default container
    • the fully adjusted quantity for this add
  4. Return values:

    • true → router provides a decision
    • containerName == null / empty → use default container
    • containerName == "X" → route to X
    • false → router vetoes; service falls back
  5. Fallback → default logical container

Routers are not auto-discovered.
A router must be explicitly assigned on the Crafting Service via the Inspector.


🧩 What routing receives

Each routing call includes:

  • CraftContext ctx
  • ItemRef item — the item being added
  • int quantity — the fully adjusted quantity, including:
  • recipe output quantity
  • output multiplier
  • batch multiplier (when applicable)
  • ctx.container — the default destination container for this craft evaluation (before routing is applied)

Routers are invoked for:

  • base recipe outputs
  • deterministic extra outputs (from modifiers)
  • chance outputs (actual delivery rolls)
  • synthetic ItemRef instances during space preflight (used only for space budgeting)

Because routing is evaluated both during preflight and delivery, routers must resolve containers deterministically for a given context.


⚙️ Included routers

OutputToContainerRouter

[DisallowMultipleComponent]
[AddComponentMenu("RevFramework/Crafting/Routers/Output To Container Router")]
public sealed class OutputToContainerRouter : MonoBehaviour, ICraftingOutputRouter
{
    [SerializeField] private string containerName;

    public bool TryResolveContainer(ref CraftContext ctx, in ItemRef item, int quantity, out string target)
    {
        target = string.IsNullOrWhiteSpace(containerName) ? null : containerName.Trim();
        return true;
    }
}

Behaviour:

  • Routes all outputs to a single configured container
  • Empty / whitespace value → use the default logical container
  • Never vetoes routing
  • Allocation-free at runtime

This router is intended as a simple baseline example.


🧩 Writing your own router

Implement:

public interface ICraftingOutputRouter
{
    bool TryResolveContainer(ref CraftContext ctx, in ItemRef item, int quantity, out string containerName);
}

Example — rare items to bank

[DisallowMultipleComponent]
public sealed class RareToBankRouter : MonoBehaviour, ICraftingOutputRouter
{
    [SerializeField] private string bankContainer = "Bank";

    public bool TryResolveContainer(ref CraftContext ctx, in ItemRef item, int quantity, out string containerName)
    {
        if (!string.IsNullOrEmpty(item.guid) &&
            item.guid.StartsWith("rare_", StringComparison.OrdinalIgnoreCase))
        {
            containerName = bankContainer.Trim();
            return true;
        }

        containerName = null; // use default container
        return true;
    }
}

💡 Routing guidelines

  • Trim and normalise container names at authoring time.
  • Return true when providing a decision — even if that decision is “use default.”
  • Return false only to veto this router and allow fallback.
  • Keep routing logic lightweight — routing is called per output add during both preflight and delivery.
  • Routers should never mutate quantities, state, or context.

⚠️ Escrow / strong craft path

The escrow (reserve/commit) craft path intentionally bypasses routing and uses a single inventory context to guarantee atomicity.

Routing applies only to the standard job-based crafting pipeline.


📁 Folder contents

File Description
OutputToContainerRouter.cs Routes all outputs to a fixed container
(custom routers) User-defined ICraftingOutputRouter implementations

Routers provide the final decision point for output destinations in Crafting. They allow precise control over where items go while keeping the Core runtime clean, deterministic, and inventory-agnostic.