feat: add NeoForge support, fix Forge installPack bug, update server proxy
- Fix MinecraftLib.installPack() returning false for Forge (was dead code) - Add NeoForgeInstaller.java with installer download and execution - Update LaunchCommandBuilder with NeoForge JVM args, classpath, launch args - Update LaunchMenu with NeoForge option, version selector, support check - Update Instance.java loader type comment (vanilla, fabric, forge, neoforge) - Update PackDownloader to handle neoforge loader type - Update ZHttpClient with NEOFORGE_MAVEN service type and detection - Add NeoForge proxy endpoints (/proxy/neoforge/versions, /proxy/neoforge/maven) - Add maven.neoforged.net to proxy allowed_domains - Add asset_index to PackMeta model and pack_manager scanning - Include asset_index in /packs list endpoint response
This commit is contained in:
@@ -370,6 +370,8 @@ public class LaunchMenu {
|
|||||||
String newLoaderVersion;
|
String newLoaderVersion;
|
||||||
if ("fabric".equalsIgnoreCase(currentLoader)) {
|
if ("fabric".equalsIgnoreCase(currentLoader)) {
|
||||||
newLoaderVersion = askFabricLoaderVersion();
|
newLoaderVersion = askFabricLoaderVersion();
|
||||||
|
} else if ("neoforge".equalsIgnoreCase(currentLoader)) {
|
||||||
|
newLoaderVersion = askNeoForgeVersion(mcVersion);
|
||||||
} else {
|
} else {
|
||||||
newLoaderVersion = askForgeVersion(mcVersion);
|
newLoaderVersion = askForgeVersion(mcVersion);
|
||||||
}
|
}
|
||||||
@@ -384,6 +386,8 @@ public class LaunchMenu {
|
|||||||
try {
|
try {
|
||||||
if ("fabric".equalsIgnoreCase(currentLoader)) {
|
if ("fabric".equalsIgnoreCase(currentLoader)) {
|
||||||
success = lib.installFabric(mcVersion, newLoaderVersion);
|
success = lib.installFabric(mcVersion, newLoaderVersion);
|
||||||
|
} else if ("neoforge".equalsIgnoreCase(currentLoader)) {
|
||||||
|
success = lib.installNeoForge(mcVersion, newLoaderVersion);
|
||||||
} else {
|
} else {
|
||||||
success = lib.installForge(mcVersion, newLoaderVersion);
|
success = lib.installForge(mcVersion, newLoaderVersion);
|
||||||
}
|
}
|
||||||
@@ -546,11 +550,23 @@ public class LaunchMenu {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String loaderType = selectedLoader.contains("Fabric") ? "fabric" : "forge";
|
String loaderType;
|
||||||
|
if (selectedLoader.contains("Fabric")) {
|
||||||
|
loaderType = "fabric";
|
||||||
|
} else if (selectedLoader.contains("NeoForge")) {
|
||||||
|
loaderType = "neoforge";
|
||||||
|
} else {
|
||||||
|
loaderType = "forge";
|
||||||
|
}
|
||||||
|
|
||||||
String loaderVersion = loaderType.equals("fabric")
|
String loaderVersion;
|
||||||
? askFabricLoaderVersion()
|
if (loaderType.equals("fabric")) {
|
||||||
: askForgeVersion(mcVersion);
|
loaderVersion = askFabricLoaderVersion();
|
||||||
|
} else if (loaderType.equals("neoforge")) {
|
||||||
|
loaderVersion = askNeoForgeVersion(mcVersion);
|
||||||
|
} else {
|
||||||
|
loaderVersion = askForgeVersion(mcVersion);
|
||||||
|
}
|
||||||
|
|
||||||
if (loaderVersion == null) return;
|
if (loaderVersion == null) return;
|
||||||
|
|
||||||
@@ -568,9 +584,14 @@ public class LaunchMenu {
|
|||||||
|
|
||||||
MinecraftLib lib = new MinecraftLib(newInstance);
|
MinecraftLib lib = new MinecraftLib(newInstance);
|
||||||
|
|
||||||
boolean success = loaderType.equals("fabric")
|
boolean success;
|
||||||
? lib.installFabric(mcVersion, loaderVersion)
|
if (loaderType.equals("fabric")) {
|
||||||
: lib.installForge(mcVersion, loaderVersion);
|
success = lib.installFabric(mcVersion, loaderVersion);
|
||||||
|
} else if (loaderType.equals("neoforge")) {
|
||||||
|
success = lib.installNeoForge(mcVersion, loaderVersion);
|
||||||
|
} else {
|
||||||
|
success = lib.installForge(mcVersion, loaderVersion);
|
||||||
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
System.out.println(ZAnsi.brightGreen("\n[OK] Сборка '" + packName + "' успешно установлена!"));
|
System.out.println(ZAnsi.brightGreen("\n[OK] Сборка '" + packName + "' успешно установлена!"));
|
||||||
@@ -585,6 +606,7 @@ public class LaunchMenu {
|
|||||||
List<String> options = new ArrayList<>();
|
List<String> options = new ArrayList<>();
|
||||||
|
|
||||||
if (isFabricSupported(mcVersion)) options.add("Fabric");
|
if (isFabricSupported(mcVersion)) options.add("Fabric");
|
||||||
|
if (isNeoForgeSupported(mcVersion)) options.add("NeoForge");
|
||||||
if (isForgeSupported(mcVersion)) options.add("Forge");
|
if (isForgeSupported(mcVersion)) options.add("Forge");
|
||||||
options.add("Vanilla");
|
options.add("Vanilla");
|
||||||
options.add("Назад");
|
options.add("Назад");
|
||||||
@@ -602,6 +624,12 @@ public class LaunchMenu {
|
|||||||
version.matches("^1\\.20.*") || version.matches("^1\\.21.*");
|
version.matches("^1\\.20.*") || version.matches("^1\\.21.*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isNeoForgeSupported(String version) {
|
||||||
|
return version.matches("^1\\.20\\.[1-9].*") ||
|
||||||
|
version.matches("^1\\.21.*") ||
|
||||||
|
version.matches("^\\d{2}\\..*");
|
||||||
|
}
|
||||||
|
|
||||||
private String askFabricLoaderVersion() throws Exception {
|
private String askFabricLoaderVersion() throws Exception {
|
||||||
System.out.println(ZAnsi.cyan("Получение списка версий Fabric Loader..."));
|
System.out.println(ZAnsi.cyan("Получение списка версий Fabric Loader..."));
|
||||||
List<String> versions = ZHttpClient.getFabricLoaderVersions();
|
List<String> versions = ZHttpClient.getFabricLoaderVersions();
|
||||||
@@ -668,4 +696,75 @@ public class LaunchMenu {
|
|||||||
versions.sort((a, b) -> b.compareTo(a));
|
versions.sort((a, b) -> b.compareTo(a));
|
||||||
return versions;
|
return versions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String askNeoForgeVersion(String mcVersion) throws Exception {
|
||||||
|
System.out.println(ZAnsi.cyan("Получение списка версий NeoForge для " + mcVersion + "..."));
|
||||||
|
|
||||||
|
List<String> allNeoForgeVersions = getAllNeoForgeVersions();
|
||||||
|
|
||||||
|
List<String> compatibleVersions = allNeoForgeVersions.stream()
|
||||||
|
.filter(v -> isNeoForgeVersionCompatible(v, mcVersion))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (compatibleVersions.isEmpty()) {
|
||||||
|
System.out.println(ZAnsi.yellow("Не найдено совместимых версий NeoForge для " + mcVersion));
|
||||||
|
ConsoleUtils.pause();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> options = compatibleVersions.stream()
|
||||||
|
.limit(30)
|
||||||
|
.map(v -> "NeoForge " + v)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
options.add("Назад");
|
||||||
|
|
||||||
|
ArrowMenu menu = new ArrowMenu("Выбор версии NeoForge для " + mcVersion, options);
|
||||||
|
int choice = menu.show();
|
||||||
|
|
||||||
|
if (choice == -1 || choice == options.size() - 1) return null;
|
||||||
|
|
||||||
|
return compatibleVersions.get(choice);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNeoForgeVersionCompatible(String version, String mcVersion) {
|
||||||
|
if (mcVersion.equals("1.20.1")) {
|
||||||
|
return version.startsWith("47.");
|
||||||
|
}
|
||||||
|
String majorMinor = mcVersion.replace("1.", "");
|
||||||
|
String[] parts = majorMinor.split("\\.");
|
||||||
|
int targetMajor = Integer.parseInt(parts[0]);
|
||||||
|
return version.startsWith(targetMajor + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getAllNeoForgeVersions() throws Exception {
|
||||||
|
List<String> versions = new ArrayList<>();
|
||||||
|
|
||||||
|
String[] mavenUrls = {
|
||||||
|
"https://maven.neoforged.net/releases/net/neoforged/neoforge/maven-metadata.xml",
|
||||||
|
"https://maven.neoforged.net/releases/net/neoforged/forge/maven-metadata.xml"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String mavenUrl : mavenUrls) {
|
||||||
|
try {
|
||||||
|
String xml = ZHttpClient.downloadString(mavenUrl);
|
||||||
|
int index = 0;
|
||||||
|
while ((index = xml.indexOf("<version>", index)) != -1) {
|
||||||
|
int start = index + 9;
|
||||||
|
int end = xml.indexOf("</version>", start);
|
||||||
|
if (end == -1) break;
|
||||||
|
|
||||||
|
String version = xml.substring(start, end).trim();
|
||||||
|
if (!versions.contains(version)) {
|
||||||
|
versions.add(version);
|
||||||
|
}
|
||||||
|
index = end;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Skip if one maven doesn't have the artifact
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
versions.sort((a, b) -> b.compareTo(a));
|
||||||
|
return versions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ public class Instance {
|
|||||||
private final Path path;
|
private final Path path;
|
||||||
|
|
||||||
private String minecraftVersion;
|
private String minecraftVersion;
|
||||||
private String loaderType; // vanilla, fabric, forge
|
private String loaderType; // vanilla, fabric, forge, neoforge
|
||||||
private String loaderVersion;
|
private String loaderVersion;
|
||||||
private String assetIndex;
|
private String assetIndex;
|
||||||
private boolean isServerPack; // флаг, что это сборка с сервера
|
private boolean isServerPack; // флаг, что это сборка с сервера
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package me.sashegdev.zernmc.launcher.minecraft;
|
|||||||
|
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.installer.FabricInstaller;
|
import me.sashegdev.zernmc.launcher.minecraft.installer.FabricInstaller;
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.installer.ForgeInstaller;
|
import me.sashegdev.zernmc.launcher.minecraft.installer.ForgeInstaller;
|
||||||
|
import me.sashegdev.zernmc.launcher.minecraft.installer.NeoForgeInstaller;
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.installer.VersionInstaller;
|
import me.sashegdev.zernmc.launcher.minecraft.installer.VersionInstaller;
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.launch.LaunchCommandBuilder;
|
import me.sashegdev.zernmc.launcher.minecraft.launch.LaunchCommandBuilder;
|
||||||
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
||||||
@@ -41,6 +42,11 @@ public class MinecraftLib {
|
|||||||
return installer.install(minecraftVersion, forgeVersion);
|
return installer.install(minecraftVersion, forgeVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean installNeoForge(String minecraftVersion, String neoforgeVersion) throws Exception {
|
||||||
|
NeoForgeInstaller installer = new NeoForgeInstaller(instance);
|
||||||
|
return installer.install(minecraftVersion, neoforgeVersion);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean installFabric(String minecraftVersion, String loaderVersion) throws Exception {
|
public boolean installFabric(String minecraftVersion, String loaderVersion) throws Exception {
|
||||||
FabricInstaller installer = new FabricInstaller(instance);
|
FabricInstaller installer = new FabricInstaller(instance);
|
||||||
boolean success = installer.install(minecraftVersion, loaderVersion);
|
boolean success = installer.install(minecraftVersion, loaderVersion);
|
||||||
@@ -76,8 +82,17 @@ public class MinecraftLib {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if ("forge".equalsIgnoreCase(loaderType)) {
|
} else if ("forge".equalsIgnoreCase(loaderType)) {
|
||||||
System.out.println(ZAnsi.yellow("Forge пока не поддерживается"));
|
boolean forgeInstalled = installForge(minecraftVersion, loaderVersion);
|
||||||
return false;
|
if (!forgeInstalled) {
|
||||||
|
System.out.println(ZAnsi.brightRed("Не удалось установить Forge"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if ("neoforge".equalsIgnoreCase(loaderType)) {
|
||||||
|
boolean neoforgeInstalled = installNeoForge(minecraftVersion, loaderVersion);
|
||||||
|
if (!neoforgeInstalled) {
|
||||||
|
System.out.println(ZAnsi.brightRed("Не удалось установить NeoForge"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. В будущем здесь будет diff и скачивание модов
|
// 3. В будущем здесь будет diff и скачивание модов
|
||||||
@@ -129,7 +144,8 @@ public class MinecraftLib {
|
|||||||
try (var stream = Files.walk(versionsDir)) {
|
try (var stream = Files.walk(versionsDir)) {
|
||||||
stream.filter(Files::isDirectory)
|
stream.filter(Files::isDirectory)
|
||||||
.filter(dir -> dir.getFileName().toString().contains("fabric-loader") ||
|
.filter(dir -> dir.getFileName().toString().contains("fabric-loader") ||
|
||||||
dir.getFileName().toString().contains("forge"))
|
dir.getFileName().toString().contains("forge") ||
|
||||||
|
dir.getFileName().toString().contains("neoforge"))
|
||||||
.filter(dir -> !dir.getFileName().toString().contains(keepVersion))
|
.filter(dir -> !dir.getFileName().toString().contains(keepVersion))
|
||||||
.forEach(this::safeDeleteDirectory);
|
.forEach(this::safeDeleteDirectory);
|
||||||
}
|
}
|
||||||
@@ -163,6 +179,8 @@ public class MinecraftLib {
|
|||||||
deleteAllExcept(libraries.resolve("net/fabricmc/fabric-loader"), currentLoaderVer);
|
deleteAllExcept(libraries.resolve("net/fabricmc/fabric-loader"), currentLoaderVer);
|
||||||
} else if ("forge".equals(loaderType)) {
|
} else if ("forge".equals(loaderType)) {
|
||||||
deleteAllExcept(libraries.resolve("net/minecraftforge/forge"), currentLoaderVer);
|
deleteAllExcept(libraries.resolve("net/minecraftforge/forge"), currentLoaderVer);
|
||||||
|
} else if ("neoforge".equals(loaderType)) {
|
||||||
|
deleteAllExcept(libraries.resolve("net/neoforged/neoforge"), currentLoaderVer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Также чистим versions/ от старых fabric/forge версий
|
// Также чистим versions/ от старых fabric/forge версий
|
||||||
|
|||||||
@@ -157,6 +157,12 @@ public class PackDownloader {
|
|||||||
System.err.println(ZAnsi.brightRed("Не удалось установить Fabric"));
|
System.err.println(ZAnsi.brightRed("Не удалось установить Fabric"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if ("neoforge".equalsIgnoreCase(manifest.getLoaderType())) {
|
||||||
|
boolean success = lib.installNeoForge(manifest.getMinecraftVersion(), manifest.getLoaderVersion());
|
||||||
|
if (!success) {
|
||||||
|
System.err.println(ZAnsi.brightRed("Не удалось установить NeoForge"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else if ("forge".equalsIgnoreCase(manifest.getLoaderType())) {
|
} else if ("forge".equalsIgnoreCase(manifest.getLoaderType())) {
|
||||||
boolean success = lib.installForge(manifest.getMinecraftVersion(), manifest.getLoaderVersion());
|
boolean success = lib.installForge(manifest.getMinecraftVersion(), manifest.getLoaderVersion());
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
|||||||
+271
@@ -0,0 +1,271 @@
|
|||||||
|
package me.sashegdev.zernmc.launcher.minecraft.installer;
|
||||||
|
|
||||||
|
import me.sashegdev.zernmc.launcher.minecraft.Instance;
|
||||||
|
import me.sashegdev.zernmc.launcher.utils.ProgressBar;
|
||||||
|
import me.sashegdev.zernmc.launcher.utils.ZAnsi;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class NeoForgeInstaller {
|
||||||
|
|
||||||
|
private final Instance instance;
|
||||||
|
private final HttpClient httpClient = HttpClient.newBuilder()
|
||||||
|
.connectTimeout(java.time.Duration.ofSeconds(30))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
public NeoForgeInstaller(Instance instance) {
|
||||||
|
this.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean install(String mcVersion, String neoForgeVersion) throws Exception {
|
||||||
|
System.out.println(ZAnsi.cyan("Установка NeoForge " + neoForgeVersion + " для Minecraft " + mcVersion));
|
||||||
|
|
||||||
|
System.out.println(ZAnsi.cyan("Установка базовой версии Minecraft " + mcVersion + "..."));
|
||||||
|
VersionInstaller vanillaInstaller = new VersionInstaller(instance.getPath());
|
||||||
|
String assetIndex = vanillaInstaller.install(mcVersion);
|
||||||
|
|
||||||
|
if (assetIndex == null || assetIndex.isEmpty()) {
|
||||||
|
System.out.println(ZAnsi.brightRed("Не удалось установить базовую версию Minecraft"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.setAssetIndex(assetIndex);
|
||||||
|
createLauncherProfile();
|
||||||
|
|
||||||
|
String mavenGroup = getMavenGroup(mcVersion);
|
||||||
|
String mavenArtifact = getMavenArtifact(mcVersion);
|
||||||
|
|
||||||
|
String installerUrl = "https://maven.neoforged.net/releases/"
|
||||||
|
+ mavenGroup.replace('.', '/') + "/"
|
||||||
|
+ mavenArtifact + "/"
|
||||||
|
+ neoForgeVersion
|
||||||
|
+ "/" + mavenArtifact + "-" + neoForgeVersion + "-installer.jar";
|
||||||
|
|
||||||
|
Path installerJar = instance.getPath().resolve("neoforge-installer.jar");
|
||||||
|
|
||||||
|
System.out.println(ZAnsi.cyan("Скачивание NeoForge Installer..."));
|
||||||
|
downloadFileWithProgress(installerUrl, installerJar);
|
||||||
|
|
||||||
|
System.out.println(ZAnsi.cyan("Запуск NeoForge Installer..."));
|
||||||
|
System.out.println(ZAnsi.yellow("Это может занять несколько минут. Пожалуйста, подождите...\n"));
|
||||||
|
|
||||||
|
boolean success = runNeoForgeInstaller(installerJar);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
try {
|
||||||
|
downloadMissingLibraries(mcVersion, neoForgeVersion, mavenGroup, mavenArtifact);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(ZAnsi.yellow("Предупреждение: не удалось докачать некоторые библиотеки: " + e.getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(ZAnsi.brightGreen("\nNeoForge " + neoForgeVersion + " успешно установлен!"));
|
||||||
|
instance.setMinecraftVersion(mcVersion);
|
||||||
|
instance.setLoaderType("neoforge");
|
||||||
|
instance.setLoaderVersion(neoForgeVersion);
|
||||||
|
|
||||||
|
Files.deleteIfExists(installerJar);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
System.out.println(ZAnsi.brightRed("\nОшибка при установке NeoForge!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMavenGroup(String mcVersion) {
|
||||||
|
if (mcVersion.equals("1.20.1")) {
|
||||||
|
return "net.neoforged";
|
||||||
|
}
|
||||||
|
return "net.neoforged";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMavenArtifact(String mcVersion) {
|
||||||
|
if (mcVersion.equals("1.20.1")) {
|
||||||
|
return "forge";
|
||||||
|
}
|
||||||
|
return "neoforge";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createLauncherProfile() throws IOException {
|
||||||
|
Path profilePath = instance.getPath().resolve("launcher_profiles.json");
|
||||||
|
if (Files.exists(profilePath)) return;
|
||||||
|
|
||||||
|
String minimalProfile = """
|
||||||
|
{
|
||||||
|
"profiles": {},
|
||||||
|
"selectedProfile": "Default"
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
Files.writeString(profilePath, minimalProfile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
|
System.out.println(ZAnsi.yellow("Создан launcher_profiles.json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadFileWithProgress(String url, Path target) throws Exception {
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(url))
|
||||||
|
.GET()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||||
|
|
||||||
|
if (response.statusCode() != 200) {
|
||||||
|
throw new IOException("HTTP " + response.statusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
long contentLength = response.headers().firstValueAsLong("Content-Length").orElse(-1);
|
||||||
|
|
||||||
|
try (InputStream in = response.body();
|
||||||
|
FileOutputStream out = new FileOutputStream(target.toFile())) {
|
||||||
|
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int bytesRead;
|
||||||
|
long totalRead = 0;
|
||||||
|
int lastPercent = -1;
|
||||||
|
|
||||||
|
while ((bytesRead = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead);
|
||||||
|
totalRead += bytesRead;
|
||||||
|
|
||||||
|
if (contentLength > 0) {
|
||||||
|
int percent = (int) ((totalRead * 100) / contentLength);
|
||||||
|
if (percent != lastPercent) {
|
||||||
|
String downloaded = ProgressBar.formatBytes(totalRead);
|
||||||
|
String total = ProgressBar.formatBytes(contentLength);
|
||||||
|
ProgressBar.show("NeoForge Installer", percent, 100, "% (" + downloaded + "/" + total + ")");
|
||||||
|
lastPercent = percent;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
char[] spinner = {'|', '/', '-', '\\'};
|
||||||
|
int idx = (int) (totalRead / 1024) % 4;
|
||||||
|
System.out.print("\rСкачивание NeoForge Installer: " + ProgressBar.formatBytes(totalRead) + " " + spinner[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressBar.finish("NeoForge Installer (" + ProgressBar.formatBytes(Files.size(target)) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean runNeoForgeInstaller(Path installerJar) throws IOException, InterruptedException {
|
||||||
|
int maxRetries = 3;
|
||||||
|
int attempt = 1;
|
||||||
|
|
||||||
|
while (attempt <= maxRetries) {
|
||||||
|
System.out.println(ZAnsi.cyan("Попытка " + attempt + " из " + maxRetries));
|
||||||
|
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(
|
||||||
|
"java",
|
||||||
|
"-jar",
|
||||||
|
installerJar.toAbsolutePath().toString(),
|
||||||
|
"--installClient"
|
||||||
|
);
|
||||||
|
|
||||||
|
pb.environment().put("JAVA_OPTS", "-Dhttp.connectionTimeout=60000 -Dhttp.socketTimeout=60000");
|
||||||
|
pb.directory(instance.getPath().toFile());
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
|
|
||||||
|
Process process = pb.start();
|
||||||
|
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
boolean hasErrors = false;
|
||||||
|
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
output.append(line).append("\n");
|
||||||
|
|
||||||
|
if (line.contains("Downloading") || line.contains("Extracting")) {
|
||||||
|
System.out.println(ZAnsi.blue(" -> " + line));
|
||||||
|
} else if (line.contains("SUCCESS") || line.contains("successfully")) {
|
||||||
|
System.out.println(ZAnsi.brightGreen(" + " + line));
|
||||||
|
} else if (line.contains("WARNING") || line.contains("warning")) {
|
||||||
|
System.out.println(ZAnsi.yellow(" ! " + line));
|
||||||
|
} else if (line.contains("ERROR") || line.contains("error") || line.contains("failed") || line.contains("timed out")) {
|
||||||
|
System.out.println(ZAnsi.brightRed(" X " + line));
|
||||||
|
if (line.contains("timed out") || line.contains("failed to download")) {
|
||||||
|
hasErrors = true;
|
||||||
|
}
|
||||||
|
} else if (!line.isBlank()) {
|
||||||
|
System.out.println(" " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
|
||||||
|
if (exitCode == 0 && !hasErrors) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attempt < maxRetries) {
|
||||||
|
System.out.println(ZAnsi.yellow("Ошибка при установке. Повторная попытка через 5 секунд..."));
|
||||||
|
Thread.sleep(5000);
|
||||||
|
|
||||||
|
Path librariesDir = instance.getPath().resolve("libraries");
|
||||||
|
if (Files.exists(librariesDir)) {
|
||||||
|
try (var stream = Files.walk(librariesDir)) {
|
||||||
|
stream.filter(p -> p.toString().contains("asm") && p.toString().endsWith(".jar"))
|
||||||
|
.forEach(p -> {
|
||||||
|
try { Files.deleteIfExists(p); }
|
||||||
|
catch (IOException e) { /* ignore */ }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println(ZAnsi.brightRed("NeoForge Installer завершился с кодом ошибки: " + exitCode));
|
||||||
|
|
||||||
|
if (output.toString().contains("timed out")) {
|
||||||
|
System.out.println(ZAnsi.yellow("\nВозможные решения:"));
|
||||||
|
System.out.println(ZAnsi.yellow("1. Проверьте интернет-соединение"));
|
||||||
|
System.out.println(ZAnsi.yellow("2. Запустите лаунчер от имени администратора"));
|
||||||
|
System.out.println(ZAnsi.yellow("3. Временно отключите антивирус/брандмауэр"));
|
||||||
|
System.out.println(ZAnsi.yellow("4. Попробуйте установить другую версию NeoForge"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attempt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadMissingLibraries(String mcVersion, String neoForgeVersion, String mavenGroup, String mavenArtifact) throws Exception {
|
||||||
|
System.out.println(ZAnsi.cyan("Проверка и докачка отсутствующих библиотек..."));
|
||||||
|
|
||||||
|
Map<String, String> alternativeUrls = new HashMap<>();
|
||||||
|
alternativeUrls.put("org/ow2/asm/asm/9.6/asm-9.6.jar",
|
||||||
|
"https://repo1.maven.org/maven2/org/ow2/asm/asm/9.6/asm-9.6.jar");
|
||||||
|
alternativeUrls.put("org/ow2/asm/asm-commons/9.6/asm-commons-9.6.jar",
|
||||||
|
"https://repo1.maven.org/maven2/org/ow2/asm/asm-commons/9.6/asm-commons-9.6.jar");
|
||||||
|
alternativeUrls.put("org/ow2/asm/asm-tree/9.6/asm-tree-9.6.jar",
|
||||||
|
"https://repo1.maven.org/maven2/org/ow2/asm/asm-tree/9.6/asm-tree-9.6.jar");
|
||||||
|
|
||||||
|
Path librariesDir = instance.getPath().resolve("libraries");
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> entry : alternativeUrls.entrySet()) {
|
||||||
|
Path target = librariesDir.resolve(entry.getKey());
|
||||||
|
if (!Files.exists(target)) {
|
||||||
|
Files.createDirectories(target.getParent());
|
||||||
|
System.out.println(ZAnsi.yellow("Докачка: " + target.getFileName()));
|
||||||
|
|
||||||
|
for (int attempt = 1; attempt <= 3; attempt++) {
|
||||||
|
try {
|
||||||
|
downloadFileWithProgress(entry.getValue(), target);
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (attempt == 3) throw e;
|
||||||
|
System.out.println(ZAnsi.yellow("Повторная попытка " + attempt + "/3..."));
|
||||||
|
Thread.sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+168
-1
@@ -44,6 +44,12 @@ public class LaunchCommandBuilder {
|
|||||||
command.add(buildForgeClasspath());
|
command.add(buildForgeClasspath());
|
||||||
command.add("cpw.mods.modlauncher.Launcher");
|
command.add("cpw.mods.modlauncher.Launcher");
|
||||||
command.addAll(getForgeArguments(options));
|
command.addAll(getForgeArguments(options));
|
||||||
|
} else if ("neoforge".equals(loaderType)) {
|
||||||
|
command.addAll(getNeoForgeJvmArguments());
|
||||||
|
command.add("-cp");
|
||||||
|
command.add(buildNeoForgeClasspath());
|
||||||
|
command.add(getNeoForgeMainClass());
|
||||||
|
command.addAll(getNeoForgeArguments(options));
|
||||||
} else {
|
} else {
|
||||||
command.add("-cp");
|
command.add("-cp");
|
||||||
command.add(buildClasspath());
|
command.add(buildClasspath());
|
||||||
@@ -312,7 +318,6 @@ public class LaunchCommandBuilder {
|
|||||||
args.add("--assetsDir");
|
args.add("--assetsDir");
|
||||||
args.add(instance.getPath().resolve("assets").toAbsolutePath().toString());
|
args.add(instance.getPath().resolve("assets").toAbsolutePath().toString());
|
||||||
|
|
||||||
// FIXED: Используем правильный assetIndex для Forge
|
|
||||||
args.add("--assetIndex");
|
args.add("--assetIndex");
|
||||||
String assetIndex = instance.getAssetIndex();
|
String assetIndex = instance.getAssetIndex();
|
||||||
if (assetIndex == null || assetIndex.isEmpty()) {
|
if (assetIndex == null || assetIndex.isEmpty()) {
|
||||||
@@ -344,6 +349,162 @@ public class LaunchCommandBuilder {
|
|||||||
return args;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
* ИСПРАВЛЕНО: для Fabric используем сохраненный fabricVersionId
|
||||||
*/
|
*/
|
||||||
@@ -367,6 +528,12 @@ public class LaunchCommandBuilder {
|
|||||||
else if ("forge".equals(loaderType)) {
|
else if ("forge".equals(loaderType)) {
|
||||||
return mcVersion + "-forge-" + loaderVer;
|
return mcVersion + "-forge-" + loaderVer;
|
||||||
}
|
}
|
||||||
|
else if ("neoforge".equals(loaderType)) {
|
||||||
|
if (mcVersion.equals("1.20.1")) {
|
||||||
|
return mcVersion + "-neoforge-" + loaderVer;
|
||||||
|
}
|
||||||
|
return "neoforge-" + loaderVer;
|
||||||
|
}
|
||||||
|
|
||||||
return mcVersion;
|
return mcVersion;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ public class ZHttpClient {
|
|||||||
MOJANG_META("https://piston-meta.mojang.com", false),
|
MOJANG_META("https://piston-meta.mojang.com", false),
|
||||||
MOJANG_RESOURCES("https://resources.download.minecraft.net", false),
|
MOJANG_RESOURCES("https://resources.download.minecraft.net", false),
|
||||||
FORGE_MAVEN("https://maven.minecraftforge.net", false),
|
FORGE_MAVEN("https://maven.minecraftforge.net", false),
|
||||||
|
NEOFORGE_MAVEN("https://maven.neoforged.net", false),
|
||||||
GOOGLE("https://google.com", false),
|
GOOGLE("https://google.com", false),
|
||||||
CLOUDFLARE("https://cloudflare.com", false);
|
CLOUDFLARE("https://cloudflare.com", false);
|
||||||
|
|
||||||
@@ -106,7 +107,8 @@ public class ZHttpClient {
|
|||||||
ServiceType.FABRIC_MAVEN,
|
ServiceType.FABRIC_MAVEN,
|
||||||
ServiceType.MOJANG_META,
|
ServiceType.MOJANG_META,
|
||||||
ServiceType.MOJANG_RESOURCES,
|
ServiceType.MOJANG_RESOURCES,
|
||||||
ServiceType.FORGE_MAVEN
|
ServiceType.FORGE_MAVEN,
|
||||||
|
ServiceType.NEOFORGE_MAVEN
|
||||||
);
|
);
|
||||||
|
|
||||||
for (ServiceType service : servicesToCheck) {
|
for (ServiceType service : servicesToCheck) {
|
||||||
@@ -237,6 +239,7 @@ public class ZHttpClient {
|
|||||||
return ServiceType.MOJANG_META;
|
return ServiceType.MOJANG_META;
|
||||||
if (url.contains("resources.download.minecraft.net")) return ServiceType.MOJANG_RESOURCES;
|
if (url.contains("resources.download.minecraft.net")) return ServiceType.MOJANG_RESOURCES;
|
||||||
if (url.contains("maven.minecraftforge.net")) return ServiceType.FORGE_MAVEN;
|
if (url.contains("maven.minecraftforge.net")) return ServiceType.FORGE_MAVEN;
|
||||||
|
if (url.contains("maven.neoforged.net")) return ServiceType.NEOFORGE_MAVEN;
|
||||||
if (url.contains("google.com")) return ServiceType.GOOGLE;
|
if (url.contains("google.com")) return ServiceType.GOOGLE;
|
||||||
if (url.contains("cloudflare.com")) return ServiceType.CLOUDFLARE;
|
if (url.contains("cloudflare.com")) return ServiceType.CLOUDFLARE;
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+58
-3
@@ -520,7 +520,8 @@ async def list_packs(current_user: dict = Depends(get_current_user)):
|
|||||||
"updated_at": updated_at,
|
"updated_at": updated_at,
|
||||||
"minecraft_version": meta.get("minecraft_version", "unknown"),
|
"minecraft_version": meta.get("minecraft_version", "unknown"),
|
||||||
"loader_type": meta.get("loader_type", "vanilla"),
|
"loader_type": meta.get("loader_type", "vanilla"),
|
||||||
"loader_version": meta.get("loader_version")
|
"loader_version": meta.get("loader_version"),
|
||||||
|
"asset_index": meta.get("asset_index")
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to load pack meta for {pack_dir.name}: {e}")
|
logger.error(f"Failed to load pack meta for {pack_dir.name}: {e}")
|
||||||
@@ -1079,6 +1080,58 @@ async def proxy_forge_maven(path: str, request: Request):
|
|||||||
raise HTTPException(502, f"Bad Gateway: {str(e)}")
|
raise HTTPException(502, f"Bad Gateway: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/proxy/neoforge/versions")
|
||||||
|
async def proxy_neoforge_versions(request: Request):
|
||||||
|
"""Прокси для списка версий NeoForge"""
|
||||||
|
client_ip = request.client.host if request.client else "unknown"
|
||||||
|
logger.info(f"Proxy request: NeoForge versions from {client_ip}")
|
||||||
|
|
||||||
|
url = "https://maven.neoforged.net/releases/net/neoforged/neoforge/maven-metadata.xml"
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await proxy_client.get(url)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
content=response.content,
|
||||||
|
media_type="application/xml",
|
||||||
|
headers={"X-Proxied-By": "ZernMC"}
|
||||||
|
)
|
||||||
|
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
logger.error(f"Proxy error for NeoForge versions: {e}")
|
||||||
|
raise HTTPException(502, f"Bad Gateway: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/proxy/neoforge/maven/{path:path}")
|
||||||
|
async def proxy_neoforge_maven(path: str, request: Request):
|
||||||
|
"""Прокси для NeoForge Maven файлов"""
|
||||||
|
client_ip = request.client.host if request.client else "unknown"
|
||||||
|
logger.info(f"Proxy request: NeoForge Maven {path} from {client_ip}")
|
||||||
|
|
||||||
|
full_url = f"https://maven.neoforged.net/{path}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await proxy_client.get(full_url)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
content_type = "application/octet-stream"
|
||||||
|
if path.endswith(".jar"):
|
||||||
|
content_type = "application/java-archive"
|
||||||
|
elif path.endswith(".pom"):
|
||||||
|
content_type = "application/xml"
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
content=response.content,
|
||||||
|
media_type=content_type,
|
||||||
|
headers={"X-Proxied-By": "ZernMC"}
|
||||||
|
)
|
||||||
|
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
logger.error(f"Proxy error for NeoForge Maven {path}: {e}")
|
||||||
|
raise HTTPException(502, f"Bad Gateway: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@app.get("/proxy/download")
|
@app.get("/proxy/download")
|
||||||
async def proxy_download(request: Request):
|
async def proxy_download(request: Request):
|
||||||
"""Универсальный прокси для скачивания файлов"""
|
"""Универсальный прокси для скачивания файлов"""
|
||||||
@@ -1096,7 +1149,8 @@ async def proxy_download(request: Request):
|
|||||||
"launchermeta.mojang.com",
|
"launchermeta.mojang.com",
|
||||||
"resources.download.minecraft.net",
|
"resources.download.minecraft.net",
|
||||||
"maven.minecraftforge.net",
|
"maven.minecraftforge.net",
|
||||||
"files.minecraftforge.net"
|
"files.minecraftforge.net",
|
||||||
|
"maven.neoforged.net"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Проверяем, что URL ведет на разрешенный домен
|
# Проверяем, что URL ведет на разрешенный домен
|
||||||
@@ -1172,7 +1226,8 @@ async def proxy_status():
|
|||||||
"piston-meta.mojang.com",
|
"piston-meta.mojang.com",
|
||||||
"launchermeta.mojang.com",
|
"launchermeta.mojang.com",
|
||||||
"resources.download.minecraft.net",
|
"resources.download.minecraft.net",
|
||||||
"maven.minecraftforge.net"
|
"maven.minecraftforge.net",
|
||||||
|
"maven.neoforged.net"
|
||||||
],
|
],
|
||||||
"note": "Use this proxy if you have network issues connecting to Fabric/Mojang/Forge"
|
"note": "Use this proxy if you have network issues connecting to Fabric/Mojang/Forge"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,3 +28,4 @@ class PackMeta(BaseModel):
|
|||||||
minecraft_version: str
|
minecraft_version: str
|
||||||
loader_type: str
|
loader_type: str
|
||||||
loader_version: Optional[str] = None
|
loader_version: Optional[str] = None
|
||||||
|
asset_index: Optional[str] = None
|
||||||
@@ -109,6 +109,7 @@ async def scan_pack(pack_name: str, force_rescan: bool = False) -> PackMeta:
|
|||||||
minecraft_version = "1.20.4"
|
minecraft_version = "1.20.4"
|
||||||
loader_type = "vanilla"
|
loader_type = "vanilla"
|
||||||
loader_version = None
|
loader_version = None
|
||||||
|
asset_index = None
|
||||||
|
|
||||||
pack_config_path = pack_path / "instance.json"
|
pack_config_path = pack_path / "instance.json"
|
||||||
if pack_config_path.exists():
|
if pack_config_path.exists():
|
||||||
@@ -119,6 +120,7 @@ async def scan_pack(pack_name: str, force_rescan: bool = False) -> PackMeta:
|
|||||||
minecraft_version = config.get("minecraftVersion", minecraft_version)
|
minecraft_version = config.get("minecraftVersion", minecraft_version)
|
||||||
loader_type = config.get("loaderType", loader_type)
|
loader_type = config.get("loaderType", loader_type)
|
||||||
loader_version = config.get("loaderVersion")
|
loader_version = config.get("loaderVersion")
|
||||||
|
asset_index = config.get("assetIndex")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Failed to load instance.json for {pack_name}: {e}")
|
logger.warning(f"Failed to load instance.json for {pack_name}: {e}")
|
||||||
|
|
||||||
@@ -131,7 +133,8 @@ async def scan_pack(pack_name: str, force_rescan: bool = False) -> PackMeta:
|
|||||||
ignored_dirs=ignored_dirs,
|
ignored_dirs=ignored_dirs,
|
||||||
minecraft_version=minecraft_version,
|
minecraft_version=minecraft_version,
|
||||||
loader_type=loader_type,
|
loader_type=loader_type,
|
||||||
loader_version=loader_version
|
loader_version=loader_version,
|
||||||
|
asset_index=asset_index
|
||||||
)
|
)
|
||||||
|
|
||||||
# Save to disk (синхронно)
|
# Save to disk (синхронно)
|
||||||
|
|||||||
Reference in New Issue
Block a user