From ec4b78acc418ff27f3f72ea2485328cbc681c7b4 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Sat, 15 Mar 2025 23:53:33 +0100 Subject: [PATCH] Cook up proper logging --- lib/main.dart | 8 ++- lib/modloader.dart | 130 ++++++++++++++++++++++++++++++++++++--------- pubspec.lock | 10 +++- pubspec.yaml | 2 + 4 files changed, 122 insertions(+), 28 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 41a5abc..b0496fd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,11 +1,17 @@ import 'package:flutter/material.dart'; -import 'package:rimworld_modman/modloader.dart'; import 'dart:io'; +import 'package:rimworld_modman/modloader.dart'; // Global variable to store loaded mods for access across the app late ModList modManager; void main() { + WidgetsFlutterBinding.ensureInitialized(); + + // Get a reference to the logger (now auto-initializes) + final logger = Logger.instance; + logger.info('Rimworld Mod Manager starting...'); + // Initialize the mod manager modManager = ModList(path: modsRoot); diff --git a/lib/modloader.dart b/lib/modloader.dart index dd04f3c..90d22ac 100644 --- a/lib/modloader.dart +++ b/lib/modloader.dart @@ -1,11 +1,83 @@ import 'dart:io'; import 'dart:async'; import 'package:xml/xml.dart'; +import 'package:path/path.dart' as path; const root = r'C:/Users/Administrator/Seafile/Games-Rimworld'; const modsRoot = '$root/294100'; const configRoot = '$root/AppData/RimWorld by Ludeon Studios/Config'; const configPath = '$configRoot/ModsConfig.xml'; +const logsPath = '$root/ModManager'; + +// Logger class for writing logs to console and file +class Logger { + static final Logger _instance = Logger._internal(); + static Logger get instance => _instance; + + File? _logFile; + IOSink? _logSink; + + Logger._internal() { + _initLogFile(); + } + + void _initLogFile() { + try { + // Use system temp directory + final tempDir = Directory.systemTemp; + final timestamp = DateTime.now().toIso8601String().replaceAll(':', '-').substring(0, 19); + final logFileName = 'rimworld_modman_$timestamp.log'; + + _logFile = File('${tempDir.path}${Platform.pathSeparator}$logFileName'); + _logSink = _logFile!.openWrite(mode: FileMode.writeOnly); + + info('Logging initialized. Log file: ${_logFile!.path}'); + } catch (e) { + print('Failed to initialize log file: $e'); + } + } + + void _log(String message, String level) { + final timestamp = DateTime.now().toIso8601String(); + final formattedMessage = '[$timestamp] [$level] $message'; + + // Always print to console + print(formattedMessage); + + // Write to file if initialized + if (_logSink != null) { + try { + _logSink!.writeln(formattedMessage); + } catch (e) { + print('Error writing to log file: $e'); + } + } + } + + void info(String message) { + _log(message, 'INFO'); + } + + void warning(String message) { + _log(message, 'WARN'); + } + + void error(String message) { + _log(message, 'ERROR'); + } + + void close() { + if (_logSink != null) { + try { + _logSink!.flush(); + _logSink!.close(); + } catch (e) { + print('Error closing log file: $e'); + } + _logSink = null; + } + } +} XmlElement findCaseInsensitive(XmlElement element, String name) { return element.childElements.firstWhere( @@ -52,6 +124,7 @@ class Mod { }); static Mod fromDirectory(String path, {bool skipFileCount = false}) { + final logger = Logger.instance; final stopwatch = Stopwatch()..start(); final aboutFile = File('$path/About/About.xml'); @@ -196,8 +269,8 @@ class Mod { stopwatch.elapsedMilliseconds - metadataTime - xmlTime; final totalTime = stopwatch.elapsedMilliseconds; - // Uncomment for detailed timing - print( + // Log detailed timing information + logger.info( 'Mod $name timing: XML=${xmlTime}ms, Metadata=${metadataTime}ms, FileCount=${fileCountTime}ms, Total=${totalTime}ms', ); @@ -231,6 +304,8 @@ class ModList { // Simplified loading with config file first Future loadWithConfig({bool skipFileCount = false}) async { + final logger = Logger.instance; + // Clear existing state if reloading if (modsLoaded) { mods.clear(); @@ -241,7 +316,7 @@ class ModList { loadingStatus = 'Loading active mods from config...'; final stopwatch = Stopwatch()..start(); - print('Loading configuration from config file: $configPath'); + logger.info('Loading configuration from config file: $configPath'); try { // First, load the config file to get the list of active mods @@ -287,7 +362,7 @@ class ModList { if (!directory.existsSync()) { loadingStatus = 'Error: Mods root directory does not exist: $path'; - print(loadingStatus); + logger.error(loadingStatus); return; } @@ -297,7 +372,7 @@ class ModList { totalModsFound = modDirectories.length; loadingStatus = 'Found $totalModsFound mod directories. Loading...'; - print('Found ${modDirectories.length} mod directories (${stopwatch.elapsedMilliseconds}ms)'); + logger.info('Found ${modDirectories.length} mod directories (${stopwatch.elapsedMilliseconds}ms)'); for (final modDir in modDirectories) { try { @@ -351,21 +426,21 @@ class ModList { loadingStatus = 'Loaded $loadedModsCount/$totalModsFound mods...'; if (loadedModsCount % 50 == 0 || loadedModsCount == totalModsFound) { - print('Progress: Loaded $loadedModsCount mods (${stopwatch.elapsedMilliseconds}ms)'); + logger.info('Progress: Loaded $loadedModsCount mods (${stopwatch.elapsedMilliseconds}ms)'); } } catch (e) { - print('Error loading mod from directory: $modDir'); - print('Error: $e'); + logger.error('Error loading mod from directory: $modDir'); + logger.error('Error: $e'); } } modsLoaded = true; final totalTime = stopwatch.elapsedMilliseconds; loadingStatus = 'Completed! Loaded $loadedModsCount mods in ${totalTime}ms.'; - print('Loading complete! Loaded ${mods.length} mods in ${totalTime}ms'); + logger.info('Loading complete! Loaded ${mods.length} mods in ${totalTime}ms'); } catch (e) { loadingStatus = 'Error loading mods: $e'; - print(loadingStatus); + logger.error(loadingStatus); } } @@ -690,24 +765,25 @@ class ConfigFile { ConfigFile({required this.path, this.mods = const []}); Future load() async { + final logger = Logger.instance; final file = File(path); - print('Loading configuration from: $path'); + logger.info('Loading configuration from: $path'); try { final xmlString = file.readAsStringSync(); - print('XML content read successfully.'); + logger.info('XML content read successfully.'); final xmlDocument = XmlDocument.parse(xmlString); - print('XML document parsed successfully.'); + logger.info('XML document parsed successfully.'); final modConfigData = xmlDocument.findElements("ModsConfigData").first; - print('Found ModsConfigData element.'); + logger.info('Found ModsConfigData element.'); final modsElement = modConfigData.findElements("activeMods").first; - print('Found activeMods element.'); + logger.info('Found activeMods element.'); final modElements = modsElement.findElements("li"); - print('Found ${modElements.length} active mods.'); + logger.info('Found ${modElements.length} active mods.'); // Get the list of known expansions final knownExpansionsElement = modConfigData.findElements("knownExpansions").firstOrNull; @@ -715,7 +791,7 @@ class ConfigFile { ? knownExpansionsElement.findElements("li").map((e) => e.innerText.toLowerCase()).toList() : []; - print('Found ${knownExpansionIds.length} known expansions.'); + logger.info('Found ${knownExpansionIds.length} known expansions.'); // Clear and recreate the mods list mods = []; @@ -749,9 +825,9 @@ class ConfigFile { ); } - print('Loaded ${mods.length} mods from config file.'); + logger.info('Loaded ${mods.length} mods from config file.'); } catch (e) { - print('Error loading configuration file: $e'); + logger.error('Error loading configuration file: $e'); throw Exception('Failed to load config file: $e'); } } @@ -767,13 +843,14 @@ class ConfigFile { // Save the current mod order back to the config file void save() { + final logger = Logger.instance; final file = File(path); - print('Saving configuration to: $path'); + logger.info('Saving configuration to: $path'); // Create a backup just in case final backupPath = '$path.bak'; file.copySync(backupPath); - print('Created backup at: $backupPath'); + logger.info('Created backup at: $backupPath'); try { // Load the existing XML @@ -798,16 +875,17 @@ class ConfigFile { // Write the updated XML back to the file file.writeAsStringSync(xmlDocument.toXmlString(pretty: true)); - print('Configuration saved successfully with ${mods.length} mods.'); + logger.info('Configuration saved successfully with ${mods.length} mods.'); } catch (e) { - print('Error saving configuration: $e'); - print('Original configuration preserved at: $backupPath'); + logger.error('Error saving configuration: $e'); + logger.info('Original configuration preserved at: $backupPath'); } } // Fix the load order of mods according to dependencies void fixLoadOrder(ModList modList) { - print("Fixing mod load order..."); + final logger = Logger.instance; + logger.info("Fixing mod load order..."); // Get the ordered mod IDs from the mod list final orderedIds = modList.sortMods(); @@ -836,7 +914,7 @@ class ConfigFile { // Replace the current mods list with the ordered one mods = orderedMods; - print( + logger.info( "Load order fixed. ${mods.length} mods are now in dependency-sorted order.", ); } diff --git a/pubspec.lock b/pubspec.lock index 524914b..24b2458 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -75,6 +75,14 @@ packages: description: flutter source: sdk version: "0.0.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" leak_tracker: dependency: transitive description: @@ -132,7 +140,7 @@ packages: source: hosted version: "1.16.0" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" diff --git a/pubspec.yaml b/pubspec.yaml index 81f4a6e..ee89dc9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,8 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 xml: ^6.5.0 + intl: ^0.20.2 + path: ^1.9.1 dev_dependencies: flutter_test: