Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

A code sample would be helpful. What you're trying to achieve is not immediately clear from your explanation, and the problems you are experiencing are unlikely to be due to the number of vessels involved (Lists can take millions of items before failing).

What I think you're trying to do is establish a sum of each resource across all vessels in a list of some sort


[COLOR=#008000]// 'vessels' is the list/array of all the relevant vessels you are looking at

// keep the resource totals in a dictionary for easy reference[/COLOR]
Dictionary<string, double> resourceTotals = new Dictionary<string, double>();
for (int i = 0; i < vessels.Count; i++)
{
Vessel v = vessels[i];
for (int j = 0; j < v.Parts.Count; j++)
{
Part p = v.Parts[j];
for (int k = 0; k < p.Resources.Count; k++)
{
PartResource r = p.Resources[k];

[COLOR=#008000] // add the resource amount in this part to the relevant resource total, or create a new total if no previous part had this resource[/COLOR]
if (resourceTotals.ContainsKey(r.name))
resourceTotals[r.name] += r.amount;
else
resourceTotals.Add(r.name, r.amount);
}
}
}

Edited by Crzyrndm
Link to comment
Share on other sites

1) Triple check the log for errors when entering the editor scenes. That callback is very fragile and an error from any of the subscribed functions is enough to bring it to a grinding halt

2) Tried manually refreshing the category UI? Click on Filter by Function to close it and then open it again

Thanks man, i looked again and found a TypeLoadException: Could not load type 'System.Func' error. I compiled against the wrong .NET Version...Although i wonder why the Partmodules in the same dll worked...

Link to comment
Share on other sites

A code sample would be helpful. What you're trying to achieve is not immediately clear from your explanation, and the problems you are experiencing are unlikely to be due to the number of vessels involved (Lists can take millions of items before failing).

What I think you're trying to do is establish a sum of each resource across all vessels in a list of some sort


[COLOR=#008000]// 'vessels' is the list/array of all the relevant vessels you are looking at

// keep the resource totals in a dictionary for easy reference[/COLOR]
Dictionary<string, double> resourceTotals = new Dictionary<string, double>();
for (int i = 0; i < vessels.Count; i++)
{
Vessel v = vessels[i];
for (int j = 0; j < v.Parts.Count; j++)
{
Part p = v.Parts[j];
for (int k = 0; k < p.Resources.Count; k++)
{
PartResource r = p.Resources[k];

[COLOR=#008000] // add the resource amount in this part to the relevant resource total, or create a new total if no previous part had this resource[/COLOR]
if (resourceTotals.ContainsKey(r.name))
resourceTotals[r.name] += r.amount;
else
resourceTotals.Add(r.name, r.amount);
}
}
}

I think we're on the same page. I'll see if I can apply your suggestion to my code.

Link to comment
Share on other sites

Thank you for your help, but I think we had a misunderstanding on what exactly it was I wanted to do. I want to be able to display the resource totals of each vessel (eg. Vessel 1 Electric Charge: 50, Vessel 2 Electric Charge: 275, etc). A Dictionary did look like it might be what I needed, however, if I write a code like the one below, the numbers will just increase infinitely. I've been looking for a way around it but nothing seems to work.


//vessels is a List<Vessel>
//ElectricChargeTotals is Dictionary<string, double>

foreach(Vessel v in vessels)
{
foreach(Part battery in v.parts)
{
if(battery.Resources.Contains("ElectricCharge"))
{
if(ElectricChargeTotals.ContainsKey(v.GetName()))
{
ElectricChargeTotals[v.GetName()] += battery.Resources["ElectricCharge"].amount;
}
else
{
ElectricChargeTotals.Add(vesselwithunit.GetName(), battery.Resources["ElectricCharge"].amount);
}
}
}
}

//information is a string that is to be displayed in a GUI.Window
//vesselnames is a List<string> containing the names of all the relevant vessels
//index is an int to enable scrolling through pages of information for each vessel

information = ElectricChargeTotals[vesselnames[index]].ToString("f0");

Link to comment
Share on other sites

Are you clearing the totals before running the sum each time? If "ElectricChargeTotals" is a global variable and you are running this in Update or similar, you need to clear the dictionary somehow before starting.

//vessels is a List<Vessel>
//ElectricChargeTotals is Dictionary<string, double>

ElectricChargeTotals = new Dictionary<string, double>();
// or
ElectricChargeTotals.Clear();
// or
foreach (KeyValuePai<string, double> kvp in ElectricChargeTotals)
kvp.Value = 0;

Link to comment
Share on other sites

Are you clearing the totals before running the sum each time? If "ElectricChargeTotals" is a global variable and you are running this in Update or similar, you need to clear the dictionary somehow before starting.

//vessels is a List<Vessel>
//ElectricChargeTotals is Dictionary<string, double>

ElectricChargeTotals = new Dictionary<string, double>();
// or
ElectricChargeTotals.Clear();
// or
foreach (KeyValuePai<string, double> kvp in ElectricChargeTotals)
kvp.Value = 0;

To my knowledge, the problem isn't that the values for each vessel are being added together, if that's what you're suggesting. When I scroll through the pages for each vessel, the strings show different doubles for the Electric Charge totals of each vessel, as the Dictionary separates them out. As far as I can tell, the issue is that the += operator here...


if(ElectricChargeTotals.ContainsKey(v.GetName()))
{
ElectricChargeTotals[v.GetName()] += battery.Resources["ElectricCharge"].amount;
}

... is continuously adding on the amount of each part because it doesn't know when to stop. I've looked for ways around this but I really can't seem to find anything.

Link to comment
Share on other sites

No, that wasn't what I was trying to say. Take this code:



public void Update()
{
foreach(Vessel v in vessels)
{
foreach(Part battery in v.parts)
{
if(battery.Resources.Contains("ElectricCharge"))
{
if(ElectricChargeTotals.ContainsKey(v.GetName()))
{
ElectricChargeTotals[v.GetName()] += battery.Resources["ElectricCharge"].amount;
}
else
{
ElectricChargeTotals.Add(v.GetName(), battery.Resources["ElectricCharge"].amount);
}
}
}
}
}
Dictionary<string, double> ElectricChargeTotals = new Dictionary<string, double>();

If you only do that, then the totals will continuously increase because each pass you make (once per rendered frame for Update) is added to the total from the previous frame (and the frame before that...)



public void Update()
{
[B][COLOR=#ff0000] ElectricChargeTotals.Clear(); [/COLOR][COLOR=#008000]// discard old pass totals[/COLOR][/B]
foreach(Vessel v in vessels)
{
foreach(Part battery in v.parts)
{
if(battery.Resources.Contains("ElectricCharge"))
{
if(ElectricChargeTotals.ContainsKey(v.GetName()))
{
ElectricChargeTotals[v.GetName()] += battery.Resources["ElectricCharge"].amount;
}
else
{
ElectricChargeTotals.Add(v.GetName(), battery.Resources["ElectricCharge"].amount);
}
}
}
}
}
Dictionary<string, double> ElectricChargeTotals = new Dictionary<string, double>();

This sets you back to having no EC recorded for any vessel before each pass

Edited by Crzyrndm
Link to comment
Share on other sites

I see what you mean now. I tried making the changes you suggested, but the numbers for Electric Charge still just keep increasing. This is despite the fact that I wrote a print(ElectricChargeTotals.count.ToString()) into the code, and it consistently printed 0. I really don't get what's going on.

Link to comment
Share on other sites

Okay, how I would approach this is with a nested dictionary. (If you were looking to store more complex data, I'd recommend a data storage class.)

I think the comments make things clear:


public class VslResTest
{
Dictionary<Vessel, Dictionary<string, double>> allVesselResources;

public void Update() //replace with your trigger, running this code every update frame will almost certainly cause noticeble slow down to the gameplay.
{
foreach (Vessel vsl in FlightGlobals.Vessels) //cycles all vessels in game, will probably nullref on vessels outside physics range
{
if(allVesselResources.ContainsKey(vsl)) //if vessel is present in our saved data remove it to avoid duplicate data
{
allVesselResources.Remove(vsl);
}
allVesselResources.Add(vsl,new Dictionary<string,double>()); //note the new, that zeros all values for the vessel
Dictionary<string, double> vesselResources = allVesselResources[vsl]; //create a shortcut reference to this vessel's dictionary
foreach(Part p in vsl.parts) //cycle through parts on the current vessel
{
foreach(PartResource pRes in p.Resources) //cycle through all resources on this part
{
if(!vesselResources.ContainsKey(pRes.resourceName)) //check if resource exists already
{
vesselResources.Add(pRes.resourceName, 0f); //add resources with zero amount if it doesn't exist
}
vesselResources[pRes.resourceName] += pRes.amount;
}
}
} //close foreach cycling through vessels
Debug.Log("Electric charge on focus vessel is " + allVesselResources[FlightGlobals.ActiveVessel].["ElectricCharge"]); //print amount of ElectricCharge on currently focused vessel to the log.
}
}

Note this is a different approach then what Crzyrndm suggested, don't try and reconcile the two methods as they are quite different solutions to this question.

D.

Edited by Diazo
Link to comment
Share on other sites

Positions are almost always transform.position (and if not, almost always transform.localPosition), but I haven't done anything with the RnD view so I don't know specifics. Have you looked at the code for the old tech tree mods and seen how they position things?

Hmmm I had tried those, but they didn't work. After loading up each RDNode as "node", none of the options listed below work, whereas something like node.tech.techID will work and give me the ID of the node. Even though the code for the other old tech tree mods I looked at seem to use node.transform.localPosition... I guess I need to find it in the config file and rewrite it directly.

node.transform.position

node.transform.localPosition

node.tech.transform.position

node.tech.transform.localPosition

transform.position

transform.localPosition

Link to comment
Share on other sites

I need a bit of help, I hope this is the right place to ask.

I want to make a contract pack for career mode but I have no idea how to go about doing this. I have zero experience with modding in general (other than using other peoples' mods and wondering how they made something so cool!)

There are a lot of mod tutorials for KSP out there but from what I've seen they only seem to cover how to add new parts to the game which isn't what I'm looking for.

Could anyone give me some tips or point me in the right direction? :) Thanks!

Link to comment
Share on other sites

I need a bit of help, I hope this is the right place to ask.

I want to make a contract pack for career mode but I have no idea how to go about doing this. I have zero experience with modding in general (other than using other peoples' mods and wondering how they made something so cool!)

There are a lot of mod tutorials for KSP out there but from what I've seen they only seem to cover how to add new parts to the game which isn't what I'm looking for.

Could anyone give me some tips or point me in the right direction? :) Thanks!

You could start here:

http://forum.kerbalspaceprogram.com/threads/92559-Community-driven-documentation-masterpost?p=1387612#post1387612

Link to comment
Share on other sites

Just wondering if there's any way to find out what part of my plugin is causing a null reference exception. I keep seeing it come up in in the debug menu under the same circumstances, but that message isn't particularly helpful when trying to find what's causing it. Otherwise would anyone be prepared to look at my code and see if they can tell what's going on? Thanks. Also, I know this isn't the right page to ask about it, but my plugin involves increasing the vessel load and unload distance (to cover the entire solar system, then it is returned to default for vessels that are not needed to prevent unnecessary vessel loading) and I've noticed an issue with overheating for no obvious reason occurring on vessels that are loaded but not the active vessel when I've been testing the plugin. Any ideas if there is some reason for this? Here's the code: http://pastebin.com/raw.php?i=8zMHDjiN The null reference exception always occurs after a vessel without the part the plugin is assigned to goes out of range and is unloaded, but only if the Activate() KSP Event is performed, even once, whilst the vessel is within the loading distance. If it is performed outside the loading distance, the issue does not occur.

Edited by Zyrac
Link to comment
Share on other sites

Welcome to debugging KSP plugins. The "Debug.Log()" command is going to become your best friend.

In this case, if you open the output_log.txt it will have some more details then what the in-game log shows, including the method name throwing the error.

Looking at your code, I think it is the SendSignals() method causing the nullref on the second ForEach.

How to prove it with the Debug.Log I mentioned earlier would be:


void SendSignal()
{
Debug.Log("Check A");
foreach(Vessel targetvessel in FlightGlobals.Vessels)
{
Debug.Log("Check B");
foreach(Part targetpart in targetvessel.parts)
{
Debug.Log("Check C");
if(targetpart.name == "ruitelecheck")
{
Debug.Log("Check D");
if(VesselsWithUnit.Contains(targetvessel) == false)
{
Debug.Log("Check E");
VesselsWithUnit.Add(targetvessel);
}
VesselNames.Add(targetvessel.GetName());
}
}
Debug.Log("Check F");

This will print "Check _" to the log, so if you see a Check B in the log, you know the next line is the problem, the foreach(Part targetpart....

Which is where I suspect the error is, when a vessel goes out of physics range it unloads and parts of the vessel class go null. I can't remember what does and doesn't off the top of my head so you will have to test it.

D.

Link to comment
Share on other sites

Just wondering if there's any way to find out what part of my plugin is causing a null reference exception

Are you checking the logs (ksp.log or output_log.txt) to get the full stack trace? That'll tell you which function the exception is occurring in which *can* narrow things down tremendously

Edited by Crzyrndm
Link to comment
Share on other sites

You can also enable VERBOSE_DEBUG_LOG in settings.cfg to get a stack trace inside the Alt+F2 log/Alt+F12 debug menu. I find that it generally has a faster turnaround than looking in the on-disk logs although you'll still need them from time to time for harder-to-find bugs or in cases where the exception is being spammed continuously:


public class ThrowAnExceptionInStart : MonoBehaviour
{
private void Start()
{
print("The vessel name is: " + FlightGlobals.ActiveVessel.name);
}
}
[KSPAddon(KSPAddon.Startup.MainMenu, false)]

[table=width: 500]

[tr]

[td]8a059b932f.png[/td]

[td]daf2ba0440.png[/td]

[/tr]

[/table]

Link to comment
Share on other sites

You can also enable VERBOSE_DEBUG_LOG in settings.cfg to get a stack trace inside the Alt+F2 log/Alt+F12 debug menu. I find that it generally has a faster turnaround than looking in the on-disk logs although you'll still need them from time to time for harder-to-find bugs or in cases where the exception is being spammed continuously:

Well, it's coming up with this:

[Exception]: NullReferenceException
UnityEngine.Component.get_gameObject ()
Vessel.recurseCoMs (.Part part)
Vessel.recurseCoMs (.Part part)
Vessel.findLocalCenterOfMass ()
ProtoVessel..ctor (.Vessel VesselRef)
Vessel.Unload ()
Vessel.Update ()

So I guess it is to do with the plugin trying to access information from a vessel that has been unloaded. I'm not sure how I'd prevent the code from trying to access the information it can't get to, while keeping it working as I want it to, short of eliminating vessel unloading completely, which really doesn't seem like a great idea. :huh:

Link to comment
Share on other sites

Well, I found a solution. I modified the code to make this:


foreach(Vessel targetvessel in FlightGlobals.Vessels)
{
foreach(Part targetpart in targetvessel.parts)
{
if(targetpart.name == "ruitelecheck")
{
if(VesselsWithUnit.Contains(targetvessel) == false)
{
VesselsWithUnit.Add(targetvessel);
}
VesselNames.Add(targetvessel.GetName());
}
}

if(VesselsWithUnit.Contains(targetvessel) == false)
{
if((targetvessel.transform.position - this.vessel.transform.position).magnitude > 2750f)
{
targetvessel.vesselRanges.landed.load = 2500f;
targetvessel.vesselRanges.landed.unload = 2250f;
targetvessel.vesselRanges.flying.load = 2500f;
targetvessel.vesselRanges.flying.unload = 2250f;
targetvessel.vesselRanges.orbit.load = 2500f;
targetvessel.vesselRanges.orbit.unload = 2250f;
targetvessel.vesselRanges.prelaunch.load = 2500f;
targetvessel.vesselRanges.prelaunch.unload = 2250f;
targetvessel.vesselRanges.splashed.load = 2500f;
targetvessel.vesselRanges.splashed.unload = 2250f;
targetvessel.vesselRanges.subOrbital.load = 2500f;
targetvessel.vesselRanges.subOrbital.unload = 2250f;
targetvessel.vesselRanges.escaping.load = 2500f;
targetvessel.vesselRanges.escaping.unload = 2250f;
}
}
}

This waits for targetvessel to go comfortably outside of loading distance before it unloads it. The set up isn't ideal, but at least it's not producing anymore big red text in the debug console :P

Link to comment
Share on other sites

Hi,

i want to force some Kerbals of a part to move out of it when the crew capacity changed. For this i use the following code in the Update() function:


if (this.part.protoModuleCrew.Count() > crewCapcityRetracted)
{
Debug.Log("[KBPS] too much crew!!, Encuraging all Kerbals to move out.");
System.Collections.Generic.List<ProtoCrewMember> crewMembers = this.part.protoModuleCrew;

ProtoCrewMember crewMember = part.protoModuleCrew.FirstOrDefault(x => x != null);
if (crewMember != null)
{
part.RemoveCrewmember(crewMember );
FlightEVA.fetch.spawnEVA(crewMember , part, part.airlock);
}
}

This does remove the Kerbal from the part but spawns a "Unknown Mystery Component" which looks like kerbal and spins wildly around.

When i remove the part.RemoveCrewmember(m); line the right Kerbal is spawned in the second update call and is removed from the vessel, but still the "Unknown Mystery Component" is spawned before.

The log mentions a

[EXC 15:38:50.823] NullReferenceException: Object reference not set to an instance of an object
FlightEVA.onGoForEVA ()
FlightEVA.spawnEVA (.ProtoCrewMember pCrew, .Part fromPart, UnityEngine.Transform fromAirlock)
PlanetarySurfaceStructures.PlanetaryModule.Update ()
[EXC 15:38:50.846] ArgumentOutOfRangeException: Argument is out of range.
Parameter name: index
System.Collections.Generic.List`1[ProtoCrewMember].get_Item (Int32 index)
KerbalEVA+.MoveNext ()
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
KerbalEVA:OnStart(StartState)
Part:ModulesOnStart()
:MoveNext()

I also added checks whether on of the arguments is null before calling the method. And also other variants like:

System.Collections.Generic.List<ProtoCrewMember> crewMembers = this.part.protoModuleCrew;
ProtoCrewMember crewMember = crewMembers.First();
if ((crewMember != null) && (crewMember.seat != null) && (crewMember.seat.kerbalRef != null))
{
FlightEVA.SpawnEVA(crewMember.seat.kerbalRef);
}

But all resulted in the same behaviour...

Is there an event or flag/variable that i missed to check whether it is save to move a Kerbal into EVA?

Ps.: I only want to "eject" the Kerbals when the scene is loaded. In the OnStart(), only the "Unknown Mystery Component" is spawned so moved the code into update() but it seems to be still at the wrong place.

Can anyone help me with that?

Thanks in advance :)

Edited by Nils277
Link to comment
Share on other sites

While it is a workaround because it uses a different method, have you looked at the GoOnEVA method?

I use that method tied to an action group without this issue so hopefully it would work for you also.

D.

Link to comment
Share on other sites

Oops, I just looked at my code. GoOnEVA is my own method name, it's not stock.

First, my code.

I am actually also using the .SpawnEVA method.

The first difference I see is I don't have the .removeCrewMember line in front of it.

The other difference is I just use the .SpawnEVA(Kerbal) overload which is different then yours.

The two things I can think of is that the .removeCrewMember is not needed, it maybe does something else besides "remove kerbal from part"? (This trips me up quite often, what Squad means with the method names and how I read the method names are quite often two different things.)

The second thing is that the .SpawnEVA(Kerbal,part,airlock) method you are using is bugged. Does it work with just the .SpawnEva(kerbal) method I use? To the best of my knowledge that method defaults to using the current part as a spawn reference.

D.

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