Skip to content

🛠️ 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 adapterrequired
  • 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 asset
  • RecipeDefinition — Unity‑authored convenience asset (optional, requires REV_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)

  1. Inputs
  2. Currency
  3. Space (routing + adjustments)
  4. Validators (if reached)
  5. Final clamp

🧾 Jobs, Persistence & Offline Progress

  • Active jobs serialize via SaveActiveJobs()
  • Restoration uses RestoreJobs()
  • Offline progress is computed using acceptedAtUtc and IWallClockProvider
  • 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 CraftJob is 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.