2 Commits
1.5.0 ... 1.6.0

Author SHA1 Message Date
a4de02bc18 UPDATE: Add mutithread download 2025-08-27 08:27:02 +07:00
a021658fa9 FIX: Fix bug check diff type 2025-08-26 09:58:30 +07:00
12 changed files with 182 additions and 246 deletions

View File

@@ -21,7 +21,7 @@ export function useGlobalEvents({
setProgressUpdate: (v: number) => void; setProgressUpdate: (v: number) => void;
setMaxProgressUpdate: (v: number) => void; setMaxProgressUpdate: (v: number) => void;
setProgressDownload: (v: number) => void; setProgressDownload: (v: number) => void;
setDownloadSpeed: (v: number) => void; setDownloadSpeed: (v: string) => void;
setMessageUpdate: (v: string) => void; setMessageUpdate: (v: string) => void;
setStageType: (v: string) => void, setStageType: (v: string) => void,
}) { }) {
@@ -33,7 +33,7 @@ export function useGlobalEvents({
const onDownload = (event: any) => { const onDownload = (event: any) => {
const { percent, speed } = event.data[0]; const { percent, speed } = event.data[0];
setProgressDownload(Number(percent)); setProgressDownload(Number(percent));
setDownloadSpeed(Number(speed)); setDownloadSpeed(speed);
}; };
const onUpdateProgress = (event: any) => { const onUpdateProgress = (event: any) => {

View File

@@ -87,19 +87,6 @@ export default function DiffPage() {
setDiffDir('') setDiffDir('')
return return
} }
const [isOk, validType, errorType] = await DiffService.CheckTypeHDiff(basePath)
if (!isOk) {
toast.error(errorType)
setDiffCheckResult('error')
setDiffDir('')
return
}
if (validType == "") {
toast.error('Not valid file type')
setDiffCheckResult('error')
setDiffDir('')
return
}
setDiffDir(basePath) setDiffDir(basePath)
setDiffCheckResult('success') setDiffCheckResult('success')
} else { } else {
@@ -134,8 +121,6 @@ export default function DiffPage() {
} }
setProgressUpdate(1) setProgressUpdate(1)
if (validType === 'hdiffmap.json') { if (validType === 'hdiffmap.json') {
setStageType('Version Validate') setStageType('Version Validate')
setProgressUpdate(0) setProgressUpdate(0)

View File

@@ -405,7 +405,7 @@ export default function LauncherPage() {
<div className="flex justify-center items-center text-sm text-white/80"> <div className="flex justify-center items-center text-sm text-white/80">
<span>{downloadType}</span> <span>{downloadType}</span>
<div className="flex items-center gap-4 ml-4"> <div className="flex items-center gap-4 ml-4">
<span className="text-cyan-400 font-semibold">{downloadSpeed.toFixed(1)} MB/s</span> <span className="text-cyan-400 font-semibold">{downloadSpeed}</span>
<span className="text-white font-bold">{progressDownload.toFixed(1)}%</span> <span className="text-white font-bold">{progressDownload.toFixed(1)}%</span>
</div> </div>
</div> </div>
@@ -414,7 +414,7 @@ export default function LauncherPage() {
className="h-full bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full" className="h-full bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full"
initial={{ width: 0 }} initial={{ width: 0 }}
animate={{ width: `${progressDownload}%` }} animate={{ width: `${progressDownload}%` }}
transition={{ duration: 0.3 }} transition={{ type: "tween", ease: "linear", duration: 0.03 }}
/> />
</div> </div>
<div className="text-center text-xs text-white/60"> <div className="text-center text-xs text-white/60">

View File

@@ -11,7 +11,7 @@ interface LauncherState {
isLoading: boolean; isLoading: boolean;
gameRunning: boolean; gameRunning: boolean;
progressDownload: number; progressDownload: number;
downloadSpeed: number; downloadSpeed: string;
launcherVersion: string; launcherVersion: string;
updateData: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>; updateData: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>;
setDownloadType: (value: string) => void; setDownloadType: (value: string) => void;
@@ -24,7 +24,7 @@ interface LauncherState {
setGameRunning: (value: boolean) => void; setGameRunning: (value: boolean) => void;
setProgressDownload: (value: number) => void; setProgressDownload: (value: number) => void;
setLauncherVersion: (value: string) => void; setLauncherVersion: (value: string) => void;
setDownloadSpeed: (value: number) => void; setDownloadSpeed: (value: string) => void;
setUpdateData: (value: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>) => void; setUpdateData: (value: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>) => void;
} }
@@ -38,7 +38,7 @@ const useLauncherStore = create<LauncherState>((set, get) => ({
proxyRunning: false, proxyRunning: false,
gameRunning: false, gameRunning: false,
progressDownload: 0, progressDownload: 0,
downloadSpeed: 0, downloadSpeed: "",
launcherVersion: "", launcherVersion: "",
updateData: { updateData: {
server: { isUpdate: false, isExists: false, version: "" }, server: { isUpdate: false, isExists: false, version: "" },
@@ -55,7 +55,7 @@ const useLauncherStore = create<LauncherState>((set, get) => ({
setGameRunning: (value: boolean) => set({ gameRunning: value }), setGameRunning: (value: boolean) => set({ gameRunning: value }),
setProgressDownload: (value: number) => set({ progressDownload: value }), setProgressDownload: (value: number) => set({ progressDownload: value }),
setLauncherVersion: (value: string) => set({ launcherVersion: value }), setLauncherVersion: (value: string) => set({ launcherVersion: value }),
setDownloadSpeed: (value: number) => set({ downloadSpeed: value }), setDownloadSpeed: (value: string) => set({ downloadSpeed: value }),
setUpdateData: (value: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>) => set({ updateData: value }), setUpdateData: (value: Record<'server' | 'proxy' | 'launcher', { isUpdate: boolean, isExists: boolean, version: string }>) => set({ updateData: value }),
})); }));

2
go.mod
View File

@@ -10,6 +10,7 @@ require (
require ( require (
aead.dev/minisign v0.3.0 // indirect aead.dev/minisign v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/text v0.28.0 // indirect golang.org/x/text v0.28.0 // indirect
) )
@@ -49,7 +50,6 @@ require (
github.com/wailsapp/go-webview2 v1.0.21 // indirect github.com/wailsapp/go-webview2 v1.0.21 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
golang.org/x/net v0.43.0 // indirect golang.org/x/net v0.43.0 // indirect
google.golang.org/protobuf v1.36.8 google.golang.org/protobuf v1.36.8

View File

@@ -18,16 +18,16 @@ import (
type DiffService struct{} type DiffService struct{}
func (h *DiffService) CheckTypeHDiff(patchPath string) (bool, string, string) { func (h *DiffService) CheckTypeHDiff(patchPath string) (bool, string, string) {
if ok, err := sevenzip.IsFileIn7z(patchPath, "manifest"); err == nil && ok {
return true, "manifest", ""
}
if ok, err := sevenzip.IsFileIn7z(patchPath, "hdifffiles.txt"); err == nil && ok { if ok, err := sevenzip.IsFileIn7z(patchPath, "hdifffiles.txt"); err == nil && ok {
return true, "hdifffiles.txt", "" return true, "hdifffiles.txt", ""
} }
if ok, err := sevenzip.IsFileIn7z(patchPath, "hdiffmap.json"); err == nil && ok { if ok, err := sevenzip.IsFileIn7z(patchPath, "hdiffmap.json"); err == nil && ok {
return true, "hdiffmap.json", "" return true, "hdiffmap.json", ""
} }
if ok, err := sevenzip.IsFileIn7z(patchPath, "manifest"); err == nil && ok {
return true, "manifest", ""
}
return false, "", "not found hdifffiles.txt or hdiffmap.json" return false, "", "not found hdifffiles.txt or hdiffmap.json"
} }
@@ -155,9 +155,12 @@ func (h *DiffService) DeleteFiles(gamePath string) (bool, string) {
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
file.Close()
return false, "no delete files exist" return false, "no delete files exist"
} }
file.Close()
for i, file := range deleteFiles { for i, file := range deleteFiles {
os.Remove(filepath.Join(gamePath, file)) os.Remove(filepath.Join(gamePath, file))
application.Get().Event.Emit("diff:progress", map[string]int{"progress": i, "maxProgress": len(deleteFiles)}) application.Get().Event.Emit("diff:progress", map[string]int{"progress": i, "maxProgress": len(deleteFiles)})

View File

@@ -38,56 +38,20 @@ func (g *GitService) GetLatestLauncherVersion() (bool, string, string) {
func (g *GitService) UpdateLauncherProgress(version string) (bool, string) { func (g *GitService) UpdateLauncherProgress(version string) (bool, string) {
resp, err := http.Get(constant.LauncherGitUrl) asset, ok := g.getReleaseAsset(version, constant.LauncherGitUrl, constant.LauncherFile)
if err != nil { if !ok {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []*models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, err.Error()
}
if len(releases) == 0 {
return false, "no releases found"
}
var releaseData *models.ReleaseType
for _, release := range releases {
if release.TagName == version {
releaseData = release
break
}
}
if releaseData == nil || releaseData.TagName == "" {
return false, "no release found" return false, "no release found"
} }
var assetWin models.AssetType resp, err := http.Get(asset.BrowserDownloadURL)
for _, asset := range releaseData.Assets {
if asset.Name == constant.LauncherFile {
assetWin = asset
break
}
}
if assetWin.Name == "" {
return false, "no assets found"
}
resp, err = http.Get(assetWin.BrowserDownloadURL)
if err != nil { if err != nil {
return false, err.Error() return false, err.Error()
} }
defer resp.Body.Close() defer resp.Body.Close()
err = selfupdate.Apply(resp.Body, selfupdate.Options{}) err = selfupdate.Apply(resp.Body, selfupdate.Options{})
if err != nil { if err != nil {
return false, err.Error() return false, err.Error()
} }
return true, "" return true, ""
} }

View File

@@ -1,6 +1,7 @@
package gitService package gitService
import ( import (
"encoding/json"
"firefly-launcher/pkg/constant" "firefly-launcher/pkg/constant"
"firefly-launcher/pkg/models" "firefly-launcher/pkg/models"
"fmt" "fmt"
@@ -8,7 +9,8 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"encoding/json" "time"
"github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/application"
) )
@@ -35,72 +37,35 @@ func (g *GitService) GetLatestProxyVersion() (bool, string, string) {
} }
func (g *GitService) DownloadProxyProgress(version string) (bool, string) { func (g *GitService) DownloadProxyProgress(version string) (bool, string) {
resp, err := http.Get(constant.ProxyGitUrl) asset, ok := g.getReleaseAsset(version, constant.ProxyGitUrl, constant.ProxyZipFile)
if err != nil { if !ok {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []*models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, err.Error()
}
if len(releases) == 0 {
return false, "no releases found"
}
var releaseData *models.ReleaseType
for _, release := range releases {
if release.TagName == version {
releaseData = release
break
}
}
if releaseData == nil || releaseData.TagName == "" {
return false, "no release found" return false, "no release found"
} }
var assetWin models.AssetType if err := os.MkdirAll(constant.ProxyStorageUrl, 0755); err != nil {
for _, asset := range releaseData.Assets { return false, err.Error()
if asset.Name == constant.ProxyZipFile {
assetWin = asset
break
}
} }
if assetWin.Name == "" { saveFile := filepath.Join(constant.ProxyStorageUrl, asset.Name)
return false, "no assets found" tmpPath, err := g.downloadFileParallel(saveFile, asset.BrowserDownloadURL, 4, func(percent float64, speed string) {
} application.Get().Event.Emit("download:proxy", map[string]interface{}{
"percent": fmt.Sprintf("%.2f", percent),
if err := os.Mkdir(constant.ProxyStorageUrl, 0755); err != nil { "speed": speed,
if !os.IsExist(err) { })
return false, err.Error() })
}
}
saveFile := filepath.Join(constant.ProxyStorageUrl, assetWin.Name)
resp, err = http.Get(assetWin.BrowserDownloadURL)
if err != nil { if err != nil {
return false, err.Error() return false, err.Error()
} }
defer resp.Body.Close() for i := 0; i < 3; i++ {
if err := os.Rename(tmpPath, saveFile); err == nil {
DownloadFile(saveFile, assetWin.BrowserDownloadURL, func(percent float64, speed float64) { return true, ""
application.Get().Event.Emit("download:proxy", map[string]interface{}{ }
"percent": fmt.Sprintf("%.2f", percent), time.Sleep(300 * time.Millisecond)
"speed": fmt.Sprintf("%.2f", speed), }
}) return false, "failed to rename tmp file after retries"
})
return true, ""
} }
func (g *GitService) UnzipProxy() { func (g *GitService) UnzipProxy() {
unzipParallel(filepath.Join(constant.ProxyStorageUrl, constant.ProxyZipFile), constant.ProxyStorageUrl) g.unzipParallel(filepath.Join(constant.ProxyStorageUrl, constant.ProxyZipFile), constant.ProxyStorageUrl)
os.Remove(filepath.Join(constant.ProxyStorageUrl, constant.ProxyZipFile)) os.Remove(filepath.Join(constant.ProxyStorageUrl, constant.ProxyZipFile))
} }

View File

@@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"time"
"github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/application"
) )
@@ -36,72 +37,35 @@ func (g *GitService) GetLatestServerVersion() (bool, string, string) {
} }
func (g *GitService) DownloadServerProgress(version string) (bool, string) { func (g *GitService) DownloadServerProgress(version string) (bool, string) {
resp, err := http.Get(constant.ServerGitUrl) asset, ok := g.getReleaseAsset(version, constant.ServerGitUrl, constant.ServerZipFile)
if err != nil { if !ok {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []*models.ReleaseType
err = json.Unmarshal(body, &releases)
if err != nil {
return false, err.Error()
}
if len(releases) == 0 {
return false, "no releases found"
}
var releaseData *models.ReleaseType
for _, release := range releases {
if release.TagName == version {
releaseData = release
break
}
}
if releaseData == nil || releaseData.TagName == "" {
return false, "no release found" return false, "no release found"
} }
var assetWin models.AssetType if err := os.MkdirAll(constant.ServerStorageUrl, 0755); err != nil {
for _, asset := range releaseData.Assets { return false, err.Error()
if asset.Name == constant.ServerZipFile {
assetWin = asset
break
}
} }
if assetWin.Name == "" { saveFile := filepath.Join(constant.ServerStorageUrl, asset.Name)
return false, "no assets found" tmpPath, err := g.downloadFileParallel(saveFile, asset.BrowserDownloadURL, 4, func(percent float64, speed string) {
} application.Get().Event.Emit("download:server", map[string]interface{}{
"percent": fmt.Sprintf("%.2f", percent),
if err := os.Mkdir(constant.ServerStorageUrl, 0755); err != nil { "speed": speed,
if !os.IsExist(err) { })
return false, err.Error() })
}
}
saveFile := filepath.Join(constant.ServerStorageUrl, assetWin.Name)
resp, err = http.Get(assetWin.BrowserDownloadURL)
if err != nil { if err != nil {
return false, err.Error() return false, err.Error()
} }
defer resp.Body.Close() for i := 0; i < 3; i++ {
if err := os.Rename(tmpPath, saveFile); err == nil {
DownloadFile(saveFile, assetWin.BrowserDownloadURL, func(percent float64, speed float64) { return true, ""
application.Get().Event.Emit("download:server", map[string]interface{}{ }
"percent": fmt.Sprintf("%.2f", percent), time.Sleep(300 * time.Millisecond)
"speed": fmt.Sprintf("%.2f", speed), }
}) return false, "failed to rename tmp file after retries"
})
return true, ""
} }
func (g *GitService) UnzipServer() { func (g *GitService) UnzipServer() {
unzipParallel(filepath.Join(constant.ServerStorageUrl, constant.ServerZipFile), constant.ServerStorageUrl) g.unzipParallel(filepath.Join(constant.ServerStorageUrl, constant.ServerZipFile), constant.ServerStorageUrl)
os.Remove(filepath.Join(constant.ServerStorageUrl, constant.ServerZipFile)) os.Remove(filepath.Join(constant.ServerStorageUrl, constant.ServerZipFile))
} }

View File

@@ -2,6 +2,8 @@ package gitService
import ( import (
"archive/zip" "archive/zip"
"encoding/json"
"firefly-launcher/pkg/models"
"fmt" "fmt"
"io" "io"
"math" "math"
@@ -13,39 +15,37 @@ import (
"time" "time"
) )
func humanFormat(bytes int64) string { func HumanFormat(bytes float64) string {
n := float64(bytes)
for _, unit := range []string{"", "Ki", "Mi", "Gi"} { for _, unit := range []string{"", "Ki", "Mi", "Gi"} {
if math.Abs(n) < 1024.0 { if math.Abs(bytes) < 1024.0 {
return fmt.Sprintf("%3.1f%sB", n, unit) return fmt.Sprintf("%3.1f%sB", bytes, unit)
} }
n /= 1024.0 bytes /= 1024.0
} }
return fmt.Sprintf("%.1fTiB", n) return fmt.Sprintf("%.1fTiB", bytes)
} }
type WriteCounter struct { type WriteCounter struct {
Total uint64 Total uint64
StartTime time.Time StartTime time.Time
OnEmit func(percent float64, speedMBps float64) OnEmit func(percent float64, speed string)
TotalSize int64 TotalSize int64
lastLoggedPercent int mu sync.Mutex
} }
func NewWriteCounter(total int64, onEmit func(percent float64, speedMBps float64)) *WriteCounter { func NewWriteCounter(total int64, onEmit func(percent float64, speed string)) *WriteCounter {
return &WriteCounter{ return &WriteCounter{
StartTime: time.Now(), StartTime: time.Now(),
TotalSize: total, TotalSize: total,
lastLoggedPercent: -1, OnEmit: onEmit,
OnEmit: onEmit,
} }
} }
func (wc *WriteCounter) Write(p []byte) (int, error) { func (wc *WriteCounter) Add(n int) {
n := len(p) wc.mu.Lock()
defer wc.mu.Unlock()
wc.Total += uint64(n) wc.Total += uint64(n)
wc.PrintProgress() wc.PrintProgress()
return n, nil
} }
func (wc *WriteCounter) PrintProgress() { func (wc *WriteCounter) PrintProgress() {
@@ -53,63 +53,109 @@ func (wc *WriteCounter) PrintProgress() {
if elapsed < 0.001 { if elapsed < 0.001 {
elapsed = 0.001 elapsed = 0.001
} }
speed := float64(wc.Total) / 1024 / 1024 / elapsed
speed := float64(wc.Total) / 1024 / 1024 / elapsed // MB/s
percent := float64(wc.Total) / float64(wc.TotalSize) * 100 percent := float64(wc.Total) / float64(wc.TotalSize) * 100
if wc.OnEmit != nil { if wc.OnEmit != nil {
wc.OnEmit(percent, speed) wc.OnEmit(percent, fmt.Sprintf("%s/s", HumanFormat(speed)))
} }
} }
func DownloadFile(filepath string, url string, onEmit func(percent float64, speed float64)) error { // --- DownloadFileParallel ---
tmpPath := filepath + ".tmp" func (g *GitService) downloadFileParallel(filePath, url string, numParts int, onEmit func(percent float64, speed string)) (tmpPath string, err error) {
resp, err := http.Head(url)
resp, err := http.Get(url)
if err != nil { if err != nil {
return fmt.Errorf("failed to get file: %w", err) return "", fmt.Errorf("failed to get head: %w", err)
} }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bad status: %s", resp.Status) return "", fmt.Errorf("bad status: %s", resp.Status)
} }
size := resp.ContentLength
tmpPath = filePath + ".tmp"
out, err := os.Create(tmpPath) out, err := os.Create(tmpPath)
if err != nil { if err != nil {
return fmt.Errorf("failed to create tmp file: %w", err) return "", fmt.Errorf("failed to create tmp file: %w", err)
} }
defer out.Close()
counter := NewWriteCounter(resp.ContentLength, onEmit) counter := NewWriteCounter(size, onEmit)
_, err = io.Copy(out, io.TeeReader(resp.Body, counter)) partSize := size / int64(numParts)
if closeErr := out.Close(); closeErr != nil { var wg sync.WaitGroup
return fmt.Errorf("failed to close tmp file: %w", closeErr) var mu sync.Mutex
}
if err != nil {
return fmt.Errorf("failed to download file: %w", err)
}
// Delete destination file if it exists for i := 0; i < numParts; i++ {
if _, err := os.Stat(filepath); err == nil { start := int64(i) * partSize
if err := os.Remove(filepath); err != nil { end := start + partSize - 1
return fmt.Errorf("failed to remove existing file: %w", err) if i == numParts-1 {
end = size - 1
} }
_ = start
_ = end
wg.Go(func() {
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end))
resp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
buf := make([]byte, 32*1024)
var written int64
for {
n, err := resp.Body.Read(buf)
if n > 0 {
mu.Lock()
out.Seek(start+written, 0)
out.Write(buf[:n])
mu.Unlock()
written += int64(n)
counter.Add(n)
}
if err == io.EOF {
break
}
if err != nil {
break
}
}
})
} }
for i := 0; i < 3; i++ { wg.Wait()
err = os.Rename(tmpPath, filepath) return tmpPath, nil
if err == nil {
break
}
time.Sleep(300 * time.Millisecond)
}
if err != nil {
return fmt.Errorf("failed to rename after retries: %w", err)
}
return nil
} }
func unzipParallel(src string, dest string) error { // --- Helper getReleaseAsset ---
func (g *GitService) getReleaseAsset(version, url, fileName string) (models.AssetType, bool) {
resp, err := http.Get(url)
if err != nil {
return models.AssetType{}, false
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var releases []*models.ReleaseType
if err := json.Unmarshal(body, &releases); err != nil || len(releases) == 0 {
return models.AssetType{}, false
}
for _, release := range releases {
if release.TagName == version {
for _, asset := range release.Assets {
if asset.Name == fileName {
return asset, true
}
}
}
}
return models.AssetType{}, false
}
func (g *GitService) unzipParallel(src string, dest string) error {
numCPU := runtime.NumCPU() numCPU := runtime.NumCPU()
reserved := 1 reserved := 1
@@ -137,11 +183,11 @@ func unzipParallel(src string, dest string) error {
jobs := make(chan job) jobs := make(chan job)
var wg sync.WaitGroup var wg sync.WaitGroup
// Worker pool
for i := 0; i < maxWorkers; i++ { for i := 0; i < maxWorkers; i++ {
wg.Go(func() { wg.Go(func() {
for j := range jobs { for j := range jobs {
err := extractFile(j.f, dest) err := g.extractFile(j.f, dest)
if err != nil { if err != nil {
fmt.Printf("Error extracting %s: %v\n", j.f.Name, err) fmt.Printf("Error extracting %s: %v\n", j.f.Name, err)
} }
@@ -149,17 +195,17 @@ func unzipParallel(src string, dest string) error {
}) })
} }
// Feed jobs
for _, f := range r.File { for _, f := range r.File {
jobs <- job{f} jobs <- job{f}
} }
close(jobs) close(jobs)
wg.Wait() wg.Wait()
return nil return nil
} }
func extractFile(f *zip.File, dest string) error { func (g *GitService) extractFile(f *zip.File, dest string) error {
fp := filepath.Join(dest, f.Name) fp := filepath.Join(dest, f.Name)
if f.FileInfo().IsDir() { if f.FileInfo().IsDir() {

View File

@@ -10,7 +10,7 @@ const ProxyZipFile = "64bit.zip"
const LauncherFile = "firefly-launcher.exe" const LauncherFile = "firefly-launcher.exe"
const TempUrl = "./temp" const TempUrl = "./temp"
const CurrentLauncherVersion = "1.5.0" const CurrentLauncherVersion = "1.6.0"
type ToolFile string type ToolFile string

View File

@@ -21,9 +21,18 @@ func IsFileIn7z(archivePath, fileInside string) (bool, error) {
lines := strings.Split(out.String(), "\n") lines := strings.Split(out.String(), "\n")
for _, line := range lines { for _, line := range lines {
if strings.Contains(line, fileInside) { line = strings.TrimSpace(line)
return true, nil if line == "" {
continue
} }
parts := strings.Fields(line)
if len(parts) > 0 {
path := parts[len(parts)-1]
if path == fileInside {
return true, nil
}
}
} }
return false, fmt.Errorf("%s not found in %s", fileInside, archivePath) return false, fmt.Errorf("%s not found in %s", fileInside, archivePath)