Skip to content

Crafting System — Public API

This page defines the supported, stable public API for the RevFramework Crafting System.

This is the contract

If something is not listed here, it is not supported as a public integration point — even if it appears accessible in code.

Audience: Developers integrating Crafting into gameplay, tools, or UI
Scope: Runtime public API and supported runtime components
Stability: Breaking changes to items listed on this page are avoided or clearly versioned

API Stability Policy

Items listed on this page are considered stable.
New public APIs and components may be added over time.
Internals may change without notice.

RevFramework guarantees stability only for documented public APIs and components.
Usage of internal or undocumented code is unsupported and may break without notice.


Core Concepts

  • CraftingService is the orchestration root.
  • Crafting is adapter‑driven (inventory, currency, authority).
  • All decisions flow through preflight → enqueue → job lifecycle.
  • Public APIs are intentionally small; behaviour is extended through adapters, validators, modifiers, and routing rather than subclassing the service.
  • Crafting avoids global state and does not require singletons.

Core Runtime Service

CraftingService

The central runtime service responsible for all crafting execution.

Responsibilities

  • Preflight validation (inputs, currency, space, validators)
  • Job creation and scheduling
  • Progress updates and completion
  • Cancellation and pause/resume
  • Persistence APIs and offline reconciliation
  • Event emission for UI / gameplay systems
  • Deterministic execution when driven explicitly

Gameplay systems typically interact with Crafting only through this service.


Service Configuration & Driving

These APIs allow code‑first configuration, deterministic tests, and headless/server simulation.

Configure

void Configure(
    ICraftingInventoryAdapter inventory,
    ICraftingCurrencyAdapter currency = null,
    ICraftingAuthority authority = null,
    ICraftingOutputRouter router = null,
    string defaultContainer = null,
    IRandomProvider rng = null,
    ITimeProvider time = null,
    IWallClockProvider wallClock = null
)

Configures adapters and runtime seams directly.

Supported scenarios:

  • code‑first projects
  • deterministic testing
  • server/headless execution
  • tool‑driven simulation

This is a supported alternative to Inspector wiring.

A null argument leaves that seam alone

Every parameter after inventory is optional, and passing null for one keeps whatever is already configured rather than clearing it. Calling Configure(inv) a second time to swap the inventory adapter therefore does not silently drop your authority, router or currency adapter.

To remove a seam, use its own setter — SetAuthority(null), SetCurrencyAdapter(null), SetOutputRouter(null).


Tick

void Tick(float now)

Runs a single simulation step using an explicit time value.

Typical uses:

  • deterministic replay
  • server simulation
  • automated tests
  • tooling

Normal gameplay continues to drive this via Update().


Runtime Policy Controls

void SetRefundInputsOnDeliveryFail(bool enabled)
void SetChanceSpacePolicy(CraftChanceSpacePolicy policy)
void SetMaxParallelJobs(int maxParallel)
void SetDefaultStationCap(int maxParallel)
void SetStationCap(string stationTag, int maxParallel)
bool RemoveStationCap(string stationTag)
void ClearStationCaps()

These methods modify runtime scheduling and failure‑handling behaviour.


Preflight Queries

CanCraft

bool CanCraft(GameObject owner, RecipeCore recipe, CraftPreflightOptions opts = default)

Returns true if at least one craft can currently be accepted.


CanCraftCount

int CanCraftCount(GameObject owner, RecipeCore recipe, int requested, CraftPreflightOptions opts = default)

Returns the maximum number of crafts possible, clamped by requested.


CanCraftDetailed

CraftCheck CanCraftDetailed(
    GameObject owner,
    RecipeCore recipe,
    int requested = 1,
    CraftPreflightOptions opts = default
)

Returns:

  • maxCrafts
  • the first blocking CraftFailReason

This is the authoritative preflight evaluation used by tools, UI, and Teachables.


Probe

CraftProbe Probe(
    GameObject owner,
    RecipeCore recipe,
    int requested = 1,
    CraftPreflightOptions opts = default
)

Returns a structured breakdown of:

  • item limits
  • currency limits
  • space limits
  • final craft count
  • blocking reason

Used primarily for UI and diagnostics.


Crafting Actions

Enqueue

CraftJob Enqueue(in CraftRequest request)

Consumes inputs/currency immediately and creates one or more crafting jobs.

Returns:

  • CraftJob on success
  • null if the request was rejected (a failure reason will be emitted via rejection events)

Convenience Helpers

CraftJob EnqueueOne(...)
CraftJob EnqueueBatch(...)
List<CraftJob> EnqueueMany(
    GameObject owner,
    RecipeCore recipe,
    int count,
    string container = null,
    object userData = null
)

These are thin helpers built on top of Enqueue.


Strong / Escrow Crafting

TryCraftImmediateEscrow

bool TryCraftImmediateEscrow(
    in CraftRequest request,
    string idempotencyKey,
    out CraftFailReason fail
)

Optional strong execution path for zero‑duration crafts.

Requirements:

  • inventory adapter supporting reservations
  • currency adapter supporting holds

Behaviour:

  • performs a reserve → commit flow intended to behave atomically when adapter contracts are honoured
  • no job is created
  • executes immediately

This path is intended for atomic, instant crafts only.


Job Queries & Control

IReadOnlyList<CraftJob> GetJobs(GameObject owner = null)
float GetJobRemainingSeconds(int jobId)

bool CancelJob(int jobId, bool refund = true)
bool PauseJob(int jobId)
bool ResumeJob(int jobId)

bool MoveQueuedFirst(int jobId)
IReadOnlyList<CraftJob> GetJobsByState(...)
int FillJobsByState(List<CraftJob> results, ...)

These methods allow runtime job inspection and control.


Lifecycle Events

event Action<CraftJob> OnJobAccepted
event Action<CraftJob> OnJobEnqueued
event Action<CraftJob> OnJobStarted
event Action<CraftJob, float> OnJobProgress
event Action<CraftJob> OnJobCompleted
event Action<CraftJob, CraftFailReason> OnJobFailed
event Action<CraftJob> OnJobCancelled

event Action<CraftJob, JobLifecyclePhase, CraftFailReason> OnJobLifecycle

event Action<GameObject, RecipeCore, CraftFailReason> OnPreflightRejected
event Action<GameObject, RecipeCore, CraftFailReason> OnJobRejected

event Action<GameObject, RecipeCore, int> OnCraftXp

These events allow gameplay systems and UI to observe crafting behaviour.


Recipe Construction

Runtime Recipe Creation

static RecipeCore Create(
    IReadOnlyList<ItemRef> inputs,
    IReadOnlyList<ItemRef> outputs,
    string currencyId = null,
    long amountPerCraft = 0,
    float craftTimeSeconds = 0f,
    string stationTag = null,
    string displayName = null,
    string description = null,
    int xpPerCraft = 0
)

Creates a runtime RecipeCore instance without editor dependencies.

Typical uses:

  • procedural crafting systems
  • importing data from external sources
  • deterministic tests
  • runtime recipe generation

Returned recipes should be treated as immutable.


Adapter Seams

Crafting integrates with other systems exclusively through adapter interfaces.

Inventory

bool HasInventoryAdapter
bool TryGetInventoryAdapter(out ICraftingInventoryAdapter adapter)
bool TryGetInventoryContext(GameObject owner, string container, out CraftingInventoryContext ctx)

Currency

bool HasCurrencyAdapter
bool TryGetCurrencyAdapter(out ICraftingCurrencyAdapter adapter)
void SetCurrencyAdapter(ICraftingCurrencyAdapter adapter)

SetCurrencyAdapter replaces the adapter without touching any other seam; null removes it, after which recipes carrying a currency cost fail rather than being charged.


Authority

bool HasAuthorityGate
bool CanMutate(GameObject owner, out CraftFailReason denyReason)
bool TryGetAuthority(out ICraftingAuthority authority)
void SetAuthority(ICraftingAuthority authority)
Allows callers to check whether crafting mutations are currently permitted under the configured authority gate.

When no ICraftingAuthority implementation is configured, CanMutate returns true and crafting behaves permissively.

SetAuthority swaps the gate at runtime — for a host/client handover, for example — and null restores the permissive default. It is the counterpart to Configure, which cannot clear a seam.


Routing

bool SetOutputRouter(ICraftingOutputRouter router)

Validators & Modifiers

Custom behaviour is implemented via components:

  • ICraftingValidator
  • ICraftingModifier
  • ICraftingOutputRouter

Discovery behaviour:

  • Components are searched on the owner and parent chain
  • Order should not be relied upon as a stable contract

Persistence & Offline Progress

List<CraftJobSnapshot> SaveActiveJobs(...)
void RestoreJobs(...)

Paused jobs do not tick offline, and neither do queued ones: offline time is credited only to a job that was already running when the snapshot was taken. Time a job spent waiting in the queue is not craft time, so a backed-up queue does not complete on reload.

Offline completion re-applies modifiers and attempts delivery when the target container can be resolved.

Skipped Snapshots

RestoreJobs skips individual snapshots rather than failing the whole restore. The remaining entries still load. A snapshot is skipped when:

Skipped because Does it tell you?
its owner cannot be resolved No — nothing is logged
its recipe cannot be resolved No — nothing is logged
its completion has already been applied (replay of an already-reconciled save) Warns
it declares a schema version newer than the running build supports Warns

The two silent skips are the expensive ones

An unresolvable owner or recipe is skipped by a bare continue with no log line, and RestoreJobs returns void — so a host driving it directly has no way at all to learn that a job was dropped. What is lost is not really the job: its inputs and currency were spent when it was accepted, long before the save was written, and nothing refunds them.

If you need that reported, resolve the ids yourself before calling and compare the counts. CraftingSaveParticipant does exactly this, which is why a restore through the save coordinator surfaces the loss as a failed section while a direct call cannot.

Note also that the two warnings above are editor and development-build only, so a release build logs nothing in any of the four cases.

Driving RestoreJobs directly? Call ClearAppliedCompletions() first

The already-applied skip is what makes a replayed snapshot set a no-op instead of a double delivery, and it is in-memory state that a load does not rewind. Load a save taken mid-craft on a craft that has since completed live in this session, and its snapshot is skipped as already-applied — while the inventory and currency around it rewind to before it finished. The player loses the inputs, the outputs and the job. The editor logs one line about a skipped completion; a release build logs nothing, and neither says anything was lost.

CraftingSaveParticipant makes that call for you before every restore. A host calling RestoreJobs itself must make it too.

Forward compatibility

CraftJobSnapshot.version is stamped on save and checked on restore. A job saved by a newer build is skipped rather than restored blind, because a craft job carries currency already taken from the player and a completion transaction id — misreading those can deliver, refund, or charge against the wrong values. Snapshots with version 0 (written before the field was populated) and with the current version both restore normally.


Persistence Responsibility

The Crafting system exposes APIs for saving and restoring active jobs.
It does not provide a save system or storage layer.

  • No file handling
  • No cloud sync
  • No automatic save/load triggers

👉 Integrating these APIs into your save system is entirely the developer’s responsibility.

This mirrors other system boundaries in RevFramework:

  • Authority → you define and enforce
  • Networking → you implement
  • Persistence → you define and control

The framework provides the data and execution model — you decide how and when it is stored.

Crafting defines what the data means — your game defines how it lives.


Explicitly Not Supported

Explicitly not supported

The following are intentionally not supported integration points:

  • Reflection into Crafting internals
  • Manipulating job lists directly
  • Inspector/SerializedObject mutation of runtime state
  • Calling internal hooks or helpers

TL;DR

TL;DR

This page defines the contract.
If something appears here, it is a supported public API.
If it does not appear here, it should be treated as internal implementation detail.