{"doc":"live-data","title":"Live data","markdown":"# Live data\n\nLive data puts a real contest into your game as it happens: the score, the\nclock, each side's numbers, and every moment, a goal, a try, a wicket, a card,\nseconds after it happens. Any sport. Your page asks for it with one call,\n`game.live()`, and draws it however it likes: a scoreboard, a momentum chart, a\n3D pitch. Scoring a prediction from the live feed is not available yet.\n\nLive data is open to Monterosa workspaces today. A deploy from any other\nworkspace that declares it is refused with `live_not_enabled`, and says how to\ndeploy without it.\n\n## How a contest reaches a fan\n\n```\n  1  You switch it on: `gamestage live on`, then deploy.\n        gamestage.yaml gains a live: section. Nothing else changes.\n              |\n              v\n  2  A producer names the contest on the edition in Studio.\n        Each edition of a live game has a \"Live contest\" setting.\n              |\n              v\n  3  Gamestage follows that contest, by itself.\n        It reads the provider and publishes what happens.\n              |\n              v\n  4  Every fan's page gets it on the connection it already has.\n        A page that opens late is sent the contest so far, at once.\n```\n\nYour game holds no provider key, makes no request to a provider and opens no\nsecond connection. The contest arrives on the same Monterosa Interaction Cloud\nconnection the Gamestage library already holds for your game.\n\n## Switch it on\n\nIn your game's folder:\n\n```\ngamestage live on\ngamestage deploy <game> --dir .\n```\n\n`gamestage live on` adds this to `gamestage.yaml`, and leaves the rest of the\nfile, comments included, as it was:\n\n```yaml\nlive:\n  provider: sportmonks\n```\n\nThe provider is where the contest's data comes from. SportMonks is the\nprovider today. `gamestage live off` takes the section out again, and the\nsame command with neither `on` nor `off` says which it is.\n\nAfter the deploy, every edition of the game has a **Live contest** setting in\nStudio. A producer types the provider's id for the contest there: for\nSportMonks, the fixture id. Leave it empty and the game shows no live data. A\nproducer can change it at any time, and every page that is open moves to the\nnew contest without a reload.\n\n## Draw it\n\n`game.live()` hands over the contest the edition names:\n\n```js\nconst game = await start();\nconst contest = game.live();\n\n// The contest so far, at once, then every change.\ncontest.on(\"state\", (state) => {\n  scoreboard(state.sides.map((side) => `${side.short} ${side.score}`).join(\"  \"));\n});\n\n// Only what happens from now on.\ncontest.on(\"moment\", (moment) => {\n  if (moment.kind === \"GOAL\") celebrate(moment.actor, moment.display);\n});\n\n// Connected, reconnecting, offline: for your own badge.\ncontest.onStatus((status) => badge(status.state));\n```\n\nWhat arrives, under each name you can listen to:\n\n| Name | What it holds |\n| --- | --- |\n| `state` | The contest's name and status (`scheduled`, `running`, `paused`, `finished`), its period, and each side's name, short name, colour and score. |\n| `clock` | Seconds since the start, what a fan reads (`34'`), and whether it is running. |\n| `side` | One side's numbers, by the provider's names: `SHOTS_TOTAL`, `BALL_POSSESSION` and the rest. Called once per side. |\n| `moments` | Every moment so far, oldest first. |\n| `moment` | The newest moment, the instant it is seen. |\n| `timeline` | Each side's numbers at each point in the contest, for a chart. |\n| `health` | When Gamestage last heard from the provider, and whether that worked. |\n\nThe same values are there to read at any time: `contest.state()`,\n`contest.clock()`, `contest.sides()`, `contest.moments()`,\n`contest.timeline()` and `contest.health()`.\n\n**The contest so far and news are different things.** A fan who opens your page\nafter a goal is sent that goal as part of the contest so far, and your page\nshould draw it in the score rather than celebrate it. A `moment` listener hears\nonly what happens after the page opened. Any other listener can ask for the\nsame with `{ news: true }`.\n\nA page can follow a contest of its own choosing rather than the edition's:\n`game.live({ contest: \"19722153\" })`.\n\n## Try it locally\n\n`gamestage dev --serve` plays a made-up contest for a game with live data, so\n`game.live()` works on your laptop with no provider, no key and no Studio. The\ntwo sides and everyone in them are invented. A whole contest takes about four\nminutes; set `GAMESTAGE_LIVE_SPEED` to change how fast it runs.\n\n`gamestage verify` checks that a game declaring live data ships the Gamestage\nlibrary, `gamestage.js`. Live data reaches a page only through it.\n\n## When the connection drops\n\nThe library reconnects by itself and reports what it is doing through\n`onStatus`: `connecting`, `live`, `reconnecting` with the attempt and when the\nnext one is, or `offline`. When it is back, the page is sent the contest as it\nstands, so it catches up without replaying a moment it already showed. If\nGamestage cannot reach the provider, `health` says so with `ok: false`, and your\npage can tell fans the data is delayed rather than letting a frozen score look\nlive.\n"}