# Lua game package format A game package is a manifest plus Lua scripts and optional assets. Typical structure: ```text assets/games// manifest.json scripts/ main.lua state.lua ui.lua runtime_defs.lua assets/ image.png audio/ click.wav ``` ## Manifest modules Lua modules must be declared in `manifest.json`. ```json { "id": "template", "version": "0.1.0", "entry": "main", "modules": { "main": "scripts/main.lua", "state": "scripts/state.lua", "ui": "scripts/ui.lua", "runtime_ui": "runtime:runtime_ui.lua", "runtime_widgets": "runtime:runtime_widgets.lua", "runtime_commands": "runtime:runtime_commands.lua", "layout": "runtime:layout.lua" } } ``` Lua imports by module name: ```lua local ui = runtime.import("ui") local widgets = runtime.import("runtime_widgets") ``` Do not use `require`, `package`, `dofile`, `loadfile`, or `os`. ## Path rules Package-local module paths: ```text scripts/*.lua ``` Runtime shared module paths: ```text runtime:*.lua ``` `runtime:` paths must not contain `/`, `..`, or an empty filename. ## Base packages A game manifest can declare a `base` field to indicate it depends on a framework package: ```json { "gameId": "ludo", "base": "_framework", "modules": { ... } } ``` The `base` field is metadata. Actual loading is controlled by `RuntimeOptions.basePackages`: ```dart LuaGameWidget( gameId: 'ludo', runtimeOptions: const RuntimeOptions( basePackages: ['_framework'], ), ) ``` Loading order: 1. Base packages are loaded first, in `basePackages` order. 2. The game package is loaded last. 3. All modules are merged into a flat map. 4. Later packages override earlier packages on name collision. 5. The entry script always comes from the last (game) package. This means a game can override any framework module by declaring a module with the same name in its own manifest. ## Multi-package module resolution When Lua code calls `runtime.import("xxx")`: 1. Look up `xxx` in the merged module map (game modules first, then framework). 2. If not found, throw `FormatException: Lua module is not declared in manifest.modules`. Framework modules are transparent to game code: ```lua local app = runtime.import("app") -- resolved from framework local state = runtime.import("state") -- resolved from game ``` ## Entry module The manifest `entry` module should expose lifecycle/event functions expected by the script engine. Keep game-specific state in Lua modules and return runtime diffs/commands through the approved protocol. ## Runtime helper modules Shared helpers are provided by the runtime package: ```text assets/runtime/lua/runtime_ui.lua assets/runtime/lua/runtime_widgets.lua assets/runtime/lua/runtime_commands.lua assets/runtime/lua/layout.lua ``` Use helpers for authoring convenience, but remember helpers must normalize into supported Runtime protocol nodes/commands before Dart validation. ## Generated Lua definitions `runtime_defs.lua` files are generated from common definitions. After changing helper APIs, run: ```bash dart run tool/generate_lua_runtime_defs.dart ``` Check without rewriting: ```bash dart run tool/generate_lua_runtime_defs.dart --check ``` ## Package sources Host apps can load packages from three common sources: ```dart // Bundled app assets. AssetGamePackageRepository(runtimeOptions: runtimeOptions) // Local development directory, useful when images should not be bundled into app assets. FileGamePackageRepository( baseDirectory: 'E:/lua_packages', runtimeOptions: runtimeOptions, ) // Remote update server. RemoteGamePackageRepository( baseUri: Uri.parse('https://example.com/lua-packages/'), runtimeOptions: runtimeOptions, ) ``` A local development directory uses the same package layout as a downloaded remote zip: ```text E:/lua_packages/gomoku/ manifest.json scripts/ assets/ ``` ## Remote compatibility Remote manifests may include a `compat` block. The server should use request query values to return the newest compatible package, and the client validates the returned manifest again before download. ```json { "gameId": "gomoku", "version": "0.3.0", "packageUrl": "https://example.com/packages/gomoku-0.3.0.zip", "sha256": "...", "compat": { "runtimeApiVersion": 1, "minRuntimeVersion": "0.4.0", "maxRuntimeVersion": "0.4.9", "minHostBuild": 120, "platforms": ["windows", "android"], "channels": ["dev", "prod"] } } ``` `RemoteGamePackageRepository` sends these query parameters when fetching `remote_manifest.json`: ```text runtimeApiVersion runtimeVersion hostBuild platform channel ``` Configure them through `RuntimeOptions`: ```dart RuntimeOptions( runtimeVersion: '0.4.0', hostBuild: 120, platform: 'windows', channel: 'dev', ) ``` If compatibility fails, the remote package is not downloaded. The repository falls back to stable cache, previous stable cache, then bundled assets. ## Package validation A host repository can validate a game package with: ```bash dart run tool/check_runtime_package.dart assets/games/template packages/flame_lua_runtime/assets/runtime/lua ``` Within this package repo, example game packages live under: ```text example/assets/games/ ```