107 lines
3.2 KiB
Dart
107 lines
3.2 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:rimworld_modman/logger.dart';
|
|
import 'package:rimworld_modman/mod.dart';
|
|
import 'package:rimworld_modman/mod_list.dart';
|
|
|
|
/// A class that helps find the minimum set of mods that exhibit a bug.
|
|
///
|
|
/// Provides two main algorithms:
|
|
/// - Binary search / bisect: Divides mods into smaller subsets to find problematic ones quickly.
|
|
/// - Linear search / batching: Tests mods in small groups to systematically identify issues.
|
|
///
|
|
/// These approaches help RimWorld mod users identify which mods are causing problems
|
|
/// when many mods are installed.
|
|
class ModListTroubleshooter {
|
|
final ModList originalModList;
|
|
ModList currentModList;
|
|
// These indices should ALWAYS represent the CURRENT selection of mods
|
|
int _startIndex = 0;
|
|
int _endIndex = 0;
|
|
|
|
ModListTroubleshooter(ModList modList)
|
|
: originalModList = modList,
|
|
currentModList = modList.copyWith(),
|
|
_startIndex = 0,
|
|
_endIndex = modList.activeMods.length;
|
|
|
|
ModList binaryForward() {
|
|
final midIndex = (_startIndex + _endIndex) ~/ 2;
|
|
final subset = originalModList.activeMods.keys.toList().sublist(
|
|
midIndex,
|
|
_endIndex,
|
|
);
|
|
currentModList.disableAll();
|
|
currentModList.enableMods(subset);
|
|
_startIndex = midIndex;
|
|
return currentModList;
|
|
}
|
|
|
|
ModList binaryBackward() {
|
|
final midIndex = ((_startIndex + _endIndex) / 2).ceil();
|
|
final subset = originalModList.activeMods.keys.toList().sublist(
|
|
_startIndex,
|
|
midIndex,
|
|
);
|
|
currentModList.disableAll();
|
|
currentModList.enableMods(subset);
|
|
_endIndex = midIndex;
|
|
return currentModList;
|
|
}
|
|
|
|
// If the current selection is not equal to our proposed step size
|
|
// We do not MOVE but instead just return the correct amount of mods from the start
|
|
ModList linearForward({int stepSize = 20}) {
|
|
// If we are not "in step"
|
|
if (_endIndex - _startIndex == stepSize) {
|
|
// Move the indices forward by the step size, step forward
|
|
_startIndex += stepSize;
|
|
_endIndex += stepSize;
|
|
} else {
|
|
// Correct the end index to be in step
|
|
_endIndex = _startIndex + stepSize;
|
|
}
|
|
if (_endIndex > originalModList.activeMods.length) {
|
|
// If we are at the end of the list, move the start index such that we return
|
|
// At most the step size amount of mods
|
|
_endIndex = originalModList.activeMods.length;
|
|
_startIndex = (_endIndex - stepSize).clamp(0, _endIndex);
|
|
}
|
|
|
|
final subset = originalModList.activeMods.keys.toList().sublist(
|
|
_startIndex,
|
|
_endIndex,
|
|
);
|
|
|
|
currentModList.disableAll();
|
|
currentModList.enableMods(subset);
|
|
return currentModList;
|
|
}
|
|
|
|
ModList linearBackward({int stepSize = 20}) {
|
|
if (_endIndex - _startIndex == stepSize) {
|
|
_startIndex -= stepSize;
|
|
_endIndex -= stepSize;
|
|
} else {
|
|
_startIndex = _endIndex - stepSize;
|
|
}
|
|
if (_startIndex < 0) {
|
|
_startIndex = 0;
|
|
_endIndex = stepSize.clamp(0, originalModList.activeMods.length);
|
|
}
|
|
|
|
final subset = originalModList.activeMods.keys.toList().sublist(
|
|
_startIndex,
|
|
_endIndex,
|
|
);
|
|
|
|
currentModList.disableAll();
|
|
currentModList.enableMods(subset);
|
|
return currentModList;
|
|
}
|
|
|
|
void reset() {
|
|
currentModList = originalModList.copyWith();
|
|
}
|
|
}
|