Thank you! I am currently in the middle of something, but as soon as I'm done I will try that. Once again, amazing tutorial, and fantastic support. I wish I could vote up your first post more than once.
Don't know if you can use it, but here is my loading plugin which automatically detects all class files in the given path ("patches/" in my case):
///////////////////////////////////////////////////////////////////////////////
// Package ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
package poersch.minecraft.bettergrassandleaves.core;
///////////////////////////////////////////////////////////////////////////////
// Imports ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.TransformerExclusions;
///////////////////////////////////////////////////////////////////////////////
// Better Grass & Leaves FML Plugin ///////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
@MCVersion(value = "1.6.2")
@TransformerExclusions( { "poersch.minecraft.bettergrassandleaves" } )
public class BGALLoadingPlugin implements IFMLLoadingPlugin {
///////////////////////////////////////////////////////////////////////////////
// Variable definitions ///////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
public static File location;
public static String[] patchList = new String[] { "" };
public static final String patchPath = "patches/";
///////////////////////////////////////////////////////////////////////////////
// Get library request class //////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
@Override
public String[] getLibraryRequestClass() {
return null;
}
///////////////////////////////////////////////////////////////////////////////
// Get ASM transformer class //////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
@Override
public String[] getASMTransformerClass() {
return new String[] { BGALClassTransformer.class.getName() };
}
///////////////////////////////////////////////////////////////////////////////
// Get mod container class ////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
@Override
public String getModContainerClass() {
return null;
}
///////////////////////////////////////////////////////////////////////////////
// Get setup class ////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
@Override
public String getSetupClass() {
return null;
}
///////////////////////////////////////////////////////////////////////////////
// Inject data ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
@Override
public void injectData(Map<String, Object> data) {
location = (File) data.get("coremodLocation");
updatePatchList();
}
///////////////////////////////////////////////////////////////////////////////
// Update patch list //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
private void updatePatchList() {
// Create class list
ArrayList<String> classes = new ArrayList<String>();
// Try to find all class files in the patches folder
int patchPathLength = patchPath.length();
try {
ZipFile zip = new ZipFile(location);
Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>)zip.entries();
while(entries.hasMoreElements()) {
String filename = entries.nextElement().getName();
if(filename.length() > patchPathLength && filename.substring(0, patchPathLength).equals(patchPath) && filename.substring(filename.length() - 6).equals(".class"))
classes.add(filename.substring(patchPathLength, filename.length() - 6));
}
zip.close();
} catch(Exception e) { }
// Update patch list
if(classes.isEmpty()) {
System.out.println("[Better Grass & Leaves Mod] Found no patches.");
patchList = new String[] { "" };
} else {
System.out.println("[Better Grass & Leaves Mod] Found " + classes.size() + " patches.");
patchList = classes.toArray(new String[classes.size()]);
}
}
}
And my class transformer:
///////////////////////////////////////////////////////////////////////////////
// Package ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
package poersch.minecraft.bettergrassandleaves.core;
///////////////////////////////////////////////////////////////////////////////
// Imports ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
import java.io.File;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
///////////////////////////////////////////////////////////////////////////////
// Better Signs Mod Class Transformer /////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
public class BGALClassTransformer implements net.minecraft.launchwrapper.IClassTransformer {
///////////////////////////////////////////////////////////////////////////////
// Transform the specified classses ///////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
@Override
public byte[] transform(String name, String arg1, byte[] data) {
for(int n = 0; n < BGALLoadingPlugin.patchList.length; n++)
if(name.equals(BGALLoadingPlugin.patchList[n]))
data = overwriteClass(name, data, BGALLoadingPlugin.location);
return data;
}
///////////////////////////////////////////////////////////////////////////////
// Overwrite specified class //////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
public byte[] overwriteClass(String name, byte[] data, File location) {
try {
ZipFile zip = new ZipFile(location);
ZipEntry entry = zip.getEntry("patches/" + name.replace('.', '/') + ".class");
if(entry == null) {
System.out.println("[Better Grass & Leaves Mod] Class " + name + " not found in " + location.getName());
} else {
InputStream zin = zip.getInputStream(entry);
data = new byte[(int) entry.getSize()];
zin.read(data);
zin.close();
System.out.println("[Better Grass & Leaves Mod] Class " + name + " patched!");
}
zip.close();
} catch (Exception e) {
throw new RuntimeException("[Better Grass & Leaves Mod] Error overwriting Class " + name + " from " + location.getName(), e);
}
return data;
}
}
Very nice! instead of hardcoding the class names, it's nicely done.
A cynic would object to the for loop in the transform method, maybe storing the class names in a hash-map would yield quicker execution.
anyway i'm holding my breath on the lastest release of forge 790. there's a TweakClass now I have no idea what that is, hope it won't break any coremods
Another thing, I saw a method were after getting the class from the zip serialized in a bytes, instead of returning the whole bytes and overriding the entire original class, what you could do is open another ASM reader on that new zip class and just find the ASM method instruction list you'd like to replace and replace them with ASM commands.
That way if 2 coremods try to replace the same class but each modify a different method, they won't conflict.
i.e. have one overwrite the work of the other.
Help! I followed your instructions, but I get this error:
Description: Initializing game
2013-07-12 13:03:25 [INFO] [STDOUT]
2013-07-12 13:03:25 [INFO] [STDOUT] java.lang.NoClassDefFoundError: net/minecraft/entity/player/EntityPlayerMP
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraftforge.common.ForgeDummyContainer.modConstruction(ForgeDummyContainer.java:151)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
2013-07-12 13:03:25 [INFO] [STDOUT] at java.lang.reflect.Method.invoke(Method.java:597)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventHandler.handleEvent(EventHandler.java:74)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.SynchronizedEventHandler.handleEvent(SynchronizedEventHandler.java:45)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.dispatch(EventBus.java:313)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.dispatchQueuedEvents(EventBus.java:296)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.post(EventBus.java:267)
2013-07-12 13:03:25 [INFO] [STDOUT] at cpw.mods.fml.common.LoadController.sendEventToModContainer(LoadController.java:193)
2013-07-12 13:03:25 [INFO] [STDOUT] at cpw.mods.fml.common.LoadController.propogateStateMessage(LoadController.java:173)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
2013-07-12 13:03:25 [INFO] [STDOUT] at java.lang.reflect.Method.invoke(Method.java:597)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventHandler.handleEvent(EventHandler.java:74)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.SynchronizedEventHandler.handleEvent(SynchronizedEventHandler.java:45)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.dispatch(EventBus.java:313)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.dispatchQueuedEvents(EventBus.java:296)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.post(EventBus.java:267)
2013-07-12 13:03:25 [INFO] [STDOUT] at cpw.mods.fml.common.LoadController.distributeStateMessage(LoadController.java:104)
2013-07-12 13:03:25 [INFO] [STDOUT] at cpw.mods.fml.common.Loader.loadMods(Loader.java:510)
2013-07-12 13:03:25 [INFO] [STDOUT] at cpw.mods.fml.client.FMLClientHandler.beginMinecraftLoading(FMLClientHandler.java:172)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.client.Minecraft.startGame(Minecraft.java:470)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.client.Minecraft.func_99999_d(Minecraft.java:796)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.client.main.Main.main(Main.java:93)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
2013-07-12 13:03:25 [INFO] [STDOUT] at java.lang.reflect.Method.invoke(Method.java:597)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.launchwrapper.Launch.launch(Launch.java:57)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.launchwrapper.Launch.main(Launch.java:18)
2013-07-12 13:03:25 [INFO] [STDOUT] Caused by: java.lang.ClassNotFoundException: net.minecraft.entity.player.EntityPlayerMP
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:179)
2013-07-12 13:03:25 [INFO] [STDOUT] at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
2013-07-12 13:03:25 [INFO] [STDOUT] at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
2013-07-12 13:03:25 [INFO] [STDOUT] ... 33 more
2013-07-12 13:03:25 [INFO] [STDOUT] Caused by: java.lang.NegativeArraySizeException
2013-07-12 13:03:25 [INFO] [STDOUT] at org.objectweb.asm.Frame.a(Unknown Source)
2013-07-12 13:03:25 [INFO] [STDOUT] at org.objectweb.asm.MethodWriter.visitMaxs(Unknown Source)
2013-07-12 13:03:25 [INFO] [STDOUT] at org.objectweb.asm.tree.MethodNode.accept(Unknown Source)
2013-07-12 13:03:25 [INFO] [STDOUT] at org.objectweb.asm.tree.MethodNode.accept(Unknown Source)
2013-07-12 13:03:25 [INFO] [STDOUT] at org.objectweb.asm.tree.ClassNode.accept(Unknown Source)
2013-07-12 13:03:25 [INFO] [STDOUT] at tlf.commandapi.ASM.Transformer.patchClassASM(Transformer.java:131)
2013-07-12 13:03:25 [INFO] [STDOUT] at tlf.commandapi.ASM.Transformer.transform(Transformer.java:32)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.launchwrapper.LaunchClassLoader.runTransformers(LaunchClassLoader.java:267)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:165)
2013-07-12 13:03:25 [INFO] [STDOUT] ... 35 more
The problem line (tlf.commandapi.ASM.Transformer.patchClassASM(Transformer.java:131)):
classNode.accept(writer);
Any idea why this is happening?
Full Transformer class here:
package tlf.commandapi.ASM;
import static org.objectweb.asm.Opcodes.LDC;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.tree.AbstractInsnNode.METHOD_INSN;
import java.util.Iterator;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
import net.minecraft.launchwrapper.IClassTransformer;
public class Transformer implements IClassTransformer {
@Override
public byte[] transform(String arg0, String arg1, byte[] arg2) {
if (arg0.equals("js")) {
System.out.println("********* INSIDE OBFUSCATED CLASS TRANSFORMER ABOUT TO PATCH: " + arg0);
return patchClassASM(arg0, arg2, true);
} else if (arg0.equals("net.minecraft.entity.player.EntityPlayerMP")) {
System.out.println("********* INSIDE DEOBFUSCATED CLASS TRANSFORMER ABOUT TO PATCH: " + arg0);
return patchClassASM(arg0, arg2, false);
}
return arg2;
}
public byte[] patchClassASM(String name, byte[] bytes, boolean obfuscated) {
String targetMethodName = "";
String targetMethodDesc = "(ILjava/lang/String;)Z";
//Our target method
if (obfuscated) {
targetMethodName = "a";
} else {
targetMethodName = "canCommandSenderUseCommand";
}
//set up ASM class manipulation stuff. Consult the ASM docs for details
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(bytes);
classReader.accept(classNode, 0);
//Now we loop over all of the methods declared inside the Explosion class until we get to the targetMethodName "doExplosionB"
Iterator methods = classNode.methods.iterator();
while(methods.hasNext())
{
MethodNode m = methods.next();
int tell_index = -1;
if ((m.name.equals(targetMethodName) && m.desc.equals(targetMethodDesc)))
{
System.out.println("********* Inside target method!");
AbstractInsnNode currentNode = null;
AbstractInsnNode targetNode = null;
Iterator iter = m.instructions.iterator();
int index = -1;
while (iter.hasNext())
{
index++;
currentNode = iter.next();
if (currentNode.getOpcode() == LDC)
{
LdcInsnNode temp = (LdcInsnNode)currentNode;
if (temp.cst.equals("tell"))
{
targetNode = currentNode;
tell_index = index;
}
}
}
if (targetNode == null || tell_index == -1)
{
System.out.println("Did not find all necessary target nodes! ABANDON CLASS!");
return bytes;
}
AbstractInsnNode[] remNodes = new AbstractInsnNode[11];
remNodes[0] = m.instructions.get(tell_index);
remNodes[1] = m.instructions.get(tell_index+1);
remNodes[2] = m.instructions.get(tell_index+2);
remNodes[3] = m.instructions.get(tell_index+3);
remNodes[4] = m.instructions.get(tell_index+4);
remNodes[5] = m.instructions.get(tell_index+5);
remNodes[6] = m.instructions.get(tell_index+6);
remNodes[7] = m.instructions.get(tell_index+7);
remNodes[8] = m.instructions.get(tell_index+8);
remNodes[9] = m.instructions.get(tell_index+9);
remNodes[10] = m.instructions.get(tell_index+10);
for (int i = 0; i < remNodes.length; i++)
{
System.out.println("Removing node #" + i);
m.instructions.remove(remNodes[i]);
}
//InsnList toInject = new InsnList();
//toInject.add(new VarInsnNode(ALOAD, 2));
//toInject.add(new MethodInsnNode(INVOKESTATIC, "tlf/commandapi/CommandAPIBase", "isPlayerCommand", "(Ljava/lang/String;)Z"));
//m.instructions.insert(targetNode, toInject);
System.out.println("Patching Complete!");
break;
}
}
//ASM specific for cleaning up and returning the final bytes for JVM processing.
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
classNode.accept(writer);
return writer.toByteArray();
}
}
Why on earth are you removing nodes?!
Best way to make sure if your bytcode is correct is to make a different forge enviroment. do manual changes on the code there. see how it looks in bytecode and try to replicate.
Best way to make sure if your bytcode is correct is to make a different forge enviroment. do manual changes on the code there. see how it looks in bytecode and try to replicate.
I did. That's how I got the list of things to remove. Here's a screenshot of the bytecode comparison. I'm trying to get the one on the left to look like the one on the right.
You did! I just changed the format a little to make it easier to scale.
I did. That's how I got the list of things to remove. Here's a screenshot of the bytecode comparison. I'm trying to get the one on the left to look like the one on the right.
You did! I just changed the format a little to make it easier to scale.
I did. That's how I got the list of things to remove. Here's a screenshot of the bytecode comparison. I'm trying to get the one on the left to look like the one on the right.
write to me in plain code what you want this method to be?
"FML and Forge will no longer be shipping entire Mojang class files, instead we will be patching our changing onto the vanilla codebase at runtime. {Don't worry its a one time hit when classes load so there is no server performance issues} This is our attempt to work with Mojang and try our best to respect there wishes in there TOS. Most notibly the 'Do not distribute any of our material'. As a unfortunate aspect of how Forge had to work before we couldn't do anything about that but now we can. The concept is simple, we take our Forge version, minus all the stuff that's identical to vanilla and only ship the changed stuff. For those who want to do a little more digging for more information, we currently use http://sourceforge.net/projects/javaxdelta/ to generate/apply our patches, and our GitHub has a lot more information."
I'll try to to change the tutorial to follow the same procedure.
Alright, one more question. How do you add a jump node? I found the code, but how do you set where it jumps to?
EDIT: I lied. I have another question. I'm thinking of maybe starting a modding tutorial series, and I would love to make a video on this. May I use your code? You would be credited, of course.
Alright, one more question. How do you add a jump node? I found the code, but how do you set where it jumps to?
EDIT: I lied. I have another question. I'm thinking of maybe starting a modding tutorial series, and I would love to make a video on this. May I use your code? You would be credited, of course.
for jump node use labels.
In the asm bytecode plugin, there's a an asm button in red (Show ASMified Code), if you click that it will give you the exact instructions.
you can copy over an entire method using that. and replace a method from the class node.
mv = new MethodNode(ACC_PUBLIC, "someMethod", "(Ljava/lang/String;[BZ)[B", null, null);
and at the end replace the classNode method at the given index.
classNode.methods.set(index, mv);
As for your tutorial, no permission is required to use anything in this tutorial. the code is under GPL/LGPL. the tutorial itself is public domain. or whatever open license minecraftforum.net posts are subjected to.
Also be aware that most if not all of the code is not my own. the people who I copied the code from are in the Special Thanks section. their code is also open source with no restrictions.
-tlf
-tlf
I dunno I never used them
Very nice! instead of hardcoding the class names, it's nicely done.
A cynic would object to the for loop in the transform method, maybe storing the class names in a hash-map would yield quicker execution.
anyway i'm holding my breath on the lastest release of forge 790. there's a TweakClass now I have no idea what that is, hope it won't break any coremods
https://github.com/MinecraftForge/FML/commit/6de36d78f57f6f08ec586b67b684d0e5406cd436
That way if 2 coremods try to replace the same class but each modify a different method, they won't conflict.
i.e. have one overwrite the work of the other.
i'll try to post some code about that soon.
2013-07-12 13:03:25 [INFO] [STDOUT]
2013-07-12 13:03:25 [INFO] [STDOUT] java.lang.NoClassDefFoundError: net/minecraft/entity/player/EntityPlayerMP
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraftforge.common.ForgeDummyContainer.modConstruction(ForgeDummyContainer.java:151)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
2013-07-12 13:03:25 [INFO] [STDOUT] at java.lang.reflect.Method.invoke(Method.java:597)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventHandler.handleEvent(EventHandler.java:74)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.SynchronizedEventHandler.handleEvent(SynchronizedEventHandler.java:45)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.dispatch(EventBus.java:313)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.dispatchQueuedEvents(EventBus.java:296)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.post(EventBus.java:267)
2013-07-12 13:03:25 [INFO] [STDOUT] at cpw.mods.fml.common.LoadController.sendEventToModContainer(LoadController.java:193)
2013-07-12 13:03:25 [INFO] [STDOUT] at cpw.mods.fml.common.LoadController.propogateStateMessage(LoadController.java:173)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
2013-07-12 13:03:25 [INFO] [STDOUT] at java.lang.reflect.Method.invoke(Method.java:597)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventHandler.handleEvent(EventHandler.java:74)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.SynchronizedEventHandler.handleEvent(SynchronizedEventHandler.java:45)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.dispatch(EventBus.java:313)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.dispatchQueuedEvents(EventBus.java:296)
2013-07-12 13:03:25 [INFO] [STDOUT] at com.google.common.eventbus.EventBus.post(EventBus.java:267)
2013-07-12 13:03:25 [INFO] [STDOUT] at cpw.mods.fml.common.LoadController.distributeStateMessage(LoadController.java:104)
2013-07-12 13:03:25 [INFO] [STDOUT] at cpw.mods.fml.common.Loader.loadMods(Loader.java:510)
2013-07-12 13:03:25 [INFO] [STDOUT] at cpw.mods.fml.client.FMLClientHandler.beginMinecraftLoading(FMLClientHandler.java:172)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.client.Minecraft.startGame(Minecraft.java:470)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.client.Minecraft.func_99999_d(Minecraft.java:796)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.client.main.Main.main(Main.java:93)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
2013-07-12 13:03:25 [INFO] [STDOUT] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
2013-07-12 13:03:25 [INFO] [STDOUT] at java.lang.reflect.Method.invoke(Method.java:597)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.launchwrapper.Launch.launch(Launch.java:57)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.launchwrapper.Launch.main(Launch.java:18)
2013-07-12 13:03:25 [INFO] [STDOUT] Caused by: java.lang.ClassNotFoundException: net.minecraft.entity.player.EntityPlayerMP
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:179)
2013-07-12 13:03:25 [INFO] [STDOUT] at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
2013-07-12 13:03:25 [INFO] [STDOUT] at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
2013-07-12 13:03:25 [INFO] [STDOUT] ... 33 more
2013-07-12 13:03:25 [INFO] [STDOUT] Caused by: java.lang.NegativeArraySizeException
2013-07-12 13:03:25 [INFO] [STDOUT] at org.objectweb.asm.Frame.a(Unknown Source)
2013-07-12 13:03:25 [INFO] [STDOUT] at org.objectweb.asm.MethodWriter.visitMaxs(Unknown Source)
2013-07-12 13:03:25 [INFO] [STDOUT] at org.objectweb.asm.tree.MethodNode.accept(Unknown Source)
2013-07-12 13:03:25 [INFO] [STDOUT] at org.objectweb.asm.tree.MethodNode.accept(Unknown Source)
2013-07-12 13:03:25 [INFO] [STDOUT] at org.objectweb.asm.tree.ClassNode.accept(Unknown Source)
2013-07-12 13:03:25 [INFO] [STDOUT] at tlf.commandapi.ASM.Transformer.patchClassASM(Transformer.java:131)
2013-07-12 13:03:25 [INFO] [STDOUT] at tlf.commandapi.ASM.Transformer.transform(Transformer.java:32)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.launchwrapper.LaunchClassLoader.runTransformers(LaunchClassLoader.java:267)
2013-07-12 13:03:25 [INFO] [STDOUT] at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:165)
2013-07-12 13:03:25 [INFO] [STDOUT] ... 35 more
Any idea why this is happening?
Full Transformer class here:
-tlf
Why on earth are you removing nodes?!
Best way to make sure if your bytcode is correct is to make a different forge enviroment. do manual changes on the code there. see how it looks in bytecode and try to replicate.
Did you get past the problem you had earlier with the "extends"
I did. That's how I got the list of things to remove.
Here's a screenshot of the bytecode comparison. I'm trying to get the one on the left to look like the one on the right.
-tlf
write to me in plain code what you want this method to be?
-tlf
And in the transformer above what does it make the code look like in plain code?
The is supposed to get rid of this area and replace it with this
-tlf
-tlf
-tlf
"FML and Forge will no longer be shipping entire Mojang class files, instead we will be patching our changing onto the vanilla codebase at runtime. {Don't worry its a one time hit when classes load so there is no server performance issues} This is our attempt to work with Mojang and try our best to respect there wishes in there TOS. Most notibly the 'Do not distribute any of our material'. As a unfortunate aspect of how Forge had to work before we couldn't do anything about that but now we can. The concept is simple, we take our Forge version, minus all the stuff that's identical to vanilla and only ship the changed stuff. For those who want to do a little more digging for more information, we currently use http://sourceforge.net/projects/javaxdelta/ to generate/apply our patches, and our GitHub has a lot more information."
I'll try to to change the tutorial to follow the same procedure.
There are no stupid questions, just poor eye sight
EDIT: I lied. I have another question. I'm thinking of maybe starting a modding tutorial series, and I would love to make a video on this. May I use your code? You would be credited, of course.
-tlf
for jump node use labels.
In the asm bytecode plugin, there's a an asm button in red (Show ASMified Code), if you click that it will give you the exact instructions.
you can copy over an entire method using that. and replace a method from the class node.
just instead of
mv = cw.visitMethod(ACC_PUBLIC, "someMethod", "(Ljava/lang/String;[BZ)[B", null, null);
you could write
mv = new MethodNode(ACC_PUBLIC, "someMethod", "(Ljava/lang/String;[BZ)[B", null, null);
and at the end replace the classNode method at the given index.
classNode.methods.set(index, mv);
As for your tutorial, no permission is required to use anything in this tutorial. the code is under GPL/LGPL. the tutorial itself is public domain. or whatever open license minecraftforum.net posts are subjected to.
Also be aware that most if not all of the code is not my own. the people who I copied the code from are in the Special Thanks section. their code is also open source with no restrictions.