factoriotools / factorio-docker

Factorio headless server in a Docker container
https://hub.docker.com/r/factoriotools/factorio/
MIT License
926 stars 219 forks source link

Updating mods will update mods to a version no longer compatible with current Factorio version. #468

Open turulix opened 1 year ago

turulix commented 1 year ago

If you are running the stable branch of factorio, and a mod releases a new version that requires an experimental branch of factorio, it will still download and update the mod and therefor disabeling the mod entirely.

Reproduce: Use the current Factorio Stable Version (1.1.80) with UPDATE_MODS_ON_START flag. Install Rampant Version 3.3.2

It will then procede to update Rampant to version 3.3.3 which requires Factorio Version >=1.1.81 which is at this point still experimental. And therefor the mod will not load anymore.

github2023-blip commented 1 year ago

Not sure how this issue can be solved here, maybe adress Wube directly via their forum?

turulix commented 1 year ago

Was thinking about something like checking the version of the base dependency of a mod from its info.json before actually updating the mod. And then only proceede with updating it if its still compatible

github2023-blip commented 1 year ago

This sounds like a good suggestion to me. But, correct me if I'm wrong, the whole mod management is done directly from within factorio, isn't it? And therefore https://forums.factorio.com/viewforum.php?f=6 might be a good place to start a discussion?

turulix commented 1 year ago

Theres this logic inside this docker image thats responsible for updating the mods. So as far as i can see it isn't related to the actual factorio website / mod management

https://github.com/factoriotools/factorio-docker/blob/d7e6952db8fca853e34b85b209f8d967442a4bb4/docker/files/update-mods.sh#L1-L96

github2023-blip commented 1 year ago

Oh, I see now, thanks for pointing this out! The helper script is called, if the appropriate flag UPDATE_MODS_ON_START is set: https://github.com/factoriotools/factorio-docker/blob/d7e6952db8fca853e34b85b209f8d967442a4bb4/docker/files/docker-entrypoint.sh#L43 And then the file you mentioned is executed. :thinking:

It appears to me, that currently only the major and minor version of factorio is taken into consideration when updating mods. Because, in the info_json ( https://mods.factorio.com/api/mods/Rampant ) only the major/minor version is mentioned (i.e. factorio_version). And this checks, if a mod version matching the major/minor factorio version has been found. https://github.com/factoriotools/factorio-docker/blob/d7e6952db8fca853e34b85b209f8d967442a4bb4/docker/files/update-mods.sh#L47


To get the base dependency MOD_INFO_URL="$MOD_BASE_URL/api/mods/$MOD_NAME_ENCODED/full" (e.g. https://mods.factorio.com/api/mods/Rampant/full) would need to be called here: https://github.com/factoriotools/factorio-docker/blob/d7e6952db8fca853e34b85b209f8d967442a4bb4/docker/files/update-mods.sh#L33 As only that endpoint provides the dependencies: https://wiki.factorio.com/Mod_portal_API#Releases

And then use something like this: MOD_INFO=$(echo "$MOD_INFO_JSON" | jq -j --arg version "$FACTORIO_VERSION" ".releases|reverse|map(select(.info_json.factorio_version as \$mod_version | \$version | startswith(\$mod_version)))[0]|.file_name, \";\", .download_url, \";\", .sha1, \";\", .info_json.dependencies[0]")

(Which, for example, provides this output: Rampant_3.3.3.zip;/download/Rampant/6459c957d2fe774477f3f390;12271dbb9e63ecc1e13987fab7e4ec094b8e884c;base >= 1.1.81)

And that: MOD_BASE_DEPENDENCY=$(echo "$MOD_INFO" | cut -f4 -d";") to parse the base dependency, to be checked later. Not sure, how to do that properly in bash, though. (Which, for example, provides this output: base >= 1.1.81)

github2023-blip commented 1 year ago

A quickly cobbled together solution (thanks stackoverflow ;) ) for comparing the versions might be something like this (following the parsing of the json from above):

MOD_BASE_DEPENDENCY_VERSION=$(echo $MOD_BASE_DEPENDENCY | awk '{print $3}')

if [ $(echo -e "${MOD_BASE_DEPENDENCY_VERSION}}}\n${VERSION}"| sort -V | head -1) != "${MOD_BASE_DEPENDENCY_VERSION}}}" ]; then echo "Base dependency fullfilled."; else echo "Base dependency is not fulfilled.";fi
turulix commented 1 year ago

Yea, i don't know bash black magic sry can't help with that :D

github2023-blip commented 1 year ago

@Fank Would you be able to help us out here, on how to implement such a check?

Fank commented 1 year ago

Maybe @KagurazakaNyaa can provide some good input quicker than me :D

KagurazakaNyaa commented 1 year ago

Oh, I see now, thanks for pointing this out! The helper script is called, if the appropriate flag UPDATE_MODS_ON_START is set:

https://github.com/factoriotools/factorio-docker/blob/d7e6952db8fca853e34b85b209f8d967442a4bb4/docker/files/docker-entrypoint.sh#L43

And then the file you mentioned is executed. 🤔 It appears to me, that currently only the major and minor version of factorio is taken into consideration when updating mods. Because, in the info_json ( https://mods.factorio.com/api/mods/Rampant ) only the major/minor version is mentioned (i.e. factorio_version). And this checks, if a mod version matching the major/minor factorio version has been found.

https://github.com/factoriotools/factorio-docker/blob/d7e6952db8fca853e34b85b209f8d967442a4bb4/docker/files/update-mods.sh#L47

To get the base dependency MOD_INFO_URL="$MOD_BASE_URL/api/mods/$MOD_NAME_ENCODED/full" (e.g. https://mods.factorio.com/api/mods/Rampant/full) would need to be called here:

https://github.com/factoriotools/factorio-docker/blob/d7e6952db8fca853e34b85b209f8d967442a4bb4/docker/files/update-mods.sh#L33

As only that endpoint provides the dependencies: https://wiki.factorio.com/Mod_portal_API#Releases And then use something like this: MOD_INFO=$(echo "$MOD_INFO_JSON" | jq -j --arg version "$FACTORIO_VERSION" ".releases|reverse|map(select(.info_json.factorio_version as \$mod_version | \$version | startswith(\$mod_version)))[0]|.file_name, \";\", .download_url, \";\", .sha1, \";\", .info_json.dependencies[0]")

(Which, for example, provides this output: Rampant_3.3.3.zip;/download/Rampant/6459c957d2fe774477f3f390;12271dbb9e63ecc1e13987fab7e4ec094b8e884c;base >= 1.1.81)

And that: MOD_BASE_DEPENDENCY=$(echo "$MOD_INFO" | cut -f4 -d";") to parse the base dependency, to be checked later. Not sure, how to do that properly in bash, though. (Which, for example, provides this output: base >= 1.1.81)

There is a problem here, if a mod's dependency is indirectly dependent on an incorrect version of base, then we cannot detect this way, such as mod a==1.1 require b<=1.1 and a==1.2 require b<=1.2, and b==1.1 require base<=1.1.80 (that is, stable), b==1.2 require base<=1.1.85 (that is, latest) In this case, if we update a to the latest version, it will still break the dependency, and its dependency b will not be updated, because the detected version of base is incorrect

KagurazakaNyaa commented 1 year ago

However, if you can ensure that each mod includes its dependencies on base, then this solution is feasible. Maybe we can refer to https://github.com/cloudflare/semver_bash to compare the semver-style version number , only need to convert <= to semverLT, >= to semverGT such a comparison method We skip updates to this mod when dependencies are not met, but we still can't properly handle dependencies other than the base package unless we reimplement factorio's mod management system in the shell