教程/编写启动器
此特性为Java版独有。
本教程介绍如何制作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文件夹路径>
当前系统下游戏运行所需的动态链接库
-cp
参数
全称为-classpath
,后为所有当前版本Minecraft的普通库文件路径及游戏主文件,中间用;
隔开。
Minecraft参数
以主类名开头,通常为net.minecraft.client.main.Main
,若安装Mod加载器则一般为net.minecraft.launchwrapper.Launch
参数通常有:
--username
后接用户名--version
后接游戏版本--gameDir
后接游戏路径--assetsDir
后接资源文件路径--assetIndex
后接资源索引版本--uuid
后接用户uuid--accessToken
后接登录令牌--userType
后接用户类型--versionType
后接版本类型,会显示在游戏主界面右下角等等,可能因版本而异,具体应参考当前版本json文件内提供的信息。
游戏文件
文件结构大致如下:
列表
.minecraft
[版本号]
[版本号].jar
[版本号].json
indexes
log_configs
objects
skins
[版本号].json
client-[版本号].xml
assets
resourcepacks
saves
screenshots
versions
launcher_profiles.json
launcher_profiles.json
options.txt
以上文件结构经过简化,具体可参考.minecraft页面。
JSON文件
Minecraft大多数信息使用JSON文档存储管理,使用这些JSON文件可以获取下载、管理及启动所需的大部分信息。
版本清单文件
主条目: /version_manifest.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文件
主条目: /version.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/
留下一份拷贝。
启动器配置文件
主条目: launcher_profiles.json
该文件不是必须的,但它是官方启动器的配置文件。所以可用于与官方启动器数据互通,以及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/ZH:Authentication#错误。
验证账号密码
后缀:
/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/ZH:Authentication#Authentication)
你可以存储这个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/ZH: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
用于区分客户端和服务端的需要。
优化下载
有时,在官方服务器下载文件会很缓慢,这时可以考虑使用第三方镜像下载。
当前常用的第三方镜像有:
BMCLAPI
另外,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>

