- Баллы
- 123
Эта статья является продолжением статьи http://rubukkit.org/threads/Способы-запуска-клиента-игры-из-Лаунчера-Часть-1.26937/
В предыдущем разделе был описан пример где вызывалась java.exe однако этот бинарник является неуправляемым, а следовательно и мы можем реализовать свой собственный бинарник без вызова java.exe, в седлающей реализации мне удалось снять основное ограничения которое описаны в предыдущем разделе, мне удалось создать java классы в коде С++ и вызывать Java методы и классы из C++, перспектив у данного метода очень много, однако я не видел ни одного лаунчера его реализующего.
Основные 3 этапы запуска игры
Самое первое что необходимо сделать это указать расположение заговочных файлов jni.h и jni_md.h в этих заговочных файлах указаны прототипы функций и классы.
Наш код Лаунчер должен загрузить jvm.dll, эта библиотека осужесвляет взаимодействие нашего кода с виртуальной машиной Java. Чтобы создать виртуальную машину мы должны вызвать функцию "JNI_CreateJavaVM" из этой jvm.dll, мы должны указать виртуальной машине где находятся нативные библиотеки игры и классы(Таблица 2)
На данный момент наш лаунчер уже создал виртуальную машину Java и подключил указанные библиотеки классов игры, и другие стандартные библиотеки классов

ЭТАП 2 Модификация net.minecraft.client.MinecraftApplet
При анализе Minecraft 1.4.6 я увидел что Нотч хранит параметры MinecraftApplet в специальном классе (Таблица 3 стр 62 и стр 64) этот класс реализует интерфейс AppletStub также он определяет алгоритм изъятия параметров из хранилища.
Устанавливается данный класс в MinecraftApplet с помощью вызова метода setStub
Все что нам нужно сделать это:
1. Создать класс HashMap
Создание объекта java осуществляется за счет вызова функции NewObject он принимает тип создаваемого класса (находится за счет вызова функции FindClass("java/util/HashMap") которое возвращает тип найденного класса), метод которое мы хотим вызвать (получает за счет вызова GetMethodID(env->FindClass("java/util/HashMap"), "<init>", "()V") обратите внимание на второй параметр он говорит о том что мы хотим вызвать конструктор и третий параметр это сигнатура данной функции говорящая что конструктор не принимает никаких параметров и не возвращает их)
Сигнатуру методов можно посмотреть с помощью утилиты javap например сигнатура конструкторадля класса asf из minecraft.jar
он принимает объект которое наследует от класса java.util.Map и имеет вид (Ljava/util/Map
V
2. Положить в объект HashMap параметры с помощью метода put
Данная функция принимает:
III в) Запуск клиента игры с собственной реализацией Java.exe без промежуточного кода на высокоуровневом языке C++ .
В предыдущем разделе был описан пример где вызывалась java.exe однако этот бинарник является неуправляемым, а следовательно и мы можем реализовать свой собственный бинарник без вызова java.exe, в седлающей реализации мне удалось снять основное ограничения которое описаны в предыдущем разделе, мне удалось создать java классы в коде С++ и вызывать Java методы и классы из C++, перспектив у данного метода очень много, однако я не видел ни одного лаунчера его реализующего.
Основные 3 этапы запуска игры
- Создание виртуальной машины Java в собственном коде
- Модификация net.minecraft.client.MinecraftApplet из minecraft.jar
- Создание фрейма и запуск Applet
Таблица 1 Возможные параметры MinecraftApplet 1.4.6 от 20.12.12
PHP:
"username" "UserName"
"stand-alone" "true"или"false"
"demo" "true"или"false"
"fullscreen" "true"или"false"
"sessionid" "sessionid"
"server" "Ip_сервера"
"port" "port_сервера"
Таблица 2 Строка запуска клиента из консоли
Java:
java -Djava.class.path=%APPDATA%\.minecraft\bin\lwjgl.jar;%APPDATA%\.minecraft\bin\jinput.jar;%APPDATA%\.minecraft\bin\lwjgl_util.jar;%APPDATA%\.minecraft\bin\minecraft.jar; -Djava.library.path=%APPDATA%\.minecraft\bin\natives net.minecraft.client.Minecraft UserName SessionId [-demo || --applet || --password]
Таблица 3 Код main функции net.minecraft.client.Minecraft
Java:
public static void main(String[] paramArrayOfString) {
HashMap localHashMap = new HashMap();
boolean bool1 = false;
boolean bool2 = true;
boolean bool3 = false;
String str1 = "Player" + F() % 1000L;
if (paramArrayOfString.length > 0) str1 = paramArrayOfString[0];
String str2 = "-";
if (paramArrayOfString.length > 1) str2 = paramArrayOfString[1];
for (int i1 = 2; i1 < paramArrayOfString.length; i1++) {
localObject1 = paramArrayOfString[i1];
localObject2 = i1 == paramArrayOfString.length - 1 ? null : paramArrayOfString[(i1 + 1)];
int i2 = 0;
if ((((String)localObject1).equals("-demo")) || (((String)localObject1).equals("--demo"))) {
bool1 = true;
} else if (((String)localObject1).equals("--applet")) {
bool2 = false;
} else if ((((String)localObject1).equals("--password")) && (localObject2 != null)) {
String[] arrayOfString = jv.a(str1, (String)localObject2);
if (arrayOfString != null) {
str1 = arrayOfString[0];
str2 = arrayOfString[1];
System.out.println("Logged in insecurely as " + str1 + " - sessionId is " + str2);
} else {
System.out.println("Could not log in as " + str1 + " with given password");
}
i2 = 1;
}
if (i2 != 0) {
i1++;
}
}
localHashMap.put("demo", "" + bool1);
localHashMap.put("stand-alone", "" + bool2);
localHashMap.put("username", str1);
localHashMap.put("fullscreen", "" + bool3);
localHashMap.put("sessionid", str2);
Frame localFrame = new Frame();
localFrame.setTitle("Minecraft");
localFrame.setBackground(Color.BLACK);
Object localObject1 = new JPanel();
localFrame.setLayout(new BorderLayout());
((JPanel)localObject1).setPreferredSize(new Dimension(854, 480));
localFrame.add((Component)localObject1, "Center");
localFrame.pack();
localFrame.setLocationRelativeTo(null);
localFrame.setVisible(true);
localFrame.addWindowListener(new ask());
Object localObject2 = new asf(localHashMap);
MinecraftApplet localMinecraftApplet = new MinecraftApplet();
localMinecraftApplet.setStub((AppletStub)localObject2);
((asf)localObject2).setLayout(new BorderLayout());
((asf)localObject2).add(localMinecraftApplet, "Center");
((asf)localObject2).validate();
localFrame.removeAll();
localFrame.setLayout(new BorderLayout());
localFrame.add((Component)localObject2, "Center");
localFrame.validate();
localMinecraftApplet.init();
localMinecraftApplet.start();
Runtime.getRuntime().addShutdownHook(new asl());
}
ЭТАП 1 Создание виртуальной машины Java в собственном кодеСамое первое что необходимо сделать это указать расположение заговочных файлов jni.h и jni_md.h в этих заговочных файлах указаны прототипы функций и классы.
Наш код Лаунчер должен загрузить jvm.dll, эта библиотека осужесвляет взаимодействие нашего кода с виртуальной машиной Java. Чтобы создать виртуальную машину мы должны вызвать функцию "JNI_CreateJavaVM" из этой jvm.dll, мы должны указать виртуальной машине где находятся нативные библиотеки игры и классы(Таблица 2)
Java:
#pragma region ЭТАП I: начальная инициализация
HMODULE hJvm = ::LoadLibrary(_T(JAVA_HOME) _T("\\bin\\server\\jvm.dll"));
jni_JNI_CreateJavaVM = (JNI_CREATEJAVAVM)GetProcAddress(hJvm, "JNI_CreateJavaVM"); //Получаем адреес функции в dll
/*
Для запуска клиента игры необходимо указать виртуальной машине java
где находятся классы клиента и нативные библиотеки
*/
JavaVMOption options[2];
options[0].optionString = "-Djava.class.path=" GAME_PATH "\\bin\\lwjgl_util.jar;"
GAME_PATH "\\bin\\lwjgl.jar;"
GAME_PATH "\\bin\\jinput.jar;"
GAME_PATH "\\bin\\minecraft.jar;";
options[1].optionString = "-Djava.library.path=" GAME_PATH "\\bin\\natives";
//Формируем опции запуска виртуальной машины
JavaVMInitArgs args;
args.options = options;
args.nOptions = 2; // Количество опций
args.ignoreUnrecognized = FALSE;
args.version = JNI_VERSION_1_6;
//Вспомогательные классы, олицетворяющие виртуальную машину Java и среду выпонения
JavaVM* jvm = NULL;
JNIEnv* env = NULL;
//Вызываем функцию JNI_CreateJavaVM из dll адрес которой получили ранее
jni_JNI_CreateJavaVM(&jvm, &env, &args);
На данный момент наш лаунчер уже создал виртуальную машину Java и подключил указанные библиотеки классов игры, и другие стандартные библиотеки классов

ЭТАП 2 Модификация net.minecraft.client.MinecraftApplet
При анализе Minecraft 1.4.6 я увидел что Нотч хранит параметры MinecraftApplet в специальном классе (Таблица 3 стр 62 и стр 64) этот класс реализует интерфейс AppletStub также он определяет алгоритм изъятия параметров из хранилища.
Устанавливается данный класс в MinecraftApplet с помощью вызова метода setStub
Java:
Object localObject2 = new asf(localHashMap);
localMinecraftApplet.setStub((AppletStub)localObject2);
Все что нам нужно сделать это:
- Создать класс HashMap (хранилище параметров MinecraftApplet)
- Положить в него параметры с помощью метода put
- Создать класс asf на основе созданного HashMap с установленными параметрами
- Создать стандартный класс MinecraftApplet и передать ему с помощью метода setStub созданный ранее класс asf
Java:
#pragma region ЭТАП II: Модифицируем стандартный MinecraftApplete
/* Создание класса MinecraftApplete */
jclass class_appletStub = env->FindClass("asf"); // Получение класса реализующего интерфей AppletStub (Minecraft 1.4.6)
jmethodID mtid_appletStub = env->GetMethodID(class_appletStub, "<init>" , "(Ljava/util/Map;)V"); // Получаем метод конструктора
jobject obj_hashMap = env->NewObject(env->FindClass("java/util/HashMap"), env->GetMethodID(env->FindClass("java/util/HashMap"), "<init>", "()V"));
jmethodID mtid_put = env->GetMethodID(env->FindClass("java/util/HashMap"), "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
//Ради чего весь геморой, это установить данные параметры
env->CallObjectMethod(obj_hashMap, mtid_put, env->NewStringUTF("username"), env->NewStringUTF("Racvol"));
env->CallObjectMethod(obj_hashMap, mtid_put, env->NewStringUTF("stand-alone"), env->NewStringUTF("true"));
env->CallObjectMethod(obj_hashMap, mtid_put, env->NewStringUTF("demo"), env->NewStringUTF("false"));
env->CallObjectMethod(obj_hashMap, mtid_put, env->NewStringUTF("fullscreen"), env->NewStringUTF("false"));
env->CallObjectMethod(obj_hashMap, mtid_put, env->NewStringUTF("sessionid"), env->NewStringUTF("-"));
//env->CallObjectMethod(obj_hashMap, mtid_put, env->NewStringUTF("server"), env->NewStringUTF("127.0.0.1"));
//env->CallObjectMethod(obj_hashMap, mtid_put, env->NewStringUTF("port"), env->NewStringUTF("25555"));
jobject obj_appletStub = env->NewObject(class_appletStub, mtid_appletStub, obj_hashMap); // Создаем обьект полученного типа реализующего AppletStub
jobject obj_minecraftApplet = env->NewObject(env->FindClass("net/minecraft/client/MinecraftApplet"), env->GetMethodID(env->FindClass("net/minecraft/client/MinecraftApplet"), "<init>", "()V"));// Создаем стандартный MinecraftApplet
// Вызываем функцию setStub и передаем в качестве аргумента модифицированный appletStub
env->CallVoidMethod(obj_minecraftApplet, env->GetMethodID(env->GetObjectClass(obj_minecraftApplet), "setStub", "(Ljava/applet/AppletStub;)V"), obj_appletStub);
#pragma endregion
1. Создать класс HashMap
Java:
jobject obj_hashMap = env->NewObject(env->FindClass("java/util/HashMap"), env->GetMethodID(env->FindClass("java/util/HashMap"), "<init>", "()V"));
Сигнатуру методов можно посмотреть с помощью утилиты javap например сигнатура конструкторадля класса asf из minecraft.jar
Java:
C:\Users\Алексей>javap -p -classpath C:\Users\Алексей\AppData\Roaming\.minecraft\bin\minecraft.jar -s asf
Compiled from "SourceFile"
public class asf extends java.applet.Applet implements java.applet.AppletStub {
final java.util.Map a;
Signature: Ljava/util/Map;
public asf(java.util.Map);
Signature: (Ljava/util/Map;)V
V2. Положить в объект HashMap параметры с помощью метода put
Java:
env->CallObjectMethod(obj_hashMap, mtid_put, env->NewStringUTF("username"), env->NewStringUTF("Racvol"));
Данная функция принимает:
- Объект java, у которого хотим вызвать метод
- Сам метод, найденный ранее
- Параметры этого метода согласно сигнатуре метода
Последнее редактирование:
