Command receipts¶
Use receipts when a script must distinguish queue admission from later native submission, rejection, or cancellation. Ordinary commands return one boolean and do not require receipt cleanup. A receipt is transport evidence; it does not establish that the game carried out the requested action.
Tracked requests occupy a bounded pool of 256 records until their terminal receipts are released. A script that does not need this evidence should use the ordinary command; ignoring tracked receipts can eventually reject new tracked requests. Some tracked commands also validate whole groups more strictly than their ordinary counterparts. Read the command-specific rules below before switching an existing script.
Start with Commands for the usual command names and arguments.
Tracked commands¶
UnitsMoveTracked(units, position, append?), UnitsTargetObjectTracked(units, target, append?), SetUnitStanceSeekShelterTracked(units)
and SetUnitStanceUngarrisonTracked(buildings, guardedOccupants) return accepted, receipt. Acceptance is
queue admission. A rejected admission includes state="rejected" and reason
without an ID. Accepted receipts use an opaque decimal string id; never
convert it to a Lua number.
UnitsStopTracked(units) uses the same group stop order as UnitsStop and
returns accepted, receipt. It exposes later actor/layout/allocation rejection,
cancellation before dispatch, and native transport evidence. Submitted means
the transport returned, not proof that each object stopped in the game.
SetUnitStanceFollowTracked(units, target) and
SetUnitStanceGuardTracked(units, target) also return accepted, receipt.
They reject invalid actor groups and require a live target both at admission
and before submission. A disappeared/dead target is reported as invalid-target.
They retain the same native follow/guard behavior as the boolean variants.
SetUnitStancePatrolTracked(units, position) and
SetUnitStanceAttackMoveTracked(units, position) submit the existing immediate,
single-waypoint orders with receipts. They require finite coordinates and valid
actor groups. They do not append an order or create a waypoint sequence. Patrol
and attack-move boolean variants also reject non-finite coordinates.
The following orders also support accepted, receipt results:
| Call | Argument contract |
|---|---|
SetUnitCombatStanceTracked(units, stance) |
Integer UnitCombatStance value, 0..3. |
SetUnitFormationTracked(units, formationId) |
Non-negative signed 32-bit integer ID. |
SetUnitStanceAutoScoutTracked(units) |
Explicit nonempty actor group. |
GatherInsideTracked(buildings[, state = -1]) |
State -1, 0, or 1. |
DeleteUnitTracked(objects) |
Explicit nonempty actor group. Deletes live game objects. |
These tracked calls reject invalid group members rather than filtering the group. They preserve the native commands and provide transport evidence, not proof of the requested effect. Formation ID range validation establishes representability, not whether the current game supports that formation. Use deletion only from an explicit script action/option; cancelling a receipt cannot restore deleted objects.
SetGatherPointTracked(buildings, target), RingTownBellTracked(townCenter[, active = true]),
SendBackToWorkTracked(townCenter), and SendAllBackToWorkTracked(townCenter)
return accepted, receipt. Rally targets accept an object or position, matching
SetGatherPoint. Object targets are re-read before submission; disappearance
or a changed definition produces rally-target-changed. Town-center commands
retain actor/session validation. Submission does not prove the requested effect.
DropResourcesTracked(units) uses native chunks of up to 60 actors. All actors
must be valid at admission and are revalidated before execution. If an earlier
packet was submitted and a later packet fails, the receipt is indeterminate
with reason partial-submission, and no further packets are attempted. It has
no elapsedMs for a complete submission. Do not retry the entire group: some
actors may already have received the order. submitted means all packet
transports returned, not that every actor dropped its resources.
Production commands also return accepted, receipt:
| Call | Request |
|---|---|
TrainUnitTracked(buildings, buildingType, unitType[, count = 1]) |
Queue unit production. |
ResearchTechnologyTracked(buildings, technology) |
Queue research. |
UnitsBuildStructureTracked(builders, buildingType, position[, options]) |
Place a foundation. |
These use the same native orders as their boolean counterparts, but reject an
invalid actor instead of filtering the group. Tracked type/technology IDs must
be integers in 0..32767; train counts must be integers in 1..32767.
Invalid values return a rejected receipt rather than wrapping the packet field.
invalid-count, invalid-type, and invalid-technology identify those failures.
Native allocation failure is packet-allocation-failed. A missing research
predicate, failed query, or unavailable research layout produces
research-query-or-layout-unavailable at execution.
Cancelling a queued receipt prevents this library from sending the request. It does not cancel an item already in the game's production queue. Submission does not prove affordability, production eligibility, legal placement, or game acceptance. Observe live queues, research state, or foundations separately.
For example, keep the receipt ID when you submit production once:
local accepted, receipt = TrainUnitTracked({ producer }, building_type, unit_type, 1)
if not accepted then
LogWarn("Training request rejected", receipt.reason)
else
pending_train_id = receipt.id
end
Here producer is a freshly queried owned building and the type IDs identify
the producer and requested unit. In a later update, read
CommandReceipt(pending_train_id). Keep waiting while queued or executing;
nil means missing evidence. On a terminal state, record the result and release
its metadata with ReleaseCommandReceipt. A submitted or indeterminate order
must be reconciled against live game observations before another production
request; neither status is permission to retry automatically. Native receipt
release is metadata cleanup, not cancellation of production.
CommandReceipt(id)returns current evidence, or nil for unknown identity.CancelCommandReceipt(id)cancels only queued work and returns actual current evidence. Execution may win the race; inspect the returned state.ReleaseCommandReceipt(id)releases terminal native metadata, returning false for queued/executing or unknown records. Keep a cleanup owner until it succeeds.
States are queued, executing, submitted, rejected, cancelled and
indeterminate. Submitted means the void native transport returned, not that
the game accepted the order. elapsedMs measures time since that return using
a native monotonic clock. sessionEnded separately indicates loss of the
originating session; it does not rewrite the historical transport outcome.
Missing receipt evidence is unknown, never proof of rejection or completion.
For recognized command functions, GetApiCapability(callable).supportsObservation
reports whether receipt observation is implemented: currently true for
SetDiplomacyTracked and UnitsTargetObjectTracked, false for other recognized commands (including boolean
SetDiplomacy). The field is absent when the callable is not classified as a
command. True does not guarantee that the native observation query is currently
available; inspect the receipt's observation state. Aliases of the same native
function report the same support.
SetDiplomacyTracked attaches observation = {kind="diplomacy", state=...}
after its execution callback runs. Each receipt read queries the original source
and target relationship using the native XS query. States are matched (raw
relationship equals the requested state), not-matched, unavailable,
not-submitted, and session-ended. Object-target observations are described below; other
commands currently omit this field.
Queued receipts can also omit it. Only submitted commands in their original
active session read live state; a session change during the read discards it.
A match is current state evidence, not proof of command causality, a transition,
or completion. It may already have been true before submission and can change
on a later read. Observation does not change transport state or trigger retries.
UnitsTargetObjectTracked issues the same default object action as
UnitsTargetObject. It rejects invalid actors as a whole group and re-resolves
the target before submission. A disappeared target produces invalid-target.
Dead resource targets remain valid for native gathering behavior. Submission
does not establish which contextual action the game selected or its completion.
After execution, its receipt also carries observation.kind="object-target".
matched means every original actor's native current target ID equals the
requested target ID. not-matched includes actors with no current target.
Missing/dead actors, changed ownership, missing target, unresolved query or read
fault produce unavailable. Reads are sequential live queries, not an atomic
group snapshot. For appended orders, this observes the current target, not the
queued future order. It cannot establish queue insertion, the contextual action,
causality or action completion. A later human order can produce the same match.
The observation never retries a command or changes its transport state.
The queue retains at most 256 tracked records, including unreleased terminals; it rejects capacity exhaustion instead of evicting active/unconsumed evidence. Scout retreat, villager anchor-return movement, reactive seek-shelter and shelter release use receipts in the managed runner. Release owns the actual container and all known occupants across shelter requests, supplying those occupants as native guards. Whole-group positive departure is required for effect completion. Tracked commands retain uncertain command ownership until positive live evidence or authoritative session loss permits reconciliation. Other managed command domains remain boolean-only.
Tracked admission rejects an invalid member or an oversized packet instead of
filtering/truncating it. After deduplication, seek-shelter supports at most 16,382
actors and ungarrison supports at most 16,378 buildings. Ungarrison still affects
the entire building. Optional guardedOccupants adds exact participant checks:
each supplied guard must share the building group's known owner, be alive with
finite positive health, and be inside one of the supplied
buildings at admission. Before sending, guards are checked again for ownership,
liveness, observed human orders, and the same containing building. A failed
guard rejects the whole queued command. Omitted or empty guards retain the
building-only contract; unlisted occupants are not protected by this argument. The command does not choose a shelter destination
or prove capacity, reachability, game acceptance or occupant safety.