Skip to content

Feeding Achievements

Upcoming SDK release

Achievements ship in an upcoming Scorbit SDK release. This section documents that release ahead of time, so details may still change before it ships.

The SDK evaluates achievements from the same game state your code already reports. There is no separate achievements input except events (named counters), and no call to ask for evaluation. Every commit() re-evaluates every achievement for every claimed player.

This page covers what to report, and how to report it, so that achievements authored for your title match what your game actually does.

Before the First Game: Data Directory

The SDK caches achievement definitions and the DMD frame bundle on disk. Give it a writable directory that survives reboots, before you create the game state. Without one, the SDK uses a temporary directory, which works but may be emptied on reboot, so definitions are downloaded again.

sb_config_t config = sb_config_create();
// ... provider, machine id, keys ...
sb_config_set_data_dir(config, "/var/lib/mygame/scorbit");

sb_game_handle_t gs = sb_create_game_state(config);
sb_config_destroy(config);
scorbit::Config config;
// ... provider, machine id, keys ...
config.setDataDir("/var/lib/mygame/scorbit");

auto gs = scorbit::createGameState(config);
import scorbit

config = scorbit.Config()
# ... provider, machine id, keys ...
config.set_data_dir("/var/lib/mygame/scorbit")

gs = scorbit.create_game_state(config)

What the SDK Watches

Each commit() records one row of game state. Achievements are derived from these rows only:

You report With Achievements can use it as
Each player's score set_score Score thresholds
The current ball set_current_ball Ball durations, "on ball one"
The active player set_active_player Which player each fact belongs to, and each player's time at the machine
Active modes add_mode, add_mode_expiring, remove_mode, clear_modes Mode starts, modes running together, how long a mode lasted
A mode the player beat set_mode_completed Mode completions
A running total add_event Counters such as spinner spins or ramps

Scores belong to the player you pass to set_score. Mode starts, completions and events belong to the active player at that commit, so keep set_active_player accurate in multiplayer games.

Commit Often

Evaluation happens on commit(), and commit cadence is also the timing resolution for achievements: two things reported in the same commit happened at the same instant. Commit at every meaningful change, and at least once per game loop cycle as the Game Loop page already recommends. Calls that don't change anything are cheap.

Mode Names Must Match Exactly

An achievement refers to a mode by name, and the name must be byte-for-byte identical to the string your game passes to add_mode, including any category prefix:

Your game calls A rule must reference
add_mode("MB:Multiball") MB:Multiball (not Multiball, not mb:multiball)
add_mode("NA:Reactor") NA:Reactor

Names are case-sensitive, and are neither trimmed nor normalized. Mode and event names must not contain ;, =, ,, " or a newline. Those characters are structural in the game log: add_event ignores a name containing them, and achievements never match a mode named with them.

An existing mode whose name already contains one of these characters keeps working for scoreboards and the game timeline; it just can't be used in an achievement. If it should be, report it under a compliant name, for example in a firmware update.

Keep names stable across firmware versions where you can. A renamed mode silently stops matching every published achievement that refers to it, and published achievements can't be edited to follow the rename.

The Creator Portal learns your names

The platform records every mode and event name your game reports, and offers them to creators authoring achievements for your title. Reporting consistent names is what makes good achievements possible for your game.

Modes That Run vs Modes That Can Be Completed

Achievements distinguish two kinds of mode:

  • Modes that simply run, such as a multiball. They start, last a while and end, and there is no failing them. Report them with add_mode and remove_mode. Achievements count their activations: each time the mode goes from inactive to active.
  • Modes the player can win or fail, such as a wizard mode or a timed quest. Report the mode as usual, and when the player succeeds, call set_mode_completed in the same commit in which you remove it. If the player fails, just remove the mode.

A completed mode is a one-time mark, not a state: it is recorded only in the next commit and is not added to the active mode list.

// Quest starts
sb_add_mode(gs, "NA:The Tale of the Forty Thieves");
sb_commit(gs);

// ... player beats the quest ...
sb_set_mode_completed(gs, "NA:The Tale of the Forty Thieves");
sb_remove_mode(gs, "NA:The Tale of the Forty Thieves");
sb_commit(gs);
// Quest starts
gs.addMode("NA:The Tale of the Forty Thieves");
gs.commit();

// ... player beats the quest ...
gs.setModeCompleted("NA:The Tale of the Forty Thieves");
gs.removeMode("NA:The Tale of the Forty Thieves");
gs.commit();
# Quest starts
gs.add_mode("NA:The Tale of the Forty Thieves")
gs.commit()

# ... player beats the quest ...
gs.set_mode_completed("NA:The Tale of the Forty Thieves")
gs.remove_mode("NA:The Tale of the Forty Thieves")
gs.commit()

Report success before the game ends

A mode the game never marks as completed can never satisfy a completion achievement. If your game has a success state for a mode, report it.

Short-lived modes and targets

add_mode_expiring adds a mode that removes itself after a few seconds: 3 is recommended, 0 means 3, and anything over 10 is clamped to 10 (Python defaults to 3). It is the right call for momentary events such as a jackpot or a lit target. Adding it while it is inactive counts as one activation. Calling it again while it is still active extends it: the timer restarts with the new duration, and it doesn't count as a new activation, so no separate "extend" call is needed. Achievements see the expiry at the next commit(). Prefer it to adding a mode and then clearing all modes, which also ends any multiball or wizard mode that is running.

sb_add_mode_expiring(gs, "NA:Super Jackpot", 3);
sb_commit(gs);
gs.addModeExpiring("NA:Super Jackpot", 3);
gs.commit();
gs.add_mode_expiring("NA:Super Jackpot", 3)
gs.commit()

Events: Named Counters

An event is a named 64-bit counter your game adds to: spinner spins, ramp shots, targets hit, lanes completed. Use events for anything you count rather than anything that is on or off.

  • add_event(name, value) adds value to the counter. value may be negative to reduce it.
  • There is no "set" operation: counters only accumulate.
  • Like a completed mode, each call is recorded in the next commit only. Several calls before a commit are all recorded.
  • In a single-game achievement the counter starts at zero each game. In a lifetime achievement it accumulates across every game the player has played.
// Three spinner spins since the last cycle
sb_add_event(gs, "spins", 3);
sb_commit(gs);
// Three spinner spins since the last cycle
gs.addEvent("spins", 3);
gs.commit();
# Three spinner spins since the last cycle
gs.add_event("spins", 3)
gs.commit()

Call add_event only when the counter actually changes. Each call marks the game state as changed, so the next commit() sends an update even if nothing else moved.

Players and Claims

Achievements belong to people, so only players who have claimed a slot (see Claiming a Position) can earn them.

  • When a player claims a slot, the SDK fetches their stored progress, whenever the claim happens. Lifetime achievements then continue from their real totals.
  • Only what happens after the claim counts. A player who claims partway through a game is not credited with anything that slot did before they claimed. Progress is never worked out retroactively.
  • Players who never claim earn nothing from that game.
  • A claim made after the game ends, or one whose progress is still being fetched when the game ends, earns nothing from that game on the machine.

Prompt players to claim before they play

Because achievements only count from the claim onward, make claiming easy before the first ball: show the claim QR code on the start screen and during ball one (see Claiming a Position), and remind players that achievements are only tracked once they claim.

Balls and Time

  • Session time is per player. A player's time at the machine is the sum of their own turns, not wall-clock time, so a four-player game produces four different session times.
  • A ball ends when the ball number changes, the active player changes, or the game ends.
  • Single-ball confinement. Ball durations, mode durations, "within N seconds" windows and "on ball N" conditions never span a ball boundary. Two multiballs on different balls never count as "two multiballs within thirty seconds".
  • All durations are in milliseconds.

Next Steps