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();
|
await engine.audio.init();
|
||||||
engine.init();
|
engine.init();
|
||||||
|
|
||||||
final persistence = DefaultRendererSettingsPersistence();
|
final persistence = DefaultRendererSettingsPersistence(
|
||||||
|
hostKey: rendererSettingsHostCli,
|
||||||
|
);
|
||||||
final WolfRendererSettings? saved = await persistence.load();
|
final WolfRendererSettings? saved = await persistence.load();
|
||||||
|
|
||||||
gameLoop = CliGameLoop(
|
gameLoop = CliGameLoop(
|
||||||
|
|||||||
+57
-3
@@ -1,20 +1,29 @@
|
|||||||
/// Native (dart:io) renderer-settings persistence.
|
/// Native (dart:io) renderer-settings persistence.
|
||||||
library;
|
library;
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:wolf_3d_dart/src/engine/rendering/renderer_settings.dart';
|
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/engine/rendering/renderer_settings_persistence.dart';
|
||||||
import 'package:wolf_3d_dart/src/platform/platform_config_dir.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.
|
/// Persists [WolfRendererSettings] as JSON to the platform config directory.
|
||||||
///
|
///
|
||||||
/// Pass an explicit [filePath] to override the default location (useful in tests).
|
/// Pass an explicit [filePath] to override the default location (useful in tests).
|
||||||
class DefaultRendererSettingsPersistence extends RendererSettingsPersistence
|
class DefaultRendererSettingsPersistence extends RendererSettingsPersistence
|
||||||
with JsonRendererSettingsPersistence {
|
with JsonRendererSettingsPersistence {
|
||||||
DefaultRendererSettingsPersistence({String? filePath}) : _filePath = filePath;
|
DefaultRendererSettingsPersistence({
|
||||||
|
String? filePath,
|
||||||
|
String hostKey = rendererSettingsHostFlutter,
|
||||||
|
}) : _filePath = filePath,
|
||||||
|
_hostKey = hostKey;
|
||||||
|
|
||||||
final String? _filePath;
|
final String? _filePath;
|
||||||
|
final String _hostKey;
|
||||||
String? _resolvedPath;
|
String? _resolvedPath;
|
||||||
|
|
||||||
Future<String> _getFilePath() async {
|
Future<String> _getFilePath() async {
|
||||||
@@ -29,7 +38,23 @@ class DefaultRendererSettingsPersistence extends RendererSettingsPersistence
|
|||||||
final String path = await _getFilePath();
|
final String path = await _getFilePath();
|
||||||
final File f = File(path);
|
final File f = File(path);
|
||||||
if (!f.existsSync()) return null;
|
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 (_) {
|
} catch (_) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -41,7 +66,36 @@ class DefaultRendererSettingsPersistence extends RendererSettingsPersistence
|
|||||||
final String path = await _getFilePath();
|
final String path = await _getFilePath();
|
||||||
final Directory dir = File(path).parent;
|
final Directory dir = File(path).parent;
|
||||||
if (!dir.existsSync()) await dir.create(recursive: true);
|
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 (_) {
|
} catch (_) {
|
||||||
// Best-effort.
|
// 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.dart';
|
||||||
import 'package:wolf_3d_dart/src/engine/rendering/renderer_settings_persistence.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.
|
/// No-op implementation used on web, where dart:io is unavailable.
|
||||||
class DefaultRendererSettingsPersistence extends RendererSettingsPersistence {
|
class DefaultRendererSettingsPersistence extends RendererSettingsPersistence {
|
||||||
// ignore: avoid_unused_constructor_parameters
|
// ignore: avoid_unused_constructor_parameters
|
||||||
DefaultRendererSettingsPersistence({String? filePath});
|
DefaultRendererSettingsPersistence({
|
||||||
|
String? filePath,
|
||||||
|
String hostKey = 'flutter',
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<WolfRendererSettings?> load() async => null;
|
Future<WolfRendererSettings?> load() async => null;
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ class GamePersistenceManager {
|
|||||||
RendererSettingsPersistence? rendererSettingsPersistence,
|
RendererSettingsPersistence? rendererSettingsPersistence,
|
||||||
SaveGamePersistence? saveGamePersistence,
|
SaveGamePersistence? saveGamePersistence,
|
||||||
}) : rendererSettingsPersistence =
|
}) : rendererSettingsPersistence =
|
||||||
rendererSettingsPersistence ?? DefaultRendererSettingsPersistence(),
|
rendererSettingsPersistence ??
|
||||||
|
DefaultRendererSettingsPersistence(
|
||||||
|
hostKey: rendererSettingsHostFlutter,
|
||||||
|
),
|
||||||
saveGamePersistence =
|
saveGamePersistence =
|
||||||
saveGamePersistence ?? DefaultSaveGamePersistence();
|
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', (
|
testWidgets('Wolf3dApp forwards configured directory to no-data screen', (
|
||||||
tester,
|
tester,
|
||||||
) async {
|
) async {
|
||||||
|
|||||||
Reference in New Issue
Block a user