# Live data

Live data puts a real contest into your game as it happens: the score, the
clock, each side's numbers, and every moment, a goal, a try, a wicket, a card,
seconds after it happens. Any sport. Your page asks for it with one call,
`game.live()`, and draws it however it likes: a scoreboard, a momentum chart, a
3D pitch. Scoring a prediction from the live feed is not available yet.

Live data is open to Monterosa workspaces today. A deploy from any other
workspace that declares it is refused with `live_not_enabled`, and says how to
deploy without it.

## How a contest reaches a fan

```
  1  You switch it on: `gamestage live on`, then deploy.
        gamestage.yaml gains a live: section. Nothing else changes.
              |
              v
  2  A producer names the contest on the edition in Studio.
        Each edition of a live game has a "Live contest" setting.
              |
              v
  3  Gamestage follows that contest, by itself.
        It reads the provider and publishes what happens.
              |
              v
  4  Every fan's page gets it on the connection it already has.
        A page that opens late is sent the contest so far, at once.
```

Your game holds no provider key, makes no request to a provider and opens no
second connection. The contest arrives on the same Monterosa Interaction Cloud
connection the Gamestage library already holds for your game.

## Switch it on

In your game's folder:

```
gamestage live on
gamestage deploy <game> --dir .
```

`gamestage live on` adds this to `gamestage.yaml`, and leaves the rest of the
file, comments included, as it was:

```yaml
live:
  provider: sportmonks
```

The provider is where the contest's data comes from. SportMonks is the
provider today. `gamestage live off` takes the section out again, and the
same command with neither `on` nor `off` says which it is.

After the deploy, every edition of the game has a **Live contest** setting in
Studio. A producer types the provider's id for the contest there: for
SportMonks, the fixture id. Leave it empty and the game shows no live data. A
producer can change it at any time, and every page that is open moves to the
new contest without a reload.

## Draw it

`game.live()` hands over the contest the edition names:

```js
const game = await start();
const contest = game.live();

// The contest so far, at once, then every change.
contest.on("state", (state) => {
  scoreboard(state.sides.map((side) => `${side.short} ${side.score}`).join("  "));
});

// Only what happens from now on.
contest.on("moment", (moment) => {
  if (moment.kind === "GOAL") celebrate(moment.actor, moment.display);
});

// Connected, reconnecting, offline: for your own badge.
contest.onStatus((status) => badge(status.state));
```

What arrives, under each name you can listen to:

| Name | What it holds |
| --- | --- |
| `state` | The contest's name and status (`scheduled`, `running`, `paused`, `finished`), its period, and each side's name, short name, colour and score. |
| `clock` | Seconds since the start, what a fan reads (`34'`), and whether it is running. |
| `side` | One side's numbers, by the provider's names: `SHOTS_TOTAL`, `BALL_POSSESSION` and the rest. Called once per side. |
| `moments` | Every moment so far, oldest first. |
| `moment` | The newest moment, the instant it is seen. |
| `timeline` | Each side's numbers at each point in the contest, for a chart. |
| `health` | When Gamestage last heard from the provider, and whether that worked. |

The same values are there to read at any time: `contest.state()`,
`contest.clock()`, `contest.sides()`, `contest.moments()`,
`contest.timeline()` and `contest.health()`.

**The contest so far and news are different things.** A fan who opens your page
after a goal is sent that goal as part of the contest so far, and your page
should draw it in the score rather than celebrate it. A `moment` listener hears
only what happens after the page opened. Any other listener can ask for the
same with `{ news: true }`.

A page can follow a contest of its own choosing rather than the edition's:
`game.live({ contest: "19722153" })`.

## Try it locally

`gamestage dev --serve` plays a made-up contest for a game with live data, so
`game.live()` works on your laptop with no provider, no key and no Studio. The
two sides and everyone in them are invented. A whole contest takes about four
minutes; set `GAMESTAGE_LIVE_SPEED` to change how fast it runs.

`gamestage verify` checks that a game declaring live data ships the Gamestage
library, `gamestage.js`. Live data reaches a page only through it.

## When the connection drops

The library reconnects by itself and reports what it is doing through
`onStatus`: `connecting`, `live`, `reconnecting` with the attempt and when the
next one is, or `offline`. When it is back, the page is sent the contest as it
stands, so it catches up without replaying a moment it already showed. If
Gamestage cannot reach the provider, `health` says so with `ok: false`, and your
page can tell fans the data is delayed rather than letting a frozen score look
live.
