Jump to content

[Solved] How can one mod determine if another is loaded?


Recommended Posts

I'm looking at the possibility of having my AutomatedScreenshots mod do something if the Historian mod is loaded.

Right now they don't play well together (Historian doesn't know when I take a screenshot), and I'm looking into the possibility of having my mod tell Historian that a screenshot is in progress.

So, I'm looking for an example of one mod detecting if another is installed, and if it is, then calling a public function in that mod.

Thanks in advance

LGG

Edited by linuxgurugamer
Link to comment
Share on other sites

I'm looking at the possibility of having my AutomatedScreenshots mod do something if the Historian mod is loaded.

Right now they don't play well together (Historian doesn't know when I take a screenshot), and I'm looking into the possibility of having my mod tell Historian that a screenshot is in progress.

So, I'm looking for an example of one mod detecting if another is installed, and if it is, then calling a public function in that mod.

Thanks in advance

LGG

Two methods off the top of my head:

  1. Check if the assembly for Historian is loaded (example here).
  2. Use ModuleManager silliness - have a node of some sort that has NEEDS[Historian]. Check if that node is loaded.

Link to comment
Share on other sites

Two methods off the top of my head:

  1. Check if the assembly for Historian is loaded (example here).
  2. Use ModuleManager silliness - have a node of some sort that has NEEDS[Historian]. Check if that node is loaded.

Thanks, #1 works well.

Although you do have a function to look for RemoteTech internal to the Version class

#2 is silly as you said, since not only do I need to know if it's there, I need to be able to access a public function in it.

The name of the public function will be known, as will the parameter list.

I think I've figured it out, I will need to use the function GetComponent.

Suggestions are useful, but (hopefully) not needed right now.

Thanks

Edited by linuxgurugamer
Link to comment
Share on other sites

Ok, I'm officially frustrated :-)

First, what I'm trying to do:

Using Xamarin Studio

I created a local copy of the Historian mod, and added a couple of public functions so that I can properly tell it when to activate.

I have verified that it is built targeting "Mono/.NET 3.5" in the project options

After building it, I manually copied the DLL into my dev install.

I'm trying to access this from my AutomatedScreenshots mod.

I have verified that it is built targeting "Mono/.NET 3.5" in the project options

First, I added a reference to the Historian dll in the References

Then, I added the following to the top of my file. There are two lines for Historian, I've tried it with each one by itself, and both together:


using System.Reflection;
using KSEA.Historian;
using Historian;

When I build with just the above addition, it builds and then runs properly.

The only thing I tried to do is to create a private variable which will be used to get a pointer to the component. For now, the ONLY code I added is the following:


private Historian historian;

I'm getting the following error in the log :


[LOG 13:29:22.986] PhysicsGlobals: Loading database
[LOG 13:29:23.304] Load(Assembly): /ModuleManager.2.6.6
[LOG 13:29:23.353] AssemblyLoader: Loading assembly at R:\KSP_1.0.4_Dev\GameData\ModuleManager.2.6.6.dll
[LOG 13:29:24.139] AssemblyLoader: KSPAssembly 'ModuleManager' V2.5
[LOG 13:29:24.139] Load(Assembly): 000_Toolbar/Toolbar
[LOG 13:29:24.140] AssemblyLoader: Loading assembly at R:\KSP_1.0.4_Dev\GameData\000_Toolbar\Toolbar.dll
[LOG 13:29:24.142] AssemblyLoader: KSPAssembly 'Toolbar' V1.0
[LOG 13:29:24.142] Load(Assembly): AutomatedScreenShots/Plugins/AutomatedScreenshots
[LOG 13:29:24.143] AssemblyLoader: Loading assembly at R:\KSP_1.0.4_Dev\GameData\AutomatedScreenShots\Plugins\AutomatedScreenshots.dll
[LOG 13:29:24.145] Load(Assembly): KSEA/Historian/Historian
[LOG 13:29:24.145] AssemblyLoader: Loading assembly at R:\KSP_1.0.4_Dev\GameData\KSEA\Historian\Historian.dll
[LOG 13:29:24.147] Load(Assembly): MinAmbience/MinAmbience
[LOG 13:29:24.147] AssemblyLoader: Loading assembly at R:\KSP_1.0.4_Dev\GameData\MinAmbience\MinAmbience.dll
[LOG 13:29:24.151] AssemblyLoader: Loading assemblies
[ERR 13:29:24.173] AssemblyLoader: Exception loading 'AutomatedScreenshots': System.Reflection.ReflectionTypeLoadException: The classes in the module cannot be loaded.
at (wrapper managed-to-native) System.Reflection.Assembly:GetTypes (bool)
at System.Reflection.Assembly.GetTypes () [0x00000] in <filename unknown>:0
at AssemblyLoader.LoadAssemblies () [0x00000] in <filename unknown>:0

Additional information about this exception:

System.TypeLoadException: Could not load type 'AutomatedScreenshots.AS' from assembly 'AutomatedScreenshots, Version=1.0.5690.22431, Culture=neutral, PublicKeyToken=null'.

I'm obviously doing something wrong, but can't figure out what. I'm guessing it probably has to do with my unfamiliarity with the language.

Help would be appreciated.

Thanks in advance.

Edited by linuxgurugamer
Link to comment
Share on other sites

My suspicion is that you are running into a race condition.

Based on the names, your mod (AutomatedScreenshots) with the letter A loads before the mod you are referencing (Historian) with the letter H. Therefore when your mod tries to find the Historian dependency, it's not there as it has not loaded yet.

Does it work (or at least give you a different error) if you change the name of the Historian folder so that it is named 000Historian? KSP loads mod in alphabetical order of the folder names in GameData AFAIK.

Personally, I prefer to use Reflection for this as it does not run into this issue because it does not check to see if the other mod is loaded until you actually make the call in your code.

I use reflection extensively in my AGX mod, the code in AGX to receive reflection calls is found here in the External.cs file and the code needed in the other mod to make reflection calls can be found here. (Ignore the warning about being out of date, just make sure you are calling the correct method name as listed in External.cs.)

The added bonus to reflection is that the mod does not care if the other mod is present or not. With the reference method you are using currently, if the mod you reference is missing your mod will always fail to load as it currenltly is.

Hope that helps,

D.

edit: It looks like there is also something called KSPAssemblyDependency you could use?

Edited by Diazo
Link to comment
Share on other sites

Many big thanks to Sarbian, who identified the fact that the DLLs weren't being loaded in the right order.

Quick fix was to add a z to the beginning of the directory name.

- - - Updated - - -

My suspicion is that you are running into a race condition.

Based on the names, your mod (AutomatedScreenshots) with the letter A loads before the mod you are referencing (Historian) with the letter H. Therefore when your mod tries to find the Historian dependency, it's not there as it has not loaded yet.

Does it work (or at least give you a different error) if you change the name of the Historian folder so that it is named 000Historian? KSP loads mod in alphabetical order of the folder names in GameData AFAIK.

Personally, I prefer to use Reflection for this as it does not run into this issue because it does not check to see if the other mod is loaded until you actually make the call in your code.

I use reflection extensively in my AGX mod, the code in AGX to receive reflection calls is found here in the External.cs file and the code needed in the other mod to make reflection calls can be found here. (Ignore the warning about being out of date, just make sure you are calling the correct method name as listed in External.cs.)

The added bonus to reflection is that the mod does not care if the other mod is present or not. With the reference method you are using currently, if the mod you reference is missing your mod will always fail to load as it currenltly is.

Hope that helps,

D.

edit: It looks like there is also something called KSPAssemblyDependency you could use?

It was, Sarbian helped me on IRC.

I now have it working using the KSPAssemblyDependency, but will be looking at your code since I think that would be safer.

Thanks

Link to comment
Share on other sites

My suspicion is that you are running into a race condition.

Based on the names, your mod (AutomatedScreenshots) with the letter A loads before the mod you are referencing (Historian) with the letter H. Therefore when your mod tries to find the Historian dependency, it's not there as it has not loaded yet.

Does it work (or at least give you a different error) if you change the name of the Historian folder so that it is named 000Historian? KSP loads mod in alphabetical order of the folder names in GameData AFAIK.

Personally, I prefer to use Reflection for this as it does not run into this issue because it does not check to see if the other mod is loaded until you actually make the call in your code.

I use reflection extensively in my AGX mod, the code in AGX to receive reflection calls is found here in the External.cs file and the code needed in the other mod to make reflection calls can be found here. (Ignore the warning about being out of date, just make sure you are calling the correct method name as listed in External.cs.)

The added bonus to reflection is that the mod does not care if the other mod is present or not. With the reference method you are using currently, if the mod you reference is missing your mod will always fail to load as it currenltly is.

Hope that helps,

D.

edit: It looks like there is also something called KSPAssemblyDependency you could use?

I've been trying to get this reflection code working.

This is the top of the Historian.cs file:


namespace KSEA.Historian
{
[KSPAddon(KSPAddon.Startup.MainMenu, true)]
public class Historian : Singleton<Historian>

I have a modified copy of Historian, with the following added functions:

        public bool set_m_Active(bool m)        {
m_Active = m;
return m_Active;
}
public bool get_m_Active() {
return m_Active;
}


I have the following:


public static bool get_m_active()
{
if (!historianAvailable) {
Log.Info ("historian not available");
return false;
}
try
{
Type calledType = Type.GetType("KSEA.Historian.Historian, Historian");
if (calledType != null)
{
Log.Info("calledType: " + calledType.ToString());
return (bool)calledType.InvokeMember("get_m_Active", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static , null, null, null);
}
}
catch (Exception e)
{
Log.Info ("Error calling type: " + e );
return false;
}
return false;
}

but I'm getting the following error:


[LOG 17:09:26.356] AutomatedScreenshots: Error calling type: System.MissingMethodException: Cannot find method get_m_Active.
at System.MonoType.InvokeMember (System.String name, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object target, System.Object[] args, System.Reflection.ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, System.String[] namedParameters) [0x00000] in <filename unknown>:0
at System.Type.InvokeMember (System.String name, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object target, System.Object[] args) [0x00000] in <filename unknown>:0
at AutomatedScreenshots.Version.get_m_active () [0x00000] in <filename unknown>:0

Any ideas?

Thanks

Edited by linuxgurugamer
Link to comment
Share on other sites

You are running into a reference issue.

Note the "Binding.Static" line here:

 return (bool)calledType.InvokeMember("get_m_Active", BindingFlags.InvokeMethod |  BindingFlags.Public | BindingFlags.Static , null, null, null);

You would need to make your "get_m_Active" method static for that to work.

Otherwise, you need to get a reference to the Historian object that get_m_Active resides in first.

Something like this would work I think?


Type calledType = Type.GetType("KSEA.Historian.Historian, Historian");
MonoBehaviour HistorianRef = (MonoBehaviour)UnityEngine.Object.FindObjectOfType(calledType); //assumes only one instance of class Historian exists as this command returns first instance found, also must inherit MonoBehavior for this command to work. Getting a reference to your Historian object another way would work also.
MethodInfo myMethod = calledType.GetMethod("get_m_Active", BindingFlags.Instance | BindingFlags.Public);
myMethod.Invoke(HistorianRef, null);

Note I'm copying some of my code where I didn't return anything, I think changing the last line to:

bool ReturnVal = myMethod.Invoke(HistorianRef, null);

will work, but you will probably want to double check with Google.

D.

Link to comment
Share on other sites

You are running into a reference issue.

Note the "Binding.Static" line here:

 return (bool)calledType.InvokeMember("get_m_Active", BindingFlags.InvokeMethod |  BindingFlags.Public | BindingFlags.Static , null, null, null);

You would need to make your "get_m_Active" method static for that to work.

Otherwise, you need to get a reference to the Historian object that get_m_Active resides in first.

Something like this would work I think?


Type calledType = Type.GetType("KSEA.Historian.Historian, Historian");
MonoBehaviour HistorianRef = (MonoBehaviour)UnityEngine.Object.FindObjectOfType(calledType); //assumes only one instance of class Historian exists as this command returns first instance found, also must inherit MonoBehavior for this command to work. Getting a reference to your Historian object another way would work also.
MethodInfo myMethod = calledType.GetMethod("get_m_Active", BindingFlags.Instance | BindingFlags.Public);
myMethod.Invoke(HistorianRef, null);

Note I'm copying some of my code where I didn't return anything, I think changing the last line to:

bool ReturnVal = myMethod.Invoke(HistorianRef, null);

will work, but you will probably want to double check with Google.

D.

yes, that did it.

Now I have another problem, but that should be simple (I know, famous last words)

Link to comment
Share on other sites

problems solved, now just waiting to hear from Zeenobit to see if he will be willing to add a simple function to Historian which i can call.

Thanks all for your help

LGG

Just an update, Zeebobit and I have worked together to get Historian updated, so now AutomatedScreenshots works with Historian

yay!

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

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
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  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.

×
×
  • Create New...