Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

How do you access a config node? I've been using this code but can't seem to figure it out:

public class BPManagedResource
{
    //public string ManagedResource = ConfigNode.GetNode("BPManagedResource");
    public void OnLoad(ConfigNode BPManagedResourceNode)
    {
        string BPManagedResourceValueHelper = BPManagedResourceNode.GetValue("BPManagedResource");
    }
    public string BPManagedResourceValue = BPManagedResourceValueHelper
}

But it keeps giving this error: 

Error    CS0103:

   The name 'BPManagedResourceValueHelper' does not exist in the current context 

Link to comment
Share on other sites

On 6/7/2024 at 12:34 AM, KspNoobUsernameTaken said:

Error    CS0103:

   The name 'BPManagedResourceValueHelper' does not exist in the current context

This indicates that the variable name has exited the scope. If you look at the curly brackets, the variable BPManagedResourceValueHelper was defined inside the function OnLoad. If you try to use it outside of that function, there's no way for the compiler to know what variable you're talking about (since there might be other functions with variables with the same name in them, or indeed none at all).

Instead, you need to define BPManagedResourceValue in the class scope, as you have, and then set it in the function scope (since that's where […]Helper is defined). Order shouldn't matter here, but it's considered good practice to put your class variables ("fields") above your class functions ("methods"), so you can immediately see all the fields of a class.

Link to comment
Share on other sites

7 minutes ago, 0111narwhalz said:

This indicates that the variable name has exited the scope. If you look at the curly brackets, the variable BPManagedResourceValueHelper was defined inside the function OnLoad. If you try to use it outside of that function, there's no way for the compiler to know what variable you're talking about (since there might be other functions with variables with the same name in them, or indeed none at all).

Instead, you need to define BPManagedResourceValue in the class scope, as you have, and then set it in the function scope (since that's where […]Helper is defined). Order shouldn't matter here, but it's considered good practice to put your class variables ("fields") above your class functions ("methods"), so you can immediately see all the fields of a class.

Thank you, but I think I solved this. It is still quite helpful.

Link to comment
Share on other sites

  • 4 weeks later...
Posted (edited)

Please help, I don't understand why this doesn't work:


using System;
using UnityEngine;
namespace ConfigFileReader
{
    [KSPAddon(KSPAddon.Startup.Instantly, false)]
    public class ConfigFileReader : MonoBehaviour
    {
        static readonly string ConfigFilePath = KSPUtil.ApplicationRootPath + "GameData/MyMod/Settings.cfg";
        static ConfigNode Node1 = ConfigNode.Load(ConfigFilePath);
        static string Value1 = Node1.GetValue("Value1");
        static ConfigNode Node2 = GameDatabase.Instance.GetConfigNode("TestNode");
        static string Value2 = Node2.GetValue("Value2");

        public void Update()
        {
            Debug.Log(Time.realtimeSinceStartup + "Value1 is: " + Value1);
            Debug.Log(Time.realtimeSinceStartup + "Value2 is: " + Value2);
        }
    }
}

 

It gives a NRE: object reference not set to instance of an object.

 

Edit: This code doesn't error:


using System;
using UnityEngine;
namespace ConfigFileReader
{
    [KSPAddon(KSPAddon.Startup.Instantly, false)]
    public class ConfigFileReader : MonoBehaviour
    {
        static readonly string ConfigFilePath = KSPUtil.ApplicationRootPath + "GameData/MyMod/Settings.cfg";
        static ConfigNode Node1;
        static string Value1;
        static ConfigNode Node2;
        static string Value2;
        public void Start()
        {
            Node1 = ConfigNode.Load(ConfigFilePath);
            Value1 = Node1.GetValue("Value1");
            Node2 = GameDatabase.Instance.GetConfigNode("TestNode");
            Value2 = Node2.GetValue("Value2");
        }
        public void Update()
        {
            if (String.IsNullOrEmpty(Value1))
            {
                Debug.Log(Time.realtimeSinceStartup + "Value1 is empty");
            }
            else
            {
                Debug.Log(Time.realtimeSinceStartup + "Value1 is: " + Value1);
            }
            if (String.IsNullOrEmpty(Value2))
            {
                Debug.Log(Time.realtimeSinceStartup + "Value2 is empty");
            }
            else
            {
                Debug.Log(Time.realtimeSinceStartup + "Value2 is: " + Value2);
            }
            
        }
    }
}

But it gives empty values. For reference, the cfg file is in a file named Settings.cfg under MyMod and these are the contents:

NODE
{
    name = TestNode
    Value1 = "Blue"
    Value2 = "Red"
}

 

Edit 2:

I think I found something useful here:

Testing this tommorow:

Value1 = Node1.GetNode("TestNode").GetValue("Value1");

 

Edited by KspNoobUsernameTaken
Link to comment
Share on other sites

10 hours ago, KspNoobUsernameTaken said:
        static ConfigNode Node2 = GameDatabase.Instance.GetConfigNode("TestNode");
            Node2 = GameDatabase.Instance.GetConfigNode("TestNode");
NODE
{
    name = TestNode
    Value1 = "Blue"
    Value2 = "Red"
}

In these lines, I think the problem is that your code is using "TestNode" instead of "NODE". GetConfigNode doesn't care about the value of the nested "name" key, it cares about the string that comes right before the open curly brace.

10 hours ago, KspNoobUsernameTaken said:
        static ConfigNode Node1 = ConfigNode.Load(ConfigFilePath);
        static string Value1 = Node1.GetValue("Value1");
            Node1 = ConfigNode.Load(ConfigFilePath);
            Value1 = Node1.GetValue("Value1");
NODE
{
    name = TestNode
    Value1 = "Blue"
    Value2 = "Red"
}

Similarly, here you're not even trying to get the node at all. There should be a GetNode("NODE") in there somewhere to handle that structure.

Link to comment
Share on other sites

So, tried this for Settings.cfg:

TestNode
{
    name = TestNode
    Value1 = "Blue"
    Value2 = "Red"
}

The following is an excerpt from debug log:

[LOG 13:19:13.702] 12.83139Value1 is: "Blue"
[LOG 13:19:13.702] 12.83148Value2 is empty
[LOG 13:19:13.732] 12.86122Value1 is: "Blue"
[LOG 13:19:13.732] 12.86133Value2 is empty
[LOG 13:19:13.732] Load(Audio): Squad/Alarms/Sounds/Classic
[LOG 13:19:13.734] 12.8632Value1 is: "Blue"
[LOG 13:19:13.734] 12.86331Value2 is empty
[LOG 13:19:14.061] 13.19004Value1 is: "Blue"
[LOG 13:19:14.061] 13.1907Value2 is empty
[LOG 13:19:14.062] 13.19196Value1 is: "Blue"
[LOG 13:19:14.062] 13.19204Value2 is empty

So approach 1 works, approach 2 does not. This is satisfactory for me.

Next step: actually integrate it into beamed power. 

Thanks for the help @HebaruSan

 

Edit: 

I noticed that the GameDatabase.Instance.GetConfigNode asks for a url, not a node name... is the url something different from the "string right before the curly braces?"

Edit 2: Ok, on review of this thread by me: 

 

The node name is the correct input, so that isn't the issue.

Edited by KspNoobUsernameTaken
Link to comment
Share on other sites

15 hours ago, KspNoobUsernameTaken said:

I noticed that the GameDatabase.Instance.GetConfigNode asks for a url, not a node name... is the url something different from the "string right before the curly braces?"

Oh sorry, my advice was distorted by some messy API design choices:

sbaVKA7.png

When these functions ask for URLs, that's a reference to the file path on disk. When they ask for a typeName, then that's the "NODE" or "TestNode" string we've been discussing. I was referencing working code that calls GetConfigNodes and reasonably but incorrectly extrapolating that GetConfigNode would work similarly, given that the only difference between them is plural vs singular. I'd go with GetConfigNodes and then just add a thin layer of logic to reconcile multiple return values if there are multiple.

Edited by HebaruSan
Link to comment
Share on other sites

6 hours ago, HebaruSan said:

When these functions ask for URLs, that's a reference to the file path on disk.

That's quite interesting, and not what I expected. If I do use .instance.getconfignode, and input the file url, will any MM patches applied to it not function? I.E, will MM patches change the file url in the game database?

Link to comment
Share on other sites

2 hours ago, KspNoobUsernameTaken said:

That's quite interesting, and not what I expected. If I do use .instance.getconfignode, and input the file url, will any MM patches applied to it not function? I.E, will MM patches change the file url in the game database?

I believe you can still rely on having all the Module Manager patches applied.

Link to comment
Share on other sites

  • 2 weeks later...

Hi, I've got this code which is throwing NRE's, and I don't understand what's wrong with it:

void Awake()
{
    string ConfigFilePath = KSPUtil.ApplicationRootPath + "GameData/BeamedPowerStandalone/Settings.cfg";
    ConfigNode MainNode;
    MainNode = ConfigNode.Load(ConfigFilePath);
    ManagedResource = MainNode.GetNode("BPSettings").GetValue("ManagedResource");
    ResourceHash = PartResourceLibrary.Instance.GetDefinition(ManagedResource).id;
    Debug.Log(Time.realtimeSinceStartup + ManagedResource);
}

The settings.cfg file:

BPSettings
{
    ManagedResource = ElectricCharge
}
 

Link to comment
Share on other sites

1 hour ago, KspNoobUsernameTaken said:

I don't understand what's wrong with it:

A good way to start is to narrow down which part of which line is throwing.

Edited by HebaruSan
Link to comment
Share on other sites

2 hours ago, HebaruSan said:

A good way to start is to narrow down which part of which line is throwing.

I found this in the KSP log, which I'm guessing is a stack trace:

[ERR 11:28:12.512] PartLoader: Encountered exception during compilation. System.NullReferenceException: Object reference not set to an instance of an object
  at PartModule.Load (ConfigNode node) [0x0010d] in <4b449f2841f84227adfaad3149c8fdba>:0 
  at Part.AddModule (ConfigNode node, System.Boolean forceAwake) [0x0006f] in <4b449f2841f84227adfaad3149c8fdba>:0 
  at PartLoader.ParsePart (UrlDir+UrlConfig urlConfig, ConfigNode node) [0x00f10] in <4b449f2841f84227adfaad3149c8fdba>:0 
  at PartLoader+<CompileParts>d__56.MoveNext () [0x00685] in <4b449f2841f84227adfaad3149c8fdba>:0 
	UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
	ModuleManager.UnityLogHandle.InterceptLogHandler:LogFormat(LogType, Object, String, Object[])
	UnityEngine.Debug:LogError(Object)
	<CompileParts>d__56:MoveNext()
	UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
	<CompileAll>d__13:MoveNext()
	UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
	PartLoader:StartLoad()
	<LoadSystems>d__11:MoveNext()
	UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
[ERR 11:28:12.512] PartCompiler: Cannot compile part
	UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
	ModuleManager.UnityLogHandle.InterceptLogHandler:LogFormat(LogType, Object, String, Object[])
	UnityEngine.Debug:LogError(Object)
	<CompileParts>d__56:MoveNext()
	UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
	<CompileAll>d__13:MoveNext()
	UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
	PartLoader:StartLoad()
	<LoadSystems>d__11:MoveNext()
	UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)

But I can't figure out what any of this means. Seems to me like ConfigNode.load is having some kind of error. Maybe moving the code to Start() will help.

Link to comment
Share on other sites

9 hours ago, KspNoobUsernameTaken said:

But I can't figure out what any of this means. Seems to me like ConfigNode.load is having some kind of error. Maybe moving the code to Start() will help.

Hmm, are you trying to write a PartModule? Because the stack trace says there's some problem loading a part, and it doesn't mention ConfigNode.Load at all. This could be some completely unrelated problem from another mod.

9 hours ago, KspNoobUsernameTaken said:
  at PartModule.Load (ConfigNode node) [0x0010d] in <4b449f2841f84227adfaad3149c8fdba>:0 
  at Part.AddModule (ConfigNode node, System.Boolean forceAwake) [0x0006f] in <4b449f2841f84227adfaad3149c8fdba>:0 
Link to comment
Share on other sites

5 hours ago, HebaruSan said:

Hmm, are you trying to write a PartModule? Because the stack trace says there's some problem loading a part, and it doesn't mention ConfigNode.Load at all. This could be some completely unrelated problem from another mod.

That's correct, it's a part module. I have identical code in several other classes too. I thought PartModule.Load is the same as ConfigNode.Load so I thought that was the error.

Link to comment
Share on other sites

15 hours ago, KspNoobUsernameTaken said:

But I can't figure out what any of this means.

18 hours ago, HebaruSan said:

A good way to start is to narrow down which part of which line is throwing.

OK, so the way you do this is with basic logic. Comment out a block of lines, compile, run, see if it still crashes. If so, then the exception is thrown by the part you didn't comment out. Otherwise it's in the part you did comment out. Change what's commented out, try again, and so on until you figure it out.

Edited by HebaruSan
Link to comment
Share on other sites

On 7/30/2024 at 6:40 AM, HebaruSan said:

OK, so the way you do this is with basic logic. Comment out a block of lines, compile, run, see if it still crashes. If so, then the exception is thrown by the part you didn't comment out. Otherwise it's in the part you did comment out. Change what's commented out, try again, and so on until you figure it out.

Oh. That seems obvious in hindsight. The funny part is I actually did this for MM patches, just never thought to apply it for programming.

 

 

Edit:

To anyone in the future: I think my error was that a static method called from a different class seems to count as a class initialization, even if it was called from Start(). KSP won't allow usage of ConfigNode.load during initialization, causing errors. Any data loading code should be outside class initialization.

Edited by KspNoobUsernameTaken
Link to comment
Share on other sites

Hi everyone! I'm struggling to understand how the orbits and their coordinate systems are managed, and there are some behaviours that I don't understand at all...

The most basic weird situation I encountered is the following code:

CelestialBody body = // Get Mun's CelestialBody

double now = Planetarium.GetUniversalTime();
body.orbit.GetOrbitalStateVectorsAtUT(now, out var r, out var v);
Orbit orbitFromState = Orbit.OrbitFromStateVectors(r, v, body.orbit.referenceBody, now);

// Draw orbitFromState in planetarium view in red
// Draw body.orbit in planetarium view in green

The rendererd orbits in the tracking station:

AP1GczPR73tQKLCzMAdg3wrm7uxbc--VWoN7UyFS

AP1GczO-_9rcUkyiDcC7D2VG--Ij75xH0kI3SR0X

The green orbit is the Mun's one, drawn for reference to check that this is not an issue from my renderer. The red one is the one calculated by OrbitFromStateVectors. I would expect GetOrbitalStateVectorsAtUT and OrbitFromStateVectors to be reciprocals of each others, but it's apparently not the case, why? Not only the calculated orbit is completely flipped 90 degrees, it also has wrong radius/semi major axis and eccentricity.

I tried using getPositionAtUT and getRelativePositionAtUT as the position parameter for OrbitFromStateVectors:

  • getPositionAtUT gives a 90 degrees flipped orbit but with the correct semi major axis and eccentricity (at least visually)
  • getRelativePositionAtUT gives a 90 degree flipper orbit with wrong semi major axis and eccentricity, but a bit different from the one from GetOrbitalStateVectorsAtUT: the closest point to the Mun is closer for the orbit of getRelativePositionAtUT  than for GetOrbitalStateVectorsAtUT (still not interesecting the Mun).

I've read the available information on the Orbit class, from the official and community API documentations. From what I understood so far is that some method of the Orbit class return vectors where the y and z component are flipped (could explain the 90° flip), but not which ones explicitely nor why. I'm also aware different reference frames are used for different situation. Some function are "relative to the body" or "body ref centered" (according to the documentation), and others are in "world space".

Is there any comprehensive documentation on how are orbits handled in KSP? More specifically, I'm looking for the following:

  • What are KSP's different coordinate systems (naming conventions)? What is up (y? z?)? When are they used?
  • How do I retrieve the physical body-centered inertial position and velocity of a body or vessel relative to the its reference body (with units in meters and meters/second)?
  • What are the mEp and t parameters of the Orbit's constructor?
  • Given an orbit (Orbit class instance), initial position on this orbit and the UT time t0 of this position, what are the proper steps to propagate that position to get the state vectors at time t0 + some delta t?

 

I'd be very grateful of any help on this! Thank you very much in advance! :)

Edited by Krafpy
Clearer description
Link to comment
Share on other sites

  • 2 weeks later...

Hi, this might be answered somewhere already, but I could not find anything:
How do I Access VesselDeltaV & DeltaVApp in VAB? When my mod executes on Start(), they are not yet instantiated and querying them returns null.

I can access them by querying every frame until they are there, but that cant be a permanent solution.

Link to comment
Share on other sites

8 hours ago, HebaruSan said:

Try subscribing to GameEvents.onDeltaVCalcsCompleted.

Thank you, but this event only toggles after a craft has been created. Is there a way to get it when entering the editor?
By now I have also found basic dv's solution of polling it using a coroutine. But if there is an event that is triggered after all editor modules are loaded, that would be great.

Link to comment
Share on other sites

5 hours ago, Ubus99 said:

Thank you, but this event only toggles after a craft has been created. Is there a way to get it when entering the editor?

I think I'm missing something. You want the result of the calculation before the calculation is performed?

Link to comment
Share on other sites

47 minutes ago, HebaruSan said:

I think I'm missing something. You want the result of the calculation before the calculation is performed?

No. Right now I just want to understand the editor code, but eventually I want to modify the dv display & config panel itself.

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