2024-10-31 21:20:20 -06:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-11-01 18:08:29 -06:00
|
|
|
"crypto/sha1"
|
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
2024-10-31 21:20:20 -06:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
2024-11-01 18:08:29 -06:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2024-10-31 21:20:20 -06:00
|
|
|
"strings"
|
2024-11-01 18:08:29 -06:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/zhyee/zipstream"
|
2024-10-31 21:20:20 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
type Forge struct{}
|
|
|
|
|
|
|
|
type ForgeVersion struct {
|
|
|
|
Version string
|
|
|
|
Time string
|
|
|
|
Url string
|
|
|
|
}
|
|
|
|
|
2024-11-01 18:08:29 -06:00
|
|
|
type ForgeLibraryArtifact struct {
|
|
|
|
Path string
|
|
|
|
Url string
|
|
|
|
Sha1 string
|
|
|
|
}
|
|
|
|
|
|
|
|
type ForgeLibraryDownload struct {
|
|
|
|
Artifact ForgeLibraryArtifact
|
|
|
|
}
|
|
|
|
|
|
|
|
type ForgeLibrary struct {
|
|
|
|
Name string
|
|
|
|
Downloads ForgeLibraryDownload
|
|
|
|
}
|
|
|
|
|
|
|
|
type ForgeInstallData struct {
|
|
|
|
Id string
|
|
|
|
Time time.Time
|
|
|
|
ReleaseTime time.Time
|
|
|
|
InheritsFrom string
|
|
|
|
Type string
|
|
|
|
MainClass string
|
|
|
|
Libraries []ForgeLibrary
|
|
|
|
}
|
|
|
|
|
2024-10-31 21:20:20 -06:00
|
|
|
func parseForgeVersions(html string) []ForgeVersion {
|
|
|
|
lines := strings.Split(html, "\n")
|
|
|
|
parsing := false
|
|
|
|
foundTR := false
|
|
|
|
buff := ""
|
|
|
|
versions := []ForgeVersion{}
|
|
|
|
for _, line := range lines {
|
|
|
|
if strings.Contains(line, "<tbody>") {
|
|
|
|
parsing = true
|
|
|
|
} else if strings.Contains(line, "</tbody>") {
|
|
|
|
parsing = false
|
|
|
|
} else if parsing {
|
|
|
|
if strings.Contains(line, "<tr>") {
|
|
|
|
buff = ""
|
|
|
|
foundTR = true
|
|
|
|
} else if strings.Contains(line, "</tr>") {
|
|
|
|
foundTR = false
|
|
|
|
versions = append(versions, parseForgeVersion(buff))
|
|
|
|
} else if foundTR {
|
|
|
|
buff += line + "\n"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return versions
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseForgeVersion(html string) ForgeVersion {
|
|
|
|
lines := strings.Split(html, "\n")
|
|
|
|
version := ForgeVersion{}
|
|
|
|
for ind, line := range lines {
|
|
|
|
if strings.Contains(line, "<td class=\"download-version\">") {
|
|
|
|
version.Version = strings.TrimSpace(lines[ind+1])
|
|
|
|
} else if strings.Contains(line, "<td class=\"download-time\"") {
|
|
|
|
version.Time = strings.Split(strings.Split(line, "<td class=\"download-time\" title=\"")[1], "\">")[0]
|
|
|
|
} else if strings.Contains(line, "https://adfoc.us") && strings.Contains(line, "installer.jar") {
|
|
|
|
version.Url = strings.Split(strings.Split(line, "&url=")[1], "\">")[0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return version
|
|
|
|
}
|
|
|
|
|
|
|
|
func (Forge) GetForgeVersions(mcVersion string) ([]ForgeVersion, error) {
|
|
|
|
resp, err := http.Get(fmt.Sprintf("https://files.minecraftforge.net/net/minecraftforge/forge/index_%s.html", mcVersion))
|
|
|
|
if err != nil {
|
|
|
|
return []ForgeVersion{}, fmt.Errorf("unable to access minecraft forge index: %e", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
return []ForgeVersion{}, fmt.Errorf("unable to access minecraft forge index: %s", err)
|
|
|
|
}
|
|
|
|
data, _ := io.ReadAll(resp.Body)
|
|
|
|
return parseForgeVersions(string(data)), nil
|
|
|
|
}
|
2024-11-01 18:08:29 -06:00
|
|
|
|
|
|
|
func GetForgeInstallDataFromVersion(version ForgeVersion) (ForgeInstallData, error) {
|
|
|
|
resp, err := http.Get(version.Url)
|
|
|
|
if err != nil {
|
|
|
|
return ForgeInstallData{}, fmt.Errorf("unable to pull jar file: %e", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
return ForgeInstallData{}, fmt.Errorf("unable to pull jar file: %s", resp.Status)
|
|
|
|
}
|
|
|
|
zs := zipstream.NewReader(resp.Body)
|
|
|
|
for {
|
|
|
|
entry, err := zs.GetNextEntry()
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if entry.Name != "version.json" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
f, _ := entry.Open()
|
|
|
|
defer f.Close()
|
|
|
|
data, _ := io.ReadAll(f)
|
|
|
|
installData := ForgeInstallData{}
|
|
|
|
json.Unmarshal(data, &installData)
|
|
|
|
return installData, nil
|
|
|
|
}
|
|
|
|
return ForgeInstallData{}, fmt.Errorf("unable to find version.json")
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetForgeInstallData(mcVersion string, forgeVersion string) (ForgeInstallData, error) {
|
|
|
|
versions, err := Forge{}.GetForgeVersions(mcVersion)
|
|
|
|
if err != nil {
|
|
|
|
return ForgeInstallData{}, fmt.Errorf("failed to pull forge versions: %e", err)
|
|
|
|
}
|
|
|
|
for _, version := range versions {
|
|
|
|
if version.Version == forgeVersion {
|
|
|
|
return GetForgeInstallDataFromVersion(version)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ForgeInstallData{}, fmt.Errorf("unable to find the requested version")
|
|
|
|
}
|
|
|
|
|
|
|
|
func InstallForgeLibs(mcVersion string, forgeVersion string, libDir string) {
|
|
|
|
installData, err := GetForgeInstallData(mcVersion, forgeVersion)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("Unable to get install data: %s\n", err)
|
|
|
|
}
|
|
|
|
for _, lib := range installData.Libraries {
|
|
|
|
if _, err := os.Stat(filepath.Join(libDir, lib.Downloads.Artifact.Path)); err == nil {
|
|
|
|
if f, err := os.OpenFile(filepath.Join(libDir, lib.Downloads.Artifact.Path), os.O_RDONLY, 0755); err == nil {
|
|
|
|
defer f.Close()
|
|
|
|
data, _ := io.ReadAll(f)
|
|
|
|
sha := sha1.Sum(data)
|
|
|
|
if hex.EncodeToString(sha[:20]) == lib.Downloads.Artifact.Sha1 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
resp, err := http.Get(lib.Downloads.Artifact.Url)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("Unable to download library %s: %s\n", lib.Name, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
fmt.Printf("Unable to download library %s: %s\n", lib.Name, resp.Status)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
tokens := strings.Split(lib.Downloads.Artifact.Path, "/")
|
|
|
|
path := ""
|
|
|
|
for ind, token := range tokens {
|
|
|
|
if ind == len(tokens)-1 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
path = filepath.Join(path, token)
|
|
|
|
}
|
|
|
|
os.MkdirAll(filepath.Join(libDir, path), 0755)
|
|
|
|
f, _ := os.OpenFile(filepath.Join(libDir, lib.Downloads.Artifact.Path), os.O_CREATE|os.O_RDWR, 0755)
|
|
|
|
defer f.Close()
|
|
|
|
io.Copy(f, resp.Body)
|
|
|
|
}
|
|
|
|
}
|