What exactly are you trying to do? I was getting the same error when I tried overriding the entire class. I got it to work by only changing the specific part I was using. If you want to see the code I'm using, here it is.
package tlf.commandapi.ASM;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.LDC;
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 net.minecraft.launchwrapper.IClassTransformer;
public class Transformer implements IClassTransformer {
@Override
public byte[] transform(String arg0, String arg1, byte[] arg2) {
if (arg0.equals("ju")) {
return patchClassASM(arg0, arg2, true);
} else if (arg0.equals("net.minecraft.entity.player.EntityPlayerMP")) {
return patchClassASM(arg0, arg2, false);
}
return arg2;
}
public byte[] patchClassASM(String name, byte[] bytes, boolean obfuscated) {
String targetMethodName = obfuscated ? "a" : "canCommandSenderUseCommand";
String targetMethodDesc = "(ILjava/lang/String;)Z";
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(bytes);
classReader.accept(classNode, 0);
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)))
{
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")) {
tell_index = index;
} else if (temp.cst.equals("me")) {
targetNode = currentNode.getNext();
}
}
}
if (targetNode == null || tell_index == -1)
{
return bytes;
}
AbstractInsnNode[] remNodes = new AbstractInsnNode[10];
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+10);
for (int i = 0; i < remNodes.length; i++)
{
m.instructions.remove(remNodes[i]);
}
InsnList toInject = new InsnList();
toInject.add(new MethodInsnNode(INVOKESTATIC, "tlf/commandapi/common/CommandAPI", "isPlayerCommand", "(Ljava/lang/String;)Z"));
m.instructions.insert(targetNode, toInject);
break;
}
}
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
classNode.accept(writer);
return writer.toByteArray();
}
}
Yes, except that's its against Minecrafts ToU to distribute any of Minecraft's code. That's why Forge changed the way it works recently. Besides, the way you described is the way that caused the error for me.
I don't want to start an argument, but I also don't want to go against Mojang, so I have to draw your attention to the first three sentences of the 'One Major Rule' section of the terms:
Do not distribute anything we've made. This includes, but not limited to, the client or the server software for the game. This also includes modified versions of anything we've made.
Forge didn't distribute unchanged classes. It had no reason to. They still changed the way it was loaded/distributed because of the above statement.
Btw. your CommandAPI looks interesting... I always wanted to include some commands to control my mods. Is the description shipped with the JAR file?
No, but all the included code (should be) fully commented. If there is something that isn't clear, submit a ticket on the CurseForge page, and I'll update it.
Yeah, but thanks to Forge this is already really easy.
The only thing you have to do differently is you have to replace the default way of adding new commands with my way. I can't remember the exact code right now, but you should be able to figure it out by looking at the code.
I'm looking to give minecraft modding a try by updating this mod here for forge, or possibly writing a new version of the same idea from scratch. I assume that it would be classified as a coremod since it needs to make edits to the base classes that generate caves, ravines and mineshafts, but I am unsure as to how to get it to work.
Like I said, this would be my first time ever trying to make a minecraft mod, and I am a beginner at java. Is this something that would be far, far over my head without any other modding experience, or would it be a good idea to cut my teeth on and learn?
When I make the class that implements IFMLLoadingPlugin, it says that I have to implement a bunch more methods than you have. Do you know why this is and how I should deal with it? Sorry if it's been asked before. I skimmed the posts and didn't see it, but I may have missed it.
Alright, I've been struggling with this for a while now but I hope you guys can help.
Since all of the alternate account switcher mods that I have found are outdated/inactive I wanted to make my own. I have successfully made one that lets me switch accounts by adding a button to GuiMultiplayer. It works great and I'm almost satisfied except I had to modify two base classes. GuiMultiplayer to add the button and the GuiButton action to open my login gui. And Minecraft to make the session public.
Since I want to make this mod compatible with nearly all other mods I want to not have to modify those base classes.
Now on to what I need help with.
I followed the tutorial and I got my mod to stop explosions from destroying items. After, I changed the class to look for the GuiMultiplayer class and the initGuiControls method. It finds them no problem but I cannot for the life of me figure out how to add the button.
ClassTransformer
package mod.ian25.AltSwitcher;
import java.util.Iterator;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
public class ASClassTransformer implements IClassTransformer {
@Override
public byte[] transform(String arg0, String arg1, byte[] arg2) {
if(arg0.equals("avk")) {
System.out.println("**Inside OBFUSCATED GuiMultiplayer... About to patch: " + arg0);
return patchClassASM(arg0, arg2, true);
}
if(arg0.equals("net.minecraft.client.gui.GuiMultiplayer")) {
System.out.println("**Inside NONOBFUSCATED GuiMultiplayer... About to patch: " + arg0);
return patchClassASM(arg0, arg2, false);
}
return arg2;
}
public byte[] patchClassASM(String name, byte[] bytes, boolean obfuscated) {
String targetMethodName = "";
if(obfuscated) {
targetMethodName = "g";
} else if(!obfuscated) {
targetMethodName = "initGuiControls";
}
ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(bytes);
classReader.accept(classNode, 0);
Iterator<MethodNode> methods = classNode.methods.iterator();
while(methods.hasNext())
{
MethodNode m = methods.next();
int target_index = -1;
if ((m.name.equals(targetMethodName) && m.desc.equals("()V")))
{
System.out.println("**Inside target method: " + m.name + "**");
AbstractInsnNode currentNode = null;
AbstractInsnNode targetNode = null;
@SuppressWarnings("unchecked")
Iterator<AbstractInsnNode> iter = m.instructions.iterator();
int index = -1;
while (iter.hasNext())
{
index++;
currentNode = iter.next();
System.out.println("** Index : " + index + " currentNode.getOpcode() = " + currentNode.getOpcode());
if (currentNode.getOpcode() == Opcodes.PUTFIELD)
{
targetNode = currentNode;
target_index = index;
}
}
System.out.println("********* target_index should be (436?) -> " + target_index);
if (targetNode == null)
{
System.out.println("Did not find all necessary target nodes! ABANDON CLASS!");
return bytes;
}
if (target_index == -1)
{
System.out.println("Did not find all necessary target nodes! ABANDON CLASS!");
return bytes;
}
//Code to add the button will go here
System.out.println("Patching Complete!");
break;
}
}
System.out.println("Debug1");
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
System.out.println("Debug2");
classNode.accept(writer);
System.out.println("Debug3");
return writer.toByteArray();
}
}
GuiMultiplayer- initGuiControls method (The last line is what I'm trying to add)
Now, although I'm having some trouble with the Eclipse Bytecode plugin I was able to get the bytecode for the method I want.
(I only included a little bit of the method)
Line 13 is what I want to add. Now, this bytecode barley makes sense to me but I can see some method to the madness and I was able to come up with this.
There's nothing to post. If I copy and paste the given class in the example (EDFMLLoadingPlugin), I get that error. I'm using the latest version of Forge. ...This is for Forge, right? Somebody on their forums linked me to this topic, but now that I look, I don't see any mention of Forge here. Maybe that's the issue.
Yes, that is basically what I did. I was looking at the access transformer section, so the class name being returned is probably different, but they are all about the same. I still don't see where it says that isn't the entire class code, even in the other sections, so thanks for the clarification. Is the class that extends AccessTransformer also missing stuff then? I don't see any links to full sets of code for that one.
You can find the complete code in culegooner'sgithub repo.
And to cite culegooner:
That's not the same as saying the classes as given are incomplete, but it doesn't matter. I looked through all of the projects listed on that page and none of them seem to have the ForgeAccessTransformer class. I guess I will just figure it out. Thanks for the help.
First off, please put all code in '[cde]' tags. It makes it much easier to read. Also, We will be able to help much more if you give us an error log.
Ahh my bad, I fixed my first post. And here is the crash report.
---- Minecraft Crash Report ----
// My bad.
Time: 8/15/13 12:37 AM
Description: Initializing game
java.lang.NoClassDefFoundError: net/minecraft/client/gui/GuiMultiplayer
at net.minecraft.client.Minecraft.startGame(Minecraft.java:516)
at net.minecraft.client.Minecraft.func_99999_d(Minecraft.java:796)
at net.minecraft.client.main.Main.main(Main.java:93)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraft.launchwrapper.Launch.launch(Launch.java:57)
at net.minecraft.launchwrapper.Launch.main(Launch.java:18)
Caused by: java.lang.ClassNotFoundException: net.minecraft.client.gui.GuiMultiplayer
at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:179)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 9 more
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: 3
at java.lang.String.charAt(Unknown Source)
at org.objectweb.asm.Type.getArgumentsAndReturnSizes(Type.java:413)
at org.objectweb.asm.Frame.pop(Frame.java:732)
at org.objectweb.asm.Frame.execute(Frame.java:1198)
at org.objectweb.asm.MethodWriter.visitMethodInsn(MethodWriter.java:818)
at org.objectweb.asm.tree.MethodInsnNode.accept(MethodInsnNode.java:102)
at org.objectweb.asm.tree.InsnList.accept(InsnList.java:162)
at org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:585)
at org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:515)
at org.objectweb.asm.tree.ClassNode.accept(ClassNode.java:340)
at mod.ian25.AltSwitcher.ASClassTransformer.patchClassASM(ASClassTransformer.java:129)
at mod.ian25.AltSwitcher.ASClassTransformer.transform(ASClassTransformer.java:33)
at net.minecraft.launchwrapper.LaunchClassLoader.runTransformers(LaunchClassLoader.java:267)
at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:165)
... 11 more
A detailed walkthrough of the error, its code path and all known details is as follows:
---------------------------------------------------------------------------------------
-- Head --
Stacktrace:
at net.minecraft.client.Minecraft.startGame(Minecraft.java:516)
-- Initialization --
Details:
Stacktrace:
at net.minecraft.client.Minecraft.func_99999_d(Minecraft.java:796)
at net.minecraft.client.main.Main.main(Main.java:93)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraft.launchwrapper.Launch.launch(Launch.java:57)
at net.minecraft.launchwrapper.Launch.main(Launch.java:18)
-- System Details --
Details:
Minecraft Version: 1.6.2
Operating System: Windows 8 (amd64) version 6.2
Java Version: 1.7.0_25, Oracle Corporation
Java VM Version: Java HotSpot™ 64-Bit Server VM (mixed mode), Oracle Corporation
Memory: 799616088 bytes (762 MB) / 1038876672 bytes (990 MB) up to 1038876672 bytes (990 MB)
JVM Flags: 3 total; -Xincgc -Xmx1024M -Xms1024M
AABB Pool Size: 0 (0 bytes; 0 MB) allocated, 0 (0 bytes; 0 MB) used
Suspicious classes: FML and Forge are installed
IntCache: cache: 0, tcache: 0, allocated: 0, tallocated: 0
FML: MCP v8.04 FML v6.2.35.804 Minecraft Forge 9.10.0.804 4 mods loaded, 3 mods active
mcp{8.04} [Minecraft Coder Pack] (minecraft.jar) Unloaded->Constructed->Pre-initialized->Initialized->Post-initialized->Available
FML{6.2.35.804} [Forge Mod Loader] (coremods) Unloaded->Constructed->Pre-initialized->Initialized->Post-initialized->Available
Forge{9.10.0.804} [Minecraft Forge] (coremods) Unloaded->Constructed->Pre-initialized->Initialized->Post-initialized->Available
AltSwitcher{0.0} [AltSwitcher] (coremods) Unloaded->Disabled
Launched Version: 1.6
LWJGL: 2.9.0
OpenGL: AMD Radeon HD 6770M GL version 4.2.11931 Compatibility Profile Context, ATI Technologies Inc.
Is Modded: Definitely; Client brand changed to 'fml,forge'
Type: Client (map_client.txt)
Resource Pack: Default
Current Language: English (US)
Profiler Position: N/A (disabled)
Vec3 Pool Size: ~~ERROR~~ NullPointerException: null
-tlf
EDIT: WOOT! 100th post!
-tlf
-tlf
I don't want to start an argument, but I also don't want to go against Mojang, so I have to draw your attention to the first three sentences of the 'One Major Rule' section of the terms:
Forge didn't distribute unchanged classes. It had no reason to. They still changed the way it was loaded/distributed because of the above statement.
-tlf
No, but all the included code (should be) fully commented. If there is something that isn't clear, submit a ticket on the CurseForge page, and I'll update it.
-tlf
Don't assume it equals to bytes.length.
Just a note, it only makes it possible for non-ops to use commands. You still have to code everything else yourself.
-tlf
The only thing you have to do differently is you have to replace the default way of adding new commands with my way. I can't remember the exact code right now, but you should be able to figure it out by looking at the code.
-tlf
Like I said, this would be my first time ever trying to make a minecraft mod, and I am a beginner at java. Is this something that would be far, far over my head without any other modding experience, or would it be a good idea to cut my teeth on and learn?
Since all of the alternate account switcher mods that I have found are outdated/inactive I wanted to make my own. I have successfully made one that lets me switch accounts by adding a button to GuiMultiplayer. It works great and I'm almost satisfied except I had to modify two base classes. GuiMultiplayer to add the button and the GuiButton action to open my login gui. And Minecraft to make the session public.
Since I want to make this mod compatible with nearly all other mods I want to not have to modify those base classes.
Now on to what I need help with.
I followed the tutorial and I got my mod to stop explosions from destroying items. After, I changed the class to look for the GuiMultiplayer class and the initGuiControls method. It finds them no problem but I cannot for the life of me figure out how to add the button.
ClassTransformer
GuiMultiplayer- initGuiControls method (The last line is what I'm trying to add)
Now, although I'm having some trouble with the Eclipse Bytecode plugin I was able to get the bytecode for the method I want.
(I only included a little bit of the method)
Line 13 is what I want to add. Now, this bytecode barley makes sense to me but I can see some method to the madness and I was able to come up with this.
I'm definitely doing something wrong because it crashes when I add that in.
My final ClassTransformer looks like this.
I hope this made some sense to one of you guys and you'll be able to point out what I did wrong.
Thanks in advance!
There's nothing to post. If I copy and paste the given class in the example (EDFMLLoadingPlugin), I get that error. I'm using the latest version of Forge. ...This is for Forge, right? Somebody on their forums linked me to this topic, but now that I look, I don't see any mention of Forge here. Maybe that's the issue.
-tlf
That's not the same as saying the classes as given are incomplete, but it doesn't matter. I looked through all of the projects listed on that page and none of them seem to have the ForgeAccessTransformer class. I guess I will just figure it out. Thanks for the help.
Ahh my bad, I fixed my first post. And here is the crash report.
-tlf
Sure thing,
This might be your problem.
Also, where are you getting '436' from?
-tlf