Jump to content

Node Manager [0.7.3 for KSP2 0.2.1]


schlosrat

Recommended Posts

Node Manager

Provides services for other mods needing to create, delete, and manage maneuver nodes.

Compatibility

  • Tested with Kerbal Space Program 2 v0.2.1 & SpaceWarp 1.9.1
  • Requires SpaceWarp 1.8.0+
  • Node Manager doesn't require other mods, other mods require it! This mod provides a handy library of functions other mods can call.

License: MIT

Source Code

Latest Release: https://github.com/schlosrat/NodeManager/releases

This mod is primarily meant as a service provider to other mods, which can call functions in this one without needing to recreate all these functions in the base mod. If you're not a mod developer, then that is about all you would need to know. Just have this mod installed and other mods will be able to call it if they're built to use its functions. It's pretty much transparent for you. If you're a mod developer and want to use this mod from your mod then keep reading!

Node Manager Capabilities

  • AddNode(burnUT): Calling this method will create an empty (0 Delta-v) node at the (optionally) specified (double) burnUT. If burnUT is not specified, then the new node will be at the next Ap (if there are currently no nodes and the eccentricity is < 1), or if there are nodes, then it will be at Min(Orbit.period / 10, 600) seconds after the last node, or if there are no nodes and e >= 1, then it will be 30s after the current time.

  • CreateManeuverNodeAtTA(burnVector, TrueAnomalyRad, burnDurationOffsetFactor): This method will create a new node using the supplied (Vector3d) burnVector at the next time the active vessel will be at the specified (double) true anomaly (in radians), applying the optional (double) burnDurationOffsetFactor. If the burnDurationOffsetFactor is not specified, a default value of -0.5 will be used resulting in the burn time starting before the specified true anomaly occurs by 1/2 of the node's burn duration (as estimated by the game for the active vessel).

  • CreateManeuverNodeAtUT(burnVector, burnUT, burnDurationOffsetFactor): This method will create a new node using the supplied (Vector3d) burnVector at the specified (double) time for burn, applying the optional (double) burnDurationOffsetFactor. If the burnDurationOffsetFactor is not specified, a default value of -0.5 will be used resulting in the burn time starting before the specified time by 1/2 of the node's burn duration (as estimated by the game for the active vessel).

  • DeleteNodes(SelectedNodeIndex): This method takes an integer node index and will delete the node at that index along will all following nodes. If you pass it 0, then all nodes will be deleted.

  • RefreshActiveVesselAndCurrentManeuver(): Calling this method will force Node Manager to refresh its local copy of the active vessel and current node. This is done automatically by Node Manager as needed, but if you want to force it to update you can.

  • RefreshManeuverNodes(): Calling this method will trigger a call to RefreshActiveVesselAndCurrentManeuver and then rebuild Node Manager's internal list of nodes for the active vessel. This is done automatically by Node Manager as needed, but if you want to force it to update you can.

  • RefreshNodes(): Calling this method will call maneuverPlanComponent.UpdateNodeDetails(node) and maneuverPlanComponent.RefreshManeuverNodeState(i) for each node the active vessel has. This can be useful if your mod is adjusting a node's BurnVector or Time and you want to make sure that node and all following nodes are properly updated for these effects.

  • SpitNode(SelectedNodeIndex, isError): This method takes an integer node index and, (optionally) a Boolean value, and will output info about the indexed node to the BepInex log. The default for isError is false. If isError is set to true, the log will go out at the Error level.

  • SpitNode(node, isError): This method takes a node (type ManeuverNodeData) and, (optionally) a Boolean value, and will output info about the indexed node to the BepInex log. The default for isError is false. If isError is set to true, the log will go out at the Error level.

  • activeVessel: Access via `NodeManagerPlugin.Instance.activeVessel`. You can access this to check Node Manager's understanding of the active vessel.

  • currentNode: Access via `NodeManagerPlugin.Instance.currentNode`. You can access this to check Node Manager's understanding of the current node.

  • Nodes: Access via `NodeManagerPlugin.Instance.Nodes`. You can use this to check the number of nodes as `NodeManagerPlugin.Instance.Nodes.Count`

NOTE: Burn Vectors are Not Delta-V!

Delta-V can be computed as the difference between two orbital velocities (the one you want minus the one you've got). In KSP this is generally done in the orbit velocity frame. That last part is very important because, while Delta-V and BurnVector do have the same magnitude and are certainly related, they are not generally in the same direction. KSP needs a node's BurnVector to be expressed as a Vector3d composed of the Radial (x), Normal (y), and Prograde (z) components needed for the burn. If you have a Vector3d of the Delta-V you want, you can convert that to a BurnVector using unit vectors for the RadialPlus, NormalPlus, and Prograde vectors at the time for your burn.

    public Vector3d DeltaVToManeuverNodeCoordinates(PatchedConicsOrbit o, double UT, Vector3d dV)
    {
        return new Vector3d(Vector3d.Dot(o.RadialPlus(UT), dV),
                            Vector3d.Dot(o.NormalPlus(UT), dV),
                            Vector3d.Dot(o.Prograde(UT), dV));
    }

To use this mod from your mod you will need to do one of the following:

Hard Dependency

This is recommended if core capabilities in your mod will rely on functions in this one, and it's actually the easier one to set up.

  • The advantage of this way is coding will be easier for you! Just call NodeManagerPlugin.Instance.method_name() for any public method in Node Manager!
  • The disadvantage to this way is you've got a hard dependency and your mod will not even start up unless Node Manager is installed. You may want to ship a copy of Node Manager with your mod (put both the node_manager plugin folder and your mod's plugin folder into the BepInEx/plugins folder before zipping it up). There may be a way to do this with CKAN in some automated fashion. This guide will be updated with those details, or a link to them, at some point.

Step 1: Configure Assemblies

Add the node_manager.dll to your mod's list of Assemblies. Generally, this means two things. First, put the node_manager.dll in a folder where your mod can find it. You may want to put it in the same folder you have Assembly-CSharp.dll. Secondly, add it to your csproj file similarly to how you're already referencing Assembly-CSharp.dll. Your mod will need to have access to it and know where to find it when you compile your mod. At run time your mod will be accessing the node_manager.dll from the node_manager plugins folder where Node Manager is installed, so you don't need to distribute the Node Manager DLL with your mod, but it will need to be installed in the players game for you to be able to access it.

Step 2: Configure Namespace and Add Dependency

Bring in the NodeManger namespace in the class you want to call it from and add Node Manager as a BepInDependency.

using NodeManager;

namespace MyCoolModsNamespace;

[BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
[BepInDependency(SpaceWarpPlugin.ModGuid, SpaceWarpPlugin.ModVer)]
[BepInDependency(NodeManagerPlugin.ModGuid, NodeManagerPlugin.ModVer)]
public class MyCoolMod : BaseSpaceWarpPlugin

 

Step 3: Call Node Manager Methods from your Mod

You can now call any of Node Manager's public methods directly and easily from your code. Here are some examples:

NodeManagerPlugin.Instance.AddNode(burnUT) // double
NodeManagerPlugin.Instance.CreateManeuverNodeAtTA(burnVector, TrueAnomalyRad, burnDurationOffsetFactor); // Vector3d, double, double (default = -0.5)
NodeManagerPlugin.Instance.CreateManeuverNodeAtUT(burnVector, burnUT, burnDurationOffsetFactor); // Vector3d, double, double (default = -0.5)
NodeManagerPlugin.Instance.DeleteNodes(SelectedNodeIndex); // int
NodeManagerPlugin.Instance.RefreshActiveVesselAndCurrentManeuver();
NodeManagerPlugin.Instance.RefreshManeuverNodes();
NodeManagerPlugin.Instance.RefreshNodes();
NodeManagerPlugin.Instance.SpitNode(SelectedNodeIndex, isError); // int, bool
NodeManagerPlugin.Instance.SpitNode(node, isError); // ManeuverNodeData, bool

Step 4: Profit!

Soft Dependency

This is the way to go if optional capabilities in your mod will rely on functions in this one, and you don't want the user to have a hard dependency on Node Manager.

  • The advantage to this way is that your mod's users don't need to have Node Manager installed if they prefer not to have it, and you can distribute your mod without needing a hard dependency on Node Manager - meaning your mod can launch and run without Node Manager, although there may be some capabilities that aren't available to your users if they choose to pass on installing Node Manager.
  • The disadvantage to this way is you'll need to do a bit more coding in your mod to be able to call Node Manager's methods from your mod.

Step 1: Configure Assemblies

This is the same as for Hard Dependency above as you'll not be able to compile without this.

Step 2: Configure Namespace and Variables

Bring in the NodeManger namespace and create some variables in the class you want to call it from. This is almost the same as above.

   using NodeManager;
   
   private bool NMLoaded;
   PluginInfo NM;

Step 3: Check for Node Manager

Somewhere in your mod, you need to check to make sure Node Manager is loaded before you use it (e.g., OnInitialized()). You don't need this with a hard dependency, but it's essential for a soft one.

    if (Chainloader.PluginInfos.TryGetValue(NodeManagerPlugin.ModGuid, out NM))
    {
        NMLoaded = true;
        Logger.LogInfo("Node Manager installed and available");
        Logger.LogInfo($"MNC = {NM}");
    }
    else NMLoaded = false;

Step 4: Create Reflection Method

This is where things get really different for you compared to what's needed to call Node Manager methods using a hard dependency. For a soft dependency to work you're going to need to create a reflection calling method for each of the Node Manager methods that you would like to call from your mod. Here's an example of one for calling Node Manager's CreateManeuverNodeAtUT method which will pass it the burn vector you want, the time to schedule the burn, and optionally a time offset. Using a time offset of -0.5 will cause the maneuver node to be centered on the time you supply rather than starting on the time.

    private void CreateNodeAtUt(Vector3d burnVector, double UT, double burnDurationOffsetFactor = -0.5)
    {
        if (NMLoaded)
        {
            // Reflections method to call Node Manager methods from your mod
            var nmType = Type.GetType($"NodeManager.NodeManagerPlugin, {NodeManagerPlugin.ModGuid}");
            Logger.LogDebug($"Type name: {nmType!.Name}");
            var instanceProperty = nmType!.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static);
            Logger.LogDebug($"Property name: {instanceProperty!.Name}");
            var methodInfo = instanceProperty!.PropertyType.GetMethod("CreateManeuverNodeAtUT");
            Logger.LogDebug($"Method name: {methodInfo!.Name}");
            methodInfo!.Invoke(instanceProperty.GetValue(null), new object[] { burnVector, UT, burnDurationOffsetFactor });
        }
    }

This example includes some (optional) debug logging that may be helpful if you are having trouble with the reflection calling method. You can safely remove those once it's working to your satisfaction.

Step 5: Call Reflection Method

Call your reflection method wherever you need to invoke the corresponding Node Manager method.

CreateNodeAtUt(burnVector, burnUT, -0.5);

Step 6: Profit!

Edited by schlosrat
Updated for v 0.7.3
Link to comment
Share on other sites

Updated to v 0.5.2

  • Fixed an issue where more than 6 nodes could not be created for a given vessel. Node Manager now gracefully allows you to create up to 9, which is the apparent (undocumented) limit for the game. Even creating nodes manually using the game's UI, you can't create more than 9 before you begin getting cascading errors in the KSP.log.
  • CreateManeuverNodeAtTA, CreateManeuverNodeAtUT, and AddNode all now return a Boolean value. True indicates the node was created successfully, and False indicates otherwise. Any time a false is returned there will be a Warning level log message in the BepInEx log (not the KSP.log) to alert as to where and why the failure occurred. Generally, not having an active vessel to attach the node to, or having too many nodes already will cause this.
Edited by schlosrat
Link to comment
Share on other sites

Updated to v 0.5.3

  • Updated package for referencing as a Nuget package
  • Updated SpitNode methods so they both take an optional isError Boolean
  • Updated AddNode to take an optional burn time to use as an override of its internal logic. AddNode no longer requires an orbit to be passed in as the active vessel's orbit is internally available to it.
Link to comment
Share on other sites

  • 1 month later...

Updated to v 0.5.4 

  • Added DeleteNode method to delete a single node if it's not on a maneuver trajectory and it's in the past
  • Added DeletePastNodes method to delete all past nodes that are not on a maneuver trajectory

Updated to v 0.5.5

  • Updated to bring in MechJeb library functions needed by ManeuverNodeController 0.9.4 and FlightPlan 0.8.9. Node Manager now provides the following things
  • OrbitExtensions
  • MathExtensions
  • MuUtils
  • MechJebLib\Primatives
  • MechJebLib\Utils
Link to comment
Share on other sites

  • 4 weeks later...
  • 2 weeks later...

Updated  to v 0.5.7

  • Updated node creation logic to allow more time for the game to "catch up" before placing the maneuver node gizmo. This has the effect of making mode creation more reliable in more situations.
  • Update logging to prevent error reports from being generated when none are needed.
Link to comment
Share on other sites

  • 3 weeks later...

Updated to v 0.5.8

  • Updated node creation logic to align better with how the game creates nodes a player makes manually in the map view. This streamlines the process, better integrates with the game's internal code, doesn't require the use of coroutines, and results in other mods getting the correct patch info for the maneuver resulting from the node.
Link to comment
Share on other sites

Updated to v 0.5.9

  • Updated CreateManeuverNodeAtUT to detect and prevent nodes from being created on top of other nodes. The method now enforces a minimum time gap of 1 second between nodes. This prevents a cascading error in the game's code which can occur if nodes are placed on top of each other. A warning message is displayed in the log when this occurs.
Link to comment
Share on other sites

  • 2 weeks later...

Updated to v 0.6.0

  • Incorporated fix for Out of Order Node Creation. With this fix, if you use Node Manager to create a node that is before any other nodes (and there are other nodes), then the new node will be the first node and the subsequent nodes will correctly be on the maneuver trajectory of the new node. No more leaving nodes behind just because you created them out of order!
Link to comment
Share on other sites

  • 5 weeks later...

Updated to v 0.6.1

  • Updated for KSP2 0.1.4 compatibility. Previous versions of Node Manager will not work correctly with KSP2 0.1.4+. You must update to this version (or later) of Node Manager if you want to use it with KSP2 0.1.4+
  • Updated dependency on Space Warp to version 1.4.2 (same reason)
Link to comment
Share on other sites

  • 3 months later...
  • 1 month later...

Love this mod.

Semi-bug/Feature request 1: Deleting a node in a sequence deletes all subsequent nodes. I would wish it was possible to delete a node without deleting all subsequent nodes.

Feature request 2: Adding a node currently adds a node last in the sequence. I would wish it would be possible to add a node in the beginning or middle of a sequence.

Link to comment
Share on other sites

9 hours ago, Kayser said:

Love this mod.

Semi-bug/Feature request 1: Deleting a node in a sequence deletes all subsequent nodes. I would wish it was possible to delete a node without deleting all subsequent nodes.

Feature request 2: Adding a node currently adds a node last in the sequence. I would wish it would be possible to add a node in the beginning or middle of a sequence.

1: Nodes exist on orbital patches and define the transition from one patch to the next. When you delete a node in a sequence (say the 3rd of 5 for example) you've removed the patches that the future nodes are associated with. In the stock game if you've got 5 nodes and you delete the 3rd one, then you lose nodes 3, 4, and 5. The same happens in NM since it's just a way to enable mods to create and destroy (manage) nodes in the base game.

2: This may be a possible feature, but it will be tricky. In the stock game, you can create a new (empty) node between two nodes. This is possible since the new node has no maneuver component initially, so any nodes following it are already in the right place and they just need to be re-mapped to the new patches that exist because of the new node. Presently, using Maneuver Node Controller to add a node to an existing set of nodes will always result in the new node appearing after what was previously the last node in the set - which is the safe thing to do, but if you're adding an empty node then you should be able to insert it just like the stock game allows. That said, we can look at what happens with Flight Plan if you try to do this where you're also going to define a burn on that node.

Consider this (wacky) scenario. Here the starting situation is that there are four nodes doing various minor things on the orbit where the first one happens to be at the Ap for the current orbit (default initial time from MNC). There are two more nodes not long after that, then one that's on the other side of the orbit.

https://i.imgur.com/O9SXu3k.mp4

Using FP, we ask it to circularize at the next Pe. This will call NM to create a node at the time for the next Pe on the current orbit. As you can see, this causes a little bit of chaos and results in the last node seeming to be hanging in space. It's not really like that, but that's because the game hasn't updated the last node for the new patch it's on. One way to get it to update all the nodes is to make a small change to one of the earlier ones. When we do this with MNC, then we can see that the nodes are where we asked for them.

K0dIsq5.png

So, as you can see, adding nodes between other nodes is possible in NM. In the example above FP called NM to do precisely that. NM will happily create nodes wherever you ask it to - or more accurately whenever since nodes are associated with a time. The issue is that when a node is inserted into a chain of nodes we need to re-update things. 

I've played around with automating this in the past, but there's a weird lag and I've never been successful in automating the update. We can trigger an update manually with MNC, and then back out the unneeded change very easily, but automating the update runs into issues with how the game handles information. The automation tends to arrive before the game is ready for it, but if you do it manually you'll be fine.

Link to comment
Share on other sites

Thanks for the quick and verbose response. I think I got the essence of what you are saying, and I think I do understand the complications, even though I'm not into the coding of all this. 

The reason for asking is that I commonly come across a situation in the game where, in a series of maneuvers, the initial maneuver is not executed to perfection. This means that, the actual trajectory of the ship after the first maneuver node is not exactly the one planned, but slightly different. What I want at that point is to update the subsequent nodes to take into account the new actual flight path, and not the one that was originally planned (i.e. to simulate what would be the result if the subsequent nodes are still executed at the same time as originally planned, and maybe to be able to adjust them.)

I wanted to achieve this by deleting the first node in the sequence, but since that deletes the entire sequence, it's not possible. Neither is it possible to insert a node at the beginning to play around with a correction burn and get the results to show. Only way I see is to re-make all the subsequent nodes. 

Maybe there's a better way to do that?

Link to comment
Share on other sites

4 hours ago, Kayser said:

Thanks for the quick and verbose response. I think I got the essence of what you are saying, and I think I do understand the complications, even though I'm not into the coding of all this. 

The reason for asking is that I commonly come across a situation in the game where, in a series of maneuvers, the initial maneuver is not executed to perfection. This means that, the actual trajectory of the ship after the first maneuver node is not exactly the one planned, but slightly different. What I want at that point is to update the subsequent nodes to take into account the new actual flight path, and not the one that was originally planned (i.e. to simulate what would be the result if the subsequent nodes are still executed at the same time as originally planned, and maybe to be able to adjust them.)

I wanted to achieve this by deleting the first node in the sequence, but since that deletes the entire sequence, it's not possible. Neither is it possible to insert a node at the beginning to play around with a correction burn and get the results to show. Only way I see is to re-make all the subsequent nodes. 

Maybe there's a better way to do that?

Ahh, I see the challenge you've got - or I think I do. Let's see if I do.

Suppose you've got a chain of 3 planned maneuvers stacked up, and you attempt to execute node 1 but it burns a bit short or too long or something. The future nodes are still planned, but the burns they're planning are based on an assumption that you execute node 1 correctly. Since it came up short or long or just a bit wrong in some way, then the planned node 2 won't have the initial conditions you had intended and so it's going to apply the wrong delta-v for the outcome you desire if you carry on and execute it perfectly without first modifying it.

In this case, there are two things you might do, and in both cases, I would recommend using Maneuver Node Controller.

First, you could do as you've suggested and add another node between the mis-executed node 1 and the future node 2 to try to get back on track. MNC doesn't want to do this natively, but it can help you nevertheless. You can manually drop a node on your trajectory approximately where you would like it, then use MNC to fine-tune that node to get things back on track. In many cases, this might be the easiest and best way to do it - much like a real mission planning a mid-course correction. 

Second, you may be able to get what you need in a situation like this by editing node 2 to compensate for the error from node 1's execution. Again, MNC can help.

What you can't presently do (no mod I'm aware of will do this) is to automatically make the updates you need. That would be some tricky orbital mechanics. You could sort of do it with Flight Plan, but that would involve deleting the future nodes and letting Flight Plan make brand-new ones for you that accomplish what you want. Not really editing the future nodes so much as scrapping and re-making them.

Did I understand the problem? If so, then I hope this may help you get where you're going, and if not then please set me straight and we'll give it another go.

Edited by schlosrat
Link to comment
Share on other sites

You understood it correctly.

I'm not looking for an automatic correction in terms of the results, just a correction in terms of the initial conditions. In fact, this is done natively by the game whenever you have your first maneuver node in front of you, your actual trajectory changes for some reason (e.g. you make a burn, release a stage, etc.). Then your first node will fall out of your path and be in "the air". However, if you make a slight change to that node, it will correct itself and fall back onto your actual flight path (including all the subsequent maneuvers that follow). It just doesn't work when your first node is in the past.

The situation is quite common for me, when I e.g. have planned out a Hoffmann transfer with a mid-plane change. The initial burn is often a fraction of a second or a few delta-V off target, and this can mean the difference between a SOI intercept or not upon arrival. Consequently, I want to perform a correctional burn or adjust one of the later nodes, I just can't do it and see the results without re-planning all of the nodes, because I can't insert the correctional burn into the sequence or delete the initial burn without deleting all the subsequent nodes.

Link to comment
Share on other sites

4 hours ago, Kayser said:

You understood it correctly.

I'm not looking for an automatic correction in terms of the results, just a correction in terms of the initial conditions. In fact, this is done natively by the game whenever you have your first maneuver node in front of you, your actual trajectory changes for some reason (e.g. you make a burn, release a stage, etc.). Then your first node will fall out of your path and be in "the air". However, if you make a slight change to that node, it will correct itself and fall back onto your actual flight path (including all the subsequent maneuvers that follow). It just doesn't work when your first node is in the past.

The situation is quite common for me, when I e.g. have planned out a Hoffmann transfer with a mid-plane change. The initial burn is often a fraction of a second or a few delta-V off target, and this can mean the difference between a SOI intercept or not upon arrival. Consequently, I want to perform a correctional burn or adjust one of the later nodes, I just can't do it and see the results without re-planning all of the nodes, because I can't insert the correctional burn into the sequence or delete the initial burn without deleting all the subsequent nodes.

Ah ha! I may have a solution for you.

Create a dummy node in the future between your vessel and the first node hanging in the air. Use MNC to make a small adjustment to that node - like +5 m/s prograde or whatever. The process that MNC uses to make this adjustment should cause all the other nodes to jump back onto their trajectories. Now just back that change out, and then you can safely delete the dummy node. Or, you might use the dummy node as a course correction node to trim out any error from the burn of node 1 (in the past).

Essentially, you'll be causing the game to do what it needs to do to update the future nodes for you.

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