Cutleast / Mod-Manager-Migrator

This is a tool for migrating modding instances between various mod managers.
https://www.nexusmods.com/site/mods/545
Other
12 stars 2 forks source link

[BUG] Some conflicts are improperly handled when migrating from Vortex to MO2 #11

Closed FORMless000 closed 5 months ago

FORMless000 commented 5 months ago

Describe the bug When I tried to migrate the Vortex collection Gate to Sovngarde to MO2, this happened: 52_`}RU5CBJDLK@X4Q CNI1 ED5HYN)T WK)OJ85 M$PH73

The mod "ELFX Shadows - Official Patches Hub - USSEP patch" should load after "ELFX Shadows Patches Hub" according to the Vortex rules, but it's not properly addressed in the generated MO2 load order.

I noticed this when I was trying to improve the loadorder generation from conflict rules to address for each mod's category. (Right now still struggling through everything) There are exactly 11 improperly addressed conflicts when I was migrating GTS. The script in the following can help to verify this:

def verify_order(loadorder):
    conflict_count = 0
    n = len(loadorder)
    for i in range(n):
        for j in range(i): # mod[j] before mod[i], and mod[j] should not supposed to be overwriting mod[i]
            if(loadorder[j] in loadorder[i].overwriting_mods):
                print("Conflict detected: ")
                print(f"Mod {j}: '{loadorder[j].name}' should be after")
                print(f"Mod {i}: '{loadorder[i].name}'")
                conflict_count += 1
    print(f"Conflict count: {conflict_count}.")
    return conflict_count

Problem lies in the mod sorting, at ln 610 of src/manager/vortex.py:

                if mod.overwriting_mods:
                    # self.log.debug(f"Sorting mod '{mod}'...")

                    old_index = index = new_loadorder.index(mod)

                    # Get smallest index of all overwriting mods
                    overwriting_mods = [
                        new_loadorder.index(overwriting_mod)
                        for overwriting_mod in mod.overwriting_mods
                    ]
                    index = min(overwriting_mods)
                    # self.log.debug(
                    #    f"Current index: {old_index} | Minimal index of overwriting_mods: {index}"
                    # )

                    if old_index > index:
                        new_loadorder.insert(index, new_loadorder.pop(old_index))
                        # self.log.debug(f"Moved mod '{mod}' from index {old_index} to {index}.")

My guess of what it does is simply iterating through every mod A once, and move it to smaller indices when some mod B that's supposed to be overwriting mod A is loaded before mod A. The move makes sure that mod A will be before mod B (and every mod that should be overwriting mod A).

I'm thinking it may break some other mod C that was and should be loaded before mod A, and is now loading after mod A after moving mod A. That might cause the improperly handled conflicts like the screenshots I listed above.

Cutleast commented 5 months ago

Thanks for pointing out! So it only happens when three (or more) mods conflict with each other, right? I'll take a deeper look at it, as soon as I got the time to do so.

FORMless000 commented 5 months ago

I'm looking over it, but I'm not sure if I'm competent enough to make an actual contribution to the code. Glad that I can get in touch!