private RecipeHandler.RecipeCategories setRecipeCategory(Object recipeOutputItem) {
if (isInstanceOf(recipeOutputItem, "net.minecraft.item.ItemArmor"))
return RecipeHandler.RecipeCategories.ARMOR;
[...]
}
private static boolean isInstanceOf(Object item, String... classPaths) {
Class matchedClass;
for (String classPath : classPaths) {
try {
matchedClass = Class.forName(classPath);
if (matchedClass.isInstance(item)) return true;
} catch (Exception e) {
// no item class, use generic category
return false;
}
}
return false;
}
However, as master801 pointed out, this will not work once it's used in a reobfuscated client.
Is there any way to dynamically access/refer to a class that *will* work when reobfuscated? How does everyone use reflection in Minecraft anyway, if things like getMethod will fail for the same reason?
My original question is moved to the second post, because spoiler tags are apparently broken for me.
This post was my original question, no longer relevant (but here for reference). Before this edit was a self-reply with my "solution" (see post above).
Alright, this is not *exactly* a Minecraft modding question, more so advanced Java. But I really don't want to find a Java forum I've never been to and ask for help, which they may not understand or care about. Apologies in advance.
I have this code, and it's really horrible to read - it will be regularly updated as the mod is developed to support other mods:
[Old code block]
Obviously, it's just a method for categorizing recipe items. It's largely incomplete, but illustrates my issue well enough.
As said, I expect this method to be updated and maintained regularly. At least, relative to your usual code. Is there some way to make this more readable, or possibly even export it to a resource (and by resource I mean "external to code", not resource as in a Minecraft asset) or at least make it more readable?
I've guessed that Scala could help here, but I don't know anything about it to know if that's true - all I know is that it acts as a sort of "generator" for repetitive Java code.
If RecipeCategories is an enum, i would suggest defining:
private Class<?> objectClass;
RecipeCategories(Class<?> objectifyingClass){//Or any interface that defines #isInstance(Object), for #contains
this.objectClass = objectifyingClass;
}
public boolean contains(Object object){
return objectClass.isInstance(object);
}
//Find first instance that works with the argument, looping over the known instances
public static RecipeCategories from(Object object){for(RecipeCategories cat : values()){
if(cat.contains(object){
return cat;
}
}
return DEFAULT;
}
Derp... once again solved it myself. Reflection to the rescue:
private RecipeHandler.RecipeCategories setRecipeCategory(Object recipeOutputItem) {
// I don't like this code *at all*. Surely there's a more readable way to do this.
// TODO: Implement some "intelligent" category detection based on heuristic reflection.
if (isInstanceOf(recipeOutputItem, "net.minecraft.item.ItemArmor"))
return RecipeHandler.RecipeCategories.ARMOR;
if (isInstanceOf(recipeOutputItem, "net.minecraft.item.ItemBlock"))
return RecipeHandler.RecipeCategories.BLOCKS;
[...]
}
private static boolean isInstanceOf(Object item, String... classPaths) {
Class matchedClass;
for (String classPath : classPaths) {
try {
matchedClass = Class.forName(classPath);
if (matchedClass.isInstance(item)) return true;
} catch (Exception e) {
// no item class, use generic category
return false;
}
}
return false;
}
This makes it simple to export the "definitions" to an external file which I can parse
This won't work when the game is not in a dev environment.
This won't work when the game is not in a dev environment.
Thanks for pointing that out.
So is there any way to dynamically check for a class in an obfuscated environment? How does everyone use reflection in Minecraft anyway, if things like getMethod will fail for the same reason?
Will update the OP to reflect this issue.
EDIT: I've found ReflectionHelper.getClass in FML, however the only example I can find is only using this for Forge's own classes. There is ObfuscationReflectionHelper instead, which is likely for access net.minecraft.* after obfuscation, but there's no "getClass" method here.
So maybe that's all I need. Providing I can call that directly...
(tries it)
Alright, looks good. net.minecraft.item.ItemArmor returns as "abb". But of course, it does this even when it's deobfuscated. How can I check for that? Google didn't help here. Derp again, isRemappedClass seems to do just that (checks if it's deobfuscated or not). Will have to compile and test in client to be sure.
So maybe that's all I need. Providing I can call that directly...
(tries it)
Alright, looks good. net.minecraft.item.ItemArmor returns as "abb". But of course, it does this even when it's deobfuscated. How can I check for that? Google didn't help here. Derp again, isRemappedClass seems to do just that (checks if it's deobfuscated or not). Will have to compile and test in client to be sure.
Oh, okay. You're trying to find the the class' name. Yeah, no idea what to do for that.
EDIT: I see where you're going wrong. In Java Reflection you're not supposed to replace the periods with slashes.
Oh, and I just remembered this, FML/Forge automatically remaps the classes. So what you need to do is just do this kinda?
public static boolean compareClasses(Class<?> classToCompare, Class<?> classToCompareWith) {
return classToCompare == classToCompareWith;//FIXME This is not advanced enough. Make sure to compare super classes too.
}
Sorry, just had to fix some unrelated code for non-dev environments (importing scala.?.Arrays or some nonsense) before I could test what I had so far.
My last post is perfectly valid and all working, sorry it's all good - I can now compare items to plain-text specified classes and it works in both dev mode and the obfuscated release mode That getClassName is directly based on ObfuscationReflectionHelper$remapFieldNames - it works well enough.
It's interesting that FMLDeobfuscatingRemapper exposes this helper for class names, and others for fields, but not for methods. Said helper class does have the fields for method mappings, but they're private and it has no method for unmapping like it does for classes. I see other modders are forced to call .map instead, manually specifying the obfuscated and deobfuscated method names in the arguments to map.
So unless I'm missing something, there's no way to getMethod in Minecraft Forge without knowing the obfuscated method name beforehand. *Or* you can use reflection on FMLDeobfuscatingRemapper yourself for access to the private field methodNameMaps (or the private method getMethodMap, which takes a class as parameter).
Forge feature lacking there, IMO. Or I suppose, getting the method of a Minecraft class is rarely needed to even bother with.
Sorry, just had to fix some unrelated code for non-dev environments (importing scala.?.Arrays or some nonsense) before I could test what I had so far.
My last post is perfectly valid and all working, sorry it's all good - I can now compare items to plain-text specified classes and it works in both dev mode and the obfuscated release mode That getClassName is directly based on ObfuscationReflectionHelper$remapFieldNames - it works well enough.
It's interesting that FMLDeobfuscatingRemapper exposes this helper for class names, and others for fields, but not for methods. Said helper class does have the fields for method mappings, but they're private and it has no method for unmapping like it does for classes. I see other modders are forced to call .map instead, manually specifying the obfuscated and deobfuscated method names in the arguments to map.
So unless I'm missing something, there's no way to getMethod in Minecraft Forge without knowing the obfuscated method name beforehand. *Or* you can use reflection on FMLDeobfuscatingRemapper yourself for access to the private field methodNameMaps (or the private method getMethodMap, which takes a class as parameter).
Forge feature lacking there, IMO. Or I suppose, getting the method of a Minecraft class is rarely needed to even bother with.
Now that you mention it, I actually have a class that handles the deobfuscating of methods for me.
Yeah, sure. Just make sure to give me a link to your project in-case I want to make a pull request that fixes some broken things.
801-Core is my main modding library so I can stop reinventing the wheel with every one of my mods.
Yes, it's still in early development, and some of the things in there are ripped from mods (which I give credit to of course).
Ah fair enough, yeah I've started my own "library" for the same reason (only has one class so far but yeah). Probably won't be a library, just the same source files I'll include in multiple mods (it's too difficult to develop with two different Eclipse workspaces in parallel).
You sure you want a link? You don't have to go to that trouble of maintaining it, I can just harass you myself if I find an issue the folder trees won't match either (you seem to have a customized Forge environment there?), I don't know how pull requests will work that way.
Ah fair enough, yeah I've started my own "library" for the same reason (only has one class so far but yeah). Probably won't be a library, just the same source files I'll include in multiple mods (it's too difficult to develop with two different Eclipse workspaces in parallel).
You sure you want a link? You don't have to go to that trouble of maintaining it, I can just harass you myself if I find an issue the folder trees won't match either (you seem to have a customized Forge environment there?), I don't know how pull requests will work that way.
Sure, send a link over. I actually don't have a customised version of FML/Forge.
So is there any way to dynamically check for a class in an obfuscated environment? How does everyone use reflection in Minecraft anyway, if things like getMethod will fail for the same reason?
Will update the OP to reflect this issue.
EDIT: I've found ReflectionHelper.getClass in FML, however the only example I can find is only using this for Forge's own classes. There is ObfuscationReflectionHelper instead, which is likely for access net.minecraft.* after obfuscation, but there's no "getClass" method here.
Personally I use ways around Reflection, for example in my ASM Class Transformer in Thaumic Machina I use this to get a hold of ItemStack's class signature regardless of if it's obfuscated or not:
I'm sure it isn't the best method, but easiest to use.
The theory behind it is while it's in a dev environment, it remains as ItemStack, however I thought that if it were to be compiled and obfuscated, it would be under the obfuscated naming (don't quite know how to put it right now, I'm tired). A clever, and I'm sure not-so-good, way to have an object name reference survive obfuscation.
Rollback Post to RevisionRollBack
Author of the Clarity, Serenity, Sapphire & Halcyon shader packs for Minecraft: Java Edition.
Did you just refer to ASM class transformation as a way around reflection? Classic!
That works well enough, though for a different purpose. getSimpleName() will return the obfuscated name once it's all compiled and running in non-dev Forge. I want to move the classname = category assignments (among other more-accurate criteria which I'm yet to code) to an external text file that users can edit and maintain themselves; better support for mod-added items then And users can even make/rename their own categories if they desire, with the end goal to allow this personalization from within their game.
Did you just refer to ASM class transformation as a way around reflection? Classic!
That works well enough, though for a different purpose. getSimpleName() will return the obfuscated name once it's all compiled and running in non-dev Forge. I want to move the classname = category assignments (among other more-accurate criteria which I'm yet to code) to an external text file that users can edit and maintain themselves; better support for mod-added items then And users can even make/rename their own categories if they desire, with the end goal to allow this personalization from within their game.
No I didn't, I'm using ASM Class Transformation to inject callback into a method so I can modify the method externally, the catch is you need to grab the method using it's signature, which relies on the class path of it's parameters, and one happens to be ItemStack. So I used that work around to get the ItemStack classes name regardless if obfuscated or not.
Rollback Post to RevisionRollBack
Author of the Clarity, Serenity, Sapphire & Halcyon shader packs for Minecraft: Java Edition.
However, as master801 pointed out, this will not work once it's used in a reobfuscated client.
Is there any way to dynamically access/refer to a class that *will* work when reobfuscated? How does everyone use reflection in Minecraft anyway, if things like getMethod will fail for the same reason?
My original question is moved to the second post, because spoiler tags are apparently broken for me.
Alright, this is not *exactly* a Minecraft modding question, more so advanced Java. But I really don't want to find a Java forum I've never been to and ask for help, which they may not understand or care about. Apologies in advance.
I have this code, and it's really horrible to read - it will be regularly updated as the mod is developed to support other mods:
[Old code block]
Obviously, it's just a method for categorizing recipe items. It's largely incomplete, but illustrates my issue well enough.
As said, I expect this method to be updated and maintained regularly. At least, relative to your usual code. Is there some way to make this more readable, or possibly even export it to a resource (and by resource I mean "external to code", not resource as in a Minecraft asset) or at least make it more readable?
I've guessed that Scala could help here, but I don't know anything about it to know if that's true - all I know is that it acts as a sort of "generator" for repetitive Java code.
Thanks in advance.
private Class<?> objectClass;
RecipeCategories(Class<?> objectifyingClass){//Or any interface that defines #isInstance(Object), for #contains
this.objectClass = objectifyingClass;
}
public boolean contains(Object object){
return objectClass.isInstance(object);
}
//Find first instance that works with the argument, looping over the known instances
public static RecipeCategories from(Object object){for(RecipeCategories cat : values()){
if(cat.contains(object){
return cat;
}
}
return DEFAULT;
}
If you don't like an enum, you can look at
com.google.common.collect.Iterators
com.google.common.base.Predicate
to filter over a collection.
This won't work when the game is not in a dev environment.
Thanks for pointing that out.
So is there any way to dynamically check for a class in an obfuscated environment? How does everyone use reflection in Minecraft anyway, if things like getMethod will fail for the same reason?
Will update the OP to reflect this issue.
EDIT: I've found ReflectionHelper.getClass in FML, however the only example I can find is only using this for Forge's own classes. There is ObfuscationReflectionHelper instead, which is likely for access net.minecraft.* after obfuscation, but there's no "getClass" method here.
Use the ObfuscationReflectionHelper class. It takes a srg and deobfuscated name.
Found that, but there's no getClass in ObfuscationReflectionHelper. However, it's remapFieldNames has this:
So maybe that's all I need. Providing I can call that directly...
(tries it)
Alright, looks good. net.minecraft.item.ItemArmor returns as "abb".
But of course, it does this even when it's deobfuscated. How can I check for that? Google didn't help here.Derp again, isRemappedClass seems to do just that (checks if it's deobfuscated or not). Will have to compile and test in client to be sure.Oh, okay. You're trying to find the the class' name. Yeah, no idea what to do for that.EDIT: I see where you're going wrong. In Java Reflection you're not supposed to replace the periods with slashes.
Oh, and I just remembered this, FML/Forge automatically remaps the classes. So what you need to do is just do this kinda?
DeObfuscated code (before fml/forge remaps it):
Obfuscated code (after fml/forge remaps it):
My last post is perfectly valid and all working, sorry it's all good - I can now compare items to plain-text specified classes and it works in both dev mode and the obfuscated release mode That getClassName is directly based on ObfuscationReflectionHelper$remapFieldNames - it works well enough.
It's interesting that FMLDeobfuscatingRemapper exposes this helper for class names, and others for fields, but not for methods. Said helper class does have the fields for method mappings, but they're private and it has no method for unmapping like it does for classes. I see other modders are forced to call .map instead, manually specifying the obfuscated and deobfuscated method names in the arguments to map.
So unless I'm missing something, there's no way to getMethod in Minecraft Forge without knowing the obfuscated method name beforehand. *Or* you can use reflection on FMLDeobfuscatingRemapper yourself for access to the private field methodNameMaps (or the private method getMethodMap, which takes a class as parameter).
Forge feature lacking there, IMO. Or I suppose, getting the method of a Minecraft class is rarely needed to even bother with.
Now that you mention it, I actually have a class that handles the deobfuscating of methods for me.
https://bitbucket.org/master801/801-core/src/6d6ce0c79867ec239bcbd58e8d9f5094c19520bc/core/helpers/MinecraftObfuscationHelper.java?at=1.7.10
Nice! Can I nick that? Won't have to do it myself Will give credit of course.
EDIT: What's this 801-core? In early development still? I'm interested, now.
Yeah, sure. Just make sure to give me a link to your project in-case I want to make a pull request that fixes some broken things.
801-Core is my main modding library so I can stop reinventing the wheel with every one of my mods.
Yes, it's still in early development, and some of the things in there are ripped from mods (which I give credit to of course).
Ah fair enough, yeah I've started my own "library" for the same reason (only has one class so far but yeah). Probably won't be a library, just the same source files I'll include in multiple mods (it's too difficult to develop with two different Eclipse workspaces in parallel).
You sure you want a link? You don't have to go to that trouble of maintaining it, I can just harass you myself if I find an issue the folder trees won't match either (you seem to have a customized Forge environment there?), I don't know how pull requests will work that way.
Sure, send a link over. I actually don't have a customised version of FML/Forge.
I only figured because there's no "src" folder
I haven't used it for anything yet so it's no committed, but the repo and current code is at https://github.com/cosmicdan/CraftingOverhaul
Cheers
P.S. Lol @ your commit log messages xD
I actually use a customised method to do my source file thingys.
Ha-ha, yeah. I'm very uncreative with my messages.
Personally I use ways around Reflection, for example in my ASM Class Transformer in Thaumic Machina I use this to get a hold of ItemStack's class signature regardless of if it's obfuscated or not:
I'm sure it isn't the best method, but easiest to use.
The theory behind it is while it's in a dev environment, it remains as ItemStack, however I thought that if it were to be compiled and obfuscated, it would be under the obfuscated naming (don't quite know how to put it right now, I'm tired). A clever, and I'm sure not-so-good, way to have an object name reference survive obfuscation.
Author of the Clarity, Serenity, Sapphire & Halcyon shader packs for Minecraft: Java Edition.
My Github page.
The entire Minecraft shader development community now has its own Discord server! Feel free to join and chat with all the developers!
Did you just refer to ASM class transformation as a way around reflection? Classic!
That works well enough, though for a different purpose. getSimpleName() will return the obfuscated name once it's all compiled and running in non-dev Forge. I want to move the classname = category assignments (among other more-accurate criteria which I'm yet to code) to an external text file that users can edit and maintain themselves; better support for mod-added items then And users can even make/rename their own categories if they desire, with the end goal to allow this personalization from within their game.
No I didn't, I'm using ASM Class Transformation to inject callback into a method so I can modify the method externally, the catch is you need to grab the method using it's signature, which relies on the class path of it's parameters, and one happens to be ItemStack. So I used that work around to get the ItemStack classes name regardless if obfuscated or not.
Author of the Clarity, Serenity, Sapphire & Halcyon shader packs for Minecraft: Java Edition.
My Github page.
The entire Minecraft shader development community now has its own Discord server! Feel free to join and chat with all the developers!