Build your game in Rive
Every game on nimiq.games is a .riv file. The store runs it, tells it what the player owns, and handles checkout when the player taps buy.
rive --publish.Start from the starter
- Download the starter source, open the folder and run
rive .to see it live. Setnameinrive.yaml: it names your built file. - Replace the art. Keep the
storeview model and the two events. - Sign in once with
rive login, then runrive pushonce. Push links the folder to a file in your Rive account; without it, every publish carries Rive's watermark. - Build with
rive . --publishand send usbuild/<name>.rivwith your application.
What the store fills in
Bind your art to these. Declare only what your game shows; anything missing is skipped.
| Path | What it holds |
|---|---|
store/boolean | True once the store has read the player's purchases |
store/boolean | A wallet is signed in |
store/boolean | The player owns the full game |
store/boolean | One per extra, in a view model named NGOwned |
store/boolean | Checkout is open over your game |
store/string | paid:<key>, cancelled:<key> or failed:<key> |
store/see below | The answer to your last store.fetch, in NGNet |
The store property sits on your main artboard's view model and holds a view model named NGStore. An extra's <key> is its type and key in camelCase: a Character with the key gold-skin is store/owned/characterGoldSkin, and your buy button fires store.buy with that same sku. The apply form shows the exact name next to each add-on.
What your file fires
| Event | Properties | What the store does |
|---|---|---|
store.buy | sku | Opens checkout for that extra, then updates store/* |
store.fetch | id, url, method, body | Calls your server, writes the answer to store/net |
Fire them from a listener on your button. Prices never live in the file: you set them when you apply.
Leaderboards and your own server
| Rule | Value |
|---|---|
| Where | Only the https origins you list when you apply |
| Methods | GET or POST with a JSON body |
| Sizes | Body up to 8 KB, answer up to 64 KB |
| Rate | 10 calls per 10 seconds per player, one at a time per id |
| Timeout | 10 seconds |
| Cookies | None are sent. Answer CORS for https://nimiq.games |
The store writes store/net/id, status, body and error, then increases store/net/seq last. Watch seq and read the rest. Scripts inside a .riv cannot reach the network on our player, so every call goes through store.fetch.
-- Leaderboard.luau in the starter: fill three rows when an answer lands
self.seq:addListener(function() show(self) end)
-- your server answers {"top":[{"name":"MATT","score":9001}, ...]}
Already calling your server from your own web page's JavaScript? That code does not come with you. Move each call into a store.fetch event and read the answer from store/net.
Signing
| Your file | On nimiq.games |
|---|---|
| No scripts | Plays as built |
Scripts, built with rive --publish | Plays, scripts run |
| Scripts, built without it | We reject it: the scripts would silently not run |
rive . --screenshot and rive . --once rebuild the file unsigned in the same place. Copy your published .riv somewhere safe before taking screenshots.
Runtime flags
| Flag | Default | Set it when |
|---|---|---|
gpuCanvas | on | Your file draws black with GPU Canvas on. Some do. |
artboard | the first | Your game is not the first artboard |
stateMachine | the first | Your game uses a different state machine |
fit | layout | You want contain, cover or fill |
Tell us these when you apply. We set them for your game.
Test it before you send it
rive . --verify # compiles your scripts
rive . --data=store/owned/characterGoldSkin=true # see the owned look
rive . --data=store/net/body='{"top":[{"name":"A","score":1}]}' --data=store/net/id=top --data=store/net/status=200 --data=store/net/seq=1
rive login && rive push # once: no watermark on your publishes
rive . --publish # the file you send us