Add Lua debug logging API

This commit is contained in:
gem
2026-06-09 10:55:08 +08:00
parent 5ebe6ee786
commit 45ab9d7861
9 changed files with 101 additions and 2 deletions

View File

@@ -552,6 +552,7 @@
---@class RuntimeImportApi
---@field import fun(moduleName: string): table
---@field log fun(...: any)
---@type RuntimeImportApi
runtime = runtime

View File

@@ -552,6 +552,7 @@
---@class RuntimeImportApi
---@field import fun(moduleName: string): table
---@field log fun(...: any)
---@type RuntimeImportApi
runtime = runtime

View File

@@ -552,6 +552,7 @@
---@class RuntimeImportApi
---@field import fun(moduleName: string): table
---@field log fun(...: any)
---@type RuntimeImportApi
runtime = runtime

View File

@@ -552,6 +552,7 @@
---@class RuntimeImportApi
---@field import fun(moduleName: string): table
---@field log fun(...: any)
---@type RuntimeImportApi
runtime = runtime

View File

@@ -130,6 +130,7 @@ String _formatDebugValue(Object? value) {
}
enum RuntimeDiagnosticType {
luaLog,
luaEventError,
diffApplyError,
packageActivationError,

View File

@@ -1,6 +1,7 @@
import 'package:flame/game.dart';
import 'package:flutter/widgets.dart';
import '../diagnostics/runtime_diagnostics.dart';
import '../packages/game_package_repository.dart';
import '../scripting/lua_dardo_script_engine.dart';
import 'flame_lua_game.dart';
@@ -24,10 +25,13 @@ class LuaGameWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final diagnostics = RuntimeDiagnostics();
return GameWidget(
game: FlameLuaGame(
scriptEngine: LuaDardoScriptEngine(),
scriptEngineFactory: LuaDardoScriptEngine.new,
scriptEngine: LuaDardoScriptEngine(diagnostics: diagnostics),
scriptEngineFactory: () =>
LuaDardoScriptEngine(diagnostics: diagnostics),
diagnostics: diagnostics,
packageRepository:
packageRepository ??
(serverUrl == null

View File

@@ -1,11 +1,16 @@
import 'package:lua_dardo_plus/lua.dart';
import '../diagnostics/runtime_diagnostics.dart';
import '../models/game_diff.dart';
import '../models/runtime_event.dart';
import '../packages/game_package.dart';
import 'script_engine.dart';
class LuaDardoScriptEngine implements ScriptEngine {
LuaDardoScriptEngine({RuntimeDiagnostics? diagnostics})
: _diagnostics = diagnostics;
final RuntimeDiagnostics? _diagnostics;
late final LuaState _lua;
late final Map<String, String> _moduleScripts;
final Set<String> _loadingModules = {};
@@ -104,9 +109,28 @@ class LuaDardoScriptEngine implements ScriptEngine {
_lua.pushDartFunction(_importModule);
_lua.setField(-2, 'import');
_lua.pushDartFunction(_log);
_lua.setField(-2, 'log');
_lua.setGlobal('runtime');
}
int _log(LuaState lua) {
final argumentCount = lua.getTop();
final messageParts = <String>[];
for (var index = 1; index <= argumentCount; index++) {
messageParts.add(_formatLuaLogValue(lua, index));
}
final message = messageParts.join(' ');
_diagnostics?.record(
type: RuntimeDiagnosticType.luaLog,
message: message,
context: {'argumentCount': argumentCount},
);
return 0;
}
int _importModule(LuaState lua) {
final moduleName = lua.toStr(1);
if (moduleName == null || moduleName.isEmpty) {
@@ -179,6 +203,25 @@ class LuaDardoScriptEngine implements ScriptEngine {
}
}
String _formatLuaLogValue(LuaState lua, int index) {
if (lua.isNil(index) || lua.isNone(index)) {
return 'nil';
}
if (lua.isBoolean(index)) {
return lua.toBoolean(index).toString();
}
if (lua.isInteger(index)) {
return lua.toInteger(index).toString();
}
if (lua.isNumber(index)) {
return lua.toNumber(index).toString();
}
if (lua.isString(index)) {
return lua.toStr(index) ?? '';
}
return lua.typeName2(index);
}
bool _isSafeModuleName(String value) {
return RegExp(r'^[A-Za-z0-9_.-]+$').hasMatch(value) &&
!value.contains('..') &&

View File

@@ -1,5 +1,6 @@
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/models/runtime_event.dart';
@@ -895,6 +896,51 @@ end
expect(c.y, 53);
});
test('runtime.log records Lua debug messages in diagnostics', () async {
final package = await _createPackage(
mainScript: '''
function smoke_test(ctx)
runtime.log("smoke", ctx.runtimeApiVersion)
return true
end
function init(ctx)
runtime.log("init", true, nil)
return {}
end
function on_event(event)
runtime.log("event", event.type, event.target)
return {}
end
''',
);
final diagnostics = RuntimeDiagnostics();
final engine = LuaDardoScriptEngine(diagnostics: diagnostics);
await engine.loadPackage(package);
expect(engine.smokeTest({'runtimeApiVersion': 1}), isTrue);
engine.init({'runtimeApiVersion': 1});
engine.dispatchEvent(
const RuntimeEvent(
type: RuntimeEventType.tap,
target: 'debug_button',
handler: 'debug',
),
);
expect(
diagnostics.entries.map((entry) => entry.type),
everyElement(RuntimeDiagnosticType.luaLog),
);
expect(diagnostics.entries.map((entry) => entry.message), [
'smoke 1',
'init true nil',
'event tap debug_button',
]);
expect(diagnostics.entries.first.context, {'argumentCount': 2});
});
test('rejects undeclared module imports', () async {
final package = await _createPackage(
mainScript: '''

View File

@@ -552,6 +552,7 @@
---@class RuntimeImportApi
---@field import fun(moduleName: string): table
---@field log fun(...: any)
---@type RuntimeImportApi
runtime = runtime