📦 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
CraftingServiceCoreimplementation. 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:
ICraftingOutputRoutercomponent (optional)- A single router component assigned explicitly on
CraftingServiceCore(Inspector) - Receives:
- the effective default container
- the fully adjusted quantity for this add
-
Return values:
true→ router provides a decisioncontainerName == null / empty→ use default containercontainerName == "X"→ route toXfalse→ router vetoes; service falls back
-
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 ctxItemRef item— the item being addedint 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
ItemRefinstances 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
truewhen providing a decision — even if that decision is “use default.” - Return
falseonly 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.