Refactor mod
This commit is contained in:
259
lib/mod.dart
Normal file
259
lib/mod.dart
Normal file
@@ -0,0 +1,259 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:rimworld_modman/logger.dart';
|
||||
import 'package:xml/xml.dart';
|
||||
|
||||
XmlElement findCaseInsensitive(XmlElement element, String name) {
|
||||
return element.childElements.firstWhere(
|
||||
(e) => e.name.local.toLowerCase() == name,
|
||||
);
|
||||
}
|
||||
|
||||
XmlElement findCaseInsensitiveDoc(XmlDocument document, String name) {
|
||||
name = name.toLowerCase();
|
||||
return document.childElements.firstWhere(
|
||||
(e) => e.name.local.toLowerCase() == name,
|
||||
);
|
||||
}
|
||||
|
||||
class Mod {
|
||||
final String name; // ModMetaData.name
|
||||
final String id; // ModMetaData.packageId
|
||||
final String path; // figure it out
|
||||
final List<String> versions; // ModMetaData.supportedVersions
|
||||
final String description; // ModMetaData.description
|
||||
final List<String> hardDependencies; // ModMetaData.modDependencies
|
||||
final List<String> loadAfter; // ModMetaData.loadAfter
|
||||
final List<String> loadBefore; // ModMetaData.loadBefore
|
||||
final List<String> incompatabilities; // ModMetaData.incompatibleWith
|
||||
final bool
|
||||
enabled; // ConfigFile.mods.firstWhere((mod) => mod.id == id).enabled
|
||||
final int size; // Count of files in the mod directory
|
||||
final bool isBaseGame; // Is this the base RimWorld game
|
||||
final bool isExpansion; // Is this a RimWorld expansion
|
||||
|
||||
Mod({
|
||||
required this.name,
|
||||
required this.id,
|
||||
required this.path,
|
||||
required this.versions,
|
||||
required this.description,
|
||||
required this.hardDependencies,
|
||||
required this.loadAfter,
|
||||
required this.loadBefore,
|
||||
required this.incompatabilities,
|
||||
required this.enabled,
|
||||
required this.size,
|
||||
this.isBaseGame = false,
|
||||
this.isExpansion = false,
|
||||
});
|
||||
|
||||
static Mod fromDirectory(String path, {bool skipFileCount = false}) {
|
||||
final logger = Logger.instance;
|
||||
final stopwatch = Stopwatch()..start();
|
||||
|
||||
logger.info('Attempting to load mod from directory: $path');
|
||||
final aboutFile = File('$path/About/About.xml');
|
||||
if (!aboutFile.existsSync()) {
|
||||
logger.error('About.xml file does not exist in $aboutFile');
|
||||
throw Exception('About.xml file does not exist in $aboutFile');
|
||||
}
|
||||
|
||||
logger.info('Parsing About.xml file...');
|
||||
final aboutXml = XmlDocument.parse(aboutFile.readAsStringSync());
|
||||
final xmlTime = stopwatch.elapsedMilliseconds;
|
||||
|
||||
late final XmlElement metadata;
|
||||
try {
|
||||
metadata = findCaseInsensitiveDoc(aboutXml, 'ModMetaData');
|
||||
logger.info('Successfully found ModMetaData in About.xml');
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
'Error: ModMetaData element is missing in About.xml ($aboutFile). Original error: $e',
|
||||
);
|
||||
throw Exception(
|
||||
'Error: ModMetaData element is missing in About.xml ($aboutFile). Original error: $e',
|
||||
);
|
||||
}
|
||||
|
||||
late final String name;
|
||||
try {
|
||||
name = metadata.findElements('name').first.innerText;
|
||||
logger.info('Mod name found: $name');
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
'Error: name element is missing in ModMetaData ($aboutFile). Original error: $e',
|
||||
);
|
||||
throw Exception(
|
||||
'Error: name element is missing in ModMetaData ($aboutFile). Original error: $e',
|
||||
);
|
||||
}
|
||||
|
||||
late final String id;
|
||||
try {
|
||||
id = metadata.findElements('packageId').first.innerText.toLowerCase();
|
||||
logger.info('Mod ID found: $id');
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
'Error: packageId element is missing in ModMetaData ($aboutFile). Original error: $e',
|
||||
);
|
||||
throw Exception(
|
||||
'Error: packageId element is missing in ModMetaData ($aboutFile). Original error: $e',
|
||||
);
|
||||
}
|
||||
|
||||
late final List<String> versions;
|
||||
try {
|
||||
versions =
|
||||
metadata
|
||||
.findElements('supportedVersions')
|
||||
.first
|
||||
.findElements('li')
|
||||
.map((e) => e.innerText)
|
||||
.toList();
|
||||
logger.info('Supported versions found: ${versions.join(", ")}');
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
'Error: supportedVersions or li elements are missing in ModMetaData ($aboutFile). Original error: $e',
|
||||
);
|
||||
throw Exception(
|
||||
'Error: supportedVersions or li elements are missing in ModMetaData ($aboutFile). Original error: $e',
|
||||
);
|
||||
}
|
||||
|
||||
String description = '';
|
||||
try {
|
||||
description = metadata.findElements('description').first.innerText;
|
||||
logger.info('Mod description found: $description');
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
'Description element is missing in ModMetaData ($aboutFile).',
|
||||
);
|
||||
}
|
||||
|
||||
List<String> hardDependencies = [];
|
||||
try {
|
||||
hardDependencies =
|
||||
metadata
|
||||
.findElements('modDependenciesByVersion')
|
||||
.first
|
||||
.children
|
||||
.whereType<XmlElement>()
|
||||
.last
|
||||
.findElements('li')
|
||||
.map(
|
||||
(e) =>
|
||||
e.findElements("packageId").first.innerText.toLowerCase(),
|
||||
)
|
||||
.toList();
|
||||
logger.info('Hard dependencies found: ${hardDependencies.join(", ")}');
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
'Hard dependencies element is missing in ModMetaData ($aboutFile).',
|
||||
);
|
||||
}
|
||||
|
||||
List<String> loadAfter = [];
|
||||
try {
|
||||
loadAfter =
|
||||
metadata
|
||||
.findElements('loadAfter')
|
||||
.first
|
||||
.findElements('li')
|
||||
.map((e) => e.innerText.toLowerCase())
|
||||
.toList();
|
||||
logger.info('Load after dependencies found: ${loadAfter.join(", ")}');
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
'Load after element is missing in ModMetaData ($aboutFile).',
|
||||
);
|
||||
}
|
||||
|
||||
List<String> loadBefore = [];
|
||||
try {
|
||||
loadBefore =
|
||||
metadata
|
||||
.findElements('loadBefore')
|
||||
.first
|
||||
.findElements('li')
|
||||
.map((e) => e.innerText.toLowerCase())
|
||||
.toList();
|
||||
logger.info('Load before dependencies found: ${loadBefore.join(", ")}');
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
'Load before element is missing in ModMetaData ($aboutFile).',
|
||||
);
|
||||
}
|
||||
|
||||
List<String> incompatabilities = [];
|
||||
try {
|
||||
incompatabilities =
|
||||
metadata
|
||||
.findElements('incompatibleWith')
|
||||
.first
|
||||
.findElements('li')
|
||||
.map((e) => e.innerText.toLowerCase())
|
||||
.toList();
|
||||
logger.info('Incompatibilities found: ${incompatabilities.join(", ")}');
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
'Incompatibilities element is missing in ModMetaData ($aboutFile).',
|
||||
);
|
||||
}
|
||||
|
||||
final metadataTime = stopwatch.elapsedMilliseconds - xmlTime;
|
||||
|
||||
int size = 0;
|
||||
if (!skipFileCount) {
|
||||
size =
|
||||
Directory(path)
|
||||
.listSync(recursive: true)
|
||||
.where(
|
||||
(entity) =>
|
||||
!entity.path
|
||||
.split(Platform.pathSeparator)
|
||||
.last
|
||||
.startsWith('.'),
|
||||
)
|
||||
.length;
|
||||
logger.info('File count in mod directory: $size');
|
||||
}
|
||||
|
||||
// Check if this is RimWorld base game or expansion
|
||||
bool isBaseGame = id == 'ludeon.rimworld';
|
||||
bool isExpansion = !isBaseGame && id.startsWith('ludeon.rimworld.');
|
||||
|
||||
// If this is an expansion, ensure it depends on the base game
|
||||
if (isExpansion && !loadAfter.contains('ludeon.rimworld')) {
|
||||
loadAfter.add('ludeon.rimworld');
|
||||
logger.info(
|
||||
'Added base game dependency for expansion mod: ludeon.rimworld',
|
||||
);
|
||||
}
|
||||
|
||||
final fileCountTime =
|
||||
stopwatch.elapsedMilliseconds - metadataTime - xmlTime;
|
||||
final totalTime = stopwatch.elapsedMilliseconds;
|
||||
|
||||
// Log detailed timing information
|
||||
logger.info(
|
||||
'Mod $name timing: XML=${xmlTime}ms, Metadata=${metadataTime}ms, FileCount=${fileCountTime}ms, Total=${totalTime}ms',
|
||||
);
|
||||
|
||||
return Mod(
|
||||
name: name,
|
||||
id: id,
|
||||
path: path,
|
||||
versions: versions,
|
||||
description: description,
|
||||
hardDependencies: hardDependencies,
|
||||
loadAfter: loadAfter,
|
||||
loadBefore: loadBefore,
|
||||
incompatabilities: incompatabilities,
|
||||
enabled: false,
|
||||
size: size,
|
||||
isBaseGame: isBaseGame,
|
||||
isExpansion: isExpansion,
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user