DLCRunOrder
Tracking Issue: #511
Tags: compatibility
The base game and Highlander have many "DLC hooks": Overridable
functions in X2DownloadableContentInfo
that the game calls for all mods
in some order so that mods can do something. The most ubiquitous hook
is OnPostTemplatesCreated
, which allows mods to modify templates.
Unfortunately, the order in which the DLC hooks of mods are executed ("run order") depends on load order, which is not guaranteed. However, run order can matter a lot. Consider a mod that creates copies of guns with different visuals: This mod really wants to run after mods that make changes to specific guns (e.g. stat changes) so that the changes translate to the copies.
The CHL Run Order system provides two ways for mods to specify their position within the run order. This information is relayed to the Highlander using configuration entries (XComGame.ini):
[zzzWeaponSkinReplacer.X2DownloadableContentInfo_WeaponSkinReplacer]
DLCIdentifier="zzzWeaponSkinReplacer"
[zzzWeaponSkinReplacer CHDLCRunOrder]
+RunAfter=PrimarySecondaries
+RunAfter=XCOM2RPGOverhaul
+RunBefore=WOTCUnderbarrelAttachments
RunPriorityGroup=RUN_LAST
Since this system is all about DLC Hooks, the important identifier here is
the DLCIdentifier
corresponding to a X2DownloadableContentInfo
class,
of which a mod may have zero, one or more. The DLCIdentifier is case-sensitive.
Run Order is about DLCInfos, not individual mods, and a mod may have an
X2DLCInfo that runs before a certain other X2DLCInfo and another that runs
after that other one.
Coarse (RunPriorityGroup)
RunPriorityGroup
is a coarse way for DLCInfos to control when they run.
RunPriorityGroup can have three different values: RUN_STANDARD
,
RUN_FIRST
and RUN_LAST
. RUN_STANDARD
is the default. All DLCInfos
with RUN_FIRST
always run before all RUN_STANDARD
ones, and all
DLCInfos with RUN_LAST
always run after all RUN_STANDARD
ones.
A DLCInfo with RunPriorityGroup=RUN_LAST
already runs after the vast
majority of other DLCInfos.
Fine (RunBefore/RunAfter)
Within these groups, DLCInfos can specify which other DLCInfos they run
before and after. [A] +RunBefore="B"
is equivalent to specifying
[B] +RunAfter="A"
. If A
and B
were in a different
RunPriorityGroup
, their relative RunBefore
/RunAfter
lines would
be ignored.
Errors
The Highlander catches some potential configuration errors and writes them to the log. Warnings and errors are printed to the log, errors are shown to the user in combination with a list of DLCIdentifiers whose authors they should report the error to.
- It is a warning for a DLCInfo B to
RunAfter
a DLCInfo A, or A toRunBefore
B, if A is in an earlierRunPriorityGroup
than B, since these config lines are redundant and always fulfilled. - It is an error for a DLCInfo B to
RunAfter
a DLCInfo A, or A toRunBefore
B, if A is in a laterRunPriorityGroup
than B, since these config lines are contradictions and never fulfilled. - It is an error for any number of DLCInfos to cause a cycle, since cycles have no solution that fulfills all requirements.
The console command CHLDumpRunOrderInternals
can be used to print all
the information the CHL has about X2DownloadableContentInfo
classes
to the log, for debugging purposes.
One configuration error that can't be caught is a missing DLCIdentifier
.
If your X2DownloadableContentInfo
subclass specifies a custom config
file via config(CustomConfig)
, then the DLCIdentifier
needs to go in
XComCustomConfig.ini
, while the CHDLCRunOrder
still goes in
XComGame.ini
.
Splitting DLCInfo
Sometimes it can be useful to split your DLC Infos into two different
classes, one containing all changes that can run whenever (RUN_STANDARD
)
and one that needs to make its changes last. You can simply create more
subclasses and specify run order for any of them:
X2DownloadableContentInfo_NormalChanges.uc
class X2DownloadableContentInfo_NormalChanges extends X2DownloadableContentInfo;
static event OnPostTemplatesCreated()
{
// Make regular changes here
}
X2DownloadableContentInfo_LastChanges.uc
class X2DownloadableContentInfo_LastChanges extends X2DownloadableContentInfo;
static event OnPostTemplatesCreated()
{
// Make last changes here
}
XComGame.ini
[MyMod.X2DownloadableContentInfo_NormalChanges]
DLCIdentifier="MyModNormal"
[MyMod.X2DownloadableContentInfo_LastChanges]
DLCIdentifier="MyModLast"
[MyModLast CHDLCRunOrder]
RunPriorityGroup=RUN_LAST
This combats the trend of mods to move all their changes to RUN_LAST
because they contain only one change that actually needs to run last.
This trend can be problematic because it requires explicit RunAfter
annotations in other mods if they want to run after your normal changes.
Splitting DLCInfos can help mods that use RUN_LAST
do the right thing
by default.