Crafting System — Mental Model¶
This page explains how to think about Crafting in RevFramework.
It is not an API reference.
It is a conceptual map that helps you decide:
- Where does this behaviour belong?
- Which extension point should I use?
- What should I never touch?
If you understand this page, the API will feel obvious.
The Core Idea¶
The core idea
Crafting is a controlled execution pipeline with explicit execution semantics, not a UI action.
A craft is not “press a button and spawn items”.
It is a multi‑stage decision and execution flow designed to guarantee:
- correct validation
- deterministic outcomes (when desired)
- safe failure and refund paths
- authority‑controlled mutation for multiplayer scenarios
Depending on configuration, execution may be best‑effort or escrow‑based.
Everything in the system exists to protect that pipeline.
The Single Orchestrator¶
CraftingService¶
There is exactly one authority on crafting truth:
CraftingService
It owns:
- job state
- scheduling
- timing
- lifecycle events
- persistence and offline recovery
Nothing else should:
- create jobs
- mutate job state
- deliver outputs
- refund currency or inputs
If something outside the service attempts to perform these actions directly, the design boundaries of the system have likely been violated.
The Crafting Pipeline (Start to Finish)¶
Every craft flows through the same conceptual stages:
Preflight
↓
Accept
↓
Schedule
↓
Run / Pause
↓
Deliver
↓
Complete / Fail / Cancel
Each stage has specific extension points and clear responsibilities.
Stage 1: Preflight (Pure Decision)¶
Question: “Can this craft happen right now?”
Preflight:
- never mutates state
- never consumes items or currency
- is safe to call from UI, tools, and teachables
Preflight evaluation order¶
The system evaluates checks in this order:
- Inputs (inventory)
- Currency
- Space (including routing and chance-space policy)
If these checks do not return early, validators are applied to the proposed result.
Extension points used here¶
ICraftingValidatorICraftingLevelSource- Station tag filters
- Output routing (for space checks)
What does NOT belong here¶
- spawning items
- consuming resources
- mutating cooldown state
- creating jobs
Preflight answers possibility, not execution.
Stage 2: Accept (Execution Boundary)¶
Question: “We are committing — do we proceed?”
Accept is the stage where:
- authority is checked
- resources are consumed or reserved
- a
CraftJobmay be created
This is the commit boundary for crafting execution.
Execution modes at accept¶
Accept behaves differently depending on execution mode.
Best‑effort (default)¶
- Inputs and currency are consumed immediately
- Delivery may fail later
- Failures attempt to compensate with refunds where possible
This mode is designed for typical single‑player or non‑critical economies.
Escrow (opt‑in)¶
- Inputs and currency are reserved
- Nothing is consumed until delivery is guaranteed
- Commit is intended to be atomic when adapter invariants are honoured
Escrow mode requires adapters that support reservations/holds.
Extension points here¶
ICraftingAuthorityICraftingModifier(first evaluation pass)
Design rule¶
If something consumes or reserves resources, it belongs here, not in UI or benches.
Craft Jobs (The Durable Unit)¶
A CraftJob represents a durable crafting operation.
It records:
- the owner performing the craft
- the recipe
- execution timing
- payment state
- progress and lifecycle state
Jobs are created only through:
CraftingService.Enqueue(...)
Jobs:
- can be queued
- can be paused
- can survive save/load
- can complete offline
You never “fake” a job. You either create one through the service — or not at all.
Persistence (Save / Load Responsibility)¶
Crafting supports persistence through job snapshots:
SaveActiveJobs(...)RestoreJobs(...)
These APIs define:
- what data to persist (active jobs)
- how jobs resume and progress (including offline behaviour)
They do not provide a persistence system.
Crafting defines the job model and lifecycle — your game defines persistence.
Responsibility Split¶
-
CraftingService
-
Owns job state
- Owns timing and offline progression
-
Owns how jobs resume and complete
-
Your Game
-
Decides when to save
- Decides where to store data (file, cloud, database)
- Provides ID resolution (owner / recipe)
- Ensures identifiers remain stable across sessions
- Calls restore at the correct time in your lifecycle
Conceptual Flow¶
Game Save:
→ SaveActiveJobs(...)
→ Store snapshot
Game Load:
→ Load snapshot
→ RestoreJobs(...)
Design Rule¶
Crafting is responsible for what the data means. Your game is responsible for when and where it is saved.
Offline Progress Context¶
Offline progression is applied during restore using:
acceptedAtUtcIWallClockProvider
It applies only to jobs that were running when the snapshot was taken. A queued job has not started crafting, so the time it spent waiting is not credited and it restores with its full timer. Paused jobs do not progress offline either.
This means:
- Running jobs may complete while the game is closed
- Jobs may partially progress
- Jobs may resume
depending on elapsed real-world time and configuration
Stage 3: Scheduling & Time¶
Scheduling answers questions such as:
- When can this craft run?
- How many jobs can run in parallel?
- Does a station have available capacity?
Time is abstracted through two providers:
ITimeProvider→ gameplay timeIWallClockProvider→ real UTC time
Why two time sources exist¶
| Time Source | Purpose |
|---|---|
Game time (ITimeProvider) | Live gameplay ticking |
Wall clock (IWallClockProvider) | Offline progress calculation |
Design rule¶
Time is data, not a direct call to
UnityEngine.Time.
This abstraction enables deterministic testing and offline completion.
Stage 4: Run / Pause¶
While running:
- progress events are emitted
- no additional resources are consumed
- modifiers may be re‑evaluated later at delivery
Pausing:
- freezes remaining time
- never re‑consumes inputs
- never re‑debits currency
Job state always lives inside the service.
Stage 5: Delivery (Final Commit)¶
Delivery attempts to produce the final outputs.
Steps:
- Re‑evaluate modifiers
- Resolve output routing
- Attempt to add outputs
- Finalise craft outcome
Delivery semantics¶
Best‑effort¶
- Outputs are attempted
- Delivery failure refunds currency
- Inputs may be refunded depending on service policy and container availability.
Escrow¶
- Outputs are committed atomically
- Delivery either fully succeeds or nothing is applied
Atomicity is conditional on the adapters
Escrow's guarantee is reserve-by-doing plus compensation: it takes what it needs, and gives it back if any later step fails. The give-back goes through the same adapter, so it depends on that adapter and its container accepting it. An adapter that refuses a give-back leaves partial state — the framework reports it and cannot prevent it. This is why escrow states adapter requirements rather than promising atomicity unconditionally.
If delivery fails:
- currency is refunded or released
- inputs may be refunded or released (mode‑dependent)
- the job ends in
Failed
Extension points used here¶
ICraftingOutputRouterICraftingModifier(delivery pass)
Validators vs Modifiers¶
Understanding this distinction is critical.
Validators¶
Validators answer:
“Should this craft be allowed?”
They:
- modify
maxCrafts - block crafting with a reason
- never mutate state
Examples:
- cooldowns
- level gates
- progression locks
Modifiers¶
Modifiers answer:
“How does this craft behave?”
They:
- adjust duration
- scale currency cost
- add or alter outputs
- run twice (accept and delivery)
Rule of thumb:
- If it blocks permission, it is a validator.
- If it changes numbers, it is a modifier.
Adapters (External Systems)¶
Crafting does not talk directly to other gameplay systems.
Instead it integrates through adapters:
ICraftingInventoryAdapterICraftingCurrencyAdapterICraftingInventoryReservationAdapterICraftingCurrencyHoldAdapter
Adapters allow Crafting to remain:
- modular
- testable
- multiplayer‑ready
If you feel tempted to access inventory directly from crafting logic, you are bypassing the system’s safety model.
Execution Modes¶
Crafting supports two execution semantics.
Best‑Effort (Default)¶
Designed for most games.
Characteristics:
- immediate resource consumption
- compensating refunds if delivery fails
- simple and fast execution
Escrow (Opt‑In)¶
Designed for multiplayer or economy‑critical systems.
Characteristics:
- resource reservation
- atomic commit when the adapter invariants hold — see the note above
- strict adapter requirements
Both modes use the same pipeline. Only the transaction guarantees differ.
Built‑in Component Roles¶
Use this mental map when extending the system.
| Component | Responsibility |
|---|---|
| Workbenches | Provide input and context |
| Validators | Permission rules |
| Modifiers | Behaviour adjustments |
| Routers | Output destination decisions |
| Service | Execution and truth |
If a component crosses these roles, it is probably misplaced.
What Never Belongs Where¶
What never belongs where
- Benches should never consume items
- Validators should never spawn outputs
- Modifiers should never check authority
- UI should never mutate job state
- Anything outside the service should never issue refunds
These boundaries keep the system predictable and safe.
The One‑Sentence Model¶
The one-sentence model
Crafting is a job‑based execution pipeline with strict boundaries and explicit execution semantics.
TL;DR¶
TL;DR
- One orchestration service
- Pipeline: preflight → accept → job → delivery
- Validators block, modifiers shape
- Adapters isolate external systems
- Execution semantics are explicit