diff --git a/cmd/root.go b/cmd/root.go index 59fd5a3..639570f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -21,7 +21,7 @@ func init() { rootCmd.Flags().String("server-dir", "mc", "Server directory path") rootCmd.Flags().String("server-file", "", "Server jar file name") rootCmd.Flags().String("proxy", "", "Use a proxy to download") - rootCmd.Flags().Int("download-thread", 8, "Download threads") + rootCmd.Flags().Int("download-threads", 8, "Download threads") rootCmd.Flags().Int("retry-times", 3, "Number of retries when a download fails") } @@ -52,7 +52,7 @@ var rootCmd = &cobra.Command{ log.Fatalln(err) } } - downloadThreads, err := cmd.Flags().GetInt("download-thread") + downloadThreads, err := cmd.Flags().GetInt("download-threads") if err != nil || downloadThreads > 64 { downloadThreads = 8 log.Println(err) @@ -75,7 +75,7 @@ var rootCmd = &cobra.Command{ } archivePath := "" - if util.FileExists(input) { + if util.PathIsFile(input) { archivePath = input } else if util.IsValidUrl(input) { @@ -141,32 +141,44 @@ var rootCmd = &cobra.Command{ log.Printf("Flavor dependencies: %+v\n", index.Dependencies) // download server if not present - if serverFile != "" && !util.FileExists(path.Join(serverDir, serverFile)) { - // Determine server platform - var provider server.Provider = nil + if !util.PathIsFile(path.Join(serverDir, serverFile)) { + log.Println("Server file not present, downloading...") + log.Println("(Point --server-dir and --server-file flags for an existing server file to skip this step.)") + + var provider server.Provider if index.Dependencies.Fabric != "" { - provider = &server.Fabric{ - MinecraftVersion: index.Dependencies.Minecraft, - FabricVersion: index.Dependencies.Fabric, + provider, err = server.NewProvider("fabric", index.Dependencies.Minecraft, index.Dependencies.Fabric) + if err != nil { + log.Fatalln(err) + } + } else if index.Dependencies.Quilt != "" { + provider, err = server.NewProvider("quilt", index.Dependencies.Minecraft, index.Dependencies.Quilt) + if err != nil { + log.Fatalln(err) + } + } else if index.Dependencies.Forge != "" { + provider, err = server.NewProvider("forge", index.Dependencies.Minecraft, index.Dependencies.Forge) + if err != nil { + log.Fatalln(err) } - } else if index.Dependencies.Quilt != "" || index.Dependencies.Forge != "" { - log.Fatalln("Automatic server deployment not yet implemented for this flavor! Supply the path to an existing server jar file with the --server-dir and --server-file flags.") } else { - // TODO: vanilla server download + provider, err = server.NewProvider("vanilla", index.Dependencies.Minecraft, "") + if err != nil { + log.Fatalln(err) + } } - // Download server - err := provider.Provide(serverDir, serverFile) + err = provider.Provide(serverDir, serverFile) if err != nil { log.Fatalln(err) } } else { - log.Println("Server jar file already present, skipping download...") + log.Println("Server file already present, proceeding...") } - // download mods + // mod downloads log.Printf("Downloading %v dependencies...\n", len(index.Files)) - var downloadPoolArray []*requester.DownloadPool + var downloadPoolArray []*requester.Download for i := range index.Files { file := index.Files[i] if file.Env.Server == modrinth.UnsupportedEnvSupport { @@ -174,26 +186,27 @@ var rootCmd = &cobra.Command{ } downloadPoolArray = append(downloadPoolArray, requester.NewDownloadPool(file.Downloads, map[string]string{"sha1": string(file.Hashes.Sha1)}, filepath.Base(file.Path), path.Join(serverDir, filepath.Dir(file.Path)))) } - downloadPools := requester.NewDownloadPools(requester.DefaultHttpClient, downloadPoolArray, downloadThreads, retryTimes) downloadPools.Do() - log.Println("Extracting overrides...") - err = mrpack.ExtractOverrides(archivePath, serverDir) - if err != nil { - log.Fatalln(err) - } - uncleanNotification := false - for i := range downloadPools.DownloadPool { - dl := downloadPools.DownloadPool[i] + modsUnclean := false + for i := range downloadPools.Downloads { + dl := downloadPools.Downloads[i] if !dl.Success { - uncleanNotification = true + modsUnclean = true log.Println("Dependency downloaded Fail:", dl.FileName) } } - if uncleanNotification { - log.Fatalln("Download failed,You can fix the error manually") + + // overrides + log.Println("Extracting overrides...") + err = mrpack.ExtractOverrides(archivePath, serverDir) + if err != nil { + log.Fatalln(err) } + if modsUnclean { + log.Println("There have been problems downloading downloading mods, you probably have to fix some dependency problems manually!") + } log.Println("Done :) Have a nice day ✌️") }, } diff --git a/cmd/server.go b/cmd/server.go index 6622c34..e2d9a83 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -52,39 +52,11 @@ var serverCmd = &cobra.Command{ minecraftVersion = latestMinecraftVersion } - var provider server.Provider = nil - switch args[0] { - case "vanilla": - provider = &server.Vanilla{ - MinecraftVersion: minecraftVersion, - } - case "fabric": - provider = &server.Fabric{ - MinecraftVersion: minecraftVersion, - FabricVersion: flavorVersion, - } - case "quilt": - provider = &server.Quilt{ - MinecraftVersion: minecraftVersion, - QuiltVersion: flavorVersion, - } - case "forge": - provider = &server.Forge{ - MinecraftVersion: minecraftVersion, - ForgeVersion: flavorVersion, - } - case "paper": - provider = &server.Paper{ - MinecraftVersion: minecraftVersion, - PaperVersion: flavorVersion, - } - case "spigot": - provider = &server.Spigot{ - MinecraftVersion: minecraftVersion, - SpigotVersion: flavorVersion, - } + flavor := args[0] + provider, err := server.NewProvider(flavor, minecraftVersion, flavorVersion) + if err != nil { + log.Fatalln(err) } - err = provider.Provide(serverDir, serverFile) if err != nil { log.Fatalln(err) diff --git a/requester/multiDownload.go b/requester/multiDownload.go index baad683..09c4e8b 100644 --- a/requester/multiDownload.go +++ b/requester/multiDownload.go @@ -6,65 +6,65 @@ import ( "sync" ) -type DownloadPool struct { - downloadLink []string - hash map[string]string - FileName string - downloadDir string - Success bool +type Download struct { + links []string + hash map[string]string + FileName string + downloadDir string + Success bool } type DownloadPools struct { - httpClient *HTTPClient - DownloadPool []*DownloadPool - downloadThreads int - retryTimes int + httpClient *HTTPClient + Downloads []*Download + threads int + retryTimes int } -func NewDownloadPools(httpClient *HTTPClient, downloadPool []*DownloadPool, downloadThreads int, retryTimes int) *DownloadPools { +func NewDownloadPools(httpClient *HTTPClient, downloadPool []*Download, downloadThreads int, retryTimes int) *DownloadPools { return &DownloadPools{httpClient, downloadPool, downloadThreads, retryTimes} } -func NewDownloadPool(downloadLink []string, hash map[string]string, fileName string, downloadDir string) *DownloadPool { - return &DownloadPool{downloadLink, hash, fileName, downloadDir, false} +func NewDownloadPool(downloadLink []string, hash map[string]string, fileName string, downloadDir string) *Download { + return &Download{downloadLink, hash, fileName, downloadDir, false} } func (downloadPools *DownloadPools) Do() { var wg sync.WaitGroup - ch := make(chan struct{}, downloadPools.downloadThreads) - for i := range downloadPools.DownloadPool { - file := downloadPools.DownloadPool[i] + ch := make(chan struct{}, downloadPools.threads) + for i := range downloadPools.Downloads { + dl := downloadPools.Downloads[i] //goroutine ch <- struct{}{} wg.Add(1) go func() { defer wg.Done() - for _, downloadLink := range file.downloadLink { - // when download failed retry - for retryTime := 0; retryTime < downloadPools.retryTimes; retryTime++ { + for _, link := range dl.links { + // retry when download failed + for retries := 0; retries < downloadPools.retryTimes; retries++ { - //download file - f, err := downloadPools.httpClient.DownloadFile(downloadLink, file.downloadDir, file.FileName) + // download file + f, err := downloadPools.httpClient.DownloadFile(link, dl.downloadDir, dl.FileName) if err != nil { - log.Println("Download failed for:", file.FileName, err, "retry times:", retryTime) + log.Println("Download failed for:", dl.FileName, err, "attempt:", retries+1) continue } - //check hashcode - if sha1code, ok := file.hash["sha1"]; ok { + // check hashcode + if sha1code, ok := dl.hash["sha1"]; ok { _, err = util.CheckFileSha1(sha1code, f) } if err != nil { - log.Println("Hash check failed for:", file.FileName, err, "retry times:", retryTime) + log.Println("Hash check failed for:", dl.FileName, err, "attempt:", retries+1) continue } log.Println("Downloaded:", f) - file.Success = true + dl.Success = true break } - if file.Success { + if dl.Success { break } } diff --git a/server/forge.go b/server/forge.go index 975a8d4..902f891 100644 --- a/server/forge.go +++ b/server/forge.go @@ -1,6 +1,8 @@ package server -import "errors" +import ( + "errors" +) type Forge struct { MinecraftVersion string diff --git a/server/interface.go b/server/interface.go deleted file mode 100644 index 64d14be..0000000 --- a/server/interface.go +++ /dev/null @@ -1,5 +0,0 @@ -package server - -type Provider interface { - Provide(serverDir string, serverFile string) error -} diff --git a/server/paper.go b/server/paper.go index 5b097ab..88eefea 100644 --- a/server/paper.go +++ b/server/paper.go @@ -25,15 +25,19 @@ func (supplier *Paper) Provide(serverDir string, serverFile string) error { } `json:"downloads"` } `json:"builds"` } - err := requester.DefaultHttpClient.GetJson("https://api.papermc.io/v2/projects/paper/versions/"+supplier.MinecraftVersion+"/builds", &response, nil) + + err := requester.DefaultHttpClient.GetJson("https://api.papermc.io/v2/projects/paper/versions/"+ + supplier.MinecraftVersion+"/builds", &response, nil) if err != nil { return err } + for i := range response.Builds { i = len(response.Builds) - 1 - i - b := response.Builds[i] - if b.Channel == "default" { - u := "https://api.papermc.io/v2/projects/paper/versions/" + supplier.MinecraftVersion + "/builds/" + strconv.Itoa(b.Id) + "/downloads/" + b.Downloads.Application.Name + if response.Builds[i].Channel == "default" { + u := "https://api.papermc.io/v2/projects/paper/versions/" + supplier.MinecraftVersion + + "/builds/" + strconv.Itoa(response.Builds[i].Id) + + "/downloads/" + response.Builds[i].Downloads.Application.Name file, err := requester.DefaultHttpClient.DownloadFile(u, serverDir, serverFile) if err != nil { return err @@ -43,5 +47,6 @@ func (supplier *Paper) Provide(serverDir string, serverFile string) error { return nil } } + return errors.New("no stable paper release found") } diff --git a/server/provider.go b/server/provider.go new file mode 100644 index 0000000..cf179dd --- /dev/null +++ b/server/provider.go @@ -0,0 +1,52 @@ +package server + +import ( + "errors" + "strings" +) + +type Provider interface { + Provide(serverDir string, serverFile string) error +} + +func NewProvider(flavor string, minecraftVersion string, flavorVersion string) (Provider, error) { + var provider Provider = nil + + switch strings.ToLower(flavor) { + case "vanilla": + provider = &Vanilla{ + MinecraftVersion: minecraftVersion, + } + case "fabric": + provider = &Fabric{ + MinecraftVersion: minecraftVersion, + FabricVersion: flavorVersion, + } + case "quilt": + provider = &Quilt{ + MinecraftVersion: minecraftVersion, + QuiltVersion: flavorVersion, + } + case "forge": + provider = &Forge{ + MinecraftVersion: minecraftVersion, + ForgeVersion: flavorVersion, + } + case "paper": + provider = &Paper{ + MinecraftVersion: minecraftVersion, + PaperVersion: flavorVersion, + } + case "spigot": + provider = &Spigot{ + MinecraftVersion: minecraftVersion, + SpigotVersion: flavorVersion, + } + } + + if provider == nil { + return nil, errors.New("no provider for this flavor") + } + + return provider, nil +} diff --git a/server/quilt.go b/server/quilt.go index 6d555ac..83c00cb 100644 --- a/server/quilt.go +++ b/server/quilt.go @@ -12,6 +12,8 @@ type Quilt struct { } func (supplier *Quilt) Provide(serverDir string, serverFile string) error { + return errors.New("quilt provider not yet implemented") + err := os.MkdirAll("work/quilt", 0755) if err != nil { log.Fatalln(err) @@ -20,5 +22,5 @@ func (supplier *Quilt) Provide(serverDir string, serverFile string) error { // TODO: download https://maven.quiltmc.org/repository/release/org/quiltmc/quilt-installer/latest/quilt-installer-latest.jar // TODO: java -jar quilt-installer-latest.jar install server ${minecraftVersion} --download-server - return errors.New("quilt provider not yet implemented") + return nil } diff --git a/server/spigot.go b/server/spigot.go index 7e72749..594d66b 100644 --- a/server/spigot.go +++ b/server/spigot.go @@ -12,6 +12,8 @@ type Spigot struct { } func (supplier *Spigot) Provide(serverDir string, serverFile string) error { + return errors.New("spigot provider not yet implemented") + err := os.MkdirAll("work/spigot", 0755) if err != nil { log.Fatalln(err) @@ -21,5 +23,5 @@ func (supplier *Spigot) Provide(serverDir string, serverFile string) error { // TODO: git config --global --unset core.autocrlf // TODO: java -jar BuildTools.jar --rev ${minecraftVersion} - return errors.New("spigot provider not yet implemented") + return nil } diff --git a/util/file.go b/util/file.go index 473a8b1..7ca1c1e 100644 --- a/util/file.go +++ b/util/file.go @@ -2,7 +2,18 @@ package util import "os" -func FileExists(path string) bool { - _, err := os.Stat(path) - return err == nil +func PathIsFile(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + return info.Mode().IsRegular() +} + +func PathIsDir(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + return info.Mode().IsDir() }