Add bidirectional host bridge

This commit is contained in:
gem
2026-06-09 16:26:37 +08:00
parent 7b3c5cb0f5
commit 0d4fbd030c
17 changed files with 632 additions and 3 deletions

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flame_lua_runtime/runtime/diagnostics/runtime_diagnostics.dart';
import 'package:flame_lua_runtime/runtime/packages/game_package.dart';
import 'package:flame_lua_runtime/runtime/packages/game_package_manifest.dart';
import 'package:flame_lua_runtime/runtime/host/runtime_host_bridge.dart';
import 'package:flame_lua_runtime/runtime/models/runtime_event.dart';
import 'package:flame_lua_runtime/runtime/network/runtime_network_manager.dart';
import 'package:flame_lua_runtime/runtime/protocol/runtime_protocol.dart';
@@ -1021,6 +1022,57 @@ function on_event(event) return {} end
expect(network.closedWebSockets, ['chat']);
});
test('exposes host bridge runtime API to Lua', () async {
final package = await _createPackage(
mainScript: '''
function smoke_test(ctx) return true end
function init(ctx)
local id = runtime.host_call({
id = "profile",
method = "user.profile",
data = { userId = 9 },
})
local notified = runtime.host_notify({
method = "analytics",
data = { event = "open" },
})
return {
commands = {
{ type = "toast", text = id .. ":" .. tostring(notified) },
},
}
end
function on_event(event)
if event.type == "host_call" then
runtime.host_respond({ id = event.data.id, result = { handled = event.data.method } })
end
return {}
end
''',
);
final hostBridge = _RecordingHostBridgeManager();
final engine = LuaDardoScriptEngine();
await engine.loadPackage(
package,
services: RuntimeScriptServices(hostBridge: hostBridge),
);
final diff = engine.init({'runtimeApiVersion': 1});
expect(diff.commands.single.payload['text'], 'profile:true');
expect(hostBridge.calls.single.method, 'user.profile');
expect(hostBridge.calls.single.data, {'userId': 9});
expect(hostBridge.notifications.single.method, 'analytics');
expect(hostBridge.notifications.single.data, {'event': 'open'});
final callFuture = hostBridge.callLua('flutter.request', data: {'id': 2});
final event = _RecordingHostBridgeManager.events.singleWhere(
(item) => item.type == RuntimeHostEventType.call,
);
engine.dispatchEvent(event);
expect(await callFuture, {'handled': 'flutter.request'});
});
test('rejects undeclared module imports', () async {
final package = await _createPackage(
mainScript: '''
@@ -1052,6 +1104,26 @@ function on_event(event) return {} end
});
}
class _RecordingHostBridgeManager extends RuntimeHostBridgeManager {
_RecordingHostBridgeManager()
: super(bridge: const RuntimeHostBridge(), eventSink: events.add);
static final events = <RuntimeEvent>[];
final calls = <RuntimeHostCall>[];
final notifications = <RuntimeHostNotification>[];
@override
Future<void> callHost(RuntimeHostCall call) async {
calls.add(call);
}
@override
bool notifyHost(RuntimeHostNotification notification) {
notifications.add(notification);
return true;
}
}
class _RecordingNetworkManager extends RuntimeNetworkManager {
_RecordingNetworkManager()
: super(