Jump to content

[ 1.7.2 ] Check if another mod is installed


Texenox

Recommended Posts

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?

Link to comment
Share on other sites

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 :D

 

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

Link to comment
Share on other sites

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
}

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

 

Link to comment
Share on other sites

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!

Link to comment
Share on other sites

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{

Link to comment
Share on other sites

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 :D

 

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 :P

Link to comment
Share on other sites

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 :D

 

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 :P

The ModAPITransformer should do the job.

I don't see why it wouldn't ?

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



×
×
  • Create New...

Important Information

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