Update installer

This commit is contained in:
IzzelAliz 2023-01-31 16:01:48 +08:00
parent 3014a12ec8
commit 92509e2344
No known key found for this signature in database
GPG Key ID: EE50E123A11D8338
3 changed files with 102 additions and 44 deletions

View File

@ -43,8 +43,7 @@ public record FileDownloader(String url, String target, String hash) implements
Files.createDirectories(path.getParent()); Files.createDirectories(path.getParent());
} }
var tmp = new File(target + ".tmp").toPath(); var tmp = new File(target + ".tmp").toPath();
URL url = new URL(this.url); try (InputStream stream = read(url)) {
try (InputStream stream = redirect(url)) {
Files.copy(stream, tmp, StandardCopyOption.REPLACE_EXISTING); Files.copy(stream, tmp, StandardCopyOption.REPLACE_EXISTING);
} catch (SocketTimeoutException | SSLException e) { } catch (SocketTimeoutException | SSLException e) {
throw new RuntimeException("Timeout " + url); throw new RuntimeException("Timeout " + url);
@ -69,11 +68,15 @@ public record FileDownloader(String url, String target, String hash) implements
} }
} }
private InputStream redirect(URL url) throws IOException { static InputStream read(String url) throws IOException {
return redirect(new URL(url));
}
private static InputStream redirect(URL url) throws IOException {
return redirect(url, new HashSet<>()); return redirect(url, new HashSet<>());
} }
private InputStream redirect(URL url, Set<String> history) throws IOException { private static InputStream redirect(URL url, Set<String> history) throws IOException {
if (history.contains(url.toString())) { if (history.contains(url.toString())) {
StringJoiner joiner = new StringJoiner("\n "); StringJoiner joiner = new StringJoiner("\n ");
joiner.add(""); joiner.add("");

View File

@ -55,18 +55,6 @@ import java.util.stream.Stream;
public class ForgeInstaller { public class ForgeInstaller {
private static final MethodHandles.Lookup IMPL_LOOKUP = Unsafe.lookup(); private static final MethodHandles.Lookup IMPL_LOOKUP = Unsafe.lookup();
private static final String[] MAVEN_REPO = {
"https://arclight.mcxk.net/"
};
private static final String INSTALLER_URL = "https://arclight.mcxk.net/net/minecraftforge/forge/%s-%s/forge-%s-%s-installer.jar";
private static final String SERVER_URL = "https://arclight.mcxk.net/net/minecraft/server/minecraft_server.%s.jar";
private static final String MAPPING_URL = "https://arclight.mcxk.net/net/minecraft/server/mappings_server.%s.txt";
private static final Map<String, String> VERSION_HASH = Map.of(
"1.18.2", "c8f83c5655308435b3dcf03c06d9fe8740a77469"
);
private static final Map<String, String> MAPPING_HASH = Map.of(
"1.18.2", "e562f588fea155d96291267465dc3323bfe1551b"
);
public static List<Path> modInstall(Consumer<String> logger) throws Throwable { public static List<Path> modInstall(Consumer<String> logger) throws Throwable {
InputStream stream = ForgeInstaller.class.getModule().getResourceAsStream("/META-INF/installer.json"); InputStream stream = ForgeInstaller.class.getModule().getResourceAsStream("/META-INF/installer.json");
@ -127,18 +115,55 @@ public class ForgeInstaller {
}); });
} }
private record MinecraftData(String mirror, String serverUrl, String serverHash, String mappingUrl,
String mappingHash) {}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static CompletableFuture<Path>[] installForge(InstallInfo info, ExecutorService pool, Consumer<String> logger) throws Exception { private static CompletableFuture<Path>[] installForge(InstallInfo info, ExecutorService pool, Consumer<String> logger) {
String format = String.format(INSTALLER_URL, info.installer.minecraft, info.installer.forge, info.installer.minecraft, info.installer.forge); var minecraftData = CompletableFuture.supplyAsync(() -> {
logger.accept("Downloading mc version manifest...");
for (Map.Entry<String, String> entry : Mirrors.getVersionManifest()) {
try (var stream = FileDownloader.read(entry.getValue())) {
var bytes = stream.readAllBytes();
var element = new JsonParser().parse(new String(bytes, StandardCharsets.UTF_8)).getAsJsonObject();
var versions = element.getAsJsonArray("versions");
for (var version : versions) {
var id = version.getAsJsonObject().get("id").getAsString();
if (Objects.equals(id, info.installer.minecraft)) {
var url = version.getAsJsonObject().get("url").getAsString();
try (var versionStream = FileDownloader.read(url)) {
var object = new JsonParser().parse(new String(versionStream.readAllBytes(), StandardCharsets.UTF_8)).getAsJsonObject();
var downloads = object.getAsJsonObject("downloads");
var server = downloads.getAsJsonObject("server");
var serverUrl = server.get("url").getAsString();
var serverHash = server.get("sha1").getAsString();
var mapping = downloads.getAsJsonObject("server_mappings");
var mappingUrl = mapping.get("url").getAsString();
var mappingHash = mapping.get("sha1").getAsString();
logger.accept("Minecraft version: %s, server: %s, mappings: %s".formatted(info.installer.minecraft, serverHash, mappingHash));
return new MinecraftData(entry.getKey(),
Mirrors.mapMojangMirror(serverUrl, entry.getKey()), serverHash,
Mirrors.mapMojangMirror(mappingUrl, entry.getKey()), mappingHash);
}
}
}
logger.accept("Version %s not available in %s".formatted(info.installer.minecraft, entry.getKey()));
} catch (Exception e) {
logger.accept("Failed to download manifest from " + entry.getKey() + "\n " + e);
}
}
return null;
}, pool);
String coord = String.format("net.minecraftforge:forge:%s-%s:installer", info.installer.minecraft, info.installer.forge);
String dist = String.format("forge-%s-%s-installer.jar", info.installer.minecraft, info.installer.forge); String dist = String.format("forge-%s-%s-installer.jar", info.installer.minecraft, info.installer.forge);
FileDownloader fd = new FileDownloader(format, dist, info.installer.hash); MavenDownloader forge = new MavenDownloader(Mirrors.getMavenRepo(), coord, dist, info.installer.hash);
var installerFuture = reportSupply(pool, logger).apply(fd).thenApply(path -> { var installerFuture = reportSupply(pool, logger).apply(forge).thenCombineAsync(minecraftData, (path, data) -> {
try (var jarFile = new JarFile(path.toFile())) { try (var jarFile = new JarFile(path.toFile())) {
Map<String, Map.Entry<String, String>> map = new HashMap<>(); Map<String, Map.Entry<String, String>> map = new HashMap<>();
var profile = jarFile.getEntry("install_profile.json"); var profile = jarFile.getEntry("install_profile.json");
map.putAll(profileLibraries(new InputStreamReader(jarFile.getInputStream(profile)), info.installer.minecraft)); map.putAll(profileLibraries(new InputStreamReader(jarFile.getInputStream(profile)), info.installer.minecraft, data));
var version = jarFile.getEntry("version.json"); var version = jarFile.getEntry("version.json");
map.putAll(profileLibraries(new InputStreamReader(jarFile.getInputStream(version)), info.installer.minecraft)); map.putAll(profileLibraries(new InputStreamReader(jarFile.getInputStream(version)), info.installer.minecraft, data));
List<Supplier<Path>> suppliers = checkMaven(map); List<Supplier<Path>> suppliers = checkMaven(map);
CompletableFuture<?>[] array = suppliers.stream().map(reportSupply(pool, logger)).toArray(CompletableFuture[]::new); CompletableFuture<?>[] array = suppliers.stream().map(reportSupply(pool, logger)).toArray(CompletableFuture[]::new);
handleFutures(logger, array); handleFutures(logger, array);
@ -147,10 +172,10 @@ public class ForgeInstaller {
} }
return stripDownloadMapping(path, logger); return stripDownloadMapping(path, logger);
}); });
var serverFuture = reportSupply(pool, logger).apply( var serverFuture = minecraftData.thenCompose(data -> reportSupply(pool, logger).apply(
new FileDownloader(String.format(SERVER_URL, info.installer.minecraft), new FileDownloader(String.format(data.serverUrl, info.installer.minecraft),
String.format("libraries/net/minecraft/server/%1$s/server-%1$s.jar", info.installer.minecraft), VERSION_HASH.get(info.installer.minecraft)) String.format("libraries/net/minecraft/server/%1$s/server-%1$s.jar", info.installer.minecraft), data.serverHash)
); ));
return new CompletableFuture[]{installerFuture, serverFuture}; return new CompletableFuture[]{installerFuture, serverFuture};
} }
@ -214,7 +239,7 @@ public class ForgeInstaller {
} }
} }
private static Map<String, Map.Entry<String, String>> profileLibraries(Reader reader, String minecraft) throws IOException { private static Map<String, Map.Entry<String, String>> profileLibraries(Reader reader, String minecraft, MinecraftData minecraftData) throws IOException {
Map<String, Map.Entry<String, String>> ret = new HashMap<>(); Map<String, Map.Entry<String, String>> ret = new HashMap<>();
var object = new JsonParser().parse(reader).getAsJsonObject(); var object = new JsonParser().parse(reader).getAsJsonObject();
JsonArray array = object.getAsJsonArray("libraries"); JsonArray array = object.getAsJsonArray("libraries");
@ -231,7 +256,7 @@ public class ForgeInstaller {
if (data.has("MOJMAPS")) { if (data.has("MOJMAPS")) {
var serverMapping = data.getAsJsonObject("MOJMAPS").get("server").getAsString(); var serverMapping = data.getAsJsonObject("MOJMAPS").get("server").getAsString();
ret.put(serverMapping.substring(1, serverMapping.length() - 1), ret.put(serverMapping.substring(1, serverMapping.length() - 1),
new AbstractMap.SimpleImmutableEntry<>(MAPPING_HASH.get(minecraft), MAPPING_URL.formatted(minecraft))); new AbstractMap.SimpleImmutableEntry<>(minecraftData.mappingHash, minecraftData.mappingUrl));
} }
} }
return ret; return ret;
@ -256,13 +281,13 @@ public class ForgeInstaller {
try { try {
String fileHash = Util.hash(path); String fileHash = Util.hash(path);
if (!fileHash.equals(hash)) { if (!fileHash.equals(hash)) {
incomplete.add(new MavenDownloader(MAVEN_REPO, maven, path, hash, url)); incomplete.add(new MavenDownloader(Mirrors.getMavenRepo(), maven, path, hash, url));
} }
} catch (Exception e) { } catch (Exception e) {
incomplete.add(new MavenDownloader(MAVEN_REPO, maven, path, hash, url)); incomplete.add(new MavenDownloader(Mirrors.getMavenRepo(), maven, path, hash, url));
} }
} else { } else {
incomplete.add(new MavenDownloader(MAVEN_REPO, maven, path, hash, url)); incomplete.add(new MavenDownloader(Mirrors.getMavenRepo(), maven, path, hash, url));
} }
} }
return incomplete; return incomplete;
@ -421,19 +446,7 @@ public class ForgeInstaller {
return new ParserData(source[0], source[1], all[1]); return new ParserData(source[0], source[1], all[1]);
} }
private static class ParserData { private record ParserData(String module, String packages, String target) {}
final String module;
final String packages;
final String target;
ParserData(String module, String packages, String target) {
this.module = module;
this.packages = packages;
this.target = target;
}
}
private static void addExtra(List<String> extras, MethodHandle implAddExtraMH, MethodHandle implAddExtraToAllUnnamedMH) { private static void addExtra(List<String> extras, MethodHandle implAddExtraMH, MethodHandle implAddExtraToAllUnnamedMH) {
extras.forEach(extra -> { extras.forEach(extra -> {

View File

@ -0,0 +1,42 @@
package io.izzel.arclight.forgeinstaller;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Mirrors {
private static final String[] MAVEN_REPO = {
"https://arclight.mcxk.net/",
"https://download.mcbbs.net/maven/",
"https://repo.spongepowered.org/maven/"
};
private static final String[] MOJANG_MIRROR = {
"https://download.mcbbs.net",
"https://bmclapi2.bangbang93.com",
"https://piston-meta.mojang.com"
};
private static final String VERSION_MANIFEST = "%s/mc/game/version_manifest.json";
public static String[] getMavenRepo() {
return MAVEN_REPO;
}
public static List<Map.Entry<String, String>> getVersionManifest() {
return Arrays.stream(MOJANG_MIRROR).map(it -> Map.entry(it, VERSION_MANIFEST.formatted(it)))
.collect(Collectors.toList());
}
public static String mapMojangMirror(String url, String mirror) {
if (mirror.equals(MOJANG_MIRROR[MOJANG_MIRROR.length - 1])) {
return url;
}
return url.replace("https://launcher.mojang.com", mirror)
.replace("https://launchermeta.mojang.com", mirror)
.replace("https://piston-meta.mojang.com", mirror)
.replace("https://piston-data.mojang.com", mirror);
}
}