Jump to content

Recommended Posts

Posted

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

Posted

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...

Posted

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.

Posted

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.

Posted

you could also maybe use only reflection, maybe this would be simpler then ASM (which usually hit people like a truck)

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

-hydroflame, author of the forge revolution-

Posted

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).

Posted

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.

Posted

@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-

  • 2 weeks later...
Posted

@ILuvYouCompanionCube

Sorry, that it takes me so long to answer:

You already give me the solution to this problem, so i didn't understand why you still helped me.

Thanks to all, for the fast and really god help.

Posted

number 1 youre right

 

number 2 you can prevent by overriding the "finalize" method and removing the object

 

just saying hashmap are easier to use then asm

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

-hydroflame, author of the forge revolution-

Posted
Therefore you would need to edit the class again. And also: ewww finalizers.

oh .... right .... DERP MODE ACTIVATE !!

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

-hydroflame, author of the forge revolution-

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.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • on my last computer, i had a similar problem with forge/ neoforge mods but instead them launcher screen was black
    • I am trying to make a mod, all it is, a config folder that tells another mod to not require a dependency, pretty simple right.. well, I dont want whoever downloads my mod to have to download 4 other mods and then decide if they want 2 more that they kinda really need.. i want to make my mod basically implement all of these mods, i really dont care how it does it, ive tried putting them in every file location you can think of, ive downloaded intellij, mcreator, and tried vmware but thats eh (had it from school). I downloaded them in hopes theyd create the correct file i needed but honestly im only more lost now. I have gotten my config file to work, if i put all these mods into my own mods folder and the config file into the config and it works (unvbelievably) but i want to share this to everyone else, lets just say this mod will legitimately get 7M downloads.  I tried putting them in a run folder then having it create all the contents in that for a game (mods,config..etc) then i drop the mods in and all the sudden i cant even open the game, like it literally works with my own world i play on, but i cant get it to work on any coding platform, they all have like built in java versions you cant switch, its a nightmare. I am on 1.20.1 I need Java 17 (i dont think the specific versions of 17 matter) I have even tried recreating the mods i want to implement and deleting import things like net.adamsandler.themodsname and replacing it with what mine is. that only creates other problems, where im at right now is i got the thing to start opening then it crashes, closest ive gotten it, then it just says this  exception in thread "main" cpw.mods.niofs.union.unionfilesystem$uncheckedioexception: java.util.zip.zipexception: zip end header not found caused by: java.util.zip.zipexception: zip end header not found basically saying theres something wrong with my java.exe file, so i tried downloading so many different versions of java and putting them all in so many different spots, nothing, someone online says its just a mod that isnt built right so i put the mod into an editor and bunch of errors came up, id post it but i deleted it on accident, i just need help integrating mods
    • Vanilla 1.16.5 Crash Report [#L2KYKaK] - mclo.gs  
    • Hello, probably the last update, if anyone has the same problem or this can be of any help, the answer was pretty simple, textures started rendering just using a Tesselator instead of a VertexConsumer given by a MultibufferSource and a RenderType, pretty simple
    • Finally circling back to this, and I think all of us were half right.  getChunk() does appear to immediately load the chunk, but what changed between 1.16 and 1.18 is that the list of entities is now stored in the server, not per chunk.  So while it was possible to load a chunk in 1.16 and immediately grab an entity out of it, 1.18 loads the chunk and submits a request to load its entities on the next tick (if I understand correctly).  All this to say, you can immediately access the chunk itself, however you have to wait an additional tick for its entities to load in. In my case what this means is that I do have to set up an interface to wait an additional tick before submitting the request to retrieve the entity.  However, other functions do appear to be available immediately.  Just depends on what you're trying to do. Thank you all for the help! 
  • Topics

×
×
  • Create New...

Important Information

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