🛠️ 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
CraftingServiceCore.
📦 Overview¶
| Concept | Description |
|---|---|
| CraftingServiceCore | 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().
🚀 Quickstart¶
1️⃣ Add the Service¶
Add CraftingServiceCore to a scene.
The service is scene‑scoped and manages all crafting jobs within that scene.
2️⃣ 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.
3️⃣ Define a Recipe¶
Use either:
RecipeCore— pure runtime, dependency‑free assetRecipeDefinition— Unity‑authored convenience asset (optional, requiresREV_INVENTORY_PRESENT)
Always resolve recipes via RecipeResolve.
4️⃣ Preflight & Enqueue¶
var svc = FindFirstObjectByType<CraftingServiceCore>();
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}");
}
5️⃣ 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}");
🧩 Core Concepts¶
CraftingServiceCore¶
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 has no dependency 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 cannot currently 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‑time
Jobs¶
Jobs represent accepted crafts and expose:
- Owner and recipe
- Duration and progress
- State transitions
- Persistence snapshots
- Lifecycle callbacks
⚙️ Preflight & Acceptance¶
var check = svc.CanCraftDetailed(player, recipe, requested: 10);
Debug.Log($"Max crafts: {check.maxCrafts} — Reason: {check.reason}");
For diagnostics:
var probe = svc.Probe(player, recipe, requested: 10);
Debug.Log($"Items:{probe.byItems} Currency:{probe.byCurrency} Space:{probe.bySpace}");
Preflight Order (Exact)¶
- Inputs
- Currency
- Space (routing + adjustments)
- Validators (if reached)
- Final clamp
🧾 Jobs, Persistence & Offline Progress¶
- Active jobs serialize via
SaveActiveJobs() - Restoration uses
RestoreJobs() - Offline progress is computed using
acceptedAtUtcandIWallClockProvider - Delivery logic, routing, modifiers, and validators all apply during offline completion
Paused jobs never tick offline.
🧠 Determinism & Batching¶
- Determinism requires:
- seeded RNG
- unchanged RNG call order
- binomial approximation disabled
- Batch jobs (
singleBatch) reduce overhead for repeated crafts - Large batches may use approximation for performance if enabled
📘 Folder Guide¶
- Core — service logic, jobs, persistence
- Context — ephemeral structs for modifiers/validators
- Lifecycle — job states and lifecycle phases
- Recipe — recipe data and resolution
- Modifiers — runtime behaviour adjustments
- Validators — gating and rule overrides
- Routing — output destination logic
- Random — deterministic and Unity RNG providers
- Time — game time vs wall‑clock time
- Integration — optional bridges to other systems
- Workbenches — trigger‑based demo stations
- Teaching — reference panels and demos
🔐 Strong / Escrow Immediate Crafting (Advanced)¶
CraftingServiceCore also exposes an opt‑in strong / escrow path for
atomic, immediate crafts via TryCraftImmediateEscrow.
This path is intentionally limited and does not use the job system.
Key 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 explicit reservation/hold‑capable adapters
This path exists for advanced use cases (server‑authoritative actions, anti‑duplication guarantees, or transactional crafting) and is not required for normal gameplay crafting.
If a backend cannot reserve inputs or capacity transactionally, escrow is intentionally unavailable.
If you are building time‑based, queued, or offline‑capable crafting, use the standard job pipeline instead.
🧾 Final Notes¶
- Crafting is adapter‑driven and backend‑agnostic
- Offline progress enables idle and AFK loops
- Validators and modifiers apply consistently in live and offline flows
- Nothing in Core depends on Unity UI, Editor tooling, or teaching panels
If you understand this README, you understand how the entire Crafting system fits together.