如果有意向,可以編輯貢獻完善本教學。
本教學部分內容參考了wiki.vg。
本教學介紹如何製作Java版啟動器,並假定你已掌握任何一門編程語言。
基本原理
因為Minecraft製作時採用了「啟動器+遊戲檔案」的模式:
- 將遊戲檔案單獨儲存,而透過啟動器調用JVM(Java Virtual Machine)執行遊戲主檔案並傳入一些遊戲參數來啟動Minecraft。
- 遊戲依賴庫檔案以及遊戲資源檔案由啟動器補全。
- 玩家登入認證由啟動器完成。
這使得我們能透過編寫第三方啟動器來接管遊戲檔案管理和登入認證。
準備
要編寫一個啟動器,你需要:
- 一門編程語言其開發環境
- Java執行時環境(Java Runtime Environment,JRE),可於Java官網下載
- 腦子和手
並擁有支援下列功能的庫:
- 解析JSON文件(得到啟動參數的關鍵)
- 解壓檔案(解壓natives檔案,也可使用連結外部程式替代)
- 網路庫(正版驗證、外觀管理)
啟動參數
啟動參數將傳入java.exe或javaw.exe,使JVM透過傳入的主類正確地啟動遊戲。
啟動參數分為JVM參數和Minecraft參數兩部分。
取得參數
執行此命令可取得目前執行的Minecraft進程的參數:
wmic process where caption="javaw.exe" get caption,commandline /value>args.txt
此時args.txt大致有這樣的檔案內容:
Caption=javaw.exe CommandLine="**javaw或java路径**" -XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump "-Dos.name=Windows 10" -Dos.version=10.0 -Xss1M -Djava.library.path=**natives文件夹路径** -Dminecraft.launcher.brand=minecraft-launcher -Dminecraft.launcher.version=2.1.3674 -cp **一大串用;分开的文件路径** -Xmx2G -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1NewSizePercent=20 -XX:G1ReservePercent=20 -XX:MaxGCPauseMillis=50 -XX:G1HeapRegionSize=32M -Dlog4j.configurationFile=**log4j配置文件路径** net.minecraft.client.main.Main --username **用户名** --version **游戏版本** --gameDir **游戏路径** --assetsDir **资源文件路径** --assetIndex **资源索引版本** --uuid **用户uuid** --accessToken **登录令牌** --userType mojang --versionType release
僅保留CommandLine後的內容,然後將該檔案後綴改為.bat,雙擊即可啟動Minecraft。
執行前注意檔案編碼。並注意官方啟動器的解壓出的natives庫檔案儲存在臨時資料夾下,可能因刪除導致無法啟動。
JVM參數
-X、-XX參數
配置JVM,如GC等:
-Xmx1024m最大堆大小為1024MB-Xmn128m新生代堆大小為128MB-XX:+UseG1GC開啟G1-XX:-UseAdaptiveSizePolicy自動選擇年輕代區大小和相應的Survivor區比例-XX:-OmitStackTraceInFastThrow省略異常棧資訊從而快速拋出
-D參數
配置JVM系統屬性,格式為-D<name>=<value>。
Dos.name=Windows 10 -Dos.version=10.0目前系統名稱及版本-Dminecraft.launcher.brand=minecraft-launcher -Dminecraft.launcher.version=2.1.3674目前啟動器名稱及版本-Dlog4j.configurationFile=**文件路径**\client-1.12.xml遊戲日誌組態檔-Djava.library.path=**natives文件夹路径**目前系統下遊戲執行所需的動態連結庫
使用下列參數來跳過正版驗證:
-Dfml.ignoreInvalidMinecraftCertificates=true-Dfml.ignorePatchDiscrepancies=true
-cp參數
全稱為-classpath,後為所有目前版本Minecraft的普通庫檔案路徑及遊戲主檔案,中間用;隔開。
Minecraft參數
以主類名開頭,通常為net.minecraft.client.main.Main,若安裝Mod載入器則一般為net.minecraft.launchwrapper.Launch
參數通常有:
--username後接使用者名稱--version後接遊戲版本--gameDir後接遊戲路徑--assetsDir後接資源檔案路徑--assetIndex後接資源索引版本--uuid後接用户uuid--accessToken後接登入令牌--userType後接用户類型--versionType後接版本類型,會顯示在遊戲主界面右下角- 等等,可能因版本而異
遊戲檔案
檔案結構大致如下:
| 列表 |
|---|
|
以上檔案結構經過簡化,具體可參考.minecraft頁面。
JSON檔案
Minecraft大多數資訊使用JSON文件儲存管理,使用這些JSON檔案可以取得下載、管理及啟動所需的大部分資訊。
版本清單檔案
該檔案可以在mojang官方伺服器下載:
https://launchermeta.mojang.com/mc/game/version_manifest.json
內容通常如下:
{
"latest": {
"release": "1.14.1",
"snapshot": "1.14.2 Pre-Release 4"
},
"versions": [
{
"id": "1.14.2 Pre-Release 4",
"type": "snapshot",
"url": "https://launchermeta.mojang.com/v1/packages/f90f601344058a812144eb71a49552b30a70d589/1.14.2_Pre-Release_4.json",
"time": "2019-05-24T15:50:42+00:00",
"releaseTime": "2019-05-24T15:48:24+00:00"
},
{
"id": "1.14.2 Pre-Release 3",
"type": "snapshot",
"url": "https://launchermeta.mojang.com/v1/packages/48004350162b58ab677efb7db5cc417af13124ef/1.14.2_Pre-Release_3.json",
"time": "2019-05-24T15:40:12+00:00",
"releaseTime": "2019-05-22T13:12:51+00:00"
},
...
]
}
其中,latest中為目前最新版本,分為發佈版和快照版。versions後為所有可下載的遊戲版本,url後為該版本的json檔案下載地址。
版本json檔案
該檔案一般下載後儲存在.minecraft/versions資料夾下,有如下內容:
內容通常如下:
{
"arguments": {
"game": [
"--username",
"${auth_player_name}",
"--version",
"${version_name}",
...,
{
"rules": [
{
"action": "allow",
"features": {
"is_demo_user": true
}
}
],
"value": "--demo"
},
...
],
"jvm": [
{
"rules": [
{
"action": "allow",
"os": {
"name": "osx"
}
}
],
"value": [
"-XstartOnFirstThread"
]
},
...
]
},
"assetIndex": {
"id": "1.14",
"sha1": "702d433ca9a27a2b75dc4e95ac57921a34d82bd3",
"size": 226168,
"totalSize": 207233561,
"url": "https://launchermeta.mojang.com/v1/packages/702d433ca9a27a2b75dc4e95ac57921a34d82bd3/1.14.json"
},
"assets": "1.14",
"downloads": {
"client": {
"sha1": "f14e1ab15fb7455c81c487b2d82b29773e7cf4f6",
"size": 18794301,
"url": "https://launcher.mojang.com/v1/objects/f14e1ab15fb7455c81c487b2d82b29773e7cf4f6/client.jar"
},
"server": {
"sha1": "631e46624daaf9e8357fcb985e0fce489b020e74",
"size": 35932929,
"url": "https://launcher.mojang.com/v1/objects/631e46624daaf9e8357fcb985e0fce489b020e74/server.jar"
}
},
"id": "1.14.2 Pre-Release 4",
"libraries": [
{
"downloads": {
"artifact": {
"path": "com/mojang/patchy/1.1/patchy-1.1.jar",
"sha1": "aef610b34a1be37fa851825f12372b78424d8903",
"size": 15817,
"url": "https://libraries.minecraft.net/com/mojang/patchy/1.1/patchy-1.1.jar"
}
},
"name": "com.mojang:patchy:1.1"
},
...,
{
"downloads": {
"artifact": {
"path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar",
"sha1": "2bb514e444994c6fece99a21f76e0c90438e377f",
"size": 317748,
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar"
},
"classifiers": {
"javadoc": {
"path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-javadoc.jar",
"sha1": "1f6b7050737559b775d797c0ea56612b8e373fd6",
"size": 1287174,
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-javadoc.jar"
},
"natives-linux": {
"path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-linux.jar",
"sha1": "9bdd47cd63ce102cec837a396c8ded597cb75a66",
"size": 87484,
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-linux.jar"
},
"natives-macos": {
"path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-macos.jar",
"sha1": "5a4c271d150906858d475603dcb9479453c60555",
"size": 39835,
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-macos.jar"
},
"natives-windows": {
"path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-windows.jar",
"sha1": "e799d06b8969db0610e68776e0eff4b6191098bd",
"size": 255871,
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-windows.jar"
},
"sources": {
"path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-sources.jar",
"sha1": "106f90ac41449004a969309488aa6e3a2f7d6731",
"size": 255671,
"url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-sources.jar"
}
}
}
},
...
],
"logging": {
"client": {
"argument": "-Dlog4j.configurationFile=${path}",
"file": {
"id": "client-1.12.xml",
"sha1": "ef4f57b922df243d0cef096efe808c72db042149",
"size": 877,
"url": "https://launcher.mojang.com/v1/objects/ef4f57b922df243d0cef096efe808c72db042149/client-1.12.xml"
},
"type": "log4j2-xml"
}
},
"mainClass": "net.minecraft.client.main.Main",
"minimumLauncherVersion": 21,
"releaseTime": "2019-05-24T15:48:24+00:00",
"time": "2019-05-24T15:48:24+00:00",
"type": "snapshot"
}
rules
用於可選條目,使用features判斷並應用其action。
arguments
1.13後新增鍵,舊版本為gameArguments,且不提供JVM參數及rules規則。
${****}
字串模板,替換相應內容來使用。
assetIndex
目前版本的資源檔案索引,包含其下載地址等資訊。
downloads
遊戲主檔案,分為用戶端及伺服器端,包含其下載地址等資訊。
libraries
遊戲所有依賴庫,包含其下載地址等資訊。
downloads下均含有artifact鍵,有些含有classifiers鍵。
- 只含有
artifact鍵的為-cp參數後所需拼接的路徑,注意path鍵中為不完整路徑,請補全為完整路徑。 - 含有
classifiers鍵的為natives庫,在遊戲啟動前將對應平台的含有jar檔案解壓至natives資料夾。
logging
log4j組態檔,包含其下載地址等資訊。
mainClass
主類名。
資源索引檔案
一般儲存於.minecraft/assets/indexes路徑下,用於指示objects檔案的資訊及其下載地址。
該檔案的下載地址等資訊儲存在版本json檔案中,該檔案可能需要不定期更新。
內容如下:
{
"objects": {
"icons/icon_16x16.png": {
"hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a",
"size": 3665
},
...
}
}
objects檔案的下載地址為:
http://resources.download.minecraft.net/<hash的前兩位字元>/<hash>
儲存路徑為:
.minecraft/assets/objects/<hash的前两位字符>/<hash>
並在.minecraft/assets/virtual/legacy/留下一份拷貝。
啟動器組態檔
該檔案不是必須的,但它是官方啟動器的組態檔。所以可用於與官方啟動器數據互通,以及forge安裝檢驗。
最簡單的組態檔為:
{
"profiles": {
"(Default)": {
"gameDir": "**游戏目录**",
"lastVersionId": "1.14.1",
"name": "(Default)"
}
},
"selectedProfileName": "(Default)",
等等乱七八糟的配置
}
profiles為啟動器中建立的所有組態檔。
遊戲主檔案
通常儲存於.minecraft/versions/<version>/<version>.jar,下載地址等資訊儲存在版本json檔案中,下載時注意將該檔案重新命名為對應版本號。
依賴庫檔案
通常儲存於.minecraft/libraries/路徑下,下載地址等資訊儲存在版本json檔案中。
普通庫檔案
在啟動前需拼接在啟動參數的-cp參數後。
natives庫檔案
在啟動前需解壓至natives路徑下。
資源檔案
通常儲存在.minecraft/assets/objects/,並在.minecraft/assets/virtual/legacy/有一份拷貝。
下載地址等資訊儲存在資源索引檔案中。
這些檔案可能在發佈後更新,留意更新資源索引檔案來更新他們。
正版驗證
Minecraft自1.6後使用了Yggdrasil驗證方法,驗證伺服器為:
https://authserver.mojang.com
驗證時需要:
- 為
POST請求 Content-Type設定為application/json- 負載以JSON編碼
若請求成功則返回狀態碼200及一個JSON文件。
若失敗則返回錯誤資訊:
{
"error": "错误简要描述",
"errorMessage": "向用户显示的长描述",
"cause": "错误原因" // 可选的
}
具體錯誤資訊可參考https://wiki.vg/Authentication#Errors。
驗證賬號密碼
- 後綴:
/authenticate
- 負載:
{
"agent": {
"name": "Minecraft", // 默认为Minecraft,可选
"version": 1 // 未来可能会改(不会)
},
"username": "mojang用户名", // 可以是邮箱地址或旧版mojang用户名
"password": "密码",
"clientToken": "客户端标识符", // 可选的,用于复用该值
}
- 回應:
{
"accessToken": "随机令牌",
"clientToken": "客户端标识符",
"availableProfiles": [
{
"id": "profile identifier",
"name": "玩家名"
}
],
"selectedProfile": {
"id": "不含-的uuid",
"name": "玩家名"
}
}
(有刪節,參考https://wiki.vg/Authentication#Authenticate)
你可以儲存這個clientToken,用來標識這個用戶端。
此處取得的uuid和accessToken即為啟動參數中所需的,傳入你剛剛獲得的值,啟動遊戲後便能發現已顯示正版外觀,即完成了正版登入。
檢驗令牌有效性
accessToken具有有效期,可能因為一些原因失效。你可以發送請求,驗證目前accessToken是否還是有效的.
- 後綴:
/validate
- 負載:
{
"accessToken": "valid accessToken",
"clientToken": "associated clientToken" //可选的
}
- 回應:
如果狀態碼為204 No Content則有效,而403 Forbidden為已失效。
刷新令牌
刷新一個accessToken,用於保持用户在遊戲對談之間登入。
- 後綴:
/refresh
- 負載:
{
"accessToken": "valid accessToken",
"clientToken": "associated clientToken" //可选的
}
- 回應:
{
"accessToken": "随机令牌",
"clientToken": "客户端标识符",
"availableProfiles": [
{
"id": "profile identifier",
"name": "玩家名"
}
],
"selectedProfile": {
"id": "不含-的uuid",
"name": "玩家名"
}
}
(有刪節,參考https://wiki.vg/Authentication#Refresh)
與「驗證賬號密碼」中相同。
啟動遊戲
- 首先必須保證啟動參數中出現的所有檔案及提供的資源索引檔案中的
object檔案都存在且未被損壞。 - 選定一個natives路徑,可以自由選定,也可像官方啟動器一樣使用臨時路徑。將natives庫檔案解壓至該路徑,並將該路徑使用
-Djava.library.path=傳入遊戲。 - 完成正版驗證,得到uuid及accessToken
- 拼接啟動參數,建立遊戲進程
- 處理遊戲輸出及遊戲錯誤
支援forge等Mod載入器
執行forge等Mod載入器的安裝包後,可以發現:
- 啟動器組態檔中加入了一條新安裝的配置
.minecraft/libraries/資料夾中多了一些檔案.minecraft/versions/資料夾中多了一個版本json檔案(也可能會有jar檔案)
額外的版本json檔案
該檔案相比原版的版本json檔案檔案多了inheritsFrom和jar鍵,而且其他鍵內容明顯是不完整的。
inheritsFrom
該參數指定了目前版本所繼承的原版版本,意思為除此版本json檔案外,同時使用inheritsFrom中指定的版本json檔案內容。即,-cp參數後同時包含兩個版本json檔案指定的普通庫檔案,且natives庫檔案也同時包含兩個版本json檔案指定的,且該版本json檔案優先於原版版本json檔案。
jar
該參數指定了-cp參數後的遊戲主檔案。
除此之外,版本json檔案中的libraries鍵的格式也有些不一樣了,如:
{
"name": "org.ow2.asm:asm-all:5.0.3",
"serverreq": true
},
{
"name": "jline:jline:2.13",
"url": "http://files.minecraftforge.net/maven/",
"checksums": [
"2d9530d0a25daffaffda7c35037b046b627bb171"
],
"serverreq": true,
"clientreq": false
}
不再有downloads鍵了,只剩下name和url,檔案路徑及下載路徑需要根據一定規則拼接。
name鍵的格式為:
<package>:<name>:<version>
我們將它變形重組一下:
<package>/<name>/<version>/<name>-<version>.jar
前方接上.minecraft/libraries/即為檔案路徑,而接上url的內容即為下載地址。
serverreq和clientreq用於區分用戶端和伺服器端的需要。
最佳化下載
有時,在官方伺服器下載檔案會很緩慢,這時可以考慮使用第三方鏡像下載。
目前常用的第三方鏡像有:
另外,Minecraft的依賴庫檔案和資源索引檔案很多為小檔案,可以考慮使用多執行緒下載來最佳化速度。
外觀管理
外觀管理需要使用Mojang API:
api.mojang.com
可透過發送GET請求獲得Mojang API的狀態:
https://status.mojang.com/check
取得外觀及披風
可透過發送GET請求獲得外觀及披風地址:
https://sessionserver.mojang.com/session/minecraft/profile/<uuid>
- 回應:
{
"id": "<配置标识符>",
"name": "<玩家名>",
"properties": [
{
"name": "textures",
"value": "<base64字符串>"
}
]
}
解碼該base64字串,可獲得另一JSON文件:
{
"timestamp": <java time in ms>,
"profileId": "<配置uuid>",
"profileName": "<玩家名>",
"textures": {
"SKIN": {
"url": "<玩家皮肤URL>"
},
"CAPE": {
"url": "<玩家披风URL>"
}
}
}
更換外觀
發送POST請求至:
https://api.mojang.com/user/profile/<uuid>/skin
- 頭:
Authorization: Bearer <access token>
- 負載:
model=<""/"slim">&url=<皮肤url>
空字串為Steve模型,「slim」為Alex模型。
上載外觀
發送PUT請求至:
https://api.mojang.com/user/profile/<uuid>/skin
- 頭:
Authorization: Bearer <access token>
- 負載:
由兩部分組成:
- model:人物模型,空字串為Steve模型,「slim」為Alex模型。
- file:原始圖像檔案數據
重設外觀
發送DELETE請求至:
https://api.mojang.com/user/profile/<uuid>/skin
- 頭:
Authorization: Bearer <access token>
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||