API
This document describes Empirica's server, client and shared APIs.
Server
Empirica.onGameStart(callback)
Empirica.onGameStart(callback)
The onGameStart
callback is called just before a game starts, when all players are ready, and it must create rounds and stages for the game.
One (and one only) onGameStart callback is required for Empirica to work.
The callback receives one argument, the game
object, which gives access to the players
and the treatment for this game.
It also offers the addRound()
method, which allows to add a round to the game
. The returned Round object will implement the addStage(stageArgs)
method, which allows to add a Stage to the Round. The stageArgs
object to be passed to the stage creation method must contain:
duration
: the stage duration, in seconds
Note that the Game has not yet been created when the callback is called, and you do not have access to the other properties of the Game which will be created subsequently.
Example
Game Callbacks
Game hooks are optional methods attached to various events throughout the game life cycle to update data on the server-side.
Contrary to client side data updates, sever-side updates are synchronous, there is no risk of conflicting updates, and important calculations can be taken at precise points along the game.
``
Empirica.onRoundStart(callback)
Empirica.onRoundStart(callback)
onRoundStart
is triggered before each round starts, and before onStageStart
. It receives the same options as onGameStart
, and the round that is starting.
Example
Empirica.onStageStart(callback)
Empirica.onStageStart(callback)
onRoundStart
is triggered before each stage starts. It receives the same options as onRoundStart
, and the stage that is starting.
Example
Empirica.onStageEnded(callback)
Empirica.onStageEnded(callback)
onStageEnded
is triggered after each stage. It receives the current game, the current round, and stage that just ended.
Example
Empirica.onRoundEnded(callback)
Empirica.onRoundEnded(callback)
onRoundEnded
is triggered after each round. It receives the current game, and the round that just ended.
Example
Empirica.onGameEnded(callback)
Empirica.onGameEnded(callback)
onGameEnded
is triggered when the game ends. It receives the game
that just ended.
Example
Empirica.on(model, callback)
on(model, callback)
listens to the creation of new mode objects. Model objects are the "game", "round", "stage", "player" and "batch". The callback receives a ctx
object, and the model object listened to. In the example below, we're listening to new games, so we receive the game
as argument.
Beware, Empirica.on(model, callback)
is called on new objects and when the server restarts (when you run empirica
or the server restarts after a code change in development mode). You should add a check such as the one in the example above, where we check if initCalc
was already set at the top of the callback.
Empirica.on(model, attributeName, callback)
on(model, attributeName, callback)
listens on changes to the attribute of attributeName
on model
. Model objects are the "game", "round", "stage", "player" and "batch". The callback receives a ctx
object, the model object, and the attribute value. In the example below, we're listening on changes to choice
on the player
model.
We recommend using the value of the attribute given on the callback argument instead of doing .get(attributeName)
on the model object as the value could have changed changed asynchronously.
Empirica.flush()
Empirica.flush(): Promise<void>
adds the ability to manually flush changes outside of callback functions. This is useful when you want to make changes asynchronously without blocking the callback. For example, if you want to call an external API and update an attribute with the results, but your don't want to block the callback while waiting for the API to respond.
Empirica.flush()
will return a promise that resolves when the changes have been flushed. You can use await Empirica.flush()
to wait for the changes to be flushed. But you do not have to wait for the flush. It is only useful if you have multiple flushes in a single function, where you want to commit the changes in steps.
The simplest example:
An example with different use cases:
Note: if you perform blocking operations between changes, the changes before the blocking operations might be flushed before flushing manually. The change log is global, so while you're blocking, other changes might happen in a callback elsewhere, and that could trigger a flush of all latent changes. If you want to make a set of changes atomic, you should not do any blocking operations between them (i.e. use await
or Promises) or collect your changes in a local datastructure and apply them at once.
If you are starting an interval (setInterval
), you should make sure to clear it properly. Here's an example with an interval per game:
Or have a single interval that manages all games:
Server Objects
Game
object
Game
objectProperty | Type | Description |
---|---|---|
| Array of Player objects | Players participating in this Game. |
| Array of Round objects | This will return every round that makes up the game. |
| Array of Stage objects | This will return every stage that makes up the game. |
| The current Round. | |
| The current Stage. |
Round
object
Round
objectProperty | Type | Description |
---|---|---|
| Array of Stage objects | Stages composing this Round. |
| Game this round is a part of. |
Stage
object
Stage
objectProperty | Type | Description | |
---|---|---|---|
| Round this stage is a part of. | ||
| Game this stage is a part of. |
Player
object
Player
objectProperty | Type | Description |
---|---|---|
| String | The ID the player used to register (e.g. MTurk ID). |
| Round the player is currently in. | |
| Stage the player is currently in. | |
| Game the player is currently in. |
Client
See the Special Empirica Component page for more info.
Last updated