mirror of
				https://github.com/packwiz/packwiz.git
				synced 2025-10-31 02:34:31 +01:00 
			
		
		
		
	Add cf detect command (experimental but should mostly work)
This commit is contained in:
		
							
								
								
									
										148
									
								
								curseforge/detect.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								curseforge/detect.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| package curseforge | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/aviddiviner/go-murmur" | ||||
| 	"github.com/comp500/packwiz/core" | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // TODO: make all of this less bad and hardcoded | ||||
|  | ||||
| // detectCmd represents the detect command | ||||
| var detectCmd = &cobra.Command{ | ||||
| 	Use:   "detect", | ||||
| 	Short: "Detect .jar files in the mods folder (experimental)", | ||||
| 	Args:  cobra.NoArgs, | ||||
| 	Run: func(cmd *cobra.Command, args []string) { | ||||
| 		fmt.Println("Loading modpack...") | ||||
| 		pack, err := core.LoadPack() | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			os.Exit(1) | ||||
| 		} | ||||
| 		index, err := pack.LoadIndex() | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			os.Exit(1) | ||||
| 		} | ||||
|  | ||||
| 		// Walk files in the mods folder | ||||
| 		var hashes []int | ||||
| 		modPaths := make(map[int]string) | ||||
| 		err = filepath.Walk("mods", func(path string, info os.FileInfo, err error) error { | ||||
| 			if info.IsDir() { | ||||
| 				return nil | ||||
| 			} | ||||
| 			if !strings.HasSuffix(path, ".jar") { | ||||
| 				// TODO: make this less bad | ||||
| 				return nil | ||||
| 			} | ||||
| 			fmt.Println("Hashing " + path) | ||||
| 			bytes, err := ioutil.ReadFile(path) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			hash := getByteArrayHash(bytes) | ||||
| 			hashes = append(hashes, int(hash)) | ||||
| 			modPaths[int(hash)] = path | ||||
| 			return nil | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			os.Exit(1) | ||||
| 		} | ||||
| 		fmt.Printf("Found %d files, submitting...\n", len(hashes)) | ||||
|  | ||||
| 		res, err := getFingerprintInfo(hashes) | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		fmt.Printf("Successfully matched %d files\n", len(res.ExactFingerprints)) | ||||
| 		if len(res.PartialMatches) > 0 { | ||||
| 			fmt.Println("The following fingerprints were partial and I don't know what to do!!!") | ||||
| 			for _, v := range res.PartialMatches { | ||||
| 				fmt.Printf("%s (%d)", modPaths[v], v) | ||||
| 			} | ||||
| 		} | ||||
| 		if len(res.UnmatchedFingerprints) > 0 { | ||||
| 			fmt.Printf("Failed to match the following %d files:\n", len(res.UnmatchedFingerprints)) | ||||
| 			for _, v := range res.UnmatchedFingerprints { | ||||
| 				fmt.Printf("%s (%d)\n", modPaths[v], v) | ||||
| 			} | ||||
| 		} | ||||
| 		fmt.Println("Installing...") | ||||
| 		for _, v := range res.ExactMatches { | ||||
| 			modInfoData, err := getModInfo(v.ID) | ||||
| 			if err != nil { | ||||
| 				fmt.Println(err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			err = createModFile(modInfoData, v.File, &index) | ||||
| 			if err != nil { | ||||
| 				fmt.Println(err) | ||||
| 				os.Exit(1) | ||||
| 			} | ||||
|  | ||||
| 			path, ok := modPaths[v.File.Fingerprint] | ||||
| 			if ok { | ||||
| 				err = os.Remove(path) | ||||
| 				if err != nil { | ||||
| 					fmt.Println(err) | ||||
| 					os.Exit(1) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		fmt.Println("Installation done") | ||||
|  | ||||
| 		err = index.Refresh() | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			return | ||||
| 		} | ||||
| 		err = index.Write() | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			return | ||||
| 		} | ||||
| 		err = pack.UpdateIndexHash() | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			return | ||||
| 		} | ||||
| 		err = pack.Write() | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			return | ||||
| 		} | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	curseforgeCmd.AddCommand(detectCmd) | ||||
| } | ||||
|  | ||||
| func getByteArrayHash(bytes []byte) uint64 { | ||||
| 	return uint64(murmur.MurmurHash2(computeNormalizedArray(bytes), 1)) | ||||
| } | ||||
|  | ||||
| func computeNormalizedArray(bytes []byte) []byte { | ||||
| 	var newArray []byte | ||||
| 	for _, b := range bytes { | ||||
| 		if !isWhitespaceCharacter(b) { | ||||
| 			newArray = append(newArray, b) | ||||
| 		} | ||||
| 	} | ||||
| 	return newArray | ||||
| } | ||||
|  | ||||
| func isWhitespaceCharacter(b byte) bool { | ||||
| 	return b == 9 || b == 10 || b == 13 || b == 32 | ||||
| } | ||||
| @@ -291,3 +291,49 @@ func getSearch(searchText string, gameVersion string) ([]modInfo, error) { | ||||
|  | ||||
| 	return infoRes, nil | ||||
| } | ||||
|  | ||||
| type addonFingerprintResponse struct { | ||||
| 	IsCacheBuilt bool `json:"isCacheBuilt"` | ||||
| 	ExactMatches []struct { | ||||
| 		ID          int           `json:"id"` | ||||
| 		File        modFileInfo   `json:"file"` | ||||
| 		LatestFiles []modFileInfo `json:"latestFiles"` | ||||
| 	} `json:"exactMatches"` | ||||
| 	ExactFingerprints        []int    `json:"exactFingerprints"` | ||||
| 	PartialMatches           []int    `json:"partialMatches"` | ||||
| 	PartialMatchFingerprints struct{} `json:"partialMatchFingerprints"` | ||||
| 	InstalledFingerprints    []int    `json:"installedFingerprints"` | ||||
| 	UnmatchedFingerprints    []int    `json:"unmatchedFingerprints"` | ||||
| } | ||||
|  | ||||
| func getFingerprintInfo(hashes []int) (addonFingerprintResponse, error) { | ||||
| 	var infoRes addonFingerprintResponse | ||||
| 	client := &http.Client{} | ||||
|  | ||||
| 	hashesData, err := json.Marshal(hashes) | ||||
| 	if err != nil { | ||||
| 		return addonFingerprintResponse{}, err | ||||
| 	} | ||||
|  | ||||
| 	req, err := http.NewRequest("POST", "https://addons-ecs.forgesvc.net/api/v2/fingerprint", bytes.NewBuffer(hashesData)) | ||||
| 	if err != nil { | ||||
| 		return addonFingerprintResponse{}, err | ||||
| 	} | ||||
|  | ||||
| 	// TODO: make this configurable application-wide | ||||
| 	req.Header.Set("User-Agent", "comp500/packwiz client") | ||||
| 	req.Header.Set("Accept", "application/json") | ||||
| 	req.Header.Set("Content-Type", "application/json") | ||||
|  | ||||
| 	resp, err := client.Do(req) | ||||
| 	if err != nil { | ||||
| 		return addonFingerprintResponse{}, err | ||||
| 	} | ||||
|  | ||||
| 	err = json.NewDecoder(resp.Body).Decode(&infoRes) | ||||
| 	if err != nil && err != io.EOF { | ||||
| 		return addonFingerprintResponse{}, err | ||||
| 	} | ||||
|  | ||||
| 	return infoRes, nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @@ -2,6 +2,7 @@ module github.com/comp500/packwiz | ||||
|  | ||||
| require ( | ||||
| 	github.com/BurntSushi/toml v0.3.1 | ||||
| 	github.com/aviddiviner/go-murmur v0.0.0-20150519214947-b9740d71e571 | ||||
| 	github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect | ||||
| 	github.com/daviddengcn/go-colortext v0.0.0-20180409174941-186a3d44e920 // indirect | ||||
| 	github.com/denormal/go-gitignore v0.0.0-20180930084346-ae8ad1d07817 | ||||
|   | ||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| @@ -7,6 +7,8 @@ github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmx | ||||
| github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= | ||||
| github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||||
| github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= | ||||
| github.com/aviddiviner/go-murmur v0.0.0-20150519214947-b9740d71e571 h1:seCdAEDyB0Hti/v1VajB7pAOIk9zmz/0/KE0D0oFqnc= | ||||
| github.com/aviddiviner/go-murmur v0.0.0-20150519214947-b9740d71e571/go.mod h1:VzSzsYCY3W9xWYWD8T2GLDidWTe5rTZv+UdDMGhLfjg= | ||||
| github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= | ||||
| github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= | ||||
| github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= | ||||
|   | ||||
		Reference in New Issue
	
	Block a user