Initial flame_lua_runtime package
This commit is contained in:
154
lib/runtime/commands/command_validation.dart
Normal file
154
lib/runtime/commands/command_validation.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user