Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

Is there a global "time" value? Something like MET but instead as a more universal clock that would have started when you started your game. Seems a lot more reliable than MET for some things.

EDIT: Did some poking around and found


Planetarium.GetUniversalTime ();

Edited by theSpeare
Link to comment
Share on other sites

Planatarium.GetUniversalTime() is what I use also.

It returns the in-game seconds since that saved game started.

Note this means it rolls back when you revert a flight and that because it is a double it returns a decimal value, not an integer.

D.

Link to comment
Share on other sites

Okay, what I hope is a simple question.

I'm trying to catch a null ref exception here.

When in the VAB, EditorLogic.SortedShipList is a List of Parts on the ship being assembled.

However, until you place the first part this list is null as no parts have been placed yet.

So, null check as follows:


public void Update()
{

print("Upd start");

if (EditorLogic.SortedShipList == null)
{
print("it's null");
}
print("update end");
}

Supposedly null is a valid variable to compare against in C# so this is supposed to work. (Google c# null check returns a page of results on this.)

However, that if statement is throwing a null ref exception when I run this code. I've tried several variations and the only conclusion I can come to is that EditorLogic itself is null and so the actual NullRef being thrown on my if statement is there, not at SortedShipList.

But how do I check for null on EditorLogic then?


if(EditorLogic == null)

throws me an error in visual studio that "EditorLogic is a type but used like a variable" and won't compile.

I can simply ignore this as no code should be running anyway if EditorLogic.SortedShipList is empty, but spamming the log with a NullRef every update frame is a bad idea.

Anyone have any ideas?

D.

Link to comment
Share on other sites

I'm trying to catch a null ref exception here.


public void Update()
{

print("Upd start");

if (EditorLogic.SortedShipList == null)
{
print("it's null");
}
print("update end");
}

...

However, that if statement is throwing a null ref exception when I run this code. I've tried several variations and the only conclusion I can come to is that EditorLogic itself is null and so the actual NullRef being thrown on my if statement is there, not at SortedShipList.

But how do I check for null on EditorLogic then?


if(EditorLogic == null)

EditorLogic.SortedShipList probably calls a function that depends on an EditorLogic instance existing. You almost have it; if (EditorLogic.fetch != null) should do the job

Link to comment
Share on other sites

There are barely any cases where it is okay to ignore an exception, and those mostly consist of unexpected IO states (network connection removed, stuff like this). Though admittedly have to live what KSP throws at us...

Anyway, back to your Exception. EditorLogic is a property, not a field. That means it is a function call, thus it can ofc throw an exception. It is kind of a standard to make Properties not throw exception and execute very fast (since they look like fields) and you should follow that while developing, but you can't really depend on that for external code that isn't a professional library. KSP has quite a few objects where you have to check for them being valid before accessing them. Another example is most of FlightGlobals static stuff, that you shouldn't use unless FlightGlobals.ready is set. As for EditorLogic, it is following a singleton pattern, so try checking .fetch property to check whether there is an actual instance those static shortcut can use (since there also is an non-static List<Part> getSortedShipList() ).

Link to comment
Share on other sites

How do i use Config.Load? whenever i use it to load a file at this path: L:/Games/KSP FULL/KSP Directory/KSP_0.23.0/GameData/ExoPlanet/StockSolarSystem/PluginData/Moho/Planet.cfg with the text in it: "recipient = Moho", it throws a NullPointerException

Link to comment
Share on other sites

Okay, I'm now confused.

if (EditorLogic.fetch != null)

returns true, so my code continues. The very next line

if (EditorLogic.SortedShipList != null)

then throws a NullRef exception.

I am now at a loss as to what is going on here.

I'll keep messing with it, but I'm not sure where to go from here.

D.

Link to comment
Share on other sites

Okay, I'm now confused.

if (EditorLogic.fetch != null)

returns true, so my code continues. The very next line

if (EditorLogic.SortedShipList != null)

then throws a NullRef exception.

Try adding a check to see if any parts exist. The game has special emphasis on a root part so I'd start there

if (EditorLogic.fetch != null && EditorLogic.startPod != null)

Link to comment
Share on other sites

In messing around I hacked this together that will work for my purposes:


public void Update()
{
try
{
if(EditorLogic.SortedShipList.Count >= 1) //throw nullref if it can't Count the list
{
ShipListOk = true; //List exists, we are good to go
}
}
catch //NullRef! SortedShipList is not present
{
ShipListOk = false; //Stop running code
}

if (ShipListOk) //SortedShipLists exists?
{
if (EditorLogic.SortedShipList.First<Part>() != null) //check first part in list is not null (see post)
{
ShipListOk = true;
}
else
{
ShipListOk = false;
}
}

if (ShipListOk) //all data needed is present, run the rest of my code
{

//run code

}
}
}
}

There are two conditions I had to satisfy. First, when you load the editor, SortedShipList is in some weird uninitialized state that was throwing the NullRefs ealier, so we try/catch that.

However, when you edit a ship and then remove all parts, there is a single Part left in SortedShipList even though there are no parts on screen. This single part is Null as expected however, so if(EditorLogic.SortedShipList.First<Part>() != null) works as expected here.

If both conditions are satisfied, ShipListOk stays true and I run my code. If ShipListOk is false, skip all the rest of the code in the Update() event.

D.

edit@Evil: That's essentially what I did in adding the second condition check. I used a different second check then you did, but the logic is the same.

Link to comment
Share on other sites

edit: Erm, I just realized you are trying Config.Load, not ConfigNode.Load. I'll leave my example as it also covers loading data from a file but it may not be exactly what you are looking for.

How do i use Config.Load? whenever i use it to load a file at this path: L:/Games/KSP FULL/KSP Directory/KSP_0.23.0/GameData/ExoPlanet/StockSolarSystem/PluginData/Moho/Planet.cfg with the text in it: "recipient = Moho", it throws a NullPointerException

Here's a copy paste that works from my mod. This is all in Start()

TWR1Node = ConfigNode.Load(KSPUtil.ApplicationRootPath + "GameData/Diazo/TWR1/TWR1.cfg"); //load .cfg file
TWR1KeyCodeString = TWR1Node.GetValue("TWR1Key"); //get the value of the TWR1Key value

So if there was a single line in the TWR1.cfg file:

TWR1Key = HelloWorld

the variable TWR1KeyCodeString would have a value of "HellowWorld".

Note that anything you read in like this will be a string. You have to convert it to another format if you want an int, float, etc.

Note you may have to add


using KSP.IO

at the top in your dependencies as I think KSPUtil.ApplicationRootPath may be out of there.

D.

Edited by Diazo
Link to comment
Share on other sites

Alright. This is a question about referring to data in a list, inside a class, inside a list.

I'll lay out a specific example:

AGXPart is a class with 2 properties, Part agxP and List<BaseAction> agBA.

I then make a List<AGXPart> AGXVessel that holds all the parts on a vessel, and a list for each part of that part's baseactions.

I then wanted to get the Name property of the first part in the list as a string to display.

In pseudo code, I'm after something like this:

 "DisplayString" = AGXVessel.First(AGXPart.agxP.name);

I'm currently working around it as follows (pseudo-code):

AGXPart1 = AGXVessel.First();
"DisplayString' = AGXPart1.agxP.name;

but I'm pretty sure there should be a way to do this without having to instantiate an AGXPart object just to get the data string out.

Any ideas?

D.

Link to comment
Share on other sites

In pseudo code, I'm after something like this:

 "DisplayString" = AGXVessel.First(AGXPart.agxP.name);

I'm currently working around it as follows (pseudo-code):

AGXPart1 = AGXVessel.First();
"DisplayString' = AGXPart1.agxP.name;

but I'm pretty sure there should be a way to do this without having to instantiate an AGXPart object just to get the data string out.

Firstly, when you assign an object like this it actually only assigns the reference to the object, it doesn't create a new copy of the object.

Second, you just have your expression a bit mixed up, you need:


String name = AGXVessel.First().agxP.name;

...though, actually, your other solution is better as it enables you to test if First() returned an object (if the list is empty it would return null and you would get an exception). If you've already tested if the list is not empty then you can safely use the shortcut version...

Edited by Padishar
Link to comment
Share on other sites

I'm not 100% sure what your problem is. If you are designing classes, there isn't any problem with designing a simple "tree" structure similar to KSP (pseudo-example, KSP is ofc a little more complicated):


class FlightGlobals{
public static List<Vesel> Vessels;
}
class Vessel{
public List<Part> Parts;
}
class Part{
public String Name;
}

UnityEngine.Debug.Log(FlightGlobals.Vessels[1337].Parts.First().Name);

What i don't understand is "without an instance". The only way to store a string in an class is via an instance or a static var.

Ofc you could write all into a single line: AGXVessel.Parts.First().Name (yours looks a little odd, btw)

Link to comment
Share on other sites

Storing references to core game objects (e.g. Part) across calls to your plugin (or for use by a background thread) is dangerous as you can't be sure that the core game engine doesn't change the objects between the calls. E.g. what would happen if a Part you have a reference to is removed from the rocket (e.g. the user stages or something blows up on your craft) and your code goes on to treat it as still being a part of the ship then unexpected things will happen. I have been modifying the KER vessel simulation code to avoid such problems, mainly by creating my own data structures for the background thread to use so it doesn't have to access any core game data when it might be changing...

Link to comment
Share on other sites

@Padishar: That you for that. Also the tidbit about the way I listed allows for testing if the object exists is something I'll keep in mind.

@Faark: Oops, I think I used instance the wrong way. From my previous post on the line "AGXPart1 = AGXVessel.First();" I was meaning the creation of the AGXPart1 object(reference?) which I felt was an extra step I did not need.

Thank you for the tip about the nested classes though, I will sit down and see if that would work better for me. However because I'm working with lists of Classes defined by KSP and not by me I'll have to see if I want to change my data structure.

This gives me lots of things to look into tonight, we'll see what I can find.

D.

edit: I can confirm

String name = AGXVessel.First().agxP.name;

works exactly as advertised.

Edited by Diazo
Link to comment
Share on other sites

Hey all, I'm running into a problem I thought would be simple to solve. I want to try to grab all the resource types a vessel might have / have storage for. I thought vessel.GetActiveResources() would give me what I wanted, but it only gives me resources that flow everywhere (stuff like ElectricCharge & MonoPropellent, but not LiquidFuel or Oxidizer). Here's the code I used:


GUILayout.BeginVertical();
foreach (Vessel.ActiveResource resource in vessel.GetActiveResources())
{
GUILayout.BeginHorizontal();
GUILayout.Label(resource.info.name);
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();

Y'all have any insights or suggestions?

Link to comment
Share on other sites

Hey all, I'm running into a problem I thought would be simple to solve. I want to try to grab all the resource types a vessel might have / have storage for. I thought vessel.GetActiveResources() would give me what I wanted, but it only gives me resources that flow everywhere (stuff like ElectricCharge & MonoPropellent, but not LiquidFuel or Oxidizer). Here's the code I used:


GUILayout.BeginVertical();
foreach (Vessel.ActiveResource resource in vessel.GetActiveResources())
{
GUILayout.BeginHorizontal();
GUILayout.Label(resource.info.name);
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();

Y'all have any insights or suggestions?

I believe ActiveResources are exactly that, resources currently being used in the ship. If you want the list of resource storage, you need to loop through the parts, like this:

        private List<PartResource> GetResources()
{
return new List<PartResource>(this.vessel.Parts.SelectMany(p => p.Resources.list).GroupBy(r => r.resourceName).Select(g => g.First()));
}

That is using linq is you're comfortable with it. A more conventional way to do it would be like this.

        private List<PartResource> GetResources()
{
List<PartResource> resources = new List<PartResource>();
foreach (Part part in this.vessel.Parts)
{
foreach (PartResource resource in part.Resources)
{
if (!resources.Contains(resource)) { resources.Add(resource); }
}
}
return resources;
}

then you can use the same code as before (nearly) to show labels

        private void ShowResources()
{
GUILayout.BeginVertical();
foreach (PartResource resource in GetResources())
{
GUILayout.BeginHorizontal();
GUILayout.Label(resource.resourceName);
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
}

Link to comment
Share on other sites

I believe ActiveResources are exactly that, resources currently being used in the ship. If you want the list of resource storage, you need to loop through the parts, like this:

Thanks for the info and code. It does seem odd though that ActiveResources() always seems to return resources on the vessel that flow everywhere, whether they're in use or not. Looping through the parts to get the storage does seem a more reliable way of getting resources, so I'll go with that. Thanks again.

Link to comment
Share on other sites

This is probably because rapiers have two ModuleEnginesFX modules (or possibly more, but definitely at least two). There is also a MultiModeEngine module which has members that specify which one is active (the KER plugin reads MultiModeEngine.mode and then looks for the ModuleEnginesFX whose engineID member matches the mode).

Link to comment
Share on other sites

Alright.

How should I go about saving data when I am in the VAB/SPH?

I have not been able to get a Save or OnSave to work and reading the documentation I believe those are in the PartModule only.

OnDestroy kind of works and is where I was running my save routine.

The problem with that is that the OnDestroy in KSP.exe does not pause and so saving in OnDestroy works for a few items as they process fast, but with how much data my mod is saving, objects were being destroyed before I could save them.

I've switched to a mouse detection method where I save if the mouse is over the Save/Launch/Exit buttons in the top right of the screen which works 100% of the time, but I know there are mods out there that allow you to exit the editor in other ways.

Does anyone know of any sort of trigger equivalent to the partModule's OnSave method for the editor?

If not, the two options I can think of are something like a 15 second timer, but I'm worried on ships with hundreds of parts this will be a noticeable hiccup that will quickly get annoying.

The other option is to move to using the PartModule's OnSave, but I have to calculate my value before saving and I'd rather calculate it once and then save it to each part from the editor (as I'm currently trying to do) instead of having each PartModule calculate the same value to save each each time the save method is called.

Anyone have thoughts on this?

D.

Link to comment
Share on other sites

Alright.

How should I go about saving data when I am in the VAB/SPH?

You just need to run some code when you exit the VAB/SPH right? Is there a GameEvent that will work for this? Maybe GameEvents.onGameSceneLoadRequested; add a check to see if you're still in the editor if you don't want it work anywhere else. Or will that have the same problem as your OnDestroy issue?

Edit:

Another question. Does anyone know how, or if it's possible, to get the degree symbol (°) to show in the right click menu? Using a KSPField I can get it to show a string, and update that string, but all of my attempts to add the degree symbol fail. I have tried directly adding it to the string with alt+0176 or alt+248, I have also tried using the UTF-8 hex code \0xB0, and other variations on that. Everything displays only a blank spot in the end, there are no errors and the rest of the string is fine, just nothing is displayed where the symbol is supposed to be. I am compiling with UTF-8.

Edit 2: Directly adding the symbol to a string works fine for writing to the debug window, the hex code doesn't though. So it seems this is an issue with the right-click menu specifically.

Edited by DMagic
Link to comment
Share on other sites

That is a line of thought for me to follow.

I had not tried that one.

The other thought I've had since my last post was to do it in the PartModule's OnSave, but simply make a method call to a method in my editor code that has a 3 second timer.

So the first part on the ship calls the save method, my values are calculated and saved both to the part and a local variable, then the second part calls the save method, but because it is within 3 seconds, the save method just passes the local variables back without doing all the calculations again.

My only worry with that is that the parts with call OnSave in parallel, not in series, so I have the calculations being performed for each part all at the same time, which is even worse from a performance perspective.

D.

Link to comment
Share on other sites

The other option is to move to using the PartModule's OnSave, but I have to calculate my value before saving and I'd rather calculate it once and then save it to each part from the editor (as I'm currently trying to do) instead of having each PartModule calculate the same value to save each each time the save method is called.

Keep it simple, especially for the first test release. Couldn't you just combine all of the current parts AG assignments into a single string? Kinda like struts do...

I don't see how else you could save data into a ShipConstruct and thus a .craft file. A dummy part might work as well, but would make even more problems when removing your mod.

ps: Are you sure your problems are Multi-Threading related? Would surprise me. Can't await to inspect your code for details :)

Edited by Faark
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
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...