Jump to content

Referencing assemblies and plugin loading oder


Recommended Posts

I have found a workaround the first time I bumped into this problem, but the workaround is cumbersome to use and I anticipate bumping into a much more severe case in the near future, so I'd rather know if a more sensible solution exists:

Assume that you wish to access methods and variables of a different KSP plugin.

The obvious way to do it, referencing the assembly at build time, and just creating new instances of it's classes and/or locating them within KSP hierarchy and using them as objects of known types works... but only in one case: If the plugin you're referencing is already loaded by the assembly loader by the time your plugin starts up. If it isn't, your assembly will fail to load.

The plugin loader apparently goes through all the assembly DLLs in alphabetical order, so there is sort of a way to ensure it loads, you just name your assembly DLL starting with Z and it will be guaranteed to load late. However, you might also want to continue working if the plugin you're referencing is not present in the system, which will in that case always fail.

With my very limited understanding of C# I've been able to cook up a way of referencing these objects by reflection, kind of like this:


targetPlugin = aValueOfaKSPclassWeFoundTheInterestingPluginAs;
FieldInfo thatBloodyField = targetPlugin.GetType ().GetField ("FieldName");

//...and then,

thatBloodyField.SetValue (targetPlugin, ourNewValue);

That works, but I'd much rather find a way of getting loaded anyway instead.

Can anyone advise?

Link to comment
Share on other sites

Do something like this:

Type targetPlugin = Type.GetType("PluginNamespace.PluginClass, PluginAssembly");
if(targetPlugin != null) {
// do stuff with the class reference
} else {
// plugin isn't loaded
}

Edit: Forgot the assembly name, that's the part I added after the comma. For KSP plugins I'd assume you know the name, it's just the name of the DLL. If you don't know the assembly name, you can also just try to get a reference from every assembly that's currently loaded:

foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies()) {
type = a.GetType("PluginNamespace.PluginClass");
if(type != null) {
// here we go...
}
}

This gives you access to classes and their static members. You can instantiate classes like this:

object pluginObject = Activator.CreateInstance(type);

And then the fields and members stuff works just like it works for static fields and members, just pass that object reference instead of the class reference to GetValue()/SetValue() etc.

You might get this error at runtime:

Method not found: 'System.Type.op_Equality'

This means your project is set to build for .Net 4.0 or higher as the target platform (likely if you're using Visual Studio or Xamarin Studio instead of the MonoDevelop that ships with Unity). You need to set it to .Net 3.5.

Edited by damny
Link to comment
Share on other sites

Actually, the problem is not deciding whether to run the code related to a particular foreign plugin or not -- if you found an instance of it in the hierarchy, you access the fields on it and/or call it's methods, otherwise, well, just don't. The problem is the fact that as far as I can tell, KSP's plugin loading mechanism will simply refuse to load your assembly if it directly references a type that is not in any assembly loaded up to that point. You can ensure your assembly loads late, but then, if that assembly you reference is never loaded because it's not present, that won't help you because your assembly will fail to load for the same reason. As far as I can see, even just having a variable to hold a reference to a missing type is enough for this to happen, and if I'm wrong, I'd prefer to see a live example that refutes it.

I'm not sure that having an object that might be a reference to an instance of a foreign class that I'm not allowed to name will be enough to convince the compiler to let me directly use it's methods. The bypass mechanism cited above works for public fields, but to call methods on that instance I think I'll have to work out a much more syntactically complex mess of creating a delegate to hold a reference to particular methods...

Well, at least encapsulating it in a class of your own takes some of the pain away.

Link to comment
Share on other sites

The bypass mechanism cited above works for public fields, but to call methods on that instance I think I'll have to work out a much more syntactically complex mess of creating a delegate to hold a reference to particular methods...

You're making this sound so complicated...


Type pluginClass = Type.getType("Namespace.Class, Assembly");
if(pluginClass != null) {
pluginInstance = Activator.CreateInstance(pluginClass);
pluginMethod = pluginClass.GetMethod("methodName");
}

...

if(pluginMethod != null) {
pluginMethod.Invoke(pluginInstance, new object[] { 1, 2, 3, 4 });
}

Of course you can encapsulate everything to make it look more tidy, but I don't see how any of this is particularly painful. I'd only bother with a full proxy class if you're calling many different methods...

You can also make a simple helper method like this


private static object call(object obj, string method, params object[] args) {
return obj.GetType().GetMethod(method).Invoke(obj, args);
}

And then call it like this:


call(pluginInstance, "methodName", 1, 2, 3, 4);

Performance is only going to be important if you're going to call this every frame...

Link to comment
Share on other sites

You're making this sound so complicated...

First week into C#, it kind of is, I'm more comfortable with holding seven levels of parentheses in my head than an object hierarchy. :P:)

I'm still writing an encapsulating class for my own plugin just for practice and because it makes use of method overloading, so locating a specific method is not quite so intuitive. This way, at least if anyone else wants to do the same I have a clear example how.

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