Skip to content

API Recipe Matching

Piggo41 edited this page Oct 16, 2026 · 11 revisions

Recipe matching

Lua API / Recipe matching

On this page

betamoon.recipes:match(query)

Matches a supplied input snapshot. Required type (name/handle); optional ingredients role map and context values validated against the type.

Returns a match snapshot, or nil when no recipe matches. Supports custom recipe types and native smelting. The tile-bound variant provides inventory application methods.

local match = betamoon.recipes:match {
    type = "smelting",
    ingredients = { input = betamoon.stack(15) }
}

Single-item roles take a stack, pools a dense list of stacks, and grids a dense matrix with the schema's exact width and height. Omitted roles supply empty inputs; an empty pool table supplies an empty pool.

ctx.recipes:match(query)

Tile-context matching. Required type and slots = { ingredients = ..., outputs = ... }; optional validated context. Returns nil or a match bound to this tile inventory.

Every role, including optional roles, requires a physical binding. Item roles bind a slot-name string. Pool/output-pool roles bind a pool handle/list; grid roles bind a grid handle/matrix. Unknown slots and overlapping physical bindings are errors.

local match = ctx.recipes:match {
    type = "smelting",
    slots = {
        ingredients = { input = "input" },
        outputs = { output = "output" }
    }
}

Discovery can return a match whose outputs are blocked. Check canApply before advancing a machine. Custom candidates are enabled recipes ordered by descending priority, then by earlier registration for equal priorities.

Match fields

Field Meaning
recipe Selected recipe reference.
type Custom qualified type name or smelting.
signature Opaque recipe/allocation/binding identity for comparing work-in-progress; do not parse it.
data Recipe data snapshot.
output Primary output stack.
outputs All output role values.
ingredients Selected single-role stacks with consume flags; pool/grid roles expose allocations.
allocations Per-role allocation lists with slot, count, selected stack; requirement and row/column when relevant.
remainders List of byproducts with ingredient role, source slot, stack, and optional output role.

Grid allocation views may also report transformation and placement row/column. Views are descriptive; changing them does not rewrite the processing plan.

match:canApply(options)

Bound matches only. Returns true if inputs, recipe revision, conditions, outputs and remainders are still valid; otherwise false, reason. No inventory mutation.

Optional options = { context = ... }. Recipes with conditions require a fresh context snapshot at application checks; omitting it is an error.

local ready, reason = match:canApply { context = { heat = 400, powered = true } }

match:apply(options)

Bound matches only. Revalidates and atomically consumes required inputs, retains catalysts, and inserts all outputs/remainders. Returns true on commit or false, reason on rejection. Options follow canApply.

A successful plan cannot be applied twice. Do not also manually consume inputs or insert outputs.

local applied, reason = match:apply()

Failure reasons

Reason Meaning
already_applied This match already committed.
invalid_inventory Bound tile inventory is no longer valid.
reentrant_apply Inventory is already inside an incompatible commit/callback operation.
stale_recipe Recipe/type/matcher revision changed or registration disappeared.
conditions_changed Fresh context no longer satisfies recipe conditions.
inputs_changed Current allocation no longer matches the discovered plan.
output_full Output insertion cannot fit completely.
remainder_blocked Remainder placement cannot complete.

Commit can also reject an inventory changed during revalidation. Invalid arguments/schema values raise Lua errors rather than returning a normal mismatch reason.

betamoon.recipeBindings:pool(definition)

Creates a reusable slot-name pool. Accepts a nonempty unique list of names or { prefix = ..., count = ... }. Count range 1–4096; generated names are prefix1, prefix2, etc.

local pool = betamoon.recipeBindings:pool { prefix = "input_", count = 4 }

Returns a pool handle with kind = "item_pool", count, and numeric indexed names.

pool:toTable()

Returns a detached list of names.

local names = pool:toTable()

betamoon.recipeBindings:grid(definition)

Creates a reusable slot-name grid from a nonempty rectangular matrix, or { prefix, width, height }. Dimensions 1–16. Generated names are prefix followed by row_column, with row/column starting at 1. Names must be unique.

local grid = betamoon.recipeBindings:grid { prefix = "work_", width = 3, height = 3 }

Returns a grid handle exposing kind = "item_grid", width, height, and indexed row views.

grid:toTable()

Returns a detached matrix of slot names.

local names = grid:toTable()

betamoon.recipeMatchers:add(definition)

Registers a custom allocation strategy for the current script generation. Required name and match function. Returns a matcher handle; duplicate names raise an error. Type schemas reference it through their matcher field.

local matcher = betamoon.recipeMatchers:add {
    name = "mymod:recipe_matcher/first_slot",
    match = function(recipe, inputs, context, plan)
        if not inputs.input.stack then return false end
        plan:use { role = "input", slot = inputs.input.slot }
        return true
    end
}

This minimal strategy assumes a single item role named input. A valid plan must still satisfy all required counts and item constraints.

match(recipe, inputs, context, plan)

Receives detached normalized recipe data (including type/key), detached input cells, copied context values, and a plan builder. Return truthy to validate the proposed allocation; false/nil means no match.

An input cell has index (one-based position within its binding), slot (name), and stack (nil if empty). Grid cells additionally have row/column; pools are cell lists, grids are matrices, single items are one cell.

Mutating snapshots does not mutate inventory. Errors or invalid plans disable the matcher until re-registration. Recursive invocation of the same matcher does not match. Validate contents before proposing uses.

plan:use(definition)

Adds a proposed allocation and returns the plan builder.

plan:use { role = "materials", slot = 1, requirement = 1, count = 4 }
Field Meaning
role Required ingredient role name.
slot Required bound slot name or one-based flattened binding index.
requirement Omit for item roles; pool requirement index for pools; recipe character for grids.
count Optional positive count; defaults to that ingredient requirement's count.

Allocation must reference the role's slots, satisfy item/damage constraints, avoid over-allocation, meet exact required counts, and respect empty/extra-slot rules. It cannot invent outputs or bypass retained ingredient rules. Using a finalized plan raises an error.

Matcher lookup

betamoon.recipeMatchers:get(name)

Returns the registered matcher handle or nil. Accepts name or matcher handle. Handle properties are documented below.

local matcher = betamoon.recipeMatchers:get("mymod:recipe_matcher/first_slot")

Matcher handle

Properties: name (qualified matcher name), owner (script filename), and exists (true while this registration is current and enabled). Registration is scoped to its owner generation; reload removes owned matchers and invalidates affected recipe plans.

Examples: Context revalidation, advanced matching, custom matcher.

Clone this wiki locally