Skip to content

Commit fb2afb5

Browse files
committed
feat: 实现客户端自动更新功能
新增功能: - 自动检查 GitHub Release 最新版本 - 国内镜像加速支持(API 和下载分开) - 超时自动切换镜像站 - 跨平台支持(Windows/Linux/macOS/Android) - 进度窗口显示(可选) - 版本比较和更新日志展示 - SHA-256 校验验证 - 备份和回滚机制 新增文件: - GitHubMirror.java: 镜像站管理 - GitHubReleaseClient.java: GitHub API 客户端 - PlatformPathResolver.java: 跨平台路径解析 - SelfUpdateWindow.java: 进度窗口 - 测试类若干 修改文件: - Main.java: 添加自更新检查入口 - AppConfig.java: 添加 ClientUpdateConfig 配置类 - mcpatch.yml: 添加 client-update 配置项
1 parent 90056f5 commit fb2afb5

20 files changed

Lines changed: 2951 additions & 442 deletions

docs/client-version.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

docs/self-update-design.md

Lines changed: 127 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -2,253 +2,186 @@
22

33
## 方案概述
44

5-
由于客户端作为 Java Agent 运行,JAR 文件在运行时被锁定无法直接替换,因此采用 **"下载-标记-替换"** 的延迟更新策略
5+
客户端通过 GitHub Release API 自动检查并更新自身版本,支持国内镜像加速
66

77
---
88

99
## 架构设计
1010

1111
```
12-
┌─────────────────────────────────────────────────────────┐
13-
│ 启动流程 │
14-
├─────────────────────────────────────────────────────────┤
15-
│ 1. 启动 Minecraft │
16-
│ ↓ │
17-
│ 2. Java Agent (update.jar) 加载 │
18-
│ ↓ │
19-
│ 3. 检查客户端自身更新 ←─────────────────┐ │
20-
│ ↓ │ │
21-
│ 4. 如有更新: 下载新版本到临时位置 │ │
22-
│ ↓ │ │
23-
│ 5. 检查游戏文件更新 │ │
24-
│ ↓ │ │
25-
│ 6. 启动 Minecraft │ │
26-
│ ↓ │ │
27-
│ 7. 程序退出时/下次启动: 替换旧版本 ──────┘ │
28-
└─────────────────────────────────────────────────────────┘
12+
┌─────────────────────────────────────────────────────────────────────┐
13+
│ 启动流程 │
14+
├─────────────────────────────────────────────────────────────────────┤
15+
│ │
16+
│ 1. 启动 Minecraft │
17+
│ │ │
18+
│ ▼ │
19+
│ 2. Java Agent (update.jar) 加载 │
20+
│ │ │
21+
│ ▼ │
22+
│ 3. 检查标记文件(有待安装的更新?) │
23+
│ ├── 有 → 安装更新 → 继续启动 │
24+
│ └── 无 → 继续 │
25+
│ │ │
26+
│ ▼ │
27+
│ 4. 请求 GitHub Release API │
28+
│ │ │
29+
│ ▼ │
30+
│ 5. 比较版本号 │
31+
│ ├── 有新版本 → 下载 → 创建标记文件 │
32+
│ └── 无新版本 → 继续 │
33+
│ │ │
34+
│ ▼ │
35+
│ 6. 检查游戏文件更新 → 启动 Minecraft │
36+
│ │
37+
└─────────────────────────────────────────────────────────────────────┘
2938
```
3039

3140
---
3241

33-
## 实现方案
42+
## 核心组件
3443

35-
### 1. 版本检测
44+
```
45+
src/main/java/com/github/balloonupdate/mcpatch/client/selfupdate/
46+
├── SelfUpdateManager.java # 更新管理器(统一调度)
47+
├── SelfUpdateChecker.java # 版本检查器
48+
├── SelfUpdateDownloader.java # 下载器
49+
├── SelfUpdateInstaller.java # 安装器
50+
├── ClientVersionInfo.java # 版本信息模型
51+
├── GitHubReleaseClient.java # GitHub Release API 客户端
52+
└── GitHubMirror.java # GitHub 镜像加速器
53+
```
3654

37-
在配置文件 `mcpatch.yml` 中添加客户端更新配置:
55+
---
3856

39-
```yaml
40-
# 客户端自身更新配置
41-
client-update:
42-
# 客户端版本检查 URL
43-
version-url: "https://your-server.com/mcpatch/client-version.json"
44-
# 是否启用自动更新
45-
enabled: true
46-
# 更新渠道: stable/beta/alpha
47-
channel: stable
57+
## GitHub Release API
58+
59+
### 获取最新版本
60+
61+
```bash
62+
GET https://api.github.com/repos/{owner}/{repo}/releases/latest
4863
```
4964

50-
### 2. 版本信息文件 (服务端)
65+
### 响应示例
5166

52-
`client-version.json`:
5367
```json
5468
{
55-
"latest_version": "0.0.12",
56-
"min_version": "0.0.10",
57-
"download_url": "https://your-server.com/mcpatch/Mcpatch-0.0.12.jar",
58-
"checksum": "abc123def456...",
59-
"changelog": "修复了若干bug",
60-
"release_date": "2025-04-04",
61-
"force_update": false
69+
"tag_name": "v0.0.12",
70+
"name": "Mcpatch v0.0.12",
71+
"body": "- 修复空文件下载崩溃问题\n- 新增镜像加速功能",
72+
"published_at": "2025-04-04T12:00:00Z",
73+
"prerelease": false,
74+
"assets": [
75+
{
76+
"name": "Mcpatch-0.0.12.jar",
77+
"browser_download_url": "https://github.com/xxx/releases/download/v0.0.12/Mcpatch-0.0.12.jar",
78+
"size": 8000000
79+
}
80+
]
6281
}
6382
```
6483

65-
### 3. 核心代码结构
66-
67-
```
68-
src/main/java/com/github/balloonupdate/mcpatch/client/
69-
├── selfupdate/
70-
│ ├── SelfUpdateChecker.java # 检查更新
71-
│ ├── SelfUpdateDownloader.java # 下载新版本
72-
│ ├── SelfUpdateInstaller.java # 安装更新
73-
│ └── ClientVersionInfo.java # 版本信息
74-
```
75-
7684
---
7785

78-
## 详细设计
86+
## 镜像加速
7987

80-
### 阶段 1: 启动时检查
88+
### 支持的镜像站
8189

82-
```java
83-
// Main.java 中的修改
84-
public static void premain(String agentArgs, Instrumentation ins) throws Throwable {
85-
// 1. 先检查客户端自身更新
86-
if (checkSelfUpdate()) {
87-
downloadSelfUpdate();
88-
}
90+
| 镜像站 | 说明 |
91+
|--------|------|
92+
| `https://gh-proxy.org/` | 主站 |
93+
| `https://hk.gh-proxy.org/` | 香港节点 |
94+
| `https://cdn.gh-proxy.org/` | CDN 加速 |
95+
| `https://edgeone.gh-proxy.org/` | EdgeOne 加速 |
8996

90-
// 2. 检查是否有待安装的更新
91-
installPendingUpdate();
97+
### 镜像选择流程
9298

93-
// 3. 继续正常的游戏文件更新流程
94-
// ...
95-
}
96-
```
99+
1. Ping 测试所有镜像站延迟
100+
2. 选择延迟最低的镜像站
101+
3. 缓存最优镜像 10 分钟
102+
4. 如果所有镜像不可达,使用原始链接
97103

98-
### 阶段 2: 下载新版本
104+
---
99105

100-
```java
101-
public class SelfUpdateDownloader {
102-
/**
103-
* 下载新版本到临时文件
104-
* Windows: %TEMP%\mcpatch-update.jar
105-
* Linux/Mac: /tmp/mcpatch-update.jar
106-
*/
107-
public static Path downloadNewVersion(String downloadUrl, String expectedChecksum) {
108-
Path tempFile = getUpdateTempFile();
106+
## 配置示例
109107

110-
// 下载文件
111-
downloadFile(downloadUrl, tempFile);
108+
```yaml
109+
client-update:
110+
# 是否启用客户端自动更新
111+
enabled: true
112112

113-
// 校验 checksum
114-
if (!verifyChecksum(tempFile, expectedChecksum)) {
115-
throw new RuntimeException("Checksum verification failed");
116-
}
113+
# GitHub 仓库配置
114+
github-repo: "BalloonUpdate/Mcpatch2JavaClient"
117115

118-
// 创建标记文件,表示有待安装的更新
119-
createUpdateMarker(tempFile);
116+
# 镜像加速(国内环境推荐开启)
117+
mirror: auto
120118

121-
return tempFile;
122-
}
123-
}
124-
```
119+
# 更新渠道
120+
channel: stable
125121

126-
### 阶段 3: 延迟替换
127-
128-
```java
129-
public class SelfUpdateInstaller {
130-
/**
131-
* 安装待处理的更新
132-
* 这个方法在程序启动最开始执行
133-
*/
134-
public static boolean installPendingUpdate() {
135-
Path markerFile = getUpdateMarkerFile();
136-
137-
if (!Files.exists(markerFile)) {
138-
return false; // 没有待安装的更新
139-
}
140-
141-
// 读取标记文件获取新版本路径
142-
String newVersionPath = Files.readString(markerFile);
143-
Path newJar = Paths.get(newVersionPath);
144-
Path currentJar = Env.getJarPath();
145-
146-
if (!Files.exists(newJar)) {
147-
// 新版本文件不存在,清理标记
148-
Files.delete(markerFile);
149-
return false;
150-
}
151-
152-
// 替换 JAR 文件
153-
backupCurrentVersion(currentJar);
154-
Files.move(newJar, currentJar, StandardCopyOption.REPLACE_EXISTING);
155-
Files.delete(markerFile);
156-
157-
Log.info("客户端已更新到最新版本");
158-
return true;
159-
}
122+
# 是否自动安装更新
123+
auto-install: true
160124

161-
/**
162-
* 备份当前版本,以防回滚
163-
*/
164-
private static void backupCurrentVersion(Path currentJar) {
165-
Path backup = currentJar.resolveSibling(currentJar.getFileName() + ".backup");
166-
Files.copy(currentJar, backup, StandardCopyOption.REPLACE_EXISTING);
167-
}
168-
}
125+
# 是否备份当前版本
126+
backup-enabled: true
127+
128+
# 更新失败时是否自动回滚
129+
rollback-on-failure: true
169130
```
170131
171132
---
172133
173-
## 更新流程图
134+
## 更新流程
174135
175136
```
176-
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
177-
│ 程序启动 │────→│ 检查标记文件 │────→│ 有待更新? │
178-
└──────────────┘ └──────────────┘ └──────┬───────┘
179-
180-
┌─────────────────────────────┴─────┐
181-
│ Yes │ No
182-
↓ ↓
183-
┌──────────────┐ ┌──────────────┐
184-
│ 替换 JAR 文件│ │ 正常启动流程 │
185-
└──────┬───────┘ └──────────────┘
186-
187-
188-
┌──────────────┐
189-
│ 检查新版本 │
190-
└──────┬───────┘
191-
192-
├── 有新版本 ──→ 下载到临时位置 ──→ 创建标记文件 ──→ 继续启动
193-
194-
└── 无新版本 ──→ 继续正常启动流程
137+
时间线:
138+
┌─────────────────────────────────────────────────────────────────┐
139+
│ │
140+
│ 第 N 次启动 │
141+
│ 1. 检查标记文件 → 不存在 │
142+
│ 2. 请求 GitHub Release API │
143+
│ 3. 发现新版本 0.0.12 │
144+
│ 4. 下载到临时目录 │
145+
│ 5. 创建标记文件 │
146+
│ 6. 继续正常运行 │
147+
│ │
148+
│ 第 N 次退出 │
149+
│ JVM 退出,JAR 文件解锁 │
150+
│ │
151+
│ 第 N+1 次启动 │
152+
│ 1. 检查标记文件 → 存在! │
153+
│ 2. 备份当前版本 │
154+
│ 3. 替换 JAR 文件 │
155+
│ 4. 删除标记文件 │
156+
│ 5. 继续启动(已是新版本) │
157+
│ │
158+
└─────────────────────────────────────────────────────────────────┘
195159
```
196160

197161
---
198162

199163
## 文件结构
200164

201165
```
202-
.minecraft/versions/xxx/
203-
├── update.jar # 当前运行的客户端
204-
├── update.jar.backup # 备份文件(用于回滚)
205-
├── update.jar.new # 待安装的新版本(Windows)
206-
└── .mcpatch-temp/
207-
└── self-update.marker # 更新标记文件
208-
```
209-
210-
---
211-
212-
## 配置示例
166+
临时目录(系统 temp):
167+
├── .mcpatch-selfupdate-marker # 标记文件
168+
└── mcpatch-update-new.jar # 新版本 JAR
213169
214-
```yaml
215-
# mcpatch.yml 完整配置示例
216-
217-
# 游戏文件更新服务器
218-
urls:
219-
- "https://update.example.com/mcpatch/"
220-
221-
# 客户端自身更新配置
222-
client-update:
223-
enabled: true
224-
version-url: "https://update.example.com/client-version.json"
225-
channel: stable
226-
auto-install: true # 自动安装更新
227-
backup-enabled: true # 是否备份旧版本
228-
rollback-on-failure: true # 更新失败时回滚
229-
230-
# 其他配置...
231-
version-file-path: "version-label.txt"
232-
window-title: "游戏更新器"
170+
游戏目录:
171+
├── update.jar # 当前版本
172+
└── update.jar.backup # 备份文件
233173
```
234174

235175
---
236176

237-
## 优势
177+
## 功能特性
238178

239179
| 特性 | 说明 |
240180
|------|------|
241-
| ✅ 非阻塞更新 | 不影响当前游戏运行 |
242-
| ✅ 自动回滚 | 更新失败可恢复 |
243-
| ✅ 校验机制 | Checksum 防止损坏 |
244-
| ✅ 跨平台 | Windows/Linux/macOS 兼容 |
245-
| ✅ 可配置 | 支持多渠道更新 |
246-
247-
---
248-
249-
## 注意事项
250-
251-
1. **首次运行**: 需要手动部署初始版本
252-
2. **权限问题**: 确保有写入 JAR 目录的权限
253-
3. **网络超时**: 需要处理网络异常情况
254-
4. **版本回退**: 保留备份文件支持回滚
181+
| ✅ GitHub Release | 免费托管,CDN 加速 |
182+
| ✅ 镜像加速 | 国内网络优化 |
183+
| ✅ 自动选择 | Ping 测试选择最快镜像 |
184+
| ✅ 安全校验 | SHA-256 文件校验 |
185+
| ✅ 自动备份 | 更新前备份当前版本 |
186+
| ✅ 失败回滚 | 更新失败自动恢复 |
187+
| ✅ 延迟安装 | 避免 JAR 文件锁定问题 |

0 commit comments

Comments
 (0)