Skip to content

Commit

Permalink
Finalize first pass of Launcher
Browse files Browse the repository at this point in the history
- Switch to SHA3-512 hashes for local downloading
- Allow for downloading of more maven hash file extensions
- Allow for dynamic hashes per-lib
- Hide unnecessary ClassNotFoundExceptions
  • Loading branch information
C0D3-M4513R committed Jan 7, 2024
1 parent b40c4c0 commit ed4405a
Show file tree
Hide file tree
Showing 14 changed files with 142 additions and 190 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/gradle-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: jar
path: build/libs/terminal-colors-*.jar
path: build/libs/kettinglauncher-*.jar

- name: Upload Hash
uses: actions/upload-artifact@v3
Expand Down Expand Up @@ -96,7 +96,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create Release
run: |
gh release create "v$VERSION" tmp/terminal-colors-*.jar \
gh release create "v$VERSION" tmp/kettinglauncher-*.jar \
--repo="$GITHUB_REPOSITORY" \
--title="${GITHUB_REPOSITORY#*/} v$VERSION" \
--generate-notes
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ configurations {

dependencies {
compileOnly 'org.jetbrains:annotations:23.0.0'
implementation 'org.kettingpowered:kettingcommon:0.0.1'
implementation 'org.kettingpowered:kettingcommon:1.0.0'
implementation 'com.google.code.gson:gson:2.10.1' //Used in Patcher
implementation 'me.tongfei:progressbar:0.10.0' //Used to display progress
transitive 'org.jline:jline-reader:3.21.0' //needed for progressbar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ abstract class GenerateLibs extends DefaultTask {
getProject().configurations.named(getConfiguration().get()).get().getResolvedConfiguration().getResolvedArtifacts().each { dep->
def art = dep.moduleVersion.id
def mavenId = "$art.group:$art.name:$art.version" + (dep.classifier != null ? ":$dep.classifier" : "") + (dep.extension != null ? "@$dep.extension" : "")
entries.add("$dep.file.sha512\t$mavenId")
entries.add("$dep.file.sha512\tSHA3-512\t$mavenId")
}

output.get().asFile.text = entries.join('\n')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import java.security.MessageDigest
final class Util {
public static void init() {
File.metaClass.sha512 = { ->
MessageDigest md = MessageDigest.getInstance('SHA-512')
MessageDigest md = MessageDigest.getInstance('SHA3-512')
delegate.eachByte 4096, {bytes, size ->
md.update(bytes, 0, size)
}
Expand Down

This file was deleted.

118 changes: 46 additions & 72 deletions src/main/java/org/kettingpowered/launcher/KettingLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.kettingpowered.launcher.betterui.BetterUI;
import org.kettingpowered.launcher.dependency.AvailableMavenRepos;
import org.kettingpowered.launcher.dependency.Dependency;
import org.kettingpowered.launcher.dependency.LibHelper;
import org.kettingpowered.launcher.dependency.Libraries;
import org.kettingpowered.launcher.dependency.LibraryClassLoader;
import org.kettingpowered.launcher.dependency.MavenArtifact;
Expand Down Expand Up @@ -65,7 +66,8 @@ public void init() throws Exception {
final String mc_version = MCVersion.getMc(args);
//This cannot get moved past the ensureOneServerAndUpdate call.
//It will cause the just downloaded server to be removed, which causes issues.
if (Patcher.updateNeeded()) {
if (false) { //todo: patcher update checking
if (Main.DEBUG) System.out.println("Patcher needs updating.");
//prematurely delete files to prevent errors
FileUtils.deleteDir(KettingFiles.NMS_BASE_DIR);
FileUtils.deleteDir(KettingFiles.KETTINGSERVER_BASE_DIR);
Expand Down Expand Up @@ -123,16 +125,20 @@ private void ensureOneServerAndUpdate(final String mc_version) throws Exception
System.out.println(Arrays.stream(kettingServerVersions).map(File::getName).collect(Collectors.joining("\n")));
}
final List<Tuple<MajorMinorPatchVersion<Integer>, MajorMinorPatchVersion<Integer>>> versions = parseKettingServerVersionList(Arrays.stream(kettingServerVersions).map(File::getName)).getOrDefault(mc_mmp, new ArrayList<>());
Tuple<MajorMinorPatchVersion<Integer>, MajorMinorPatchVersion<Integer>> serverVersion = null;

if(versions.isEmpty()) FileUtils.deleteDir(KettingFiles.KETTINGSERVER_FORGE_DIR); //we have multiple ketting versions, but 0 that match the requested minecraft version.
else if(versions.size() > 1) {
Tuple<MajorMinorPatchVersion<Integer>, MajorMinorPatchVersion<Integer>> version = versions.get(0);
serverVersion = versions.get(0);
Tuple<MajorMinorPatchVersion<Integer>, MajorMinorPatchVersion<Integer>> version = serverVersion;
Arrays.stream(Objects.requireNonNullElse(KettingFiles.KETTINGSERVER_FORGE_DIR.listFiles(File::isDirectory), new File[0]))
.filter(file -> !Objects.equals(file.getName(),String.format("%s-%s-%s", mc_mmp, version.t1(), version.t2())))
.forEach(FileUtils::deleteDir);
}else{
serverVersion = versions.get(0);
}
final boolean needsDownload = versions.isEmpty();

if (needsDownload) System.out.println("Downloading Server, since there is none currently present. Using determined Minecraft version: "+ mc_version);
if (args.enableServerUpdator() || needsDownload) {
final List<String> serverVersions = MavenArtifact.getDepVersions(KettingConstants.KETTINGSERVER_GROUP, "forge");
Expand All @@ -142,78 +148,39 @@ else if(versions.size() > 1) {
System.out.println(String.join("\n", serverVersions));
}
if (parsedServerVersions.isEmpty()) {
System.err.println("Found no Ketting version for the requested Minecraft Version: "+mc_version);
System.err.println("Found no Ketting version for the requested Minecraft Version: " + mc_version);
System.exit(1);
}
Tuple<MajorMinorPatchVersion<Integer>, MajorMinorPatchVersion<Integer>> version = parsedServerVersions.get(0);
final String mc_minecraft_forge = String.format("%s-%s-%s", mc_mmp, version.t1(), version.t2());
final String path = MavenArtifact.getPath(KettingConstants.KETTINGSERVER_GROUP, Main.FORGE_SERVER_ARTIFACT_ID)+"/" + mc_minecraft_forge + "/";
serverVersion = parsedServerVersions.get(0);
}
//todo: Server version is populated here. We could theoretically do what used to be the patched invalidation check here.
if (args.enableServerUpdator() || needsDownload) {
final String mc_minecraft_forge = String.format("%s-%s-%s", mc_mmp, serverVersion.t1(), serverVersion.t2());
final File forgeDir = new File(KettingFiles.KETTINGSERVER_FORGE_DIR,mc_minecraft_forge);
final String forgeFilePrefix = Main.FORGE_SERVER_ARTIFACT_ID+"-"+mc_minecraft_forge+"-";
final String serverBinPatchesEnding = "server-bin-patches.lzma";
final String installerJsonEnding = "installscript.json";
final String kettingLibsEnding = "ketting-libraries.txt";
final String universalJarEnding = "universal.jar";
final File installerJson = new File(forgeDir, forgeFilePrefix+installerJsonEnding);
final File kettingLibs = new File(forgeDir, forgeFilePrefix+kettingLibsEnding);
final File universalJar = new File(forgeDir, forgeFilePrefix+universalJarEnding);

//noinspection ResultOfMethodCallIgnored
forgeDir.mkdirs();
if (Main.DEBUG) System.out.println(forgeDir.getAbsolutePath());
//noinspection ResultOfMethodCallIgnored
KettingFiles.SERVER_LZMA.getParentFile().mkdirs();
Exception exception = new Exception("Failed to download all required files");
boolean downloaded = false;
for (final String repo:AvailableMavenRepos.INSTANCE) {
Exception exception1 = new Exception("the repo only provided some files");
boolean allFine = true;
try{
final String serverBinPatches = repo+path+forgeFilePrefix+serverBinPatchesEnding;
NetworkUtils.downloadFile(serverBinPatches, KettingFiles.SERVER_LZMA, NetworkUtils.readFile(serverBinPatches+".sha512"),"SHA-512");
}catch (Throwable throwable) {
exception1.addSuppressed(throwable);
allFine = false;
}

try {
final String installerJsonURL = repo+path+forgeFilePrefix+installerJsonEnding;
NetworkUtils.downloadFile(installerJsonURL, installerJson, NetworkUtils.readFile(installerJsonURL+".sha512"), "SHA-512");
}catch (Throwable throwable){
exception1.addSuppressed(throwable);
allFine = false;
}

try {
final String kettingLibsURL = repo+path+forgeFilePrefix+kettingLibsEnding;
NetworkUtils.downloadFile(kettingLibsURL, kettingLibs, NetworkUtils.readFile(kettingLibsURL+".sha512"), "SHA-512");
} catch (Throwable throwable){
exception1.addSuppressed(throwable);
allFine = false;
}

try {
final String universalJarURL = repo+path+forgeFilePrefix+universalJarEnding;
NetworkUtils.downloadFile(universalJarURL, universalJar, NetworkUtils.readFile(universalJarURL+".sha512"), "SHA-512");
} catch (Throwable throwable){
exception1.addSuppressed(throwable);
allFine = false;
}
try{
final MavenArtifact serverBinPatchesArtifact = new MavenArtifact(KettingConstants.KETTINGSERVER_GROUP, "forge", mc_minecraft_forge, Optional.of("server-bin-patches"), Optional.of("lzma"));
final MavenArtifact installerJsonArtifact = new MavenArtifact(KettingConstants.KETTINGSERVER_GROUP, "forge", mc_minecraft_forge, Optional.of("installscript"), Optional.of("json"));
final MavenArtifact kettingLibsArtifact = new MavenArtifact(KettingConstants.KETTINGSERVER_GROUP, "forge", mc_minecraft_forge, Optional.of("ketting-libraries"), Optional.of("txt"));
final MavenArtifact universalJarArtifact = new MavenArtifact(KettingConstants.KETTINGSERVER_GROUP, "forge", mc_minecraft_forge, Optional.of("universal"), Optional.of("jar"));

if (allFine) {
downloaded = true;
break;
}
exception.addSuppressed(exception1);
}
if (!downloaded) {
LibHelper.downloadDependency(LibHelper.downloadDependencyHash(serverBinPatchesArtifact));
LibHelper.downloadDependency(LibHelper.downloadDependencyHash(installerJsonArtifact));
LibHelper.downloadDependency(LibHelper.downloadDependencyHash(kettingLibsArtifact));
LibHelper.downloadDependency(LibHelper.downloadDependencyHash(universalJarArtifact));
}catch (IOException|NoSuchAlgorithmException ignored){
FileUtils.deleteDir(forgeDir);
throw exception;
throw ignored;
}
}
}

void launch() throws NoSuchAlgorithmException, IOException {
void launch() throws Exception {
Libraries libs = new Libraries();
{
StringBuilder builder = new StringBuilder();
Expand All @@ -227,25 +194,30 @@ void launch() throws NoSuchAlgorithmException, IOException {
.filter(dep-> Arrays.stream(MANUALLY_PATCHED_LIBS).noneMatch(path -> dep.maven().get().getPath().startsWith(path)))
.peek(dep-> builder.append(File.pathSeparator).append(new File(KettingFiles.LIBRARIES_DIR, dep.maven().get().getPath()).getAbsolutePath()))
.toList(),
true,
"SHA-512"
true
);
}

builder.append(File.pathSeparator).append(KettingFileVersioned.FORGE_UNIVERSAL_JAR.getAbsolutePath());
libs.addLoadedLib(KettingFileVersioned.FORGE_UNIVERSAL_JAR.toURI().toURL());
MavenArtifact universalJarArtifact = new MavenArtifact(KettingConstants.KETTINGSERVER_GROUP, "forge", KettingConstants.MINECRAFT_VERSION+"-"+KettingConstants.FORGE_VERSION+"-"+KettingConstants.KETTING_VERSION, Optional.of("universal"), Optional.of("jar"));

libs.loadDep(LibHelper.downloadDependencyHash(universalJarArtifact));

System.setProperty("java.class.path", builder.toString());
System.setProperty("ketting.remapper.dump", "./.mixin.out/plugin_classes");
addToClassPath(KettingFileVersioned.FORGE_PATCHED_JAR);
addToClassPath(KettingFileVersioned.FMLCORE);
addToClassPath(KettingFileVersioned.FMLLOADER);
addToClassPath(KettingFileVersioned.JAVAFMLLANGUAGE);
addToClassPath(KettingFileVersioned.LOWCODELANGUAGE);
addToClassPath(KettingFileVersioned.MCLANGUAGE);
addToClassPath(KettingFileVersioned.SERVER_JAR);
// addToClassPath(KettingFileVersioned.FORGE_UNIVERSAL_JAR);
// addToClassPath(KettingFileVersioned.FMLCORE);
// addToClassPath(KettingFileVersioned.FMLLOADER);
// addToClassPath(KettingFileVersioned.JAVAFMLLANGUAGE);
// addToClassPath(KettingFileVersioned.LOWCODELANGUAGE);
// addToClassPath(KettingFileVersioned.MCLANGUAGE);
}
Libraries.downloadMcp();

if (Patcher.updateNeeded()) new Patcher();

new Patcher(); //todo: patcher update checking

System.out.println("Launching Ketting...");
final List<String> arg_list = new ArrayList<>(args.args());
Expand All @@ -261,9 +233,11 @@ void launch() throws NoSuchAlgorithmException, IOException {
Class.forName("net.minecraftforge.bootstrap.ForgeBootstrap", true, loader)
.getMethod("main", String[].class)
.invoke(null, (Object) arg_list.toArray(String[]::new));
} catch (Throwable t) {
throw new RuntimeException("Could not launch server", t);
} finally{
}
// catch (Throwable t) {
// throw new RuntimeException("Could not launch server", t);
// }
finally{
Thread.currentThread().setContextClassLoader(oldCL);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/kettingpowered/launcher/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static void agentmain(String agentArgs, Instrumentation inst) throws IOEx
final Libraries libs = new Libraries();
//Download all needed libs for the Launcher itself
try (BufferedReader stream = new BufferedReader(new InputStreamReader(Objects.requireNonNull(Main.class.getClassLoader().getResourceAsStream("data/launcher_libraries.txt"))))){
libs.downloadExternal(stream.lines().map(Dependency::parse).filter(Optional::isPresent).map(Optional::get).toList(), false, "SHA-512");
libs.downloadExternal(stream.lines().map(Dependency::parse).filter(Optional::isPresent).map(Optional::get).toList(), false);
}

Arrays.stream(libs.getLoadedLibs()).forEach(url -> {
Expand Down
64 changes: 15 additions & 49 deletions src/main/java/org/kettingpowered/launcher/Patcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public class Patcher {
public Patcher() throws IOException, NoSuchAlgorithmException {
downloadServer();
readInstallScript();
extractJarContents();
prepareTokens();
readAndExecuteProcessors();
}
Expand Down Expand Up @@ -80,28 +79,23 @@ private void downloadServer() throws IOException {
}

private void readInstallScript() {
InputStream stream = getClass().getClassLoader().getResourceAsStream(KettingFiles.DATA_DIR + "installscript.json");
if (stream == null) {
System.err.println("Failed to load installscript.json");
try(FileReader reader = new FileReader(KettingFileVersioned.FORGE_INSTALL_JSON)){
final JsonObject object = JsonParser.parseReader(reader).getAsJsonObject();
JsonArray rawProcessors = object.getAsJsonArray("processors");

rawProcessors.forEach(p -> {
JsonObject processor = p.getAsJsonObject();
if (!processor.has("sides") || processor.get("sides").getAsString().contains("server"))
processors.add(processor);
});

processors.remove(0); //Remove the extracting processor, we'll handle that ourselves
}catch (IOException exception){
System.err.println("Failed to load/read installscript.json");
System.exit(1);
}

final JsonObject object = JsonParser.parseReader(new InputStreamReader(stream)).getAsJsonObject();
JsonArray rawProcessors = object.getAsJsonArray("processors");

rawProcessors.forEach(p -> {
JsonObject processor = p.getAsJsonObject();
if (!processor.has("sides") || processor.get("sides").getAsString().contains("server"))
processors.add(processor);
});

processors.remove(0); //Remove the extracting processor, we'll handle that ourselves
}

private void extractJarContents() throws IOException {
ForgeServerLibExtractor.extract();
JarTool.extractJarContent(KettingFiles.DATA_DIR + "server.lzma", KettingFiles.SERVER_LZMA);
}


private void prepareTokens() {
tokens.put("{SIDE}", "server");
Expand All @@ -119,8 +113,6 @@ private void prepareTokens() {
}

private void readAndExecuteProcessors() throws NoSuchAlgorithmException, IOException {
if (!updateNeeded()) return;

final File logFile = KettingFiles.PATCHER_LOGS;
if (!logFile.exists()) {
try {
Expand Down Expand Up @@ -183,16 +175,6 @@ public void write(int b) {
}
});
}

final File hashes = KettingFiles.STORED_HASHES;
//noinspection ResultOfMethodCallIgnored
hashes.getParentFile().mkdirs();
//noinspection ResultOfMethodCallIgnored
hashes.createNewFile();

try (FileWriter writer = new FileWriter(hashes)) {
writer.write("serverjar=" + Hash.getHash(JarTool.getJar(), "SHA-512"));
}
}

private void mute() {
Expand All @@ -202,21 +184,5 @@ private void mute() {
private void unmute() {
System.setOut(out);
}

public static boolean updateNeeded() throws NoSuchAlgorithmException, IOException {
final File hashes = KettingFiles.STORED_HASHES;
if (hashes.exists()) {
final String serverHash = Hash.getHash(JarTool.getJar(), "SHA-512");

try (FileReader reader = new FileReader(hashes)) {
final Properties properties = new Properties();
properties.load(reader);

final String storedServerHash = properties.getProperty("serverjar");
if (storedServerHash != null && storedServerHash.equals(serverHash))
return false;
}
}
return true;
}

}
Loading

0 comments on commit ed4405a

Please sign in to comment.