Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

I'm trying to load some music into the game database. I have no idea what goes wrong here (it even worked at some time before I refactored the code).

The path is right: C:\Kerbal Space Program\GameData\Akustik\Music\Moho.mp3

Class extends MonoBehaviourExtended (KSPPluginFramework by TriggerAu), code is inside Start() (tried Awake() before - same error).

File = "[B][U]Music/Moho.mp3[/U][/B]";
...
AssetLoader.LogFormatted_DebugOnly("Adding " + KSPUtil.ApplicationRootPath + "GameData/Akustik/" + File + " to database.");
Asset = GameDatabase.Instance.GetAudioClip(KSPUtil.ApplicationRootPath +"GameData/Akustik/" + File); // this fails and I don't know why.

AssetLoader.LogFormatted_DebugOnly("AudioClip is " + ((Asset == null) ? "null." : "loaded."));
Asset.name = File; // exception because Asset == null

GetAudioClip just searches the GameDatabase of already-loaded audio clips. Your first problem is that your file won't be in GameDatabase; it only loads ogg and wav. The second is that GetAudioClip wants a path relative to GameData, so if your file is in "(KSPROOT)/GameData/Akustik/MyFile.ogg", you should pass "Akustik/MyFile" in to get it

Link to comment
Share on other sites

I am having a GUI problem, the .log is saying I am pushing more GUIClips than I am popping, but I don't see how that is possible. Here is the log snippet and the GUI Methods.

UnityEngine.TextEditor.ClampPos ()
UnityEngine.GUI.DoTextField (Rect position, Int32 id, UnityEngine.GUIContent content, Boolean multiline, Int32 maxLength, UnityEngine.GUIStyle style)
UnityEngine.GUILayout.DoTextField (System.String text, Int32 maxLength, Boolean multiline, UnityEngine.GUIStyle style, UnityEngine.GUILayoutOption[] options)
UnityEngine.GUILayout.TextArea (System.String text, UnityEngine.GUILayoutOption[] options)
KerbLog.KlGUI.AutomaticLayoutGUI (Int32 winID)
UnityEngine.GUI.CallWindowDelegate (UnityEngine.WindowFunction func, Int32 id, UnityEngine.GUISkin _skin, Int32 forceRect, Single width, Single height, UnityEngine.GUIStyle style)
[ERR 11:53:22.035] GUI Error: You are pushing more GUIClips than you are popping. Make sure they are balanced

Here are the two GUI functions.

public void AutomaticLayoutGUI(int winID)
{
// begin area
GUILayout.BeginArea (new Rect (0, 0, 400, 800));
// horizontal 1
GUILayout.BeginHorizontal ();
// The bezel space.
GUILayout.Space (10);
// vertical 1
GUILayout.BeginVertical ();
// H2
GUILayout.BeginHorizontal ();
GUILayout.FlexibleSpace ();
GUILayout.Label ("Folder name");
GUILayout.FlexibleSpace ();
// end H2
GUILayout.EndHorizontal();
fileName = GUILayout.TextField (fileName);
// H3
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace ();
GUILayout.Label ("Log name");
GUILayout.FlexibleSpace ();
// end H3
GUILayout.EndHorizontal();
LogName = GUILayout.TextField (LogName);
// H4
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace ();
GUILayout.Label ("Log");
GUILayout.FlexibleSpace ();
// end H4
GUILayout.EndHorizontal();
// S1
ScrollPos = GUILayout.BeginScrollView(ScrollPos);
KerbLogtxt = GUILayout.TextArea (KerbLogtxt, GUILayout.ExpandHeight(true));
// end S1
GUILayout.EndScrollView ();
GUILayout.Space (5);
if (GUILayout.Button ("Save"))
{
SaveToFile();
}
if(GUILayout.Button("Reset"))
{
SetTimeRun = false;
LogName = "Log: ";
KerbLogtxt = "Log:";
fileName = "";
}
if (GUILayout.Button ("Load"))
{
ShowLoadGUI = !ShowLoadGUI;
}
GUILayout.Space (10);
// end V1
GUILayout.EndVertical ();
GUILayout.Space (10);
// end H1
GUILayout.EndHorizontal ();
// end area
GUILayout.EndArea ();
GUI.DragWindow ();
}
public void LoadGUI(int winID)
{
bool showloadtxt = false;
// start area
GUILayout.BeginArea (new Rect (0, 0, 400, 600));
// start horizontal 1
GUILayout.BeginHorizontal ();
GUILayout.Space (10);
//begin vertical 1
GUILayout.BeginVertical ();
//begin horizontal 2
GUILayout.BeginHorizontal ();
ShowFolder = GUILayout.Toggle (ShowFolder, "Use Folder");
fileName = GUILayout.TextField (fileName);
GUILayout.Space (10);
// end horizontal 2
GUILayout.EndHorizontal ();
// begin scroll view
ScrollPos2 = GUILayout.BeginScrollView (ScrollPos2, GUILayout.ExpandHeight(true));
foreach(string txtfile in LoadName())
{
string returntxt = txtfile.Replace(getPath(), "");
returntxt = returntxt.Replace(".txt", "");
showloadtxt = GUILayout.Button(returntxt);
if(showloadtxt)
{
LogName = returntxt;
KerbLogtxt = LoadFromtxt();
}
}
// end scroll view
GUILayout.EndScrollView ();
GUILayout.Space (10);
// end vertical 1
GUILayout.EndVertical ();
GUILayout.Space (10);
// end horiontal 1
GUILayout.EndHorizontal ();
// end area
GUILayout.EndArea ();
GUI.DragWindow ();
}

Link to comment
Share on other sites

I am having a GUI problem, the .log is saying I am pushing more GUIClips than I am popping, but I don't see how that is possible. Here is the log snippet and the GUI Methods.

I'm guessing that the GUILayout.TextArea method is choking on something, which prevents all of the following GUILayout.EndSomethings from being run. Try to comment out the TextArea, or figure out why it's broken.

Link to comment
Share on other sites

I would suggest looking at the LateUpdate() method.

Rather then having to dig into the code to modify costs, that method allows you to hook into the screen display and change what is displayed without actually changing anything underneath.

I used in here in my Landing Height mode to override the altitude display at the top of the screen without actually changing any of the altitude variables on the vessel.

I would think this trick would also work in the editor. (Also you would have to track down what that widget is named to override it.)

Hope that helps,

D.

Link to comment
Share on other sites

I'll look at that. Still going to need to get the total craft value one way or another though and I'm starting to worry I may have to calculate it myself by iterating through parts. Be much nicer if I could just re-use the code that already does that but I suspect there's no API exposure of that.

EDIT - Nevermind, I can catch it with OnFundsChanged - just won't be updating the widget. Meh.

Edited by AlphaAsh
Link to comment
Share on other sites

I'm guessing that the GUILayout.TextArea method is choking on something, which prevents all of the following GUILayout.EndSomethings from being run. Try to comment out the TextArea, or figure out why it's broken.

Thanks, I checked my other functions and it was a problem with them.

Link to comment
Share on other sites

Still going to need to get the total craft value one way or another though and I'm starting to worry I may have to calculate it myself by iterating through parts. Be much nicer if I could just re-use the code that already does that but I suspect there's no API exposure of that.

There is, it's just been slightly complicated with the addition of strategies. Here's what AlignedCurrencyFormatter uses:

float dryCost = 0f;
float fuelCost = 0f;
float total = EditorLogic.fetch.ship.GetShipCosts(out dryCost, out fuelCost);

// player might have a strategy active which increases/decreases total ship cost
var cm = CurrencyModifierQuery.RunQuery(TransactionReasons.VesselRollout, total, 0f, 0f);
total += cm.GetEffectDelta(Currency.Funds); // it's not a multiplier, it's the difference between current and modified total

Another clever option might be to create your own strategy that's temporarily applied in the editor. I have zero experience with that, though

Link to comment
Share on other sites

There is, it's just been slightly complicated with the addition of strategies.... Another clever option might be to create your own strategy that's temporarily applied in the editor. I have zero experience with that, though

I like the second idea a lot because it's already tied in everywhere it needs to be. Still, if it's easy enough to pass back and the widget catches that, then idea one will do. Cheers chap.

Link to comment
Share on other sites

Is there any way to find all partModules in the game, as in every module that is loaded?

PartModule[] allPMs = FindObjectsOfType<PartModule>(); only returns ACTIVE modules, not all modules.

I was hoping there would be something in protoParts DB, but it either is not there or my search skills are failing me. I'm after the equivalent of the protoParts DB actually as I want to make edits to partModules that will globally affect all parts with that partModule. (Add BaseActions to the partModule.Actions list.)

Any thoughts?

D.

Edited by Diazo
Link to comment
Share on other sites

Is there any way to find all partModules in the game, as in every module that is loaded?

PartModule[] allPMs = FindObjectsOfType<PartModule>(); only returns ACTIVE modules, not all modules.

I was hoping there would be something in protoParts DB, but it either is not there or my search skills are failing me. I'm after the equivalent of the protoParts DB actually as I want to make edits to partModules that will globally affect all parts with that partModule. (Add BaseActions to the partModule.Actions list.)

Any thoughts?

D.

You can loop through all the part prefabs from PartLoader and then look at their modules:


foreach (AvailablePart a in PartLoader.LoadedPartsList) {
if (a.partPrefab.Modules != null) {
foreach (PartModule m in a.partPrefab.Modules) {
Debug.Log("Part " + a.name + " has module " + m.name);
}
}
}

You'll obviously hit the same module type multiple times, but you can just put them in a list iff you don't have one of that type yet.

Link to comment
Share on other sites

Hmm, that would get me a list of all partModules, but can I edit the partModules themselves from the list that would generate?

Testing incoming it looks like.

D.

edit: Have not tested it yet, but I don't think that will work. That would get me the instance of the partModule on that part, not a global partModule I could edit once and be done.

I could edit each partModule individually, but I can see that increasing the load time when you start the game. Hmmm.....

Edited by Diazo
Link to comment
Share on other sites

True, but I'm more worried that the partModule object does not actually exist at that level and is instantiated from the .dll each time the part is loaded into the editor or flight mode. If that is case then this is a dead end and I'll have to find another way.

Out to time to actually test tonight so I still have to actually experiment tomorrow to see exactly what is happening.

D.

Link to comment
Share on other sites

but I'm more worried that the partModule object does not actually exist at that level and is instantiated from the .dll each time the part is loaded into the editor or flight mode. If that is case then this is a dead end and I'll have to find another way.

It's cloned (as part of the part prefab) and then any persistent data gets copied into its fields. I'm not sure it would work in your case; if that action list gets re-created during the second part (say, if it were being loaded from a save to map its actions to an action group*) then any changes you made to the prefab one wouldn't take. It's worth a try anyhow.

*I don't know for certain this is how it works. I've never worked with BaseAction

Link to comment
Share on other sites

For all my work with BaseAction, I'm in totally unknown territory also, this is as much a partModule loading question as a baseAction question.

While I've yet to run any tests, I have had a sleep on it (and based on your comment about cloning) and my current expectation is that during the loading process each part in the prefrab gets a copy of the appropriate partModules and by the time I can affect anything it is too late for me to affect the global partModule object. This means I will have to walk the entire part prefab cycling through each partModule on each part. After that for new vessels created I think that would be fine, the big question mark is what happens to existing vessels in flight?

This is all pure speculation until I can get home and run some tests though, have a work day to get through first.

D.

Edited by Diazo
Link to comment
Share on other sites

Grrr.

It's looking like this is a dead end.

AvailablePart.partPrefab.Modules is null so I can't reference it.

It looks like the partModules are stored in the List<string>AvailablePart.moduleInfos, but that appears to just be the partModule names that I'm assuming the list gets referenced to load the partModules when the part is spawned.

Which still leaves me with no clue where the default partModules sit around before they get used. My best lead was in the AssemblyLoader class, but I drew a blank there also.

Unless anyone has a brainwave, this is a dead end and I'll come at this another way.

D.

Link to comment
Share on other sites

It looks like the partModules are stored in the List<string>AvailablePart.moduleInfos, but that appears to just be the partModule names that I'm assuming the list gets referenced to load the partModules when the part is spawned.

Which still leaves me with no clue where the default partModules sit around before they get used. My best lead was in the AssemblyLoader class, but I drew a blank there also.

There isn't a "default" ModuleScienceExperiment, for example. There are ModuleScienceExperiments, pre-loaded with relevant data (from ConfigNodes like you'd expect), already attached to the part prefab. Here's what one looks like:

[LOG 01:26:25.837] DebugTools, mk1pod has components:
[LOG 01:26:25.838] DebugTools, ...c: UnityEngine.Transform
[LOG 01:26:25.838] DebugTools, ...c: Part
[LOG 01:26:25.839] DebugTools, ...c: ModuleCommand
[LOG 01:26:25.839] DebugTools, ...c: PartResource
[LOG 01:26:25.840] DebugTools, ...c: ModuleSAS
[LOG 01:26:25.841] DebugTools, ...c: ModuleReactionWheel
[LOG 01:26:25.841] DebugTools, ...c: ModuleScienceExperiment
[LOG 01:26:25.842] DebugTools, ...c: ModuleScienceContainer
[LOG 01:26:25.842] DebugTools, ...c: PartResource
[LOG 01:26:25.843] DebugTools, ...c: FlagDecal
[LOG 01:26:25.843] DebugTools, ...c: ModuleTripLogger
[LOG 01:26:25.844] DebugTools, --->model has components:
[LOG 01:26:25.844] DebugTools, ......c: UnityEngine.Transform
[LOG 01:26:25.845] DebugTools, ------>capsule has components:
[LOG 01:26:25.846] DebugTools, .........c: UnityEngine.Transform
[LOG 01:26:25.846] DebugTools, .........c: UnityEngine.MeshFilter
[LOG 01:26:25.847] DebugTools, .........c: UnityEngine.MeshRenderer
[LOG 01:26:25.847] DebugTools, --------->collider has components:
[LOG 01:26:25.848] DebugTools, ............c: UnityEngine.Transform
[LOG 01:26:25.848] DebugTools, ............c: UnityEngine.MeshCollider
[LOG 01:26:25.849] DebugTools, --------->flagTransform has components:
[LOG 01:26:25.850] DebugTools, ............c: UnityEngine.Transform
[LOG 01:26:25.850] DebugTools, ............c: UnityEngine.MeshFilter
[LOG 01:26:25.851] DebugTools, ............c: UnityEngine.MeshRenderer
[LOG 01:26:25.851] DebugTools, --------->hatch has components:
[LOG 01:26:25.852] DebugTools, ............c: UnityEngine.Transform
[LOG 01:26:25.852] DebugTools, ............c: UnityEngine.MeshFilter
[LOG 01:26:25.853] DebugTools, ............c: UnityEngine.MeshRenderer
[LOG 01:26:25.854] DebugTools, --------->rung has components:
[LOG 01:26:25.854] DebugTools, ............c: UnityEngine.Transform
[LOG 01:26:25.855] DebugTools, ............c: UnityEngine.MeshFilter
[LOG 01:26:25.855] DebugTools, ............c: UnityEngine.MeshRenderer
[LOG 01:26:25.856] DebugTools, --------->rung has components:
[LOG 01:26:25.856] DebugTools, ............c: UnityEngine.Transform
[LOG 01:26:25.857] DebugTools, ............c: UnityEngine.MeshFilter
[LOG 01:26:25.858] DebugTools, ............c: UnityEngine.MeshRenderer
[LOG 01:26:25.859] DebugTools, --------->window has components:
[LOG 01:26:25.860] DebugTools, ............c: UnityEngine.Transform
[LOG 01:26:25.860] DebugTools, ............c: UnityEngine.MeshFilter
[LOG 01:26:25.861] DebugTools, ............c: UnityEngine.MeshRenderer
[LOG 01:26:25.862] DebugTools, ------>ladder has components:
[LOG 01:26:25.862] DebugTools, .........c: UnityEngine.Transform
[LOG 01:26:25.863] DebugTools, .........c: UnityEngine.CapsuleCollider
[LOG 01:26:25.863] DebugTools, ------>airlock has components:
[LOG 01:26:25.864] DebugTools, .........c: UnityEngine.Transform
[LOG 01:26:25.864] DebugTools, .........c: UnityEngine.CapsuleCollider

(this being from)

[KSPAddon(KSPAddon.Startup.MainMenu, true)]
class PrefabDump : MonoBehaviour
{
void Start()
{
var ap = PartLoader.getPartInfoByName("mk1pod");
ap.partPrefab.gameObject.PrintComponents();
}
}

When a part is spawned, this whole GameObject gets cloned. If you were to destroy the ModuleScienceExperiment on this GO in the main menu, no mk1pods would be able to produce crew reports any more, whether they were loaded from a save game or created fresh.

I think you're still sunk, though. I had a look around and couldn't find a way to create a brand-new BaseAction without using reflection

Link to comment
Share on other sites

Hmmm.

I'm not going to give up just yet then. If I can use that component list to reference the module I may not be done yet.

I have an idea or two about making a dummy BaseAction and adding it to the partModule.actions list, but until I can access the partModule itself I can't test that.

Thanks for the help,

D.

Link to comment
Share on other sites

Okay, I actually think I almost have this, maybe.

I can add the action to the partmodule and everything looks fine, except it does not show up once I place a part.

Adding the action to the ModuleScienceExperiment on the Mk 1 pod, this is running in KSPAddon.Startup.MainMenu:

AvailablePart ap = PartLoader.getPartInfoByName("mk1pod");
foreach(Component cp in ap.partPrefab.gameObject.GetComponents<Component>())
{
if (cp.GetType().FullName == "ModuleScienceExperiment")
{
ModuleScienceExperiment mdlGim = (ModuleScienceExperiment)cp;
BaseActionList testList = mdlGim.Actions;
print("a " + testList.Count); //prints 2 for the two actions on the part
testList.Add(new BaseAction(testList, "HWAct", new BaseActionDelegate(HWTest), new KSPAction("HWTestGUI")));
print("b " + testList.Count); //prints 3 to show my action having been added
}
}

This throws no errors or any other indication something has gone wrong.

However, when I go to the editor and place a Mk 1 Pod, I get the following in the log which confuses me:

[LOG 19:44:17.642] a 3  //start my loop through the AvailablePart in the partLoaded, 3 for 3 actions
[LOG 19:44:17.643] ba DeployAction //note I have 3 actions
[LOG 19:44:17.644] ba ResetAction //Deploy and Reset come with the module
[LOG 19:44:17.644] ba HWAct //new Action I have added
[LOG 19:44:17.645] pm acts 2 //start of my loop through the placed parts, 2 for 2 actions
[LOG 19:44:17.646] pmba DeployAction //my action is missing on the part
[LOG 19:44:17.646] pmba ResetAction

So, any ideas what is going on? When I am in the editor the AvailablePart in the partLoader shows 3 actions the entire time I am in the editor, both before and after I place the part, but the part I place from that AvailablePart does not have the action I added.

There are absolutely no message that help, error messages or otherwise, did I mess up the creation of the Action or am I misunderstanding how the AvailablePart class works? I don't even know where to start.

D.

Link to comment
Share on other sites

Sorry for delayed response, somehow this post slipped by me

I can add the action to the partmodule and everything looks fine, except it does not show up once I place a part.

...

So, any ideas what is going on? When I am in the editor the AvailablePart in the partLoader shows 3 actions the entire time I am in the editor, both before and after I place the part, but the part I place from that AvailablePart does not have the action I added.

It looks like PartModule BaseActions are recreated after the prefab gets copied. Sadly, this means you'll have to modify every single spawned instance instead of the general set-and-forget solution you were looking for:

[KSPAddon(KSPAddon.Startup.MainMenu, true)]
class EditPodModules : MonoBehaviour
{
void Start()
{
PartLoader.getPartInfoByName("mk1pod").partPrefab.AddModule("AddAnAction");
}
}


class AddAnAction : PartModule
{
public override void OnAwake()
{
Log.Normal("AddAnAction running");
var exp = GetComponent<ModuleScienceExperiment>();

exp.Actions.Add(new BaseAction(exp.Actions, "HWAct", new BaseActionDelegate(TestMethod), new KSPAction("HWTestGUI")));
exp.Actions.ForEach(ba => Log.Normal("Instance part action: {0}", ba.guiName));
}

void TestMethod(KSPActionParam p)
{
print("TestMethod");
}
}

Link to comment
Share on other sites

Alright, that confirms this as a dead end then.

I started down this line of investigation for two reasons.

First to avoid having to modify the part as it is placed as I'm worried about code order and other things running on part creation trying to reference the action but running earlier in the start up cycle so the action does not exist yet. In theory this would be easy enough to deal with, in practice I'm still a beginner programmer really and I can see that stymieing me quite easily.

Second was to avoid having to add a partModule. This was only for looks on the part detail window, but I did not want to see two ModuleGimbal (ModuleGimbal and ModuleGimbalAction) entries in the partModule list that appears when you right-click.

If I have to add a partModule anyway, I'm going to go with the easier route of just using the KSPAction property on the partModule.

Is there some way to hide a partModule from showing in the partModule list in the editor? (The list you pop out with RMB that shows details such as engine thrust, ISP, etc.)

Thanks for all the help xEvilReeperx,

D.

Link to comment
Share on other sites

Hmmm, interesting.

The ModuleGimbalActions partModule I add to the part to add more actions to the ModuleGimbal partModule does show in that list. I wonder what the criteria are to be visible?

Experiments incoming.

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