Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

@Ippo

Nah, that would be total mess. I will go with module manager approach, and write simple module to communicate with my part.

@Diazo

To be honest this dazzled me so much to force me to write here. Uid in persistent.sfs file differs from uid in game, and, like ConstructID, changes after every ship load. It makes no sense.

Link to comment
Share on other sites

Okay, uid is generated based on a ship somehow, it's not random.

Start a fresh save, lauch a Kerbal 2, it has the same uid as my previous test did with the kerbal 2's in my old save. The uid that the first Kerbal 2 has on launch that swapped to the second Kerbal 2 when I launched it has now appeared a 3rd time on the 3rd Kerbal 2 (first Kerbal 2 in my new save).

So really, what good is the uid?

D.

Link to comment
Share on other sites

I was not able to find anything when I looked for it while working on my Velocity mod, although I was more looking for lift then drag at the time.

Later I noticed something about drag in one of the Flight classes (FlightIntegrator I think?) but I was working on something else at the time and did not investigate.

D.

Link to comment
Share on other sites

I'm having trouble rendering GUIText objects correctly. In brief: i believe it is rendered by the wrong camera and therefore appears in the wrong place on screen.

So i have code like this


GameObject CreateGUIText(string name, string text, TextAnchor anchor, Color color, float x, float y, Transform parent)
{
GameObject o = new GameObject("KFI-"+name);
o.layer = 12; // navball layer
o.transform.parent = parent;
o.transform.localPosition = new Vector3(x, y, 0);
GUIText t = o.AddComponent<GUIText>();
t.text = text;
t.alignment = TextAlignment.Left;
t.anchor = anchor;
t.material.color = color;
}

in OnGui


if (text1 == null)
text1 = CreateGUIText("test1", "Hello", TextAnchor.LowerLeft, Color.yellow, 0f, 0.2f, guiInfo.navball.transform);

where guiInfo.navball points to the navball game object.

text1.transform.position is reported as the coordinates i expect, matching those of other gui stuff like the speed indicator text. So it should be aligned with the center of the nav ball. But the text actually appears in the far right of the screen.

The "layer" variable is set the value which the other gui stuff has, too. To my understanding this should take care of selecting the proper camera. But it doesn't apparently.

Any help is appreciated. I have been banging my head against gui issues for way too long now ...

Link to comment
Share on other sites

I'm having trouble rendering GUIText objects correctly. In brief: i believe it is rendered by the wrong camera and therefore appears in the wrong place on screen.

Right camera, wrong position. The problem is that the NavBall's position is defined in world space, whereas GUIText's position is expected to be in normalized screen space. You need to convert it:

Camera cam = Camera.allCameras.FirstOrDefault(c => (c.cullingMask & (1 << 12)) != 0);

if (cam != null)
t.transform.position = cam.WorldToViewportPoint(/* navball position here */);

Link to comment
Share on other sites

Okay, I'm trying to wrap my head around how KSP's saving system works.

Is there anyway I can tell from the code what kind of save has been called when it is not my mod making the save request?

The best I currently have come up with is to watch the quick-save key and if that is pressed it is an auto-save, otherwise it is a regular save. However, I know there are several mods out there for manipulating auto-saves and if a player has one of those installed I'll be making a regular save when they auto-save via the mod and that will be a critical failure for my mod.

The other fallback I have is to simply save in a cascading file list so every time a save happens I make a new file. Then when a load is called, I use the time-stamp to determine which file I load. The problem with this is that if I save 10 files, if a person goes long enough without loading there auto-save I will lose the auto-save file and can't load it.

Anyone have any thoughts or know what code I need to be looking at to do this?

D.

Link to comment
Share on other sites

Right camera, wrong position. The problem is that the NavBall's position is defined in world space, whereas GUIText's position is expected to be in normalized screen space. You need to convert it:

Thanks! This makes total sense. I've seen the coordinates go from 0 to 2 and just dismissed it as some oddity. Now i know where that came from.

Sorry if i take the whole hand instead instead of the pinkie ... do you happen to know how to use ScreenSafeGUIText? I tried


ScreenSafeGUIText t2 = o.AddComponent<ScreenSafeGUIText>();
t2.text = text;
t2.textSize = 12;
t2.textStyle = GUI.skin.label;

instead of using GUIText. Here i don't get anything drawn. Also i guess ScreenSafeGUIText is meant to be in world space then, right?

Edit: Got it! With


t2.textStyle = new GUIStyle();

i can see the text. And as suspected it lives in world coordinates.

Edited by DaMichel
Link to comment
Share on other sites

Hey guys, quick question, and I know this has been asked in the past, Im just not that good at programming. :P

I am trying to write a quick function in a PartModule class to call on my IConfigNode class to save a value into the craft file, and also to load that value from the file at the start of the flight. Perhaps I am misunderstanding something, here is my IConfigNode Code:

	

[Serializable]
public class ExtendedTrimConfigNode : IConfigNode
{

private bool bDebug = true;

[SerializeField]
public float config_fPT;
[SerializeField]
public float config_fYT;
[SerializeField]
public float config_fRT;

public string CLASS_NAME = "ExtendedTrimConfigNode";
public string SAVED_PITCH_TRIM = "Saved_fPT";
public string SAVED_YAW_TRIM = "Saved_fYT";
public string SAVED_ROLL_TRIM = "Saved_fRT";
public string CURRENT_PITCH_TRIM = "Current_fPt";
public string CURRENT_YAW_TRIM = "Current_fYt";
public string CURRENT_ROLL_TRIM = "Current_fRt";

public void Load(ConfigNode node)
{
float f;
int iFound = 0;
if (node.HasValue (SAVED_PITCH_TRIM) && float.TryParse (node.GetValue (SAVED_PITCH_TRIM), out f))
{
config_fPT = f;
iFound = iFound + 1;
if (bDebug) {Debug.Log (CLASS_NAME + "_Load(): Pitch Trim to " + f.ToString ());}
}
if (node.HasValue (SAVED_YAW_TRIM) && float.TryParse (node.GetValue (SAVED_YAW_TRIM), out f))
{
config_fYT = f;
iFound = iFound + 1;
if (bDebug) {Debug.Log (CLASS_NAME + "_Load(): Yaw Trim to " + f.ToString());}
}
if (node.HasValue (SAVED_ROLL_TRIM) && float.TryParse (node.GetValue (SAVED_ROLL_TRIM), out f))
{
config_fRT = f;
iFound = iFound + 1;
if (bDebug) {Debug.Log (CLASS_NAME + "_Load(): Roll Trim to " + f.ToString());}
}
if (iFound == 0) {
config_fPT = 0.0f;
config_fRT = 0.0f;
config_fYT = 0.0f;
if (bDebug){Debug.Log (CLASS_NAME + "_Load(): No saved trim found");}
}

}
public void Save(ConfigNode node)
{
node.AddValue (SAVED_PITCH_TRIM, config_fPT.ToString ());
node.AddValue (SAVED_ROLL_TRIM, config_fRT.ToString ());
node.AddValue (SAVED_YAW_TRIM, config_fYT.ToString ());
if (bDebug) {Debug.Log (CLASS_NAME + "_Save(): Trim values saved to CFG");}
}

}

I am currently trying to create a new instance of it, and then simply calling it at a specific point in the partModule code, i.e. when the user triggers an action group:



public ExtendedTrimModule()
{
if (myNode == null) {
myNode = new ExtendedTrimConfigNode();
if (bDebug) { Debug.Log (CLASS_NAME +"(): Setting new Config Node");}
}
}

[KSPAction("Save Trim to CFG")]
public void SaveTrimAG(KSPActionParam param)
{
myNode.config_fPT = Current_fPT;
myNode.config_fRT = Current_fRT;
myNode.config_fYT = Current_fYT;
myNode.Save ();
if (bDebug) {Debug.Log (CLASS_NAME + "_SaveTrimAG(): Trim saved to CFG");}
}
[KSPAction("Set Trim from Saved")]
public void setFromSavedTrimAG(KSPActionParam param)
{
myNode.config_fPT = Current_fPT;
myNode.config_fRT = Current_fRT;
myNode.config_fYT = Current_fYT;
myNode.Load ();
if (bDebug) {Debug.Log (CLASS_NAME + "_SetFromSavedTrimAG(): Trim SET from CFG");}
}

Please note, I tried to paste only the relevant parts of the code for ease, let me know if this makes sense.

Also, is an in flight craft allowed to read from the original .craft file? or would all the reading and setting of a value be strictly into the persistence file?

Thank you for all the help.

Link to comment
Share on other sites

I can't tell for sure with the snippets posted, but there is something weird going on with your save call.

In the second snippet you call myNode.Save(); but the Save method in the first snippet expects a ConfigNode node. This should throw you a compile error and not run.

On your second question, there is no way in game to access the .craft file after the initial read to launch the vessel. You can write your own method to do so if you really need to, but it is a significant chunk of code.

D.

Link to comment
Share on other sites

I can't tell for sure with the snippets posted, but there is something weird going on with your save call.

In the second snippet you call myNode.Save(); but the Save method in the first snippet expects a ConfigNode node. This should throw you a compile error and not run.

On your second question, there is no way in game to access the .craft file after the initial read to launch the vessel. You can write your own method to do so if you really need to, but it is a significant chunk of code.

D.

Apologies for not being clearer, thats what I cant figure out, which ConfigNode should I be passing there?

Link to comment
Share on other sites

Oh, alright. Backing up a step here.

The IConfig node interface is how KSP saves data.

When KSP goes "it's time to save!", everything tagged with IConfignode gets handed a ConfigNode by KSP that represents the persistent.sfs file and the Save() method runs.

Then at load time, KSP passes back the same ConfigNode and runs the Load() method.

You can't call the Save/Load routine yourself, you can only add code to the Save/Load methods to run when KSP calls for the save.

Taking a guess at what you are trying to do, I don't think you need this at all. In your partModule class, the Save/Load methods are valid, just save/load your data in those. You don't need anything fancier to save 3 floats like you are trying to do.

You can even use KSPFields, those are save/load automatically as long as the persistent = true flag is set.

D.

Link to comment
Share on other sites

Oh, alright. Backing up a step here.

The IConfig node interface is how KSP saves data.

When KSP goes "it's time to save!", everything tagged with IConfignode gets handed a ConfigNode by KSP that represents the persistent.sfs file and the Save() method runs.

Then at load time, KSP passes back the same ConfigNode and runs the Load() method.

You can't call the Save/Load routine yourself, you can only add code to the Save/Load methods to run when KSP calls for the save.

Taking a guess at what you are trying to do, I don't think you need this at all. In your partModule class, the Save/Load methods are valid, just save/load your data in those. You don't need anything fancier to save 3 floats like you are trying to do.

You can even use KSPFields, those are save/load automatically as long as the persistent = true flag is set.

D.

Perfect - I think I understand this now, really appreciate the explanation.

Link to comment
Share on other sites

I'm making a planet randomizer mod and I have a bug where when creating more than one game during the same KSP session, the second one uses the starting configuration of the first one, instead of starting with the stock planets. If KSP is restarted between starting different games, this problem goes away.

This is what my starting function looks like (I'm saving the planet configuration in a file along with the regular save file):

        public void Start()
{
GameObject.DontDestroyOnLoad(this);
GameEvents.onGameSceneLoadRequested.Add(new EventData<GameScenes>.OnEvent(OnGameSceneLoadRequested));
PlanetSettings.Load(KSPUtil.ApplicationRootPath + "/GameData/PlanetRandomizer/Resources/PlanetRandomizer.cfg");
}

void OnGameSceneLoadRequested(GameScenes scene)
{
if (scene == GameScenes.SPACECENTER)
{
if (File.Exists(KSPUtil.ApplicationRootPath + "/saves/" + HighLogic.SaveFolder + "/PlanetRandomizer.cfg"))
{
print("Loading System");
PlanetSettings.Load(KSPUtil.ApplicationRootPath + "/saves/" + HighLogic.SaveFolder + "/PlanetRandomizer.cfg");
RebuildSystem();
}
else if (!File.Exists(KSPUtil.ApplicationRootPath + "/saves/" + HighLogic.SaveFolder + "/PlanetRandomizer.cfg"))
{
print("Showing GUI");
showGUI = true;
}
}
}

Anyone have any suggestions on how to fix this? I took this code mostly from another mod and I'm not exactly sure what's happening here.

Link to comment
Share on other sites

Here is the issue I think:

GameObject.DontDestroyOnLoad(this);

Because you call this, your mod does not unload upon returning to the main menu to start another game, therefore the settings from the previous game are used rather then KSP default.

You'll need to find the code to destroy your mod manually and call it when switching scenes to the main menu. (I think, I've never used DontDestroyOnLoad myself.)

D.

Link to comment
Share on other sites

Here is the issue I think:

GameObject.DontDestroyOnLoad(this);

Because you call this, your mod does not unload upon returning to the main menu to start another game, therefore the settings from the previous game are used rather then KSP default.

You'll need to find the code to destroy your mod manually and call it when switching scenes to the main menu. (I think, I've never used DontDestroyOnLoad myself.)

D.

Hmm, I just tried destroying the mod manually when switching to main menu. This doesn't fix the bug though. I think it's just that celestial bodies don't get reset at mainmenu, but only at game start.

Link to comment
Share on other sites

You mean the default Kerbol system?

Ya, that would not surprise me that it only gets defined once during game start.

I'd look at making a "default" PlanetRandomizer.cfg file that gets loaded whenever a new game is started to deal with this issue, but not knowing your code you may have some sort of shortcut you could do.

D.

Link to comment
Share on other sites

You mean the default Kerbol system?

Ya, that would not surprise me that it only gets defined once during game start.

I'd look at making a "default" PlanetRandomizer.cfg file that gets loaded whenever a new game is started to deal with this issue, but not knowing your code you may have some sort of shortcut you could do.

D.

This is exactly what I did. Worked very nicely and also took care of some other bugs. Thanks!

A different question: are there any tutorials on how to upload source code to Github and keep it updated (with git)?

Link to comment
Share on other sites

I was frustrated trying to understand the ControlTypes enum for use with the InputLockManager, so I converted the values into binary:


None 0
PITCH 1
ROLL 100
YAW 1000
THROTTLE 10000
SAS 100000
PAUSE 1000000
STAGING 10000000
CAMERAMODES 1 00000000
MISC 10 00000000
CAMERACONTROLS 100 00000000
TIMEWARP 1000 00000000
MAP 10000 00000000
LINEAR 100000 00000000
QUICKSAVE 1000000 00000000
QUICKLOAD 10000000 00000000
VESSEL_SWITCHING 1 00000000 00000000
CUSTOM_ACTION_GROUPS 10 00000000 00000000
GROUP_ABORT 100 00000000 00000000
GROUP_GEARS 1000 00000000 00000000
GROUP_LIGHTS 10000 00000000 00000000
GROUP_BRAKES 100000 00000000 00000000
GROUP_STAGE 1000000 00000000 00000000
GROUPS_ALL 1111110 00000000 00000000
ACTIONS_SHIP 10000000 00000000 00000000
ACTIONS_EXTERNAL 1 00000000 00000000 00000000
ACTIONS_ALL 1 10000000 00000000 00000000
RCS 10 00000000 00000000 00000000
WHEEL_STEER 100 00000000 00000000 00000000
WHEEL_THROTTLE 1000 00000000 00000000 00000000
EVA_INPUT 10000 00000000 00000000 00000000
EDITOR_ICON_HOVER 100000 00000000 00000000 00000000
EDITOR_ICON_PICK 1000000 00000000 00000000 00000000
EDITOR_TAB_SWITCH 10000000 00000000 00000000 00000000
EDITOR_SAVE 1 00000000 00000000 00000000 00000000
EDITOR_LOAD 10 00000000 00000000 00000000 00000000
EDITOR_EXIT 100 00000000 00000000 00000000 00000000
EDITOR_NEW 1000 00000000 00000000 00000000 00000000
EDITOR_LAUNCH 10000 00000000 00000000 00000000 00000000
EDITOR_UI_TOPRIGHT 11111 00000000 00000000 00000000 00000000
EDITOR_PAD_PICK_PLACE 100000 00000000 00000000 00000000 00000000
EDITOR_PAD_PICK_COPY 1000000 00000000 00000000 00000000 00000000
EDITOR_EDIT_STAGES 10000000 00000000 00000000 00000000 00000000
EDITOR_EDIT_NAME_FIELDS 1 00000000 00000000 00000000 00000000 00000000
EDITOR_ROTATE_PARTS 10 00000000 00000000 00000000 00000000 00000000
EDITOR_UNDO_REDO 100 00000000 00000000 00000000 00000000 00000000
EDITOR_SYM_SNAP 1000 00000000 00000000 00000000 00000000 00000000
EDITOR_OVERLAYS 10000 00000000 00000000 00000000 00000000 00000000
EDITOR_MODE_SWITCH 100000 00000000 00000000 00000000 00000000 00000000
EDITOR_UI_TOPBAR 100001 00011111 00000000 00000000 00000000 00000000
EDITOR_UI 101101 10011111 00000000 00000000 00000000 00000000
EDITOR_SOFT_LOCK 111110 01100000 11100000 00000000 00000000 00000000
EDITOR_LOCK 111110 01100000 11100000 00000000 00000100 00000000
TRACKINGSTATION_UI 1000000 00000000 00000000 00000000 00000000 00000000
TRACKINGSTATION_ALL 1000000 00000000 00000000 00000000 00001100 00000000
KSC_FACILITIES 10000000 00000000 00000000 00000000 00000000 00000000
KSC_UI 1 00000000 00000000 00000000 00000000 00000000 00000000
KSC_ALL 1 10000000 00000000 00000000 00000000 00001100 00000000
APPLAUNCHER_BUTTONS 10 00000000 00000000 00000000 00000000 00000000 00000000
ALL_SHIP_CONTROLS 11111111 11111111 11111111 11111111 11111111 11111110 00100010 10111111
All 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111

Now it is easier to see which flags are set with which values.

Here is the simple (and ugly) Python script that I threw together:


def insert_spaces(string):
return ' '.join(string[i:i+8] for i in range(0, len(string), 8))

with open('flags.txt') as file:
for line in file:
tokens = line.split()
flagName = tokens[0]
flagValue = int(tokens[1])

flagString = "{:64b}".format(flagValue)
flagString = insert_spaces(flagString)

print("{0:23} {1} {0}".format(flagName, flagString))

Note that I already scrubbed the ControlTypes file so that instead of:


using System;

[Flags]
public enum ControlTypes
{
None = 0,
PITCH = 1,
ROLL = 4,
YAW = 8,
...etc...

it looked like:


None 0
PITCH 1
ROLL 4
YAW 8
...etc...

Link to comment
Share on other sites

I'm trying to render a vector using LineRenderer and I'm running into a snag. I've got this set up:

oDirObj = new GameObject("Indicator");
oDirObj.layer = 10; // Map layer!
oDirection = oDirObj.AddComponent<LineRenderer>();
Vector3d mapFarPos = ScaledSpace.LocalToScaledSpace(far.orbit.pos.xzy); //far is a Vessel
oDirection.useWorldSpace = false;
oDirection.transform.parent = farTarget.transform; // farTarget is far's parent body's MapObject
oDirection.transform.localPosition = mapFarPos;
oDirection.material = new Material(Shader.Find("Particles/Additive"));
oDirection.SetColors(Color.green, Color.green);
oDirection.SetWidth(2.0f, 2.0f);
oDirection.SetVertexCount(2);
oDirection.SetPosition(0, mapFarPos);
oDirection.SetPosition(1, mapFarPos + exitTraj); // exitTraj is the vector I want to represent.
oDirection.enabled = true;

It draws the line, and it's close to what it should be, but it's always offset just a little bit and it's skewed in directions it shouldn't be going. That green line in the pictures should be tangent to the red orbit, and it should originate with the outer orbiting satellite on the red orbit. Am I using the wrong references to grab orbital position or something? What could be causing the exit vector to go up like that when every element in this is on the same plane? I'm using the same exitTraj variable to calculate both the red orbit and the green line, and I know the red orbit is accurate. Any help on this would be much appreciated, I've been bashing my head against this particular brick wall for a long time.

Javascript is disabled. View full album
Link to comment
Share on other sites

It looks to me, on a quick cursory glance, that you're trying to set the position of the line the same way twice, thus making it have the translation offset twice over rather than once over.

See these two lines:


oDirection.transform.localPosition = mapFarPos;


oDirection.SetPosition(0, mapFarPos);

There's two approaches to take:

1 - Give the line object a transformation matrix that describes a local frame of reference in which the origin point is zero'ed at what what the world coordinates would call mapFarPos.

2 - Use the coordinates raw without a local transform, and tell the line to draw from mapFarPos.

It looks like you're trying to do both, which essentially offsets the origin point twice over.

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