Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
turulix opened this issue Jun 8, 2023 · 11 comments

Comments

@turulix
Copy link

turulix commented Jun 8, 2023

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
Copy link

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

@turulix
Copy link
Author

turulix commented Jun 19, 2023

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
Copy link

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
Copy link
Author

turulix commented Jun 19, 2023

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

#!/bin/bash
set -eou pipefail
FACTORIO_VERSION=$1
MOD_DIR=$2
USERNAME=$3
TOKEN=$4
MOD_BASE_URL="https://mods.factorio.com"
print_step()
{
echo "$1"
}
print_success()
{
echo "$1"
}
print_failure()
{
echo "$1"
}
update_mod()
{
MOD_NAME="$1"
MOD_NAME_ENCODED="${1// /%20}"
print_step "Checking for update of mod $MOD_NAME..."
MOD_INFO_URL="$MOD_BASE_URL/api/mods/$MOD_NAME_ENCODED"
MOD_INFO_JSON=$(curl --silent "$MOD_INFO_URL")
if ! echo "$MOD_INFO_JSON" | jq -e .name >/dev/null; then
print_success " Custom mod not on $MOD_BASE_URL, skipped."
return 0
fi
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")
MOD_FILENAME=$(echo "$MOD_INFO" | cut -f1 -d";")
MOD_URL=$(echo "$MOD_INFO" | cut -f2 -d";")
MOD_SHA1=$(echo "$MOD_INFO" | cut -f3 -d";")
if [[ $MOD_FILENAME == null ]]; then
print_failure " Not compatible with version"
return 0
fi
if [[ -f $MOD_DIR/$MOD_FILENAME ]]; then
print_success " Already up-to-date."
return 0
fi
print_step "Downloading..."
FULL_URL="$MOD_BASE_URL$MOD_URL?username=$USERNAME&token=$TOKEN"
HTTP_STATUS=$(curl --silent -L -w "%{http_code}" -o "$MOD_DIR/$MOD_FILENAME" "$FULL_URL")
if [[ $HTTP_STATUS != 200 ]]; then
print_failure " Download failed: Code $HTTP_STATUS."
rm -f "$MOD_DIR/$MOD_FILENAME"
return 1
fi
if [[ ! -f $MOD_DIR/$MOD_FILENAME ]]; then
print_failure " Downloaded file missing!"
return 1
fi
if ! [[ $(sha1sum "$MOD_DIR/$MOD_FILENAME") =~ $MOD_SHA1 ]]; then
print_failure " SHA1 mismatch!"
rm -f "$MOD_DIR/$MOD_FILENAME"
return 1
fi
print_success " Download complete."
for file in "$MOD_DIR/${MOD_NAME}_"*".zip"; do # wildcard does usually not work in quotes: https://unix.stackexchange.com/a/67761
if [[ $file != $MOD_DIR/$MOD_FILENAME ]]; then
print_success " Deleting old version: $file"
rm -f "$file"
fi
done
return 0
}
if [[ -f $MOD_DIR/mod-list.json ]]; then
jq -r ".mods|map(select(.enabled))|.[].name" "$MOD_DIR/mod-list.json" | while read -r mod; do
if [[ $mod != base ]]; then
update_mod "$mod"
fi
done
fi

@github2023-blip
Copy link

github2023-blip commented Jun 19, 2023

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

./docker-update-mods.sh

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.

if [[ $MOD_FILENAME == null ]]; then


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:

MOD_INFO_URL="$MOD_BASE_URL/api/mods/$MOD_NAME_ENCODED"

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
Copy link

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
Copy link
Author

turulix commented Jun 20, 2023

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

@github2023-blip
Copy link

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

@Fank
Copy link
Member

Fank commented Jun 20, 2023

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

@KagurazakaNyaa
Copy link

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

./docker-update-mods.sh

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.

if [[ $MOD_FILENAME == null ]]; then

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:

MOD_INFO_URL="$MOD_BASE_URL/api/mods/$MOD_NAME_ENCODED"

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
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants