|
2 | 2 |
|
3 | 3 | ## 方案概述 |
4 | 4 |
|
5 | | -由于客户端作为 Java Agent 运行,JAR 文件在运行时被锁定无法直接替换,因此采用 **"下载-标记-替换"** 的延迟更新策略。 |
| 5 | +客户端通过 GitHub Release API 自动检查并更新自身版本,支持国内镜像加速。 |
6 | 6 |
|
7 | 7 | --- |
8 | 8 |
|
9 | 9 | ## 架构设计 |
10 | 10 |
|
11 | 11 | ``` |
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 | +└─────────────────────────────────────────────────────────────────────┘ |
29 | 38 | ``` |
30 | 39 |
|
31 | 40 | --- |
32 | 41 |
|
33 | | -## 实现方案 |
| 42 | +## 核心组件 |
34 | 43 |
|
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 | +``` |
36 | 54 |
|
37 | | -在配置文件 `mcpatch.yml` 中添加客户端更新配置: |
| 55 | +--- |
38 | 56 |
|
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 |
48 | 63 | ``` |
49 | 64 |
|
50 | | -### 2. 版本信息文件 (服务端) |
| 65 | +### 响应示例 |
51 | 66 |
|
52 | | -`client-version.json`: |
53 | 67 | ```json |
54 | 68 | { |
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 | + ] |
62 | 81 | } |
63 | 82 | ``` |
64 | 83 |
|
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 | | -
|
76 | 84 | --- |
77 | 85 |
|
78 | | -## 详细设计 |
| 86 | +## 镜像加速 |
79 | 87 |
|
80 | | -### 阶段 1: 启动时检查 |
| 88 | +### 支持的镜像站 |
81 | 89 |
|
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 加速 | |
89 | 96 |
|
90 | | - // 2. 检查是否有待安装的更新 |
91 | | - installPendingUpdate(); |
| 97 | +### 镜像选择流程 |
92 | 98 |
|
93 | | - // 3. 继续正常的游戏文件更新流程 |
94 | | - // ... |
95 | | -} |
96 | | -``` |
| 99 | +1. Ping 测试所有镜像站延迟 |
| 100 | +2. 选择延迟最低的镜像站 |
| 101 | +3. 缓存最优镜像 10 分钟 |
| 102 | +4. 如果所有镜像不可达,使用原始链接 |
97 | 103 |
|
98 | | -### 阶段 2: 下载新版本 |
| 104 | +--- |
99 | 105 |
|
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 | +## 配置示例 |
109 | 107 |
|
110 | | - // 下载文件 |
111 | | - downloadFile(downloadUrl, tempFile); |
| 108 | +```yaml |
| 109 | +client-update: |
| 110 | + # 是否启用客户端自动更新 |
| 111 | + enabled: true |
112 | 112 |
|
113 | | - // 校验 checksum |
114 | | - if (!verifyChecksum(tempFile, expectedChecksum)) { |
115 | | - throw new RuntimeException("Checksum verification failed"); |
116 | | - } |
| 113 | + # GitHub 仓库配置 |
| 114 | + github-repo: "BalloonUpdate/Mcpatch2JavaClient" |
117 | 115 |
|
118 | | - // 创建标记文件,表示有待安装的更新 |
119 | | - createUpdateMarker(tempFile); |
| 116 | + # 镜像加速(国内环境推荐开启) |
| 117 | + mirror: auto |
120 | 118 |
|
121 | | - return tempFile; |
122 | | - } |
123 | | -} |
124 | | -``` |
| 119 | + # 更新渠道 |
| 120 | + channel: stable |
125 | 121 |
|
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 |
160 | 124 |
|
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 |
169 | 130 | ``` |
170 | 131 |
|
171 | 132 | --- |
172 | 133 |
|
173 | | -## 更新流程图 |
| 134 | +## 更新流程 |
174 | 135 |
|
175 | 136 | ``` |
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 | +└─────────────────────────────────────────────────────────────────┘ |
195 | 159 | ``` |
196 | 160 |
|
197 | 161 | --- |
198 | 162 |
|
199 | 163 | ## 文件结构 |
200 | 164 |
|
201 | 165 | ``` |
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 |
213 | 169 |
|
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 # 备份文件 |
233 | 173 | ``` |
234 | 174 |
|
235 | 175 | --- |
236 | 176 |
|
237 | | -## 优势 |
| 177 | +## 功能特性 |
238 | 178 |
|
239 | 179 | | 特性 | 说明 | |
240 | 180 | |------|------| |
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