1302 lines
50 KiB
Dart
1302 lines
50 KiB
Dart
//import 'dart:io';
|
|
//
|
|
//import 'package:flutter/material.dart';
|
|
//import 'package:rimworld_modman/logger.dart';
|
|
//import 'package:rimworld_modman/mod.dart';
|
|
//import 'package:rimworld_modman/mod_list.dart';
|
|
//
|
|
//// Constants for file paths
|
|
//final String root =
|
|
// Platform.isWindows
|
|
// ? r'C:/Users/Administrator/Seafile/Games-RimWorld'
|
|
// : '~/Library/Application Support/RimWorld';
|
|
//final String modsRoot = Platform.isWindows ? '$root/294100' : '$root/Mods';
|
|
//final String configRoot =
|
|
// Platform.isWindows
|
|
// ? '$root/AppData/RimWorld by Ludeon Studios/Config'
|
|
// : '$root/Config';
|
|
//final String configPath = '$configRoot/ModsConfig.xml';
|
|
//final String logsPath = '$root/ModManager';
|
|
//
|
|
//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(modsPath: modsRoot, configPath: configPath);
|
|
//
|
|
// // Start the app
|
|
// runApp(const RimWorldModManager());
|
|
//}
|
|
//
|
|
//class RimWorldModManager extends StatelessWidget {
|
|
// const RimWorldModManager({super.key});
|
|
//
|
|
// @override
|
|
// Widget build(BuildContext context) {
|
|
// return MaterialApp(
|
|
// title: 'RimWorld Mod Manager',
|
|
// theme: ThemeData.dark().copyWith(
|
|
// primaryColor: const Color(0xFF3D4A59),
|
|
// colorScheme: ColorScheme.fromSeed(
|
|
// seedColor: const Color(0xFF3D4A59),
|
|
// brightness: Brightness.dark,
|
|
// ),
|
|
// scaffoldBackgroundColor: const Color(0xFF1E262F),
|
|
// cardColor: const Color(0xFF2A3440),
|
|
// appBarTheme: const AppBarTheme(
|
|
// backgroundColor: Color(0xFF2A3440),
|
|
// foregroundColor: Colors.white,
|
|
// ),
|
|
// ),
|
|
// home: const ModManagerHomePage(),
|
|
// );
|
|
// }
|
|
//}
|
|
//
|
|
//class ModManagerHomePage extends StatefulWidget {
|
|
// const ModManagerHomePage({super.key});
|
|
//
|
|
// @override
|
|
// State<ModManagerHomePage> createState() => _ModManagerHomePageState();
|
|
//}
|
|
//
|
|
//class _ModManagerHomePageState extends State<ModManagerHomePage> {
|
|
// int _selectedIndex = 0;
|
|
//
|
|
// final List<Widget> _pages = [
|
|
// const ModManagerPage(),
|
|
// const TroubleshootingPage(),
|
|
// ];
|
|
//
|
|
// @override
|
|
// Widget build(BuildContext context) {
|
|
// return Scaffold(
|
|
// appBar: AppBar(title: const Text('RimWorld Mod Manager')),
|
|
// body: _pages[_selectedIndex],
|
|
// bottomNavigationBar: BottomNavigationBar(
|
|
// currentIndex: _selectedIndex,
|
|
// onTap: (index) {
|
|
// setState(() {
|
|
// _selectedIndex = index;
|
|
// });
|
|
// },
|
|
// items: const [
|
|
// BottomNavigationBarItem(icon: Icon(Icons.extension), label: 'Mods'),
|
|
// BottomNavigationBarItem(
|
|
// icon: Icon(Icons.build),
|
|
// label: 'Troubleshoot',
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// );
|
|
// }
|
|
//}
|
|
//
|
|
//// Combined page for mod management with two-panel layout
|
|
//class ModManagerPage extends StatefulWidget {
|
|
// const ModManagerPage({super.key});
|
|
//
|
|
// @override
|
|
// State<ModManagerPage> createState() => _ModManagerPageState();
|
|
//}
|
|
//
|
|
//class _ModManagerPageState extends State<ModManagerPage> {
|
|
// // For all available mods (left panel)
|
|
// List<Mod> _availableMods = [];
|
|
//
|
|
// // For active mods (right panel)
|
|
// List<Mod> _activeMods = [];
|
|
//
|
|
// bool _isLoading = false;
|
|
// String _statusMessage = '';
|
|
// int _totalModsFound = 0;
|
|
// bool _skipFileCount = false;
|
|
// bool _hasCycles = false;
|
|
// List<String>? _cycleInfo;
|
|
// List<List<String>> _incompatibleMods = [];
|
|
// List<String>? _loadOrderErrors;
|
|
//
|
|
// final TextEditingController _searchController = TextEditingController();
|
|
// String _searchQuery = '';
|
|
//
|
|
// @override
|
|
// void initState() {
|
|
// super.initState();
|
|
// // Check if mods are already loaded
|
|
// if (modManager.mods.isNotEmpty) {
|
|
// _loadModsFromGlobalState();
|
|
// }
|
|
//
|
|
// _searchController.addListener(() {
|
|
// setState(() {
|
|
// _searchQuery = _searchController.text.toLowerCase();
|
|
// });
|
|
// });
|
|
// }
|
|
//
|
|
// @override
|
|
// void dispose() {
|
|
// _searchController.dispose();
|
|
// super.dispose();
|
|
// }
|
|
//
|
|
// void _loadModsFromGlobalState() {
|
|
// setState(() {
|
|
// // Get all mods for the left panel (sorted alphabetically)
|
|
// _availableMods = modManager.mods.values.toList();
|
|
// _availableMods.sort((a, b) => a.name.compareTo(b.name));
|
|
//
|
|
// // Get active mods for the right panel (in load order)
|
|
// _activeMods = modManager.mods.values.where((m) => m.enabled).toList();
|
|
// _isLoading = false;
|
|
// _statusMessage = 'Loaded ${_availableMods.length} mods';
|
|
// _totalModsFound = _availableMods.length;
|
|
// });
|
|
// }
|
|
//
|
|
// @override
|
|
// Widget build(BuildContext context) {
|
|
// return Scaffold(
|
|
// body:
|
|
// _isLoading && _availableMods.isEmpty
|
|
// ? _buildLoadingView()
|
|
// : _availableMods.isEmpty
|
|
// ? _buildEmptyState()
|
|
// : _buildSplitView(),
|
|
// );
|
|
// }
|
|
//
|
|
// Widget _buildLoadingView() {
|
|
// return Center(
|
|
// child: Column(
|
|
// mainAxisAlignment: MainAxisAlignment.center,
|
|
// children: [
|
|
// const CircularProgressIndicator(),
|
|
// const SizedBox(height: 16),
|
|
// Text(
|
|
// _statusMessage,
|
|
// style: Theme.of(context).textTheme.bodyMedium,
|
|
// textAlign: TextAlign.center,
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// );
|
|
// }
|
|
//
|
|
// Widget _buildEmptyState() {
|
|
// return Center(
|
|
// child: Column(
|
|
// mainAxisAlignment: MainAxisAlignment.center,
|
|
// children: [
|
|
// const Icon(Icons.extension, size: 64),
|
|
// const SizedBox(height: 16),
|
|
// Text(
|
|
// 'Mod Manager',
|
|
// style: Theme.of(context).textTheme.headlineMedium,
|
|
// ),
|
|
// const SizedBox(height: 16),
|
|
// Text(
|
|
// 'Ready to scan for RimWorld mods.',
|
|
// 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),
|
|
// ElevatedButton(
|
|
// onPressed: _startLoadingMods,
|
|
// child: const Text('Scan for Mods'),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// );
|
|
// }
|
|
//
|
|
// Widget _buildSplitView() {
|
|
// // Filter both available and active mods based on search query
|
|
// final filteredAvailableMods =
|
|
// _searchQuery.isEmpty
|
|
// ? _availableMods
|
|
// : _availableMods
|
|
// .where(
|
|
// (mod) =>
|
|
// mod.name.toLowerCase().contains(_searchQuery) ||
|
|
// mod.id.toLowerCase().contains(_searchQuery),
|
|
// )
|
|
// .toList();
|
|
//
|
|
// final filteredActiveMods =
|
|
// _searchQuery.isEmpty
|
|
// ? _activeMods
|
|
// : _activeMods
|
|
// .where(
|
|
// (mod) =>
|
|
// mod.name.toLowerCase().contains(_searchQuery) ||
|
|
// mod.id.toLowerCase().contains(_searchQuery),
|
|
// )
|
|
// .toList();
|
|
//
|
|
// return Column(
|
|
// children: [
|
|
// Padding(
|
|
// padding: const EdgeInsets.all(8.0),
|
|
// child: Row(
|
|
// children: [
|
|
// // Search field
|
|
// Expanded(
|
|
// child: TextField(
|
|
// controller: _searchController,
|
|
// decoration: InputDecoration(
|
|
// hintText: 'Search mods by name or ID...',
|
|
// prefixIcon: const Icon(Icons.search),
|
|
// border: OutlineInputBorder(
|
|
// borderRadius: BorderRadius.circular(8.0),
|
|
// ),
|
|
// suffixIcon:
|
|
// _searchQuery.isNotEmpty
|
|
// ? IconButton(
|
|
// icon: const Icon(Icons.clear),
|
|
// onPressed: () {
|
|
// _searchController.clear();
|
|
// },
|
|
// )
|
|
// : null,
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// const SizedBox(width: 8),
|
|
// // Reload button
|
|
// IconButton(
|
|
// icon: const Icon(Icons.refresh),
|
|
// tooltip: 'Reload mods',
|
|
// onPressed: _startLoadingMods,
|
|
// ),
|
|
// const SizedBox(width: 8),
|
|
// // Load Dependencies button
|
|
// Tooltip(
|
|
// message:
|
|
// 'Automatically load missing dependencies for active mods',
|
|
// child: ElevatedButton.icon(
|
|
// icon: const Icon(Icons.download),
|
|
// label: const Text('Load Deps'),
|
|
// onPressed: _loadRequiredDependencies,
|
|
// ),
|
|
// ),
|
|
// const SizedBox(width: 8),
|
|
// // Auto-sort button
|
|
// ElevatedButton.icon(
|
|
// icon: const Icon(Icons.sort),
|
|
// label: const Text('Auto-Sort'),
|
|
// onPressed: _sortActiveMods,
|
|
// ),
|
|
// const SizedBox(width: 8),
|
|
// // Save button
|
|
// ElevatedButton.icon(
|
|
// icon: const Icon(Icons.save),
|
|
// label: const Text('Save'),
|
|
// onPressed: _saveModOrder,
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
//
|
|
// // Status message
|
|
// if (!_isLoading && _statusMessage.isNotEmpty)
|
|
// Padding(
|
|
// padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
|
// child: Text(
|
|
// _statusMessage,
|
|
// style: TextStyle(
|
|
// color:
|
|
// _hasCycles || _incompatibleMods.isNotEmpty
|
|
// ? Colors.orange
|
|
// : Colors.green,
|
|
// ),
|
|
// ),
|
|
// ),
|
|
//
|
|
// // Error display section
|
|
// if (_hasCycles ||
|
|
// _incompatibleMods.isNotEmpty ||
|
|
// (_loadOrderErrors?.isNotEmpty ?? false))
|
|
// Container(
|
|
// margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
|
// padding: const EdgeInsets.all(8.0),
|
|
// decoration: BoxDecoration(
|
|
// color: Colors.red.shade900.withOpacity(0.3),
|
|
// borderRadius: BorderRadius.circular(4.0),
|
|
// border: Border.all(color: Colors.red.shade800),
|
|
// ),
|
|
// child: Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// // Cycle warnings
|
|
// if (_hasCycles && _cycleInfo != null)
|
|
// Padding(
|
|
// padding: const EdgeInsets.only(bottom: 4.0),
|
|
// child: Row(
|
|
// children: [
|
|
// const Icon(Icons.loop, color: Colors.orange, size: 16),
|
|
// const SizedBox(width: 4),
|
|
// Expanded(
|
|
// child: Text(
|
|
// 'Dependency cycle detected: ${_cycleInfo!.join(" -> ")}',
|
|
// style: const TextStyle(color: Colors.orange),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
//
|
|
// // Incompatible mod warnings
|
|
// if (_incompatibleMods.isNotEmpty)
|
|
// Padding(
|
|
// padding: const EdgeInsets.only(bottom: 4.0),
|
|
// child: Row(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// const Icon(
|
|
// Icons.warning,
|
|
// color: Colors.orange,
|
|
// size: 16,
|
|
// ),
|
|
// const SizedBox(width: 4),
|
|
// Expanded(
|
|
// child: Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// Text(
|
|
// '${_incompatibleMods.length} incompatible mod pairs:',
|
|
// style: const TextStyle(color: Colors.orange),
|
|
// ),
|
|
// if (_incompatibleMods.length <= 3)
|
|
// ...List.generate(_incompatibleMods.length, (
|
|
// index,
|
|
// ) {
|
|
// final pair = _incompatibleMods[index];
|
|
// final mod1 =
|
|
// modManager.mods[pair[0]]?.name ?? pair[0];
|
|
// final mod2 =
|
|
// modManager.mods[pair[1]]?.name ?? pair[1];
|
|
// return Padding(
|
|
// padding: const EdgeInsets.only(
|
|
// left: 12.0,
|
|
// top: 2.0,
|
|
// ),
|
|
// child: Text(
|
|
// '• $mod1 ↔ $mod2',
|
|
// style: TextStyle(
|
|
// color: Colors.orange.shade300,
|
|
// fontSize: 12,
|
|
// ),
|
|
// ),
|
|
// );
|
|
// }),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
//
|
|
// // Other errors (missing dependencies, etc.)
|
|
// if (_loadOrderErrors?.isNotEmpty ?? false)
|
|
// Row(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// const Icon(
|
|
// Icons.error_outline,
|
|
// color: Colors.red,
|
|
// size: 16,
|
|
// ),
|
|
// const SizedBox(width: 4),
|
|
// Expanded(
|
|
// child: Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// const Text(
|
|
// 'Dependency errors:',
|
|
// style: TextStyle(color: Colors.red),
|
|
// ),
|
|
// ...List.generate(
|
|
// _loadOrderErrors!.length > 5
|
|
// ? 5
|
|
// : _loadOrderErrors!.length,
|
|
// (index) => Padding(
|
|
// padding: const EdgeInsets.only(
|
|
// left: 12.0,
|
|
// top: 2.0,
|
|
// ),
|
|
// child: Text(
|
|
// '• ${_loadOrderErrors![index]}',
|
|
// style: TextStyle(
|
|
// color: Colors.red.shade300,
|
|
// fontSize: 12,
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// if (_loadOrderErrors!.length > 5)
|
|
// Padding(
|
|
// padding: const EdgeInsets.only(
|
|
// left: 12.0,
|
|
// top: 4.0,
|
|
// ),
|
|
// child: Text(
|
|
// '(${_loadOrderErrors!.length - 5} more errors...)',
|
|
// style: TextStyle(
|
|
// color: Colors.red.shade300,
|
|
// fontSize: 12,
|
|
// fontStyle: FontStyle.italic,
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
//
|
|
// // Main split view
|
|
// Expanded(
|
|
// child: Row(
|
|
// children: [
|
|
// // LEFT PANEL - All available mods (alphabetical)
|
|
// Expanded(
|
|
// child: Card(
|
|
// margin: const EdgeInsets.all(8.0),
|
|
// child: Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
// children: [
|
|
// Container(
|
|
// color: Theme.of(context).primaryColor,
|
|
// padding: const EdgeInsets.all(8.0),
|
|
// child: Text(
|
|
// 'Available Mods (${filteredAvailableMods.length}${_searchQuery.isNotEmpty ? '/${_availableMods.length}' : ''})',
|
|
// style: const TextStyle(fontWeight: FontWeight.bold),
|
|
// textAlign: TextAlign.center,
|
|
// ),
|
|
// ),
|
|
// if (_searchQuery.isNotEmpty)
|
|
// Padding(
|
|
// padding: const EdgeInsets.all(4.0),
|
|
// child: Text(
|
|
// 'Searching: "$_searchQuery"',
|
|
// style: TextStyle(
|
|
// fontSize: 12,
|
|
// fontStyle: FontStyle.italic,
|
|
// color: Colors.grey.shade400,
|
|
// ),
|
|
// textAlign: TextAlign.center,
|
|
// ),
|
|
// ),
|
|
// Expanded(
|
|
// child: ListView.builder(
|
|
// itemCount: filteredAvailableMods.length,
|
|
// itemBuilder: (context, index) {
|
|
// final mod = filteredAvailableMods[index];
|
|
// final isActive = mod.enabled;
|
|
//
|
|
// return GestureDetector(
|
|
// onDoubleTap: () => _toggleModActive(mod),
|
|
// child: ListTile(
|
|
// title: Text(
|
|
// mod.name,
|
|
// style: TextStyle(
|
|
// color:
|
|
// isActive ? Colors.green : Colors.white,
|
|
// ),
|
|
// ),
|
|
// subtitle: Text(
|
|
// 'ID: ${mod.id}\nSize: ${mod.size} files',
|
|
// style: Theme.of(context).textTheme.bodySmall,
|
|
// ),
|
|
// trailing: Row(
|
|
// mainAxisSize: MainAxisSize.min,
|
|
// children: [
|
|
// if (mod.isBaseGame)
|
|
// const Tooltip(
|
|
// message: 'Base Game',
|
|
// child: Icon(
|
|
// Icons.home,
|
|
// color: Colors.blue,
|
|
// size: 24,
|
|
// ),
|
|
// ),
|
|
// if (mod.isExpansion)
|
|
// const Tooltip(
|
|
// message: 'Expansion',
|
|
// child: Icon(
|
|
// Icons.star,
|
|
// color: Colors.yellow,
|
|
// size: 24,
|
|
// ),
|
|
// ),
|
|
// const SizedBox(width: 4),
|
|
// if (mod.dependencies.isNotEmpty)
|
|
// Tooltip(
|
|
// message:
|
|
// 'Dependencies:\n${mod.dependencies.join('\n')}',
|
|
// child: const Icon(
|
|
// Icons.link,
|
|
// color: Colors.orange,
|
|
// size: 24,
|
|
// ),
|
|
// ),
|
|
// if (mod.loadAfter.isNotEmpty)
|
|
// Tooltip(
|
|
// message:
|
|
// 'Loads after:\n${mod.loadAfter.join('\n')}',
|
|
// child: const Icon(
|
|
// Icons.arrow_downward,
|
|
// color: Colors.blue,
|
|
// size: 24,
|
|
// ),
|
|
// ),
|
|
// if (mod.loadBefore.isNotEmpty)
|
|
// Tooltip(
|
|
// message:
|
|
// 'Loads before:\n${mod.loadBefore.join('\n')}',
|
|
// child: const Icon(
|
|
// Icons.arrow_upward,
|
|
// color: Colors.green,
|
|
// size: 24,
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// onTap: () {
|
|
// // Show mod details in future
|
|
// },
|
|
// ),
|
|
// );
|
|
// },
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// ),
|
|
//
|
|
// // RIGHT PANEL - Active mods (load order)
|
|
// Expanded(
|
|
// child: Card(
|
|
// margin: const EdgeInsets.all(8.0),
|
|
// child: Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
// children: [
|
|
// Container(
|
|
// color: Theme.of(context).primaryColor,
|
|
// padding: const EdgeInsets.all(8.0),
|
|
// child: Text(
|
|
// 'Active Mods (${filteredActiveMods.length}${_searchQuery.isNotEmpty ? '/${_activeMods.length}' : ''})',
|
|
// style: const TextStyle(fontWeight: FontWeight.bold),
|
|
// textAlign: TextAlign.center,
|
|
// ),
|
|
// ),
|
|
// Padding(
|
|
// padding: const EdgeInsets.all(8.0),
|
|
// child: Text(
|
|
// _searchQuery.isNotEmpty
|
|
// ? 'Searching: "$_searchQuery"'
|
|
// : 'Larger mods are prioritized during auto-sorting.',
|
|
// style: TextStyle(
|
|
// fontSize: 12,
|
|
// color: Colors.grey.shade400,
|
|
// fontStyle: FontStyle.italic,
|
|
// ),
|
|
// textAlign: TextAlign.center,
|
|
// ),
|
|
// ),
|
|
// Expanded(
|
|
// child: ListView.builder(
|
|
// itemCount: filteredActiveMods.length,
|
|
// itemBuilder: (context, index) {
|
|
// final mod = filteredActiveMods[index];
|
|
// // Find the actual position in the complete active mods list (for correct load order)
|
|
// final actualLoadOrderPosition =
|
|
// _activeMods.indexWhere((m) => m.id == mod.id) +
|
|
// 1;
|
|
//
|
|
// return GestureDetector(
|
|
// onDoubleTap: () {
|
|
// // Don't allow deactivating base game or expansions
|
|
// if (!mod.isBaseGame && !mod.isExpansion) {
|
|
// _toggleModActive(mod);
|
|
// } else {
|
|
// ScaffoldMessenger.of(context).showSnackBar(
|
|
// const SnackBar(
|
|
// content: Text(
|
|
// 'Core game and expansions cannot be deactivated',
|
|
// ),
|
|
// backgroundColor: Colors.orange,
|
|
// ),
|
|
// );
|
|
// }
|
|
// },
|
|
// child: Card(
|
|
// margin: const EdgeInsets.symmetric(
|
|
// horizontal: 8.0,
|
|
// vertical: 4.0,
|
|
// ),
|
|
// child: ListTile(
|
|
// leading: SizedBox(
|
|
// width: 50,
|
|
// child: Column(
|
|
// mainAxisSize: MainAxisSize.min,
|
|
// mainAxisAlignment:
|
|
// MainAxisAlignment.center,
|
|
// children: [
|
|
// SizedBox(
|
|
// height: 24,
|
|
// child: Container(
|
|
// padding: const EdgeInsets.symmetric(
|
|
// horizontal: 4,
|
|
// ),
|
|
// decoration: BoxDecoration(
|
|
// color:
|
|
// _searchQuery.isNotEmpty
|
|
// ? Colors.blue.withOpacity(
|
|
// 0.2,
|
|
// )
|
|
// : null,
|
|
// borderRadius:
|
|
// BorderRadius.circular(4),
|
|
// ),
|
|
// child: Tooltip(
|
|
// message:
|
|
// 'Load position: $actualLoadOrderPosition of ${_activeMods.length}${_searchQuery.isNotEmpty ? " (preserved when filtering)" : ""}',
|
|
// child: Text(
|
|
// '$actualLoadOrderPosition',
|
|
// style: TextStyle(
|
|
// fontWeight: FontWeight.bold,
|
|
// fontSize: 12,
|
|
// color:
|
|
// _searchQuery.isNotEmpty
|
|
// ? Colors.blue.shade300
|
|
// : null,
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// SizedBox(
|
|
// height: 24,
|
|
// child: Center(
|
|
// child:
|
|
// mod.size > 0
|
|
// ? Tooltip(
|
|
// message:
|
|
// 'This mod contains ${mod.size} files.',
|
|
// child: Text(
|
|
// '${mod.size}',
|
|
// style: const TextStyle(
|
|
// fontSize: 10,
|
|
// color: Colors.grey,
|
|
// ),
|
|
// ),
|
|
// )
|
|
// : const SizedBox(),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// title: Text(mod.name),
|
|
// subtitle: Text(
|
|
// mod.id,
|
|
// style: const TextStyle(fontSize: 12),
|
|
// ),
|
|
// trailing: Row(
|
|
// mainAxisSize: MainAxisSize.min,
|
|
// children: [
|
|
// if (mod.isBaseGame)
|
|
// const Tooltip(
|
|
// message: 'Base Game',
|
|
// child: Icon(
|
|
// Icons.home,
|
|
// color: Colors.blue,
|
|
// size: 24,
|
|
// ),
|
|
// ),
|
|
// if (mod.isExpansion)
|
|
// const Tooltip(
|
|
// message: 'Expansion',
|
|
// child: Icon(
|
|
// Icons.star,
|
|
// color: Colors.yellow,
|
|
// size: 24,
|
|
// ),
|
|
// ),
|
|
// if (mod.dependencies.isNotEmpty)
|
|
// Tooltip(
|
|
// message:
|
|
// 'Dependencies:\n${mod.dependencies.join('\n')}',
|
|
// child: const Icon(
|
|
// Icons.link,
|
|
// color: Colors.orange,
|
|
// size: 24,
|
|
// ),
|
|
// ),
|
|
// const SizedBox(width: 4),
|
|
// if (mod.loadAfter.isNotEmpty)
|
|
// Tooltip(
|
|
// message:
|
|
// 'Loads after other mods:\n${mod.loadAfter.join('\n')}',
|
|
// child: const Icon(
|
|
// Icons.arrow_downward,
|
|
// color: Colors.blue,
|
|
// size: 24,
|
|
// ),
|
|
// ),
|
|
// const SizedBox(width: 4),
|
|
// if (mod.loadBefore.isNotEmpty)
|
|
// Tooltip(
|
|
// message:
|
|
// 'Loads before other mods:\n${mod.loadBefore.join('\n')}',
|
|
// child: const Icon(
|
|
// Icons.arrow_upward,
|
|
// color: Colors.green,
|
|
// size: 24,
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// onTap: () {
|
|
// // Show mod details in future
|
|
// },
|
|
// ),
|
|
// ),
|
|
// );
|
|
// },
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// );
|
|
// }
|
|
//
|
|
// void _startLoadingMods() {
|
|
// setState(() {
|
|
// _availableMods.clear();
|
|
// _activeMods.clear();
|
|
// _isLoading = true;
|
|
// _statusMessage = 'Scanning for mods...';
|
|
// _hasCycles = false;
|
|
// _cycleInfo = null;
|
|
// _incompatibleMods = [];
|
|
// });
|
|
//
|
|
// // Create an async function to load mods
|
|
// Future<void> loadMods() async {
|
|
// try {
|
|
// // First load available mods
|
|
// await for (final mod in modManager.loadAvailable()) {
|
|
// // Update UI for each mod loaded
|
|
// if (mounted) {
|
|
// setState(() {
|
|
// _statusMessage = 'Loaded mod: ${mod.name}';
|
|
// _totalModsFound = modManager.mods.length;
|
|
// });
|
|
// }
|
|
// }
|
|
//
|
|
// // Then load active mods from config
|
|
// await for (final mod in modManager.loadActive()) {
|
|
// // Update UI as active mods are loaded
|
|
// if (mounted) {
|
|
// setState(() {
|
|
// _statusMessage = 'Loading active mod: ${mod.name}';
|
|
// });
|
|
// }
|
|
// }
|
|
//
|
|
// // Update the UI with all loaded mods
|
|
// if (mounted) {
|
|
// _loadModsFromGlobalState();
|
|
// setState(() {
|
|
// _statusMessage =
|
|
// 'Loaded ${_availableMods.length} mods, ${_activeMods.length} active';
|
|
// });
|
|
// }
|
|
// } catch (error) {
|
|
// if (mounted) {
|
|
// setState(() {
|
|
// _isLoading = false;
|
|
// _statusMessage = 'Error loading mods: $error';
|
|
// });
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// // Start the loading process
|
|
// loadMods();
|
|
// }
|
|
//
|
|
// void _toggleModActive(Mod mod) {
|
|
// // Cannot deactivate base game or expansions
|
|
// if ((mod.isBaseGame || mod.isExpansion) && mod.enabled) {
|
|
// ScaffoldMessenger.of(context).showSnackBar(
|
|
// const SnackBar(
|
|
// content: Text('Core game and expansions cannot be deactivated'),
|
|
// backgroundColor: Colors.orange,
|
|
// ),
|
|
// );
|
|
// return;
|
|
// }
|
|
//
|
|
// // Get the current state before toggling
|
|
// final bool wasEnabled = mod.enabled;
|
|
//
|
|
// // Toggle the mod in the global mod manager
|
|
// modManager.setEnabled(mod.id, !wasEnabled);
|
|
// Logger.instance.info(
|
|
// 'Toggled mod ${mod.name} (${mod.id}) from ${wasEnabled ? 'enabled' : 'disabled'} to ${!wasEnabled ? 'enabled' : 'disabled'}',
|
|
// );
|
|
//
|
|
// // Update the UI
|
|
// setState(() {
|
|
// // Update in the available mods list
|
|
// final index = _availableMods.indexWhere((m) => m.id == mod.id);
|
|
// if (index >= 0) {
|
|
// _availableMods[index] = modManager.mods[mod.id]!;
|
|
// }
|
|
//
|
|
// // Update the active mods list
|
|
// _activeMods = modManager.mods.values.where((m) => m.enabled).toList();
|
|
//
|
|
// _statusMessage =
|
|
// 'Mod "${mod.name}" ${!wasEnabled ? 'activated' : 'deactivated'}';
|
|
// });
|
|
// }
|
|
//
|
|
// void _sortActiveMods() async {
|
|
// if (_activeMods.isEmpty) {
|
|
// setState(() {
|
|
// _statusMessage = 'No active mods to sort';
|
|
// });
|
|
// return;
|
|
// }
|
|
//
|
|
// setState(() {
|
|
// _isLoading = true;
|
|
// _statusMessage = 'Sorting mods based on dependencies...';
|
|
// _hasCycles = false;
|
|
// _cycleInfo = null;
|
|
// _incompatibleMods = [];
|
|
// _loadOrderErrors = null;
|
|
// });
|
|
//
|
|
// // Use a Future.delayed to allow the UI to update
|
|
// await Future.delayed(Duration.zero);
|
|
//
|
|
// try {
|
|
// final logger = Logger.instance;
|
|
// logger.info('Starting auto-sort of ${_activeMods.length} active mods');
|
|
//
|
|
// // Generate a load order for active mods
|
|
// final loadOrder = modManager.generateLoadOrder();
|
|
//
|
|
// // Store all errors
|
|
// if (loadOrder.hasErrors) {
|
|
// setState(() {
|
|
// _loadOrderErrors = List<String>.from(loadOrder.errors);
|
|
// });
|
|
//
|
|
// logger.warning(
|
|
// 'Found ${loadOrder.errors.length} errors during sorting',
|
|
// );
|
|
// for (final error in loadOrder.errors) {
|
|
// logger.warning(' - $error');
|
|
// }
|
|
//
|
|
// setState(() {
|
|
// _hasCycles = loadOrder.errors.any(
|
|
// (e) => e.contains('Cyclic dependency'),
|
|
// );
|
|
// if (_hasCycles) {
|
|
// // Extract cycle info from error message
|
|
// final cycleError = loadOrder.errors.firstWhere(
|
|
// (e) => e.contains('Cyclic dependency'),
|
|
// orElse: () => '',
|
|
// );
|
|
// logger.warning('Detected dependency cycle: $cycleError');
|
|
//
|
|
// if (cycleError.isNotEmpty) {
|
|
// // Extract cycle path from error message
|
|
// final startIndex = cycleError.indexOf(':');
|
|
// if (startIndex != -1) {
|
|
// final pathStr = cycleError.substring(startIndex + 1).trim();
|
|
// _cycleInfo = pathStr.split(' -> ');
|
|
// logger.info(
|
|
// 'Extracted cycle path: ${_cycleInfo!.join(" -> ")}',
|
|
// );
|
|
// }
|
|
// }
|
|
// }
|
|
// });
|
|
// } else {
|
|
// _loadOrderErrors = null;
|
|
// }
|
|
//
|
|
// // Check for incompatibilities
|
|
// _incompatibleMods = modManager.checkIncompatibilities(
|
|
// modManager.activeMods.keys.toList(),
|
|
// );
|
|
//
|
|
// if (_incompatibleMods.isNotEmpty) {
|
|
// logger.warning(
|
|
// 'Found ${_incompatibleMods.length} incompatible mod pairs:',
|
|
// );
|
|
// for (final pair in _incompatibleMods) {
|
|
// final mod1 = modManager.mods[pair[0]]?.name ?? pair[0];
|
|
// final mod2 = modManager.mods[pair[1]]?.name ?? pair[1];
|
|
// logger.warning(' - $mod1 is incompatible with $mod2');
|
|
// }
|
|
// }
|
|
//
|
|
// // Get sorted mods from the load order
|
|
// final List<Mod> sortedMods = [];
|
|
// for (final modId in loadOrder.loadOrder) {
|
|
// if (modManager.mods.containsKey(modId)) {
|
|
// sortedMods.add(modManager.mods[modId]!);
|
|
// }
|
|
// }
|
|
//
|
|
// logger.info(
|
|
// 'Sorting complete. Arranged ${sortedMods.length} mods in load order',
|
|
// );
|
|
// if (sortedMods.isNotEmpty) {
|
|
// logger.info(
|
|
// 'First 5 mods in order: ${sortedMods.take(5).map((m) => m.name).join(', ')}',
|
|
// );
|
|
// if (sortedMods.length > 5) {
|
|
// logger.info('... (${sortedMods.length - 5} more) ...');
|
|
// logger.info(
|
|
// 'Last 3 mods in order: ${sortedMods.reversed.take(3).map((m) => m.name).toList().reversed.join(', ')}',
|
|
// );
|
|
// }
|
|
// }
|
|
//
|
|
// setState(() {
|
|
// _activeMods = sortedMods;
|
|
// _isLoading = false;
|
|
// _statusMessage = 'Sorting complete! ${sortedMods.length} mods sorted.';
|
|
// if (_hasCycles) {
|
|
// _statusMessage += ' Warning: Dependency cycles were found and fixed.';
|
|
// }
|
|
// if (_incompatibleMods.isNotEmpty) {
|
|
// _statusMessage +=
|
|
// ' Warning: ${_incompatibleMods.length} incompatible mod pairs found.';
|
|
// }
|
|
// });
|
|
// } catch (e) {
|
|
// Logger.instance.error('Error during auto-sort: $e');
|
|
// setState(() {
|
|
// _isLoading = false;
|
|
// _statusMessage = 'Error sorting mods: $e';
|
|
// });
|
|
// }
|
|
// }
|
|
//
|
|
// void _saveModOrder() async {
|
|
// if (_activeMods.isEmpty) {
|
|
// setState(() {
|
|
// _statusMessage = 'No active mods to save';
|
|
// });
|
|
// return;
|
|
// }
|
|
//
|
|
// setState(() {
|
|
// _isLoading = true;
|
|
// _statusMessage = 'Saving mod load order...';
|
|
// });
|
|
//
|
|
// try {
|
|
// final logger = Logger.instance;
|
|
// logger.info(
|
|
// 'Saving mod load order for ${_activeMods.length} active mods to $configPath',
|
|
// );
|
|
//
|
|
// // Save the mod list to the XML config file
|
|
// final file = File(configPath);
|
|
// final buffer = StringBuffer();
|
|
//
|
|
// buffer.writeln('<?xml version="1.0" encoding="utf-8"?>');
|
|
// buffer.writeln('<ModsConfigData>');
|
|
// buffer.writeln(' <version>1</version>');
|
|
//
|
|
// // Write active mods
|
|
// buffer.writeln(' <activeMods>');
|
|
// for (final mod in _activeMods) {
|
|
// buffer.writeln(' <li>${mod.id}</li>');
|
|
// logger.info(' - Adding mod to config: ${mod.name} (${mod.id})');
|
|
// }
|
|
// buffer.writeln(' </activeMods>');
|
|
//
|
|
// // Count expansions
|
|
// final expansions = _availableMods.where((m) => m.isExpansion).toList();
|
|
// logger.info('Found ${expansions.length} expansions to include in config');
|
|
//
|
|
// // Add known expansions
|
|
// buffer.writeln(' <knownExpansions>');
|
|
// for (final mod in expansions) {
|
|
// buffer.writeln(' <li>${mod.id}</li>');
|
|
// logger.info(' - Adding expansion to config: ${mod.name} (${mod.id})');
|
|
// }
|
|
// buffer.writeln(' </knownExpansions>');
|
|
//
|
|
// buffer.writeln('</ModsConfigData>');
|
|
//
|
|
// // Ensure directory exists
|
|
// final directory = Directory(configRoot);
|
|
// if (!directory.existsSync()) {
|
|
// logger.info('Creating config directory: $configRoot');
|
|
// directory.createSync(recursive: true);
|
|
// }
|
|
//
|
|
// // Write to file
|
|
// logger.info('Writing config file to $configPath');
|
|
// await file.writeAsString(buffer.toString());
|
|
// logger.info('Successfully saved mod configuration');
|
|
//
|
|
// setState(() {
|
|
// _isLoading = false;
|
|
// _statusMessage = 'Mod load order saved successfully!';
|
|
// });
|
|
//
|
|
// // Show success message
|
|
// ScaffoldMessenger.of(context).showSnackBar(
|
|
// SnackBar(
|
|
// content: Text(
|
|
// 'Mod order saved to config. ${_activeMods.length} mods enabled.',
|
|
// ),
|
|
// backgroundColor: Colors.green,
|
|
// ),
|
|
// );
|
|
// } catch (e) {
|
|
// final logger = Logger.instance;
|
|
// logger.error('Error saving mod load order: $e');
|
|
//
|
|
// setState(() {
|
|
// _isLoading = false;
|
|
// _statusMessage = 'Error saving mod load order: $e';
|
|
// });
|
|
//
|
|
// // Show error message
|
|
// ScaffoldMessenger.of(context).showSnackBar(
|
|
// SnackBar(
|
|
// content: Text('Error saving mod order: $e'),
|
|
// backgroundColor: Colors.red,
|
|
// ),
|
|
// );
|
|
// }
|
|
// }
|
|
//
|
|
// // Load all required dependencies for active mods
|
|
// void _loadRequiredDependencies() async {
|
|
// if (_activeMods.isEmpty) {
|
|
// setState(() {
|
|
// _statusMessage = 'No active mods to load dependencies for';
|
|
// });
|
|
// return;
|
|
// }
|
|
//
|
|
// setState(() {
|
|
// _isLoading = true;
|
|
// _statusMessage = 'Loading required dependencies...';
|
|
// _hasCycles = false;
|
|
// _cycleInfo = null;
|
|
// _incompatibleMods = [];
|
|
// _loadOrderErrors = null;
|
|
// });
|
|
//
|
|
// // Use a Future.delayed to allow the UI to update
|
|
// await Future.delayed(Duration.zero);
|
|
//
|
|
// try {
|
|
// final logger = Logger.instance;
|
|
// logger.info(
|
|
// 'Starting dependency resolution for ${_activeMods.length} active mods',
|
|
// );
|
|
//
|
|
// // Get current active mod count for comparison
|
|
// final initialActiveCount = modManager.activeMods.length;
|
|
//
|
|
// // Load required dependencies and get the load order
|
|
// final loadOrder = modManager.loadRequired();
|
|
//
|
|
// // Store any errors
|
|
// if (loadOrder.hasErrors) {
|
|
// setState(() {
|
|
// _loadOrderErrors = List<String>.from(loadOrder.errors);
|
|
// });
|
|
//
|
|
// logger.warning(
|
|
// 'Found ${loadOrder.errors.length} errors during dependency loading',
|
|
// );
|
|
// for (final error in loadOrder.errors) {
|
|
// logger.warning(' - $error');
|
|
// }
|
|
//
|
|
// // Check for cycles
|
|
// setState(() {
|
|
// _hasCycles = loadOrder.errors.any(
|
|
// (e) => e.contains('Cyclic dependency'),
|
|
// );
|
|
// if (_hasCycles) {
|
|
// // Extract cycle info from error message
|
|
// final cycleError = loadOrder.errors.firstWhere(
|
|
// (e) => e.contains('Cyclic dependency'),
|
|
// orElse: () => '',
|
|
// );
|
|
// logger.warning('Detected dependency cycle: $cycleError');
|
|
//
|
|
// if (cycleError.isNotEmpty) {
|
|
// // Extract cycle path from error message
|
|
// final startIndex = cycleError.indexOf(':');
|
|
// if (startIndex != -1) {
|
|
// final pathStr = cycleError.substring(startIndex + 1).trim();
|
|
// _cycleInfo = pathStr.split(' -> ');
|
|
// logger.info(
|
|
// 'Extracted cycle path: ${_cycleInfo!.join(" -> ")}',
|
|
// );
|
|
// }
|
|
// }
|
|
// }
|
|
// });
|
|
// } else {
|
|
// _loadOrderErrors = null;
|
|
// }
|
|
//
|
|
// // Check for incompatibilities
|
|
// _incompatibleMods = modManager.checkIncompatibilities(
|
|
// modManager.activeMods.keys.toList(),
|
|
// );
|
|
//
|
|
// if (_incompatibleMods.isNotEmpty) {
|
|
// logger.warning(
|
|
// 'Found ${_incompatibleMods.length} incompatible mod pairs:',
|
|
// );
|
|
// for (final pair in _incompatibleMods) {
|
|
// final mod1 = modManager.mods[pair[0]]?.name ?? pair[0];
|
|
// final mod2 = modManager.mods[pair[1]]?.name ?? pair[1];
|
|
// logger.warning(' - $mod1 is incompatible with $mod2');
|
|
// }
|
|
// }
|
|
//
|
|
// // Get sorted mods from the load order
|
|
// final List<Mod> sortedMods = [];
|
|
// for (final modId in loadOrder.loadOrder) {
|
|
// if (modManager.mods.containsKey(modId)) {
|
|
// sortedMods.add(modManager.mods[modId]!);
|
|
// }
|
|
// }
|
|
//
|
|
// // Calculate how many dependencies were added
|
|
// final newModsCount = modManager.activeMods.length - initialActiveCount;
|
|
//
|
|
// logger.info(
|
|
// 'Dependency loading complete. Enabled $newModsCount new dependencies.',
|
|
// );
|
|
// if (newModsCount > 0) {
|
|
// logger.info('Newly enabled dependencies:');
|
|
// final activeModIds = modManager.activeMods.keys.toList();
|
|
// for (int i = 0; i < newModsCount; i++) {
|
|
// final modId = activeModIds[initialActiveCount + i];
|
|
// logger.info(' - ${modManager.mods[modId]?.name ?? modId} ($modId)');
|
|
// }
|
|
// }
|
|
//
|
|
// setState(() {
|
|
// _activeMods = sortedMods;
|
|
// _isLoading = false;
|
|
// if (newModsCount > 0) {
|
|
// _statusMessage = 'Added $newModsCount required dependencies!';
|
|
// } else {
|
|
// _statusMessage = 'All dependencies are already loaded.';
|
|
// }
|
|
//
|
|
// if (_hasCycles) {
|
|
// _statusMessage += ' Warning: Dependency cycles were found and fixed.';
|
|
// }
|
|
// if (_incompatibleMods.isNotEmpty) {
|
|
// _statusMessage +=
|
|
// ' Warning: ${_incompatibleMods.length} incompatible mod pairs found.';
|
|
// }
|
|
// });
|
|
// } catch (e) {
|
|
// Logger.instance.error('Error during dependency loading: $e');
|
|
// setState(() {
|
|
// _isLoading = false;
|
|
// _statusMessage = 'Error loading dependencies: $e';
|
|
// });
|
|
// }
|
|
// }
|
|
//}
|
|
//
|
|
//// Page for troubleshooting problematic mods
|
|
//class TroubleshootingPage extends StatelessWidget {
|
|
// const TroubleshootingPage({super.key});
|
|
//
|
|
// @override
|
|
// Widget build(BuildContext context) {
|
|
// return Center(
|
|
// child: Column(
|
|
// mainAxisAlignment: MainAxisAlignment.center,
|
|
// children: [
|
|
// const Icon(Icons.build, size: 64),
|
|
// const SizedBox(height: 16),
|
|
// Text(
|
|
// 'Troubleshooting',
|
|
// style: Theme.of(context).textTheme.headlineMedium,
|
|
// ),
|
|
// const SizedBox(height: 16),
|
|
// Padding(
|
|
// padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
|
// child: Text(
|
|
// 'Find problematic mods with smart batch testing.',
|
|
// style: Theme.of(context).textTheme.bodyLarge,
|
|
// textAlign: TextAlign.center,
|
|
// ),
|
|
// ),
|
|
// const SizedBox(height: 24),
|
|
// ElevatedButton(
|
|
// onPressed: () {
|
|
// // TODO: Implement troubleshooting wizard
|
|
// },
|
|
// child: const Text('Start Troubleshooting'),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// );
|
|
// }
|
|
//}
|
|
// |