← All documentation

What your game can and cannot do

Read this first. It explains the one rule everything else follows from, and why the SDK is shaped the way it is.


Your game never decides who wins

Your game reports what the player did. The platform decides what that was worth.

This is not a policy we ask you to respect. It is enforced by the architecture, and it is enforced against first-party games in exactly the same way as third-party ones.

What your game is given

At init your game receives precisely three things:

{ seed: 1234567, tickRate: 60, config: { /* your own settings */ } }

That is all. Your game is never told:

  • the stake, or that money is involved at all
  • a player's balance or wallet
  • who the other players are, or how they are doing
  • the prize, the payout split, or the platform's fee

The manifest validator rejects a bundle whose config so much as mentions stake, balance, wallet, payout or prize. There is nothing to leak because nothing is sent.

What your game reports

{ type: "complete", finalTick: 3600, displayScore: 4200, inputs: [ ... ] }

displayScore is display only. It is what your game shows the player while they play. The platform does not use it to pay anybody.

What the platform does with it

The server takes the seed it issued and the inputs you submitted, runs your game again from scratch, and uses the score it calculates.

    your device                          our server
    ───────────                          ──────────
    seed 1234567  ──────────────────────▶ seed 1234567
    player plays                          replays your inputs
    inputs recorded ────────────────────▶ derives the score
    displayScore: 4200                    score: 4200  ← this one is paid

If the two disagree, the run is rejected. Not averaged, not disputed — rejected. A client claiming a score its inputs do not produce simply does not get paid.


Why cheating does not work

Editing the score in memory. The score you send is ignored. Only your inputs matter.

Sending fabricated inputs. They are replayed. Fabricated inputs produce whatever score those inputs actually produce — which is the honest answer to a dishonest question.

Modifying the game bundle. The player's copy is not what the server replays with. The server uses the certified bundle.

Slowing time down or speeding it up. The score comes from replaying the input log through a fixed tick count, so a device that runs slowly produces the same result, just later. Note that this is a statement about the score, not about how long a player gets: a timed round is a number of ticks, and running them faster than the declared rate would shorten it. That is what createLoop exists to prevent — see below.

Calling home for help. The sandbox blocks all network access.

Reading or writing storage. The sandbox blocks that too.

Reaching the hosting page. The frame is sandboxed without allow-same-origin, so there is no page to reach.


What this means for how you build

Three rules follow from all of the above, and certification enforces every one:

1. Use the SDK's random generator, never the engine's

Math.random(), Defold's math.random(), GDevelop's random expressions — none can be reproduced. Five players in a pool must face the identical puzzle, and the server must be able to rebuild it. Use the seeded generator the SDK gives you.

2. Drive your simulation from ticks, never from elapsed time

Never delta, dt, Date.now() or performance.now() in game logic. One tick is one step, regardless of how long the frame took. Rendering may use frame time freely — the distinction is between what the player sees and what the game decides.

But a clock still decides how many ticks to run, and you must let it. These are two different questions and conflating them is the single most expensive mistake on this page:

May read a clock?
What happens on tick n Never. This is what the server replays
How many ticks have happened by now Yes. The server does not replay this at all

The trap is requestAnimationFrame, which fires at the display's refresh rate. One tick per callback gives 60 ticks a second on a 60Hz screen and 144 on a 144Hz one — the same game, at two and a half times the speed, with a timed round over in 40% of the time. Every first-party game here shipped that way, and Snake ran at 24 cells a second on a gaming monitor against 10 on a phone.

Use createLoop from the SDK. It spends elapsed time on whole ticks, so your tick rate is the one your manifest declares whatever the display does — and your update still never sees a clock, so the replay is unchanged.

3. Make your score an integer

Floating-point results can differ in the last bit between devices. Two numbers that are "nearly equal" are not equal, and the comparison is exact.


Hidden information

For real-time games where players hold private state — a hand of cards — the server holds it, and each player is sent only their own view.

Your game implements viewFor(state, playerId), returning what that player may see. A card game returns the player's own hand and only the count of everyone else's. The engine never broadcasts raw state, so a game that forgets to redact leaks nothing by default; it simply shows nothing.


Practice mode

Practice matches run the identical code path. There is no separate "free" build, no relaxed verification. The only difference is that no money moves.

That is deliberate: a game that behaves differently in practice than in a real match is a game whose testing tells you nothing.


Related