Jump to content

Add a field to a base class


CreativeMD

Recommended Posts

Hi,

Is there i way to add a field to a base class?

I read this tutorial http://www.minecraftforge.net/wiki/Using_Access_Transformers.

The auhtor writes about editing field, but not adding a field.

I'm not really in this topic, i just started, because since 1.6 jar mods are really hard to install.

So i have to transform my both jar mods to core mods:

http://www.minecraftforum.net/topic/1795185-162universalrandom-additions-v056-pillar-air-jump-jetpack-and-much-more-2000-downloads/#entry22245488

http://www.minecraftforum.net/topic/1879772-162-world-generation-manager-v02-regenerate-a-specific-mod/

 

Thanks previously  :D

Link to comment
Share on other sites

I believe you can, but you should make sure you know what you are doing before attempting to use ASM :)

 

 

Also if you feel there's a very good and valid reason for the change to forge, please do write a post about it explaining the change and why it's useful, there may be that this change could be added later if it's useful to more than just your specific case :)

If you guys dont get it.. then well ya.. try harder...

Link to comment
Share on other sites

First, you probadly think that I'm not so much experienced in programming, but i had just no clue about this specific topic.

(Don't understand that wrong!!!!!!!!! ;))

So my mod RandomAdditions adds a new items physic, to see how it works just visit my mod topic.

Therefore i only have to overwrite some methods, but for my second mod this isn't possible.

 

It allows you to regenerate a mod's worldgenerator. So i have to edit GameRegistry and overwrite the method registerWorldGenerator and change the field worldgenerators to public (need it for other things).

I also have to replace the class Chunk because i have to add a new field for the mods that were generated in it.

The last methods are the onChunkLoad and onChunkUnload (or some thing like that) in the AnvilChunkLoader class.

 

I hope i could give you a little overview about this problem.

Link to comment
Share on other sites

You may want to look at the ChunkEvent.

Yes, i'm using it, but for regnerate the needed mods. I have no access to the NBTData of the chunk so i can't save the information in the chunks data.

 

So far so good, i will have to try it out. Thanks for fast and good answers.

Link to comment
Share on other sites

First of all, you need to create a coremod. One of the pages people linked probably already show you how to do that.

 

Then you'll have to have a class that implements IClassTransformer. The only method this interface defines is transform:

 

public class Transformer implements IClassTransformer 
{
@Override
        //Imagine we wanted to add the field to the EntityPlayer class
public byte[] transform(String className, String dontKnowWhatThisIs, byte[] bytecodeForClass)
{
                //note that this will only work in a non obfuscated environment (i.e. running minecraft from eclipse). To make it work within an
                //obfuscated envirionment, you'll have to find out the obfuscated name for the class you want to add your field to. If you don't know
                //how to do that, I can elaborate the answer to show you how.
	if(className.equals("net.minecraft.entity.player.EntityPlayer"))
	{
		return patchEntityPlayer(bytecodeForClass);
	}

                //note that we return the bytecodes for every class other than the one(s) we need to alter unchanged
	return bytecodeForClass;
}


private byte[] patchEntityPlayer(byte[] bytecode, boolean isObfuscated)
{
                //A ClassNode represents a class in a tree-like structure
	ClassNode classNode = new ClassNode();
                //A ClassReader is able to read the java bytecode. It kinda foward events to the ClassVisitor it receaves through it's accept method.
	ClassReader classReader = new ClassReader(bytecode);
                //the method accept of the ClassReader let's a ClassVisitor in (ClassNode is a subclass of the abstract class ClassVisitor)
                //he ClassReader will now read the bytecode on behalf of the ClassVisitor (ClassNode) and call a lot of visit*something* method for it
	classReader.accept(classNode, 0);

                //to create a field we instantiate a FieldNode. The first argument is mandatory, the second is the access flag to the field, the third is
                //the name of the field, the fourth is the descriptor of the field (basically, you need to call Type.getDescriptor and pass the class of
                //the field as argument. In this case, we're creating a field of type String. The fifth argument is the signature of the field. Since I don't
                //know what's a signature in the context of a field, and since there's no problem in letting it be null, I set it as null.
                //The last argument is the initial value of the field. Since this value isn't inherited (I don't know why), we can't set it,
                //because EntityPlayer is, in game, EntityPlayerSP or EntityPlayerMP
	FieldNode newField = new FieldNode(Opcodes.ASM4, Opcodes.ACC_PUBLIC, "TheNewField",
            Type.getDescriptor(String.class), null, null);

                //ClassNode has a List<FieldNode> that you can access through it's "fields" field. I add my new field to it.
	classNode.fields.add(newField);

                //the ClassWriter will actually write the new bytecode. Think of this as a chain. from ClassReader to ClassNode to ClassWriter
	ClassWriter writer = new ClassWriter(0);
	classNode.accept(writer);

                //here we actually return the new bytecode for EntityPlayer, containing the new Field.
	return writer.toByteArray();
}

 

I made a very basic item to test this Transformer:

public class TestItem extends Item
{
public TestItem(int par1) {
	super(par1);
	setMaxDamage(100);
}


@Override
public ItemStack onItemRightClick(ItemStack par1ItemStack, World world, EntityPlayer player)
        {
	par1ItemStack.setItemDamage(par1ItemStack.getItemDamage() + 1);
	Field theField;
	try
	{
		theField = player.getClass().getField("TheNewField");
		String theFieldValue = (String)theField.get(player);
		player.addChatMessage("TheNewField had the value of " + theFieldValue);
		player.addChatMessage("Altering the TheNewField value to \"MAAATHEMATICAL!!\"");
		theField.set(player, "MAAATHEMATICAL!!");
		theFieldValue = (String)theField.get(player);
		player.addChatMessage("TheNewField now has the value had the value of " + theFieldValue);
	}
	catch (Exception e)
	{
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

	return par1ItemStack;
    }
}

 

Of course, using reflection everytime you'd need to access the item may not be a good idea. To minimize this problem we could at least make a field of type Field in our item class, and we'd avoid the call to Class.getField everytime, by making it just once and storing the return in our field of type Field. All sets and gets would still need reflection though (probably).

Link to comment
Share on other sites

ILuvYouCompanionCube: How I solve this particular problem:

Make an Interface which contains an abstract setter and getter method for your new field.

Then with ASM not only add the field but also implement the interface (quite easy: just 2 new methods one GETFIELD one PUTFIELD).

In your code you can then cast the minecraft class you transformed to your interface and call the getter/setter methods without needing reflection.

Would look something like this:

World world = // whatever
String awesomeField = ((WorldProxy)world).getMyAwesomeField();
awesomeField = awesomeField.toLowerCase();
((WorldProxy)world).setMyAwesomeField(awesomeField);

 

I said it once and I'll say it again. You, my friend, are very smart. Loved the interface idea.

Link to comment
Share on other sites

@dies, you could also make the change in your dev env, so that it compiles thinking there will be a field called "myAwesomeField" and make sure the coremod insert that field.

 

 

this is how I would solve that.

how to debug 101:http://www.minecraftforge.net/wiki/Debug_101

-hydroflame, author of the forge revolution-

Link to comment
Share on other sites

  • 2 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.