@@ -77,41 +77,69 @@ check_modrinth_mod() {
7777
7878 # STAGE 2: Try major.minor version match if exact match failed
7979 # Example: "1.21.3" -> try "1.21", "1.21.x", "1.21.0"
80+ # BUT ONLY if no specific patch version exists that's higher than what we're looking for
8081 if [[ -z " $file_url " || " $file_url " == " null" ]]; then
8182 local mc_major_minor
8283 mc_major_minor=$( echo " $MC_VERSION " | grep -oE ' ^[0-9]+\.[0-9]+' ) # Extract "1.21" from "1.21.3"
8384
84- # Try exact major.minor (e.g., "1.21")
85- file_url= $( printf " %s " " $version_json " | jq -r --arg v " $mc_major_minor " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .files[] | select(.primary == true) | .url ' 2> /dev/null | head -n1 )
86- if [[ -n " $file_url " && " $file_url " != " null " ]] ; then
87- dep_ids =$( printf " %s " " $version_json " | jq -r --arg v " $mc_major_minor " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .dependencies[]? | select(.dependency_type=="required") | .project_id ' 2> /dev/null | tr ' \n ' ' ' )
88- fi
85+ # Before trying major.minor match, check if this version is higher than existing patch versions
86+ # This prevents matching 1.21 when looking for 1.21.6 if the highest patch version is only 1.21.5
87+ local mc_patch_version
88+ mc_patch_version =$( echo " $MC_VERSION " | grep -oE ' ^[0-9]+\.[0-9]+\.([0-9]+) ' | grep -oE ' [0-9]+$ ' )
89+ local should_try_fallback=true
8990
90- # Try wildcard version format (e.g., "1.21.x")
91- if [[ -z " $file_url " || " $file_url " == " null" ]]; then
92- local mc_major_minor_x=" $mc_major_minor .x"
93- file_url=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor_x " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .files[] | select(.primary == true) | .url' 2> /dev/null | head -n1)
94- if [[ -n " $file_url " && " $file_url " != " null" ]]; then
95- dep_ids=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor_x " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .dependencies[]? | select(.dependency_type=="required") | .project_id' 2> /dev/null | tr ' \n' ' ' )
91+ # If we have a patch version (e.g. 1.21.6), check if it's higher than any available versions
92+ if [[ -n " $mc_patch_version " ]]; then
93+ # Check if there's a standalone major.minor version (e.g., "1.21" without patch)
94+ local has_standalone_major_minor=$( printf " %s" " $version_json " | jq -r --arg major_minor " $mc_major_minor " '
95+ .[] | select(.game_versions[] == $major_minor and (.loaders[] == "fabric")) | .version_number' 2> /dev/null | head -n1)
96+
97+ # Get the highest patch version available for this major.minor series
98+ local highest_patch=$( printf " %s" " $version_json " | jq -r --arg major_minor " $mc_major_minor " '
99+ [.[] | select(.game_versions[] | test("^" + $major_minor + "\\.[0-9]+$") and (.loaders[] == "fabric")) |
100+ .game_versions[] | select(test("^" + $major_minor + "\\.[0-9]+$")) |
101+ split(".")[2] | tonumber] | if length > 0 then max else empty end' 2> /dev/null)
102+
103+ # Don't try fallback if:
104+ # 1. There's a standalone major.minor version (e.g., "1.21") and we're requesting a patch version, OR
105+ # 2. There are patch versions and our requested patch is higher than the highest available
106+ if [[ -n " $has_standalone_major_minor " && " $has_standalone_major_minor " != " null" ]] ||
107+ [[ -n " $highest_patch " && " $highest_patch " != " null" && " $mc_patch_version " -gt " $highest_patch " ]]; then
108+ should_try_fallback=false
96109 fi
97110 fi
98111
99- # Try zero-padded version format (e.g., "1.21.0")
100- if [[ -z " $file_url " || " $file_url " == " null " ]]; then
101- local mc_major_minor_0= " $mc_major_minor .0 "
102- file_url=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor_0 " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .files[] | select(.primary == true) | .url' 2> /dev/null | head -n1)
112+ # Only try major.minor fallback if it's safe to do so
113+ if [[ " $should_try_fallback " == true ]]; then
114+ # Try exact major.minor (e.g., "1.21")
115+ file_url=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .files[] | select(.primary == true) | .url' 2> /dev/null | head -n1)
103116 if [[ -n " $file_url " && " $file_url " != " null" ]]; then
104- dep_ids=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor_0 " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .dependencies[]? | select(.dependency_type=="required") | .project_id' 2> /dev/null | tr ' \n' ' ' )
117+ dep_ids=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .dependencies[]? | select(.dependency_type=="required") | .project_id' 2> /dev/null | tr ' \n' ' ' )
105118 fi
106- fi
107-
108- # Try prefix matching (any version starting with major.minor)
109- if [[ -z " $file_url " || " $file_url " == " null" ]]; then
110- file_url=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor " ' .[] | select(.game_versions[] | startswith($v) and (.loaders[] == "fabric")) | .files[] | select(.primary == true) | .url' 2> /dev/null | head -n1)
111- if [[ -n " $file_url " && " $file_url " != " null" ]]; then
112- dep_ids=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor " ' .[] | select(.game_versions[] | startswith($v) and (.loaders[] == "fabric")) | .dependencies[]? | select(.dependency_type=="required") | .project_id' 2> /dev/null | tr ' \n' ' ' )
119+
120+ # Try wildcard version format (e.g., "1.21.x")
121+ if [[ -z " $file_url " || " $file_url " == " null" ]]; then
122+ local mc_major_minor_x=" $mc_major_minor .x"
123+ file_url=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor_x " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .files[] | select(.primary == true) | .url' 2> /dev/null | head -n1)
124+ if [[ -n " $file_url " && " $file_url " != " null" ]]; then
125+ dep_ids=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor_x " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .dependencies[]? | select(.dependency_type=="required") | .project_id' 2> /dev/null | tr ' \n' ' ' )
126+ fi
127+ fi
128+
129+ # Try zero-padded version format (e.g., "1.21.0")
130+ if [[ -z " $file_url " || " $file_url " == " null" ]]; then
131+ local mc_major_minor_0=" $mc_major_minor .0"
132+ file_url=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor_0 " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .files[] | select(.primary == true) | .url' 2> /dev/null | head -n1)
133+ if [[ -n " $file_url " && " $file_url " != " null" ]]; then
134+ dep_ids=$( printf " %s" " $version_json " | jq -r --arg v " $mc_major_minor_0 " ' .[] | select(.game_versions[] == $v and (.loaders[] == "fabric")) | .dependencies[]? | select(.dependency_type=="required") | .project_id' 2> /dev/null | tr ' \n' ' ' )
135+ fi
113136 fi
114137 fi
138+
139+ # DISABLED: Limited prefix matching - this was allowing false positives
140+ # We've disabled this section to prevent matching lower patch versions
141+ # when a higher patch version is requested that doesn't exist
142+ # (e.g., preventing 1.21.5 from matching when 1.21.6 is requested)
115143 fi
116144
117145 # STAGE 3: Advanced pattern matching with comprehensive version range support
@@ -123,43 +151,72 @@ check_modrinth_mod() {
123151 local mc_major_minor_x=" $mc_major_minor .x"
124152 local mc_major_minor_0=" $mc_major_minor .0"
125153
126- # Simplified and corrected jq filter that handles multiple version pattern types
127- # Fixed: game_versions is always at release level in Modrinth API, not file level
128- local jq_filter='
129- .[] as $release
130- | select($release.loaders[] == "fabric")
131- | select(
132- $release.game_versions[]
133- | test("^" + $mc_major_minor + "\\..*$") or
134- . == $mc_version or
135- . == $mc_major_minor or
136- . == $mc_major_minor_x or
137- . == $mc_major_minor_0
138- )
139- | $release.files[]
140- | select(.primary == true)
141- | {
142- url,
143- dependencies: ($release.dependencies // [] | map(select(.dependency_type == "required") | .project_id))
144- }
145- | @base64
146- '
154+ # Apply the same fallback safety check as in STAGE 2
155+ local mc_patch_version
156+ mc_patch_version=$( echo " $MC_VERSION " | grep -oE ' ^[0-9]+\.[0-9]+\.([0-9]+)' | grep -oE ' [0-9]+$' )
157+ local should_try_stage3_fallback=true
147158
148- # Execute the corrected jq filter with all version variants
149- local jq_result
150- jq_result=$( printf " %s" " $version_json " | jq -r \
151- --arg mc_version " $MC_VERSION " \
152- --arg mc_major_minor " $mc_major_minor " \
153- --arg mc_major_minor_x " $mc_major_minor_x " \
154- --arg mc_major_minor_0 " $mc_major_minor_0 " \
155- " $jq_filter " 2> /dev/null | head -n1)
159+ # If we have a patch version, check if it's higher than any available versions
160+ if [[ -n " $mc_patch_version " ]]; then
161+ # Check if there's a standalone major.minor version (e.g., "1.21" without patch)
162+ local has_standalone_major_minor=$( printf " %s" " $version_json " | jq -r --arg major_minor " $mc_major_minor " '
163+ .[] | select(.game_versions[] == $major_minor and (.loaders[] == "fabric")) | .version_number' 2> /dev/null | head -n1)
164+
165+ # Get the highest patch version available for this major.minor series
166+ local highest_patch=$( printf " %s" " $version_json " | jq -r --arg major_minor " $mc_major_minor " '
167+ [.[] | select(.game_versions[] | test("^" + $major_minor + "\\.[0-9]+$") and (.loaders[] == "fabric")) |
168+ .game_versions[] | select(test("^" + $major_minor + "\\.[0-9]+$")) |
169+ split(".")[2] | tonumber] | if length > 0 then max else empty end' 2> /dev/null)
170+
171+ # Don't try fallback if:
172+ # 1. There's a standalone major.minor version (e.g., "1.21") and we're requesting a patch version, OR
173+ # 2. There are patch versions and our requested patch is higher than the highest available
174+ if [[ -n " $has_standalone_major_minor " && " $has_standalone_major_minor " != " null" ]] ||
175+ [[ -n " $highest_patch " && " $highest_patch " != " null" && " $mc_patch_version " -gt " $highest_patch " ]]; then
176+ should_try_stage3_fallback=false
177+ fi
178+ fi
179+
180+ # Only proceed with STAGE 3 if fallback is safe
181+ if [[ " $should_try_stage3_fallback " == true ]]; then
182+ # Simplified and corrected jq filter with stricter version matching
183+ # Fixed: game_versions is always at release level in Modrinth API, not file level
184+ # Made version matching more strict to avoid false positives
185+ local jq_filter='
186+ .[] as $release
187+ | select($release.loaders[] == "fabric")
188+ | select(
189+ $release.game_versions[]
190+ | (. == $mc_version or
191+ . == $mc_major_minor or
192+ . == $mc_major_minor_x or
193+ . == $mc_major_minor_0)
194+ )
195+ | $release.files[]
196+ | select(.primary == true)
197+ | {
198+ url,
199+ dependencies: ($release.dependencies // [] | map(select(.dependency_type == "required") | .project_id))
200+ }
201+ | @base64
202+ '
203+
204+ # Execute the corrected jq filter with all version variants
205+ local jq_result
206+ jq_result=$( printf " %s" " $version_json " | jq -r \
207+ --arg mc_version " $MC_VERSION " \
208+ --arg mc_major_minor " $mc_major_minor " \
209+ --arg mc_major_minor_x " $mc_major_minor_x " \
210+ --arg mc_major_minor_0 " $mc_major_minor_0 " \
211+ " $jq_filter " 2> /dev/null | head -n1)
156212
157- # Decode the base64-encoded result and extract URL and dependencies
158- if [[ -n " $jq_result " && " $jq_result " != " null" ]]; then
159- local decoded
160- if decoded=$( echo " $jq_result " | base64 --decode 2> /dev/null) ; then
161- file_url=$( echo " $decoded " | jq -r ' .url' 2> /dev/null)
162- dep_ids=$( echo " $decoded " | jq -r ' .dependencies[]?' 2> /dev/null | tr ' \n' ' ' )
213+ # Decode the base64-encoded result and extract URL and dependencies
214+ if [[ -n " $jq_result " && " $jq_result " != " null" ]]; then
215+ local decoded
216+ if decoded=$( echo " $jq_result " | base64 --decode 2> /dev/null) ; then
217+ file_url=$( echo " $decoded " | jq -r ' .url' 2> /dev/null)
218+ dep_ids=$( echo " $decoded " | jq -r ' .dependencies[]?' 2> /dev/null | tr ' \n' ' ' )
219+ fi
163220 fi
164221 fi
165222 fi
@@ -672,12 +729,12 @@ resolve_modrinth_dependencies_api() {
672729 local mc_major_minor
673730 mc_major_minor=$( echo " $MC_VERSION " | grep -oE ' ^[0-9]+\.[0-9]+' ) # Extract "1.21" from "1.21.3"
674731
675- # Simple jq filter to get dependencies from any compatible fabric version
732+ # Simple jq filter to get dependencies from compatible fabric versions with strict matching
676733 # Use temporary file to avoid command line length limits
677734 dependencies=$( jq -r "
678735 .[]
679736 | select(.loaders[]? == \" fabric\" )
680- | select(.game_versions[]? | test( \" $mc_major_minor \" ))
737+ | select(.game_versions[]? | (. == \" $MC_VERSION \" or . == \" $ mc_major_minor\" or . == \" ${mc_major_minor} .x \" or . == \" ${mc_major_minor} .0 \" ))
681738 | .dependencies[]?
682739 | select(.dependency_type == \" required\" )
683740 | .project_id
@@ -785,8 +842,8 @@ resolve_curseforge_dependencies_api() {
785842 local mc_major_minor
786843 mc_major_minor=$( echo " $MC_VERSION " | grep -oE ' ^[0-9]+\.[0-9]+' )
787844
788- # Extract file ID from the most recent compatible file
789- local file_id=$( jq -r --arg v " $MC_VERSION " --arg mmv " $mc_major_minor " ' .data[] | select(.gameVersions[] == $v or .gameVersions[] == $mmv or ( .gameVersions[] | startswith ($mmv) )) | .id' " $files_temp " 2> /dev/null | head -n1)
845+ # Extract file ID from the most recent compatible file with strict version matching
846+ local file_id=$( jq -r --arg v " $MC_VERSION " --arg mmv " $mc_major_minor " ' .data[] | select(.gameVersions[] == $v or .gameVersions[] == $mmv or .gameVersions[] == ($mmv + ".x") or .gameVersions[] == ($mmv + ".0" )) | .id' " $files_temp " 2> /dev/null | head -n1)
790847
791848 if [[ -n " $file_id " && " $file_id " != " null" ]]; then
792849 # Get dependencies for this specific file
@@ -1086,9 +1143,16 @@ get_curseforge_download_url() {
10861143 download_url=$( jq -r --arg v " $mc_major_minor_x " ' .data[]? | select(.gameVersions[]? == $v) | .downloadUrl' " $temp_file " 2> /dev/null | head -n1)
10871144 fi
10881145
1089- # Try prefix matching (any version starting with major.minor )
1146+ # Try limited previous patch version (more restrictive than prefix matching )
10901147 if [[ -z " $download_url " || " $download_url " == " null" ]]; then
1091- download_url=$( jq -r --arg v " $mc_major_minor " ' .data[]? | select(.gameVersions[]? | startswith($v)) | .downloadUrl' " $temp_file " 2> /dev/null | head -n1)
1148+ local mc_patch_version
1149+ mc_patch_version=$( echo " $MC_VERSION " | grep -oE ' ^[0-9]+\.[0-9]+\.([0-9]+)' | grep -oE ' [0-9]+$' )
1150+ if [[ -n " $mc_patch_version " && $mc_patch_version -gt 0 ]]; then
1151+ # Try one patch version down (e.g., if looking for 1.21.6, try 1.21.5)
1152+ local prev_patch=$(( mc_patch_version - 1 ))
1153+ local mc_prev_version=" $mc_major_minor .$prev_patch "
1154+ download_url=$( jq -r --arg v " $mc_prev_version " ' .data[]? | select(.gameVersions[]? == $v) | .downloadUrl' " $temp_file " 2> /dev/null | head -n1)
1155+ fi
10921156 fi
10931157
10941158 # If still no URL found, try the latest file
0 commit comments