Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

So I recently got into writing KSP plugins and don't have a whole lot of knowledge on the subject but have recently taken over development of a little plugin (that was still in alpha anyway) called Kerbal Construction Time. I want to be able to read and write a custom (well, eventually several) thing(s) to the persistence file instead of a separate file. I already have my code working using a separate file (thanks to Trigger Au's tutorials) but is there an easy way of reading/writing to the persistence.sfs file? The reason I want to do this is because otherwise dealing with time "going backwards" due to reverts and quicksaves will be a big pain later and if I can avoid it that would be awesome.

I won't get time to work on it until tomorrow night but figured I'd see if you guys have any thoughts.

Basically, I want to save a List of my object to the persistence.sfs and read it back in when a flight is loaded. Basically (for now at least) I need to save the time that the ship will finish being built along with some other data. I can already save a [Persistent] List<myObject> to a separate file and read it back in without issue.

Thanks :)

Link to comment
Share on other sites

So I recently got into writing KSP plugins and don't have a whole lot of knowledge on the subject but have recently taken over development of a little plugin (that was still in alpha anyway) called Kerbal Construction Time. I want to be able to read and write a custom (well, eventually several) thing(s) to the persistence file instead of a separate file. I already have my code working using a separate file (thanks to Trigger Au's tutorials) but is there an easy way of reading/writing to the persistence.sfs file? The reason I want to do this is because otherwise dealing with time "going backwards" due to reverts and quicksaves will be a big pain later and if I can avoid it that would be awesome.

I won't get time to work on it until tomorrow night but figured I'd see if you guys have any thoughts.

Basically, I want to save a List of my object to the persistence.sfs and read it back in when a flight is loaded. Basically (for now at least) I need to save the time that the ship will finish being built along with some other data. I can already save a [Persistent] List<myObject> to a separate file and read it back in without issue.

Thanks :)

I've been wondering the same thing. As far as I have been able to tell, it may involve using ScenarioModules.

Try looking at something like TAC life support, which generates its own section in save files.

Link to comment
Share on other sites

Good catch! If you look at TacLifeSupport.cs on their github you can see them using a ScenarioModule and then in the save it does this:

SCENARIO
{
name = TacLifeSupport
scene = 5, 7, 6, 9
SavedGameSettings
{
... Plus more stuff...

So I reckon by looking through the code for that I'll be able to figure something out. Thanks Headhunter09!

Edit: ninja'd kinda. Thanks xEvilReeperx, I'll check that out too! Like I said I don't have time to work on it now, but I should be able to get it working this weekend. Thanks again you guys :)

Edited by magico13
Link to comment
Share on other sites

The above was my intent with the ConfigNodeStorage class in the tutorials for sfs storage. If you have an object (lets call it fred) of type ConfigNodeStorage then once you hook the ScenarioModule you can use the fred.AsConfigNode() method to serialise your lists/settings, and then in xEvilReeperx's code you could do do the below:

public class InternalModuleSaver : ScenarioModule
{
public override void OnSave(ConfigNode node)
{
base.OnSave(node);

if (HighLogic.LoadedSceneIsFlight)
{
node.AddNode(fred.AsConfigNode());
}
}

Link to comment
Share on other sites

I have a part that I want to have play different animations based on its orientation. So one animation plays when the part is placed on a horizontal surface, like a rover body, and another plays when placed on a vertical surface, like the side of a fuel tank. I haven't seen this done anywhere, but I'm wondering if there is some standard or common way of determining a part's orientation.

So far I've come up with what seems to be a mostly working solution.


[KSPEvent(guiActive = true, guiName = "Deploy Drill", active = true)]
public void startDrill()
{
findrot();
if (rotz < 30 && rotz >= 0 || rotz > 330 && rotz <0 || rotz < 210 && rotz >= 180 || rotz > 150 && rotz < 180)
{
deployDrill(verticalDrillName);
}
else
{
deployDrill(animationName);
}
}


public void findrot()
{
rotx = this.part.transform.eulerAngles.x;
roty = this.part.transform.eulerAngles.y;
rotz = this.part.transform.eulerAngles.z;
}

Basically, I have a part with two animations on it and an animator method that can call either one of them. The findrot method determines the part's rotation (seemingly in reference to the planet's surface, not the vessel, or the terrain) in Euler angles. Then I check if the part is within a certain range.

The z axis (that would be the east/west axis on the planet's surface) seems to be telling me what I need to know. I've given the method some range, so that anything within 30o of vertical plays the vertical animation (vertical parts have a rotz of around 0 or 180o depending on which side of the z axis they're on, horizontal parts are around 90 or 270o).

This seems to work, with one obvious problem. Any part directly lined up on the z axis returns a value of 0o, so I lose the ability to determine orientation based on the z axis in that case. This is easy to do when you're testing it on the launchpad, but not so easy in real situations. So I'm thinking that I could leave it that way, or make a special case for rotz == 0, and do a second check using quaternions, but I'm not sure I'll be able to figure out such a simple method for that.

Does anyone foresee any other problems with this method? Or does anyone know of a better way to do this (or maybe how to check this using quaternions without giving myself an aneurysm)? I'm also not sure if something like an Infernal Robotics hinge would play nice with this or not.

Link to comment
Share on other sites

I have a part that I want to have play different animations based on its orientation. So one animation plays when the part is placed on a horizontal surface, like a rover body, and another plays when placed on a vertical surface, like the side of a fuel tank.

This will compare vertical directions of a part and its parent (partially tested):

double cosineAngle = Mathf.Rad2Deg * Math.Acos(Vector3d.Dot(part.transform.up, part.localRoot.transform.up));

if (cosineAngle > 180)
cosineAngle = 360 - cosineAngle;

if (cosineAngle > 90)
cosineAngle -= 180;

// angle is in range of [-90, 90]
if (Math.Abs(cosineAngle) < 30d)
// ... within 30 degrees of parent's up/down axis

It might not be exactly what you're looking for though. An octostrut placed on the side of a fuel tank would work like you'd expect. A fuel tank radially attached to another fuel tank, though, will still be considered "vertical" (assuming default orientation).

Link to comment
Share on other sites

Okay, rather then a code-not-working question, how about a procedural one?

I'm working with nearby vessels for my mod and I'm refreshing my list every update cycle. (Landing a skycrane requires the fastest update cycle I can manage.)

I'm currently using the following, which does work:

foreach (Vessel ves in FlightGlobals.Vessels) 

if (ves.loaded == true && !ves.isActiveVessel) //only parse loaded vessels that are not the current vessel
{
<Run My Code>
}
}

But this parses every vessel in the game. If I'm trying to land a science probe on Eeloo, I don't need to be looking at all the stuff I've got back over near Kerbin.

I don't think the performance hit for this is going to be noticeable, but I can just see a game someone's been running for months with a hundred-plus ships in it and I am touching each ship once per update cycle which is non-trival.

Does anyone have a better way of doing this, or know of a call I could make to KSP that only returns loaded vessels?

Again, what I have works, I'm just 99% sure it could work better.

D.

Link to comment
Share on other sites

I don't think the performance hit for this is going to be noticeable, but I can just see a game someone's been running for months with a hundred-plus ships in it and I am touching each ship once per update cycle which is non-trival.

Does anyone have a better way of doing this, or know of a call I could make to KSP that only returns loaded vessels?

Again, what I have works, I'm just 99% sure it could work better.

D.

Well, it's technically (but unlikely) for an unloaded vessel to become loaded while your skycrane is flying under exactly the right circumstances. How about instead of iterating through all vessels everywhere, create a cache of vessels with the same mainBody as your vessel. That could cut down your list considerably while not creating (very unlikely) edge cases where you could miss one.

Link to comment
Share on other sites

Na, your algorithm should scale just fine and have a negligible performance impact as long as you run the quoted code just once per frame. You might want to worry about saves with lots of debris (amounts with 4-5 digit), but that should be fairly rare. The performance issues i found in mods (mj2 & rt2) where when such a code was in a function that itself was called thousands of times per frame.

Is there sth available to run some measurements on?

Link to comment
Share on other sites

Yes, that code is once per Update(), so I believe that translates to once per frame.

I'd have to make a save with that many vessels in it first to actually test but I don't think this is a real concern, it was more me going "this feels kludgy, I can do better".

I stuck a time stamp on my code, it looks like this runs every 12-15 milliseconds, which is 7-8 times a second, so that looks okay to me.

D.

edit: Oops, did not refresh page to see previous post.

Edited by Diazo
Link to comment
Share on other sites

Bah.

Ya, 83 times a second makes more sense.

Still, infrequently enough I'm not going to worry about it any farther I don't think. I suppose I really was hoping there was a .loadedvessels call I could make to KSP but I'll go with what I have.

Onto fighting with the List<T> object some more.

D.

Link to comment
Share on other sites

Hi, I'd like some help to programmatically create a vessel with certain parts and place it at a location. Here's what I've got so far:


//Using KSP class ShipConstruct, instantiate a new vessel
ShipConstruct newShip = new ShipConstruct();

//Define the part to be added to the vessel. This line doesn't work. I don't know how to look up and reference Parts.
Part cockpit = new BaseEventData.Get<Part> ("cupola");

//Add the above referenced part to newShip. I'm pretty sure this line will work.
newShip.Add(cockpit);

//Using KSP class ShipConstruction, place the vessel at the location of the part calling the instantiate method
ShipConstruction.PutShipToGround(newShihp, this.transform);

Thanks for the help!

Link to comment
Share on other sites

So the whole saving to the persistence is going great for me, so thank you all for the help with that! Does anyone know if there's an easy way of reverting a flight back to the editor the same way the game does in the pause menu? I'm trying to setup the ability to do "simulations" which will revert back to the editor when the vessel is destroyed or the apoapsis > 250km or the periapsis > 70km. I can do the conditions no problem, just need a way to revert (and not have the time spent "simulating" be counted for anything). Thanks again!

Link to comment
Share on other sites

@magico

A quick search in visual studios object browser found out that FlightDriver has two static methods that might be interesting for you:

static void FlightDriver.RevertToLaunch()

static void FlightDriver.RevertToPrelaunch(GameScenes sceneToLoad)

ps: Keep in mind that this should reload the game and thus will kill most MonoBehavoir-instances. KSP should ofc create new ones as configured once the new scene is loaded.

Edited by Faark
Link to comment
Share on other sites

In my code I'm changing the maximum_drag of all the Parts on my Vessel to something low.

I would like to save the initial maximum_drag so I can later reset the maximum_drag to it's original state.

I could make a Array in my Partmodule to save the value for every Part but that seems a bad solution.

Rather I would extend on the Part Class, so I can assign a new float to every Part with the name initial_max_drag.

How do I do that? I have not enough knowledge of the inheritance system of C#.

Link to comment
Share on other sites

In my code I'm changing the maximum_drag of all the Parts on my Vessel to something low.

I would like to save the initial maximum_drag so I can later reset the maximum_drag to it's original state.

I could make a Array in my Partmodule to save the value for every Part but that seems a bad solution.

Rather I would extend on the Part Class, so I can assign a new float to every Part with the name initial_max_drag.

How do I do that? I have not enough knowledge of the inheritance system of C#.

Why would you need to store it in an array? There is a PartModule instance for each occurence of that module on the ship. Make so that the PartModule saves the initial value of the part it's instance is located on and you're set.

Link to comment
Share on other sites

I would need an Array because I want this for every part on the vessel the module is attached to. So also all the stock parts / other mod parts attached to the vessel with my module.

To clearify I want to accelerate with great speed and the problem is that because every Part has his own drag it tears itself apart. So I set the max_drag of every part on the vessel to low (all the same) and that works. But somehow after the acceleration is done I want to put back the initial maximum_drag of every Part so the vessel will behave like a normal vessel again.

Link to comment
Share on other sites

Having an initial max drag is more elegant, but getting it all is pretty easy too. Something like this would do it in one line:

private Dictionary<Part, float> drag_coefs = new Dictionary<Part, float>();
drag_coefs = this.vessel.parts.ToDictionary(p => p, p => p.maximum_drag);

Then if you want to change back all the part's drag coef to the old value, you could do it this way:

this.vessel.parts.ForEach(p => drag_coefs.TryGetValue(p, out p.maximum_drag));

Using a dictionary will simplify your job by a lot.

This is assuming you don't mind using System.Collections.Generic and System.Linq.

Link to comment
Share on other sites

Thank you, thats one solution I can wrap my brain around. :)

I see the dictionary is really handy (did not know of it's existence) and a lot easier to use then 2 arrays next to each other.

But I guess I did not understand your first answer. Could you elaborate a bit? "Make so that the PartModule saves the initial value of the part it's instance is located on and you're set."

I have the feeling that you are trying to tell me that I can do exactly the same as I'm trying to do now with a Dictionary.

Link to comment
Share on other sites

Thank you, thats one solution I can wrap my brain around. :)

I see the dictionary is really handy (did not know of it's existence) and a lot easier to use then 2 arrays next to each other.

But I guess I did not understand your first answer. Could you elaborate a bit? "Make so that the PartModule saves the initial value of the part it's instance is located on and you're set."

I have the feeling that you are trying to tell me that I can do exactly the same as I'm trying to do now with a Dictionary.

I was thinking you would have a partmodule on each part you wished to save the original drag coefficient. It would've been simpler to do it individually at that point.

Also, tip on modifying drag values of parts: do it gradually, using a Mathf.Lerp() for example. Switching rapidly, even from small values, can give some pretty strong shocks.

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