Initial flame_lua_runtime package

This commit is contained in:
gem
2026-06-07 22:53:58 +08:00
commit 733b2fb798
262 changed files with 28439 additions and 0 deletions

View File

@@ -0,0 +1,154 @@
part of 'command_executor.dart';
extension _CommandExecutorValidation on CommandExecutor {
void _validate(RuntimeCommand command) {
if (!RuntimeCommandType.isSupported(command.type)) {
throw UnsupportedError('Unsupported runtime command: ${command.type}');
}
RuntimeProtocolSchema.ensureKnownKeys(
command.payload,
allowed: RuntimeProtocolSchema.allowedCommandPayloadFields(command.type),
context: 'RuntimeCommand.${command.type}.payload',
);
_optionalString(command.payload['id'], 'id');
_optionalString(command.payload['group'], 'group');
_optionalString(command.payload['commandGroup'], 'commandGroup');
_optionalString(command.payload['scope'], 'scope');
_estimatedDuration(command);
}
double _estimatedDuration(RuntimeCommand command) {
_optionalString(command.payload['onComplete'], 'onComplete');
switch (command.type) {
case RuntimeCommandType.movePath:
_requiredTarget(command);
_validatePath(command.payload['path']);
return _duration(command, defaultValue: 0.4);
case RuntimeCommandType.moveTo:
_requiredTarget(command);
_requiredVector(command);
return _duration(command, defaultValue: 0.2);
case RuntimeCommandType.fadeTo:
_requiredTarget(command);
_requiredNormalizedDouble(command.payload['alpha'], 'fade_to.alpha');
return _duration(command, defaultValue: 0.2);
case RuntimeCommandType.scaleTo:
_requiredTarget(command);
_requiredDouble(command.payload['scale'], 'scale_to.scale');
return _duration(command, defaultValue: 0.2);
case RuntimeCommandType.rotateTo:
_requiredTarget(command);
_requiredDouble(command.payload['angle'], 'rotate_to.angle');
return _duration(command, defaultValue: 0.2);
case RuntimeCommandType.removeNode:
_requiredTarget(command);
return 0;
case RuntimeCommandType.delay:
return _duration(command, defaultValue: 0);
case RuntimeCommandType.sequence:
return _commandsFromPayload(
command,
).fold<double>(0, (sum, child) => sum + _estimatedDuration(child));
case RuntimeCommandType.parallel:
var maxDuration = 0.0;
for (final child in _commandsFromPayload(command)) {
final duration = _estimatedDuration(child);
if (duration > maxDuration) {
maxDuration = duration;
}
}
return maxDuration;
case RuntimeCommandType.toast:
_toastText(command);
return _duration(command, defaultValue: 1.8);
case RuntimeCommandType.playSound:
_requiredAudioResource(command);
_optionalVolume(command);
return 0;
case RuntimeCommandType.playBgm:
_requiredAudioResource(command);
_optionalVolume(command);
_audioChannel(command);
_optionalBool(command.payload['loop'], 'play_bgm.loop');
return 0;
case RuntimeCommandType.pauseBgm:
case RuntimeCommandType.resumeBgm:
case RuntimeCommandType.stopBgm:
_audioChannel(command);
return 0;
case RuntimeCommandType.preloadResources:
_requiredResourceGroup(command);
_optionalBool(
command.payload['failOnError'],
'preload_resources.failOnError',
);
return 0;
case RuntimeCommandType.evictResources:
_requiredResourceGroup(command);
return 0;
case RuntimeCommandType.cancelCommands:
_validateCancelCommands(command);
return 0;
case RuntimeCommandType.playSpineAnimation:
_requiredTarget(command);
_requiredSpineAnimation(command);
final track = _optionalInt(
command.payload['track'],
'play_spine_animation.track',
);
if (track != null && track < 0) {
throw const FormatException(
'play_spine_animation.track must be >= 0',
);
}
_optionalBool(command.payload['loop'], 'play_spine_animation.loop');
_optionalBool(command.payload['queue'], 'play_spine_animation.queue');
final delay = _readDouble(command.payload['delay']);
if (delay != null && delay < 0) {
throw const FormatException(
'play_spine_animation.delay must be >= 0',
);
}
return 0;
case RuntimeCommandType.copyText:
_requiredText(command, 'copy_text.text');
return 0;
default:
throw UnsupportedError('Unsupported runtime command: ${command.type}');
}
}
void _validatePath(Object? pathValue) {
if (pathValue is! List || pathValue.isEmpty) {
throw const FormatException('move_path.path must be a non-empty list');
}
for (final point in pathValue) {
if (point is! Map) {
throw const FormatException('move_path.path item must be a map');
}
final x = _readDouble(point['x']);
final y = _readDouble(point['y']);
if (x == null || y == null) {
throw const FormatException('move_path point requires x/y');
}
}
}
List<RuntimeCommand> _commandsFromPayload(RuntimeCommand command) {
final value = command.payload['commands'];
if (value is! List) {
throw FormatException('${command.type}.commands must be a list');
}
return value
.map((item) {
if (item is! Map) {
throw FormatException(
'${command.type}.commands item must be a map',
);
}
return RuntimeCommand.fromMap(Map<String, Object?>.from(item));
})
.toList(growable: false);
}
}