diff --git a/main.go b/main.go index 55028e2..24bdba5 100644 --- a/main.go +++ b/main.go @@ -515,6 +515,10 @@ func doDownload(downloadURL, destDir string) error { buf := make([]byte, 32*1024) + var lastSpeedUpdate time.Time + var lastBytes int64 + var speedBytesPerSec float64 + for { if downloadCancelled.Load() { tmpFile.Close() @@ -533,12 +537,34 @@ func doDownload(downloadURL, destDir string) error { os.Remove(tmpPath) return fmt.Errorf("다운로드 크기가 제한을 초과했습니다") } + + now := time.Now() + if now.Sub(lastSpeedUpdate) >= 500*time.Millisecond { + elapsed := now.Sub(lastSpeedUpdate).Seconds() + if elapsed > 0 { + speedBytesPerSec = float64(downloaded-lastBytes) / elapsed + } + lastBytes = downloaded + lastSpeedUpdate = now + } + if total > 0 { pct := int(downloaded * 100 / total) if pct > 100 { pct = 100 } - setProgress(fmt.Sprintf("다운로드 중... %d%%", pct), pct) + + speedMB := speedBytesPerSec / 1024 / 1024 + text := fmt.Sprintf("다운로드 중... %d%% (%.1f MB/s)", pct, speedMB) + if speedBytesPerSec > 0 { + remaining := float64(total-downloaded) / speedBytesPerSec + if remaining < 60 { + text = fmt.Sprintf("다운로드 중... %d%% (%.1f MB/s, %d초 남음)", pct, speedMB, int(remaining)) + } else { + text = fmt.Sprintf("다운로드 중... %d%% (%.1f MB/s, %d분 남음)", pct, speedMB, int(remaining/60)) + } + } + setProgress(text, pct) } } if err == io.EOF { @@ -853,10 +879,6 @@ func uninstall() error { return err } } - // 설치 디렉토리 삭제 (자기 자신은 실행 중이라 삭제 실패할 수 있음 — 무시) - if dir, err := installDir(); err == nil { - os.RemoveAll(dir) - } return nil } @@ -1049,6 +1071,20 @@ func handleURI(rawURI string) error { serverInfo, err := fetchServerInfo() if err != nil { + // 오프라인 모드: 게임이 이미 설치되어 있으면 직접 실행 + if _, statErr := os.Stat(gamePath); statErr == nil { + ret := msgBox("One of the plans", "서버에 연결할 수 없습니다.\n설치된 게임을 실행하시겠습니까?\n(업데이트 확인 불가)", mbYesNo|mbQ) + if ret == idYes { + cmd := exec.Command(gamePath) + cmd.Dir = gameDir + cmd.Env = append(os.Environ(), "A301_TOKEN="+token) + if err := cmd.Start(); err != nil { + return fmt.Errorf("게임 실행 실패: %w", err) + } + return nil + } + return fmt.Errorf("사용자가 취소했습니다") + } return fmt.Errorf("버전 확인 실패: %w", err) } @@ -1129,12 +1165,22 @@ func main() { msgBox("One of the plans 런처", "a301:// 프로토콜이 등록되었습니다.", mbOK|mbInfo) case arg == "uninstall": + ret := msgBox("One of the plans 런처", "게임 데이터도 함께 삭제하시겠습니까?", mbYesNo|mbQ) + deleteData := ret == idYes if err := uninstall(); err != nil { msgBox("One of the plans 런처 - 오류", fmt.Sprintf("제거 실패:\n%v", err), mbOK|mbError) os.Exit(1) } + if deleteData { + if dir, err := installDir(); err == nil { + os.RemoveAll(dir) + } + } msgBox("One of the plans 런처", "a301:// 프로토콜이 제거되었습니다.", mbOK|mbInfo) + case arg == "--version" || arg == "version": + msgBox("One of the plans 런처", "버전: 1.0.0", mbOK|mbInfo) + case strings.HasPrefix(arg, protocolName+"://"): if err := handleURI(arg); err != nil { msgBox("One of the plans 런처 - 오류", fmt.Sprintf("실행 실패:\n%v", err), mbOK|mbError)