Files
flutter_lua_runtime/docs/architecture.md
gem 8ddc3be3a7 feat: multi-package loading with base framework support
- Add RuntimeOptions.basePackages for loading framework packages before game package
- Add ScriptEngine.loadPackages() for multi-package module merging
- LuaDardoScriptEngine merges modules from all packages, game overrides framework
- PackageActivationController loads base packages first, then game package
- GamePackageManifest parses optional 'base' field
- Update docs: README, quick-start, lua-package-format, architecture
- Update all test mocks with loadPackages() implementation
2026-06-10 00:04:00 +08:00

4.0 KiB

Architecture

flame_lua_runtime separates generic runtime infrastructure from game-specific Lua packages.

Boundary

RuntimeEvent -> Lua -> GameDiff / RuntimeCommand -> Flame
  • Dart receives input, lifecycle, resource, and host events.
  • Dart forwards white-listed runtime events to Lua.
  • Lua returns a GameDiff and/or RuntimeCommand list.
  • Dart validates and applies diffs/commands to Flame-owned components.

Responsibilities

Dart / Flutter / Flame owns

  • Rendering tree and Component lifecycle.
  • Resource loading and cache ownership.
  • Audio playback.
  • Spine and particle object creation.
  • Input/event dispatch.
  • Package validation and activation.
  • Runtime command execution.
  • Diagnostics and error reporting.

Lua owns

  • Game state transitions.
  • UI tree description.
  • Layout calculation.
  • Event handling decisions.
  • Command/node table construction.

Lua should only describe desired runtime state. It must not receive or retain Dart/Flame object references.

Key Dart areas

lib/runtime/models/       Protocol data models
lib/runtime/protocol/     Protocol validation/parsing
lib/runtime/scripting/    Lua engine boundary
lib/runtime/packages/     Manifest/package loading and activation
lib/runtime/rendering/    Flame render tree adapter
lib/runtime/commands/     Runtime command execution
lib/runtime/resources/    Resource loading and cache management
lib/runtime/events/       Event dispatch and gates
lib/runtime/lifecycle/    Async/session/task safety

Package loading

Game packages are manifest-driven. Bundled assets and remote/downloaded packages should flow through the same package abstraction where possible.

Shared Runtime Lua modules are loaded from RuntimeOptions.runtimeLuaRoot.

Default source-tree root:

assets/runtime/lua

Package dependency root:

packages/flame_lua_runtime/assets/runtime/lua

Multi-package loading

The runtime supports loading multiple packages in sequence, with module merging:

RuntimeOptions.basePackages = ['_framework']
  -> load _framework package (modules: app, diff, ids, net, ...)
  -> load game package (modules: state, rules, main, ...)
  -> merge into flat _moduleScripts map
  -> game modules override framework modules on name collision
  -> execute game entry script

Key classes:

  • RuntimeOptions.basePackages — ordered list of framework package IDs.
  • PackageActivationController._prepareCandidate() — loads base packages first, then game package, passes combined list to ScriptEngine.loadPackages().
  • LuaDardoScriptEngine.loadPackages() — iterates all packages, merges manifest.modules into _moduleScripts, executes entry from last package.
  • GamePackageManifest.base — optional metadata field declaring framework dependency.

Module resolution is flat: runtime.import("xxx") looks up _moduleScripts[xxx]. Game modules and framework modules share the same namespace. Later-loaded packages win on collision.

Safety model

  • Lua module loading is manifest-declared.
  • runtime.import(moduleName) is the only intended module import API.
  • runtime: module paths are restricted to runtime:*.lua.
  • Package-local scripts are restricted to scripts/*.lua.
  • Runtime protocol fields are white-listed.
  • Invalid diffs/commands should fail clearly instead of being silently ignored.

Extension guidance

When adding features, prefer this order:

  1. Lua helper composition if it can be represented by existing nodes/commands.
  2. Generic protocol field if multiple games need the capability.
  3. Generic Runtime node/command if it requires Dart/Flame ownership.
  4. Avoid game-specific Dart code.

Examples of valid generic runtime features:

  • listView node.
  • particle node.
  • toast command.
  • copy_text command.
  • play_spine_animation command.
  • Button image states.

Examples that should stay Lua-side:

  • Game board rules.
  • Game-specific dialog composition.
  • Showcase category/menu behavior.
  • Ludo/flight-specific animation choices.