Compare commits

..

No commits in common. "main" and "owlsys/tiny-mappings" have entirely different histories.

13 changed files with 137 additions and 225 deletions

View file

@ -8,14 +8,14 @@ plugins {
} }
group = "dev.frogmc" group = "dev.frogmc"
version = "0.0.1-alpha.18" version = "0.0.1-alpha.6"
repositories { repositories {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
implementation("com.electronwill.night-config:json:3.8.1") implementation("com.electronwill.night-config:json:3.8.0")
implementation("org.ow2.asm:asm:9.7") implementation("org.ow2.asm:asm:9.7")
implementation("org.ow2.asm:asm-commons:9.7") implementation("org.ow2.asm:asm-commons:9.7")
} }

View file

@ -1,15 +1,8 @@
package dev.frogmc.thyroxine; package dev.frogmc.thyroxine;
import java.util.Objects;
import com.electronwill.nightconfig.json.JsonParser; import com.electronwill.nightconfig.json.JsonParser;
import org.objectweb.asm.Opcodes;
public final class Constants { public final class Constants {
public static final String USER_AGENT = "FrogMC Thyroxine/" +
Objects.requireNonNullElse(Constants.class.getPackage().getImplementationVersion(), "development") +
" <frogmc.dev>";
public static final String VERSION_MANIFEST = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"; public static final String VERSION_MANIFEST = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json";
public static final JsonParser JSON_PARSER = new JsonParser(); public static final JsonParser JSON_PARSER = new JsonParser();
public static final int ASM_VERSION = Opcodes.ASM9;
} }

View file

@ -1,12 +1,13 @@
package dev.frogmc.thyroxine; package dev.frogmc.thyroxine;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.net.http.HttpClient; import java.net.URISyntaxException;
import java.net.http.HttpRequest; import java.nio.charset.StandardCharsets;
import java.net.http.HttpResponse; import java.util.HashMap;
import java.net.http.HttpClient.Redirect; import java.util.Map;
import java.nio.file.Path; import java.util.Optional;
import com.electronwill.nightconfig.core.UnmodifiableConfig; import com.electronwill.nightconfig.core.UnmodifiableConfig;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
@ -16,29 +17,21 @@ import static dev.frogmc.thyroxine.Constants.JSON_PARSER;
@UtilityClass @UtilityClass
public class HttpHelper { public class HttpHelper {
public static UnmodifiableConfig getJson(String url) throws IOException { private static final Map<String, String> requestCache = new HashMap<>();
return JSON_PARSER.parse(getString(url)).unmodifiable();
public static Optional<UnmodifiableConfig> getJson(String url) {
return getString(url).map(s -> JSON_PARSER.parse(s).unmodifiable());
} }
public static String getString(String url) throws IOException { public static Optional<String> getString(String url) {
return request(url, HttpResponse.BodyHandlers.ofString()).body(); return Optional.ofNullable(requestCache.computeIfAbsent(url, s -> {
} try (InputStream in = URI.create(url).parseServerAuthority().toURL().openStream()) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
public static void download(String url, Path file) throws IOException { } catch (IOException | URISyntaxException e) {
request(url, HttpResponse.BodyHandlers.ofFile(file)); e.printStackTrace();
} // TODO
}
private static <T> HttpResponse<T> request(String url, HttpResponse.BodyHandler<T> handler) throws IOException { return null;
try (HttpClient client = HttpClient.newBuilder().followRedirects(Redirect.NORMAL).build()) { }));
HttpRequest request = HttpRequest.newBuilder()
.header("User-Agent", Constants.USER_AGENT)
.header("Accept", "*/*")
.GET()
.uri(URI.create(url))
.build();
return client.send(request, handler);
} catch (InterruptedException e) {
throw new IOException(e);
}
} }
} }

View file

@ -1,8 +0,0 @@
package dev.frogmc.thyroxine;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.commons.Remapper;
public interface RemappingStep {
ClassVisitor run(ClassVisitor previous, Remapper remapper);
}

View file

@ -9,34 +9,27 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import dev.frogmc.thyroxine.api.Mapper;
import dev.frogmc.thyroxine.api.ParameterClassRemapper; import dev.frogmc.thyroxine.api.ParameterClassRemapper;
import dev.frogmc.thyroxine.api.data.MappingBundle; import dev.frogmc.thyroxine.api.data.MappingBundle;
import dev.frogmc.thyroxine.api.data.MappingData;
import dev.frogmc.thyroxine.parser.ProguardParser;
import dev.frogmc.thyroxine.parser.tiny.TinyV1Parser;
import dev.frogmc.thyroxine.parser.tiny.TinyV2Parser;
import dev.frogmc.thyroxine.provider.MojmapProvider; import dev.frogmc.thyroxine.provider.MojmapProvider;
import dev.frogmc.thyroxine.provider.ParchmentProvider; import dev.frogmc.thyroxine.provider.ParchmentProvider;
import dev.frogmc.thyroxine.api.Mapper;
import dev.frogmc.thyroxine.api.data.MappingData;
import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.ClassRemapper; import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.Remapper;
public class Thyroxine { public class Thyroxine {
private static boolean SYSOUT = false;
public static void run(String minecraftVersion, Path inputJar, Path outputJar, boolean skipMetaInf, boolean renameParameters) throws IOException, InterruptedException { public static void run(String minecraftVersion, Path inputJar, Path outputJar, boolean skipMetaInf, boolean renameParameters) throws IOException, InterruptedException {
Path out = outputJar.toAbsolutePath();
MappingBundle data = MojmapProvider.get(minecraftVersion, MappingBundle data = MojmapProvider.get(minecraftVersion,
out.resolveSibling("client-" + minecraftVersion + ".txt"), outputJar.resolveSibling("client-" + minecraftVersion + ".txt")).orElseThrow().reverse();
out.resolveSibling("server-" + minecraftVersion + ".txt")).reverse();
MappingBundle parchment = null; MappingBundle parchment = null;
if (renameParameters) { if (renameParameters) {
parchment = ParchmentProvider.getParchment(minecraftVersion, out.getParent()); parchment = ParchmentProvider.getParchment(minecraftVersion, outputJar.getParent());
} }
MappingBundle result; MappingBundle result;
@ -46,33 +39,21 @@ public class Thyroxine {
result = data; result = data;
} }
SYSOUT = true; remap(result, inputJar, outputJar, skipMetaInf, renameParameters, "official", "named");
remap(result, inputJar, out, skipMetaInf, renameParameters, "official", "named");
SYSOUT = false;
} }
public static void remap(MappingData data, Path inputJar, Path outputJar, boolean skipMetaInf, Path... context) throws IOException, InterruptedException { public static void remap(MappingData data, Path inputJar, Path outputJar, boolean skipMetaInf) throws IOException, InterruptedException {
remap(data, inputJar, outputJar, skipMetaInf, true, context); remap(data, inputJar, outputJar, skipMetaInf, true);
} }
public static void remap(MappingBundle bundle, Path inputJar, Path outputJar, boolean skipMetaInf, boolean renameParameters, String srcNamespace, String dstNamespace, Path... context) throws IOException, InterruptedException { public static void remap(MappingBundle bundle, Path inputJar, Path outputJar, boolean skipMetaInf, boolean renameParameters, String srcNamespace, String dstNamespace) throws IOException, InterruptedException {
remap(bundle.forNamespaces(srcNamespace, dstNamespace), inputJar, outputJar, skipMetaInf, renameParameters, context); remap(bundle.forNamespaces(srcNamespace, dstNamespace), inputJar, outputJar, skipMetaInf, renameParameters);
} }
public static void remap(MappingData data, Path inputJar, Path outputJar, boolean skipMetaInf, boolean renameParameters, Path... context) throws IOException, InterruptedException { public static void remap(MappingData data, Path inputJar, Path outputJar, boolean skipMetaInf, boolean renameParameters) throws IOException, InterruptedException {
List<RemappingStep> steps = List.of(
ClassRemapper::new,
(cv, mapper) -> renameParameters ? new ParameterClassRemapper(cv, mapper, data) : cv
);
remap(data, inputJar, outputJar, skipMetaInf, steps, context);
}
public static void remap(MappingData data, Path inputJar, Path outputJar, boolean skipMetaInf, List<RemappingStep> steps, Path... context) throws IOException, InterruptedException {
Files.deleteIfExists(outputJar); Files.deleteIfExists(outputJar);
if (SYSOUT) { System.out.println("Remapping...");
System.out.println("Remapping...");
}
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
@ -80,15 +61,10 @@ public class Thyroxine {
FileSystem inFs = FileSystems.newFileSystem(inputJar); FileSystem inFs = FileSystems.newFileSystem(inputJar);
FileSystem outFs = FileSystems.newFileSystem(outputJar, Map.of("create", "true"))) { FileSystem outFs = FileSystems.newFileSystem(outputJar, Map.of("create", "true"))) {
List<Callable<Void>> tasks = new ArrayList<>(); List<Callable<Void>> tasks = new ArrayList<>();
Map<Path, FileSystem> contexts = new HashMap<>(context.length + 1);
contexts.put(inputJar, inFs);
for (Path p : context) {
if (!contexts.containsKey(p)) {
contexts.put(p, FileSystems.newFileSystem(p));
}
}
Remapper mapper = createMapper(data, contexts.values()); Map<String, List<String>> lazyParents = new ConcurrentHashMap<>();
Mapper mapper = new Mapper(data, className -> lazyParents.computeIfAbsent(className, ignored ->
Thyroxine.computeInheritances(className, inFs)));
Files.walkFileTree(inFs.getPath("/"), new SimpleFileVisitor<>() { Files.walkFileTree(inFs.getPath("/"), new SimpleFileVisitor<>() {
@ -112,9 +88,12 @@ public class Thyroxine {
byte[] bytes = Files.readAllBytes(path); byte[] bytes = Files.readAllBytes(path);
ClassReader reader = new ClassReader(bytes); ClassReader reader = new ClassReader(bytes);
ClassWriter writer = new ClassWriter(0); ClassWriter writer = new ClassWriter(0);
ClassVisitor visitor = writer; ClassVisitor remapper = new ClassRemapper(writer, mapper);
for (RemappingStep pass : steps) { ClassVisitor visitor;
visitor = pass.run(visitor, mapper); if (renameParameters) {
visitor = new ParameterClassRemapper(remapper, mapper, data);
} else {
visitor = remapper;
} }
reader.accept(visitor, 0); reader.accept(visitor, 0);
@ -140,32 +119,7 @@ public class Thyroxine {
exec.invokeAll(tasks); exec.invokeAll(tasks);
for (FileSystem fs : contexts.values()) { System.out.printf("Finished remapping (%.2fs)%n", (System.currentTimeMillis() - startTime) / 1000F);
fs.close();
}
if (SYSOUT) {
System.out.printf("Finished remapping (%.2fs)%n", (System.currentTimeMillis() - startTime) / 1000F);
}
}
}
public static Remapper createMapper(MappingData data, Collection<FileSystem> contexts) {
Map<String, List<String>> lazyParents = new ConcurrentHashMap<>();
MappingData reverseData = data.reverse();
return new Mapper(data, className -> lazyParents.computeIfAbsent(className, ignored ->
computeInheritances(className, reverseData, contexts)));
}
public static MappingBundle parse(Path mappings) throws IOException {
String data = Files.readString(mappings);
String header = data.split("\n", 2)[0];
if (header.matches("tiny\t+v2\t+.*")) {
return TinyV2Parser.parse(data);
} else if (TinyV1Parser.HEADER.matcher(header).matches()) {
return TinyV1Parser.parse(data);
} else {
return new MappingBundle(ProguardParser.read(data));
} }
} }
@ -183,6 +137,7 @@ public class Thyroxine {
boolean remapParams = args.length > 3 && hasArg(args, "--remap-parameters"); boolean remapParams = args.length > 3 && hasArg(args, "--remap-parameters");
run(minecraftVersion, Paths.get(minecraftJar), Paths.get(outJar), removeMetaInf, remapParams); run(minecraftVersion, Paths.get(minecraftJar), Paths.get(outJar), removeMetaInf, remapParams);
} }
private static boolean hasArg(String[] arguments, String arg) { private static boolean hasArg(String[] arguments, String arg) {
@ -194,32 +149,23 @@ public class Thyroxine {
return false; return false;
} }
private static List<String> computeInheritances(String className, MappingData reverse, Collection<FileSystem> jars) { private static List<String> computeInheritances(String className, FileSystem inFs){
try { try {
if (reverse.classes().containsKey(className)) { Path path = inFs.getPath("/" + className + ".class");
className = reverse.classes().get(className); if (!Files.isRegularFile(path))
} return List.of();
List<String> superTypes = new ArrayList<>();
for (FileSystem fs : jars) { byte[] bytes = Files.readAllBytes(path);
Path path = fs.getPath("/" + className + ".class"); ClassReader reader = new ClassReader(bytes);
if (!Files.isRegularFile(path)) { List<String> superTypes = new ArrayList<>();
continue; reader.accept(new ClassVisitor(Opcodes.ASM9) {
} @Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
superTypes.add(superName);
superTypes.addAll(Arrays.asList(interfaces));
}
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
byte[] bytes = Files.readAllBytes(path);
ClassReader reader = new ClassReader(bytes);
reader.accept(new ClassVisitor(Constants.ASM_VERSION) {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
if (interfaces != null) {
superTypes.addAll(Arrays.asList(interfaces));
}
if (!"java/lang/Object".equals(superName)) {
superTypes.add(superName);
}
}
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
}
return superTypes; return superTypes;
} catch (IOException error) { } catch (IOException error) {
throw new Error(error); // not our problem :^) for now throw new Error(error); // not our problem :^) for now

View file

@ -60,4 +60,9 @@ public class Mapper extends Remapper {
return null; return null;
} }
@Override
public String mapMethodDesc(String methodDescriptor) {
return super.mapMethodDesc(methodDescriptor);
}
} }

View file

@ -3,30 +3,22 @@ package dev.frogmc.thyroxine.api;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import dev.frogmc.thyroxine.Constants;
import dev.frogmc.thyroxine.api.data.MappingData; import dev.frogmc.thyroxine.api.data.MappingData;
import dev.frogmc.thyroxine.api.data.Member; import dev.frogmc.thyroxine.api.data.Member;
import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.Remapper; import org.objectweb.asm.commons.Remapper;
public class ParameterClassRemapper extends ClassVisitor { public class ParameterClassRemapper extends ClassRemapper {
private final MappingData data; private final MappingData data;
private final Remapper remapper;
private String className;
public ParameterClassRemapper(ClassVisitor classVisitor, Remapper remapper, MappingData data) { public ParameterClassRemapper(ClassVisitor classVisitor, Remapper remapper, MappingData data) {
super(Constants.ASM_VERSION, classVisitor); super(Opcodes.ASM9, classVisitor, remapper);
this.remapper = remapper;
this.data = data; this.data = data;
} }
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.className = name;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override @Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
String remappedDescriptor = remapper.mapMethodDesc(descriptor); String remappedDescriptor = remapper.mapMethodDesc(descriptor);
@ -35,6 +27,6 @@ public class ParameterClassRemapper extends ClassVisitor {
Member member = new Member(remapper.map(className), remapper.mapMethodName(className, name, descriptor), remappedDescriptor); Member member = new Member(remapper.map(className), remapper.mapMethodName(className, name, descriptor), remappedDescriptor);
Map<Integer, String> parameters = data != null ? data.parameters().getOrDefault(member, Collections.emptyMap()) : Collections.emptyMap(); Map<Integer, String> parameters = data != null ? data.parameters().getOrDefault(member, Collections.emptyMap()) : Collections.emptyMap();
return new ParameterMethodRemapper(methodVisitor, remapper, parameters); return methodVisitor == null ? null : new ParameterMethodRemapper(methodVisitor, remapper, parameters);
} }
} }

View file

@ -1,24 +1,20 @@
package dev.frogmc.thyroxine.api; package dev.frogmc.thyroxine.api;
import java.util.HashSet; import java.util.*;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import dev.frogmc.thyroxine.Constants;
import org.objectweb.asm.Label; import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import org.objectweb.asm.commons.MethodRemapper;
import org.objectweb.asm.commons.Remapper; import org.objectweb.asm.commons.Remapper;
public class ParameterMethodRemapper extends MethodVisitor { public class ParameterMethodRemapper extends MethodRemapper {
private final Remapper remapper;
private final Map<Integer, String> parameters; private final Map<Integer, String> parameters;
private final Set<String> usedLocalNames = new HashSet<>(); private final Set<String> usedLocalNames = new HashSet<>();
public ParameterMethodRemapper(MethodVisitor methodVisitor, Remapper remapper, Map<Integer, String> parameters) { public ParameterMethodRemapper(MethodVisitor methodVisitor, Remapper remapper, Map<Integer, String> parameters) {
super(Constants.ASM_VERSION, methodVisitor); super(Opcodes.ASM9, methodVisitor, remapper);
this.remapper = remapper;
this.parameters = parameters; this.parameters = parameters;
} }

View file

@ -163,14 +163,6 @@ public record MappingBundle(List<MappingData> data, List<DocumentationData> docu
String w = res; String w = res;
res = d.classes().get(res); res = d.classes().get(res);
if (res == null) { if (res == null) {
int dollar = w.indexOf("$");
if (dollar != -1) {
String prefix = d.classes().get(w.substring(0, dollar));
if (prefix != null) {
res = prefix + w.substring(dollar);
break;
}
}
if (!allowIncomplete) { if (!allowIncomplete) {
/* /*
* The mapping set is incomplete at this point. * The mapping set is incomplete at this point.

View file

@ -9,7 +9,7 @@ import dev.frogmc.thyroxine.api.data.Member;
public class TinyV1Parser { public class TinyV1Parser {
public static final Pattern HEADER = Pattern.compile("v(?<majorVer>[12]+)\\s\\w*\\s?(?<srcns>\\S+)\\s(?<dstns>\\S+)"); private static final Pattern HEADER = Pattern.compile("v(?<majorVer>[12]+)\\s\\w*\\s?(?<srcns>\\S+)\\s(?<dstns>\\S+)");
public static MappingBundle parse(String mappings) { public static MappingBundle parse(String mappings) {
String[] lines = mappings.split("\n"); String[] lines = mappings.split("\n");

View file

@ -43,9 +43,9 @@ public class TinyV2Parser {
if ("c".equals(line[0])) { if ("c".equals(line[0])) {
if (1+ns >= line.length) { if (1+ns >= line.length) {
while (lines[i + 1].startsWith("\t")) { do {
i++; i++;
} } while (lines[i].startsWith("\t"));
continue; continue;
} }
currentClass = line[ns]; currentClass = line[ns];

View file

@ -1,64 +1,65 @@
package dev.frogmc.thyroxine.provider; package dev.frogmc.thyroxine.provider;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import dev.frogmc.thyroxine.Constants;
import dev.frogmc.thyroxine.HttpHelper;
import dev.frogmc.thyroxine.api.data.MappingBundle;
import dev.frogmc.thyroxine.parser.ProguardParser;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import dev.frogmc.thyroxine.Constants;
import dev.frogmc.thyroxine.HttpHelper;
import dev.frogmc.thyroxine.api.data.MappingBundle;
import dev.frogmc.thyroxine.api.data.MappingData;
import dev.frogmc.thyroxine.parser.ProguardParser;
public class MojmapProvider { public class MojmapProvider {
public static MappingBundle get(String gameVersion, Path clientCacheFile, Path serverCacheFile) throws IOException { public static Optional<MappingBundle> get(String gameVersion, Path cacheFile) {
MappingData client = ProguardParser.read(getMappings(gameVersion, "client", clientCacheFile)); return getMappings(gameVersion, cacheFile).map(ProguardParser::read).map(MappingBundle::new);
MappingData server = ProguardParser.read(getMappings(gameVersion, "server", serverCacheFile));
client.classes().putAll(server.classes());
client.fields().putAll(server.fields());
client.methods().putAll(server.methods());
client.parameters().putAll(server.parameters());
return new MappingBundle(client);
} }
public static MappingBundle get(String gameVersion, String env, Path cacheFile) throws IOException { @SuppressWarnings("unchecked")
return new MappingBundle(ProguardParser.read(getMappings(gameVersion, env, cacheFile))); private static Optional<String> getMappings(String gameVersion, Path cacheFile) {
} if (Files.exists(cacheFile)){
try {
@SuppressWarnings("unchecked") return Optional.of(Files.readString(cacheFile, StandardCharsets.UTF_8));
private static String getMappings(String gameVersion, String env, Path cacheFile) throws IOException { } catch (IOException e) {
if (Files.exists(cacheFile)) { // TODO
return Files.readString(cacheFile, StandardCharsets.UTF_8); e.printStackTrace();
}
UnmodifiableConfig manifest = HttpHelper.getJson(Constants.VERSION_MANIFEST);
String versionName;
if (gameVersion.startsWith("latest-")) {
versionName = (String) ((Map<?, ?>) manifest.get("latest")).get(gameVersion.split("-")[1]);
} else {
versionName = gameVersion;
}
System.out.println("Loading version: " + versionName);
for (UnmodifiableConfig version : (List<UnmodifiableConfig>) manifest.get("versions")) {
if (version.get("id").equals(versionName)) {
UnmodifiableConfig versionManifest = HttpHelper.getJson(version.get("url"));
String mappingsUrl = ((UnmodifiableConfig) ((UnmodifiableConfig) versionManifest
.get("downloads")).get(env+"_mappings")).get("url");
String s = HttpHelper.getString(mappingsUrl);
Files.createDirectories(cacheFile.getParent());
Files.writeString(cacheFile, s);
return s;
} }
} }
throw new IllegalArgumentException("Could not find mojmap for the specified version: " + gameVersion + "/"+env+"!"); return HttpHelper.getJson(Constants.VERSION_MANIFEST).flatMap(manifest -> {
} String versionName;
if (gameVersion.startsWith("latest-")) {
versionName = (String) ((Map<?, ?>) manifest.get("latest")).get(gameVersion.split("-")[1]);
} else {
versionName = gameVersion;
}
System.out.println("Loading version: " + versionName);
for (UnmodifiableConfig version : (List<UnmodifiableConfig>) manifest.get("versions")) {
if (version.get("id").equals(versionName)) {
UnmodifiableConfig versionManifest = HttpHelper.getJson(version.get("url")).orElseThrow();
String mappingsUrl = ((UnmodifiableConfig) ((UnmodifiableConfig) versionManifest
.get("downloads")).get("client_mappings")).get("url");
return HttpHelper.getString(mappingsUrl).map(s -> {
try {
Files.writeString(cacheFile, s);
} catch (IOException e) {
// TODO
e.printStackTrace();
}
return s;
});
}
}
return Optional.empty();
});
}
} }

View file

@ -2,9 +2,12 @@ package dev.frogmc.thyroxine.provider;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI; import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem; import java.nio.file.FileSystem;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
import java.nio.file.Files; import java.nio.file.Files;
@ -15,7 +18,6 @@ import java.util.*;
import com.electronwill.nightconfig.core.UnmodifiableConfig; import com.electronwill.nightconfig.core.UnmodifiableConfig;
import dev.frogmc.thyroxine.Constants; import dev.frogmc.thyroxine.Constants;
import dev.frogmc.thyroxine.HttpHelper;
import dev.frogmc.thyroxine.api.data.DocumentationData; import dev.frogmc.thyroxine.api.data.DocumentationData;
import dev.frogmc.thyroxine.api.data.MappingBundle; import dev.frogmc.thyroxine.api.data.MappingBundle;
import dev.frogmc.thyroxine.api.data.MappingData; import dev.frogmc.thyroxine.api.data.MappingData;
@ -34,9 +36,9 @@ public class ParchmentProvider {
private static String findParchmentVersion(String gameVersion) throws IOException { private static String findParchmentVersion(String gameVersion) throws IOException {
String url = getParchmentUrl(gameVersion) + "/maven-metadata.xml"; String url = getParchmentUrl(gameVersion) + "/maven-metadata.xml";
try (InputStream try (InputStream
stream = URI.create(url).toURL().openStream()) { stream = URI.create(url).toURL().openStream()) {
Document document = DocumentBuilderFactory.newInstance() Document document = DocumentBuilderFactory.newInstance()
.newDocumentBuilder().parse(stream); .newDocumentBuilder().parse(stream);
return document.getElementsByTagName("release").item(0).getTextContent(); return document.getElementsByTagName("release").item(0).getTextContent();
} catch (IOException | SAXException | ParserConfigurationException e) { } catch (IOException | SAXException | ParserConfigurationException e) {
throw new IOException(e); throw new IOException(e);
@ -59,12 +61,12 @@ public class ParchmentProvider {
var url = "%s/%s/parchment-%s-%s.zip".formatted(getParchmentUrl(gameVersion), parchmentVer, gameVersion, parchmentVer); var url = "%s/%s/parchment-%s-%s.zip".formatted(getParchmentUrl(gameVersion), parchmentVer, gameVersion, parchmentVer);
if (forceDownload || Files.notExists(cachePath)) { if (forceDownload || Files.notExists(cachePath)) {
HttpHelper.download(url, cachePath); Files.copy(URI.create(url).toURL().openStream(), cachePath);
} }
{ {
try { try (InputStream input = URI.create(url+".sha512").toURL().openStream()) {
var hash = HttpHelper.getString(url + ".sha512").split("\n", 2)[0]; var hash = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)).readLine();
MessageDigest digest = MessageDigest.getInstance("SHA-512"); MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] out = digest.digest(Files.readAllBytes(cachePath)); byte[] out = digest.digest(Files.readAllBytes(cachePath));
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@ -73,10 +75,10 @@ public class ParchmentProvider {
} }
String local = sb.toString(); String local = sb.toString();
if (!hash.equals(local)) { if (!hash.equals(local)) {
System.out.println("Hashes do not match for " + cachePath + ": " + local + " != " + hash + "; redownloading!"); System.out.println("Hashes do not match for "+cachePath+": "+local+" != "+hash+"; redownloading!");
return getParchment(gameVersion, parchmentVer, cacheDir, true); return getParchment(gameVersion, parchmentVer, cacheDir, true);
} }
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e){
throw new IOException(e); throw new IOException(e);
} }
} }
@ -90,7 +92,7 @@ public class ParchmentProvider {
List<DocumentationData.Package> packages = new ArrayList<>(); List<DocumentationData.Package> packages = new ArrayList<>();
packagesList.forEach(config -> packagesList.forEach(config ->
packages.add(new DocumentationData.Package(config.get("name"), new ArrayList<>(Objects.requireNonNullElse(c.get("javadoc"), packages.add(new DocumentationData.Package(config.get("name"), new ArrayList<>(Objects.requireNonNullElse(c.get("javadoc"),
Collections.emptyList()))))); Collections.emptyList())))));
List<DocumentationData.Class> classes = new ArrayList<>(); List<DocumentationData.Class> classes = new ArrayList<>();
List<UnmodifiableConfig> classesList = Objects.requireNonNullElse(c.get("classes"), Collections.emptyList()); List<UnmodifiableConfig> classesList = Objects.requireNonNullElse(c.get("classes"), Collections.emptyList());