Add metrics and a UI of some sort
This commit is contained in:
@@ -1,16 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rimworld_modman/modloader.dart';
|
import 'package:rimworld_modman/modloader.dart';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
// 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() {
|
||||||
|
// Initialize the mod manager
|
||||||
modManager = ModList(path: modsRoot);
|
modManager = ModList(path: modsRoot);
|
||||||
modManager.load().listen((mod) {
|
|
||||||
print(mod);
|
// Start the app
|
||||||
});
|
runApp(const RimWorldModManager());
|
||||||
|
|
||||||
// runApp(const RimWorldModManager());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RimWorldModManager extends StatelessWidget {
|
class RimWorldModManager extends StatelessWidget {
|
||||||
@@ -95,14 +95,14 @@ class _ModListPageState extends State<ModListPage> {
|
|||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
String _loadingStatus = '';
|
String _loadingStatus = '';
|
||||||
int _totalModsFound = 0;
|
int _totalModsFound = 0;
|
||||||
|
bool _skipFileCount = true; // Skip file counting by default for faster loading
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body:
|
body: _loadedMods.isEmpty && !_isLoading
|
||||||
_loadedMods.isEmpty && !_isLoading
|
? _buildEmptyState()
|
||||||
? _buildEmptyState()
|
: _buildModList(),
|
||||||
: _buildModList(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +119,21 @@ class _ModListPageState extends State<ModListPage> {
|
|||||||
'Ready to scan for RimWorld mods.',
|
'Ready to scan for RimWorld mods.',
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: _skipFileCount,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_skipFileCount = value ?? true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Text('Skip file counting (faster loading)'),
|
||||||
|
],
|
||||||
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _startLoadingMods,
|
onPressed: _startLoadingMods,
|
||||||
@@ -205,14 +220,27 @@ class _ModListPageState extends State<ModListPage> {
|
|||||||
_loadingStatus = 'Scanning for mods...';
|
_loadingStatus = 'Scanning for mods...';
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use batch loading for better performance
|
// First get the mod directories to know the total count
|
||||||
|
final directory = Directory(modsRoot);
|
||||||
|
if (directory.existsSync()) {
|
||||||
|
final List<FileSystemEntity> entities = directory.listSync();
|
||||||
|
final List<String> modDirectories =
|
||||||
|
entities.whereType<Directory>().map((dir) => dir.path).toList();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_totalModsFound = modDirectories.length;
|
||||||
|
_loadingStatus = 'Found $_totalModsFound mod directories. Loading...';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the serial loading with our skipFileCount option
|
||||||
modManager
|
modManager
|
||||||
.load()
|
.load(skipFileCount: _skipFileCount)
|
||||||
.listen(
|
.listen(
|
||||||
(mod) {
|
(mod) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_loadedMods.add(mod);
|
_loadedMods.add(mod);
|
||||||
_loadingStatus = 'Loaded ${_loadedMods.length} mods...';
|
_loadingStatus = 'Loaded ${_loadedMods.length}/$_totalModsFound mods...';
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onError: (error) {
|
onError: (error) {
|
||||||
|
@@ -46,12 +46,16 @@ class Mod {
|
|||||||
required this.size,
|
required this.size,
|
||||||
});
|
});
|
||||||
|
|
||||||
static Mod fromDirectory(String path) {
|
static Mod fromDirectory(String path, {bool skipFileCount = false}) {
|
||||||
|
final stopwatch = Stopwatch()..start();
|
||||||
|
|
||||||
final aboutFile = File('$path/About/About.xml');
|
final aboutFile = File('$path/About/About.xml');
|
||||||
if (!aboutFile.existsSync()) {
|
if (!aboutFile.existsSync()) {
|
||||||
throw Exception('About.xml file does not exist in $aboutFile');
|
throw Exception('About.xml file does not exist in $aboutFile');
|
||||||
}
|
}
|
||||||
|
|
||||||
final aboutXml = XmlDocument.parse(aboutFile.readAsStringSync());
|
final aboutXml = XmlDocument.parse(aboutFile.readAsStringSync());
|
||||||
|
final xmlTime = stopwatch.elapsedMilliseconds;
|
||||||
|
|
||||||
late final XmlElement metadata;
|
late final XmlElement metadata;
|
||||||
try {
|
try {
|
||||||
@@ -99,7 +103,7 @@ class Mod {
|
|||||||
try {
|
try {
|
||||||
description = metadata.findElements('description').first.innerText;
|
description = metadata.findElements('description').first.innerText;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('$name has no description');
|
// Silent error for optional element
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> hardDependencies = [];
|
List<String> hardDependencies = [];
|
||||||
@@ -115,7 +119,7 @@ class Mod {
|
|||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('$name has no hard dependencies');
|
// Silent error for optional element
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> softDependencies = [];
|
List<String> softDependencies = [];
|
||||||
@@ -128,7 +132,7 @@ class Mod {
|
|||||||
.map((e) => e.innerText.toLowerCase())
|
.map((e) => e.innerText.toLowerCase())
|
||||||
.toList();
|
.toList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('$name has no soft dependencies');
|
// Silent error for optional element
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> incompatabilities = [];
|
List<String> incompatabilities = [];
|
||||||
@@ -141,21 +145,31 @@ class Mod {
|
|||||||
.map((e) => e.innerText.toLowerCase())
|
.map((e) => e.innerText.toLowerCase())
|
||||||
.toList();
|
.toList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('$name has no incompatabilities');
|
// Silent error for optional element
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final metadataTime = stopwatch.elapsedMilliseconds - xmlTime;
|
||||||
|
|
||||||
final size =
|
int size = 0;
|
||||||
Directory(path)
|
if (!skipFileCount) {
|
||||||
.listSync(recursive: true)
|
size = Directory(path)
|
||||||
.where(
|
.listSync(recursive: true)
|
||||||
(entity) =>
|
.where(
|
||||||
!entity.path
|
(entity) =>
|
||||||
.split(Platform.pathSeparator)
|
!entity.path
|
||||||
.last
|
.split(Platform.pathSeparator)
|
||||||
.startsWith('.'),
|
.last
|
||||||
)
|
.startsWith('.'),
|
||||||
.length;
|
)
|
||||||
|
.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
final fileCountTime = stopwatch.elapsedMilliseconds - metadataTime - xmlTime;
|
||||||
|
final totalTime = stopwatch.elapsedMilliseconds;
|
||||||
|
|
||||||
|
// Uncomment for detailed timing
|
||||||
|
print('Mod $name timing: XML=${xmlTime}ms, Metadata=${metadataTime}ms, FileCount=${fileCountTime}ms, Total=${totalTime}ms');
|
||||||
|
|
||||||
return Mod(
|
return Mod(
|
||||||
name: name,
|
name: name,
|
||||||
id: id,
|
id: id,
|
||||||
@@ -177,7 +191,8 @@ class ModList {
|
|||||||
|
|
||||||
ModList({required this.path});
|
ModList({required this.path});
|
||||||
|
|
||||||
Stream<Mod> load() async* {
|
Stream<Mod> load({bool skipFileCount = false}) async* {
|
||||||
|
final stopwatch = Stopwatch()..start();
|
||||||
final directory = Directory(path);
|
final directory = Directory(path);
|
||||||
print('Loading configuration from: $path');
|
print('Loading configuration from: $path');
|
||||||
|
|
||||||
@@ -186,32 +201,34 @@ class ModList {
|
|||||||
final List<String> modDirectories =
|
final List<String> modDirectories =
|
||||||
entities.whereType<Directory>().map((dir) => dir.path).toList();
|
entities.whereType<Directory>().map((dir) => dir.path).toList();
|
||||||
|
|
||||||
print('Found ${modDirectories.length} mod directories:');
|
print('Found ${modDirectories.length} mod directories (${stopwatch.elapsedMilliseconds}ms)');
|
||||||
|
int processedCount = 0;
|
||||||
|
int totalMods = modDirectories.length;
|
||||||
|
|
||||||
for (final modDir in modDirectories) {
|
for (final modDir in modDirectories) {
|
||||||
try {
|
try {
|
||||||
// Add a small delay to prevent UI freezing and give time for rendering
|
final modStart = stopwatch.elapsedMilliseconds;
|
||||||
await Future.delayed(const Duration(milliseconds: 5));
|
|
||||||
|
|
||||||
final mod = Mod.fromDirectory(modDir);
|
final mod = Mod.fromDirectory(modDir, skipFileCount: skipFileCount);
|
||||||
mods[mod.id] = mod;
|
mods[mod.id] = mod;
|
||||||
print(
|
processedCount++;
|
||||||
'Loaded mod: ${mod.name} (ID: ${mod.id}) from directory: $modDir. '
|
|
||||||
'Size: ${mod.size}, '
|
final modTime = stopwatch.elapsedMilliseconds - modStart;
|
||||||
'Hard Dependencies: ${mod.hardDependencies.join(', ')}, '
|
if (processedCount % 50 == 0 || processedCount == totalMods) {
|
||||||
'Soft Dependencies: ${mod.softDependencies.join(', ')}, '
|
print('Progress: Loaded $processedCount/$totalMods mods (${stopwatch.elapsedMilliseconds}ms, avg ${stopwatch.elapsedMilliseconds/processedCount}ms per mod)');
|
||||||
'Incompatibilities: ${mod.incompatabilities.join(', ')}',
|
}
|
||||||
);
|
|
||||||
|
|
||||||
yield mod;
|
yield mod;
|
||||||
print('Current total mods loaded: ${mods.length}');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error loading mod from directory: $modDir');
|
print('Error loading mod from directory: $modDir');
|
||||||
print('Error: $e');
|
print('Error: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final totalTime = stopwatch.elapsedMilliseconds;
|
||||||
|
print('Loading complete! Loaded ${mods.length} mods in ${totalTime}ms (${totalTime/mods.length}ms per mod)');
|
||||||
} else {
|
} else {
|
||||||
print('Mods root directory does not exist: $modsRoot');
|
print('Mods root directory does not exist: $path');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user