🛠️ Crafting System¶
The Crafting system is a scene-scoped, data-driven job service that performs preflight checks, accept-time consumption, queued job execution, delivery, persistence, modifiers, validators, routing, and offline progress — all through a clean, result-based API.
✔ Deterministic when seeded (subject to call order and batching rules) ✔ Adapter-driven (Inventory + Currency) ✔ Validators, modifiers, cooldowns, and authority gates ✔ Full offline progress + save/restore ✔ Suitable for survival, RPG, idle, and base-builder loops
If something is documented here, it reflects actual runtime behaviour as implemented by
CraftingService.
📦 Folder Overview¶
| Concept | Description |
|---|---|
| CraftingService | Orchestrates preflight → accept → queue → run → deliver → lifecycle → offline restore. |
| RecipeCore / RecipeDefinition | Pure runtime recipe vs Unity-authored designer asset (optional). |
| Adapters | Connect Crafting to Inventory, Currency, and routing backends. |
| Validators | Gate or block crafts (cooldowns, level gates, global rules). |
| Modifiers | Adjust duration, outputs, cost, and bonus chances. |
| Jobs | Accepted crafting tasks with lifecycle events and persistence. |
Lifecycle signals are exposed via:
OnJobAccepted, OnJobStarted, OnJobProgress, OnJobCompleted, OnJobFailed, OnJobCancelled.
Persistence is handled with:
SaveActiveJobs() and RestoreJobs().
🧠 Usage Guidance¶
Add the Service¶
Add CraftingService to a scene.
The service is scene-scoped and manages all crafting jobs within that scene.
Bind Adapters¶
- Inventory adapter → required
- Currency adapter → optional (crafts are free if missing)
Built-in adapters may auto-resolve if present; custom projects can supply their own implementations.
Define a Recipe¶
Use either:
RecipeCore— pure runtime, dependency-free assetRecipeDefinition— Unity-authored convenience asset (optional, requiresREV_INVENTORY_PRESENT)
Always resolve recipes via RecipeResolve.
Preflight & Enqueue¶
var svc = FindFirstObjectByType<CraftingService>();
var check = svc.CanCraftDetailed(player, recipe, requested: 1);
if (check.maxCrafts > 0)
{
var req = new CraftRequest(player, recipe, 1, "Backpack", "Forge");
var job = svc.Enqueue(req);
}
else
{
Debug.LogWarning($"Cannot craft: {check.reason}");
}
Subscribe to Lifecycle Events¶
svc.OnJobAccepted += j => Debug.Log($"Accepted {j.recipe.name}");
svc.OnJobCompleted += j => Debug.Log($"Completed {j.recipe.name}");
svc.OnJobFailed += (j,r) => Debug.LogWarning($"Failed: {r}");
🧩 What Lives Here¶
CraftingService¶
The orchestration root. It:
- Runs deterministic preflight
- Consumes inputs and currency at accept-time
- Schedules jobs with global and per-station concurrency
- Re-applies modifiers at delivery
- Handles refunds, failures, and lifecycle events
- Restores and completes jobs offline using wall-clock time
Recipes¶
- RecipeCore — authoritative runtime data
- RecipeDefinition — Unity-only authoring helper (optional)
- Conversion and resolution are performed via
RecipeResolve
Adapters¶
Adapters form the integration boundary:
- Inventory → counts, space checks, consume/add
- Currency → balance checks, debit, refund
- Output routing → destination container resolution
Crafting Core does not depend on any concrete backend.
Validators¶
Validators run after core preflight (subject to service short-circuit rules) and may:
- Block crafting entirely
- Replace the reported failure reason
- Apply global or contextual policy checks
Validators do not bypass ingredient, currency, or space preflight failures.
Modifiers¶
Modifiers adjust runtime behaviour:
- Duration
- Output multipliers
- Currency multipliers
- Extra and chance-based outputs
They are evaluated multiple times per craft:
- during preflight/accept
- again at delivery
Jobs¶
Jobs represent accepted crafts and expose:
- Owner and recipe
- Duration and progress
- State transitions
- Persistence snapshots
- Lifecycle callbacks
🧪 Diagnostics¶
Preflight¶
var check = svc.CanCraftDetailed(player, recipe, requested: 10);
Debug.Log($"Max crafts: {check.maxCrafts} — Reason: {check.reason}");
Probe¶
var probe = svc.Probe(player, recipe, requested: 10);
Debug.Log($"Items:{probe.byItems} Currency:{probe.byCurrency} Space:{probe.bySpace}");
Preflight Order¶
- Inputs
- Currency
- Space (routing + adjustments)
- Validators (if reached)
- Final clamp
🎯 Purpose¶
The Crafting system provides a structured way to:
- validate crafting requests before execution
- execute crafting over time using jobs
- integrate with inventory and currency through adapters
- support offline progress and persistence
It is designed for systems that require queued, time-based, or policy-driven crafting flows.
⚠️ Important Notes¶
- Crafting is adapter-driven and backend-agnostic
- Offline progress depends on
IWallClockProvider - Validators and modifiers apply in both live and offline flows
- Determinism depends on RNG seeding and call order
- Batch approximation may affect deterministic behaviour if enabled
🚫 Not for Production Use¶
- Teachables and demo panels in this module are for learning and validation only
- They are not part of the runtime crafting pipeline
🔗 Related Documentation¶
- Inventory (for item storage and consumption)
- Currency (for cost handling and refunds)
- Integrations (for adapter implementations)
🧾 Jobs, Persistence & Offline Progress¶
- Active jobs serialize via
SaveActiveJobs() - Restoration uses
RestoreJobs() - Offline progress is computed using
acceptedAtUtcandIWallClockProvider - Delivery logic, routing, modifiers, and validators apply during offline completion
Paused jobs do not progress offline.
🧩 Advanced: Immediate Escrow Crafting¶
CraftingService exposes an optional escrow path for atomic, immediate crafts via
TryCraftImmediateEscrow.
Characteristics:
- Immediate only (zero-duration crafts)
- No
CraftJobis created - No job lifecycle events are emitted
- Inputs, currency, and outputs are reserved and committed atomically
- Uses a single inventory container (no output routing)
- Requires reservation-capable adapters
This path is intended for advanced use cases (e.g. transactional or server-authoritative flows).
For time-based or offline crafting, use the standard job pipeline.
🧾 Final Notes¶
- Nothing in Core depends on Unity UI, Editor tooling, or teaching panels
- Integration behaviour depends on installed adapters
- Optional systems (Inventory, Currency) extend behaviour but are not required