Skip to content

Commit

Permalink
Refactoring to expose more limited API
Browse files Browse the repository at this point in the history
  • Loading branch information
LunNova committed Jan 29, 2016
1 parent f5d0a06 commit b56abfb
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 128 deletions.
3 changes: 1 addition & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ ext.fullForgeVersion = mcVersion + "-" + forgeVersion

minecraft {
version = fullForgeVersion
runDir = "eclipse/assets"
mappings = "stable_20"
}

Expand Down Expand Up @@ -120,5 +119,5 @@ jar.manifest.mainAttributes(
"Implementation-Title": name,
"Implementation-Version": version + "+" + ciSystem + "-b" + buildNumber + ".git-" + commit,
"Implementation-Vendor": url,
"FMLCorePlugin": "me.nallar.modpatcher.CoreMod"
"FMLCorePlugin": "me.nallar.modpatcher.coremod.CoreMod"
)
5 changes: 0 additions & 5 deletions src/main/java/javassist/ClassLoaderPool.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ public ClassLoaderPool() {
this.importPackage("java.util");
}

@Override
protected void cacheCtClass(String className, CtClass c, boolean dynamic) {
super.cacheCtClass(className, c, dynamic);
}

@Override
public CtClass getCached(String className) {
return super.getCached(className);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ private static byte[] runTransformer(final String name, final String transformed
private static byte[] transformUpToSrg(final String name, final String transformedName, byte[] basicClass) {
Iterable<IClassTransformer> transformers = getTransformers();
for (final IClassTransformer transformer : transformers) {
if (transformer == ModPatcher.getInstance()) {
if (transformer == ModPatcherTransformer.getInstance()) {
cacheSrgBytes(transformedName, basicClass);
return basicClass;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package me.nallar.modpatcher.mappings;
package me.nallar.modpatcher;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import me.nallar.javapatcher.mappings.ClassDescription;
import me.nallar.javapatcher.mappings.FieldDescription;
import me.nallar.javapatcher.mappings.Mappings;
import me.nallar.javapatcher.mappings.MethodDescription;
import me.nallar.modpatcher.PatcherLog;

import java.io.*;
import java.util.*;
import java.util.regex.*;

public class MCPMappings extends Mappings {
class MCPMappings extends Mappings {
private static final Pattern classObfuscatePattern = Pattern.compile("\\^class:([^\\^]+)\\^", Pattern.DOTALL | Pattern.MULTILINE);
private static final Pattern methodObfuscatePattern = Pattern.compile("\\^method:([^\\^/]+)/([^\\^/]+)\\^", Pattern.DOTALL | Pattern.MULTILINE);
private static final Pattern fieldObfuscatePattern = Pattern.compile("\\^field:([^\\^/]+)/([^\\^/]+)\\^", Pattern.DOTALL | Pattern.MULTILINE);
Expand Down
117 changes: 18 additions & 99 deletions src/main/java/me/nallar/modpatcher/ModPatcher.java
Original file line number Diff line number Diff line change
@@ -1,50 +1,28 @@
package me.nallar.modpatcher;

import javassist.ClassLoaderPool;
import me.nallar.javapatcher.patcher.Patcher;
import me.nallar.javapatcher.patcher.Patches;
import me.nallar.modpatcher.mappings.MCPMappings;
import net.minecraft.launchwrapper.IClassTransformer;

import java.io.*;
import java.nio.file.*;

public class ModPatcher implements IClassTransformer {
private static final String MOD_PATCHES_DIRECTORY = "./ModPatches/";
private static final Patcher patcher;
private static final String ALREADY_LOADED_PROPERTY_NAME = "nallar.ModPatcher.alreadyLoaded";
private static final String DUMP_PROPERTY_NAME = "nallar.ModPatcher.dump";
private static final boolean DUMP = !System.getProperty(DUMP_PROPERTY_NAME, "").isEmpty();

/**
*
*/
public class ModPatcher {
static {
PatcherLog.info("ModPatcher running under classloader " + ModPatcher.class.getClassLoader().getClass().getName());
boolean alreadyLoaded = System.getProperty(ALREADY_LOADED_PROPERTY_NAME) != null;
if (alreadyLoaded) {
PatcherLog.error("Detected multiple classloads of ModPatcher - classloading issue?", new Throwable());
} else {
System.setProperty(ALREADY_LOADED_PROPERTY_NAME, "true");
if (ModPatcherTransformer.class.getClassLoader().getClass().getName().contains("LaunchClassLoader")) {
throw new Error("ModPatcher must be loaded in the system classloader, not: " + ModPatcherTransformer.class.getClassLoader());
}
Patcher postSrgPatcher_;
try {
postSrgPatcher_ = new Patcher(new ClassLoaderPool(), Patches.class, new MCPMappings());
} catch (Exception t) {
PatcherLog.error("Failed to create Patcher", t);
throw new RuntimeException(t);
}
patcher = postSrgPatcher_;
// TODO - issue #2. Determine layout/config file structure
recursivelyAddXmlFiles(new File(MOD_PATCHES_DIRECTORY), patcher);
}

private boolean init;

/**
* Gets the JavaPatcher Patcher instance
*
* @return the Patcher
*/
public static Patcher getPatcher() {
return patcher;
return ModPatcherTransformer.getPatcher();
}

public static void loadMixin(Class<?> mixinClass) {

}

/**
Expand All @@ -56,71 +34,12 @@ public static String getSetupClass() {
return "me.nallar.modpatcher.ModPatcherSetup";
}

private static void recursivelyAddXmlFiles(File directory, Patcher patcher) {
if (!directory.isDirectory()) {
return;
}
try {
for (File f : directory.listFiles()) {
if (f.isDirectory()) {
recursivelyAddXmlFiles(f, patcher);
} else if (f.getName().endsWith(".xml")) {
patcher.readPatchesFromXmlInputStream(new FileInputStream(f));
} else if (f.getName().endsWith(".json")) {
patcher.readPatchesFromJsonInputStream(new FileInputStream(f));
}
}
} catch (IOException e) {
PatcherLog.warn("Failed to load patch", e);
}
}

public static byte[] postSrgTransformationHook(String name, String transformedName, byte[] originalBytes) {
LaunchClassLoaderUtil.cacheSrgBytes(transformedName, originalBytes);
try {
return patcher.patch(transformedName, originalBytes);
} catch (Throwable t) {
PatcherLog.error("Failed to patch " + transformedName, t);
}
return originalBytes;
}

static void modPatcherAsCoreModStartup() {
File modPatchesDirectory = new File(MOD_PATCHES_DIRECTORY);
if (!modPatchesDirectory.exists()) {
modPatchesDirectory.mkdir();
try {
Files.copy(ModPatcher.class.getResourceAsStream("/modpatcher.json.example"), new File(modPatchesDirectory, "/modpatcher.json.example").toPath(), StandardCopyOption.REPLACE_EXISTING);
Files.copy(ModPatcher.class.getResourceAsStream("/modpatcher.xml.example"), new File(modPatchesDirectory, "/modpatcher.xml.example").toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
PatcherLog.warn("Failed to extract example patcher files", e);
}
}
}

public static IClassTransformer getInstance() {
return LazyModPatcherHolder.INSTANCE;
}

@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (!init) {
init = true;
getPatcher().logDebugInfo();
}
if (DUMP) {
Path path = Paths.get("./DUMP/" + name);
try {
Files.createDirectories(path.getParent());
Files.write(path, bytes);
} catch (IOException e) {
PatcherLog.error("Failed to dump class " + name, e);
}
}
return postSrgTransformationHook(name, transformedName, bytes);
}

private static class LazyModPatcherHolder {
private static final ModPatcher INSTANCE = new ModPatcher();
/**
* Gets the directory
*
* @return
*/
public static String getDefaultPatchesDirectory() {
return ModPatcherTransformer.getDefaultPatchesDirectory();
}
}
16 changes: 1 addition & 15 deletions src/main/java/me/nallar/modpatcher/ModPatcherSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,9 @@
* <pre><code>@Override public String getSetupClass() { return ModPatcher.getSetupClass(); }</code></pre>
*/
public class ModPatcherSetup implements IFMLCallHook {
private static boolean modPatcherInitialised = false;

@Override
public void injectData(Map<String, Object> data) {
initialised((LaunchClassLoader) data.get("classLoader"));
}

private void initialised(LaunchClassLoader classLoader) {
if (modPatcherInitialised) {
return;
}
modPatcherInitialised = true;

classLoader.addClassLoaderExclusion("me.nallar.modpatcher");
classLoader.addClassLoaderExclusion("javassist");
LaunchClassLoaderUtil.instance = classLoader;
LaunchClassLoaderUtil.addTransformer(ModPatcher.getInstance());
ModPatcherTransformer.initialiseClassLoader((LaunchClassLoader) data.get("classLoader"));
}

@Override
Expand Down
117 changes: 117 additions & 0 deletions src/main/java/me/nallar/modpatcher/ModPatcherTransformer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package me.nallar.modpatcher;

import javassist.ClassLoaderPool;
import me.nallar.javapatcher.patcher.Patcher;
import me.nallar.javapatcher.patcher.Patches;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.LaunchClassLoader;

import java.io.*;
import java.nio.file.*;

class ModPatcherTransformer implements IClassTransformer {
private static final String MOD_PATCHES_DIRECTORY = "./ModPatches/";
private static final Patcher patcher;
private static final String ALREADY_LOADED_PROPERTY_NAME = "nallar.ModPatcher.alreadyLoaded";
private static final String DUMP_PROPERTY_NAME = "nallar.ModPatcher.dump";
private static final boolean DUMP = !System.getProperty(DUMP_PROPERTY_NAME, "").isEmpty();
private static boolean classLoaderInitialised = false;

static {
PatcherLog.info("ModPatcher running under classloader " + ModPatcherTransformer.class.getClassLoader().getClass().getName());
boolean alreadyLoaded = System.getProperty(ALREADY_LOADED_PROPERTY_NAME) != null;
if (alreadyLoaded) {
PatcherLog.error("Detected multiple classloads of ModPatcher - classloading issue?", new Throwable());
} else {
System.setProperty(ALREADY_LOADED_PROPERTY_NAME, "true");
}
Patcher postSrgPatcher_;
try {
postSrgPatcher_ = new Patcher(new ClassLoaderPool(), Patches.class, new MCPMappings());
} catch (Exception t) {
PatcherLog.error("Failed to create Patcher", t);
throw new RuntimeException(t);
}
patcher = postSrgPatcher_;
// TODO - issue #2. Determine layout/config file structure
recursivelyAddXmlFiles(new File(MOD_PATCHES_DIRECTORY), patcher);
}

private boolean init;

static Patcher getPatcher() {
return patcher;
}

private static void recursivelyAddXmlFiles(File directory, Patcher patcher) {
File[] files = directory.listFiles();
if (files == null)
return;

try {
for (File f : files) {
if (f.isDirectory()) {
recursivelyAddXmlFiles(f, patcher);
} else if (f.getName().endsWith(".xml")) {
patcher.readPatchesFromXmlInputStream(new FileInputStream(f));
} else if (f.getName().endsWith(".json")) {
patcher.readPatchesFromJsonInputStream(new FileInputStream(f));
}
}
} catch (IOException e) {
PatcherLog.warn("Failed to load patch", e);
}
}

static byte[] postSrgTransformationHook(String name, String transformedName, byte[] originalBytes) {
LaunchClassLoaderUtil.cacheSrgBytes(transformedName, originalBytes);
try {
return patcher.patch(transformedName, originalBytes);
} catch (Throwable t) {
PatcherLog.error("Failed to patch " + transformedName, t);
}
return originalBytes;
}

static IClassTransformer getInstance() {
return LazyModPatcherHolder.INSTANCE;
}

public static void initialiseClassLoader(LaunchClassLoader classLoader) {
if (classLoaderInitialised)
return;

classLoaderInitialised = true;

classLoader.addClassLoaderExclusion("me.nallar.modpatcher");
classLoader.addClassLoaderExclusion("javassist");
LaunchClassLoaderUtil.instance = classLoader;
LaunchClassLoaderUtil.addTransformer(ModPatcherTransformer.getInstance());
}

static String getDefaultPatchesDirectory() {
return MOD_PATCHES_DIRECTORY;
}

@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
if (!init) {
init = true;
patcher.logDebugInfo();
}
if (DUMP) {
Path path = Paths.get("./DUMP/" + name);
try {
Files.createDirectories(path.getParent());
Files.write(path, bytes);
} catch (IOException e) {
PatcherLog.error("Failed to dump class " + name, e);
}
}
return postSrgTransformationHook(name, transformedName, bytes);
}

private static class LazyModPatcherHolder {
private static final ModPatcherTransformer INSTANCE = new ModPatcherTransformer();
}
}
23 changes: 20 additions & 3 deletions ...in/java/me/nallar/modpatcher/CoreMod.java → ...me/nallar/modpatcher/coremod/CoreMod.java
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package me.nallar.modpatcher;
package me.nallar.modpatcher.coremod;

import me.nallar.modpatcher.ModPatcher;
import me.nallar.modpatcher.PatcherLog;
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.appender.FileAppender;

import java.io.*;
import java.nio.file.*;
import java.util.*;

@IFMLLoadingPlugin.Name("ModPatcher")
Expand All @@ -20,6 +24,10 @@ private static void logToFile() {
((org.apache.logging.log4j.core.Logger) LogManager.getLogger("JavaPatcher")).addAppender(fa);
}

private static void extractFile(String name, File to) throws IOException {
Files.copy(CoreMod.class.getResourceAsStream(name), new File(to, name).toPath(), StandardCopyOption.REPLACE_EXISTING);
}

@Override
public String[] getASMTransformerClass() {
return new String[0];
Expand All @@ -37,12 +45,21 @@ public String getSetupClass() {

@Override
public void injectData(Map<String, Object> data) {
ModPatcher.modPatcherAsCoreModStartup();
File modPatchesDirectory = new File(ModPatcher.getDefaultPatchesDirectory());
if (!modPatchesDirectory.exists() && !modPatchesDirectory.mkdirs()) {
throw new IOError(new IOException("Failed to make directory " + modPatchesDirectory));
}

try {
extractFile("/modpatcher.json.example", modPatchesDirectory);
extractFile("/modpatcher.xml.example", modPatchesDirectory);
} catch (IOException e) {
PatcherLog.warn("Failed to extract example patcher files", e);
}
}

@Override
public String getAccessTransformerClass() {
return null;
}

}

0 comments on commit b56abfb

Please sign in to comment.