Cephei

The official unoffical "help a fellow plugin developer" thread

Recommended Posts

@blizzy78: Thank you for that, .removeNode and .addNode are working as expected.

My suspicion is that .setNode was not working either because I had not named the configNodes, or if you try and .setNode into a node that is named the same, it assumes the nodes are the same and does not update. I'm not going to experiment further as I actually want to make progress though.

@Noro: You want to be looking at [KSPField] with the persistent attribute set to be saving data. A quick google should turn up one of the previous posts explaining how those work better then I can here in a couple minutes.

D.

Share this post


Link to post
Share on other sites

@Diazo: Nope, [KSPField] is definetely not the thing I want. It's for part plugins mainly and it just save attributes by itself. I have fully functional save/load functionality in my plugin already.

What I want, is a way to create instance of the plugin's class that will live through entire game session, without destroying it and recreating in every scene change. But it seems impossible with the way KSP manages plugins currently.

Sign, I guess I just have to save and recreate entire data structure on save/load(and that's gonna be a lot complicated data :( ).

Share this post


Link to post
Share on other sites
What I want, is a way to create instance of the plugin's class that will live through entire game session, without destroying it and recreating in every scene change. But it seems impossible with the way KSP manages plugins currently.

Well, you could do something similar to SCANsat. Treat your ScenarioModule as a repository for information and then create a separate KSPAddon-marked MonoBehaviour for the work logic. KSP will destroy ScenarioModules between scenes regardless but you can use the DontDestroyOnLoad(this) on a regular KSPAddon and it will work as you'd like

Share this post


Link to post
Share on other sites

How would one go about overriding a built in class? Like, let's say I extended Wheel, overrode a method, and now I want KSP to use my wheel rather than it's own? I've looked into Reflection but, well it's a bit confusing and a LOT of code for something that seems like it should be simple.

Share this post


Link to post
Share on other sites
How would one go about overriding a built in class? Like, let's say I extended Wheel, overrode a method, and now I want KSP to use my wheel rather than it's own? I've looked into Reflection but, well it's a bit confusing and a LOT of code for something that seems like it should be simple.

You mean you extended the wheel module? Something as simple as changing the names of the modules in the configs will do.

Share this post


Link to post
Share on other sites
You mean you extended the wheel module? Something as simple as changing the names of the modules in the configs will do.

Yeah I know I could do that, but I'm trying to avoid it. The question is more theoretical than something I intend to use in a practical mod.

I don't have the .dll open atm so these are just made up names but let's say the .dll has a Wheel class with a method named Roll(). Is there any way to make it so when the game calls Wheel.Roll() it actually calls MyNewWheel.Roll(). For this example let's assume MyNewWheel : Wheel and only the Roll method has been overridden. Again all theoretical, any resemblance to real people or places is purely fiction.

Share this post


Link to post
Share on other sites

Well unfortunately for you I can see two ways to do this, an easy and a complex one. The easy one is to do what I proposed and replace the name of modules with ModuleManager. It's really as simple as this:

@PART[*]:HAS[@MODULE[ModuleWheel]]
{
@MODULE[ModuleWheel]
{
@name = YourModuleWheel
}
}

And that will do everything for you. You just really have to inherit ModuleWheel and do whatever tweaks you need to do.

The other one might be harder and it might not even work, and it necessitate reflection. Basically it all depends if ModuleWheel uses a public Wheel field, and if so you could be able to dynamically replace it for your own Wheel class since. But if it's not public, you're pretty much doomed because you won't be able to know if it's there or even if theres one given we can only look with non-decompiling assembly browsers at what KSP does.

Really theres no easy way to do it if it's what you were wondering.

Share this post


Link to post
Share on other sites

Yeah, I didn't think it'd be easy or else the answer would have shown up from my first hour of googling. Well, it did, in the form of reflection. I guess it's time to look into the reflection API. Thanks for the replies. Honestly, this wasn't even directly KSP related but there are a lot of talented programmers here so I thought I might get a good answer, which I did, thus why I am quick to dismiss module manager. Once again, thanks for the help.

Share this post


Link to post
Share on other sites
Yeah, I didn't think it'd be easy or else the answer would have shown up from my first hour of googling. Well, it did, in the form of reflection. I guess it's time to look into the reflection API. Thanks for the replies. Honestly, this wasn't even directly KSP related but there are a lot of talented programmers here so I thought I might get a good answer, which I did, thus why I am quick to dismiss module manager. Once again, thanks for the help.

I'm pretty sure most people here would tell you that ModuleManager is the best solution for what you need. Really, if you can avoid reflection, you always should.

Share this post


Link to post
Share on other sites
I'm pretty sure most people here would tell you that ModuleManager is the best solution for what you need. Really, if you can avoid reflection, you always should.

Yep, I agree, but with what I'm doing I can't avoid it. I actually just found exactly what I'm looking for. So long and thanks for all the fish!

Share this post


Link to post
Share on other sites

What's the proper way to add a new AppLauncher button to the stock toolbar? I want it to show in the space centre only. Right now I am doing this (posted to pastebin because it's a bit long and terrible without highlighting).

Am I missing something obvious?

--- EDIT:

I changed the code around to mimic the code I found in FAR. I changed OnAwake to Awake (dammit, KSP). Overall the code is pretty much the same as it was before: code.

However, this throws an exception that I don't understand: in the log file, I find this


[DangIt]: Instantiating runtime...

(Filename: C:/BuildAgent/work/d63dfc6385190b60/artifacts/StandalonePlayerGenerated/UnityEngineDebug.cpp Line: 49)


[DangIt]: Awaking runtime...

(Filename: C:/BuildAgent/work/d63dfc6385190b60/artifacts/StandalonePlayerGenerated/UnityEngineDebug.cpp Line: 49)


NullReferenceException: Object reference not set to an instance of an object
at ScenarioModule.Load (.ConfigNode node) [0x00000] in <filename unknown>:0


at ScenarioRunner.AddModule (.ConfigNode node) [0x00000] in <filename unknown>:0


at ProtoScenarioModule.Load (.ScenarioRunner host) [0x00000] in <filename unknown>:0


at ScenarioRunner+.MoveNext () [0x00000] in <filename unknown>:0

--- EDIT 2: solved (well... kinda), two posts down.

Edited by Ippo
"solved"

Share this post


Link to post
Share on other sites

I see a confignode in there, my first through is that there is a scenariomodule saved in the persistent.sfs file that no longer has an attached .dll file.

So KSP sees the OldScenarioData configNode, goes to load it, but there is no .dll with OldScenarioData :ScenarioModule present anymore.

Can not test this at the moment, but I vaguely remember something like this from a while ago when I was testing.

D.

Share this post


Link to post
Share on other sites

Well, no, it had to be something else.

I was able to replicate the bug even in new saves and even with just my mod installed to rule out every other possibility, so it was definitely a bug in my code.

However I was looking at the logs, and the order in which the debug logs appeared didn't quite match what I expected (it tried to add the button BEFORE Awake :huh:), so I tried a coroutine:


public DangIt()
{
// constructor stuff
this.StartCoroutine("AddAppButton");
}

IEnumerator AddAppButton()
{
while (!ApplicationLauncher.Ready || !this.IsReady)
yield return null;


if (ApplicationLauncher.Ready && this.IsReady)
{
try
{
Debug.Log("About to add the app button...");


Texture btnTex = GameDatabase.Instance.GetTexture("DangIt/Textures/appBtn", false);
if (btnTex == null)
throw new Exception("The texture wasn't loaded!");


appBtn = ApplicationLauncher.Instance.AddModApplication(
onAppBtnToggleOn,
onAppBtnToggleOff,
dummyVoid,
dummyVoid,
dummyVoid,
dummyVoid,
ApplicationLauncher.AppScenes.SPACECENTER,
btnTex);
}
catch (Exception e)
{
Debug.Log("[DangIt]: Error! " + e.Message);
throw;
}
}
}

Maybe it's a bit overkill, but it works now. :shrug:

Share this post


Link to post
Share on other sites

I've implemented it in a mod myself, pretty much just following the same stuff you have here. The only diff I can see/think of is that I add the button to the launcher in response to the GameEvents.onGUIApplicationLauncherReady event (from this note: http://forum.kerbalspaceprogram.com/threads/86682-Appilcation-Launcher-and-Mods?p=1302840&viewfull=1#post1302840). You can see my code in the ARP mod from my signature.

Share this post


Link to post
Share on other sites

Okay, I'm trying to find all game objects of type ShipTemplate and I'm not sure what's going wrong.

The following works:

UnityEngine.GameObject[] loadShipList = FindObjectsOfType<UnityEngine.GameObject>();

and returns 5000 or so items, including the 18 items I want to find.

The following does not:

ShipTemplate[] loadShipList = FindObjectsOfType<ShipTemplate>();

and it throws the following error:

Error	2	The type 'ShipTemplate' cannot be used as type parameter 'T' in the generic type or method 'UnityEngine.Object.FindObjectsOfType<T>()'. There is no implicit reference conversion from 'ShipTemplate' to 'UnityEngine.Object'.	H:\KSPEdits\AGExt\AGExt\Editor.cs	315	43	AGExt

So, ShipTemplate does not inherit UnityEngine.Object? Then how do I go about finding the 18 ShipTemplates I know exist? But doesn't it have to inherit? I find them in the list when I get all UnityEngine.GameObjects.

D.

Edited by Diazo

Share this post


Link to post
Share on other sites
Okay, I'm trying to find all game objects of type ShipTemplate and I'm not sure what's going wrong.

I'm at work, so I can't look it up myself here. What type is ShipTemplate actually? Is it a MonoBehaviour? If so, you could try finding all GameObjects that have it attached:


FindObjectsOfType<GameObject>().Where(go => go.GetComponent<ShipTemplate>() != null)

This works with all types that are derived from Component (such as MonoBehaviour.)

Share this post


Link to post
Share on other sites

Alright, I'll give that a shot, it looks like it should work.

I do not remember what the type of ShipTemplate was exactly but I'll give it a shot.

Thanks,

D.

Share this post


Link to post
Share on other sites

Hi, I've made a bit of progress on adding a science experiment module to a part through a KSP event button, but I'm having some problems. I have the following sections of code in my plugin:


public class RUILifeTech005 : PartModule
{
[KSPEvent(guiActive = true, guiName = "Activate", active = true)]
public void Activate()
{
Events["Activate"].active = false;
Events["Deactivate"].active = true;
print("Adding Module...");
part.AddModule("LifeTechScience");
}

[KSPEvent(guiActive = false, active = false)]
public void Deactivate()
{
Events["Activate"].active = true;
Events["Deactivate"].active = false;
}
}

and then:


public class LifeTechScience : ModuleScienceExperiment
{
public override void OnSave(ConfigNode lifetechnode)
{
print("Added Module!");
lifetechnode.AddNode("MODULESCIENCEEXPERIMENT");
lifetechnode.AddValue(name, "MODULESCIENCEEXPERIMENT");
lifetechnode.AddValue(experimentID, "LifeTechAnalysis");
lifetechnode.AddValue(experimentActionName, "Conduct Analysis");
lifetechnode.AddValue(resetActionName, "Reset Data");
lifetechnode.AddValue("useStaging", true);
lifetechnode.AddValue("useActionGroups", true);
lifetechnode.AddValue("hideUIwhenUnavailable", true);
lifetechnode.AddValue("xmitDataScalar", "0.2f");
lifetechnode.AddValue("dataIsCollectable", true);
lifetechnode.AddValue(collectActionName, "Collect Data");
lifetechnode.AddValue("interactionRange", 1.2f);
lifetechnode.AddValue("rerunnable", true);
}
}

The problem seems to be with the actual values of the experiment module. It seems to register the addition of a new experiment module, coming up with buttons for Deploy, Review Data and Reset, though none of these actually do anything. The debug console prints the "Adding Module..." that I added to the code to see what bits work, but not "Added Module!". It also comes up with an error: "Reference not set to an instance of an object." I'm new to creating config nodes in plugins, so any help would be great. :) Thanks

Share this post


Link to post
Share on other sites

Hi, is there any way to obtain part persistent id?

I'm trying to access other parts from my part and I cannot find any id field that is persistent in VAB, in game, after reload or vessel switch.


if ((Time.time - lastUpdate) > logInterval)
{
foreach (Part _part in this.vessel.Parts)
{
Debug.Log("vessel name " + _part.vessel.name +
" | name " + _part.name +
" | ClassID " + _part.ClassID +
" | ClassName " + _part.ClassName +
" | ConstructID " + _part.ConstructID +
" | uid " + _part.uid +
" | flightID " + _part.flightID);
}
lastUpdate = Time.time;
Debug.Log("My Module " + this.vessel.Parts.Count);
}

Code above generates this output:


[LOG 00:44:02.865] [FLIGHT GLOBALS]: Switching To Vessel test 3 ----------------------

[LOG 00:44:19.965] vessel name mumech.MJ2.Pod (test 3) | name largeSolarPanel | ClassID 2480147 | ClassName Part | ConstructID largeSolarPanel_4294870102 | uid 4294870098 | flightID 3679346614

[LOG 00:44:19.987] [FLIGHT GLOBALS]: Switching To Vessel test 2 ----------------------

(...)

[LOG 00:46:04.011] [FLIGHT GLOBALS]: Switching To Vessel test 3 ----------------------

[LOG 00:46:15.636] vessel name mumech.MJ2.Pod (test 3) | name largeSolarPanel | ClassID 2480147 | ClassName Part | ConstructID largeSolarPanel_4294469078 | uid 4294469074 | flightID 3679346614

Edited by McOffsky

Share this post


Link to post
Share on other sites

Part derives from UnityEngine.Object, so it has a GetInstanceID() method. However, I wouldn't bet on it being persistent across different scenes as I imagine it will change each time that you focus back to the ship / reload a save.

Try and see if the instance ID is persistent, but personally I think you will have to add your own persistent field.

Share this post


Link to post
Share on other sites

@McOffSky:

First, there is no link between the VAB and the flight scene for parts.

When you launch a vessel, KSP reads the craft file and makes the vessel with new parts so there is no relation between the information in the .craft file and the parts in flight.

However, once that vessel is created in flight, that part is persistent in the vessel in the persistent.sfs and I would think on of the ID fields is the same? However the two fields I would have assumed that of, ConstructID and uID does seem to stay the same based on your test above. Odd, and I have no answer for that.

D.

Share this post


Link to post
Share on other sites

@Ippo

GetInstanceID() is not useful for me, I need some way of storing part reference to perform actions on it even after game reload.

@Diazo

I will read your plugin code (Actions Extended) for inspiration, it does almost exactly what I need.

Share this post


Link to post
Share on other sites

Well, the only solution that comes to my mind is to create a PartModule and attach it to every part.

In this module you can have a field with your ID and make it persistent by saving / loading in the OnSave / OnLoad methods.

This would work, but it's so god-awful that there must be a better way. :/

Share this post


Link to post
Share on other sites

Hmm, looking at the persistent.sfs file, uid is a valid field.

ConstructID is not there that I can see though so I would expect uid should do this.

I'll run some actual experiments after supper.

D.

edit: Nope, uid is not persistent. Launch a Kerbal 2, land it next to the launch pad, launch a second Kerbal 2, it now has the same Uid that the first Kerbal 2 did at lauch and the first kerbal 2 now has a different uid.

What does uid stand for then? I though it was Unique IDentifier, but obviously not.

Edited by Diazo

Share this post


Link to post
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
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.