Skip to content

Overview ​

A game on VIMP is a plugin package: you supply the simulation core (Rust compiled to Wasm), the configs and the content (maps, images, sounds); the engine supplies the match rules (rounds, teams, votes, chat, panel, statistics), the client with rendering, sound and input, and the platform's lobby and catalog. This page is the model you need to write a game: what you provide, the version numbers, what a room does with your game and the constraints your code runs under.

The host is authoritative: the simulation of a match runs in one place and clients render the result, masking network latency with interpolation and prediction. The host role may move to another player during a match.

What you provide ​

ConcernYou (the plugin)The engine
Entities, movement, weapons, damage, AIthe Rust core—
Rendering (sprites, effects, particles)PixiJS partscanvases, camera, the render loop
Maps, models, weapons, sounds, texturesthe contentloading and playback
Rounds, scoring, teams, chat, votes, statistics, panelconfig, schemas and textsthe rules and mechanisms
Auth formfields and textsthe form shell and the identity
Bots ("scripted participants")the implementationlifecycle hooks

A plugin is a package with dist/manifest.json, two bundles (host and client) and a Wasm binary; the engine loads it at run time and never imports a plugin statically. The host browser is untrusted: anti-cheat is out of scope and the engine has no countermeasure today.

Version numbers ​

Two independent values:

ConstantValueMeaningChecked where
ENGINE_API_VERSION4, frozenGeneration label of the plugin contract (GameManifest, HostPlugin, ClientPlugin, WASM ABI, form schema). Not a gate: no plugin is rejected for its agenowhere at runtime; contract rule B2 checks it is consistent inside the package
SNAPSHOT_FORMAT_VERSION5Byte layout of the state frameinside the WASM core, both ends

A plugin publishes engineApi in three places that must agree with each other (a mismatch means a stale dist/): manifest.engineApi, hostPlugin.engineApi, clientPlugin.engineApi. Import ENGINE_API_VERSION from vimp-engine/config/opcodes.js rather than hardcoding 4. Agreeing with the installed engine is not required — a game built a year ago runs on today's build.

Compatibility is negotiated by capability, not by number: a game that cannot run without a specific engine feature lists it in the optional manifest.requires (see plugin-api.md). The engine rejects the plugin only when a name is unknown to its capability registry (capabilities), i.e. the game is newer than the engine; the platform lists such a game as unavailable in the lobby, while loadClientPlugin and the standalone SDK throw. The SDK reads the list from HostPlugin.requires / ClientPlugin.requires (solo mode has no manifest), so the same list is declared in all three places and kept equal — rule B2 refuses a package whose manifest and halves disagree. A manifest with no requires needs nothing beyond the base contract.

manifest.id is the game's identity on the platform: it must equal the id the game is registered under (submitting.md); a mismatch makes the game silently disappear from the lobby.

What a room does with your game ​

  1. Create. A logged-in user fills the room form (fields from manifest.roomForm, defaults from manifest.roomDefaults); maxPlayers is clamped to the game's roomDefaults.maxPlayers.
  2. Boot. The host plugin is loaded, the required gameConfig fields are validated, the engine defaults are merged with gameConfig and the room overrides, and the core is built from the result.
  3. Join. A player picks the room and receives the client config and the first snapshot (snapshot.md).
  4. Play. The simulation steps at a fixed rate and a binary state frame goes out every Nth tick; meta events (chat, votes, panel, stat, sounds, informs) travel as JSON.
  5. Rotate. A round ends on a team wipe (the timer expiring starts a new one without a result); the map rotates when the map timer expires and a system vote picks the next one (host-meta.md).
  6. Host change. The room lives while it has people (lobby mode): when the host leaves, lags, hides its tab or is voted out, another member takes the room over. A game with gameConfig.migration.midRound: true continues from the same tick; any other game migrates softly — meta kept, the round restarts, the physics world is not carried. Dedicated and standalone hosts never change.

Threading constraints ​

The host plugin and everything it imports run in a Web Worker: no window, document, DOM APIs or PixiJS — only isomorphic APIs (Date, Math, performance, setTimeout, queueMicrotask, fetch). The client plugin runs on the main thread and may use PixiJS, but PixiJS must stay a single shared instance supplied by the engine (plugin-api.md, contract rule A1), never bundled into the plugin.

Latency hiding ​

Three mechanisms smooth the network; all live in the client core, the ClientCore Wasm class from the same Rust binary (details — client-api.md, ABI — core.md):

  • Interpolation (provided by vimp-engine-core): frames are buffered and the world renders in the past (serverNow − 100 ms); events are emitted exactly once, positions are interpolated.
  • Prediction (your core, e.g. @vimp-games/tanks): the local entity is simulated by a replica of the authoritative motion model (formulas shared with the game's core); the host confirms input (lastInputSeq), reconciliation replays unconfirmed input, and the discrepancy decays smoothly.
  • Client-side shot spawning (your core): a shot is seen and heard instantly; duplicates from the host are suppressed by author id.

Key invariants ​

  • Source of truth for ports — vimp-engine/src/config/wsports.js; for the binary format version — vimp-engine/src/config/opcodes.js; for snapshot keys — the game's own schema, supplied through HostPlugin.gameConfig.snapshot (see plugin-api.md).
  • Motion replica parity: authoritative motion and the client prediction replica must share the tick formulas — this is the game's concern (a cargo parity test over client::predictor in the game's crate); the engine only provides the generic Predictor<G>/interpolation machinery.
  • A single numeric id space for humans and scripted participants (bots); distinguished via isScripted/isNetworked. The core operates on numeric ids, meta keys by string — the conversion happens at the GameCoreAdapter boundary.

← Back to the SDK index · Next: Scaffolding a game plugin (create-vimp-game) →

vimp-engine 0.38.1 · vimp-engine-core 0.23.2 · create-vimp-game 0.5.4