-
Notifications
You must be signed in to change notification settings - Fork 49
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
Use a "filter" based system for more robust version resolution #155
Comments
Personal opinion: |
I spent some time thinking about the logic for how to change libium's upgrade code (the "check" function) to improve the built-in checking, add fallback support and even a way to plug-in the custom filtering for #29. But I'm interested to hear from @theRookieCoder if they have ideas for that already |
I think this is great solution actually! Currently my idea is to create multiple lists of the mods (maybe using a hashmap to reduce duplications) and filter them. Then we can intersect the lists to get the compatible mods, and of course pick the latest version. This would also allow for more constructive feedback on why a mod was unable to be resolved (#76) |
@theRookieCoder I came up with this idea. I know this post is long but I think the logic is good so I ended up making it more explicit. I think a lot of this will quickly make sense to you and I'm also happy to hear your feedback on this or more on the hash table idea. Filter SystemHierarchy: The github release / asset structure is hierarchical or like a tree with depth 2. The first level is the release, the second level is the asset within the release. Filters in this context can apply to either level, and in other contexts / problems this kind of thing might be useful for even deeper structures. Filter: A filter takes a set of objects (either releases in a repo, or assets within a release) and strategically prunes the set using tags, or pieces of info / metadata extracted (via regex) from the objects. A filter There are two types of filters we'll use:
New Compatibility Checking
[footnote 1] - I know libium just searches these in order, but for this set-based logic we can reduce this set by:
[footnote 2] - A previous regex I tried missed the Regexes Basic regexes: # You can try these on https://regexr.com or https://regex101.com
# version number - has numbers separated by '.'
([0-9]+)(\.([0-9]+))*
# Minecraft version numbers (V1): Extracts major version and minor version, also detects snapshots.
# Its possible this could pull out version numbers for things that are not minecraft.
# Note the period separator might be `.` or `-` or `_` in practice, but it probably
(?<![0-9])(([1-9])\.([0-9]+))(\.(0|([1-9]+))){0,1}(\+{0,1})|([0-9]{2}[a-z][0-9]{2}[a-z]) Minecraft Version Regex (V2): Way better.
(mc)(.?)[1-9].([0-9]+).(x|0|([1-9]+)){0,1}(\+{0,1})|
(?<![0-9])(
((1)\.([0-9]{2}))(\.(x|0|([1-9]+))){0,1}(\+{0,1})|
((1)\-([0-9]{2}))(\-(x|0|([1-9]+))){0,1}(\+{0,1})|
((1)\_([0-9]{2}))(\_(x|0|([1-9]+))){0,1}(\+{0,1}))|([0-9]{2}[a-z][0-9]{2}[a-z])
|
I'd like to contribute a basic implementation that I believe covers 80% of the use cases described: Whenever This could be done relatively easily and allows the workflow of "set new Minecraft version, try to upgrade everything at once, test if mods are still compatible, upgrade to guaranteed compatible versions eventually" that I personally use for minor releases. Regarding the complete implementation, I believe you would have to have some sort of constraint solver to resolve valid version sets reliably. The |
Here's a quick-and-dirty proof-of-concept to get my modpack updated: Index: src/upgrade/check.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/upgrade/check.rs b/src/upgrade/check.rs
--- a/src/upgrade/check.rs (revision 337cfe81412aa180f2646f26623c9285a17dc914)
+++ b/src/upgrade/check.rs (date 1658983776949)
@@ -1,13 +1,25 @@
-use crate::{config::structs::ModLoader, version_ext::VersionExt};
use ferinth::structures::version_structs::{Version, VersionFile};
use furse::structures::file_structs::File;
+use lazy_regex::regex_find;
use octocrab::models::repos::{Asset, Release};
+use crate::{config::structs::ModLoader, version_ext::VersionExt};
+
+fn strip_minor_mc_version(version: &str) -> String {
+ let result = regex_find!(r#"\d+\.\d+"#x, version);
+ return result.unwrap_or(version).to_string();
+}
+
/// Check if the target `to_check` version is present in `game_versions`.
fn check_game_version(game_versions: &[String], to_check: &str) -> bool {
game_versions.iter().any(|version| version == to_check)
}
+/// Check if the target `to_check` version or a slightly older version is present in `game_versions`.
+fn check_game_version_relaxed(game_versions: &[String], to_check: &str) -> bool {
+ game_versions.iter().any(|version| version == to_check || version == &strip_minor_mc_version(to_check))
+}
+
/// Check if the target `to_check` mod loader is present in `mod_loaders`
fn check_mod_loader(mod_loaders: &[String], to_check: &ModLoader) -> bool {
mod_loaders
@@ -27,11 +39,21 @@
files.sort_unstable_by_key(|file| file.file_date);
files.reverse();
- for file in files {
+ for file in files.iter() {
if (Some(false) == should_check_game_version
|| check_game_version(&file.game_versions, game_version_to_check))
&& (Some(false) == should_check_mod_loader
|| check_mod_loader(&file.game_versions, mod_loader_to_check))
+ {
+ return Some(file);
+ }
+ }
+ // No exact match found, relax to allow slightly older versions
+ for file in files.iter() {
+ if (Some(false) == should_check_game_version
+ || check_game_version_relaxed(&file.game_versions, game_version_to_check))
+ && (Some(false) == should_check_mod_loader
+ || check_mod_loader(&file.game_versions, mod_loader_to_check))
{
return Some(file);
}
@@ -47,11 +69,21 @@
should_check_game_version: Option<bool>,
should_check_mod_loader: Option<bool>,
) -> Option<(&'a VersionFile, &'a Version)> {
- for version in versions {
+ for version in versions.iter() {
if (Some(false) == should_check_game_version
|| check_game_version(&version.game_versions, game_version_to_check))
&& (Some(false) == should_check_mod_loader
|| check_mod_loader(&version.loaders, mod_loader_to_check))
+ {
+ return Some((version.get_version_file(), version));
+ }
+ }
+ // No exact match found, relax to allow slightly older versions
+ for version in versions.iter() {
+ if (Some(false) == should_check_game_version
+ || check_game_version_relaxed(&version.game_versions, game_version_to_check))
+ && (Some(false) == should_check_mod_loader
+ || check_mod_loader(&version.loaders, mod_loader_to_check))
{
return Some((version.get_version_file(), version));
}
Index: Cargo.toml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/Cargo.toml b/Cargo.toml
--- a/Cargo.toml (revision 337cfe81412aa180f2646f26623c9285a17dc914)
+++ b/Cargo.toml (date 1658982430736)
@@ -34,6 +34,7 @@
] }
tokio = { version = "~1.20.0", default-features = false, features = ["fs"] }
rfd = { version = "~0.9.1", default-features = false, optional = true }
+lazy-regex = "~2.3.0"
serde = { version = "~1.0.139", features = ["derive"] }
clap = { version = "~3.2.12", features = ["derive"] }
url = { version = "~2.2.2", features = ["serde"] } |
Please add onto this resolution logic the scanning of release names for game versions and mod loaders, not only assets, the current scanning of only assets prevents Earthcomputer/clientcommands from working without both Sure it doesn't have any mod loader indicator but it only supports Fabric, so using |
I've decided to call these filters. Basically, they will run on the list of versions that a project has. For example if there is a mod loader filter, you can set it to filter out only Fabric mods, or allow both Fabric and Quilt mods (which will be the default when running Quilt). Then another filter, like a game version filter, will run again on the list of mods to determine versions compatible with the game version configured. The release channel can also be a filter. These will then be intersected to form a final list of versions, from which the latest one will be picked automatically or this list will be shown to the user (#95). The intersection feature will hopefully resolve #76. Hopefully being able to manually pick exactly what to filter will make ferium's version resolution very powerful. |
Here is a list of filters I plan on implementing. Please do provide feedback.
These will all contain lists of values, so they will be highly configurable. There will also be 'negative' variants to exclude patterns, specifically for the GitHub ones. |
The custom URL download will be part of #141 |
Tasks
game_version
andmod_loader
fields to filtersFeedback on why the version was considered incompatible #76
This will allow all game versions that were considered to not have significant changes between them, e.g.
1.21.2
and1.21.3
. This is determined using Modrinth's version list tag.Add support for release channels #196
Alpha and Beta will be mapped to prerelease in GitHub releases.
The Problem
--dont-check-game-version
or the config option"check_game_version": false
which introduces its own issues if either a) the profile is for a non-current game version, or b) ferium can't determine what game version the mod is for. And after updating mods or the profile game version, it can be tedious to review which mods dont need the"check_game_version": false
setting anymore. The fallback feature uses the most common version patterns to help users get the mod versions they want using more robust workarounds that don't break profile configs in the future.My Solution
Introduce a new flag
--fallback=<spec>
which specifies which mod version(s) to consider as compatible if the original cannot be found. This replaces--dont-check-game-version
. I recommend several options for fallback specifications which collectively would fix #29, #95, and #154.Syntax
The syntax would be something like
ferium add X --fallback=<spec>
where<spec>
is one of the following:latest
: use the latest version of the mod available. This is the easiest way to get a mod to work when the profile version is the latest minecraft version. However it has a significant flaw - it is not future-proof. Creating a profile today with this setting may work now but might break when mods update. The current game version check shares this issue, and as a result I would like to propose a way to save / export a profile that saves a specific jar file URL with each mod that points to the exact URL thatferium upgrade
would pull from. That way, when a user gets a profile working (after troubleshooting launching & ingame mod conflicts) they can go back to ferium and "lock" the versions to make the profile to work at any point in the far future (seeurl
option below).minor
: The latest minor version for the same major version. If the profile is set to 1.18 or 1.18.3 for example, then setting--fallback=minor
means the latest mod release (maybe its earlier, like 1.18.1, or later, like 1.18.9) should be considered compatible. This option is almost the same aslatest
except it would not download a 1.19 version into a 1.18.2 profile. Think of this as the "probably future-proof version oflatest
." The "probably" comes from the fact that I don't know what to do if the game version can't be decided, OR if the mod later releases a breaking update for a later minor version.previous
: Attempt to use the mod version for the minor game version preceeding the profile game version. Example: If 1.18.2 does not exist,previous
is short for "1.18.1". If the profile is set to 1.18.1, then "previous" means 1.18. This would be the ideal setting for making a 1.16.5 profile where almost all 1.16.4 mods would work just fine because 1.16.5 had only a couple small security changes. More generally this option is to address mods which don't update compatibility info for a minor version update because the previous version still works.major
: Consider any mod release for the previous major version to be compatible. This is the perfect solution for mods likeFakeDomi/FastChest
which "bucket" their releases by major version.x.y.z
(number / version string): A comma-separated list of version numbers to check in order if the exact version fails. For exampleferium add X --fallback=1.18.1,1.18
would attempt to find a mod release for 1.18.1 first, then try 1.18 after.url
: a URL to a specific github release, github jar file within a release, curseforge file download url etc. Github jar file URLs at a minimum, compatibility with the "save" feature idea above at a maximum. Tells ferium explicitly where to find a mod if it doesn't find a matching version. This is necessary for mods such asA5b84/dark-loading-screen
which only lists version compatibility in their changelog.User Workflow and When the fallback gets used
Suppose the user makes a profile using
ferium add ...
, then later (maybe days or weeks later) runsferium upgrade
. When creating a profile the user may have to specify fallback options to get compatible versions of mods, or simply accept that some mods may not be compatible (#142). When they runferium upgrade
, an updated mod version which exactly matches the profile game version may be available and should be used instead. Second, the actual mod version that gets used as the result of a fallback option may be updated, so that's important to detect when runningferium upgrade
. I think this logic fits in with what ferium does already but I wanted to be specific.Max Version Constraints
If this gets implemented I would also propose separate and independant flag to constrain the mod versions that are considered compatible. The purpose would be to future-proof the use of
fallback=latest
. Another way to do that would be exporting exact mod versions. Perhaps something like one of these:--max-version=profile
: If a mod which is marked compatible via a fallback has a version which ferium can detect and the version is greater than the profile game version, this option marks the candidate as incompatible.--max-version=major
: If the profile is for1.18.1
or1.18
, this disqualifies mods which have an identifiable version of1.19.x / 1.19+
.--max-version=<date>
, where<date>
is something like20220623
which restricts compatibility to releases before this dateThe text was updated successfully, but these errors were encountered: