Texenox Posted February 23, 2014 Share Posted February 23, 2014 I want to create an extension for my mod that requires another mod, but I don't want to have to make separate mods that add that functionality. So instead, I want to integrate that extension in the main mod, but make it so that it fires when that mod is installed alongside. Any ideas? Quote Link to comment Share on other sites More sharing options...
Texenox Posted February 23, 2014 Author Share Posted February 23, 2014 Make an interface, let's call it Module. In there add the methods you need, load() should be enough for a start. Then in your main mod make a Collection of these modules and call the load() method on every one of them in preInit. Then make class that implements said Module interface and does the mod interaction stuff. Then you check with Loader.isModLoaded()= if the other mod is loaded and if so, load the module and add it to the collection. Otherwise you don't load it. Make sure you do not directly reference the class name of the module, only via a String constant (Class.forName("whatever.your.package.is.MyModule").newInstance()) so that it really only gets loaded when the other mod is there. Hope that made any sense. Describing a coding concept is hard Would it be okay if you were to add source code for me to clarify? Sorry, it's just that I couldn't get my head around that. I know how interfaces work, but... Quote Link to comment Share on other sites More sharing options...
coolAlias Posted February 23, 2014 Share Posted February 23, 2014 A simpler, but perhaps less elegant solution: // in your main mod class: public static boolean isOtherModLoaded; // in PreInit, initialize the field: isOtherModLoaded = Loader.isModLoaded("other_mod_id"); // then whenever you want to do something that relates to that other mod, // just check if it's loaded: if (isOtherModLoaded) { // do your stuff } Quote http://i.imgur.com/NdrFdld.png[/img] Link to comment Share on other sites More sharing options...
Sync Views Posted February 23, 2014 Share Posted February 23, 2014 diesieben07's and other similar methods is the only way I know of if you have source dependencies where you want to import any type from the other mod, rather than just use a basic Block returned by GameRegistry or such. If that "do your stuff" depends on anything from the other mod, then you will get a NoClassDefFoundError as soon as Java tries to load your classes because it tries to fully resolve everything as soon as the class files are loaded, not waiting until the first reference is actually executed (the contents of your if statement). Just make a central class (ideally in its own package to keep things separate, say mymodpackage.othermod.Extension implementing an interface mymodpackage.IExtension) for the extension. And make sure that nothing else in your mod ever imports one of the classes from that package (likely to give you a NoClassDefFound if the other mod isn't present). If you detect the mod is present (be careful of load order, e.g. I think its possible for Loader.isModLoaded to return false for a mod that is present if you call it too early, possible preinit and certainly any static initialisers) then use "(IExtension)Class.forName("mymodpackage.othermod.Extension").newInstance()" to get an instance of that class without it ever being imported (Java will only attempt to load those class files at this point, avoiding the NoClassDefFound if the other mod is not present). You could also thus do a try/catch around the Class.forName(...).netInstancerather than check Loader.isModLoaded. Quote Link to comment Share on other sites More sharing options...
Texenox Posted February 23, 2014 Author Share Posted February 23, 2014 diesieben07's and other similar methods is the only way I know of if you have source dependencies where you want to import any type from the other mod, rather than just use a basic Block returned by GameRegistry or such. If that "do your stuff" depends on anything from the other mod, then you will get a NoClassDefFoundError as soon as Java tries to load your classes because it tries to fully resolve everything as soon as the class files are loaded, not waiting until the first reference is actually executed (the contents of your if statement). Just make a central class (ideally in its own package to keep things separate, say mymodpackage.othermod.Extension implementing an interface mymodpackage.IExtension) for the extension. And make sure that nothing else in your mod ever imports one of the classes from that package (likely to give you a NoClassDefFound if the other mod isn't present). If you detect the mod is present (be careful of load order, e.g. I think its possible for Loader.isModLoaded to return false for a mod that is present if you call it too early, possible preinit and certainly any static initialisers) then use "(IExtension)Class.forName("mymodpackage.othermod.Extension").newInstance()" to get an instance of that class without it ever being imported (Java will only attempt to load those class files at this point, avoiding the NoClassDefFound if the other mod is not present). You could also thus do a try/catch around the Class.forName(...).netInstancerather than check Loader.isModLoaded. Okay, I think this is slightly easier for me to understand, but I may need some example code of this to clarify. Quote Link to comment Share on other sites More sharing options...
Alexiy Posted February 23, 2014 Share Posted February 23, 2014 A simpler, but perhaps less elegant solution: // in your main mod class: public static boolean isOtherModLoaded; // in PreInit, initialize the field: isOtherModLoaded = Loader.isModLoaded("other_mod_id"); // then whenever you want to do something that relates to that other mod, // just check if it's loaded: if (isOtherModLoaded) { // do your stuff } This is exactly as I do. And consider dependencies - after, required-after... Quote Link to comment Share on other sites More sharing options...
Texenox Posted February 23, 2014 Author Share Posted February 23, 2014 This is exactly as I do. And consider dependencies - after, required-after... No, I don't want to declare dependencies, I want my mod to be in its simple state when the other mods aren't loaded, but when those other mods are loaded, I want to make it so that extensions for those mods only load up then. Quote Link to comment Share on other sites More sharing options...
coolAlias Posted February 23, 2014 Share Posted February 23, 2014 No, I don't want to declare dependencies, I want my mod to be in its simple state when the other mods aren't loaded, but when those other mods are loaded, I want to make it so that extensions for those mods only load up then. "Dependency" in this case does not mean that your mod requires the other mod, it's a field in the mcmod.info file that FML uses to determine load order, so if your mod is "dependent" i.e. capable of using stuff from another mod, then FML will be sure to load that mod first; this ensures that Loader.isModLoaded will work correctly when it comes your mod's turn to load. On a side note, the method I described works fine even for classes that implement / extend a class in another mod, so long as you do not use that class anywhere in your mod when the other mod is not loaded. For instance, I have a sword class for my Zelda swords, and I wanted them compatible with BattleGear2 dual-wielding, so this is what I did: // create a second class extending my swords: public class ItemZeldaSwordBG2 extends ItemZeldaSword implements IBattlegearWeapon { // super constructor plus BG2 interface implementation only } // then when I declare my items: if (Loader.isModLoaded("battlegear2")) { swordKokiri = new ItemZeldaSwordBG2(blah blah blah); } else { swordKokiri = new ItemZeldaSword(blah blah blah); } In this way my mod works fine with or without BattleGear2, but if BG2 is installed alongside, it will use the correct class that interfaces with BG2. It seems wasteful to have 2 classes for the same Item, but I couldn't figure out any way to add the interface and have the mod still work independently. Quote http://i.imgur.com/NdrFdld.png[/img] Link to comment Share on other sites More sharing options...
GotoLink Posted February 24, 2014 Share Posted February 24, 2014 On a side note, the method I described works fine even for classes that implement / extend a class in another mod, so long as you do not use that class anywhere in your mod when the other mod is not loaded. For instance, I have a sword class for my Zelda swords, and I wanted them compatible with BattleGear2 dual-wielding, so this is what I did: // create a second class extending my swords: public class ItemZeldaSwordBG2 extends ItemZeldaSword implements IBattlegearWeapon { // super constructor plus BG2 interface implementation only } // then when I declare my items: if (Loader.isModLoaded("battlegear2")) { swordKokiri = new ItemZeldaSwordBG2(blah blah blah); } else { swordKokiri = new ItemZeldaSword(blah blah blah); } In this way my mod works fine with or without BattleGear2, but if BG2 is installed alongside, it will use the correct class that interfaces with BG2. It seems wasteful to have 2 classes for the same Item, but I couldn't figure out any way to add the interface and have the mod still work independently. Had to add my answer here, since i work on Battlegear2. The @Optional from fml can "remove" interfaces depending on a mod being installed or not. This is one case where bytecode manipulation is more interesting than reflection, since it allows to hide the code that would crash/throw exception otherwise. The best part being that you don't need to understand asm to use @Optional. Quote Link to comment Share on other sites More sharing options...
coolAlias Posted February 24, 2014 Share Posted February 24, 2014 Had to add my answer here, since i work on Battlegear2. The @Optional from fml can "remove" interfaces depending on a mod being installed or not. This is one case where bytecode manipulation is more interesting than reflection, since it allows to hide the code that would crash/throw exception otherwise. The best part being that you don't need to understand asm to use @Optional. I don't suppose you could provide a short example of how the annotation would look above a class? Whenever I try to add it, I get an error saying I need to fix my project setup because it can't find Optional, and even manually importing the cpw.mods.fml.common package doesn't resolve the issue. The java docs in the Optional class itself weren't enough for me to make sense of what the syntax ought to be. Sorry if this is considered basic Java, but it's not something I'm familiar with. Thanks for the help! Quote http://i.imgur.com/NdrFdld.png[/img] Link to comment Share on other sites More sharing options...
GotoLink Posted February 24, 2014 Share Posted February 24, 2014 I don't suppose you could provide a short example of how the annotation would look above a class? Whenever I try to add it, I get an error saying I need to fix my project setup because it can't find Optional, and even manually importing the cpw.mods.fml.common package doesn't resolve the issue. The java docs in the Optional class itself weren't enough for me to make sense of what the syntax ought to be. Sorry if this is considered basic Java, but it's not something I'm familiar with. Thanks for the help! Well, i guess it would help a lot of people to understand, so, here is a full description. Optional has three annotations, Optional.@InterfaceList, Optional.@Interface, Optional.@Method. The first two use @Target(ElementType.TYPE), meaning you put them above the class declaration. (like @Mod) The latter has @Target(ElementType.METHOD) is obviously supposed to be put above a method. (like @Mod.EventHandler) Optional.@InterfaceList accepts only a array of @Interface, while @Method only accepts a string, the modid. Example time ! @Interface(iface="mods.battlegear2.api.IDyable", modid="battlegear2", striprefs = true)//Will strip IDyable interface from Battlegear2 API if it isn't loaded public class ItemGun extends Item implements ISheathed, IDyable{ //imagine some working code here @Method(modid="battlegear2")//Will strip that specific method if Battlegear2 isn't loaded public boolean sheatheOnBack(ItemStack item){ return false; } } @InterfaceList(@Interface(iface="class.path.Interface1", modid="examplemod1"), @Interface(iface="class.path.Interface2", modid="examplemod2")) public class AwesomeImplementation implements Interface1, Interface2{ Quote Link to comment Share on other sites More sharing options...
coolAlias Posted February 24, 2014 Share Posted February 24, 2014 Well, i guess it would help a lot of people to understand, so, here is a full description. Optional has three annotations, Optional.@InterfaceList, Optional.@Interface, Optional.@Method. The first two use @Target(ElementType.TYPE), meaning you put them above the class declaration. (like @Mod) The latter has @Target(ElementType.METHOD) is obviously supposed to be put above a method. (like @Mod.EventHandler) Optional.@InterfaceList accepts only a array of @Interface, while @Method only accepts a string, the modid. Example time ! Wow, no wonder it wasn't working for me, I was trying to add the @Optional directly...<facepalm> Thanks so much for explaining that. This will be extremely useful EDIT: Though this still doesn't prevent me from crashing when I try to run the client in Eclipse with BG2 as a referenced library... hmmm. One step at a time Quote http://i.imgur.com/NdrFdld.png[/img] Link to comment Share on other sites More sharing options...
GotoLink Posted February 24, 2014 Share Posted February 24, 2014 Wow, no wonder it wasn't working for me, I was trying to add the @Optional directly...<facepalm> Thanks so much for explaining that. This will be extremely useful EDIT: Though this still doesn't prevent me from crashing when I try to run the client in Eclipse with BG2 as a referenced library... hmmm. One step at a time The ModAPITransformer should do the job. I don't see why it wouldn't ? Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.