feat: 오프라인 모드 + 다운로드 UX + 언인스톨 개선
오프라인 모드: - 서버 미응답 시 설치된 게임 직접 실행 옵션 다운로드 UX: - 속도(MB/s) + 남은 시간 표시 (초/분) 언인스톨: - 게임 데이터 삭제 여부 사용자 선택 - --version 플래그 추가 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
56
main.go
56
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)
|
||||
|
||||
Reference in New Issue
Block a user