Jump to content

How Does Forge Reflection Work?


Acejhm

Recommended Posts

I know this is not a direct Minecraft modding question, but it is related to Forge.  I was wondering how the reflection, the examination of classes at runtime, works with Forge?  I read up on how to do reflection from the Oracle website, but it didn't mention much about how to examine unknown classes, like forge does.  You never can know, unless at runtime, how many mods, or which ones, you will have in your Minecraft instance.  I was wanting to learn a bit more about how it all happened.  Can anyone shed some light on the matter?

Link to comment
Share on other sites

You need to look at ClassLoaders.

 

ClassLoaders are objects that load other classes when needed.  This means that you can write a custom class loader that fetches a class, examines it, makes changes (if it wants), and then passes it off to the JVM.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Set a breakpoint in a constructor of your main mod class. Run in the debugger and then see the call stack that got you there. Set more break points in some of those loaders. See how they work. If/when you write an event handler, do likewise. Then go back and see what the mod and event handler annotations do. It'll make your head spin.

 

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

You need to look at ClassLoaders.

 

ClassLoaders are objects that load other classes when needed.  This means that you can write a custom class loader that fetches a class, examines it, makes changes (if it wants), and then passes it off to the JVM.

Set a breakpoint in a constructor of your main mod class. Run in the debugger and then see the call stack that got you there. Set more break points in some of those loaders. See how they work. If/when you write an event handler, do likewise. Then go back and see what the mod and event handler annotations do. It'll make your head spin.

Thank you both for your replies.  I'm wanting to get much deeper into Java, and programming, than one can learn in casual research, so the feedback is much appreciated.

Link to comment
Share on other sites

Set a breakpoint in a constructor of your main mod class. Run in the debugger and then see the call stack that got you there. Set more break points in some of those loaders. See how they work. If/when you write an event handler, do likewise. Then go back and see what the mod and event handler annotations do. It'll make your head spin.

 

Question about this, I did as you suggested, and after looking at the stack trace at the time my mod's main constructor was called, I get a method return from Class.class in the "forName" method.  However, I can't see where that method gets it's class name from.  I see what it's doing to it, and, examining it briefly, it makes sense, but I don't understand HOW it retrieves the needed class name. I even added a breakpoint on the "forName" method header, but I couldn't get it to tell me where it was being called from.  It's passed some information about the class it's loading, but that information is retrieved what I wanted to know.  I got a little closer to my answer with the "loadClass" method inside ClassLoader.class, but it's passed a binary name for a class to find.  Which method passes it that I still don't know, even after adding another breakpoint to that method.  Maybe I'm debugging wrong?  Idk.

 

 

Link to comment
Share on other sites

I think what you are asking is this: How does FML actually know what mods to load, right?

Check out the ModDiscoverer class.

 

Yep, that totally helped.  Thanks!  And now my head hurts...  That is a complicated call hierarchy, and some impressive code.  The ModDiscover Class links, indirectly, to like 6 other classes, which all indirectly and directly link to each other.  I barely understand all this, but I'm quite impressed.  CPW, sir, you have done well.  For anyone interested, I found it in the referenced forge libraries in "net.minecraftforge.fml.common.discovery".

Link to comment
Share on other sites

Isn't the JVM amazing?

 

(There's also a reason that I call coremodding / ASM an abyss that looks back.  Javacode can modify javacode at runtime.  Hell, you can create new class types at runtime, entirely through the bytecode manipulation features if you really want to.)

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Hell, you can create new class types at runtime

hehehehe

 

But yes, this has nothign to do with reflection its bytecode inspection. It's simple if you know what you're doing. But stupid complex if you're the average modder.

Its WAY more complex then a normal system needs to be.

I do Forge for free, however the servers to run it arn't free, so anything is appreciated.
Consider supporting the team on Patreon

Link to comment
Share on other sites

Hell, you can create new class types at runtime

hehehehe

 

Figures Forge would do that. :P

What I don't know is why runtime class creation is preferable over a standard compiled class.  That is: I can't think of a use-case where it would be necessary.  Then again, the only example I saw was just that: an example to show that it could be done.  And no, I have no idea what that Forge one does.

 

It's simple if you know what you're doing. But stupid complex if you're the average modder.

 

It's still stupid complex even if you know what you're doing, because of how many things you have to keep in your head at one time in order to write a single line of code.  You have to know what's on the stack, what operation you need to do next, how it will affect the stack, and so on.  So I wouldn't say it's easy, more like doable.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Ah! So that's how the event handlers work.  I always wondered about that.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

If forge were to invoke all
@SubscribeEvent

methods using reflection the game would just die (there are a lot of events fired on a per-frame basis alone). Instead a wrapper class is emitted at runtime that directly invokes the target method and also implements an interface so the (pre-compiled) event handler code can call the methods in that interface directly.

And here I was, naively believing that reflection came without a "price".

 

PS: The class rewriting looks like a way to divorce horse armor from entity horse someday. Then we modders can finally create mods with custom horse armor (I can create the item in inventory, but multiple threads over the last few years have all failed to produce a solution short of a core-mod that can be worn by a horse and protect it).

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

And here I was, naively believing that reflection came without a "price".

 

Ohgodno.  Reflection is awful.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

And here I was, naively believing that reflection came without a "price".

 

Ohgodno.  Reflection is awful.

60 to 80 tiles slower then normal invocation. Thats why our event bus is a BEAST. Its specifically designed to be the least overhead as possible.

I do Forge for free, however the servers to run it arn't free, so anything is appreciated.
Consider supporting the team on Patreon

Link to comment
Share on other sites

Thats why our event bus is a BEAST. Its specifically designed to be the least overhead as possible.

 

Props on making it, too.

Modding Minecraft has seriously been one of the most enjoyable programming experiences I've ever had.  Everything is laid out so well with incredibly intuitive and easy to use systems that let me hook into nearly everything.

 

I've had to coremod a few things, but they were things that have either been turned down (an event for when the player feeds animals...I still don't understand that one getting declined*) or don't have a general-purpose use (modifying the overworld moon phase calculation, an event for crop growth ticks, tweaking silverfish to enter custom blocks (actually that one might be worth trying to patch into Forge), couple other things).

 

*I was told that the method that sets animals into love mode (func_146082_f) never got an event because there were other ways to get at what it does.  And while true, my usecase needed to specifically detect the player setting the animal into love mode, rather than it coming from another source.  I wanted to make it take more food to feed an animal before it would breed, but also make animals go into love mode on their own.  If I couldn't detect that it was a player setting love mode, vs. it being from an AI task, then I coudn't achieve the effect I wanted.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

tweaking silverfish to enter custom blocks (actually that one might be worth trying to patch into Forge)

It was already suggested by PR (by diesieben) more than half a year ago. Still open. Don't know why forge never merged it.

https://github.com/MinecraftForge/MinecraftForge/pull/2012

Link to comment
Share on other sites

Well then.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

And here I was, naively believing that reflection came without a "price".

 

Ohgodno.  Reflection is awful.

60 to 80 tiles slower then normal invocation.

I just reviewed at one of my reflections. In a class constructor, I acquire the field object of a private (and static) vanilla class data field and tell it to be accessible. That same field object is later reused during the game to fetch the field's value. If I understand correctly, then reflection's high price is paid in the setup, so the runtime value assignment is reasonably fast. Is that right, or are data gets slow too?

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

I just reviewed at one of my reflections. In a class constructor, I acquire the field object of a private (and static) vanilla class data field and tell it to be accessible. That same field object is later reused during the game to fetch the field's value. If I understand correctly, then reflection's high price is paid in the setup, so the runtime value assignment is reasonably fast. Is that right, or are data gets slow too?

 

IIRC, the lookup ("find this field/method") is the most expensive portion, but the rest of it is more costly than direct access.  But a single per-tick field.setValue() or method.invoke() isn't going to be noticeable.

 

The reason it matters for Forge events is because there are potentially thousands of them running per-tick.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

The lookup is the most expensive part... sortof.

The invocation is still ~60x slower then normal invocation.

Constant usage of reflection is noticeable.

People don't think it is because SOME JVMs SOMETIMES optimize the reflection classes that are used often by compiling them into native classes.

 

The problem through extensive research that I've done i've found that this JVM optimization is not reliable, only about 15% of end users JVMs do this at all. And of those 15% only about 1/2 do it aggressively enough to be useful in MC world.

 

And even when they do JIT it the resulting class still goes though a bulk of reflection redirection which make it at most 1/12th the speed of native code in my tests.

I do Forge for free, however the servers to run it arn't free, so anything is appreciated.
Consider supporting the team on Patreon

Link to comment
Share on other sites

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

    • They were already updated, and just to double check I even did a cleanup and fresh update from that same page. I'm quite sure drivers are not the problem here. 
    • i tried downloading the drivers but it says no AMD graphics hardware has been detected    
    • Update your AMD/ATI drivers - get the drivers from their website - do not update via system  
    • As the title says i keep on crashing on forge 1.20.1 even without any mods downloaded, i have the latest drivers (nvidia) and vanilla minecraft works perfectly fine for me logs: https://pastebin.com/5UR01yG9
    • Hello everyone, I'm making this post to seek help for my modded block, It's a special block called FrozenBlock supposed to take the place of an old block, then after a set amount of ticks, it's supposed to revert its Block State, Entity, data... to the old block like this :  The problem I have is that the system breaks when handling multi blocks (I tried some fix but none of them worked) :  The bug I have identified is that the function "setOldBlockFields" in the item's "setFrozenBlock" function gets called once for the 1st block of multiblock getting frozen (as it should), but gets called a second time BEFORE creating the first FrozenBlock with the data of the 1st block, hence giving the same data to the two FrozenBlock :   Old Block Fields set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=head] BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@73681674 BlockEntityData : id:"minecraft:bed",x:3,y:-60,z:-6} Old Block Fields set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} Frozen Block Entity set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockPos{x=3, y=-60, z=-6} BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} Frozen Block Entity set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockPos{x=2, y=-60, z=-6} BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} here is the code inside my custom "freeze" item :    @Override     public @NotNull InteractionResult useOn(@NotNull UseOnContext pContext) {         if (!pContext.getLevel().isClientSide() && pContext.getHand() == InteractionHand.MAIN_HAND) {             BlockPos blockPos = pContext.getClickedPos();             BlockPos secondBlockPos = getMultiblockPos(blockPos, pContext.getLevel().getBlockState(blockPos));             if (secondBlockPos != null) {                 createFrozenBlock(pContext, secondBlockPos);             }             createFrozenBlock(pContext, blockPos);             return InteractionResult.SUCCESS;         }         return super.useOn(pContext);     }     public static void createFrozenBlock(UseOnContext pContext, BlockPos blockPos) {         BlockState oldState = pContext.getLevel().getBlockState(blockPos);         BlockEntity oldBlockEntity = oldState.hasBlockEntity() ? pContext.getLevel().getBlockEntity(blockPos) : null;         CompoundTag oldBlockEntityData = oldState.hasBlockEntity() ? oldBlockEntity.serializeNBT() : null;         if (oldBlockEntity != null) {             pContext.getLevel().removeBlockEntity(blockPos);         }         BlockState FrozenBlock = setFrozenBlock(oldState, oldBlockEntity, oldBlockEntityData);         pContext.getLevel().setBlockAndUpdate(blockPos, FrozenBlock);     }     public static BlockState setFrozenBlock(BlockState blockState, @Nullable BlockEntity blockEntity, @Nullable CompoundTag blockEntityData) {         BlockState FrozenBlock = BlockRegister.FROZEN_BLOCK.get().defaultBlockState();         ((FrozenBlock) FrozenBlock.getBlock()).setOldBlockFields(blockState, blockEntity, blockEntityData);         return FrozenBlock;     }  
  • Topics

×
×
  • Create New...

Important Information

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