Interview questionnaire
How to run this interview
Conduct the interview in whatever language the user writes to you in — translate the questions on the fly.
Rules:
- Read the SDK documentation first (order:
README.md). Half the value of this interview is your ability to say "the engine already does that" instead of letting the user design a feature that cannot exist. - One block at a time. Ask a block, get the answers, confirm your understanding in one sentence, move on. Never dump all 14 blocks at once.
- Every question has a "same as tanks" default. State it, and let the user accept it with a single word. Blocks 1, 5, 6, 7, 10 are the ones that really define the game — spend the time there.
- Push back immediately on impossible answers. The list of things that do not exist is at the end of
pitfalls.md. Offer the nearest workable alternative in the same message. - Skip what is already settled. If the user described the game in the first message, do not re-ask; confirm instead.
- After the last block, write the design document and get confirmation before generating anything.
Block 1. Game identity and package
- What is the game called? A short
id(Latin letters, no spaces) is needed — it becomesmanifest.id, the URL segment/games/<id>/and must match the identifier the game is registered under on the platform. In tanks —tanks. - The npm package name? The convention is
@vimp-games/<id>; your own scope is also fine. In tanks —@vimp-games/tanks. - The display title (
manifest.title) — what the player sees in the lobby. In tanks —VIMP Tanks. - In one paragraph: what kind of game is it, what genre, what does the player do in a match? (Needed to choose the shape of entities and the snapshot.)
Block 2. Room and lobby
- What is the target room size (
roomDefaults.maxPlayers)? Note that the platform clamps it to 8 anyway. In tanks — 8. - Which fields belong in the server creation form? Important: the host accepts only
maxPlayers,map,roundTime,mapTime,friendlyFire(plus the map catalog). Any custom form field is accepted by the form and silently dropped — a setting that affects the rules cannot be passed through the room form. If such a setting is needed, we will discuss how to bake it into the game config or the core logic. - The form controls are
text,select,checkbox,radio;range,number,toggleandsegmentedare permanent aliases of them (see Form schema). Numeric bounds are set by themin/maxfields and the auto-generatedregExp. Is that fine? - Is friendly fire (
friendlyFire) on or off by default? In tanks — off (false); the player can switch it when creating a room. - The default round and map duration? In tanks — 120 s and 600 s; custom values are clamped to 10 s … 3600 s.
Block 3. Sign-in screen (auth)
- What does the player choose before entering a match? (Model/class/colour/ faction — any set of fields.) The nick is not among them: the name comes from the lobby JWT. In tanks — only the tank model.
- The screen texts: the title and the hint block (usually the controls layout — "keys → action" pairs). Give the wording.
- Which fields must be remembered between sessions (
storage) and which must be checked on the host side (validator)? In tanks — the model is remembered and validated against the model list.
Block 4. Match rules, chat, votes
- A round ends when the whole opposing team is destroyed — that is an engine rule. Does that fit? If the game is not round-based, we will discuss workarounds (one long "session", a single playing team, respawn inside the core).
- What happens to a player after death? In the engine they become a spectator of the killer until the end of the round. Whether a respawn within the round is needed is implemented in your Rust core, not in the config.
- Is a post-respawn invulnerability window needed? The engine has none — it is implemented in the core (a flag on the actor + a check in the damage calculation).
- Votes: which types are needed besides the built-in
teamChangeandmapChange? Note the 30 s category cooldown, the 10 s voting window, one active vote at a time, and ties resolved randomly. The engine has no kick-player vote, no/banendpoint and no server rating (/like//unlike). - Which chat commands does the game need? The engine parses none of its own — the game declares the whole set (
HostPlugin.chatCommands). The usual minimum:/rank,/nr(dev), plus/timeleftand/mapnameif the round and the map are finite. Tanks has all four and its own/bot; snakes has only/nr,/rankand/bot. - Which system chat messages does the game add? The group letters
s,v,m,care taken by the engine — choose your own (in tanks it isb). The texts with{0},{1}placeholders are needed. - The maximum chat message length? The default is 60 characters.
- Does the kick policy need changing? Defaults: inactivity 120 s for a player (off for a spectator), kick on average latency > 1000 ms, kick after 5 missed pings, a ping every 3 s, an inactivity check every 30 s.
Block 5. Controls and prediction
- The full list of player actions with assigned keys. For each: hold (movement) or one-shot (a shot, a weapon switch)? One-shot actions are consumed once per tick,
upis ignored. - What do the spectator keys do? The mandatory minimum is
nextPlayerandprevPlayer. In tanks —n/p. - Is client-side movement prediction needed (an instant response of your own actor)? Without it the controls feel delayed by ~100 ms + ping. Limit: the predicted state is exactly 8 ×
f32. List which values go into it. In tanks — x, y, hull angle, gun angle, vx, vy, engine load, state. - Is shot prediction needed (an instant visual effect before the authoritative event arrives)? If so, de-duplication by the author id in the event block is required.
Block 6. Weapons and combat mechanics
- The list of weapons/abilities. For each: the hit type (an instant ray, a projectile in flight, a radial explosion, something else), damage, range, spread, impulse, camera shake.
- Reload is set by the
fireRatefield in seconds (0 — no cooldown). Give the values. - Ammunition: the starting ammo values are set by the panel field values (
panel.fields[*].value) — the same number goes into the core as the starting resource. How much of what? - Are there actor models/classes with different characteristics? For each: dimensions, acceleration, maximum speed, turning, damping, density/friction/restitution, starting weapon.
- Are game events needed that the engine does not understand (picking up an item, capturing a point, scoring outside kills)? Such events go through
custom→HostPlugin.onCoreEvent.
Block 7. Snapshot and entities
- List all the entities the client sees: players, projectiles, effects, dynamic map objects.
- For each entity — which fields to send and with what precision (
f32,u8,u16,u32)? Which of them to interpolate (lerp), which as an angle (lerpAngle), which discretely? - For each entity — the block shape:
indexed8— up to 255 addressable objects (players);indexed32— many short-lived objects with an id (projectiles in flight);list16— anonymous one-off events (tracers, explosions);indexedNoNull8— fixed slots (map dynamics). Limit: onlyindexed8andindexedNoNull8go into the hot buffer (smooth rendering every frame). Everything else arrives as events.
- In every weapon event block the last field is the id of the shot's author (needed to suppress duplicates of the predicted effect). Confirm.
Block 8. HUD, statistics, sounds, inform messages
- Which resources does the panel show? For each: the cell type —
bar(a bar, 30 blocks by default, 100 at most),value(a number),weapon(the active one is highlighted),time(M:SS). The round-time cell is mandatory and hard-bound to the keyt. - The statistics table: the engine writes exactly five values —
name,status,score,deaths,latency(this is the "ping"). The engine will not fill custom columns. Column headings and a (numeric) sort order are needed. Team aggregates are computed by the methods'='(replace),'+'(accumulate),'#'(count rows). - Sounds: the full list with priorities (more important — higher; the limit is 30 simultaneous spatial voices) and volumes. Each sound needs a
webm+mp3pair. - The mapping of engine events to your sounds:
roundStart,victory,defeat,frag,death. Which of them are needed? - The texts of the three inform messages (the indexes are fixed): a team's victory (
{0}— the team), round start, game over. In tanks —{0} WINS!,ROUND START!,GAME OVER!. - Is a vote right after entering the match needed (
initialVote)? In tanks — the team choice. If the game has a single team and no spectators as a phenomenon, the answer is "no": instead ofinitialVote/spectatorTeamdeclaregameConfig.noSpectators: true(exactly one team, entry straight into it), andmodules.votemay be omitted altogether. If the game's round is endless, addgameConfig.endlessRound: trueas well.
Block 9. Bots (scripted participants)
- Are bots needed? If so, how should they behave (look for the nearest opponent, patrol, hold a position)? Describe the desired behaviour, not numeric constants: the AI decision rate and other parameters are Rust core code, not a config setting.
- How are bots created: by a chat command, by a vote, automatically when there are too few players? In tanks — the
/botcommand with a vote. - The bot name prefix and the default model?
Block 10. Maps and environment
- How many maps are needed in the first version, and of what size (in tiles)?
- Tiles: what is the tile size (
step) and which indexes make a wall (physicsStatic)? The tile images are shipped by your package — the engine serves no game images at all. Which tile-sheet files do you put inassets/img/(from there they land indist/img/and reach the client as${assetsBase}img/<file>)? - Render layers: which tile indexes on which layer (
layers)? - Are dynamic objects (crates, barrels) needed? For each: position, size, density, image, layer, damping. The image is also a file from your package's
assets/img/. - Respawn points: their number per team is the hard capacity of that team on the map. How many points per team? (It must cover
maxPlayers.) - The map scale (
mapScale, andscaleof an individual map if needed)? In tanks — 0.3, some maps have their own. - How many maps to offer in the rotation vote (
mapsInVote)? In tanks — 4.
Block 11. Canvases, camera and client UI
- How many canvases are needed? In tanks — the main one (
vimp) and the radar (radar). For each: the initial size, the aspect ratio (aspectRatio) or a fixed size (fixSize), the base scale (baseScale). - Is a dynamic camera needed (a forward offset by speed + zoom-out) and camera shake on hits/explosions?
- Which DOM elements to show after authorization (
initIdList)? Usually — all canvases pluspanelandchat. - Are there custom styles (team colours, panel styling)? The game's CSS is handed to the engine as a string. Note that the engine CSS is designed for five statistics columns and the classes
.line1–.line3. - Will there be particles/effects in large numbers (smoke, sparks)? If so, we plan an object pool, otherwise the garbage collector will be noticeable.
- Which parts need the
rendererandsoundManagerservices? (The service pool is fixed to these two.)
Block 12. Network assumptions
- Which entities must look smooth (interpolation/prediction), and which are fine to show on an event?
- Frame send rate: by default every 4th simulation tick — 30 frames/s at a 120 Hz tick. Keep it?
- Does the game have pickups, destructible objects or capturable points? The engine has none — they are modelled with snapshot keys (usually
indexedNoNull8orindexed8) plus core logic. Needed? - What must be delivered reliably (events, shake), and what may be lost (positions)? By default a frame goes over the reliable channel if it contains events,
forceResetor shake. - Must the game survive a host change in the middle of a round (the new host continues from the same tick)? Yes —
gameConfig.migration.midRound: true, and then the whole core state must be inGameSim::serialize, and stateful host modules needserializeState/restoreState. No — on a host change the round restarts (the soft mode, the default).
Block 13. Progression
- Is progression between matches needed? The engine stores
rank(an integer, a format common to all games) andstate(arbitrary JSON of your game). - The engine computes
rankitself: +1 per frag, −1 per team kill. There is no hook to change this rule. Is that acceptable? - What to put into
stateand what a new player starts with (playerState.defaultState)? The data is written fromonCoreEventand synchronised at round end, on map change and when the player leaves.
Block 14. Process
- Is a
cargomovement parity test needed (the predictor against the authoritative simulation)? Strongly recommended — without it a prediction divergence is only detected by eye in the game. - Is a JS↔WASM integration harness needed (tests that run the real core through
pkg-node)? - Localisation: the engine does not support switching the language at runtime — all texts (chat, panel, votes, informs, the sign-in screen) are set as one set in the config. In which language do we write the texts?
- How do you run the game locally during development?
npm run dev(a standalone tab with bots) andnpm run simwork without an engine checkout. Is a custom dev harness needed beyond the templatedev/main.js? - Do we publish
entries.wasmNode(the path tocore/pkg-node) in the manifest? Strongly recommended: without it the game cannot be run through the engine's headless runner (npm run sim), and all contract checks would have to be done by eye in the browser. Seedebugging.md. - Which debug-run scenarios do we write right away (movement, shooting, death/respawn, round end, map change, a vote)? A scenario is plain JSON, the format is in
debugging.md. - Do we implement
GameClientDef::predicted_state(optional, defaultNone) for a component-wise prediction-divergence report? Without it a coarse comparison by the camera works.
Answer → artifact map
| Block | Produces |
|---|---|
| 1 | package.json, scripts/build-game-manifest.js, id/title in both plugins |
| 2 | gameConfig.roomDefaults, gameConfig.roomForm, parts.friendlyFire, timers.roundTime/mapTime |
| 3 | src/config/auth.js (elems, texts, params, validators) |
| 4 | gameConfig.teams/spectatorTeam (or noSpectators, then without initialVote, spectatorTeam and modules.vote), endlessRound, chatMaxLength, idleKickTimeout/rtt, src/host/systemMessages.js, chat commands, client modules.chat.params.messages, modules.vote.params |
| 5 | gameConfig.playerKeys, client modules.controls.keySetList, spectatorKeys, core apply_input, predictor.rs, PLAYER_STATE_LEN layout |
| 6 | src/data/weapons.js, src/data/models.js, core weapon modules, onCoreEvent for custom events |
| 7 | src/config/snapshot.js, Rust build_snapshot_blocks rows, client parts.gameSets |
| 8 | gameConfig.panel + client modules.panel, gameConfig.stat + client modules.stat, src/config/sounds.js, gameConfig.soundCues, gameInform.list |
| 9 | gameConfig.scripted, src/host/<Bot>Manager.js, createModules, bot chat command, core/src/bots/ |
| 10 | src/data/maps/*.js, gameConfig.maps/currentMap/mapScale/mapSetId/mapsInVote, map parts |
| 11 | client modules.canvasManager.canvases, initIdList, styles, parts.bakedAssets, parts.componentDependencies |
| 12 | snapshot kind/class decisions, timers.networkSendRate, extra snapshot keys for pickups/objectives, gameConfig.migration.midRound + GameSim::serialize coverage + module serializeState/restoreState, a checkpointRestore step in the sim scenario |
| 13 | gameConfig.playerState.defaultState, onCoreEvent state writes |
| 14 | vitest.config.js projects, core/src/client/predictor.rs parity tests, tests/, the dev/ harness, entries.wasmNode in the manifest, debug scenarios for npm run sim, optional predicted_state |