Cook up proper logging
This commit is contained in:
@@ -1,11 +1,17 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rimworld_modman/modloader.dart';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:rimworld_modman/modloader.dart';
|
||||||
|
|
||||||
// Global variable to store loaded mods for access across the app
|
// Global variable to store loaded mods for access across the app
|
||||||
late ModList modManager;
|
late ModList modManager;
|
||||||
|
|
||||||
void main() {
|
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
|
// Initialize the mod manager
|
||||||
modManager = ModList(path: modsRoot);
|
modManager = ModList(path: modsRoot);
|
||||||
|
|
||||||
|
@@ -1,11 +1,83 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:xml/xml.dart';
|
import 'package:xml/xml.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
const root = r'C:/Users/Administrator/Seafile/Games-Rimworld';
|
const root = r'C:/Users/Administrator/Seafile/Games-Rimworld';
|
||||||
const modsRoot = '$root/294100';
|
const modsRoot = '$root/294100';
|
||||||
const configRoot = '$root/AppData/RimWorld by Ludeon Studios/Config';
|
const configRoot = '$root/AppData/RimWorld by Ludeon Studios/Config';
|
||||||
const configPath = '$configRoot/ModsConfig.xml';
|
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) {
|
XmlElement findCaseInsensitive(XmlElement element, String name) {
|
||||||
return element.childElements.firstWhere(
|
return element.childElements.firstWhere(
|
||||||
@@ -52,6 +124,7 @@ class Mod {
|
|||||||
});
|
});
|
||||||
|
|
||||||
static Mod fromDirectory(String path, {bool skipFileCount = false}) {
|
static Mod fromDirectory(String path, {bool skipFileCount = false}) {
|
||||||
|
final logger = Logger.instance;
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
|
|
||||||
final aboutFile = File('$path/About/About.xml');
|
final aboutFile = File('$path/About/About.xml');
|
||||||
@@ -196,8 +269,8 @@ class Mod {
|
|||||||
stopwatch.elapsedMilliseconds - metadataTime - xmlTime;
|
stopwatch.elapsedMilliseconds - metadataTime - xmlTime;
|
||||||
final totalTime = stopwatch.elapsedMilliseconds;
|
final totalTime = stopwatch.elapsedMilliseconds;
|
||||||
|
|
||||||
// Uncomment for detailed timing
|
// Log detailed timing information
|
||||||
print(
|
logger.info(
|
||||||
'Mod $name timing: XML=${xmlTime}ms, Metadata=${metadataTime}ms, FileCount=${fileCountTime}ms, Total=${totalTime}ms',
|
'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
|
// Simplified loading with config file first
|
||||||
Future<void> loadWithConfig({bool skipFileCount = false}) async {
|
Future<void> loadWithConfig({bool skipFileCount = false}) async {
|
||||||
|
final logger = Logger.instance;
|
||||||
|
|
||||||
// Clear existing state if reloading
|
// Clear existing state if reloading
|
||||||
if (modsLoaded) {
|
if (modsLoaded) {
|
||||||
mods.clear();
|
mods.clear();
|
||||||
@@ -241,7 +316,7 @@ class ModList {
|
|||||||
loadingStatus = 'Loading active mods from config...';
|
loadingStatus = 'Loading active mods from config...';
|
||||||
|
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
print('Loading configuration from config file: $configPath');
|
logger.info('Loading configuration from config file: $configPath');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// First, load the config file to get the list of active mods
|
// First, load the config file to get the list of active mods
|
||||||
@@ -287,7 +362,7 @@ class ModList {
|
|||||||
|
|
||||||
if (!directory.existsSync()) {
|
if (!directory.existsSync()) {
|
||||||
loadingStatus = 'Error: Mods root directory does not exist: $path';
|
loadingStatus = 'Error: Mods root directory does not exist: $path';
|
||||||
print(loadingStatus);
|
logger.error(loadingStatus);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,7 +372,7 @@ class ModList {
|
|||||||
|
|
||||||
totalModsFound = modDirectories.length;
|
totalModsFound = modDirectories.length;
|
||||||
loadingStatus = 'Found $totalModsFound mod directories. Loading...';
|
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) {
|
for (final modDir in modDirectories) {
|
||||||
try {
|
try {
|
||||||
@@ -351,21 +426,21 @@ class ModList {
|
|||||||
loadingStatus = 'Loaded $loadedModsCount/$totalModsFound mods...';
|
loadingStatus = 'Loaded $loadedModsCount/$totalModsFound mods...';
|
||||||
|
|
||||||
if (loadedModsCount % 50 == 0 || loadedModsCount == totalModsFound) {
|
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) {
|
} catch (e) {
|
||||||
print('Error loading mod from directory: $modDir');
|
logger.error('Error loading mod from directory: $modDir');
|
||||||
print('Error: $e');
|
logger.error('Error: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
modsLoaded = true;
|
modsLoaded = true;
|
||||||
final totalTime = stopwatch.elapsedMilliseconds;
|
final totalTime = stopwatch.elapsedMilliseconds;
|
||||||
loadingStatus = 'Completed! Loaded $loadedModsCount mods in ${totalTime}ms.';
|
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) {
|
} catch (e) {
|
||||||
loadingStatus = 'Error loading mods: $e';
|
loadingStatus = 'Error loading mods: $e';
|
||||||
print(loadingStatus);
|
logger.error(loadingStatus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,24 +765,25 @@ class ConfigFile {
|
|||||||
ConfigFile({required this.path, this.mods = const []});
|
ConfigFile({required this.path, this.mods = const []});
|
||||||
|
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
|
final logger = Logger.instance;
|
||||||
final file = File(path);
|
final file = File(path);
|
||||||
print('Loading configuration from: $path');
|
logger.info('Loading configuration from: $path');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final xmlString = file.readAsStringSync();
|
final xmlString = file.readAsStringSync();
|
||||||
print('XML content read successfully.');
|
logger.info('XML content read successfully.');
|
||||||
|
|
||||||
final xmlDocument = XmlDocument.parse(xmlString);
|
final xmlDocument = XmlDocument.parse(xmlString);
|
||||||
print('XML document parsed successfully.');
|
logger.info('XML document parsed successfully.');
|
||||||
|
|
||||||
final modConfigData = xmlDocument.findElements("ModsConfigData").first;
|
final modConfigData = xmlDocument.findElements("ModsConfigData").first;
|
||||||
print('Found ModsConfigData element.');
|
logger.info('Found ModsConfigData element.');
|
||||||
|
|
||||||
final modsElement = modConfigData.findElements("activeMods").first;
|
final modsElement = modConfigData.findElements("activeMods").first;
|
||||||
print('Found activeMods element.');
|
logger.info('Found activeMods element.');
|
||||||
|
|
||||||
final modElements = modsElement.findElements("li");
|
final modElements = modsElement.findElements("li");
|
||||||
print('Found ${modElements.length} active mods.');
|
logger.info('Found ${modElements.length} active mods.');
|
||||||
|
|
||||||
// Get the list of known expansions
|
// Get the list of known expansions
|
||||||
final knownExpansionsElement = modConfigData.findElements("knownExpansions").firstOrNull;
|
final knownExpansionsElement = modConfigData.findElements("knownExpansions").firstOrNull;
|
||||||
@@ -715,7 +791,7 @@ class ConfigFile {
|
|||||||
? knownExpansionsElement.findElements("li").map((e) => e.innerText.toLowerCase()).toList()
|
? knownExpansionsElement.findElements("li").map((e) => e.innerText.toLowerCase()).toList()
|
||||||
: <String>[];
|
: <String>[];
|
||||||
|
|
||||||
print('Found ${knownExpansionIds.length} known expansions.');
|
logger.info('Found ${knownExpansionIds.length} known expansions.');
|
||||||
|
|
||||||
// Clear and recreate the mods list
|
// Clear and recreate the mods list
|
||||||
mods = [];
|
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) {
|
} catch (e) {
|
||||||
print('Error loading configuration file: $e');
|
logger.error('Error loading configuration file: $e');
|
||||||
throw Exception('Failed to load config 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
|
// Save the current mod order back to the config file
|
||||||
void save() {
|
void save() {
|
||||||
|
final logger = Logger.instance;
|
||||||
final file = File(path);
|
final file = File(path);
|
||||||
print('Saving configuration to: $path');
|
logger.info('Saving configuration to: $path');
|
||||||
|
|
||||||
// Create a backup just in case
|
// Create a backup just in case
|
||||||
final backupPath = '$path.bak';
|
final backupPath = '$path.bak';
|
||||||
file.copySync(backupPath);
|
file.copySync(backupPath);
|
||||||
print('Created backup at: $backupPath');
|
logger.info('Created backup at: $backupPath');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Load the existing XML
|
// Load the existing XML
|
||||||
@@ -798,16 +875,17 @@ class ConfigFile {
|
|||||||
|
|
||||||
// Write the updated XML back to the file
|
// Write the updated XML back to the file
|
||||||
file.writeAsStringSync(xmlDocument.toXmlString(pretty: true));
|
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) {
|
} catch (e) {
|
||||||
print('Error saving configuration: $e');
|
logger.error('Error saving configuration: $e');
|
||||||
print('Original configuration preserved at: $backupPath');
|
logger.info('Original configuration preserved at: $backupPath');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix the load order of mods according to dependencies
|
// Fix the load order of mods according to dependencies
|
||||||
void fixLoadOrder(ModList modList) {
|
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
|
// Get the ordered mod IDs from the mod list
|
||||||
final orderedIds = modList.sortMods();
|
final orderedIds = modList.sortMods();
|
||||||
@@ -836,7 +914,7 @@ class ConfigFile {
|
|||||||
// Replace the current mods list with the ordered one
|
// Replace the current mods list with the ordered one
|
||||||
mods = orderedMods;
|
mods = orderedMods;
|
||||||
|
|
||||||
print(
|
logger.info(
|
||||||
"Load order fixed. ${mods.length} mods are now in dependency-sorted order.",
|
"Load order fixed. ${mods.length} mods are now in dependency-sorted order.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
10
pubspec.lock
10
pubspec.lock
@@ -75,6 +75,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -132,7 +140,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.16.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||||
|
@@ -35,6 +35,8 @@ dependencies:
|
|||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
xml: ^6.5.0
|
xml: ^6.5.0
|
||||||
|
intl: ^0.20.2
|
||||||
|
path: ^1.9.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user