feat: Enhance DefaultRendererSettingsPersistence to support scoped settings for CLI and Flutter
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -111,7 +111,9 @@ void main(List<String> arguments) async {
|
||||
await engine.audio.init();
|
||||
engine.init();
|
||||
|
||||
final persistence = DefaultRendererSettingsPersistence();
|
||||
final persistence = DefaultRendererSettingsPersistence(
|
||||
hostKey: rendererSettingsHostCli,
|
||||
);
|
||||
final WolfRendererSettings? saved = await persistence.load();
|
||||
|
||||
gameLoop = CliGameLoop(
|
||||
|
||||
+57
-3
@@ -1,20 +1,29 @@
|
||||
/// Native (dart:io) renderer-settings persistence.
|
||||
library;
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:wolf_3d_dart/src/engine/rendering/renderer_settings.dart';
|
||||
import 'package:wolf_3d_dart/src/engine/rendering/renderer_settings_persistence.dart';
|
||||
import 'package:wolf_3d_dart/src/platform/platform_config_dir.dart';
|
||||
|
||||
const String rendererSettingsHostCli = 'cli';
|
||||
const String rendererSettingsHostFlutter = 'flutter';
|
||||
|
||||
/// Persists [WolfRendererSettings] as JSON to the platform config directory.
|
||||
///
|
||||
/// Pass an explicit [filePath] to override the default location (useful in tests).
|
||||
class DefaultRendererSettingsPersistence extends RendererSettingsPersistence
|
||||
with JsonRendererSettingsPersistence {
|
||||
DefaultRendererSettingsPersistence({String? filePath}) : _filePath = filePath;
|
||||
DefaultRendererSettingsPersistence({
|
||||
String? filePath,
|
||||
String hostKey = rendererSettingsHostFlutter,
|
||||
}) : _filePath = filePath,
|
||||
_hostKey = hostKey;
|
||||
|
||||
final String? _filePath;
|
||||
final String _hostKey;
|
||||
String? _resolvedPath;
|
||||
|
||||
Future<String> _getFilePath() async {
|
||||
@@ -29,7 +38,23 @@ class DefaultRendererSettingsPersistence extends RendererSettingsPersistence
|
||||
final String path = await _getFilePath();
|
||||
final File f = File(path);
|
||||
if (!f.existsSync()) return null;
|
||||
return await f.readAsString();
|
||||
final String raw = await f.readAsString();
|
||||
final Object? decoded = jsonDecode(raw);
|
||||
if (decoded is! Map<String, Object?>) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Object? rendererSettings = decoded['rendererSettings'];
|
||||
if (rendererSettings is! Map<String, Object?>) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Object? scoped = rendererSettings[_hostKey];
|
||||
if (scoped is! Map<String, Object?>) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return jsonEncode(scoped);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
@@ -41,7 +66,36 @@ class DefaultRendererSettingsPersistence extends RendererSettingsPersistence
|
||||
final String path = await _getFilePath();
|
||||
final Directory dir = File(path).parent;
|
||||
if (!dir.existsSync()) await dir.create(recursive: true);
|
||||
await File(path).writeAsString(json, flush: true);
|
||||
|
||||
final File f = File(path);
|
||||
Map<String, Object?> root = <String, Object?>{};
|
||||
if (f.existsSync()) {
|
||||
try {
|
||||
final Object? existing = jsonDecode(await f.readAsString());
|
||||
if (existing is Map<String, Object?>) {
|
||||
root = Map<String, Object?>.from(existing);
|
||||
}
|
||||
} catch (_) {
|
||||
root = <String, Object?>{};
|
||||
}
|
||||
}
|
||||
|
||||
final Object? scopedDecoded = jsonDecode(json);
|
||||
if (scopedDecoded is! Map<String, Object?>) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, Object?> rendererSettings =
|
||||
root['rendererSettings'] is Map<String, Object?>
|
||||
? Map<String, Object?>.from(
|
||||
root['rendererSettings']! as Map<String, Object?>,
|
||||
)
|
||||
: <String, Object?>{};
|
||||
|
||||
rendererSettings[_hostKey] = Map<String, Object?>.from(scopedDecoded);
|
||||
root['rendererSettings'] = rendererSettings;
|
||||
|
||||
await f.writeAsString(jsonEncode(root), flush: true);
|
||||
} catch (_) {
|
||||
// Best-effort.
|
||||
}
|
||||
|
||||
+7
-1
@@ -4,10 +4,16 @@ library;
|
||||
import 'package:wolf_3d_dart/src/engine/rendering/renderer_settings.dart';
|
||||
import 'package:wolf_3d_dart/src/engine/rendering/renderer_settings_persistence.dart';
|
||||
|
||||
const String rendererSettingsHostCli = 'cli';
|
||||
const String rendererSettingsHostFlutter = 'flutter';
|
||||
|
||||
/// No-op implementation used on web, where dart:io is unavailable.
|
||||
class DefaultRendererSettingsPersistence extends RendererSettingsPersistence {
|
||||
// ignore: avoid_unused_constructor_parameters
|
||||
DefaultRendererSettingsPersistence({String? filePath});
|
||||
DefaultRendererSettingsPersistence({
|
||||
String? filePath,
|
||||
String hostKey = 'flutter',
|
||||
});
|
||||
|
||||
@override
|
||||
Future<WolfRendererSettings?> load() async => null;
|
||||
|
||||
@@ -9,7 +9,10 @@ class GamePersistenceManager {
|
||||
RendererSettingsPersistence? rendererSettingsPersistence,
|
||||
SaveGamePersistence? saveGamePersistence,
|
||||
}) : rendererSettingsPersistence =
|
||||
rendererSettingsPersistence ?? DefaultRendererSettingsPersistence(),
|
||||
rendererSettingsPersistence ??
|
||||
DefaultRendererSettingsPersistence(
|
||||
hostKey: rendererSettingsHostFlutter,
|
||||
),
|
||||
saveGamePersistence =
|
||||
saveGamePersistence ?? DefaultSaveGamePersistence();
|
||||
|
||||
|
||||
@@ -154,6 +154,70 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('DefaultRendererSettingsPersistence', () {
|
||||
test('stores separate scoped settings for flutter and cli', () async {
|
||||
final tempDir = await Directory.systemTemp.createTemp(
|
||||
'wolf3d-renderer-config-',
|
||||
);
|
||||
addTearDown(() async {
|
||||
if (await tempDir.exists()) {
|
||||
await tempDir.delete(recursive: true);
|
||||
}
|
||||
});
|
||||
|
||||
final String path = '${tempDir.path}/settings.json';
|
||||
final flutterPersistence = DefaultRendererSettingsPersistence(
|
||||
filePath: path,
|
||||
hostKey: rendererSettingsHostFlutter,
|
||||
);
|
||||
final cliPersistence = DefaultRendererSettingsPersistence(
|
||||
filePath: path,
|
||||
hostKey: rendererSettingsHostCli,
|
||||
);
|
||||
|
||||
const flutterSettings = WolfRendererSettings(
|
||||
mode: WolfRendererMode.hardware,
|
||||
);
|
||||
const cliSettings = WolfRendererSettings(mode: WolfRendererMode.sixel);
|
||||
|
||||
await flutterPersistence.save(flutterSettings);
|
||||
await cliPersistence.save(cliSettings);
|
||||
|
||||
final loadedFlutter = await flutterPersistence.load();
|
||||
final loadedCli = await cliPersistence.load();
|
||||
expect(loadedFlutter, isNotNull);
|
||||
expect(loadedCli, isNotNull);
|
||||
expect(loadedFlutter!.mode, flutterSettings.mode);
|
||||
expect(loadedCli!.mode, cliSettings.mode);
|
||||
|
||||
final String raw = await File(path).readAsString();
|
||||
expect(raw, contains('"rendererSettings"'));
|
||||
expect(raw, contains('"flutter"'));
|
||||
expect(raw, contains('"cli"'));
|
||||
});
|
||||
|
||||
test('does not fall back to legacy unscoped renderer payload', () async {
|
||||
final tempDir = await Directory.systemTemp.createTemp(
|
||||
'wolf3d-renderer-config-',
|
||||
);
|
||||
addTearDown(() async {
|
||||
if (await tempDir.exists()) {
|
||||
await tempDir.delete(recursive: true);
|
||||
}
|
||||
});
|
||||
|
||||
final String path = '${tempDir.path}/settings.json';
|
||||
await File(path).writeAsString('{"mode":"hardware"}');
|
||||
|
||||
final persistence = DefaultRendererSettingsPersistence(
|
||||
filePath: path,
|
||||
hostKey: rendererSettingsHostFlutter,
|
||||
);
|
||||
|
||||
expect(await persistence.load(), isNull);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('Wolf3dApp forwards configured directory to no-data screen', (
|
||||
tester,
|
||||
) async {
|
||||
|
||||
Reference in New Issue
Block a user