Fix up activemods to be a String, Mod

This commit is contained in:
2025-03-18 00:01:05 +01:00
parent 72b6f3486d
commit 512bd644ab

View File

@@ -6,8 +6,9 @@ import 'package:rimworld_modman/mod.dart';
import 'package:xml/xml.dart'; import 'package:xml/xml.dart';
class LoadOrder { class LoadOrder {
final List<String> loadOrder = []; final List<Mod> order = [];
final List<String> errors = []; final List<String> errors = [];
List<String> get loadOrder => order.map((e) => e.id).toList();
LoadOrder(); LoadOrder();
@@ -18,7 +19,7 @@ class ModList {
String configPath = ''; String configPath = '';
String modsPath = ''; String modsPath = '';
// O(1) lookup // O(1) lookup
Map<String, bool> activeMods = {}; Map<String, Mod> activeMods = {};
Map<String, Mod> mods = {}; Map<String, Mod> mods = {};
ModList({this.configPath = '', this.modsPath = ''}); ModList({this.configPath = '', this.modsPath = ''});
@@ -202,9 +203,10 @@ class ModList {
void setEnabled(String modId, bool enabled) { void setEnabled(String modId, bool enabled) {
if (mods.containsKey(modId)) { if (mods.containsKey(modId)) {
mods[modId]!.enabled = enabled; final mod = mods[modId]!;
mod.enabled = enabled;
if (enabled) { if (enabled) {
activeMods[modId] = true; activeMods[modId] = mod;
} else { } else {
activeMods.remove(modId); activeMods.remove(modId);
} }
@@ -247,9 +249,10 @@ class ModList {
// return generateLoadOrder(loadOrder); // return generateLoadOrder(loadOrder);
//} //}
LoadOrder generateLoadOrder() { LoadOrder generateLoadOrder([LoadOrder? loadOrder]) {
final modMap = {for (final m in mods.values) m.id: m}; loadOrder ??= LoadOrder();
_validateIncompatibilities(mods.values.toList()); final modMap = {for (final m in activeMods.values) m.id: m};
_validateIncompatibilities(loadOrder);
// Hard dependency graph // Hard dependency graph
final inDegree = <String, int>{}; final inDegree = <String, int>{};
@@ -260,7 +263,7 @@ class ModList {
final reverseLoadAfter = <String, List<Mod>>{}; final reverseLoadAfter = <String, List<Mod>>{};
// Initialize data structures // Initialize data structures
for (final mod in mods.values) { for (final mod in activeMods.values) {
mod.loadBeforeNotPlaced = mod.loadBefore.length; mod.loadBeforeNotPlaced = mod.loadBefore.length;
mod.loadAfterPlaced = 0; mod.loadAfterPlaced = 0;
@@ -271,7 +274,7 @@ class ModList {
} }
// Build dependency graph and reverse soft constraints // Build dependency graph and reverse soft constraints
for (final mod in mods.values) { for (final mod in activeMods.values) {
for (final depId in mod.dependencies) { for (final depId in mod.dependencies) {
adjacency[depId]!.add(mod.id); adjacency[depId]!.add(mod.id);
inDegree[mod.id] = (inDegree[mod.id] ?? 0) + 1; inDegree[mod.id] = (inDegree[mod.id] ?? 0) + 1;
@@ -318,17 +321,15 @@ class ModList {
}); });
// Initialize heap with available mods // Initialize heap with available mods
for (final modId in activeMods.keys) { for (final mod in activeMods.values) {
final mod = modMap[modId]; if (inDegree[mod.id] == 0) {
if (mod != null && inDegree[modId] == 0) {
heap.add(mod); heap.add(mod);
} }
} }
final sortedMods = <Mod>[];
while (heap.isNotEmpty) { while (heap.isNotEmpty) {
final current = heap.removeFirst(); final current = heap.removeFirst();
sortedMods.add(current); loadOrder.order.add(current);
// Update dependents' in-degree // Update dependents' in-degree
for (final neighborId in adjacency[current.id]!) { for (final neighborId in adjacency[current.id]!) {
@@ -342,34 +343,32 @@ class ModList {
_updateReverseConstraints( _updateReverseConstraints(
current, current,
reverseLoadBefore, reverseLoadBefore,
sortedMods, loadOrder,
heap, heap,
(mod) => mod.loadBeforeNotPlaced--, (mod) => mod.loadBeforeNotPlaced--,
); );
_updateReverseConstraints( _updateReverseConstraints(
current, current,
reverseLoadAfter, reverseLoadAfter,
sortedMods, loadOrder,
heap, heap,
(mod) => mod.loadAfterPlaced++, (mod) => mod.loadAfterPlaced++,
); );
} }
if (sortedMods.length != mods.length) { if (loadOrder.order.length != activeMods.length) {
throw Exception("Cyclic dependencies detected"); loadOrder.errors.add("Cyclic dependencies detected");
} }
final loadOrder = LoadOrder();
loadOrder.loadOrder.addAll(sortedMods.map((e) => e.id));
return loadOrder; return loadOrder;
} }
void _validateIncompatibilities(List<Mod> mods) { void _validateIncompatibilities(LoadOrder loadOrder) {
final enabledMods = mods.where((m) => m.enabled).toList(); final enabledMods = loadOrder.order.where((m) => m.enabled).toList();
for (final mod in enabledMods) { for (final mod in enabledMods) {
for (final incompatibleId in mod.incompatibilities) { for (final incompatibleId in mod.incompatibilities) {
if (enabledMods.any((m) => m.id == incompatibleId)) { if (enabledMods.any((m) => m.id == incompatibleId)) {
throw Exception("Conflict: ${mod.id} vs $incompatibleId"); loadOrder.errors.add("Incompatible mods: ${mod.id} and $incompatibleId");
} }
} }
} }
@@ -378,12 +377,12 @@ class ModList {
void _updateReverseConstraints( void _updateReverseConstraints(
Mod current, Mod current,
Map<String, List<Mod>> reverseMap, Map<String, List<Mod>> reverseMap,
List<Mod> sortedMods, LoadOrder loadOrder,
PriorityQueue<Mod> heap, PriorityQueue<Mod> heap,
void Function(Mod) update, void Function(Mod) update,
) { ) {
reverseMap[current.id]?.forEach((affectedMod) { reverseMap[current.id]?.forEach((affectedMod) {
if (!sortedMods.contains(affectedMod)) { if (!loadOrder.order.contains(affectedMod)) {
update(affectedMod); update(affectedMod);
// If mod is already in heap, re-add to update position // If mod is already in heap, re-add to update position
if (heap.contains(affectedMod)) { if (heap.contains(affectedMod)) {
@@ -396,7 +395,7 @@ class ModList {
LoadOrder loadRequired() { LoadOrder loadRequired() {
final loadOrder = generateLoadOrder(); final loadOrder = generateLoadOrder();
for (final modId in loadOrder.loadOrder) { for (final modId in loadOrder.order.map((e) => e.id)) {
setEnabled(modId, true); setEnabled(modId, true);
} }
return loadOrder; return loadOrder;