Skip to content

Your first bundle

A bundle is a folder with index.html at its root, zipped. This page takes the playground’s sample game through every step; your own game follows the same path.

The playground loads a bundle in an iframe, plays each host around it and logs every SDK call and host message. Open developers.filudi.com/playground/: nothing to install. If you have the Filudi repository, the same app runs locally from apps/sdk-playground.

Run the playground from the repository

Terminal window
pnpm install
pnpm --filter @filudi/game-sdk build # the sample game loads dist/filudi-sdk.iife.js
pnpm --filter @filudi/sdk-playground dev # http://localhost:5173

Pick “Sample game” and the filudi host, press Load. The log shows the handshake: filudi:ready from the bundle, filudi:init from the host with a game token that starts with fg1., then filudi:loaded when the game calls ready(). Tap the canvas: filudi:gameplay { running: true }. Fall: the score goes out, the board comes back, every third fall asks for an interstitial and the host answers with the result you set. Tick “Mock API” so saves and scores hit an in-memory API and show as requests with their bearer. Untick it to use a real API: type its origin in “API URL”, or open the playground with ?api=: /playground/?host=filudi&mockApi=0&api=https://api.filudi.com for production, api=http://localhost:8080 for the API you run. That API must list the playground’s origin in its HUB_URLS, and the game slug must exist on the site, else the log says game token refused.

The other hosts install the portal’s global in the frame before the bundle runs: CrazyGames.SDK, PokiSDK, Playgama’s bridge. Each call the game makes is a game→host line, each answer a host→game line. Change the ad outcome, the data module, the leaderboard type, and watch what the game does.

The sample game is two files, index.html and main.js (apps/sdk-playground/public/sample-game/ in the repository). main.js calls every SDK method at least once; it is the shortest complete integration. Read it next to the SDK overview.

The sample game's boot, in short

const sdk = window.FiludiSDK;
async function boot() {
await sdk.init({ gameId: 'infinijump', version: '1.0.0' });
corner = { nickname: sdk.player.get().nickname, locale: sdk.i18n.locale() };
applySave(await sdk.save.get());
sdk.save.on('replaced', applySave);
sdk.ads.on('start', () => (muted = true));
sdk.ads.on('end', () => (muted = false));
requestAnimationFrame(frame); // the first frame calls sdk.ready()
}

Run your dev server, choose “Bundle URL”, paste http://localhost:5174/index.html, pick a host, press Load. The hosted playground frames a localhost URL from its https page: browsers treat localhost as secure. Your server must let the playground’s origin frame it, https://developers.filudi.com (or http://localhost:5173 when you run the playground from the repository): no X-Frame-Options: DENY, and frame-ancestors must list it if you send a CSP. The portal hosts fetch your index.html, so the server answers that origin with CORS. Vite’s default CORS allows localhost origins only: add server: { cors: { origin: ['https://developers.filudi.com'] } } to its config while you test.

Add <script src="https://developers.filudi.com/playground/sdk-trace.js"></script> after the SDK script while you work (http://localhost:5173/sdk-trace.js for the local playground): the log then shows each SDK call. Remove it before the build.

Tick “Production CSP” on the filudi host. The playground’s server then sends the policy filudi.com applies to bundles it serves itself, and the log gives you the header to send from your own server. Each violation shows as a CSP violation line. An inline script or a call to another host works without it and fails in production.

Build, then zip the content of the build folder, not the folder itself. index.html must be at the root of the archive. Leave the source maps out unless you want them public.

Zip the build

Terminal window
npm run build
cd dist && zip -r ../gloop-1.4.0.zip . -x '*.map' && cd ..
unzip -l gloop-1.4.0.zip | head -5 # index.html first, at the root

Limits: 200 MB for the zip, 500 MB unpacked, 10,000 files, 64 MB per file, no .. in a path, no symbolic link, no encrypted entry. The upload refuses a zip that breaks one and names the entry.

In the console, open the game, Versions, “Upload a version”. The version string follows semantic versioning (1.4.0, 2.0.0-beta.1) and is unique for the game. Pass the same string to init({ version }) in the build so events carry it. The browser uploads the zip straight to the files service; the console then finalizes: the API unpacks the zip under games/<slug>/<version>/, checks index.html, counts files and bytes. The version is a draft.

“Preview” opens a signed link, valid one hour, in a new tab. It serves the draft from the preview host under the same CSP as production, at the top level. The SDK is on its mock there: check loading, layout and play. Saves and scores need the frame on filudi.com; the playground covers them.

When the draft is right, press “Request review” on the version. Staff gets an email, plays the preview and activates it. A pending game needs its approval first; the overview says which step is next. Once the version is live, open the game on filudi.com and play it once: saves, the leaderboard and your statistics now run against production.

Game lifecycle on filudi.com explains what happened in that frame. The release checklist is the list to run before every zip.