[Themeda启动器]用Java编写的Minecraft启动器

启动器全部由本人一人编写完成,作为练手的作品。编写过程磕磕绊绊,大多时间都在查阅各种网上资料。

现在写下这篇文章以供后来者参考。

 

本启动器预计实现以下内容

启动器预计实现的功能
是否完成 项目名称 功能简介 完成日期
登录器的打开及退出 打开登录器界面及关闭登录器 2018/7/10
登录器GUI布局 登录器用户互动界面规划 2018/7/10
注册功能 在登陆器上注册游戏账号 2018/7/17
  宣传图的更新 可以实时获取网站上的最新宣传图数据并更新启动器上的图片  
  公告栏的更新 可以实时获取网站上的公告数据并更新到启动器上的公告栏  
  新闻栏的更新 可以实时获取网站上的新闻数据并更新到启动器上的新闻栏  
  客户端更新功能 比对客户端与服务端的版本号,若不同则进入自动更新  
  客户端绑定功能 只有该启动器发起的请求可以进入服务器,否则拒绝  
  商店功能 可以在启动器上浏览服务器的商店  
  积分兑换 隶属商店功能,使用用户的积分兑换商品  
  充值功能 隶属商店功能,可以直接充值商店点数  
  查询功能 查询玩家信息(玩家等级、注册时间、最后登录时间等)  
  下载功能 提供现行JAVA下载、安装  
启动器配置 JAVA路径、内存等参数 2018/7/24
窗口可拖动 按住窗口部位可拖动窗口 2018/7/24
游戏启动 直接启动游戏,并实现可以直接进入服务器功能 2018/8/1
⊕为完成一部分功能

本篇文章侧重于启动器对游戏的启动过程。若有其他问题。可私信我提问。知无不言。


作为一个开发白痴。一开始写到启动功能时,两眼一抹黑。

最开始引用了MCBBS论坛上开源的JMCCC类库

但是在实际使用中碰到了以下问题。

AL lib: (EE) alc_cleanup: 1 device not closed
游戏进程退出,状态码:-1

四处求助无果后,我开始转向另一种方法。在论坛上看到的解析JSON来启动游戏。于是我参考了HMCL启动器生成的启动脚本

其生成脚本如下

@echo off
set appdata=[游戏根目录]\.minecraft
"C:\Program Files\Java\jre1.8.0_181\bin\javaw.exe"
 -XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump
 -XX:+UseG1GC
 -XX:-UseAdaptiveSizePolicy
 -XX:-OmitStackTraceInFastThrow
 -Xmn128m
 -Xmx2048m
 -Djava.library.path=[游戏根目录]\.minecraft\versions\1.12.2\1.12.2-natives
 -Dfml.ignoreInvalidMinecraftCertificates=true
 -Dfml.ignorePatchDiscrepancies=true
 -cp /C:/Users/郑志钦/Desktop/格式工厂输出/3d/HMCL-2.4.1.6.exe org.jackhuang.hellominecraft.launcher.Launcher
 -cp=[游戏根目录]\.minecraft\libraries\com\mojang\patchy\1.1\patchy-1.1.jar;
。
。。。。省略一万个支持库路径。。。。
。
[游戏根目录]\.minecraft\libraries\com\mojang\text2speech\1.10.3\text2speech-1.10.3.jar;
[游戏根目录]\.minecraft\versions\1.12.2\1.12.2.jar
 -mainClass=net.minecraft.client.main.Main
 --username need
 --version "HMCL 2.4.1.6"
 --gameDir [游戏根目录]\.minecraft
 --assetsDir [游戏根目录]\.minecraft\assets
 --assetIndex 1.12
 --uuid e877c56e4fb621e81fd30dbd114a545b
 --accessToken e877c56e4fb621e81fd30dbd114a545b
 --userType Legacy
 --versionType "HMCL 2.4.1.6"
 --height 480
 --width 854

于是按照正常思维,我将其中引用了HMCL类库部分语句删去 却发现无法启动 提示

[Themeda启动器]用Java编写的Minecraft启动器_第1张图片

多次尝试无果后再次转变思想,打算抛弃已有类库,尝试自己写一个启动核心。

在论坛发帖求助后(论坛帖)。得知在wiki.vg上有有关启动游戏的资料及看了KMCCC贴中的参考资料后。(该过程耗时3天)

小白我觉得这个不适合我,所以又回到了解析JSON文件上。

我先是按照HMCL的脚本格式自己解析了JSON

JSON解析过程源码如下

package minecraft.launcher;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.UUID;

public class GamebatWriter {
	String integrationArgs;
	String completePath;
	String fmlAgreement = " -Dfml.ignoreInvalidMinecraftCertificates=true -Dfml.ignorePatchDiscrepancies=true";
    String mainClassPath;
    String versionArg;
    public static String main(String args[]) throws IOException {
    	String Content = null;
    	GamebatWriter o =new GamebatWriter();
    	o.FileHandle();
    	Content = o.integrationContent();
    	return (Content);
    }
    public void FileHandle() throws IOException {
        File file = new File(".\\.minecraft\\versions\\"+Mainframe.GameVersion+"\\"+Mainframe.GameVersion+".json");
    	BufferedReader tempreader = new BufferedReader(new FileReader(file));
    	String[] path;
    	String allpath = "";
    	String prepath = "";
    	String name1 = "";
    	String name2 = "";
    	String tempcontent;
    	String finalpath = "";
    	int count = 0;
    	completePath = "";
    	while ((tempcontent = tempreader.readLine()) != null) {//循环处理每一个文件的路径
            if(tempcontent.contains("\"name\"" ) && tempcontent.length()>38) {//取出包含"name"的内容
            	allpath = "";
            	prepath = "";
            	tempcontent = tempcontent.substring(21, tempcontent.length()-2);
            	//System.out.println(tempcontent);
                prepath=tempcontent.substring(0,tempcontent.lastIndexOf(":"));
            	path = prepath.split("[\\.:]");//分割得到的内容,做成路径
            	int n;
            	for(n=0;n

代码有些部分比较冗杂。还望见谅

 

以上内容绝大部分通过解析JSON获得,部分内容因非必要与版本同步,所以采用内置存储。

接下来我会以 1.12.2.json 为例讲一下我是如何解析JSON的。

将JSON文件以记事本方式打开,可见以下内容。 我将需要解析部分单独标注

{
    "assetIndex": {//参数中的assetIndex数据从这里的"id"下取出
        "id": "1.12",
        "sha1": "e75e9535754c6f2158b0b18b35660f45c4495d78",
        "size": 169257,
        "url": "https://launchermeta.mojang.com/mc/assets/1.12/e75e9535754c6f2158b0b18b35660f45c4495d78/1.12.json",
        "totalSize": 127722338
    },
    "assets": "1.12",//参数中的asset数据从这里直接取出
    "downloads": {
        "client": {
            "sha1": "0f275bc1547d01fa5f56ba34bdc87d981ee12daf",
            "size": 10180113,
            "url": "https://launcher.mojang.com/mc/game/1.12.2/client/0f275bc1547d01fa5f56ba34bdc87d981ee12daf/client.jar"
        },
        "server": {
            "sha1": "886945bfb2b978778c3a0288fd7fab09d315b25f",
            "size": 30222121,
            "url": "https://launcher.mojang.com/mc/game/1.12.2/server/886945bfb2b978778c3a0288fd7fab09d315b25f/server.jar"
        }
    },
    "id": "1.12.2",//参数中的游戏版本可以由此取出。不过--version现在并不重要。随意内容均可
    "libraries": [//以下为需要载入的支持库。支持库在参数中以 -cp  开头载入。注意,要将所有支持库路径写出后将所有路径用双引号括住每个路径之间用一个分号(;)隔开
        {
            "name": "com.mojang:patchy:1.1",//这里是支持库的路径及名称
//该支持库路径为\.minecraft\libraries\com\mojang\patchy\1.1\patchy-1.1.jar
            "downloads": {//这里是该支持库的下载地址、大小、哈希值、及路径
                "artifact": {
                    "size": 15817,
                    "sha1": "aef610b34a1be37fa851825f12372b78424d8903",
                    "path": "com/mojang/patchy/1.1/patchy-1.1.jar",//路径可以直接取该值,我也才发现这一行,但我不打算改源码了
                    "url": "https://libraries.minecraft.net/com/mojang/patchy/1.1/patchy-1.1.jar"
                }
            }
        },
          
。
。
。
。
。。。。。省略一万个支持库路径。。。。。
。
。
。
。
。
。



    "mainClass": "net.minecraft.client.main.Main",//客户端的main类 必须载入 载入时直接在库后面空格 再加上该value即可

//以下内容在原文件中为一行,这里为了方便讲解,我将它拆成多行来讲解。
    "minecraftArguments": "--username ${auth_player_name}//玩家昵称。
 --version ${version_name}//版本号,不过并不是需要和版本同步的,随意内容均可
 --gameDir ${game_directory}//游戏路径,精确到\.minecraft
 --assetsDir ${assets_root}//assets的路径,精确到\.minecraft\assets
 --assetIndex ${assets_index_name}//由该文件第一行位置取到
 --uuid ${auth_uuid}//非正版登录可随机一个UUID JAVA有随机UUID的API
 --accessToken ${auth_access_token}//同上UUID
 --userType ${user_type}//不明确 一般填Legacy
 --versionType ${version_type}",//该版本号也是非必要。不过会在游戏刚启动后的左下角显示。
    "minimumLauncherVersion": 18,//最低启动器版本号。无用
    "releaseTime": "2017-09-18T08:39:46+00:00",
    "time": "2018-06-25T15:37:20+00:00",
    "type": "release"
}

解析出来的JSON应该如下

"C:\Program Files\Java\jre1.8.0_181\bin\javaw.exe"
 -XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump
 -XX:+UseG1GC
 -XX:-UseAdaptiveSizePolicy
 -XX:-OmitStackTraceInFastThrow
 -Xmn128m
 -Xmx2048m
 -Djava.library.path=[游戏根目录]\.minecraft\versions\1.12.2\1.12.2-natives
 -Dfml.ignoreInvalidMinecraftCertificates=true
 -Dfml.ignorePatchDiscrepancies=true
 -cp /C:/Users/郑志钦/Desktop/格式工厂输出/3d/HMCL-2.4.1.6.exe org.jackhuang.hellominecraft.launcher.Launcher
 -cp "[游戏根目录]\.minecraft\libraries\com\mojang\patchy\1.1\patchy-1.1.jar;
。
。。。。省略一万个支持库路径。。。。
。
[游戏根目录]\.minecraft\libraries\com\mojang\text2speech\1.10.3\text2speech-1.10.3.jar;
[游戏根目录]\.minecraft\versions\1.12.2\1.12.2.jar"
 net.minecraft.client.main.Main
 --username need
 --version "HMCL 2.4.1.6"
 --gameDir [游戏根目录]\.minecraft
 --assetsDir [游戏根目录]\.minecraft\assets
 --assetIndex 1.12
 --uuid e877c56e4fb621e81fd30dbd114a545b
 --accessToken e877c56e4fb621e81fd30dbd114a545b
 --userType Legacy
 --versionType "HMCL 2.4.1.6"
 --height 480
 --width 854

与HMCL生成脚本不同的是

1. -cp 后用的不是 等号 而是双引号

2. 在客户端main类的前端没有 -mainClasspath=

最重要的一点是以上所有参数之间使用空格隔开并且没有换行。

在写完解析JSON后,尝试使用JAVA自带的RUNTIME启动cmd运行以上参数

public void run() {
        String nice = null;
		try {
			nice = GamebatWriter.main(null);
		} catch (IOException e1) {
			System.out.println(e1.getMessage());
			e1.printStackTrace();
		}
		System.out.println("nice = " + nice);
        String cmd = "cmd /c " + nice;
        try {
			p = Runtime.getRuntime().exec(cmd);
			isGameRunning = true;
			System.out.println("游戏已启动");
		} catch (IOException e1) {
			System.out.println(e1.getMessage());
			e1.printStackTrace();
		} 
    }

但是并没有成功。

多方查阅资料后,在某篇文章中看到了 需要在 "cmd /c "后加上"start \"\" "

改正后该句代码为

String cmd = "cmd /c start \"\" " + nice;

即可正常启动。

以上为启动器的启动功能解析及JSON解析。


接下来因为启动器在启动游戏后要与服务端取得通信以完成用该启动器启动游戏的玩家无需再输入密码就可以直接进入服务器的功能

需要启动器在启动游戏后依然可以操作

但是因为本人技术所限,所以无法做到process不阻塞主进程。

在尝试编写自定义监听器未果后。我又开动小脑筋改了方向。

既然启动游戏前的界面被阻塞了。那我就再创建另外一个界面不就好了

于是在启动游戏后--获取缓冲流之前的代码区间里 ,

我先是隐藏了启动前的窗口,然后再创建了一个新的窗口用于与服务器端通信。解决了游戏启动后进程卡死的情况。

 

                        #以上方法行不通所有进程依旧会被阻塞#


接下来要实现的任务——找到启动游戏后直连服务器的参数

你可能感兴趣的:(Java启动器项目)