package main import ( "crypto/sha1" "encoding/hex" "encoding/json" "fmt" "io" "net/http" "os" "path/filepath" "strings" "time" "github.com/zhyee/zipstream" ) type Forge struct{} type ForgeVersion struct { Version string Time string Url string } 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 } 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, "") { parsing = true } else if strings.Contains(line, "") { parsing = false } else if parsing { if strings.Contains(line, "") { buff = "" foundTR = true } else if strings.Contains(line, "") { 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, "") { version.Version = strings.TrimSpace(lines[ind+1]) } else if strings.Contains(line, "")[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 } 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) } }