Preparing junit tests

This commit is contained in:
Lassebq 2023-08-25 11:06:07 +03:00
parent 6cfbaf91de
commit e693b7254a
No known key found for this signature in database
GPG key ID: DE0866BB0C980B6E
17 changed files with 112 additions and 50 deletions

View file

@ -1,6 +1,5 @@
plugins {
id 'java'
id 'eclipse'
id 'maven-publish'
}
@ -21,13 +20,23 @@ group = 'org.mcphackers'
archivesBaseName = 'launchwrapper'
version = '1.0-SNAPSHOT'
sourceCompatibility = 1.5
targetCompatibility = 1.5
dependencies {
implementation 'org.mcphackers.rdi:rdi:1.0'
implementation 'org.ow2.asm:asm:9.3'
implementation 'org.ow2.asm:asm-tree:9.3'
implementation 'org.json:json:20230311'
// implementation name: 'discord-rpc-1.6.2'
// I'll bring discord RPC support later, when I have an environment to compile natives
testCompileOnly 'junit:junit:4.12'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.0.0'
}
test {
useJUnitPlatform()
workingDir = "build"
testLogging.showStandardStreams = true
}
task sourcesJar(type: Jar) {

View file

@ -6,6 +6,10 @@ import java.awt.Image;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
import org.mcphackers.launchwrapper.tweak.AppletWrapper;
/**
* Runs an applet
* (For example net.minecraft.client.MinecraftApplet)
*/
public class AppletLaunchTarget extends LaunchTarget {
private final String targetClass;

View file

@ -1,14 +1,12 @@
package org.mcphackers.launchwrapper;
import java.io.File;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
import org.mcphackers.launchwrapper.tweak.Tweak;
public class Launch {
/**
* Class loader where overwritten classes are stored
* Class loader where overwritten classes will be stored
*/
public static final LaunchClassLoader CLASS_LOADER = LaunchClassLoader.instantiate();
static {
@ -36,7 +34,6 @@ public class Launch {
}
public void launch() {
Launch.CLASS_LOADER.setDebugOutput(new File(config.gameDir.get(), "debug"));
Tweak mainTweak = Tweak.get(CLASS_LOADER, config);
if(mainTweak == null) {
System.err.println("Could not find launch target");
@ -46,6 +43,7 @@ public class Launch {
if(config.discordRPC.get()) {
setupDiscordRPC();
}
mainTweak.clear(); // Clearing some garbage
mainTweak.getLaunchTarget().launch(CLASS_LOADER);
} else {
System.err.println("Tweak could not be applied");
@ -53,38 +51,7 @@ public class Launch {
}
protected void setupDiscordRPC() {
// String applicationId = "356875570916753438";
// DiscordEventHandlers handlers = new DiscordEventHandlersAdapter() {};
// DiscordRPC.discordInitialize(applicationId, handlers, true);
// DiscordRichPresence presence = new DiscordRichPresence();
// if(config.version.get() != null) {
// presence.state = "Version: " + config.version.get();
// }
// if(config.username.get() != null) {
// presence.details = "Username: " + config.username.get();
// }
// presence.startTimestamp = System.currentTimeMillis() / 1000;
// presence.largeImageKey = "https://cdn.discordapp.com/app-icons/356875570916753438/166fbad351ecdd02d11a3b464748f66b.png?size=256";
// DiscordRPC.discordUpdatePresence(presence);
// final Thread thread =
// new Thread("Discord RPC") {
// public void run() {
// while(true) {
// DiscordRPC.discordRunCallbacks();
// try {
// Thread.sleep(2000);
// } catch (InterruptedException e) {
// }
// }
// }
// };
// Runtime.getRuntime().addShutdownHook(new Thread() {
// public void run() {
// DiscordRPC.discordShutdown();
// thread.interrupt();
// }
// });
// thread.start();
// TODO
}
public static Launch getInstance() {

View file

@ -14,6 +14,7 @@ import org.mcphackers.launchwrapper.util.OS;
public class LaunchConfig {
private static final File defaultGameDir = getDefaultGameDir();
//TODO allow arbitary parameters instead of the ones documented below
private Map<String, LaunchParameter<?>> parameters = new HashMap<String, LaunchParameter<?>>();
public LaunchParameterSwitch demo = new LaunchParameterSwitch("demo", false);

View file

@ -2,6 +2,9 @@ package org.mcphackers.launchwrapper;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
/**
* Different types of entry point (main method, applet, etc)
*/
public abstract class LaunchTarget {
public abstract void launch(LaunchClassLoader classLoader);

View file

@ -2,6 +2,10 @@ package org.mcphackers.launchwrapper;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
/**
* Invokes MainClass.main(String[] args)
* (For example net.minecraft.client.main.Main)
*/
public class MainLaunchTarget extends LaunchTarget {
public String targetClass;

View file

@ -20,6 +20,7 @@ import org.mcphackers.launchwrapper.util.Util;
/**
* DO NOT use this class under any circumstances. This class is meant to be
* initialized in the context of LaunchClassLoader
* This class contains methods which are used in different game classes, sort of like mixins
*
* @author Lassebq
*

View file

@ -39,6 +39,7 @@ public class SaveLevelURLConnection extends HttpURLConnection {
}
@Override
@SuppressWarnings("unused")
public InputStream getInputStream() throws IOException {
Exception exception = null;
try {

View file

@ -4,5 +4,10 @@ import org.objectweb.asm.tree.ClassNode;
public abstract class ClassLoaderTweak {
/**
* Called on every class node loaded by LaunchClassLoader
* @param node
* @return true if the given ClassNode was modified
*/
public abstract boolean tweakClass(ClassNode node);
}

View file

@ -0,0 +1,19 @@
package org.mcphackers.launchwrapper.tweak;
/**
* Info about features applied by a tweak
* Useful for tests
*/
public class FeatureInfo {
public final String feature;
public String[] info;
public FeatureInfo(String name) {
feature = name;
}
public FeatureInfo(String name, String[] additionalInfo) {
feature = name;
info = additionalInfo;
}
}

View file

@ -13,10 +13,15 @@ import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
/**
* Compatibility fixes for Java 5
* Replaces most of Java 6 API used by Minecraft with Java 5 alternative
*/
public class LegacyClassLoaderTweak extends ClassLoaderTweak {
public boolean tweakClass(ClassNode node) {
boolean changed = false;
// Decrease class version
if(node.version > LaunchClassLoader.CLASS_VERSION) {
node.version = LaunchClassLoader.CLASS_VERSION;
changed = true;
@ -25,6 +30,7 @@ public class LegacyClassLoaderTweak extends ClassLoaderTweak {
AbstractInsnNode insn = m.instructions.getFirst();
while(insn != null) {
AbstractInsnNode[] insns = fill(insn, 3);
// Replace String.isEmpty() with String.length == 0
if(compareInsn(insns[0], INVOKEVIRTUAL, "java/lang/String", "isEmpty", "()Z")) {
MethodInsnNode invoke = new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "length", "()I");
m.instructions.set(insns[0], invoke);
@ -42,6 +48,7 @@ public class LegacyClassLoaderTweak extends ClassLoaderTweak {
changed = true;
continue;
}
// Replace String.getBytes(Charset.forName(charset)) with String.getBytes(charset)
if(compareInsn(insns[1], INVOKESTATIC, "java/nio/charset/Charset", "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;")
&& compareInsn(insns[2], INVOKEVIRTUAL, "java/lang/String", "getBytes", "(Ljava/nio/charset/Charset;)[B")
&& compareInsn(insns[0], LDC)) {
@ -49,6 +56,7 @@ public class LegacyClassLoaderTweak extends ClassLoaderTweak {
m.instructions.remove(insns[1]);
changed = true;
}
// Same thing but for new String()
if(compareInsn(insns[1], INVOKESTATIC, "java/nio/charset/Charset", "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;")
&& compareInsn(insns[2], INVOKESPECIAL, "java/lang/String", "<init>", "([BLjava/nio/charset/Charset;)V")
&& compareInsn(insns[0], LDC)) {

View file

@ -553,7 +553,7 @@ public class LegacyTweak extends Tweak {
}
if(!foundTitle && launch.title.get() != null) {
//TODO
//TODO figure out where to insert title
}
if(afterLabel != null
@ -936,7 +936,7 @@ public class LegacyTweak extends Tweak {
}
}
}
tweakInfo("MouseHelper fixed in " + (System.currentTimeMillis() - i) + " ms");
tweakInfo("MouseHelper fix", (System.currentTimeMillis() - i) + " ms");
source.overrideClass(mouseHelper);
}
@ -987,7 +987,7 @@ public class LegacyTweak extends Tweak {
&& containsInvoke(m.instructions, new MethodInsnNode(INVOKESTATIC, "org/lwjgl/input/Keyboard", "destroy", "()V"))
&& containsInvoke(m.instructions, new MethodInsnNode(INVOKESTATIC, "org/lwjgl/opengl/Display", "destroy", "()V"))) {
destroy = m;
tweakInfo(destroy.name + destroy.desc + " is the destroy() method");
tweakInfo("destroy()", destroy.name + destroy.desc);
break;
}
}
@ -1048,7 +1048,7 @@ public class LegacyTweak extends Tweak {
MethodInsnNode invoke = (MethodInsnNode) insns2[2];
if(Type.getReturnType(invoke.desc).getSort() == Type.VOID) {
addTryCatch(destroy, insns2[0], insns2[2], "java/lang/Throwable");
tweakInfo("Fixed sound manager shutdown (b1.0 - b1.3)");
tweakInfo("SoundManager shutdown");
break;
}
}
@ -1085,7 +1085,7 @@ public class LegacyTweak extends Tweak {
insn = insn.getPrevious();
}
if(lbl2 != null) {
tweakInfo("Indev launch tweak");
tweakInfo("Indev launch");
removeRange(setLevel.instructions, handler.start.getNext(), lbl2);
setLevel.tryCatchBlocks.remove(handler);
}
@ -1323,6 +1323,7 @@ public class LegacyTweak extends Tweak {
&& compareInsn(insns3[1], LDC, "DemoUser")
&& compareInsn(insns3[2], LDC, "n/a")
&& compareInsn(insns3[3], INVOKESPECIAL, node.superName, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V")) {
tweakInfo("DemoUser");
InsnList insert = new InsnList();
insert.add(new LdcInsnNode(launch.username.get()));
insert.add(new LdcInsnNode(launch.sessionid.get()));
@ -1359,6 +1360,7 @@ public class LegacyTweak extends Tweak {
insert.add(new JumpInsnNode(IFNULL, label));
init.instructions.insertBefore(insns2[0], insert);
init.instructions.insert(insns2[4], label);
tweakInfo("Custom server fix");
}
insn = nextInsn(insn);
}

View file

@ -1,5 +1,8 @@
package org.mcphackers.launchwrapper.tweak;
import java.util.ArrayList;
import java.util.List;
import org.mcphackers.launchwrapper.LaunchConfig;
import org.mcphackers.launchwrapper.LaunchTarget;
import org.mcphackers.launchwrapper.loader.LaunchClassLoader;
@ -7,16 +10,25 @@ import org.mcphackers.launchwrapper.util.ClassNodeSource;
public abstract class Tweak {
private static final boolean DEBUG = false;
protected ClassNodeSource source;
protected LaunchConfig launch;
private List<FeatureInfo> features = new ArrayList<FeatureInfo>();
/**
* Every tweak must implement this constructor
* Overloads are not supported!!!
* @param source
* @param launch
*/
public Tweak(ClassNodeSource source, LaunchConfig launch) {
this.source = source;
this.launch = launch;
}
/**
* This method does return true even if some of the changes weren't applied, even when they should've been
* @return true if given ClassNodeSource was modified without fatal errors
*/
public abstract boolean transform();
public abstract ClassLoaderTweak getLoaderTweak();
@ -34,6 +46,7 @@ public abstract class Tweak {
private static Tweak getTweak(LaunchClassLoader classLoader, LaunchConfig launch) {
if(launch.tweakClass.get() != null) {
try {
// Instantiate custom tweak if it's present on classpath;
return (Tweak)Class.forName(launch.tweakClass.get())
.getConstructor(ClassNodeSource.class, LaunchConfig.class)
.newInstance(classLoader, launch);
@ -41,6 +54,7 @@ public abstract class Tweak {
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
if(launch.isom.get()) {
@ -62,8 +76,11 @@ public abstract class Tweak {
return null; // Tweak not found
}
protected void tweakInfo(String msg) {
if(DEBUG)
System.out.println("TWEAK: " + msg);
protected void tweakInfo(String name, String... extra) {
features.add(new FeatureInfo(name));
}
public void clear() {
features.clear();
}
}

View file

@ -50,7 +50,7 @@ public class VanillaTweak extends Tweak {
MethodInsnNode invoke = (MethodInsnNode) insn;
minecraft = source.getClass(invoke.owner);
run = NodeHelper.getMethod(minecraft, invoke.name, invoke.desc);
tweakInfo(minecraft.name + "." + invoke.name + invoke.desc + " is Minecraft.run()");
tweakInfo("Minecraft.run()", minecraft.name + "." + invoke.name + invoke.desc);
break;
}
insn = previousInsn(insn);
@ -100,7 +100,7 @@ public class VanillaTweak extends Tweak {
LdcInsnNode ldc = (LdcInsnNode) insn;
if(ldc.cst instanceof String) {
if(launch.title.get() != null) {
tweakInfo("Replaced title");
tweakInfo("Replaced title", launch.title.get());
ldc.cst = launch.title.get();
return true;
}

View file

@ -31,6 +31,7 @@ public class InsnHelper {
return insn;
}
@SuppressWarnings("unused")
public static boolean compareInsn(AbstractInsnNode insn, int opcode, Object... compare) {
if(insn == null) {
return false;

View file

@ -0,0 +1,13 @@
package org.mcphackers.launchwrapper.test;
import java.io.File;
import org.junit.Test;
public class ClassicTest extends TweakTest {
@Test
public void test() {
System.out.println(new File(".").getAbsolutePath());
}
}

View file

@ -0,0 +1,7 @@
package org.mcphackers.launchwrapper.test;
public abstract class TweakTest {
public void testFramePatch() {
}
}