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

  1. Inputs
  2. Currency
  3. Space (routing + adjustments)
  4. Validators (if reached)
  5. 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

  • 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 acceptedAtUtc and IWallClockProvider
  • 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 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 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