refactor(launch): dynamic version JSON parsing for Forge/NeoForge compatibility
- Replace hardcoded Forge/NeoForge args with version.json parsing
- Add VersionManifest.java — parses mainClass, arguments, libraries from JSON
- Implement rule matching for OS-specific library/argument filtering
- Build classpath dynamically from manifest libraries with fallback resolution
- Resolve game args with variable substitution (${version_name}, ${game_directory}, etc.)
- Auto-discover version.json path with multiple candidate formats
- Support all Forge versions (1.12.2 through 1.21+) and NeoForge out of the box
This commit is contained in:
+322
-436
@@ -3,11 +3,14 @@ package me.sashegdev.zernmc.launcher.minecraft.launch;
|
|||||||
import me.sashegdev.zernmc.launcher.minecraft.Instance;
|
import me.sashegdev.zernmc.launcher.minecraft.Instance;
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
||||||
import me.sashegdev.zernmc.launcher.utils.ZAnsi;
|
import me.sashegdev.zernmc.launcher.utils.ZAnsi;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class LaunchCommandBuilder {
|
public class LaunchCommandBuilder {
|
||||||
|
|
||||||
@@ -22,111 +25,266 @@ public class LaunchCommandBuilder {
|
|||||||
|
|
||||||
List<String> command = new ArrayList<>();
|
List<String> command = new ArrayList<>();
|
||||||
|
|
||||||
// 1. Путь к Java
|
String javaPath = "java";
|
||||||
String javaPath = getJavaPath();
|
|
||||||
command.add(javaPath);
|
command.add(javaPath);
|
||||||
|
|
||||||
// 2. JVM аргументы
|
|
||||||
command.addAll(getJvmArguments(options));
|
command.addAll(getJvmArguments(options));
|
||||||
|
|
||||||
// 3. Natives
|
|
||||||
Path nativesDir = instance.getPath().resolve("natives");
|
Path nativesDir = instance.getPath().resolve("natives");
|
||||||
if (!Files.exists(nativesDir)) {
|
if (!Files.exists(nativesDir)) {
|
||||||
Files.createDirectories(nativesDir);
|
Files.createDirectories(nativesDir);
|
||||||
}
|
}
|
||||||
command.add("-Djava.library.path=" + nativesDir.toAbsolutePath());
|
command.add("-Djava.library.path=" + nativesDir.toAbsolutePath());
|
||||||
|
|
||||||
String loaderType = instance.getLoaderType().toLowerCase();
|
VersionManifest manifest = resolveVersionManifest();
|
||||||
|
if (manifest != null) {
|
||||||
if ("forge".equals(loaderType)) {
|
|
||||||
command.addAll(getForgeJvmArguments());
|
|
||||||
command.add("-cp");
|
command.add("-cp");
|
||||||
command.add(buildForgeClasspath());
|
command.add(buildClasspathFromManifest(manifest));
|
||||||
command.add("cpw.mods.modlauncher.Launcher");
|
|
||||||
command.addAll(getForgeArguments(options));
|
String mainClass = resolveMainClass(manifest);
|
||||||
} else if ("neoforge".equals(loaderType)) {
|
command.add(mainClass);
|
||||||
command.addAll(getNeoForgeJvmArguments());
|
|
||||||
command.add("-cp");
|
command.addAll(resolveGameArguments(manifest, options));
|
||||||
command.add(buildNeoForgeClasspath());
|
|
||||||
command.add(getNeoForgeMainClass());
|
|
||||||
command.addAll(getNeoForgeArguments(options));
|
|
||||||
} else {
|
} else {
|
||||||
command.add("-cp");
|
command.add("-cp");
|
||||||
command.add(buildClasspath());
|
command.add(buildVanillaClasspath());
|
||||||
command.add(getMainClass());
|
command.add(getVanillaMainClass());
|
||||||
command.addAll(getMinecraftArguments(options));
|
command.addAll(getVanillaGameArguments(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getJavaPath() {
|
private VersionManifest resolveVersionManifest() {
|
||||||
return "java";
|
try {
|
||||||
|
Path versionJson = findVersionJson();
|
||||||
|
if (versionJson != null && Files.exists(versionJson)) {
|
||||||
|
String content = Files.readString(versionJson);
|
||||||
|
JSONObject json = new JSONObject(content);
|
||||||
|
System.out.println(ZAnsi.green("Найден version.json: " + versionJson.getFileName()));
|
||||||
|
return new VersionManifest(json);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(ZAnsi.yellow("Не удалось загрузить version.json: " + e.getMessage()));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getJvmArguments(LaunchOptions options) {
|
private Path findVersionJson() {
|
||||||
List<String> jvmArgs = new ArrayList<>();
|
Path versionsDir = instance.getPath().resolve("versions");
|
||||||
|
String loaderType = instance.getLoaderType().toLowerCase();
|
||||||
|
String mcVersion = instance.getMinecraftVersion();
|
||||||
|
String loaderVersion = instance.getLoaderVersion();
|
||||||
|
|
||||||
int ramMB = options.getMaxMemory() > 0 ? options.getMaxMemory() : 4096;
|
if ("forge".equals(loaderType) || "neoforge".equals(loaderType)) {
|
||||||
jvmArgs.add("-Xmx" + ramMB + "M");
|
String[] candidates = {
|
||||||
jvmArgs.add("-Xms" + Math.max(512, ramMB / 2) + "M");
|
getVersionId(),
|
||||||
|
mcVersion + "-" + loaderType + "-" + loaderVersion,
|
||||||
|
loaderType + "-" + loaderVersion,
|
||||||
|
mcVersion + "-" + loaderVersion,
|
||||||
|
mcVersion
|
||||||
|
};
|
||||||
|
for (String candidate : candidates) {
|
||||||
|
Path jsonPath = versionsDir.resolve(candidate).resolve(candidate + ".json");
|
||||||
|
if (Files.exists(jsonPath)) {
|
||||||
|
return jsonPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jvmArgs.add("-XX:+UseG1GC");
|
try {
|
||||||
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
|
if (Files.exists(versionsDir)) {
|
||||||
jvmArgs.add("-XX:G1NewSizePercent=20");
|
try (var stream = Files.list(versionsDir)) {
|
||||||
jvmArgs.add("-XX:G1ReservePercent=20");
|
return stream
|
||||||
jvmArgs.add("-XX:MaxGCPauseMillis=50");
|
.filter(Files::isDirectory)
|
||||||
jvmArgs.add("-XX:G1HeapRegionSize=32M");
|
.filter(dir -> dir.getFileName().toString().contains("forge") ||
|
||||||
|
dir.getFileName().toString().contains("neoforge"))
|
||||||
|
.filter(dir -> dir.getFileName().toString().contains(mcVersion))
|
||||||
|
.findFirst()
|
||||||
|
.map(dir -> dir.resolve(dir.getFileName().toString() + ".json"))
|
||||||
|
.filter(Files::exists)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Path fallback = versionsDir.resolve(mcVersion).resolve(mcVersion + ".json");
|
||||||
|
if (Files.exists(fallback)) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getVersionId() {
|
||||||
|
String loaderType = instance.getLoaderType().toLowerCase();
|
||||||
|
String mcVersion = instance.getMinecraftVersion();
|
||||||
|
String loaderVer = instance.getLoaderVersion();
|
||||||
|
|
||||||
|
if ("vanilla".equals(loaderType)) {
|
||||||
|
return mcVersion;
|
||||||
|
}
|
||||||
|
else if ("fabric".equals(loaderType)) {
|
||||||
|
String fabricId = instance.getFabricVersionId();
|
||||||
|
if (fabricId != null && !fabricId.isEmpty()) {
|
||||||
|
return fabricId;
|
||||||
|
}
|
||||||
|
return "fabric-loader-" + loaderVer + "-" + mcVersion;
|
||||||
|
}
|
||||||
|
else if ("forge".equals(loaderType)) {
|
||||||
|
return mcVersion + "-forge-" + loaderVer;
|
||||||
|
}
|
||||||
|
else if ("neoforge".equals(loaderType)) {
|
||||||
|
if (mcVersion.equals("1.20.1")) {
|
||||||
|
return mcVersion + "-neoforge-" + loaderVer;
|
||||||
|
}
|
||||||
|
return "neoforge-" + loaderVer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mcVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveMainClass(VersionManifest manifest) {
|
||||||
|
return manifest.getMainClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getVanillaMainClass() {
|
||||||
|
String loaderType = instance.getLoaderType().toLowerCase();
|
||||||
|
if ("fabric".equals(loaderType)) {
|
||||||
|
return "net.fabricmc.loader.impl.launch.knot.KnotClient";
|
||||||
|
}
|
||||||
|
return "net.minecraft.client.main.Main";
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> resolveGameArguments(VersionManifest manifest, LaunchOptions options) {
|
||||||
|
List<String> args = new ArrayList<>();
|
||||||
|
Map<String, String> vars = buildVariableMap(options);
|
||||||
|
|
||||||
|
for (String raw : manifest.getGameArguments()) {
|
||||||
|
args.add(resolveVariable(raw, vars));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.getWidth() > 0) {
|
||||||
|
args.add("--width");
|
||||||
|
args.add(String.valueOf(options.getWidth()));
|
||||||
|
}
|
||||||
|
if (options.getHeight() > 0) {
|
||||||
|
args.add("--height");
|
||||||
|
args.add(String.valueOf(options.getHeight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getVanillaGameArguments(LaunchOptions options) {
|
||||||
|
List<String> args = new ArrayList<>();
|
||||||
|
|
||||||
|
args.add("--version");
|
||||||
|
args.add(instance.getName());
|
||||||
|
args.add("--gameDir");
|
||||||
|
args.add(instance.getPath().toAbsolutePath().toString());
|
||||||
|
args.add("--assetsDir");
|
||||||
|
args.add(instance.getPath().resolve("assets").toAbsolutePath().toString());
|
||||||
|
args.add("--assetIndex");
|
||||||
|
String assetIndex = instance.getAssetIndex();
|
||||||
|
if (assetIndex == null || assetIndex.isEmpty()) {
|
||||||
|
assetIndex = instance.getMinecraftVersion();
|
||||||
|
System.out.println(ZAnsi.yellow("Asset index не найден, использую версию: " + assetIndex));
|
||||||
|
} else {
|
||||||
|
System.out.println(ZAnsi.green("Использую asset index: " + assetIndex));
|
||||||
|
}
|
||||||
|
args.add(assetIndex);
|
||||||
|
args.add("--username");
|
||||||
|
args.add(options.getUsername() != null ? options.getUsername() : "Player");
|
||||||
|
args.add("--accessToken");
|
||||||
|
args.add(options.getAccessToken() != null ? options.getAccessToken() : "0");
|
||||||
|
args.add("--uuid");
|
||||||
|
args.add(options.getUuid() != null ? options.getUuid() : "00000000-0000-0000-0000-000000000000");
|
||||||
|
args.add("--userType");
|
||||||
|
args.add("legacy");
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> buildVariableMap(LaunchOptions options) {
|
||||||
|
Map<String, String> vars = new HashMap<>();
|
||||||
|
|
||||||
|
Path gameDir = instance.getPath().toAbsolutePath();
|
||||||
|
Path assetsDir = gameDir.resolve("assets");
|
||||||
|
Path nativesDir = gameDir.resolve("natives");
|
||||||
|
Path librariesDir = gameDir.resolve("libraries");
|
||||||
|
|
||||||
|
vars.put("version_name", instance.getName());
|
||||||
|
vars.put("game_directory", gameDir.toString());
|
||||||
|
vars.put("assets_root", assetsDir.toString());
|
||||||
|
vars.put("assets_index_name", instance.getAssetIndex() != null ? instance.getAssetIndex() : instance.getMinecraftVersion());
|
||||||
|
vars.put("auth_player_name", options.getUsername() != null ? options.getUsername() : "Player");
|
||||||
|
vars.put("auth_access_token", options.getAccessToken() != null ? options.getAccessToken() : "0");
|
||||||
|
vars.put("auth_uuid", options.getUuid() != null ? options.getUuid() : "00000000-0000-0000-0000-000000000000");
|
||||||
|
vars.put("auth_xuid", "0");
|
||||||
|
vars.put("user_type", "legacy");
|
||||||
|
vars.put("version_type", "release");
|
||||||
|
vars.put("natives_directory", nativesDir.toString());
|
||||||
|
vars.put("library_directory", librariesDir.toString());
|
||||||
|
vars.put("launcher_name", "ZernMC");
|
||||||
|
vars.put("launcher_version", "1.0");
|
||||||
|
vars.put("classpath_separator", System.getProperty("os.name").toLowerCase().contains("win") ? ";" : ":");
|
||||||
|
vars.put("resolution_width", String.valueOf(options.getWidth() > 0 ? options.getWidth() : 1920));
|
||||||
|
vars.put("resolution_height", String.valueOf(options.getHeight() > 0 ? options.getHeight() : 1080));
|
||||||
|
vars.put("game_directory", gameDir.toString());
|
||||||
|
|
||||||
String loaderType = instance.getLoaderType().toLowerCase();
|
String loaderType = instance.getLoaderType().toLowerCase();
|
||||||
|
if ("forge".equals(loaderType)) {
|
||||||
if ("fabric".equals(loaderType)) {
|
vars.put("forge_version", instance.getLoaderVersion() != null ? instance.getLoaderVersion() : "");
|
||||||
jvmArgs.add("--add-modules=ALL-MODULE-PATH");
|
} else if ("neoforge".equals(loaderType)) {
|
||||||
jvmArgs.add("--add-opens=java.base/java.io=ALL-UNNAMED");
|
vars.put("neoforge_version", instance.getLoaderVersion() != null ? instance.getLoaderVersion() : "");
|
||||||
jvmArgs.add("--add-opens=java.base/java.util=ALL-UNNAMED");
|
vars.put("fml.neoForgeVersion", instance.getLoaderVersion() != null ? instance.getLoaderVersion() : "");
|
||||||
jvmArgs.add("--add-opens=java.base/java.lang=ALL-UNNAMED");
|
vars.put("fml.neoForgeGroup", "net.neoforged");
|
||||||
jvmArgs.add("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.nio=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.net=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/sun.nio.ch=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.getExtraJvmArgs() != null && !options.getExtraJvmArgs().isEmpty()) {
|
return vars;
|
||||||
jvmArgs.addAll(options.getExtraJvmArgs());
|
}
|
||||||
|
|
||||||
|
private String resolveVariable(String raw, Map<String, String> vars) {
|
||||||
|
if (!raw.contains("${")) return raw;
|
||||||
|
String result = raw;
|
||||||
|
for (Map.Entry<String, String> entry : vars.entrySet()) {
|
||||||
|
result = result.replace("${" + entry.getKey() + "}", entry.getValue());
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
return jvmArgs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getForgeJvmArguments() {
|
private String buildClasspathFromManifest(VersionManifest manifest) throws Exception {
|
||||||
List<String> jvmArgs = new ArrayList<>();
|
|
||||||
|
|
||||||
jvmArgs.add("--add-modules=ALL-MODULE-PATH");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.util.jar=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.io=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.nio=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.net=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.util=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/sun.nio.ch=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED");
|
|
||||||
|
|
||||||
jvmArgs.add("-Dforge.logging.console.level=debug");
|
|
||||||
jvmArgs.add("-Dforge.logging.mojang.level=info");
|
|
||||||
jvmArgs.add("-DignoreList=bootstraplauncher,securejarhandler,asm-commons,asm-util,asm-analysis,asm-tree,asm,JarJarFileSystems,client-extra,fmlcore,javafmllanguage,lowcodelanguage,mclanguage,forge-");
|
|
||||||
jvmArgs.add("-DmergeModules=jna-5.10.0.jar,jna-platform-5.10.0.jar");
|
|
||||||
|
|
||||||
return jvmArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildClasspath() throws Exception {
|
|
||||||
List<String> paths = new ArrayList<>();
|
List<String> paths = new ArrayList<>();
|
||||||
|
Path librariesDir = instance.getPath().resolve("libraries");
|
||||||
|
|
||||||
|
for (VersionManifest.Library lib : manifest.getLibraries()) {
|
||||||
|
Path libPath = librariesDir.resolve(lib.artifactPath);
|
||||||
|
if (Files.exists(libPath)) {
|
||||||
|
paths.add(libPath.toAbsolutePath().toString());
|
||||||
|
} else {
|
||||||
|
String mavenPath = mavenToPath(lib.name);
|
||||||
|
Path fallbackPath = librariesDir.resolve(mavenPath);
|
||||||
|
if (Files.exists(fallbackPath)) {
|
||||||
|
paths.add(fallbackPath.toAbsolutePath().toString());
|
||||||
|
} else {
|
||||||
|
System.out.println(ZAnsi.yellow(" Библиотека не найдена: " + lib.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Path versionJar = findVersionJar();
|
||||||
|
if (versionJar != null) {
|
||||||
|
paths.add(0, versionJar.toAbsolutePath().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
String separator = System.getProperty("os.name").toLowerCase().contains("win") ? ";" : ":";
|
||||||
|
return String.join(separator, paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildVanillaClasspath() throws Exception {
|
||||||
|
List<String> paths = new ArrayList<>();
|
||||||
String versionId = getVersionId();
|
String versionId = getVersionId();
|
||||||
|
|
||||||
Path versionJar = instance.getPath()
|
Path versionJar = instance.getPath()
|
||||||
.resolve("versions")
|
.resolve("versions")
|
||||||
.resolve(versionId)
|
.resolve(versionId)
|
||||||
@@ -158,383 +316,111 @@ public class LaunchCommandBuilder {
|
|||||||
return String.join(separator, paths);
|
return String.join(separator, paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildForgeClasspath() throws Exception {
|
private Path findVersionJar() {
|
||||||
List<String> paths = new ArrayList<>();
|
|
||||||
|
|
||||||
String versionId = getVersionId();
|
String versionId = getVersionId();
|
||||||
String mcVersion = instance.getMinecraftVersion();
|
Path versionsDir = instance.getPath().resolve("versions");
|
||||||
String forgeVersion = instance.getLoaderVersion();
|
|
||||||
|
|
||||||
Path librariesDir = instance.getPath().resolve("libraries");
|
Path[] candidates = {
|
||||||
if (Files.exists(librariesDir)) {
|
versionsDir.resolve(versionId).resolve(versionId + ".jar"),
|
||||||
try (var stream = Files.walk(librariesDir)) {
|
versionsDir.resolve(instance.getMinecraftVersion()).resolve(instance.getMinecraftVersion() + ".jar")
|
||||||
stream.filter(p -> p.toString().endsWith(".jar"))
|
};
|
||||||
.map(p -> p.toAbsolutePath().toString())
|
|
||||||
.forEach(paths::add);
|
for (Path candidate : candidates) {
|
||||||
|
if (Files.exists(candidate)) {
|
||||||
|
return candidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Path versionJar = instance.getPath()
|
try {
|
||||||
.resolve("versions")
|
if (Files.exists(versionsDir)) {
|
||||||
.resolve(versionId)
|
try (var stream = Files.list(versionsDir)) {
|
||||||
.resolve(versionId + ".jar");
|
return stream
|
||||||
if (Files.exists(versionJar)) {
|
.filter(Files::isDirectory)
|
||||||
paths.add(0, versionJar.toAbsolutePath().toString());
|
.filter(dir -> dir.getFileName().toString().contains("forge") ||
|
||||||
} else {
|
dir.getFileName().toString().contains("neoforge"))
|
||||||
Path vanillaJar = instance.getPath()
|
.filter(dir -> dir.getFileName().toString().contains(instance.getMinecraftVersion()))
|
||||||
.resolve("versions")
|
.findFirst()
|
||||||
.resolve(mcVersion)
|
.map(dir -> dir.resolve(dir.getFileName().toString() + ".jar"))
|
||||||
.resolve(mcVersion + ".jar");
|
.filter(Files::exists)
|
||||||
if (Files.exists(vanillaJar)) {
|
.orElse(null);
|
||||||
paths.add(0, vanillaJar.toAbsolutePath().toString());
|
}
|
||||||
}
|
}
|
||||||
}
|
} catch (Exception ignored) {}
|
||||||
|
|
||||||
Path forgeUniversal = instance.getPath()
|
return null;
|
||||||
.resolve("libraries")
|
|
||||||
.resolve("net")
|
|
||||||
.resolve("minecraftforge")
|
|
||||||
.resolve("forge")
|
|
||||||
.resolve(mcVersion + "-" + forgeVersion)
|
|
||||||
.resolve("forge-" + mcVersion + "-" + forgeVersion + "-universal.jar");
|
|
||||||
if (Files.exists(forgeUniversal)) {
|
|
||||||
paths.add(forgeUniversal.toAbsolutePath().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
Path forgeClient = instance.getPath()
|
|
||||||
.resolve("libraries")
|
|
||||||
.resolve("net")
|
|
||||||
.resolve("minecraftforge")
|
|
||||||
.resolve("forge")
|
|
||||||
.resolve(mcVersion + "-" + forgeVersion)
|
|
||||||
.resolve("forge-" + mcVersion + "-" + forgeVersion + "-client.jar");
|
|
||||||
if (Files.exists(forgeClient)) {
|
|
||||||
paths.add(forgeClient.toAbsolutePath().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] forgeModules = {"fmlcore", "javafmllanguage", "lowcodelanguage", "mclanguage"};
|
|
||||||
for (String module : forgeModules) {
|
|
||||||
Path modulePath = instance.getPath()
|
|
||||||
.resolve("libraries")
|
|
||||||
.resolve("net")
|
|
||||||
.resolve("minecraftforge")
|
|
||||||
.resolve(module)
|
|
||||||
.resolve(mcVersion + "-" + forgeVersion)
|
|
||||||
.resolve(module + "-" + mcVersion + "-" + forgeVersion + ".jar");
|
|
||||||
if (Files.exists(modulePath)) {
|
|
||||||
paths.add(modulePath.toAbsolutePath().toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String separator = System.getProperty("os.name").toLowerCase().contains("win") ? ";" : ":";
|
|
||||||
return String.join(separator, paths);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMainClass() {
|
private String mavenToPath(String mavenName) {
|
||||||
|
String[] parts = mavenName.split(":");
|
||||||
|
if (parts.length < 3) return mavenName;
|
||||||
|
|
||||||
|
String group = parts[0].replace('.', '/');
|
||||||
|
String artifact = parts[1];
|
||||||
|
String version = parts[2];
|
||||||
|
|
||||||
|
if (parts.length == 4) {
|
||||||
|
String classifier = parts[3];
|
||||||
|
return group + "/" + artifact + "/" + version + "/" + artifact + "-" + version + "-" + classifier + ".jar";
|
||||||
|
}
|
||||||
|
|
||||||
|
return group + "/" + artifact + "/" + version + "/" + artifact + "-" + version + ".jar";
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getJvmArguments(LaunchOptions options) {
|
||||||
|
List<String> jvmArgs = new ArrayList<>();
|
||||||
|
|
||||||
|
int ramMB = options.getMaxMemory() > 0 ? options.getMaxMemory() : 4096;
|
||||||
|
jvmArgs.add("-Xmx" + ramMB + "M");
|
||||||
|
jvmArgs.add("-Xms" + Math.max(512, ramMB / 2) + "M");
|
||||||
|
|
||||||
|
jvmArgs.add("-XX:+UseG1GC");
|
||||||
|
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
|
||||||
|
jvmArgs.add("-XX:G1NewSizePercent=20");
|
||||||
|
jvmArgs.add("-XX:G1ReservePercent=20");
|
||||||
|
jvmArgs.add("-XX:MaxGCPauseMillis=50");
|
||||||
|
jvmArgs.add("-XX:G1HeapRegionSize=32M");
|
||||||
|
|
||||||
String loaderType = instance.getLoaderType().toLowerCase();
|
String loaderType = instance.getLoaderType().toLowerCase();
|
||||||
|
|
||||||
if ("fabric".equals(loaderType)) {
|
if ("fabric".equals(loaderType)) {
|
||||||
return "net.fabricmc.loader.impl.launch.knot.KnotClient";
|
jvmArgs.add("--add-modules=ALL-MODULE-PATH");
|
||||||
}
|
jvmArgs.add("--add-opens=java.base/java.io=ALL-UNNAMED");
|
||||||
else if ("forge".equals(loaderType)) {
|
jvmArgs.add("--add-opens=java.base/java.util=ALL-UNNAMED");
|
||||||
return "cpw.mods.modlauncher.Launcher";
|
jvmArgs.add("--add-opens=java.base/java.lang=ALL-UNNAMED");
|
||||||
}
|
jvmArgs.add("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED");
|
||||||
else {
|
jvmArgs.add("--add-opens=java.base/java.nio=ALL-UNNAMED");
|
||||||
return "net.minecraft.client.main.Main";
|
jvmArgs.add("--add-opens=java.base/java.net=ALL-UNNAMED");
|
||||||
}
|
jvmArgs.add("--add-opens=java.base/sun.nio.ch=ALL-UNNAMED");
|
||||||
}
|
jvmArgs.add("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED");
|
||||||
|
jvmArgs.add("--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED");
|
||||||
/**
|
} else if ("forge".equals(loaderType)) {
|
||||||
* ИСПРАВЛЕНО: используем instance.getAssetIndex() вместо minecraftVersion
|
jvmArgs.add("--add-modules=ALL-MODULE-PATH");
|
||||||
*/
|
jvmArgs.add("--add-opens=java.base/java.util.jar=ALL-UNNAMED");
|
||||||
private List<String> getMinecraftArguments(LaunchOptions options) {
|
jvmArgs.add("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED");
|
||||||
List<String> args = new ArrayList<>();
|
jvmArgs.add("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED");
|
||||||
|
jvmArgs.add("--add-opens=java.base/java.io=ALL-UNNAMED");
|
||||||
args.add("--version");
|
jvmArgs.add("--add-opens=java.base/java.nio=ALL-UNNAMED");
|
||||||
args.add(instance.getName());
|
jvmArgs.add("--add-opens=java.base/java.net=ALL-UNNAMED");
|
||||||
|
jvmArgs.add("--add-opens=java.base/java.util=ALL-UNNAMED");
|
||||||
args.add("--gameDir");
|
jvmArgs.add("--add-opens=java.base/sun.nio.ch=ALL-UNNAMED");
|
||||||
args.add(instance.getPath().toAbsolutePath().toString());
|
jvmArgs.add("--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED");
|
||||||
|
} else if ("neoforge".equals(loaderType)) {
|
||||||
args.add("--assetsDir");
|
jvmArgs.add("--add-modules=ALL-MODULE-PATH");
|
||||||
args.add(instance.getPath().resolve("assets").toAbsolutePath().toString());
|
jvmArgs.add("--add-opens=java.base/java.util.jar=ALL-UNNAMED");
|
||||||
|
jvmArgs.add("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED");
|
||||||
// FIXED: Используем правильный assetIndex
|
jvmArgs.add("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED");
|
||||||
args.add("--assetIndex");
|
jvmArgs.add("--add-opens=java.base/java.io=ALL-UNNAMED");
|
||||||
String assetIndex = instance.getAssetIndex();
|
jvmArgs.add("--add-opens=java.base/java.nio=ALL-UNNAMED");
|
||||||
if (assetIndex == null || assetIndex.isEmpty()) {
|
jvmArgs.add("--add-opens=java.base/java.net=ALL-UNNAMED");
|
||||||
assetIndex = instance.getMinecraftVersion();
|
jvmArgs.add("--add-opens=java.base/java.util=ALL-UNNAMED");
|
||||||
System.out.println(ZAnsi.yellow("Asset index не найден, использую версию: " + assetIndex));
|
jvmArgs.add("--add-opens=java.base/sun.nio.ch=ALL-UNNAMED");
|
||||||
} else {
|
jvmArgs.add("--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED");
|
||||||
System.out.println(ZAnsi.green("Использую asset index: " + assetIndex));
|
|
||||||
}
|
|
||||||
args.add(assetIndex);
|
|
||||||
|
|
||||||
args.add("--username");
|
|
||||||
args.add(options.getUsername() != null ? options.getUsername() : "Player");
|
|
||||||
|
|
||||||
args.add("--accessToken");
|
|
||||||
args.add(options.getAccessToken() != null ? options.getAccessToken() : "0");
|
|
||||||
|
|
||||||
args.add("--uuid");
|
|
||||||
args.add(options.getUuid() != null ? options.getUuid() : "00000000-0000-0000-0000-000000000000");
|
|
||||||
|
|
||||||
args.add("--userType");
|
|
||||||
args.add("legacy");
|
|
||||||
|
|
||||||
if (options.getWidth() > 0) {
|
|
||||||
args.add("--width");
|
|
||||||
args.add(String.valueOf(options.getWidth()));
|
|
||||||
}
|
|
||||||
if (options.getHeight() > 0) {
|
|
||||||
args.add("--height");
|
|
||||||
args.add(String.valueOf(options.getHeight()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
if (options.getExtraJvmArgs() != null && !options.getExtraJvmArgs().isEmpty()) {
|
||||||
}
|
jvmArgs.addAll(options.getExtraJvmArgs());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ИСПРАВЛЕНО: для Forge тоже используем правильный assetIndex
|
|
||||||
*/
|
|
||||||
private List<String> getForgeArguments(LaunchOptions options) {
|
|
||||||
List<String> args = new ArrayList<>();
|
|
||||||
|
|
||||||
args.add("--launchTarget");
|
|
||||||
args.add("forgeclient");
|
|
||||||
|
|
||||||
args.add("--fml.forgeVersion");
|
|
||||||
args.add(instance.getLoaderVersion());
|
|
||||||
|
|
||||||
args.add("--fml.mcVersion");
|
|
||||||
args.add(instance.getMinecraftVersion());
|
|
||||||
|
|
||||||
args.add("--fml.forgeGroup");
|
|
||||||
args.add("net.minecraftforge");
|
|
||||||
|
|
||||||
args.add("--gameDir");
|
|
||||||
args.add(instance.getPath().toAbsolutePath().toString());
|
|
||||||
|
|
||||||
args.add("--assetsDir");
|
|
||||||
args.add(instance.getPath().resolve("assets").toAbsolutePath().toString());
|
|
||||||
|
|
||||||
args.add("--assetIndex");
|
|
||||||
String assetIndex = instance.getAssetIndex();
|
|
||||||
if (assetIndex == null || assetIndex.isEmpty()) {
|
|
||||||
assetIndex = instance.getMinecraftVersion();
|
|
||||||
}
|
|
||||||
args.add(assetIndex);
|
|
||||||
|
|
||||||
args.add("--username");
|
|
||||||
args.add(options.getUsername() != null ? options.getUsername() : "Player");
|
|
||||||
|
|
||||||
args.add("--accessToken");
|
|
||||||
args.add(options.getAccessToken() != null ? options.getAccessToken() : "0");
|
|
||||||
|
|
||||||
args.add("--uuid");
|
|
||||||
args.add(options.getUuid() != null ? options.getUuid() : "00000000-0000-0000-0000-000000000000");
|
|
||||||
|
|
||||||
args.add("--userType");
|
|
||||||
args.add("legacy");
|
|
||||||
|
|
||||||
if (options.getWidth() > 0) {
|
|
||||||
args.add("--width");
|
|
||||||
args.add(String.valueOf(options.getWidth()));
|
|
||||||
}
|
|
||||||
if (options.getHeight() > 0) {
|
|
||||||
args.add("--height");
|
|
||||||
args.add(String.valueOf(options.getHeight()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getNeoForgeJvmArguments() {
|
|
||||||
List<String> jvmArgs = new ArrayList<>();
|
|
||||||
|
|
||||||
jvmArgs.add("--add-modules=ALL-MODULE-PATH");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.util.jar=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.lang.invoke=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.io=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.nio=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.net=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/java.util=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=java.base/sun.nio.ch=ALL-UNNAMED");
|
|
||||||
jvmArgs.add("--add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED");
|
|
||||||
|
|
||||||
jvmArgs.add("-Dneoforge.logging.console.level=debug");
|
|
||||||
jvmArgs.add("-Dneoforge.logging.mojang.level=info");
|
|
||||||
|
|
||||||
String mcVersion = instance.getMinecraftVersion();
|
|
||||||
if (!mcVersion.equals("1.20.1")) {
|
|
||||||
jvmArgs.add("-DignoreList=bootstraplauncher,securejarhandler,asm-commons,asm-util,asm-analysis,asm-tree,asm,JarJarFileSystems,client-extra,neoforge-");
|
|
||||||
jvmArgs.add("-DmergeModules=jna-5.10.0.jar,jna-platform-5.10.0.jar");
|
|
||||||
jvmArgs.add("-DlibraryDirectory=libraries");
|
|
||||||
}
|
|
||||||
|
|
||||||
return jvmArgs;
|
return jvmArgs;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private String buildNeoForgeClasspath() throws Exception {
|
|
||||||
List<String> paths = new ArrayList<>();
|
|
||||||
|
|
||||||
String versionId = getVersionId();
|
|
||||||
String mcVersion = instance.getMinecraftVersion();
|
|
||||||
String neoForgeVersion = instance.getLoaderVersion();
|
|
||||||
|
|
||||||
Path librariesDir = instance.getPath().resolve("libraries");
|
|
||||||
if (Files.exists(librariesDir)) {
|
|
||||||
try (var stream = Files.walk(librariesDir)) {
|
|
||||||
stream.filter(p -> p.toString().endsWith(".jar"))
|
|
||||||
.map(p -> p.toAbsolutePath().toString())
|
|
||||||
.forEach(paths::add);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Path versionJar = instance.getPath()
|
|
||||||
.resolve("versions")
|
|
||||||
.resolve(versionId)
|
|
||||||
.resolve(versionId + ".jar");
|
|
||||||
if (Files.exists(versionJar)) {
|
|
||||||
paths.add(0, versionJar.toAbsolutePath().toString());
|
|
||||||
} else {
|
|
||||||
Path vanillaJar = instance.getPath()
|
|
||||||
.resolve("versions")
|
|
||||||
.resolve(mcVersion)
|
|
||||||
.resolve(mcVersion + ".jar");
|
|
||||||
if (Files.exists(vanillaJar)) {
|
|
||||||
paths.add(0, vanillaJar.toAbsolutePath().toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String mavenArtifact = mcVersion.equals("1.20.1") ? "forge" : "neoforge";
|
|
||||||
|
|
||||||
Path neoForgeUniversal = instance.getPath()
|
|
||||||
.resolve("libraries")
|
|
||||||
.resolve("net")
|
|
||||||
.resolve("neoforged")
|
|
||||||
.resolve(mavenArtifact)
|
|
||||||
.resolve(neoForgeVersion)
|
|
||||||
.resolve(mavenArtifact + "-" + neoForgeVersion + "-universal.jar");
|
|
||||||
if (Files.exists(neoForgeUniversal)) {
|
|
||||||
paths.add(neoForgeUniversal.toAbsolutePath().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
Path neoForgeClient = instance.getPath()
|
|
||||||
.resolve("libraries")
|
|
||||||
.resolve("net")
|
|
||||||
.resolve("neoforged")
|
|
||||||
.resolve(mavenArtifact)
|
|
||||||
.resolve(neoForgeVersion)
|
|
||||||
.resolve(mavenArtifact + "-" + neoForgeVersion + "-client.jar");
|
|
||||||
if (Files.exists(neoForgeClient)) {
|
|
||||||
paths.add(neoForgeClient.toAbsolutePath().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
String separator = System.getProperty("os.name").toLowerCase().contains("win") ? ";" : ":";
|
|
||||||
return String.join(separator, paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getNeoForgeMainClass() {
|
|
||||||
String mcVersion = instance.getMinecraftVersion();
|
|
||||||
if (mcVersion.equals("1.20.1")) {
|
|
||||||
return "cpw.mods.modlauncher.Launcher";
|
|
||||||
}
|
|
||||||
return "io.neoforged.neoforgespi.CoreMod";
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getNeoForgeArguments(LaunchOptions options) {
|
|
||||||
List<String> args = new ArrayList<>();
|
|
||||||
String mcVersion = instance.getMinecraftVersion();
|
|
||||||
|
|
||||||
if (mcVersion.equals("1.20.1")) {
|
|
||||||
args.add("--launchTarget");
|
|
||||||
args.add("forgeclient");
|
|
||||||
args.add("--fml.forgeVersion");
|
|
||||||
args.add(instance.getLoaderVersion());
|
|
||||||
args.add("--fml.mcVersion");
|
|
||||||
args.add(mcVersion);
|
|
||||||
args.add("--fml.forgeGroup");
|
|
||||||
args.add("net.neoforged");
|
|
||||||
} else {
|
|
||||||
args.add("--launchTarget");
|
|
||||||
args.add("neoforgeclient");
|
|
||||||
args.add("--fml.neoForgeVersion");
|
|
||||||
args.add(instance.getLoaderVersion());
|
|
||||||
args.add("--fml.mcVersion");
|
|
||||||
args.add(mcVersion);
|
|
||||||
args.add("--fml.neoForgeGroup");
|
|
||||||
args.add("net.neoforged");
|
|
||||||
}
|
|
||||||
|
|
||||||
args.add("--gameDir");
|
|
||||||
args.add(instance.getPath().toAbsolutePath().toString());
|
|
||||||
|
|
||||||
args.add("--assetsDir");
|
|
||||||
args.add(instance.getPath().resolve("assets").toAbsolutePath().toString());
|
|
||||||
|
|
||||||
args.add("--assetIndex");
|
|
||||||
String assetIndex = instance.getAssetIndex();
|
|
||||||
if (assetIndex == null || assetIndex.isEmpty()) {
|
|
||||||
assetIndex = mcVersion;
|
|
||||||
}
|
|
||||||
args.add(assetIndex);
|
|
||||||
|
|
||||||
args.add("--username");
|
|
||||||
args.add(options.getUsername() != null ? options.getUsername() : "Player");
|
|
||||||
|
|
||||||
args.add("--accessToken");
|
|
||||||
args.add(options.getAccessToken() != null ? options.getAccessToken() : "0");
|
|
||||||
|
|
||||||
args.add("--uuid");
|
|
||||||
args.add(options.getUuid() != null ? options.getUuid() : "00000000-0000-0000-0000-000000000000");
|
|
||||||
|
|
||||||
args.add("--userType");
|
|
||||||
args.add("legacy");
|
|
||||||
|
|
||||||
if (options.getWidth() > 0) {
|
|
||||||
args.add("--width");
|
|
||||||
args.add(String.valueOf(options.getWidth()));
|
|
||||||
}
|
|
||||||
if (options.getHeight() > 0) {
|
|
||||||
args.add("--height");
|
|
||||||
args.add(String.valueOf(options.getHeight()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ИСПРАВЛЕНО: для Fabric используем сохраненный fabricVersionId
|
|
||||||
*/
|
|
||||||
private String getVersionId() {
|
|
||||||
String loaderType = instance.getLoaderType().toLowerCase();
|
|
||||||
String mcVersion = instance.getMinecraftVersion();
|
|
||||||
String loaderVer = instance.getLoaderVersion();
|
|
||||||
|
|
||||||
if ("vanilla".equals(loaderType)) {
|
|
||||||
return mcVersion;
|
|
||||||
}
|
|
||||||
else if ("fabric".equals(loaderType)) {
|
|
||||||
// Используем сохраненный fabricVersionId если есть
|
|
||||||
String fabricId = instance.getFabricVersionId();
|
|
||||||
if (fabricId != null && !fabricId.isEmpty()) {
|
|
||||||
return fabricId;
|
|
||||||
}
|
|
||||||
// fallback
|
|
||||||
return "fabric-loader-" + loaderVer + "-" + mcVersion;
|
|
||||||
}
|
|
||||||
else if ("forge".equals(loaderType)) {
|
|
||||||
return mcVersion + "-forge-" + loaderVer;
|
|
||||||
}
|
|
||||||
else if ("neoforge".equals(loaderType)) {
|
|
||||||
if (mcVersion.equals("1.20.1")) {
|
|
||||||
return mcVersion + "-neoforge-" + loaderVer;
|
|
||||||
}
|
|
||||||
return "neoforge-" + loaderVer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mcVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
package me.sashegdev.zernmc.launcher.minecraft.launch;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class VersionManifest {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final String mainClass;
|
||||||
|
private final String assetIndexId;
|
||||||
|
private final List<String> jvmArguments;
|
||||||
|
private final List<String> gameArguments;
|
||||||
|
private final List<Library> libraries;
|
||||||
|
|
||||||
|
public VersionManifest(JSONObject json) {
|
||||||
|
this.id = json.getString("id");
|
||||||
|
this.mainClass = json.getString("mainClass");
|
||||||
|
|
||||||
|
if (json.has("assetIndex")) {
|
||||||
|
JSONObject ai = json.getJSONObject("assetIndex");
|
||||||
|
this.assetIndexId = ai.has("id") ? ai.getString("id") : "unknown";
|
||||||
|
} else {
|
||||||
|
this.assetIndexId = "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.jvmArguments = parseArguments(json, "jvm");
|
||||||
|
this.gameArguments = parseArguments(json, "game");
|
||||||
|
this.libraries = parseLibraries(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() { return id; }
|
||||||
|
public String getMainClass() { return mainClass; }
|
||||||
|
public String getAssetIndexId() { return assetIndexId; }
|
||||||
|
public List<String> getJvmArguments() { return jvmArguments; }
|
||||||
|
public List<String> getGameArguments() { return gameArguments; }
|
||||||
|
public List<Library> getLibraries() { return libraries; }
|
||||||
|
|
||||||
|
private List<String> parseArguments(JSONObject json, String type) {
|
||||||
|
List<String> args = new ArrayList<>();
|
||||||
|
if (!json.has("arguments")) return args;
|
||||||
|
|
||||||
|
JSONObject arguments = json.getJSONObject("arguments");
|
||||||
|
if (!arguments.has(type)) return args;
|
||||||
|
|
||||||
|
JSONArray arr = arguments.getJSONArray(type);
|
||||||
|
for (int i = 0; i < arr.length(); i++) {
|
||||||
|
Object item = arr.get(i);
|
||||||
|
if (item instanceof String) {
|
||||||
|
args.add((String) item);
|
||||||
|
} else if (item instanceof JSONObject) {
|
||||||
|
JSONObject ruleObj = (JSONObject) item;
|
||||||
|
if (ruleMatches(ruleObj)) {
|
||||||
|
Object value = ruleObj.get("value");
|
||||||
|
if (value instanceof String) {
|
||||||
|
args.add((String) value);
|
||||||
|
} else if (value instanceof JSONArray) {
|
||||||
|
JSONArray valArr = (JSONArray) value;
|
||||||
|
for (int j = 0; j < valArr.length(); j++) {
|
||||||
|
args.add(valArr.getString(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean ruleMatches(JSONObject ruleObj) {
|
||||||
|
JSONArray rules = ruleObj.getJSONArray("rules");
|
||||||
|
boolean result = false;
|
||||||
|
for (int i = 0; i < rules.length(); i++) {
|
||||||
|
JSONObject rule = rules.getJSONObject(i);
|
||||||
|
String action = rule.getString("action");
|
||||||
|
boolean matches = true;
|
||||||
|
|
||||||
|
if (rule.has("os")) {
|
||||||
|
JSONObject os = rule.getJSONObject("os");
|
||||||
|
String osName = System.getProperty("os.name").toLowerCase();
|
||||||
|
if (os.has("name")) {
|
||||||
|
String reqName = os.getString("name").toLowerCase();
|
||||||
|
if (reqName.equals("windows") && !osName.contains("win")) matches = false;
|
||||||
|
else if (reqName.equals("linux") && !osName.contains("linux") && !osName.contains("nix")) matches = false;
|
||||||
|
else if (reqName.equals("osx") && !osName.contains("mac")) matches = false;
|
||||||
|
}
|
||||||
|
if (os.has("arch")) {
|
||||||
|
String reqArch = os.getString("arch");
|
||||||
|
String osArch = System.getProperty("os.arch");
|
||||||
|
if (!reqArch.equals(osArch)) matches = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule.has("features")) {
|
||||||
|
JSONObject features = rule.getJSONObject("features");
|
||||||
|
for (String key : features.keySet()) {
|
||||||
|
if (key.startsWith("is_demo_user") || key.startsWith("has_custom_resolution")) continue;
|
||||||
|
matches = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("allow".equals(action) && matches) {
|
||||||
|
result = true;
|
||||||
|
} else if ("disallow".equals(action) && matches) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Library> parseLibraries(JSONObject json) {
|
||||||
|
List<Library> libs = new ArrayList<>();
|
||||||
|
if (!json.has("libraries")) return libs;
|
||||||
|
|
||||||
|
JSONArray arr = json.getJSONArray("libraries");
|
||||||
|
for (int i = 0; i < arr.length(); i++) {
|
||||||
|
JSONObject libJson = arr.getJSONObject(i);
|
||||||
|
if (libJson.has("downloads") && libJson.getJSONObject("downloads").has("artifact")) {
|
||||||
|
String name = libJson.getString("name");
|
||||||
|
String artifactPath = libJson.getJSONObject("downloads").getJSONObject("artifact").getString("path");
|
||||||
|
Library lib = new Library(name, artifactPath);
|
||||||
|
|
||||||
|
if (libJson.has("natives")) {
|
||||||
|
JSONObject natives = libJson.getJSONObject("natives");
|
||||||
|
for (String key : natives.keySet()) {
|
||||||
|
String osKey = key.toLowerCase();
|
||||||
|
lib.natives.put(osKey, natives.getString(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (libJson.has("rules")) {
|
||||||
|
JSONObject dummyObj = new JSONObject();
|
||||||
|
dummyObj.put("rules", libJson.getJSONArray("rules"));
|
||||||
|
dummyObj.put("value", "");
|
||||||
|
if (ruleMatches(dummyObj)) {
|
||||||
|
libs.add(lib);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
libs.add(lib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return libs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Library {
|
||||||
|
public final String name;
|
||||||
|
public final String artifactPath;
|
||||||
|
public final Map<String, String> natives = new HashMap<>();
|
||||||
|
|
||||||
|
public Library(String name, String artifactPath) {
|
||||||
|
this.name = name;
|
||||||
|
this.artifactPath = artifactPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSimpleName() {
|
||||||
|
return name.substring(name.indexOf(':') + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user