From f51a0e4a510c3b0c458f7cef5116f39aff6b2ee4 Mon Sep 17 00:00:00 2001 From: Fellsoul Date: Thu, 7 Mar 2024 00:18:17 -0700 Subject: [PATCH 1/6] Add supports for Papermc and Arclight --- README.MD | 30 ++++--- README_zh.MD | 16 +++- arclight_installer.go | 195 ++++++++++++++++++++++++++++++++++++++++++ changelogs/v1.2.4.MD | 9 ++ cli/main.go | 34 ++++++++ cli/usage.go | 30 ------- httpclient.go | 41 ++++++++- papermc_installer.go | 157 ++++++++++++++++++++++++++++++++++ 8 files changed, 467 insertions(+), 45 deletions(-) create mode 100644 arclight_installer.go create mode 100644 changelogs/v1.2.4.MD create mode 100644 papermc_installer.go diff --git a/README.MD b/README.MD index b6b8324..90879aa 100644 --- a/README.MD +++ b/README.MD @@ -19,15 +19,17 @@ This is a minecraft server online installer You can use this cli to easily install minecraft server. We also support some types of servers and modpacks -| Server Type | Support | -|-------------|---------| -| Vanilla | true | -| Fabric | true | -| Forge | true | -| Quilt | true | -| Spigot | true | -| PaperMC | TODO | -| ArcLight | TODO | +| Server Type | Support | +|--------------|---------| +| Vanilla | true | +| Fabric | true | +| Forge | true | +| Quilt | true | +| Spigot | true | +| PaperMC | true | +| ArcLight | true | +| Mohist | TODO | +| Catserver | TODO | | Modpack Type | Support | |--------------|---------| @@ -51,7 +53,7 @@ Flags: the version of the server need to be installed, default is the latest (default "latest") Args: string - type of the server [fabric forge quilt spigot vanilla] (default "vanilla" ) + type of the server [fabric forge quilt spigot vanilla papermc arclight] (default "vanilla" ) filepath | URL the modpack's local path or an URL. If it's an URL, installer will download the modpack first ``` @@ -80,6 +82,12 @@ minecraft_installer -name minecraft_server -version 1.16.5 -server forge minecraft_installer -name minecraft_server -version 1.19.2 -server fabric -path server ``` +```sh +# Install papermc 1.14.4 server into {PATH}/server-1.14.4-{BUILDNUM}/{PAPERMC-INSTALLED-NAME}.jar +minecraft_installer -name minecraft_server -version 1.14.4 -server papermc +# papermc and arclight installation will automatically generate different directories for different builder. +``` + ### Install modpacks ```sh @@ -105,8 +113,8 @@ minecraft_installer versions minecraft_installer -version snapshot versions ``` + ## TODO -- [ ] PaperMC - [ ] Search modpacks from modrinth - [ ] Configurable proxy diff --git a/README_zh.MD b/README_zh.MD index 3edb2da..4eb6491 100644 --- a/README_zh.MD +++ b/README_zh.MD @@ -22,8 +22,10 @@ | Forge | 是 | | Quilt | 是 | | Spigot | 是 | -| PaperMC | 进行中 | -| ArcLight | 进行中 | +| PaperMC | 是 | +| ArcLight | 是 | +| Mohist | 计划中/否 | +| Catserver | 计划中/否 | | 整合包类型 | 支持 | |--------------|----------| @@ -76,6 +78,12 @@ minecraft_installer -name minecraft_server -version 1.16.5 -server forge minecraft_installer -name minecraft_server -version 1.19.2 -server fabric -path server ``` +```sh +# 将 papermc 1.14.4 服务端下载到 {PATH}/server-1.14.4-{BUILDNUM}/{PAPERMC-INSTALLED-NAME}.jar +minecraft_installer -name minecraft_server -version 1.14.4 -server papermc +# 注:papermc以及arclight服务器因build区别会区分到不同文件夹中,执行中会自动建立此文件夹,更加方便识别 +``` + ### 安装整合包 ```sh @@ -100,3 +108,7 @@ minecraft_installer versions ```sh minecraft_installer -version snapshot versions ``` +## 计划中 + +- [ ] 从modrinth搜寻整合包 +- [ ] 代理相关 \ No newline at end of file diff --git a/arclight_installer.go b/arclight_installer.go new file mode 100644 index 0000000..247e3eb --- /dev/null +++ b/arclight_installer.go @@ -0,0 +1,195 @@ +package installer + +import ( + "context" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" +) + +type ( + ArclightInstaller struct { + } + + ArclightRelease struct { + Assets []ArclightAssets `json:"assets"` + IsExpired bool + PublishTime string `json:"published_at"` + } + + ArclightAssets struct { + AssetsUrl string `json:"url"` + AssetsName string `json:"name"` + DownloadUrl string `json:"browser_download_url"` + } +) + +var DefaultArclightInstaller = &ArclightInstaller{} + +var _ Installer = DefaultArclightInstaller + +func init() { + Installers["arclight"] = DefaultArclightInstaller +} + +func (r *ArclightInstaller) Install(path, name string, target string) (installed string, err error) { + return r.InstallWithLoader(path, name, target, "") +} + +func (r *ArclightInstaller) InstallWithLoader(path, name string, target string, loader string) (installed string, err error) { + data, err := r.GetInstallerVersions() + if err != nil { + return "", err + } + if len(loader) == 0 { + allVersions := r.GetOnlyVersions(data) + if target == "latest" { + loader, err = r.GetLatestVersion() + if err != nil { + return "", err + } + goto DownloadPart + } + for i := 0; i < len(allVersions); i += 1 { + if allVersions[i] == target { + loader = target + goto DownloadPart + } + } + loger.Info("not find the suitable builder, the version should be included in the following list:") + for i := 0; i < len(allVersions); i += 1 { + if data[allVersions[i]].IsExpired == true { + loger.Info("versions:", allVersions[i], " EXPIRED, DO NOT SUPPORT") + } else { + loger.Info("versions:", allVersions[i]) + } + } + return "", &VersionNotFoundErr{target} + } +DownloadPart: + ExactDownloadeName := data[loader].Assets[0].AssetsName + ArclightInstallerUrl := data[loader].Assets[0].DownloadUrl + if data[loader].IsExpired == true { + loger.Fatal("Sorry, the one you choose has already expired, try another version.") + return "", &VersionNotFoundErr{target} + } + var buildJar string + if buildJar, err = DefaultHTTPClient.DownloadDirect(ArclightInstallerUrl, ExactDownloadeName, downloadingCallback(ArclightInstallerUrl)); err != nil { + return + } + installed, err = r.Runbuilder(buildJar, ExactDownloadeName, path) + if err != nil { + loger.Info("an error occurred while running the server jar file, but you can still do that manually.") + loger.Error(err) + } + return +} + +func (r *ArclightInstaller) ListVersions(snapshot bool) (versions []string, err error) { + data, err := r.GetInstallerVersions() + if err != nil { + return + } + var dataVersions []string = r.GetOnlyVersions(data) + for _, v := range dataVersions { + versions = append(versions, v) + } + return +} + +func (r *ArclightInstaller) GetLatestVersion() (version string, err error) { + data, err := r.GetInstallerVersions() + if err != nil { + return + } + var dataVersions []string = r.GetOnlyVersions(data) + var v0, v1 Version + for _, v := range dataVersions { + if v1, err = VersionFromString(v); err != nil { + return + } + if v0.Less(v1) { + v0 = v1 + } + } + version = v0.String() + return +} + +func (r *ArclightInstaller) GetInstallerVersions() (map[string]ArclightRelease, error) { + data := make(map[string]ArclightRelease) + link := "https://api.github.com/repos/IzzelAliz/Arclight/releases" + var releases []ArclightRelease + err := DefaultHTTPClient.GetJson(link, &releases) + if err != nil { + return data, err + } + for i := 0; i < len(releases); i += 1 { + details := strings.Split(releases[i].Assets[0].AssetsName, "-") + //details should be ["arclight","forge","{VERSION}","{BUILDNUM}.jar"], so append value of index 2 + timeDetails := strings.Split(releases[i].PublishTime, "-") + //time should be "{YEAR}-{MONTH}-{DATE}T{CLOCK}}" + year, err := strconv.Atoi(timeDetails[0]) + if err != nil { + return data, err + } + month, err := strconv.Atoi(timeDetails[1]) + if err != nil { + return data, err + } + if year < 2024 || (year == 2024 && month < 2) { + releases[i].IsExpired = true + } else { + releases[i].IsExpired = false + } + if len(data[details[2]].Assets) == 0 { + data[details[2]] = releases[i] + } + //to get the newest builder for each version + } + return data, err +} + +func (r *ArclightInstaller) GetOnlyVersions(data map[string]ArclightRelease) (versions []string) { + for k := range data { + versions = append(versions, k) + } + return +} + +func (r *ArclightInstaller) Runbuilder(buildJar string, ExactDownloadName string, path string) (installed string, err error) { + currentDir, err := os.Getwd() + if err != nil { + return + } + serverDirectory := filepath.Join(currentDir, "server-"+ExactDownloadName[0:len(ExactDownloadName)-4]) + os.RemoveAll(serverDirectory) + err = os.MkdirAll(serverDirectory, os.ModePerm) + if err != nil { + return + } + err = os.Rename(buildJar, filepath.Join(serverDirectory, ExactDownloadName)) + if err != nil { + return + } + buildJar = filepath.Join(serverDirectory, ExactDownloadName) + loger.Info("Server jar file is successfully installed in path: " + buildJar) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + javapath, err := lookJavaPath() + if err != nil { + return + } + cmd := exec.CommandContext(ctx, javapath, "-jar", buildJar) + cmd.Dir = filepath.Join(path, "server-"+ExactDownloadName[0:len(ExactDownloadName)-4]) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stdout + loger.Infof("Running %q...", cmd.String()) + if err = cmd.Run(); err != nil { + return + } + installed = buildJar + "\n" + return +} diff --git a/changelogs/v1.2.4.MD b/changelogs/v1.2.4.MD new file mode 100644 index 0000000..8725826 --- /dev/null +++ b/changelogs/v1.2.4.MD @@ -0,0 +1,9 @@ + +#### Adds + +- Add the support of Papermc server downloading and available versions inquiry +- Add the support of Arclight server downloading and available versions inquiry + +#### Changes + +- Support new types of server: Papermc, Arclight diff --git a/cli/main.go b/cli/main.go index c5a6c27..94ec394 100644 --- a/cli/main.go +++ b/cli/main.go @@ -11,6 +11,36 @@ import ( installer "github.com/kmcsr/server-installer" ) +const UsageText = ` +minecraft_installer [...flags] +minecraft_installer [...flags] modpack +minecraft_installer [...flags] versions [] + +Example: + Install servers: + minecraft_installer -name minecraft_server -version 1.7.10 vanilla + Install minecraft 1.7.10 vanilla server into minecraft_server.jar + minecraft_installer -name minecraft_server -version 1.19.2 forge + Install minecraft 1.19.2 forge server into current directory and the executable is minecraft_server.sh + Hint: forge installer will make run scripts for the minecraft version that higher or equal than 1.17 + for version that less than 1.17, you still need to use 'java -jar' to run the server + minecraft_installer -name minecraft_server -version 1.19.2 -output server fabric + Install minecraft 1.19.2 fabric server into server/minecraft_server.jar + Install modpacks: + minecraft_installer -name modpack_server modpack /path/to/modrinth-modpack.mrpack + Install the modpack from local to the current directory + Hint: Only support modrinth modpack for now, curseforge is in progress + minecraft_installer -name modpack_server modpack 'https://cdn-raw.modrinth.com/data/sl6XzkCP/versions/i4agaPF2/Automation%20v3.3.mrpack' + Install the modpack from internet to the current directory + Hint: if you want to install modpack from the internet, + you must add the prefixs [https://, http://] + List Versions: + minecraft_installer versions + List all vanilla versions but without snapshots + minecraft_installer -version snapshot versions + List all vanilla versions include snapshots +` + var loger logger.Logger func initLogger() { @@ -101,6 +131,10 @@ func main() { installed, err = installer.DefaultFabricInstaller.InstallWithLoader(InstallPath, ExecutableName, minecraft, fabric) } else if quilt, ok := pack.Deps["quilt-loader"]; ok { installed, err = installer.DefaultQuiltInstaller.InstallWithLoader(InstallPath, ExecutableName, minecraft, quilt) + } else if papermc, ok := pack.Deps["papermc-loader"]; ok { + installed, err = installer.DefaultQuiltInstaller.InstallWithLoader(InstallPath, ExecutableName, minecraft, papermc) + } else if arclight, ok := pack.Deps["arclight-loader"]; ok { + installed, err = installer.DefaultQuiltInstaller.InstallWithLoader(InstallPath, ExecutableName, minecraft, arclight) } else if mok { installed, err = installer.VanillaIns.Install(InstallPath, ExecutableName, minecraft) } else { diff --git a/cli/usage.go b/cli/usage.go index 49cddd0..06ab7d0 100644 --- a/cli/usage.go +++ b/cli/usage.go @@ -1,31 +1 @@ package main - -const UsageText = ` -minecraft_installer [...flags] -minecraft_installer [...flags] modpack -minecraft_installer [...flags] versions [] - -Example: - Install servers: - minecraft_installer -name minecraft_server -version 1.7.10 vanilla - Install minecraft 1.7.10 vanilla server into minecraft_server.jar - minecraft_installer -name minecraft_server -version 1.19.2 forge - Install minecraft 1.19.2 forge server into current directory and the executable is minecraft_server.sh - Hint: forge installer will make run scripts for the minecraft version that higher or equal than 1.17 - for version that less than 1.17, you still need to use 'java -jar' to run the server - minecraft_installer -name minecraft_server -version 1.19.2 -output server fabric - Install minecraft 1.19.2 fabric server into server/minecraft_server.jar - Install modpacks: - minecraft_installer -name modpack_server modpack /path/to/modrinth-modpack.mrpack - Install the modpack from local to the current directory - Hint: Only support modrinth modpack for now, curseforge is in progress - minecraft_installer -name modpack_server modpack 'https://cdn-raw.modrinth.com/data/sl6XzkCP/versions/i4agaPF2/Automation%20v3.3.mrpack' - Install the modpack from internet to the current directory - Hint: if you want to install modpack from the internet, - you must add the prefixs [https://, http://] - List Versions: - minecraft_installer versions - List all vanilla versions but without snapshots - minecraft_installer -version snapshot versions - List all vanilla versions include snapshots -` diff --git a/httpclient.go b/httpclient.go index f2cc084..e757af1 100644 --- a/httpclient.go +++ b/httpclient.go @@ -34,7 +34,7 @@ func (c *HTTPClient) NewRequest(method string, url string, body io.Reader) (req } func (c *HTTPClient) Do(req *http.Request) (res *http.Response, err error) { - if ua := req.Header.Get("User-Agent"); ua == "" { + if _, ok := req.Header["User-Agent"]; !ok { req.Header.Set("User-Agent", c.UserAgent) } if res, err = c.Client.Do(req); err != nil { @@ -165,12 +165,49 @@ func (c *HTTPClient) DownloadTmp(url string, pattern string, mode os.FileMode, h func (c *HTTPClient) Download(url string, path string, mode os.FileMode, hashes StringMap, size int64, cb DlCallback) (err error) { var tmppath string tmppath, err = c.DownloadTmp(url, path+".*.downloading", mode, hashes, size, cb) - if err = renameIfNotExist(tmppath, path, 0644); err != nil { + if err = renameIfNotExist(tmppath, path); err != nil { return } return } +func (c *HTTPClient) DownloadDirect(url string, ExactDownloadeName string, cb DlCallback) (installed string, err error) { + resp, err := http.Head(url) + if err != nil { + return + } + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return + } + resp, err = http.DefaultClient.Do(req) + if err != nil { + return + } + defer resp.Body.Close() + filename := filepath.Base(url) + flags := os.O_CREATE | os.O_WRONLY + f, err := os.OpenFile(filename, flags, 0666) + if err != nil { + return + } + defer f.Close() + + buf := make([]byte, 16*1024) + _, err = io.CopyBuffer(f, resp.Body, buf) + if err != nil { + if err == io.EOF { + return + } + } + cpath, err := os.Getwd() + if err != nil { + return + } + installed = filepath.Join(cpath, ExactDownloadeName) + return +} + func (c *HTTPClient) Head(url string) (res *http.Response, err error) { var req *http.Request if req, err = c.NewRequest("HEAD", url, nil); err != nil { diff --git a/papermc_installer.go b/papermc_installer.go new file mode 100644 index 0000000..9da5367 --- /dev/null +++ b/papermc_installer.go @@ -0,0 +1,157 @@ +package installer + +import ( + "context" + "net/url" + "os" + "os/exec" + "path/filepath" + "strconv" +) + +type ( + PapermcInstaller struct { + PaperUrl string + } + + PapermcVersions struct { + Pid string `json:"project_id"` + Pname string `json:"project_name"` + VersionsGroup []string `json:"version_groups"` + PaperVersions []string `json:"versions"` + } + + PapermcBuilders struct { + Pid string `json:"project_id"` + Pname string `json:"project_name"` + TgVersion string `json:"version"` + Builders []int `json:"builds"` + } +) + +var DefaultPapermcInstaller = &PapermcInstaller{ + PaperUrl: "https://api.papermc.io/v2/projects/paper/versions", +} +var _ Installer = DefaultPapermcInstaller + +func init() { + Installers["papermc"] = DefaultPapermcInstaller +} + +func (r *PapermcInstaller) Install(path, name string, target string) (installed string, err error) { + return r.InstallWithLoader(path, name, target, "") +} + +func (r *PapermcInstaller) InstallWithLoader(path, name string, target string, loader string) (installed string, err error) { + if len(loader) == 0 { + allVersions, err := r.GetInstallerVersions() + if err != nil { + return "", err + } + if target == "latest" { + loader = allVersions[len(allVersions)-1] + goto DownloadPart + } + for i := 0; i < len(allVersions); i += 1 { + if allVersions[i] == target { + loader = target + goto DownloadPart + } + } + loger.Info("not find the suitable builder, the version should be included in the following list:") + for i := 0; i < len(allVersions); i += 1 { + loger.Info("versions:", allVersions[i]) + } + return "", &VersionNotFoundErr{target} + } +DownloadPart: + buildNumInt, err := r.GetBuildNumber(loader) + if err != nil { + return + } + buildNum := strconv.Itoa(buildNumInt) + ExactDownloadeName := "paper-" + loader + "-" + buildNum + ".jar" + PapermcInstallerUrl, err := url.JoinPath(r.PaperUrl, loader, "builds", buildNum, "downloads/"+ExactDownloadeName) + if err != nil { + return + } + loger.Infof("Getting papermc server installer %s at %q...", ExactDownloadeName, PapermcInstallerUrl) + var buildJar string + if buildJar, err = DefaultHTTPClient.DownloadDirect(PapermcInstallerUrl, ExactDownloadeName, downloadingCallback(PapermcInstallerUrl)); err != nil { + return + } + installed, err = r.Runbuilder(buildJar, ExactDownloadeName, path) + if err != nil { + loger.Info("an error occurred while running the server jar file, but you can still do that manually.") + loger.Error(err) + } + return +} + +func (r *PapermcInstaller) ListVersions(snapshot bool) (versions []string, err error) { + data, err := r.GetInstallerVersions() + if err != nil { + return + } + for _, v := range data { + versions = append(versions, v) + } + return +} + +func (r *PapermcInstaller) GetInstallerVersions() (data []string, err error) { + link := "https://api.papermc.io/v2/projects/paper" + var versions PapermcVersions + err = DefaultHTTPClient.GetJson(link, &versions) + if err != nil { + return + } + data = versions.PaperVersions + return data, err +} + +func (r *PapermcInstaller) GetBuildNumber(version string) (buildNum int, err error) { + buildUrl := "https://api.papermc.io/v2/projects/paper/versions/" + version + var builders PapermcBuilders + err = DefaultHTTPClient.GetJson(buildUrl, &builders) + if err != nil { + return + } + buildNum = builders.Builders[len(builders.Builders)-1] + return buildNum, err +} + +func (r *PapermcInstaller) Runbuilder(buildJar string, ExactDownloadName string, path string) (installed string, err error) { + currentDir, err := os.Getwd() + if err != nil { + return + } + serverDirectory := filepath.Join(currentDir, "server-"+ExactDownloadName[0:len(ExactDownloadName)-4]) + os.RemoveAll(serverDirectory) + err = os.MkdirAll(serverDirectory, os.ModePerm) + if err != nil { + return + } + err = os.Rename(buildJar, filepath.Join(serverDirectory, ExactDownloadName)) + if err != nil { + return + } + buildJar = filepath.Join(serverDirectory, ExactDownloadName) + loger.Info("Server jar file is successfully installed in path: " + buildJar) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + javapath, err := lookJavaPath() + if err != nil { + return + } + cmd := exec.CommandContext(ctx, javapath, "-jar", buildJar) + cmd.Dir = filepath.Join(path, "server-"+ExactDownloadName[0:len(ExactDownloadName)-4]) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stdout + loger.Infof("Running %q...", cmd.String()) + if err = cmd.Run(); err != nil { + return + } + installed = buildJar + "\n" + return +} From d8c50dcd9c3b6f27dcc32a84a04c7f86ab898576 Mon Sep 17 00:00:00 2001 From: Fellsoul Date: Thu, 7 Mar 2024 00:29:49 -0700 Subject: [PATCH 2/6] update supports for papermc and arclight --- utils.go | 37 ++++--------------------------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/utils.go b/utils.go index abf3d94..62f9900 100644 --- a/utils.go +++ b/utils.go @@ -21,28 +21,7 @@ var EmptyLinkArrayErr = errors.New("Link array is empty") type StringMap = map[string]string -func osCopy(src, dst string, mode os.FileMode) (err error) { - srcFd, err := os.Open(src) - if err != nil { - return - } - defer srcFd.Close() - dstFd, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) - if err != nil { - return - } - _, err = io.Copy(dstFd, srcFd) - if er := dstFd.Close(); err == nil && er != nil { - err = er - } - if err != nil { - os.Remove(dst) - return - } - return -} - -func renameIfNotExist(src, dst string, mode os.FileMode) (err error) { +func renameIfNotExist(src, dst string) (err error) { if _, e := os.Stat(dst); os.IsNotExist(e) { if err = os.MkdirAll(filepath.Dir(dst), 0755); err != nil { return @@ -56,16 +35,8 @@ func renameIfNotExist(src, dst string, mode os.FileMode) (err error) { } } if err = os.Rename(src, dst); err != nil { - if crossDevice(err) { - if err = osCopy(src, dst, mode); err != nil { - return - } - os.Remove(src) - return - } return } - os.Chmod(dst, mode) return } @@ -80,7 +51,7 @@ func safeDownload(reader io.Reader, path string) (err error) { if err != nil { return } - if err = renameIfNotExist(fd.Name(), path, 0644); err != nil { + if err = renameIfNotExist(fd.Name(), path); err != nil { return } return nil @@ -160,12 +131,12 @@ func downloadAnyAndCheckHashes(links []string, path string, hashes StringMap, si } for _, l := range links { var tmp string - if tmp, err = DefaultHTTPClient.DownloadTmp(l, "*.downloading", 0644, hashes, size, + if tmp, err = DefaultHTTPClient.DownloadTmp(l, "downloading_", 0644, hashes, size, downloadingCallback(l)); err != nil { continue } defer os.Remove(tmp) - if err = renameIfNotExist(tmp, path, 0644); err != nil { + if err = renameIfNotExist(tmp, path); err != nil { return } break From f7e297f72863c940bf7a4024ffa43fe3d71afae2 Mon Sep 17 00:00:00 2001 From: Fellsoul Date: Thu, 7 Mar 2024 00:38:57 -0700 Subject: [PATCH 3/6] add support for papermc and arclight --- httpclient.go | 50 +++++++++++++++++++++++++------------------------- utils.go | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 29 deletions(-) diff --git a/httpclient.go b/httpclient.go index e757af1..9e28ed2 100644 --- a/httpclient.go +++ b/httpclient.go @@ -34,7 +34,7 @@ func (c *HTTPClient) NewRequest(method string, url string, body io.Reader) (req } func (c *HTTPClient) Do(req *http.Request) (res *http.Response, err error) { - if _, ok := req.Header["User-Agent"]; !ok { + if ua := req.Header.Get("User-Agent"); ua == "" { req.Header.Set("User-Agent", c.UserAgent) } if res, err = c.Client.Do(req); err != nil { @@ -165,12 +165,35 @@ func (c *HTTPClient) DownloadTmp(url string, pattern string, mode os.FileMode, h func (c *HTTPClient) Download(url string, path string, mode os.FileMode, hashes StringMap, size int64, cb DlCallback) (err error) { var tmppath string tmppath, err = c.DownloadTmp(url, path+".*.downloading", mode, hashes, size, cb) - if err = renameIfNotExist(tmppath, path); err != nil { + if err = renameIfNotExist(tmppath, path, 0644); err != nil { return } return } +func (c *HTTPClient) Head(url string) (res *http.Response, err error) { + var req *http.Request + if req, err = c.NewRequest("HEAD", url, nil); err != nil { + return + } + return c.Do(req) +} + +func (c *HTTPClient) Post(url string, contentType string, body io.Reader) (res *http.Response, err error) { + var req *http.Request + if req, err = c.NewRequest("POST", url, body); err != nil { + return + } + req.Header.Set("Content-Type", contentType) + return c.Do(req) +} + +func (c *HTTPClient) PostForm(url string, form url.Values) (res *http.Response, err error) { + formStr := form.Encode() + return c.Post(url, "application/x-www-form-urlencoded", + strings.NewReader(formStr)) +} + func (c *HTTPClient) DownloadDirect(url string, ExactDownloadeName string, cb DlCallback) (installed string, err error) { resp, err := http.Head(url) if err != nil { @@ -207,26 +230,3 @@ func (c *HTTPClient) DownloadDirect(url string, ExactDownloadeName string, cb Dl installed = filepath.Join(cpath, ExactDownloadeName) return } - -func (c *HTTPClient) Head(url string) (res *http.Response, err error) { - var req *http.Request - if req, err = c.NewRequest("HEAD", url, nil); err != nil { - return - } - return c.Do(req) -} - -func (c *HTTPClient) Post(url string, contentType string, body io.Reader) (res *http.Response, err error) { - var req *http.Request - if req, err = c.NewRequest("POST", url, body); err != nil { - return - } - req.Header.Set("Content-Type", contentType) - return c.Do(req) -} - -func (c *HTTPClient) PostForm(url string, form url.Values) (res *http.Response, err error) { - formStr := form.Encode() - return c.Post(url, "application/x-www-form-urlencoded", - strings.NewReader(formStr)) -} diff --git a/utils.go b/utils.go index 62f9900..abf3d94 100644 --- a/utils.go +++ b/utils.go @@ -21,7 +21,28 @@ var EmptyLinkArrayErr = errors.New("Link array is empty") type StringMap = map[string]string -func renameIfNotExist(src, dst string) (err error) { +func osCopy(src, dst string, mode os.FileMode) (err error) { + srcFd, err := os.Open(src) + if err != nil { + return + } + defer srcFd.Close() + dstFd, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) + if err != nil { + return + } + _, err = io.Copy(dstFd, srcFd) + if er := dstFd.Close(); err == nil && er != nil { + err = er + } + if err != nil { + os.Remove(dst) + return + } + return +} + +func renameIfNotExist(src, dst string, mode os.FileMode) (err error) { if _, e := os.Stat(dst); os.IsNotExist(e) { if err = os.MkdirAll(filepath.Dir(dst), 0755); err != nil { return @@ -35,8 +56,16 @@ func renameIfNotExist(src, dst string) (err error) { } } if err = os.Rename(src, dst); err != nil { + if crossDevice(err) { + if err = osCopy(src, dst, mode); err != nil { + return + } + os.Remove(src) + return + } return } + os.Chmod(dst, mode) return } @@ -51,7 +80,7 @@ func safeDownload(reader io.Reader, path string) (err error) { if err != nil { return } - if err = renameIfNotExist(fd.Name(), path); err != nil { + if err = renameIfNotExist(fd.Name(), path, 0644); err != nil { return } return nil @@ -131,12 +160,12 @@ func downloadAnyAndCheckHashes(links []string, path string, hashes StringMap, si } for _, l := range links { var tmp string - if tmp, err = DefaultHTTPClient.DownloadTmp(l, "downloading_", 0644, hashes, size, + if tmp, err = DefaultHTTPClient.DownloadTmp(l, "*.downloading", 0644, hashes, size, downloadingCallback(l)); err != nil { continue } defer os.Remove(tmp) - if err = renameIfNotExist(tmp, path); err != nil { + if err = renameIfNotExist(tmp, path, 0644); err != nil { return } break From 4e38ddf4944f576de5cc6196d856ab69856016d5 Mon Sep 17 00:00:00 2001 From: zyxkad Date: Thu, 7 Mar 2024 15:22:35 -0700 Subject: [PATCH 4/6] fix go vet error --- cli/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/main.go b/cli/main.go index 94ec394..f369042 100644 --- a/cli/main.go +++ b/cli/main.go @@ -3,6 +3,7 @@ package main import ( "flag" "fmt" + "io" "net/url" "os" @@ -73,7 +74,7 @@ func parseArgs() { flag.Usage = func() { out := flag.CommandLine.Output() fmt.Fprintf(out, "Usage of %s (%s):\n", os.Args[0], installer.PkgVersion) - fmt.Fprint(out, UsageText) + io.WriteString(out, UsageText) fmt.Fprintln(out, "Flags:") fmt.Fprintln(out, " -h, -help") fmt.Fprintln(out, " Show this help page") From 197373b52ca82ba37d2eb8c0e3326d6ec113de5c Mon Sep 17 00:00:00 2001 From: Fellsoul <161669890+Fellsoul@users.noreply.github.com> Date: Fri, 29 Mar 2024 14:47:57 -0600 Subject: [PATCH 5/6] Update README.MD --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 90879aa..c7657f3 100644 --- a/README.MD +++ b/README.MD @@ -83,7 +83,7 @@ minecraft_installer -name minecraft_server -version 1.19.2 -server fabric -path ``` ```sh -# Install papermc 1.14.4 server into {PATH}/server-1.14.4-{BUILDNUM}/{PAPERMC-INSTALLED-NAME}.jar +# Install papermc 1.14.4 server into server/minecraft_server.jar minecraft_installer -name minecraft_server -version 1.14.4 -server papermc # papermc and arclight installation will automatically generate different directories for different builder. ``` From 850ed8159e0fd9828a1e1bb60201d69325c77711 Mon Sep 17 00:00:00 2001 From: Fellsoul <161669890+Fellsoul@users.noreply.github.com> Date: Fri, 29 Mar 2024 14:49:19 -0600 Subject: [PATCH 6/6] Update README_zh.MD --- README_zh.MD | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README_zh.MD b/README_zh.MD index 4eb6491..e629707 100644 --- a/README_zh.MD +++ b/README_zh.MD @@ -79,9 +79,9 @@ minecraft_installer -name minecraft_server -version 1.19.2 -server fabric -path ``` ```sh -# 将 papermc 1.14.4 服务端下载到 {PATH}/server-1.14.4-{BUILDNUM}/{PAPERMC-INSTALLED-NAME}.jar +# 将 papermc 1.14.4 服务端下载到 server/minecraft_server.jar minecraft_installer -name minecraft_server -version 1.14.4 -server papermc -# 注:papermc以及arclight服务器因build区别会区分到不同文件夹中,执行中会自动建立此文件夹,更加方便识别 +# 注:papermc以及arclight服务器因build区别会区分到不同文件夹中,执行中会自动建立并命名该server文件夹,更加方便识别 ``` ### 安装整合包 @@ -111,4 +111,4 @@ minecraft_installer -version snapshot versions ## 计划中 - [ ] 从modrinth搜寻整合包 -- [ ] 代理相关 \ No newline at end of file +- [ ] 代理相关