Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a4de02bc18 | |||
| a021658fa9 |
@@ -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) => {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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
2
go.mod
@@ -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
|
||||||
|
|||||||
@@ -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)})
|
||||||
|
|||||||
@@ -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, ""
|
||||||
}
|
}
|
||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user