Files
flutter_lua_runtime/docs/protocol.md
2026-06-09 16:26:37 +08:00

5.5 KiB
Raw Permalink Blame History

Runtime protocol

The runtime protocol is the stable boundary between Lua and Dart.

RuntimeEvent -> Lua -> GameDiff / RuntimeCommand -> Flame

RuntimeEvent

Dart sends white-listed events to Lua. Events represent input, lifecycle, command/resource outcomes, and host/runtime notifications.

Lua should treat events as data and return explicit state changes.

GameDiff

A GameDiff describes desired changes to the runtime render tree.

Typical operations include creating, updating, and removing runtime nodes. Dart validates node fields before applying them to Flame components.

RuntimeNode

Runtime nodes are generic render descriptions. They are not Flame objects.

Supported node concepts include:

  • Containers/panels.
  • Text.
  • Sprites/images.
  • Buttons.
  • List views.
  • Particles.
  • Spine nodes.

Lua may compose higher-level widgets, but those widgets must normalize into supported runtime nodes.

Color and alpha

RuntimeNode.color supports #RRGGBB and #AARRGGBB. When the alpha channel is present in the color, it is multiplied with RuntimeNode.alpha and any runtime animation alpha.

Final opacity = color alpha × node alpha × runtime animation alpha

#RRGGBB colors behave as fully opaque colors. #00000000 is fully transparent.

Text shadow

Text-capable nodes may use flat shadow fields:

  • textShadowColor: #RRGGBB or #AARRGGBB shadow color.
  • textShadowOffsetX / textShadowOffsetY: shadow offset in runtime pixels.
  • textShadowBlur: non-negative blur radius.

The shadow color alpha is multiplied by RuntimeNode.alpha and any runtime animation alpha.

Image regions and nine-slice

Image-capable nodes (image, sprite, and image-backed button) may draw only part of an asset.

For manual atlas regions, set source fields in image pixels:

  • sourceX / sourceY: top-left source position inside the loaded image.
  • sourceWidth / sourceHeight: source region size.

For TexturePacker JSON atlases, declare atlas on an image resource and set frame on the node. Image-backed buttons may also use pressedFrame and disabledFrame. The runtime supports TexturePacker JSON Hash and JSON Array frames formats with non-rotated frames. rotated: true frames are rejected.

When frame/source fields are omitted, the full image is used.

Image-capable nodes may also use nine-slice scaling with source-pixel insets:

  • sliceLeft / sliceTop / sliceRight / sliceBottom.

Nine-slice keeps corners unscaled, stretches edges on one axis, and stretches the center on both axes. Insets are clamped to the selected source region and destination size.

Runtime network API

Lua may use runtime-owned async networking without blocking script execution:

  • runtime.http_request({ id?, method?, url, headers?, body?, timeout? })
  • runtime.ws_connect({ id?, url, protocols? })
  • runtime.ws_send(id, message)
  • runtime.ws_close(id)

HTTP requests support http and https URLs. WebSocket connections support ws and wss URLs.

Network results are delivered back to Lua through on_event(event):

  • network_http: HTTP request completed or failed. event.data includes id, url, method, ok, and either status/headers/body or error.
  • network_ws_open: WebSocket connection opened.
  • network_ws_message: WebSocket message received.
  • network_ws_error: WebSocket connection or stream error.
  • network_ws_close: WebSocket connection closed.

Runtime host bridge

Flutter host apps may register a RuntimeHostBridge when creating LuaGameWidget or FlameLuaGame.

Lua-to-Flutter calls:

  • runtime.host_call({ id?, method, data? }): async request. Result is delivered to Lua as host_call_result with id, method, ok, and either result or error.
  • runtime.host_notify({ method, data? }): fire-and-forget notification to Flutter host code.

Flutter-to-Lua calls:

  • FlameLuaGame.notifyLua(method, data?): emits a host_notify event into Lua.
  • FlameLuaGame.callLua(method, data?, timeout?): emits a host_call event into Lua and waits for Lua to call runtime.host_respond({ id, result?, error? }).

Host bridge payloads must be JSON-like values: null, bool, number, string, list, or string-keyed map. Unsupported Dart objects are converted to strings.

RuntimeCommand

Runtime commands request generic side effects owned by Dart/Flame.

Examples:

  • move_path
  • move_to
  • fade_to
  • scale_to
  • rotate_to
  • remove_node
  • sequence
  • parallel
  • delay
  • toast
  • play_sound
  • play_bgm
  • pause_bgm
  • resume_bgm
  • stop_bgm
  • preload_resources
  • evict_resources
  • cancel_commands
  • play_spine_animation
  • copy_text

Commands must remain generic. Do not add commands like roll_ludo_dice or move_airplane_piece; those belong in Lua game logic.

Validation expectations

  • Unknown protocol fields should not become implicit behavior.
  • Required fields should fail clearly when absent.
  • Helper-only aliases must be normalized before Dart protocol validation.
  • Runtime code should not silently convert invalid game state into fake success.

Where to change protocol code

lib/runtime/models/          Data models
lib/runtime/protocol/        Protocol validation
lib/runtime/commands/        Command validation/execution
lib/runtime/rendering/       Runtime node to Flame component mapping
assets/runtime/lua/          Lua helper constructors/aliases

When protocol changes affect Lua authoring, update:

tool/lua_runtime_defs_common.lua

Then run:

dart run tool/generate_lua_runtime_defs.dart