From 945f852259a9aa01f7c726770f1568b0e28b7c24 Mon Sep 17 00:00:00 2001
From: TheEpicBlock <61842090+TheEpicBlock@users.noreply.github.com>
Date: Wed, 18 Feb 2026 23:01:04 +0100
Subject: [PATCH] Version fetching refactoring (#373)
* Put modloader version data in struct for legibility
* Create modloader map from list
This is a minor change, but it prevents any errors with the keys not being equal to the modloader's Name field
* Less anonymous functions in modloader version retrieval
With previous code, almost everything was lazy and returned a function. Changed this to only have anonymous function in the modloader definitions, and the rest of the functions all just execute their results immediately instead or returning a function. Makes for a bit more legible code.
* Simplify FetchMavenVersionFiltered
* Rewrite version retrieval
* Use Errorf in versionutil
* Remove httpclient parameter
turned out not to be needed
* Add testing for version retrieval
* Explain fetchMavenWithFilterMap
---
cmd/init.go | 18 +-
core/version_test_files/fabric.xml | 107 +++++++
core/version_test_files/forge.xml | 290 +++++++++++++++++++
core/version_test_files/liteloader.xml | 23 ++
core/version_test_files/neoforge.xml | 183 ++++++++++++
core/version_test_files/neoforge_old.xml | 75 +++++
core/version_test_files/quilt.xml | 73 +++++
core/versionutil.go | 353 ++++++++++++++---------
core/versionutil_test.go | 129 +++++++++
go.mod | 1 +
go.sum | 2 +
migrate/loader.go | 48 +--
12 files changed, 1123 insertions(+), 179 deletions(-)
create mode 100644 core/version_test_files/fabric.xml
create mode 100644 core/version_test_files/forge.xml
create mode 100644 core/version_test_files/liteloader.xml
create mode 100644 core/version_test_files/neoforge.xml
create mode 100644 core/version_test_files/neoforge_old.xml
create mode 100644 core/version_test_files/quilt.xml
create mode 100644 core/versionutil_test.go
diff --git a/cmd/init.go b/cmd/init.go
index 52448c9..d8cdd09 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -3,6 +3,7 @@ package cmd
import (
"bufio"
"fmt"
+ "maps"
"os"
"path/filepath"
"slices"
@@ -89,7 +90,7 @@ var initCmd = &cobra.Command{
modLoaderVersions := make(map[string]string)
if modLoaderName != "none" {
if ok {
- versions, latestVersion, err := loader.VersionListGetter(mcVersion)
+ versionData, err := core.DoQuery(core.MakeQuery(loader, mcVersion))
if err != nil {
fmt.Printf("Error loading versions: %s\n", err)
os.Exit(1)
@@ -97,9 +98,9 @@ var initCmd = &cobra.Command{
componentVersion := viper.GetString("init." + loader.Name + "-version")
if len(componentVersion) == 0 {
if viper.GetBool("init." + loader.Name + "-latest") {
- componentVersion = latestVersion
+ componentVersion = versionData.Latest
} else {
- componentVersion = initReadValue(loader.FriendlyName+" version ["+latestVersion+"]: ", latestVersion)
+ componentVersion = initReadValue(loader.FriendlyName+" version ["+versionData.Latest+"]: ", versionData.Latest)
}
}
v := componentVersion
@@ -108,7 +109,7 @@ var initCmd = &cobra.Command{
if loader.Name == "forge" || (loader.Name == "neoforge" && mcVersion == "1.20.1") {
v = cmdshared.GetRawForgeVersion(componentVersion)
}
- if !slices.Contains(versions, v) {
+ if !slices.Contains(versionData.Versions, v) {
fmt.Println("Given " + loader.FriendlyName + " version cannot be found!")
os.Exit(1)
}
@@ -116,13 +117,8 @@ var initCmd = &cobra.Command{
} else {
fmt.Println("Given mod loader is not supported! Use \"none\" to specify no modloader, or to configure one manually.")
fmt.Print("The following mod loaders are supported: ")
- keys := make([]string, len(core.ModLoaders))
- i := 0
- for k := range core.ModLoaders {
- keys[i] = k
- i++
- }
- fmt.Println(strings.Join(keys, ", "))
+ loader_names := slices.Collect(maps.Keys(core.ModLoaders))
+ fmt.Println(strings.Join(loader_names, ", "))
os.Exit(1)
}
}
diff --git a/core/version_test_files/fabric.xml b/core/version_test_files/fabric.xml
new file mode 100644
index 0000000..bee0486
--- /dev/null
+++ b/core/version_test_files/fabric.xml
@@ -0,0 +1,107 @@
+
+
+ net.fabricmc
+ fabric-loader
+
+ 0.17.3
+ 0.17.3
+
+ 0.1.0.48
+ 0.1.0.49
+ 0.1.0.50
+ 0.1.0.51
+ 0.1.0.52
+ 0.2.0.53
+ 0.2.0.54
+ 0.2.0.55
+ 0.2.0.56
+ 0.2.0.57
+ 0.2.0.58
+ 0.2.0.59
+ 0.2.0.60
+ 0.2.0.61
+ 0.2.0.62
+ 0.3.3.99
+ 0.3.3.100
+ 0.3.3.101
+ 0.3.3.102
+ 0.3.7.109
+ 0.3.7.110
+ 0.3.7.111
+ 0.4.0+build.112
+ 0.4.0+build.113
+ 0.4.0+build.114
+ 0.4.0+build.115
+ 0.4.0+build.116
+ 0.4.1+build.122
+ 0.4.1+build.123
+ 0.4.1+build.124
+ 0.4.1+build.127
+ 0.9.1+build.205
+ 0.9.2+build.206
+ 0.9.3+build.207
+ 0.10.0+build.208
+ 0.10.1+build.209
+ 0.10.2+build.210
+ 0.10.5+build.213
+ 0.10.6+build.214
+ 0.10.7
+ 0.10.8
+ 0.11.0
+ 0.11.1
+ 0.11.2
+ 0.11.3
+ 0.11.5
+ 0.12.4
+ 0.12.5
+ 0.12.6
+ 0.12.7
+ 0.12.8
+ 0.12.9
+ 0.12.10
+ 0.12.11
+ 0.12.12
+ 0.13.0
+ 0.13.1
+ 0.13.2
+ 0.13.3
+ 0.14.0
+ 0.14.4
+ 0.14.5
+ 0.14.6
+ 0.14.7
+ 0.14.8
+ 0.14.9
+ 0.14.10
+ 0.14.11
+ 0.14.12
+ 0.14.13
+ 0.14.14
+ 0.14.19
+ 0.14.20
+ 0.14.21
+ 0.14.22
+ 0.14.23
+ 0.14.24
+ 0.14.25
+ 0.15.0
+ 0.15.8
+ 0.15.9
+ 0.15.10
+ 0.15.11
+ 0.16.0
+ 0.16.1
+ 0.16.2
+ 0.16.3
+ 0.16.11
+ 0.16.12
+ 0.16.13
+ 0.16.14
+ 0.17.0
+ 0.17.1
+ 0.17.2
+ 0.17.3
+
+ 20251009160949
+
+
diff --git a/core/version_test_files/forge.xml b/core/version_test_files/forge.xml
new file mode 100644
index 0000000..b11aea8
--- /dev/null
+++ b/core/version_test_files/forge.xml
@@ -0,0 +1,290 @@
+
+
+ net.minecraftforge
+ forge
+
+ 1.21.10-60.0.14
+ 1.21.10-60.0.14
+
+ 1.21-51.0.33
+ 1.21-51.0.32
+ 1.21-51.0.21
+ 1.21-51.0.18
+ 1.21-51.0.17
+ 1.21-51.0.16
+ 1.21-51.0.15
+ 1.21-51.0.13
+ 1.21-51.0.8
+ 1.21-51.0.7
+ 1.21-51.0.6
+ 1.21-51.0.5
+ 1.21-51.0.4
+ 1.21-51.0.3
+ 1.21-51.0.1
+ 1.21-51.0.0
+ 1.21.8-58.0.3
+ 1.21.8-58.0.2
+ 1.21.8-58.0.1
+ 1.21.8-58.0.0
+ 1.21.7-57.0.3
+ 1.21.7-57.0.2
+ 1.21.5-55.0.17
+ 1.21.1-52.0.0
+ 1.20-46.0.14
+ 1.20-46.0.11
+ 1.20-46.0.10
+ 1.20-46.0.2
+ 1.20-46.0.1
+ 1.20.6-50.2.1
+ 1.20.6-50.2.0
+ 1.20.6-50.1.51
+ 1.19-41.0.98
+ 1.19.2-43.2.21
+ 1.18.2-40.1.92
+ 1.16.1-32.0.10
+ 1.16.1-32.0.9
+ 1.16.1-32.0.8
+ 1.16.1-32.0.7
+ 1.16.1-32.0.6
+ 1.16.1-32.0.2
+ 1.16.1-32.0.1
+ 1.15-29.0.4
+ 1.15-29.0.3
+ 1.15-29.0.2
+ 1.15-29.0.1
+ 1.15-29.0.0
+ 1.15.2-31.2.57
+ 1.14.3-27.0.3
+ 1.14.3-27.0.2
+ 1.14.3-27.0.1
+ 1.14.3-27.0.0
+ 1.14.2-26.0.63
+ 1.14.2-26.0.62
+ 1.14.2-26.0.61
+ 1.14.2-26.0.60
+ 1.12-14.21.0.2324
+ 1.12-14.21.0.2323
+ 1.12-14.21.0.2322
+ 1.12-14.21.0.2320
+ 1.12.2-14.23.5.2860
+ 1.12.2-14.23.4.2721
+ 1.12.2-14.23.4.2720-4627
+ 1.12.2-14.23.4.2719
+ 1.12.1-14.22.0.2444
+ 1.11-13.19.1.2199
+ 1.11-13.19.0.2130
+ 1.11-13.19.0.2129-1.11.x
+ 1.11-13.19.0.2128-1.11.x
+ 1.11-13.19.0.2127-1.11.x
+ 1.11-13.19.0.2126-1.11.x
+ 1.11.2-13.20.1.2588
+ 1.11.2-13.20.0.2245-3630
+ 1.11.2-13.20.0.2244
+ 1.11.2-13.20.0.2200
+ 1.10-12.18.0.2000-1.10.0
+ 1.10-12.18.0.1999-1.10.0
+ 1.10-12.18.0.1981-1.10.0
+ 1.10.2-12.18.3.2511
+ 1.10.2-12.18.3.2488
+ 1.10.2-12.18.1.2019
+ 1.10.2-12.18.1.2018
+ 1.10.2-12.18.1.2017
+ 1.10.2-12.18.1.2016-failtests
+ 1.10.2-12.18.1.2015-failtests
+ 1.10.2-12.18.1.2014
+ 1.10.2-12.18.1.2013
+ 1.10.2-12.18.0.2010
+ 1.10.2-12.18.0.2009
+ 1.10.2-12.18.0.2008
+ 1.10.2-12.18.0.2007-1.10.0
+ 1.10.2-12.18.0.2001-1.10.0
+ 1.9-12.16.1.1938-1.9.0
+ 1.9-12.16.1.1934
+ 1.9-12.16.0.1886
+ 1.9-12.16.0.1885-1.9
+ 1.9-12.16.0.1766-1.9
+ 1.9.4-12.17.0.2317-1.9.4
+ 1.9.4-12.17.0.2051
+ 1.9.4-12.17.0.1990
+ 1.9.4-12.17.0.1987
+ 1.9.4-12.17.0.1976
+ 1.9.4-12.17.0.1975
+ 1.9.4-12.17.0.1974-1.9.4
+ 1.9.4-12.17.0.1973
+ 1.9.4-12.17.0.1937
+ 1.9.4-12.17.0.1936-1.9.4
+ 1.9.4-12.17.0.1935-1.9.4
+ 1.9.4-12.17.0.1920-1.9.4
+ 1.9.4-12.17.0.1919-EHUnit
+ 1.9.4-12.17.0.1908-1.9.4
+ 1.8-11.14.4.1577
+ 1.8-11.14.0.1296
+ 1.8-11.14.0.1295-1.8
+ 1.8-11.14.0.1237-1.8
+ 1.8.9-11.15.1.2318-1.8.9
+ 1.8.9-11.15.1.1902-1.8.9
+ 1.8.9-11.15.1.1890-1.8.9
+ 1.8.9-11.15.1.1875
+ 1.8.9-11.15.1.1873
+ 1.8.9-11.15.1.1758
+ 1.8.9-11.15.1.1757
+ 1.8.9-11.15.1.1756
+ 1.8.9-11.15.1.1755
+ 1.8.9-11.15.0.1657
+ 1.8.9-11.15.0.1656
+ 1.8.8-11.15.0.1655
+ 1.8.8-11.15.0.1654-1.8.8
+ 1.8.8-11.15.0.1653-1.8.8
+ 1.8.8-11.14.4.1579-1.8.8
+ 1.8.8-11.14.4.1576-1.8.8
+ 1.8.8-11.14.4.1575-1.8.8
+ 1.7.10-10.13.4.1614-1.7.10
+ 1.7.10-10.13.3.1406-1.7.10
+ 1.7.10-10.13.3.1403-1.7.10
+ 1.7.10-10.13.3.1401-1710ls
+ 1.7.10-10.13.3.1400-1.7.10
+ 1.7.10-10.13.3.1399-1.7.10
+ 1.7.10-10.13.3.1395-1710ls
+ 1.7.10-10.13.3.1394-1710ls
+ 1.7.10-10.13.3.1393-1710ls
+ 1.7.10-10.13.3.1391-1710ls
+ 1.7.10-10.13.3.1389-1710ls
+ 1.7.10-10.13.3.1388-1.7.10
+ 1.7.10-10.13.2.1307-1.7.10
+ 1.7.10-10.13.2.1300-1.7.10
+ 1.7.10-10.13.2.1291
+ 1.7.10-10.13.2.1286
+ 1.7.10-10.13.2.1284
+ 1.7.10-10.13.2.1283
+ 1.7.10-10.13.2.1277
+ 1.7.10-10.13.2.1276
+ 1.7.10-10.13.2.1275
+ 1.7.10-10.13.1.1219
+ 1.7.10-10.13.1.1217
+ 1.7.10-10.13.1.1216-new
+ 1.7.10-10.13.1.1215-new
+ 1.7.10-10.13.1.1214-new
+ 1.7.10-10.13.1.1213-new
+ 1.7.10-10.13.1.1212-new
+ 1.7.10-10.13.1.1211-new
+ 1.7.10-10.13.1.1210-new
+ 1.7.10-10.13.0.1208
+ 1.7.10-10.13.0.1150
+ 1.7.2-10.12.2.1161-mc172
+ 1.7.2-10.12.2.1155-mc172
+ 1.7.2-10.12.2.1154-mc172
+ 1.7.2-10.12.2.1147
+ 1.7.2-10.12.2.1145
+ 1.7.2-10.12.0.1030
+ 1.7.2-10.12.0.1029
+ 1.7.2-10.12.0.1028
+ 1.7.2-10.12.0.1027
+ 1.7.2-10.12.0.1026
+ 1.7.2-10.12.0.1025
+ 1.7.2-10.12.0.1024
+ 1.7.2-10.12.0.1023
+ 1.7.2-10.12.0.1022
+ 1.7.2-10.12.0.1021
+ 1.7.2-10.12.0.1020
+ 1.7.2-10.12.0.1000
+ 1.7.2-10.12.0.999
+ 1.7.2-10.12.0.998
+ 1.7.10_pre4-10.12.2.1149-prerelease
+ 1.7.10_pre4-10.12.2.1148-prerelease
+ 1.7.10_pre4-10.12.2.1146-prerelease
+ 1.7.10_pre4-10.12.2.1144-prerelease
+ 1.7.10_pre4-10.12.2.1143-prerelease
+ 1.7.10_pre4-10.12.2.1142-prerelease
+ 1.7.10_pre4-10.12.2.1141-prerelease
+ 1.7.10_pre4-10.12.2.1139-prerelease
+ 1.7.10_pre4-10.12.2.1138-prerelease
+ 1.7.10_pre4-10.12.2.1137-prerelease
+ 1.6.4-9.11.1.1345
+ 1.6.4-9.11.1.965
+ 1.6.4-9.11.1.964
+ 1.6.4-9.11.1.963
+ 1.6.4-9.11.1.961
+ 1.6.4-9.11.1.960
+ 1.6.1-8.9.0.775
+ 1.6.1-8.9.0.749
+ 1.5-7.7.0.598
+ 1.5-7.7.0.565
+ 1.5-7.7.0.563
+ 1.5-7.7.0.562
+ 1.5-7.7.0.561
+ 1.5-7.7.0.560
+ 1.5-7.7.0.559
+ 1.5.2-7.8.1.738
+ 1.5.2-7.8.1.737
+ 1.5.2-7.8.0.736
+ 1.5.2-7.8.0.735
+ 1.5.2-7.8.0.734
+ 1.5.2-7.8.0.733
+ 1.5.2-7.8.0.732
+ 1.5.2-7.8.0.731
+ 1.5.2-7.8.0.730
+ 1.5.2-7.8.0.729
+ 1.5.2-7.8.0.728
+ 1.5.2-7.8.0.727
+ 1.1-1.3.2.3
+ 1.1-1.3.2.2
+ 1.1-1.3.2.1
+ 1.21.8-58.0.4
+ 1.21.1-52.1.3
+ 1.20.1-47.4.6
+ 1.21.8-58.0.5
+ 1.19.4-45.4.2
+ 1.15.2-31.2.60
+ 1.21.8-58.0.6
+ 1.21.8-58.0.7
+ 1.21.8-58.0.8
+ 1.21.8-58.0.9
+ 1.21.8-58.0.10
+ 1.21.5-55.0.25
+ 1.21.5-55.1.0
+ 1.21.8-58.1.0
+ 1.20.4-49.2.1
+ 1.20.1-47.4.8
+ 1.21.8-58.1.1
+ 1.21.8-58.1.2
+ 1.21.8-58.1.3
+ 1.21.8-58.1.4
+ 1.20.1-47.4.9
+ 1.21.5-55.1.1
+ 1.21.3-53.1.3
+ 1.21.4-54.1.7
+ 1.20.6-50.2.2
+ 1.20.4-49.2.2
+ 1.21.1-52.1.4
+ 1.21.8-58.1.6
+ 1.21.5-55.1.2
+ 1.21.4-54.1.8
+ 1.21.3-53.1.4
+ 1.21.1-52.1.5
+ 1.21.9-59.0.0
+ 1.21.9-59.0.1
+ 1.21.9-59.0.2
+ 1.21.8-58.1.7
+ 1.21.9-59.0.3
+ 1.21.9-59.0.4
+ 1.21.9-59.0.5
+ 1.21.10-60.0.0
+ 1.20.1-47.4.10
+ 1.21.10-60.0.1
+ 1.21.10-60.0.3
+ 1.21.10-60.0.4
+ 1.21.10-60.0.5
+ 1.21.10-60.0.6
+ 1.21.10-60.0.7
+ 1.21.10-60.0.8
+ 1.21.10-60.0.9
+ 1.21.1-52.1.6
+ 1.21.10-60.0.10
+ 1.21.10-60.0.11
+ 1.21.10-60.0.12
+ 1.21.10-60.0.13
+ 1.21.10-60.0.14
+
+ 20251031200755
+
+
diff --git a/core/version_test_files/liteloader.xml b/core/version_test_files/liteloader.xml
new file mode 100644
index 0000000..4411e1c
--- /dev/null
+++ b/core/version_test_files/liteloader.xml
@@ -0,0 +1,23 @@
+
+
+ com.mumfrey
+ liteloader
+
+ 1.9.4-SNAPSHOT
+
+
+ 1.8.9-SNAPSHOT
+ 1.9-SNAPSHOT
+ 1.9.4-SNAPSHOT
+ 1.8-SNAPSHOT
+ 1.10-SNAPSHOT
+ 1.10.2-SNAPSHOT
+ 1.11-SNAPSHOT
+ 1.11.2-SNAPSHOT
+ 1.12-SNAPSHOT
+ 1.12.1-SNAPSHOT
+ 1.12.2-SNAPSHOT
+
+ 20171128144432
+
+
diff --git a/core/version_test_files/neoforge.xml b/core/version_test_files/neoforge.xml
new file mode 100644
index 0000000..0662a4f
--- /dev/null
+++ b/core/version_test_files/neoforge.xml
@@ -0,0 +1,183 @@
+
+
+ net.neoforged
+ neoforge
+
+ 21.10.43-beta
+ 21.10.43-beta
+
+ 21.10.5-beta
+ 21.10.4-beta
+ 21.10.3-beta
+ 21.10.2-beta
+ 21.10.1-beta
+ 21.10.0-beta
+ 21.9.16-beta
+ 21.9.15-beta
+ 21.9.14-beta
+ 21.9.13-beta
+ 21.9.12-beta
+ 21.9.11-beta
+ 21.9.10-beta
+ 21.9.9-beta
+ 21.9.8-beta
+ 21.9.7-beta
+ 21.9.6-beta
+ 21.9.5-beta
+ 21.9.4-beta
+ 21.9.3-beta
+ 21.9.2-beta
+ 21.9.1-beta
+ 21.9.0-beta
+ 21.8.47
+ 21.8.42
+ 21.8.41
+ 21.3.1-beta
+ 21.3.0-beta
+ 21.2.1-beta
+ 21.2.0-beta
+ 21.1.209
+ 21.1.208
+ 21.1.207
+ 21.1.206
+ 21.1.205
+ 21.1.204
+ 21.1.203
+ 21.1.202
+ 21.1.201
+ 21.1.35
+ 21.1.34
+ 21.1.33
+ 21.1.32
+ 21.1.31
+ 21.1.30
+ 21.1.29
+ 21.1.28
+ 21.1.27
+ 21.1.26
+ 21.1.25
+ 21.1.24
+ 21.1.23
+ 21.1.22
+ 21.1.21
+ 21.1.20
+ 21.1.19
+ 21.1.18
+ 21.1.17
+ 21.1.16
+ 21.1.15
+ 21.1.13
+ 21.1.12
+ 21.1.11
+ 21.1.10
+ 21.1.9
+ 21.1.8
+ 21.1.7
+ 21.1.6
+ 21.1.1
+ 21.0.167
+ 21.0.143
+ 21.0.142-beta
+ 21.0.138-beta
+ 21.0.102-beta
+ 21.0.100-beta
+ 21.0.99-beta
+ 21.0.92-beta
+ 21.0.91-beta
+ 21.0.90-beta
+ 21.0.89-beta
+ 21.0.41-beta
+ 21.0.40-beta
+ 21.0.39-beta
+ 21.0.2-beta
+ 21.0.1-beta
+ 21.0.0-beta
+ 20.6.138
+ 20.6.115
+ 20.6.114-beta
+ 20.6.80-beta
+ 20.6.79-beta
+ 20.6.11-beta
+ 20.6.10-beta
+ 20.6.9-beta
+ 20.6.8-beta
+ 20.5.10-beta
+ 20.5.9-beta
+ 20.5.8-beta
+ 20.5.7-beta
+ 20.5.6-beta
+ 20.5.5-beta
+ 20.5.4-beta
+ 20.5.3-beta
+ 20.5.2-beta
+ 20.5.1-beta
+ 20.5.0-beta
+ 20.4.250
+ 20.4.249
+ 20.4.167
+ 20.4.166-beta
+ 20.4.95-beta
+ 20.4.44-beta
+ 20.3.1-beta
+ 20.2.93
+ 20.2.92
+ 20.2.91
+ 20.2.90
+ 20.2.89
+ 20.2.88
+ 20.2.86
+ 20.2.85-beta
+ 20.2.59-beta
+ 20.2.30-beta
+ 20.2.29-beta
+ 20.2.8-beta
+ 20.2.6-beta
+ 20.2.5-beta
+ 20.2.4-beta
+ 20.2.3-beta
+ 0.25w14craftmine.5-beta
+ 0.25w14craftmine.4-beta
+ 0.25w14craftmine.3-beta
+ 21.10.6-beta
+ 21.4.155
+ 21.10.7-beta
+ 21.10.8-beta
+ 21.10.9-beta
+ 21.10.10-beta
+ 21.1.210
+ 21.10.11-beta
+ 21.10.12-beta
+ 21.10.13-beta
+ 21.10.14-beta
+ 21.10.15-beta
+ 21.1.211
+ 21.10.16-beta
+ 21.10.17-beta
+ 21.10.18-beta
+ 21.10.19-beta
+ 21.10.20-beta
+ 21.10.21-beta
+ 21.10.22-beta
+ 21.10.23-beta
+ 21.10.24-beta
+ 21.10.25-beta
+ 21.1.212
+ 21.8.48
+ 21.10.28-beta
+ 21.10.29-beta
+ 21.1.213
+ 20.4.251
+ 20.6.139
+ 21.3.94
+ 21.10.30-beta
+ 21.10.31-beta
+ 21.10.32-beta
+ 21.10.33-beta
+ 21.8.49
+ 21.10.34-beta
+ 21.10.42-beta
+ 21.10.43-beta
+
+ 20251101100329
+
+
diff --git a/core/version_test_files/neoforge_old.xml b/core/version_test_files/neoforge_old.xml
new file mode 100644
index 0000000..9969f7e
--- /dev/null
+++ b/core/version_test_files/neoforge_old.xml
@@ -0,0 +1,75 @@
+
+
+ net.neoforged
+ forge
+
+ 1.20.1-47.1.106
+ 1.20.1-47.1.106
+
+ 1.20.1-47.1.7
+ 1.20.1-47.1.5
+ 1.20.1-47.1.8
+ 1.20.1-47.1.9
+ 1.20.1-47.1.11
+ 1.20.1-47.1.12
+ 1.20.1-47.1.23
+ 1.20.1-47.1.25
+ 1.20.1-47.1.26
+ 1.20.1-47.1.27
+ 1.20.1-47.1.28
+ 1.20.1-47.1.54
+ 1.20.1-47.1.55
+ 1.20.1-47.1.56
+ 1.20.1-47.1.57
+ 1.20.1-47.1.58
+ 1.20.1-47.1.59
+ 1.20.1-47.1.60
+ 1.20.1-47.1.61
+ 1.20.1-47.1.62
+ 1.20.1-47.1.63
+ 1.20.1-47.1.64
+ 1.20.1-47.1.65
+ 1.20.1-47.1.66
+ 1.20.1-47.1.67
+ 1.20.1-47.1.69
+ 1.20.1-47.1.70
+ 1.20.1-47.1.71
+ 1.20.1-47.1.72
+ 1.20.1-47.1.73
+ 1.20.1-47.1.74
+ 1.20.1-47.1.75
+ 1.20.1-47.1.76
+ 1.20.1-47.1.77
+ 1.20.1-47.1.78
+ 1.20.1-47.1.79
+ 1.20.1-47.1.80
+ 1.20.1-47.1.81
+ 47.1.82
+ 1.20.1-47.1.83
+ 1.20.1-47.1.84
+ 1.20.1-47.1.85
+ 1.20.1-47.1.86
+ 1.20.1-47.1.87
+ 1.20.1-47.1.88
+ 1.20.1-47.1.90
+ 1.20.1-47.1.91
+ 1.20.1-47.1.89
+ 1.20.1-47.1.92
+ 1.20.1-47.1.93
+ 1.20.1-47.1.94
+ 1.20.1-47.1.95
+ 1.20.1-47.1.96
+ 1.20.1-47.1.97
+ 1.20.1-47.1.98
+ 1.20.1-47.1.99
+ 1.20.1-47.1.100
+ 1.20.1-47.1.101
+ 1.20.1-47.1.102
+ 1.20.1-47.1.103
+ 1.20.1-47.1.104
+ 1.20.1-47.1.105
+ 1.20.1-47.1.106
+
+ 20240528174429
+
+
diff --git a/core/version_test_files/quilt.xml b/core/version_test_files/quilt.xml
new file mode 100644
index 0000000..fec0013
--- /dev/null
+++ b/core/version_test_files/quilt.xml
@@ -0,0 +1,73 @@
+
+
+ org.quiltmc
+ quilt-loader
+
+ 0.29.3-beta.1
+ 0.29.3-beta.1
+
+ 0.16.0-beta.1
+ 0.16.0-beta.2
+ 0.16.0-beta.3
+ 0.16.0-beta.5
+ 0.16.0-beta.6
+ 0.16.0-beta.7
+ 0.16.0-beta.8
+ 0.16.0-beta.9
+ 0.18.1-beta.18
+ 0.18.1-beta.19
+ 0.17.7
+ 0.18.1-beta.20
+ 0.18.1-beta.21
+ 0.18.1-beta.77
+ 0.18.1-beta.78
+ 0.18.1-beta.79
+ 0.18.1
+ 0.18.2
+ 0.18.3-pre.1
+ 0.18.3-pre.2
+ 0.18.3
+ 0.18.4-pre.1
+ 0.18.4-pre.2
+ 0.18.4-pre.3
+ 0.18.4
+ 0.18.5
+ 0.19.0-beta.1
+ 0.18.6-beta.1
+ 0.19.0-beta.2
+ 0.18.6
+ 0.19.0-beta.3
+ 0.18.7
+ 0.19.0-beta.4
+ 0.18.9
+ 0.18.10
+ 0.19.0-beta.12
+ 0.19.0-beta.13
+ 0.19.0-beta.16
+ 0.19.3-beta.1
+ 0.20.0-beta.4
+ 0.20.0-beta.5
+ 0.20.0-beta.6
+ 0.20.0-beta.7
+ 0.20.0-beta.8
+ 0.20.0-beta.9
+ 0.20.0-beta.10
+ 0.20.0-beta.11
+ 0.29.0-beta.4
+ 0.29.0-beta.5
+ 0.29.0-beta.6
+ 0.29.0-beta.7
+ 0.29.0-beta.8
+ 0.29.0
+ 0.29.1-beta.1
+ 0.29.1
+ 0.29.2-beta.1
+ 0.29.2-beta.2
+ 0.29.2-beta.4
+ 0.29.2-beta.5
+ 0.29.2
+ 0.29.3-beta.1
+
+ 20251025205845
+
+
diff --git a/core/versionutil.go b/core/versionutil.go
index d9dcd46..3a54a54 100644
--- a/core/versionutil.go
+++ b/core/versionutil.go
@@ -24,167 +24,248 @@ type MavenMetadata struct {
} `xml:"versioning"`
}
-type ModLoaderComponent struct {
- Name string
- FriendlyName string
- VersionListGetter func(mcVersion string) ([]string, string, error)
+type ModLoaderVersions struct {
+ // All Versions of this modloader
+ Versions []string
+ // The Latest/preferred version for this modloader
+ Latest string
}
-var ModLoaders = map[string]ModLoaderComponent{
- "fabric": {
+type ModLoaderComponent struct {
+ // An identifier for the modloader
+ Name string
+ // A user-friendly name
+ FriendlyName string
+ // Retrieves the list of all modloader versions. Modloader versions are always filtered to those compatible
+ // with a specific minecraft version.
+ VersionListGetter func(q VersionListQuery) (*ModLoaderVersions, error)
+}
+
+var modLoadersList = []ModLoaderComponent{
+ {
// There's no need to specify yarn version - yarn isn't used outside a dev environment, and intermediary corresponds to game version anyway
- Name: "fabric",
- FriendlyName: "Fabric loader",
- VersionListGetter: FetchMavenVersionList("https://maven.fabricmc.net/net/fabricmc/fabric-loader/maven-metadata.xml"),
+ Name: "fabric",
+ FriendlyName: "Fabric loader",
+ VersionListGetter: func(q VersionListQuery) (*ModLoaderVersions, error) {
+ // Fabric loaders isn't locked to a mc version per se
+ return fetchVersionsFromMaven(q, "https://maven.fabricmc.net/net/fabricmc/fabric-loader/maven-metadata.xml")
+ },
},
- "forge": {
+ {
Name: "forge",
FriendlyName: "Forge",
- VersionListGetter: FetchMavenVersionPrefixedListStrip("https://files.minecraftforge.net/maven/net/minecraftforge/forge/maven-metadata.xml", "Forge"),
+ VersionListGetter: fetchForForge,
},
- "liteloader": {
- Name: "liteloader",
- FriendlyName: "LiteLoader",
- VersionListGetter: FetchMavenVersionPrefixedList("https://repo.mumfrey.com/content/repositories/snapshots/com/mumfrey/liteloader/maven-metadata.xml", "LiteLoader"),
+ {
+ Name: "liteloader",
+ FriendlyName: "LiteLoader",
+ VersionListGetter: func(q VersionListQuery) (*ModLoaderVersions, error) {
+ return fetchLiteloaderStyle(q, "https://repo.mumfrey.com/content/repositories/snapshots/com/mumfrey/liteloader/maven-metadata.xml")
+ },
},
- "quilt": {
- Name: "quilt",
- FriendlyName: "Quilt loader",
- VersionListGetter: FetchMavenVersionList("https://maven.quiltmc.org/repository/release/org/quiltmc/quilt-loader/maven-metadata.xml"),
+ {
+ Name: "quilt",
+ FriendlyName: "Quilt loader",
+ VersionListGetter: func(q VersionListQuery) (*ModLoaderVersions, error) {
+ return fetchVersionsFromMaven(q, "https://maven.quiltmc.org/repository/release/org/quiltmc/quilt-loader/maven-metadata.xml")
+ },
},
- "neoforge": {
+ {
Name: "neoforge",
FriendlyName: "NeoForge",
- VersionListGetter: FetchNeoForge(),
+ VersionListGetter: fetchForNeoForge,
},
}
-func FetchMavenVersionList(url string) func(mcVersion string) ([]string, string, error) {
- return func(mcVersion string) ([]string, string, error) {
- res, err := GetWithUA(url, "application/xml")
- if err != nil {
- return []string{}, "", err
- }
- dec := xml.NewDecoder(res.Body)
- out := MavenMetadata{}
- err = dec.Decode(&out)
- if err != nil {
- return []string{}, "", err
- }
- return out.Versioning.Versions.Version, out.Versioning.Release, nil
+// A map containing information about all supported modloaders.
+// Can be indexed by the [ModLoaderComponent]'s name, which serves as an identifier.
+var ModLoaders = createModloaderMap(modLoadersList)
+
+func createModloaderMap(input []ModLoaderComponent) map[string]ModLoaderComponent {
+ var mlMap = make(map[string]ModLoaderComponent)
+ for _, loader := range input {
+ mlMap[loader.Name] = loader
+ }
+ return mlMap
+}
+
+type QueryType int
+
+const (
+ // The Latest field will contain the last released loader version
+ Latest QueryType = iota
+ // The Latest field will contain the loader version recommended for use
+ Recommended
+)
+
+type VersionListQuery struct {
+ // Which loader to query versions for
+ Loader ModLoaderComponent
+ // Which minecraft version the returned loader versions should be compatible with
+ McVersion string
+ // Determines how the latest version is determined
+ QueryType QueryType
+}
+
+func MakeQuery(loader ModLoaderComponent, mcVersion string) VersionListQuery {
+ return VersionListQuery{
+ Loader: loader,
+ McVersion: mcVersion,
+ QueryType: Latest,
}
}
-func FetchMavenVersionFiltered(url string, friendlyName string, filter func(version string, mcVersion string) bool) func(mcVersion string) ([]string, string, error) {
- return func(mcVersion string) ([]string, string, error) {
- res, err := GetWithUA(url, "application/xml")
- if err != nil {
- return []string{}, "", err
- }
- dec := xml.NewDecoder(res.Body)
- out := MavenMetadata{}
- err = dec.Decode(&out)
- if err != nil {
- return []string{}, "", err
- }
- allowedVersions := make([]string, 0, len(out.Versioning.Versions.Version))
- for _, v := range out.Versioning.Versions.Version {
- if filter(v, mcVersion) {
- allowedVersions = append(allowedVersions, v)
- }
- }
- if len(allowedVersions) == 0 {
- return []string{}, "", errors.New("no " + friendlyName + " versions available for this Minecraft version")
- }
- if filter(out.Versioning.Release, mcVersion) {
- return allowedVersions, out.Versioning.Release, nil
- }
- if filter(out.Versioning.Latest, mcVersion) {
- return allowedVersions, out.Versioning.Latest, nil
- }
- // Sort list to get largest version
- flexver.VersionSlice(allowedVersions).Sort()
- return allowedVersions, allowedVersions[len(allowedVersions)-1], nil
+func (in VersionListQuery) WithQueryType(queryType QueryType) VersionListQuery {
+ return VersionListQuery{
+ Loader: in.Loader,
+ McVersion: in.McVersion,
+ QueryType: queryType,
}
}
-func FetchMavenVersionPrefixedList(url string, friendlyName string) func(mcVersion string) ([]string, string, error) {
- return FetchMavenVersionFiltered(url, friendlyName, hasPrefixSplitDash)
+// Queries the versions of a modloader
+func DoQuery(q VersionListQuery) (*ModLoaderVersions, error) {
+ return q.Loader.VersionListGetter(q)
}
-func FetchMavenVersionPrefixedListStrip(url string, friendlyName string) func(mcVersion string) ([]string, string, error) {
- noStrip := FetchMavenVersionPrefixedList(url, friendlyName)
- return func(mcVersion string) ([]string, string, error) {
- versions, latestVersion, err := noStrip(mcVersion)
- if err != nil {
- return nil, "", err
- }
- for k, v := range versions {
- versions[k] = removeMcVersion(v, mcVersion)
- }
- latestVersion = removeMcVersion(latestVersion, mcVersion)
- return versions, latestVersion, nil
- }
+// Retrieve a list of versions from maven, with no filtering or processing of the maven data
+func fetchVersionsFromMaven(q VersionListQuery, url string) (*ModLoaderVersions, error) {
+ identity_function := func(version string) *string { return &version }
+ return fetchMavenWithFilterMap(q, url, identity_function)
}
-func removeMcVersion(str string, mcVersion string) string {
- components := strings.Split(str, "-")
- newComponents := make([]string, 0)
- for _, v := range components {
- if v != mcVersion {
- newComponents = append(newComponents, v)
+func fetchForgeStyle(q VersionListQuery, url string) (*ModLoaderVersions, error) {
+ // Forge style:
+ // each version is formatted like `mcVersion-forgeVersion`
+ // eg: `1.18.1-39.0.18`
+ return fetchMavenWithFilterMap(q, url, func(version string) *string {
+ before, after, f := strings.Cut(version, "-")
+ if !f {
+ // The version didn't have a dash? Lets just reject it entirely
+ return nil
}
- }
- return strings.Join(newComponents, "-")
-}
-
-func hasPrefixSplitDash(str string, prefix string) bool {
- components := strings.Split(str, "-")
- if len(components) > 1 && components[0] == prefix {
- return true
- }
- return false
-}
-
-func FetchNeoForge() func(mcVersion string) ([]string, string, error) {
- // NeoForge reused Forge's versioning scheme for 1.20.1, but moved to their own versioning scheme for 1.20.2 and above
- neoforgeOld := FetchMavenVersionPrefixedListStrip("https://maven.neoforged.net/releases/net/neoforged/forge/maven-metadata.xml", "NeoForge")
- neoforgeNew := FetchMavenWithNeoForgeStyleVersions("https://maven.neoforged.net/releases/net/neoforged/neoforge/maven-metadata.xml", "NeoForge")
-
- return func(mcVersion string) ([]string, string, error) {
- if mcVersion == "1.20.1" {
- return neoforgeOld(mcVersion)
- } else {
- return neoforgeNew(mcVersion)
+ if before != q.McVersion {
+ // The part before the dash should match the mc version we're looking for
+ return nil
}
- }
-}
-
-func FetchMavenWithNeoForgeStyleVersions(url string, friendlyName string) func(mcVersion string) ([]string, string, error) {
- return FetchMavenVersionFiltered(url, friendlyName, func(neoforgeVersion string, mcVersion string) bool {
- // Minecraft versions are in the form of 1.a.b
- // Neoforge versions are in the form of a.b.x
- // Eg, for minecraft 1.20.6, neoforge version 20.6.2 and 20.6.83-beta would both be valid versions
- // for minecraft 1.20.2, neoforge version 20.2.23-beta
- // for minecraft 1.21, neoforge version 21.0.143 would be valid
- var mcSplit = strings.Split(mcVersion, ".")
- if len(mcSplit) < 2 {
- // This does not appear to be a minecraft version that's formatted in a way that matches neoforge
- return false
- }
- var mcMajor = mcSplit[1]
- var mcMinor = "0"
- if len(mcSplit) > 2 {
- mcMinor = mcSplit[2]
- }
-
- // We can only accept an exact version number match, because otherwise 1.21.1 would pull in loader versions for 1.21.10.
- var requiredPrefix = mcMajor + "." + mcMinor + "."
-
- return strings.HasPrefix(neoforgeVersion, requiredPrefix)
+ // The part after the dash is the actual version, and the part we care about
+ return &after
})
}
+func fetchNeoForgeStyle(q VersionListQuery, url string) (*ModLoaderVersions, error) {
+ // NeoForge style:
+ // If minecraft versions are in the form of 1.a.b, then neoforge versions are in the form of a.b.x
+ // Eg, for minecraft 1.20.6, neoforge version 20.6.2 and 20.6.83-beta would both be valid versions
+ // for minecraft 1.20.2, neoforge version 20.2.23-beta
+ // for minecraft 1.21, neoforge version 21.0.143 would be valid
+
+ var mcSplit = strings.Split(q.McVersion, ".")
+ if len(mcSplit) < 2 {
+ // This does not appear to be a minecraft version that's formatted in a way that matches neoforge
+ return nil, fmt.Errorf("packwiz cannot detect compatible %s versions for this Minecraft version (%s)", q.Loader.FriendlyName, q.McVersion)
+ }
+ var mcMajor = mcSplit[1]
+ var mcMinor = "0"
+ if len(mcSplit) > 2 {
+ mcMinor = mcSplit[2]
+ }
+ // Note that the period at the end is significant, we don't want to match `21.10.43` as being for 1.21.1 (instead of 1.21.10)
+ var requiredPrefix = mcMajor + "." + mcMinor + "."
+
+ return fetchMavenWithFilterMap(q, url, func(version string) *string {
+ if !strings.HasPrefix(version, requiredPrefix) {
+ // Reject NeoForge versions that don't have the right prefix for this mc version
+ return nil
+ }
+ return &version
+ })
+}
+
+func fetchLiteloaderStyle(q VersionListQuery, url string) (*ModLoaderVersions, error) {
+ // Liteloader style:
+ // each version is formatted like `mcVersion-SNAPSHOT`
+ // eg: `1.12.2-SNAPSHOT`
+ // (yes, it appears like liteloader only has a single version per mc version)
+ return fetchMavenWithFilterMap(q, url, func(version string) *string {
+ before, _, f := strings.Cut(version, "-")
+ // Check if the part before the dash matches the mc version we're looking for
+ if f && before == q.McVersion {
+ return &version
+ } else {
+ return nil
+ }
+ })
+}
+
+// Retrieves all versions through maven metadata, and then processes the using the provided `filterMap` function.
+// When `filterMap` returns a string, the version will be renamed to the provided string. If `nil` is returned, the
+// version is marked as invalid and will not be considered in the result.
+func fetchMavenWithFilterMap(q VersionListQuery, url string, filterMap func(version string) *string) (*ModLoaderVersions, error) {
+ res, err := GetWithUA(url, "application/xml")
+ if err != nil {
+ return nil, err
+ }
+ dec := xml.NewDecoder(res.Body)
+ out := MavenMetadata{}
+ err = dec.Decode(&out)
+ if err != nil {
+ return nil, err
+ }
+
+ // Pass all of the versions listed in the maven through our filterMap
+ versions := make([]string, 0, len(out.Versioning.Versions.Version))
+ for _, v := range out.Versioning.Versions.Version {
+ mappedV := filterMap(v)
+ if mappedV != nil {
+ versions = append(versions, *mappedV)
+ }
+ }
+
+ if len(versions) == 0 {
+ return nil, errors.New("no " + q.Loader.FriendlyName + " versions available for " + q.McVersion)
+ }
+
+ // Determine the latest release
+ var latestRelease = ""
+ release := filterMap(out.Versioning.Release)
+ latest := filterMap(out.Versioning.Latest)
+ if release != nil {
+ latestRelease = *release
+ } else if latest != nil {
+ latestRelease = *latest
+ } else {
+ // Maven was useless, just rely on flexver sorting
+ flexver.VersionSlice(versions).Sort()
+ latestRelease = versions[len(versions)-1]
+ }
+ return &ModLoaderVersions{versions, latestRelease}, nil
+}
+
+func fetchForNeoForge(q VersionListQuery) (*ModLoaderVersions, error) {
+ // NeoForge reused Forge's versioning scheme for 1.20.1, but moved to their own versioning scheme for 1.20.2 and above
+ if q.McVersion == "1.20.1" {
+ return fetchForgeStyle(q, "https://maven.neoforged.net/releases/net/neoforged/forge/maven-metadata.xml")
+ } else {
+ return fetchNeoForgeStyle(q, "https://maven.neoforged.net/releases/net/neoforged/neoforge/maven-metadata.xml")
+ }
+}
+
+func fetchForForge(q VersionListQuery) (*ModLoaderVersions, error) {
+ result, err := fetchForgeStyle(q, "https://files.minecraftforge.net/maven/net/minecraftforge/forge/maven-metadata.xml")
+ if err != nil {
+ return nil, err
+ }
+ // Forge is the only loader which defines a special recommended version
+ if q.QueryType == Recommended {
+ recommended := getForgeRecommended(q)
+ if recommended != "" {
+ result.Latest = recommended
+ }
+ }
+ return result, nil
+}
+
func ComponentToFriendlyName(component string) string {
if component == "minecraft" {
return "Minecraft"
@@ -215,8 +296,8 @@ type ForgeRecommended struct {
Versions map[string]string `json:"promos"`
}
-// GetForgeRecommended gets the recommended version of Forge for the given Minecraft version
-func GetForgeRecommended(mcVersion string) string {
+// getForgeRecommended gets the recommended version of Forge for the given Minecraft version
+func getForgeRecommended(q VersionListQuery) string {
res, err := GetWithUA("https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json", "application/json")
if err != nil {
return ""
@@ -230,11 +311,11 @@ func GetForgeRecommended(mcVersion string) string {
}
// Get mcVersion-recommended, if it doesn't exist then get mcVersion-latest
// If neither exist, return empty string
- recommendedString := fmt.Sprintf("%s-recommended", mcVersion)
+ recommendedString := fmt.Sprintf("%s-recommended", q.McVersion)
if out.Versions[recommendedString] != "" {
return out.Versions[recommendedString]
}
- latestString := fmt.Sprintf("%s-latest", mcVersion)
+ latestString := fmt.Sprintf("%s-latest", q.McVersion)
if out.Versions[latestString] != "" {
return out.Versions[latestString]
}
diff --git a/core/versionutil_test.go b/core/versionutil_test.go
new file mode 100644
index 0000000..c40db5c
--- /dev/null
+++ b/core/versionutil_test.go
@@ -0,0 +1,129 @@
+package core
+
+import (
+ "embed"
+ "os"
+ "slices"
+ "strings"
+ "testing"
+
+ "github.com/jarcoal/httpmock"
+)
+
+// For reproducability, we store a list of sample xml files for various endpoints
+// these have been edited slightly to cut down on the number of entries, but are
+// otherwise taken from the endpoints themselves
+
+//go:embed version_test_files/*
+var versionTestFiles embed.FS
+
+func registerMock(url string, filename string) {
+ bytes, err := versionTestFiles.ReadFile("version_test_files/" + filename)
+ if err != nil {
+ println("Error " + filename + " not in version_test_files/")
+ os.Exit(1)
+ }
+ httpmock.RegisterResponder("GET", url, httpmock.NewBytesResponder(200, bytes))
+}
+
+func queryWithMock(t *testing.T, q VersionListQuery) *ModLoaderVersions {
+ httpmock.Activate(t)
+
+ registerMock("https://maven.fabricmc.net/net/fabricmc/fabric-loader/maven-metadata.xml", "fabric.xml")
+ registerMock("https://repo.mumfrey.com/content/repositories/snapshots/com/mumfrey/liteloader/maven-metadata.xml", "liteloader.xml")
+ registerMock("https://maven.quiltmc.org/repository/release/org/quiltmc/quilt-loader/maven-metadata.xml", "quilt.xml")
+ registerMock("https://files.minecraftforge.net/maven/net/minecraftforge/forge/maven-metadata.xml", "forge.xml")
+ registerMock("https://maven.neoforged.net/releases/net/neoforged/forge/maven-metadata.xml", "neoforge_old.xml")
+ registerMock("https://maven.neoforged.net/releases/net/neoforged/neoforge/maven-metadata.xml", "neoforge.xml")
+
+ versionData, err := DoQuery(q)
+
+ if err != nil {
+ t.Logf("Error fetching versions for %s: %s", q.Loader.FriendlyName, err)
+ if strings.Contains(err.Error(), "no responder found") {
+ t.Log("You likely need to register a mock for this url")
+ }
+ t.FailNow()
+ }
+
+ return versionData
+}
+
+func expectLatest(t *testing.T, loader string, version string, expectedLatest string) string {
+ loaderData, ok := ModLoaders[loader]
+ if !ok {
+ t.Fatal("Could not find loader")
+ }
+ versionData := queryWithMock(t, MakeQuery(loaderData, version))
+
+ if len(versionData.Versions) == 0 {
+ t.Error("There should be at least one version")
+ }
+ if versionData.Latest != expectedLatest {
+ t.Errorf("Expected latest version to be %s, found %s", expectedLatest, versionData.Latest)
+ }
+
+ return versionData.Latest
+}
+
+func expectValid(t *testing.T, loader string, version string, expectedValid string) {
+ loaderData, ok := ModLoaders[loader]
+ if !ok {
+ t.Fatal("Could not find loader")
+ }
+ versionData := queryWithMock(t, MakeQuery(loaderData, version))
+
+ if !slices.Contains(versionData.Versions, expectedValid) {
+ t.Errorf("Expected %s to be a valid version for %s. Valid versions:\n%s", expectedValid, loaderData.FriendlyName, versionData.Versions)
+ }
+}
+
+func expectInvalid(t *testing.T, loader string, version string, expectedValid string) {
+ loaderData, ok := ModLoaders[loader]
+ if !ok {
+ t.Fatal("Could not find loader")
+ }
+ versionData := queryWithMock(t, MakeQuery(loaderData, version))
+
+ if slices.Contains(versionData.Versions, expectedValid) {
+ t.Errorf("Expected %s not to be a valid version for %s. Valid versions:\n%s", expectedValid, loaderData.FriendlyName, versionData.Versions)
+ }
+}
+
+func TestFabric121(t *testing.T) {
+ expectLatest(t, "fabric", "1.21", "0.17.3")
+}
+
+func TestFabric010Valid(t *testing.T) {
+ expectValid(t, "fabric", "1.21", "0.10.6+build.214")
+}
+
+func TestQuilt121(t *testing.T) {
+ expectLatest(t, "quilt", "1.21", "0.29.3-beta.1")
+}
+
+func TestForge121(t *testing.T) {
+ expectLatest(t, "forge", "1.21", "51.0.33")
+}
+
+func TestLiteLoader112(t *testing.T) {
+ expectLatest(t, "liteloader", "1.12", "1.12-SNAPSHOT")
+}
+
+func TestNeoForge1201(t *testing.T) {
+ expectLatest(t, "neoforge", "1.20.1", "47.1.106")
+}
+
+func TestNeoForge121(t *testing.T) {
+ expectLatest(t, "neoforge", "1.21", "21.0.167")
+}
+
+func TestNeoForge1211(t *testing.T) {
+ expectLatest(t, "neoforge", "1.21.1", "21.1.213")
+ expectValid(t, "neoforge", "1.21.1", "21.1.201")
+ expectInvalid(t, "neoforge", "1.21.1", "21.10.43-beta")
+}
+
+func TestNeoForge1210(t *testing.T) {
+ expectLatest(t, "neoforge", "1.21.10", "21.10.43-beta")
+}
diff --git a/go.mod b/go.mod
index 8fcb8e4..5c431d2 100644
--- a/go.mod
+++ b/go.mod
@@ -39,6 +39,7 @@ require (
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/jarcoal/httpmock v1.4.1
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
diff --git a/go.sum b/go.sum
index 1e4dc76..22fe0e0 100644
--- a/go.sum
+++ b/go.sum
@@ -40,6 +40,8 @@ github.com/igorsobreira/titlecase v0.0.0-20140109233139-4156b5b858ac h1:AfRcPFr4
github.com/igorsobreira/titlecase v0.0.0-20140109233139-4156b5b858ac/go.mod h1:KOzUkqpWM2xArNm82cehGc5PBFYV1Qadzzt81aJi7F0=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/jarcoal/httpmock v1.4.1 h1:0Ju+VCFuARfFlhVXFc2HxlcQkfB+Xq12/EotHko+x2A=
+github.com/jarcoal/httpmock v1.4.1/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
diff --git a/migrate/loader.go b/migrate/loader.go
index b068498..3b6bdf1 100644
--- a/migrate/loader.go
+++ b/migrate/loader.go
@@ -40,12 +40,18 @@ var loaderCommand = &cobra.Command{
fmt.Printf("Error getting Minecraft version: %s\n", err)
os.Exit(1)
}
- if args[0] == "latest" {
- fmt.Println("Updating to latest loader version")
+ if args[0] == "latest" || args[0] == "recommended" {
+ fmt.Printf("Updating to %s loader version\n", args[0])
+
+ queryType := core.Latest
+ if args[0] == "recommended" {
+ queryType = core.Recommended
+ }
+
// We'll be updating to the latest loader version
for _, loader := range currentLoaders {
- _, latest, gottenLoader := getVersionsForLoader(loader, mcVersion)
- if !updatePackToVersion(latest, modpack, gottenLoader) {
+ versionData, gottenLoader := getVersionsForLoader(loader, mcVersion, queryType)
+ if !updatePackToVersion(versionData.Latest, modpack, gottenLoader) {
continue
}
// Write the pack to disk
@@ -55,36 +61,14 @@ var loaderCommand = &cobra.Command{
continue
}
}
- } else if args[0] == "recommended" {
- // TODO: Figure out a way to get the recommended version, this is Forge only
- // Ensure we're on Forge
- if !slices.Contains(currentLoaders, "forge") {
- fmt.Println("The recommended loader version is only available on Forge!")
- os.Exit(1)
- }
- // We'll be updating to the recommended loader version
- recommendedVer := core.GetForgeRecommended(mcVersion)
- if recommendedVer == "" {
- fmt.Println("Error getting recommended Forge version!")
- os.Exit(1)
- }
- if ok := updatePackToVersion(recommendedVer, modpack, core.ModLoaders["forge"]); !ok {
- os.Exit(1)
- }
- // Write the pack to disk
- err = modpack.Write()
- if err != nil {
- fmt.Printf("Error writing pack.toml: %s", err)
- os.Exit(1)
- }
} else {
fmt.Println("Updating to explicit loader version")
// This one is easy :D
- versions, _, loader := getVersionsForLoader(currentLoaders[0], mcVersion)
+ versionData, loader := getVersionsForLoader(currentLoaders[0], mcVersion, core.Latest)
// Check if the loader happens to be Forge/NeoForge, since there's two version formats
if loader.Name == "forge" || loader.Name == "neoforge" {
wantedVersion := cmdshared.GetRawForgeVersion(args[0])
- validateVersion(versions, wantedVersion, loader)
+ validateVersion(versionData.Versions, wantedVersion, loader)
_ = updatePackToVersion(wantedVersion, modpack, loader)
} else if loader.Name == "liteloader" {
// These are weird and just have a MC version
@@ -92,7 +76,7 @@ var loaderCommand = &cobra.Command{
os.Exit(0)
} else {
// We're on Fabric or quilt
- validateVersion(versions, args[0], loader)
+ validateVersion(versionData.Versions, args[0], loader)
if ok := updatePackToVersion(args[0], modpack, loader); !ok {
os.Exit(1)
}
@@ -111,18 +95,18 @@ func init() {
migrateCmd.AddCommand(loaderCommand)
}
-func getVersionsForLoader(loader, mcVersion string) ([]string, string, core.ModLoaderComponent) {
+func getVersionsForLoader(loader, mcVersion string, queryType core.QueryType) (*core.ModLoaderVersions, core.ModLoaderComponent) {
gottenLoader, ok := core.ModLoaders[loader]
if !ok {
fmt.Printf("Unknown loader %s\n", loader)
os.Exit(1)
}
- versions, latestVersion, err := gottenLoader.VersionListGetter(mcVersion)
+ versionData, err := core.DoQuery(core.MakeQuery(gottenLoader, mcVersion).WithQueryType(queryType))
if err != nil {
fmt.Printf("Error getting version list for %s: %s\n", gottenLoader.FriendlyName, err)
os.Exit(1)
}
- return versions, latestVersion, gottenLoader
+ return versionData, gottenLoader
}
func validateVersion(versions []string, version string, gottenLoader core.ModLoaderComponent) {