6441592534
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
180 lines
6.6 KiB
Dart
180 lines
6.6 KiB
Dart
/// High-level Flutter facade for discovering game data and sharing runtime services.
|
|
library;
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:wolf_3d_dart/wolf_3d_data.dart';
|
|
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';
|
|
import 'package:wolf_3d_dart/wolf_3d_engine.dart';
|
|
import 'package:wolf_3d_flutter/audio/wolf3d_platform_audio.dart';
|
|
import 'package:wolf_3d_flutter/managers/desktop_windowing_support.dart'
|
|
as desktop_windowing_support;
|
|
import 'package:wolf_3d_flutter/managers/game_data_directory_persistence.dart';
|
|
import 'package:wolf_3d_flutter/wolf_3d_input_flutter.dart';
|
|
|
|
export 'package:wolf_3d_dart/wolf_3d_audio.dart' show DebugMusicPlayer;
|
|
|
|
export 'audio/wolf3d_platform_audio.dart' show Wolf3dPlatformAudio;
|
|
export 'managers/game_app_lifecycle_manager.dart' show GameAppLifecycleManager;
|
|
export 'managers/game_data_directory_persistence.dart'
|
|
show DefaultGameDataDirectoryPersistence;
|
|
export 'managers/game_display_manager.dart' show GameDisplayManager;
|
|
export 'managers/game_persistence_manager.dart' show GamePersistenceManager;
|
|
export 'managers/game_renderer_mode_manager.dart'
|
|
show GameRendererMode, gameRendererModeFromSettings, handleGlslUnavailable;
|
|
export 'managers/game_screen_input_manager.dart'
|
|
show
|
|
HostShortcutBinding,
|
|
HostShortcutHandler,
|
|
HostShortcutIntent,
|
|
HostShortcutRegistry,
|
|
GameScreenInputManager,
|
|
isAltEnterShortcut;
|
|
export 'managers/wolf3d_app_manager.dart' show Wolf3dAppManager;
|
|
export 'screens/audio_gallery.dart' show AudioGallery;
|
|
export 'screens/debug_tools_screen.dart' show DebugToolsScreen;
|
|
export 'screens/game_screen.dart' show GameScreen;
|
|
export 'screens/no_game_data_screen.dart' show NoGameDataScreen;
|
|
export 'screens/sprite_gallery.dart' show SpriteGallery;
|
|
export 'screens/vga_gallery.dart' show VgaGallery;
|
|
export 'widgets/gallery_game_selector.dart'
|
|
show GalleryGameSelector, formatGalleryGameTitle;
|
|
export 'widgets/wolf3d_app.dart' show Wolf3dApp;
|
|
export 'widgets/wolf_menu_shell.dart' show WolfMenuShell;
|
|
|
|
/// Flutter-specific host facade built on top of [Wolf3dEngine].
|
|
///
|
|
/// This type keeps platform-neutral session/engine state in the Dart package
|
|
/// while owning Flutter-only concerns such as bundle loading and discovery.
|
|
class Wolf3dFlutterEngine extends Wolf3dEngine {
|
|
/// Creates an empty facade that must be initialized with [init].
|
|
Wolf3dFlutterEngine({
|
|
bool debug = false,
|
|
EngineAudio? audioBackend,
|
|
Wolf3dFlutterInput? inputBackend,
|
|
DefaultGameDataDirectoryPersistence? dataDirectoryPersistence,
|
|
}) : dataDirectoryPersistence =
|
|
dataDirectoryPersistence ?? DefaultGameDataDirectoryPersistence(),
|
|
super(
|
|
audio: audioBackend ?? Wolf3dPlatformAudio(),
|
|
input: inputBackend ?? Wolf3dFlutterInput(),
|
|
) {
|
|
if (debug) {
|
|
enableDebug();
|
|
}
|
|
}
|
|
|
|
/// Persists and restores the preferred external game-data directory.
|
|
final DefaultGameDataDirectoryPersistence dataDirectoryPersistence;
|
|
|
|
/// Last configured/loaded external game-data directory path.
|
|
String? configuredDataDirectory;
|
|
|
|
/// Shared Flutter input adapter reused by gameplay screens.
|
|
@override
|
|
Wolf3dFlutterInput get input => super.input as Wolf3dFlutterInput;
|
|
|
|
/// Enables host-level debug affordances such as debug navigation UI.
|
|
@override
|
|
Wolf3dFlutterEngine enableDebug() {
|
|
super.enableDebug();
|
|
return this;
|
|
}
|
|
|
|
/// Initializes the engine by loading available game data.
|
|
Future<Wolf3dFlutterEngine> init({
|
|
String? directory,
|
|
Iterable<String>? additionalDirectories,
|
|
}) async {
|
|
await desktop_windowing_support.ensureDesktopWindowingInitialized();
|
|
await audio.init();
|
|
availableGames.clear();
|
|
|
|
final String? requestedDirectory = directory?.trim();
|
|
final String? resolvedDirectory =
|
|
requestedDirectory != null && requestedDirectory.isNotEmpty
|
|
? requestedDirectory
|
|
: await dataDirectoryPersistence.loadDataDirectory();
|
|
configuredDataDirectory = resolvedDirectory;
|
|
|
|
if (requestedDirectory != null && requestedDirectory.isNotEmpty) {
|
|
await dataDirectoryPersistence.saveDataDirectory(requestedDirectory);
|
|
}
|
|
|
|
// Bundled assets let the GUI work out of the box on supported platforms.
|
|
final versionsToTry = [
|
|
(version: GameVersion.retail, path: 'retail'),
|
|
(version: GameVersion.shareware, path: 'shareware'),
|
|
];
|
|
|
|
for (final version in versionsToTry) {
|
|
try {
|
|
final ext = version.version.fileExtension;
|
|
final folder = 'packages/wolf_3d_assets/assets/${version.path}';
|
|
|
|
final data = WolfensteinLoader.loadFromBytes(
|
|
version: version.version,
|
|
vswap: await _tryLoad('$folder/VSWAP.$ext'),
|
|
mapHead: await _tryLoad('$folder/MAPHEAD.$ext'),
|
|
gameMaps: await _tryLoad('$folder/GAMEMAPS.$ext'),
|
|
vgaDict: await _tryLoad('$folder/VGADICT.$ext'),
|
|
vgaHead: await _tryLoad('$folder/VGAHEAD.$ext'),
|
|
vgaGraph: await _tryLoad('$folder/VGAGRAPH.$ext'),
|
|
audioHed: await _tryLoad('$folder/AUDIOHED.$ext'),
|
|
audioT: await _tryLoad('$folder/AUDIOT.$ext'),
|
|
);
|
|
|
|
availableGames.add(data);
|
|
} catch (e) {
|
|
debugPrint(e.toString());
|
|
}
|
|
}
|
|
|
|
// On non-web platforms, also scan local filesystem locations for
|
|
// user-supplied data folders so the host can pick up extra versions.
|
|
final Set<String> directoriesToScan = <String>{};
|
|
if (resolvedDirectory != null && resolvedDirectory.isNotEmpty) {
|
|
directoriesToScan.add(resolvedDirectory);
|
|
}
|
|
|
|
if (additionalDirectories != null) {
|
|
for (final String directoryPath in additionalDirectories) {
|
|
final String trimmedPath = directoryPath.trim();
|
|
if (trimmedPath.isNotEmpty) {
|
|
directoriesToScan.add(trimmedPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!kIsWeb && directoriesToScan.isNotEmpty) {
|
|
for (final String directoryPath in directoriesToScan) {
|
|
try {
|
|
final externalGames = await WolfensteinLoader.discover(
|
|
directoryPath: directoryPath,
|
|
recursive: true,
|
|
);
|
|
for (var entry in externalGames.entries) {
|
|
if (!availableGames.any((g) => g.version == entry.key)) {
|
|
availableGames.add(entry.value);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
debugPrint("External discovery failed: $e");
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/// Loads an asset from the Flutter bundle, returning `null` when absent.
|
|
Future<ByteData?> _tryLoad(String path) async {
|
|
try {
|
|
return await rootBundle.load(path);
|
|
} catch (e) {
|
|
debugPrint("Asset not found: $path");
|
|
return null;
|
|
}
|
|
}
|
|
}
|