Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

4 hours ago, sarbian said:

If you want to change the content you will need to get the text field reference and update their text. The new UI system is not like the old one, you don't recreate stuff each frame.

So I try this code:

PopupDialog monitorWindow;
MultiOptionDialog monitorDialog;

public void DisplayData()  // Called when the AppLauncher button is clicked
{
	monitorDialog = new MultiOptionDialog("", "Health Monitor", HighLogic.UISkin);
	monitorWindow = PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), monitorDialog, true, HighLogic.UISkin, false); 
}

public void Update()
{
	monitorDialog.message = "Time is " + KSPUtil.PrintDateCompact(Planetarium.GetUniversalTime(), true, true);
	monitorDialog.Update();
}

public void UndisplayData()
{
	monitorWindow.Dismiss();
}

I expected it to create a dialog window with dynamically updating time. But the window doesn't update at all.

Edited by garwel
Link to comment
Share on other sites

You are trying to set the title field of the popup window with the time?

For some reason it looks like the PopupDialog doesn't store a reference to the text component for the title (some of the different DialogGUI classes do store their text field), so you'll have to get it the dumb way with GetComponent or GetComponentInChildren. Use Debug Stuff to figure out the hierarchy, but I suspect the title will be the first TextMeshProUGUI object from the reference to the PopupDialog.

Setting the text field for the TMP component should update the text on screen, the only problem might be that the text could potentially overflow the space set for that object; you could try resizing it, but it might be simpler to just set the initial title as something long enough to fit any potential text that you might use.

Link to comment
Share on other sites

Actually, I wanted to update the message field of the dialog, not the title.

Anyway, I think I see the problem. MultiOptionDialog just doesn't seem to have a method for updating its contents. One should create DialogGUI* objects instead and put them inside the MultiOptionDialog. This code works:

DialogGUILabel monitorLabel;
PopupDialog monitorWindow;

public void DisplayData()  // Called when the AppLauncher button is clicked
{
	monitorLabel = new DialogGUILabel("", true, true);
	monitorWindow = PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new MultiOptionDialog("", "Health Monitor", HighLogic.UISkin, monitorLabel), false, HighLogic.UISkin, false);
}

public void Update()
{
	if (monitorWindow != null)
		monitorLabel.SetOptionText("Time is " + KSPUtil.PrintDateCompact(Planetarium.GetUniversalTime(), true, true));
}

public void UndisplayData()
{
	monitorWindow.Dismiss();
}

The only problem here is that dragging the window is slow and doesn't seem to follow the mouse cursor exactly. It also naturally doesn't remember the window position after it's been closed. But I guess I can live with it for now.

Edited by garwel
Link to comment
Share on other sites

I've been playing with this quite a bit already and can probably help you.  I'm trying to reliably set the position of a ship in my mod (LMP), and so understanding the various attributes of Vessel, OrbitDriver, orbit, and others have been key.

 

Before you start, though, you must watch the following video until re-entry fx (approximately 24.5 minutes in).  The second half of the video is how render and mapping works, which is less critical for your question and can be skipped:

 

 

Also read and understand this page:

http://kos.wikia.com/wiki/XYZ_system_of_KSP

 

Once you understand both of those, send me a message on the forums or at Skype (same handle as the forums), and I can try to walk you through the logic.

On 1/17/2017 at 3:11 PM, JeanLucJ said:

Hello,

I'm new to ksp modding, but have done quite a lot of personnal mods in other games, in various technical environnements.

A simple question : are there some ground explanations on the principles behind ksp code?

I don't mind doing research, for now I understand already quite a lot (not counting the flight model :p), there are a lot of mod sources code available to help, but if something exist, it would help.

 

Thanks :)

JeanLucJ, take a look at my previous post.  I forgot to hit quote when I wrote that up--it was addressed to you.

Link to comment
Share on other sites

Hi there,

I am trying to figure out how or why the newly-updated variable radioFrequency of a part module of an active vessel is not immediately reflected on the global scope in the flight scene.

Here's the part module I added to every command part via ModuleManager:

//This class is coupled with the MM patch that inserts CNConstellationModule into every command part
public class CNConstellationModule : PartModule
{
    [KSPField(isPersistant = true)]
    public short radioFrequency = CNCSettings.Instance.PublicRadioFrequency;
  
    [KSPEvent(guiActive = true, guiActiveEditor =true, guiActiveUnfocused = false, guiName = "CommNet Constellation", active = true)]
    public void KSPEventConstellationSetup()
    {
    	new VesselSetupDialog("Vessel - <color=#00ff00>Setup</color>", this.vessel, this.part, null).launch(); // pass the active vessel and right-clicked part references
    }
}

Inside my VesselSetupDialog codes, the relevant codes for updating the part module are as follows.

if (rightClickedPart != null) // either in editor or flight
{
	CNConstellationModule cncModule = rightClickedPart.FindModuleImplementing<CNConstellationModule>();
	cncModule.radioFrequency = this.conFreq; // the change is not immediately reflected until I exit the flight scene
  	message = "The frequency of this command part is updated";
}

The problem is that the change is reflected only upon existing the flight scene but not immediately in flight. I tried the GameEvent.onVesselWasModified.Fire(hostVessel) but it has no effect on immediate reflection. I tried the ProtoPartSnapshot updating but it doesn't immediately reflect until flight exit. I am poking in the plugin dev forum and DLLs for answers but there is a lot of information to sift through.

Does anyone know how to notify the KSP that the active vessel has some changes that should be reflected immediately?

Edited by TaxiService
Link to comment
Share on other sites

How are you checking the code has updated?

There is an issue with the part right-click menu that it doesn't refresh automatically, you have to tell it to. (or Open/Close it, such as leaving/reloading the flight scene.)

If you stick a Debug.log in CNConstellation.Update(), what does the log say the value is? You should see the number printed to the log change when you execute that code.

D.

Link to comment
Share on other sites

When is GameEvents.onGameStatePostLoad called? I don't see my callback being called.

 

Edit: Maybe the solution is mentioned between the lines here, that the callback is (only) for reverted flights?

Edited by Rodhern
solution possibly found
Link to comment
Share on other sites

17 hours ago, Diazo said:

How are you checking the code has updated?

There is an issue with the part right-click menu that it doesn't refresh automatically, you have to tell it to. (or Open/Close it, such as leaving/reloading the flight scene.)

If you stick a Debug.log in CNConstellation.Update(), what does the log say the value is? You should see the number printed to the log change when you execute that code.

D.

Through the debugging environment (link), I observed any change to the part module's variable is indeed saved. But it feels like the local copy of the vessel is only changed until it is committed to the KSP's global database upon existing the flight scene. I am searching for the trigger to this commit action. (last resort is to call an update function outside the flight scene to change the variable in the global database)

Also this variable is not intended for a part-action menu as it is for behind-scene information. 

Edit: All right, I don't even understand now.

if (rightClickedPart != null) // either in editor or flight
{
	CNConstellationModule cncModule = rightClickedPart.FindModuleImplementing<CNConstellationModule>(); // TODO: in flight, update has no effect
	cncModule.radioFrequency = this.conFreq;

	Vessel gv = FlightGlobals.Vessels.Find(x => x.id == this.hostVessel.id);
	Part gp = gv.parts.Find(x => x.flightID == rightClickedPart.flightID);
	CNConstellationModule gm = gp.FindModuleImplementing<CNConstellationModule>(); // contain the same change!
}

I checked the same vessel through FlightGlobals and found the change is reflected immediately after the update. BUT somewhere else (say in the mapview in the same session), the same FlightGlobals shows the vessel's unchanged value! Why two different states of FlightGlobals?

Edited by TaxiService
Link to comment
Share on other sites

Hmm, we're reaching the limit of what I know on this subject, but here's what comes to mind:

1) "Global database"? What do you mean by this? The persistent.sfs file on disk is the save file, it only updates when the game saves, such as switching scenes. If you mean the objects stored in memory, those are real-time and are the numbers actually used to run the game.

2) Are you using Vessel and ProtoVessel at the correct times? A ship only exists as a Vessel while loading within the physics bubble in flight. Otherwise it just exists as a ProtoVessel object. Further in the editor, it doesn't exist as either and you are working with the ShipConstruct.

Hope that helps,

D.

Link to comment
Share on other sites

19 hours ago, Diazo said:

Hmm, we're reaching the limit of what I know on this subject, but here's what comes to mind:

1) "Global database"? What do you mean by this? The persistent.sfs file on disk is the save file, it only updates when the game saves, such as switching scenes. If you mean the objects stored in memory, those are real-time and are the numbers actually used to run the game.

2) Are you using Vessel and ProtoVessel at the correct times? A ship only exists as a Vessel while loading within the physics bubble in flight. Otherwise it just exists as a ProtoVessel object. Further in the editor, it doesn't exist as either and you are working with the ShipConstruct.

Hope that helps,

D.

*smack my forehead on my desk* 

The FlightGlobals didn't have two conflicting personalities. The cause of this whole problem turns out to be my tiny mistake. I forgot about my caching of vessel data and thus chased after the cached value instead of the actual value changed. Sorry for this trouble, Diazo.

Link to comment
Share on other sites

When using the GameEvents.onGameStateSaved my callback gets passed a Game object. I think that if I inspect the Game object too thoroughly I trigger an additional call to .onGameStateSaved, potentially leading to an infinite loop.

Has this caught you off guard too? Do you happen to know which part of the Game object it is that I should keep my palms off?

Edited by Rodhern
courier font
Link to comment
Share on other sites

1 hour ago, Rodhern said:

When using the GameEvents.onGameStateSaved my callback gets passed a Game object. I think that if I inspect the Game object too thoroughly I trigger an additional call to .onGameStateSaved, potentially leading to an infinite loop.

Has this caught you off guard too? Do you happen to know which part of the Game object it is that I should keep my palms off?

Uh ? inspecting an object should not trigger anything. What are you doing exactly ?

Link to comment
Share on other sites

5 minutes ago, sarbian said:

Uh ? inspecting an object should not trigger anything. What are you doing exactly ?

The exact thing is that I was debugging like this

sprintf "%A" game

where "sprintf "%A"" is F# compiler magic for formatting all of the field values of the object to a string. You can think of it as an enhanced version of .ToString().

I think I have been able to recreate the crash with this (simpler) C# code:

[KSPAddon(KSPAddon.Startup.SpaceCentre, true)]
public class Class1 : MonoBehaviour
{
    void GameStateSaved (Game game)
    {
        Debug.Log ("Call .ToString():");
        Debug.Log (game.ToString());
    }
        
    void Start ()
    {
        GameEvents.onGameStateSaved.Add(GameStateSaved);
    }
}

At least I think that is the offending code bit.

Link to comment
Share on other sites

So, I fixed my rendezvous problem by stealing some Hyperedit code(totally legal) but i might have a small problem... I have a [KSPEvent] but it cant bind for some reason:

[EXC 00:25:12.469] ArgumentException: Couldn't bind to method 'fireCannon'.
	System.Delegate.GetCandidateMethod (System.Type type, System.Type target, System.String method, BindingFlags bflags, Boolean ignoreCase, Boolean throwOnBindFailure)
	System.Delegate.CreateDelegate (System.Type type, System.Object target, System.String method, Boolean ignoreCase, Boolean throwOnBindFailure)
	System.Delegate.CreateDelegate (System.Type type, System.Object target, System.String method, Boolean ignoreCase)
	BaseEventList.CreateDelegates (System.Object part)
	BaseEventList..ctor (.Part part, .PartModule module)
	PartModule.ModularSetup ()
	PartModule.Awake ()
	UnityEngine.GameObject:AddComponent(Type)
	Part:AddModule(String, Boolean)
	Part:AddModule(ConfigNode, Boolean)
	PartLoader:ParsePart(UrlConfig, ConfigNode)
	<CompileParts>c__Iterator62:MoveNext()
	UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
[ERR 00:25:12.473] PartLoader: Encountered exception during compilation. System.NullReferenceException: Object reference not set to an instance of an object
  at PartModule.Load (.ConfigNode node) [0x00000] in <filename unknown>:0 
  at Part.AddModule (.ConfigNode node, Boolean forceAwake) [0x00000] in <filename unknown>:0 
  at PartLoader.ParsePart (.UrlConfig urlConfig, .ConfigNode node) [0x00000] in <filename unknown>:0 
  at PartLoader+<CompileParts>c__Iterator62.MoveNext () [0x00000] in <filename unknown>:0 

[ERR 00:25:12.475] PartCompiler: Cannot compile part

My code consists of:

[KSPEvent(guiActive = true, guiName = "Fire cannon")]
        public bool fireCannon()
        {
            ScreenMessages.PostScreenMessage("The precharge will not check if you have fuel, and just use it. Check to have 7.5 units of Korex and plenty of EC");
            //check if enough fuel
            if (part.RequestResource(korexID, chargeFuelUsage) == chargeFuelUsage)
            {
                //start charge coroutine
                StartCoroutine(prechargeThing());
            }
            return false;
        }

And this prevents PartCompiler from loading the part

Edited by JuhaJGamer
Better explanation of exception
Link to comment
Share on other sites

13 hours ago, sarbian said:

Did you try with a void return type ?

Alright thanks, but now I have an another problem, I can get any other resource, but I cant get EC for some reason, I have tried

if (part.RequestResource("ElectricCharge", rayPowerUsage / 1000) < chargeFuelUsage / 1000)
if (part.RequestResource("Electric Charge", rayPowerUsage / 1000) < chargeFuelUsage / 1000)

and 

if (part.RequestResource("EC", rayPowerUsage / 1000) < chargeFuelUsage / 1000)

None of them seem to work though

Link to comment
Share on other sites

I do not believe that is possible through the event itself.

If it is, you would have to manually hook into the OnMouseDown and OnMouseUp events of the button and not use the KSPEvent code itself.

So you'd have an empty game event, then in Start() hook into OnMouseDown and OnMouseUp.

I assume these events exist, they did in the old EzGUI system and are pretty typical, but I have no clue what they'd actually be called to find them.

Well, just the OnMouseUp event, the KSPEvent code is the OnMouseDown event I believe.

 

D.

Edited by Diazo
Link to comment
Share on other sites

17 hours ago, SkyKaptn said:

If I were to release just some ASET/RPM configs as well as a ModuleManager patch, does this still require a licence?

I know it seems silly for a small thing like that, but by default, we have no rights at all to your creation.  If you want to share your stuff, then you have to tell us what we're allowed to do with it.  Can we copy it?  Can we make an improved version and give that new version to people?  Can we sell our improved version without sharing any improvements we make, and without crediting you?

Besides letting us know how we're allowed to use the software, most licenses have a disclaimer about liability and warranty, so that you don't get sued.  It's a very small probability for KSP mods, but it doesn't hurt to have it.

If the only thing you want is credit, then the MIT license is good.  If you don't even want credit, you can make it public domain.

There's plenty more "standard" licenses to choose from:

For software: https://choosealicense.com/

For art and other stuff that isn't software: https://creativecommons.org/share-your-work/

MM patches would qualify as software, since they're a set of instructions.  ASET/RPM configs.. I dunno, but applying a software license to non-software is fine, but not the other way around.

Note that you cannot change a license retroactively!  If you release something under license A one day, and then under license "B" the next, then anyone who obtained a copy under license A is free to follow those terms forever, for that copy.  If you release version 2.0 under license A, then a new improved version 3.0 under license B, then people can't use version 3.0 under license A; they can only use version 2.0 under license A.

Note that if you're just contributing patches to an existing project, your patches by default use the project's existing license.  You can contribute patches under a different license, but the project maintainer doesn't have to accept them.

Link to comment
Share on other sites

Hi,

With these codes of CommNetNetwork class below, I am trying to find out how to overcome the protected set and assign with my own subclass i.e. "CommNetNetwork.Instance = new SomeSubclassCommNetNetwork();".

//Edited out the KSP codes I was not supposed to post

I tried this trick but it doesn't work.

CommNetNetwork a = CommNetNetwork.FindObjectOfType<CommNetNetwork>(); // or CommNetNetwork.Instance.GetComponentInParent<CommNetNetwork>();
a = new SomeSubclassCommNetNetwork(); // result is a = null

Anyone can give tips on overcoming protected set property?

Edited by TaxiService
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...