Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

8 minutes ago, wile1411 said:

If you do have a proof of concept bit of code, it'd be great to have a look.

Is all the clean up necessary only if you want to keep a tree intact when pruning from the middle? I figures with craft always being broken via various impacts, that killing off a parts should be within scope of the default process.

I'm looking to use a part I've built and add a part to that known piece as an "end node". Depending on when the player does down the track, I plan to remove it later using the part.die command. I was hoping with the information I already know about my parts location and orientation, spawning in a part attached to it could be a matter of working out the location if reference to the known part rather than from the root part.

 

Part tree in game is almost proper tree. You have Part->AttachmentNode->Part, no id's at all. However editing this tree is horror from what I saw in KAS/KIS code. Check out CreatePart (line 306) in KIS_Sharded.cs. It appears KIS is chasing targeted attachment node with new part until game allows coupling to happen. Part tree will get more and more convulted with each docking, decoupling or KAS/KIS operation. And then you risk awakening some sort of kraken when physics kicks in again for changed vessel.

In .sfs save file part tree is flattened to list, just open it in notepad and search for your VESSEL by name ( in my lib it is root.Find("GAME\\FLIGHTSTATE").Select("VESSEL","name","<vessel name")[0].Select("PART"), or you can just use LINQ instead of those ugly constructs, but LINQ/foreach in Mono leaves tons of garbage in memory ). In PART entries you have "parent = <parent_id>" , "attN = <node name>, <other_part_id>" and "surfaceN = surface, <parent_id>" (I forgot exact keys for surface attachment). "parent" is most important, required to build proper part tree when save is loaded, with incorrect values you get uncontrollable rocket. Proper values "attN" are needed only for rocket to retain its shape. With incorrect part id in "attN"  I got, for example, quad-symmetry radial Thuds to decouple from rocket (zero force) and sit all in same spot (in place where I clicked in VAB).

Cleaning will always be necessary when editing .sfs, because removing part in middle changes id's of every part after it. So you need to update parent id in children and its attachment nodes as well as child id in parent attachment nodes. Not the best format I had to work with. 

There is problem with finding part you clicked on in .sfs. There is no unique id to use, we would need to first add custom field with our own unique id to part you clicked, then find it in .sfs. From what i understand it should be doable. If all else fails, it could be done by custom resource (hide id in amount)..

I'll upload my code later today, when I'll be at my computer.

Link to comment
Share on other sites

5 hours ago, PT said:

I picked different approach in my project.

  1. Save game
  2. Edit .sfs
  3. Save modified .sfs
  4. Load game

(mods can save and load saves)

*cringe*

Decoupling is easy. ( part.decouple(0)  or part.parent.decouple(0) depending on where you want to cut). Doing it by hand is a bit more complex but not that much and has been done in other mods.

There are many mods that does it and many other that adds part (KIS and Extraplanetary-Launchpads being the two that do the most).

So why use a complex solution that may break horribly with future update and could easily corrupt save ???

 

Link to comment
Share on other sites

15 minutes ago, sarbian said:

*cringe*

Decoupling is easy. ( part.decouple(0)  or part.parent.decouple(0) depending on where you want to cut). Doing it by hand is a bit more complex but not that much and has been done in other mods.

There are many mods that does it and many other that adds part (KIS and Extraplanetary-Launchpads being the two that do the most).

So why use a complex solution that may break horribly with future update and could easily corrupt save ???

 

I do not want to decouple. I want to remove many parts in many places. And them add parts back again when I will feel like doing so.

As I wrote earlier, I did read KIS sources where adding parts is complex solution, working in complex and weird api of KSP intestines, where child is not certain to be child of its parent.

Haven't thought about stealing ideas from launchpad mod.

Link to comment
Share on other sites

I'm looking to programmatically identify the celestial body (CB) a science experiment is related to. Note, I have the Experiment data first - the CB isn't known in this context. Ie the player isn't landed on the body the experiment came from.

Looking at the resulting data object from ModuleScienceContainer, I can see there is a SubjectID. From that I found the function that specifically uses subjectID to get some information about the science status of a given experiment. However I can't find any direct reference to which celestial body it is related to.
Example:

SubjectID = "surfaceSample@KerbinSrfLandedLaunchPad"
ScienceSubject scisub;
scisub = ResearchAndDevelopment.GetSubjectByID(subjectID);
print(scisub.title);

This outputs the title of the experiment to the debug screen - in the example this would be: "Surface Sample from LaunchPad" but does contain the name as a property or within the text. There is also no property for "body" that I found.

Only thing I did see was ScienceSubject.IsFromBody(cb) - Am I safe to say there is no direct reference and it has to be looked up via this method anytime I want the CB info/name?

EDIT: 

Received an answer from @ShotgunNinja via msg (Thank you!) - the answer was surprisingly simple - even how stock does it.

20 hours ago, ShotgunNinja said:

The science system is a bit of a mess. The key to understand it is the 'subject_id'. You ask "where is the body stored?" and the answer is "the subject_id". If you'd ask "where is the situation stored?" the answer will be the same.

So, how does the stock game recognize when some science data is from a particular body? What is ScienceSubject.IsFromBody(db) doing? A substring search like this:


bool IsFromBody(CelestialBody cb)
{
  return this.id.Contains(cb.bodyName);
}

 

Edited by wile1411
added answer that was msg'ed to me
Link to comment
Share on other sites

26 minutes ago, RocketSquid said:

Is there any way to alter the range of docking port magnetics?

Please quote or mention me if you answer.

Yes, there's a field acquireRange on ModuleDockingNode which defaults to 0.5

I don't think this strictly has to do with plugin development though

Link to comment
Share on other sites

Getting an engine normalized thrust.

I get the engine this way:

                    if (part.Modules.OfType<ModuleEnginesFX>().Count() == 0)
                    {
                        if (part.Modules.OfType<ModuleEngines>().Count() == 0)
                        {
                            return;
                        }
                        else
                        {
                            myEngine = part.Modules.OfType<ModuleEngines>().FirstOrDefault();
                        }
                    }
                    else
                    {
                            myEngine = part.Modules.OfType<ModuleEnginesFX>().FirstOrDefault();
                    }

When I do this:

                        MPLog.Writelog("[Maritime Pack] (aniEngine) normalized Thrust " + myEngine.normalizedThrustOutput);
                        MPLog.Writelog("[Maritime Pack] (aniEngine) normalized Output " + myEngine.normalizedOutput);
                        MPLog.Writelog("[Maritime Pack] (aniEngine) Current Thrust " + myEngine.finalThrust);
                        MPLog.Writelog("[Maritime Pack] (aniEngine) Max Thrust " + myEngine.GetMaxThrust());
                        MPLog.Writelog("[Maritime Pack] (aniEngine) Current Throttle " + myEngine.currentThrottle);

Everything except GetMaxThrust and currentThrottle constantly read 0 

I'm hoping to get a normalizedThrust, which I assume reads 0-1.

Edited by Fengist
Link to comment
Share on other sites

@Fengist not sure about your issue, but you could simplify that code quite a bit.  part.Modules.OfType<ModuleEngines> will give you both ModuleEngines and ModuleEnginesFX, since ModuleEnginesFX derives from ModuleEngines.  Also probably easier to check whether FirstOrDefault gave you anything rather than looking at the count as well.  Also, part has a method for doing this:

myEngine = part.FindModuleImplementing<ModuleEngines>();

if (myEngine == null) return;

 

Link to comment
Share on other sites

7 minutes ago, blowfish said:

@Fengist not sure about your issue, but you could simplify that code quite a bit.  part.Modules.OfType<ModuleEngines> will give you both ModuleEngines and ModuleEnginesFX, since ModuleEnginesFX derives from ModuleEngines.  Also probably easier to check whether FirstOrDefault gave you anything rather than looking at the count as well.  Also, part has a method for doing this:


myEngine = part.FindModuleImplementing<ModuleEngines>();

if (myEngine == null) return;

 

Yea, I was trying different things to find out why I wasn't getting data from the engine.  That's pretty much how I originally had it, assuming engine would inherit the data from engineFX

I know why now, my own utter stupidity.

I was doing this just before the log output:

(partType == "Engine" && !myEngine.EngineIgnited)

So, the only time it showed any data was when the engine was shut down.

Duh!

Thanks :wink:

Link to comment
Share on other sites

2 hours ago, RocketSquid said:

What version of unity does KSP currently use?

That depends on what version of KSP you are referring to.
If you go to the output_log.txt you will find a line like this:
Initialize engine version: 5.2.4f1


That is the Unity version number (in this case KSP1.1.3)

Link to comment
Share on other sites

I have a method in a plugin I'm working on that will go through a list of vessels and return a list of vessels that implement my plugin.  I've been trying to troubleshoot the problem and it looks like the list is not returning correctly.  The list as Start() sees it has 0 elements, instead of the number of ships (2).  I've added some log writes at different points.  The code I am running:

namespace CivilianPopulationRevamp
{
  [KSPAddon (KSPAddon.Startup.Flight, false)]
  public class BackgroundGrowth : CivilianPopulationRegulator
  {
    List<Vessel> listCivilianVessels = new List<Vessel> ();

    public void Start ()
    {
      Debug.Log (debuggingClass.modName + "Running OnStart in BackgroundGrowth!");
      List<Vessel> listOfVessels = FlightGlobals.Vessels;
      listCivilianVessels = getCivilianVessels (listOfVessels);
      Debug.Log (debuggingClass.modName + listCivilianVessels.Count);
      foreach (Vessel civvieVessel in listCivilianVessels) {
        Debug.Log (debuggingClass.modName + civvieVessel.vesselName);
      }
    }//end start
      
      
    public List<Vessel> getCivilianVessels (List<Vessel> listOfVessels)
    {
      bool hasCivModule = false;//used to flag whether or not my plugin is on that craft
      List<Vessel> civilianDockList = new List<Vessel> ();//list of civilian vessels to be returned

      foreach (Vessel sumVes in listOfVessels) {
        hasCivModule = false;
        if (!sumVes.isActiveVessel && !sumVes.loaded) {
          var partSnapshots = sumVes.protoVessel.protoPartSnapshots;
          foreach (var partSnapshot in partSnapshots) {
            var protoPartModules = partSnapshot.modules;
            foreach (var protoPartModule in protoPartModules) {
              if (protoPartModule.moduleName == "CivilianDockGrowth") {
                hasCivModule = true;
              }//end if plugin name matches
            }//end foreach ProtoPartModuleSnapshot
          }//end foreach protoPartSnapshot
        }//end if vessel active
        if (hasCivModule) {
          listCivilianVessels.Add (sumVes);
          Debug.Log (debuggingClass.modName + "Added " + sumVes.vesselName + " to civilian list, implementing CivilianDockGrowth");
        }
      }//end foreach Vessel
      return civilianDockList;
    }//end getCivilianVessels
  }//end class
}//end namespace

This returns the following in the KSP log:

[LOG 17:08:25.604] [CivilianPop Revamp]Running OnStart in BackgroundGrowth!
[LOG 17:08:25.605] [CivilianPop Revamp]Added Untitled Space Craft to civilian list, implementing CivilianDockGrowth
[LOG 17:08:25.605] [CivilianPop Revamp]Added ActiveTestVessel1 to civilian list, implementing CivilianDockGrowth
[LOG 17:08:25.605] [CivilianPop Revamp]0
[LOG 17:08:25.615] [CivilianPop Revamp]civieDock (Untitled Space Craft) is running OnStart()!

Note:  The last line is from another portion of code, meaning that the above Start() method has finished executing.  Has anyone seen something similar to this?  The searching I've done says that the above (return variable_name) is the correct way to return an object.  Is there something I'm missing (like a need to return by reference)?

Edited by Tralfagar
Link to comment
Share on other sites

Is it possible to make an opaque Gui like the KSPedia, or Astronaut complex Gui? I want it to be open and closable via the appLauncher from the KSC. I thought it would be possible with this new KSPAsset system, but I just can't get my head round how to do it, and from what I've read, it's not easy to get gui's made in this way to be dynamic, which is something I'm looking for. In an ideal world, I'd actually like to add it to the Administration building as another tab, but I'm guessing this is impossible, or more effort that it's worth.

Any help would be greatly appreciated.

Link to comment
Share on other sites

@sarbian Thank you!  Now I feel like a real dunce.:confused:

Looking back, I'm trying to make my code more readable/less resource intensive.  But I'm having trouble figuring out if what I want to do is possible in C#.  It probably is, but I don't know the technical term for it.

Right now, I am going through a list of each inactive vessel and selecting each that has one of my part modules.  Form there, I cycle through each part to find the specific part implementing my mod.  Then I use the part snapshot (another array nested in the above part) to obtain the resourceSnapshot.  This is a lot of lists to go through in order to find something that should not be changing much (this section only runs on vessels that are unloaded).  I would like to save that memory location in a new class so I can directly modify it without having to set up several nested foreach loops.  In C/C++, I'd use pointers to keep that information and bundle them in a struct.  From there, if I want to change the value, I need only pass the pointer a new value.

So instead of this in the start AND update section of code:

foreach (Vessel civilianVessel in listCivilianVessels) {
  bool appliedVesselChanges = false;
  var civilianPartSnapshots = civilianVessel.protoVessel.protoPartSnapshots;
  foreach (ProtoPartSnapshot civilianPartSnapshot in civilianPartSnapshots) {
    foreach (ProtoPartModuleSnapshot civilianPartSnapshotModule in civilianPartSnapshot.modules) {
      Debug.Log (debuggingClass.modName + civilianPartSnapshotModule.moduleName);
      if (civilianPartSnapshotModule.moduleName == "populationGrowthModifier" && !appliedVesselChanges) {
        var civilianResourceList = civilianPartSnapshot.resources;
        foreach (ProtoPartResourceSnapshot civilianResource in civilianResourceList) {
          //apply logic to increment resources here
          appliedVesselChanges = true;
        }
      }
    }
  }
}

The above would be run once to collect information and log it into an array of classes My code could look like this:

public class myBackgroundClass
{
  List<UnloadedVesselClass> listUnloadedVessels = new List<UnloadedVesselClass>();

  OnStart(startState state)
  {
    //apply logic to find appropriate vessel resource, as above
    var snapshotResourceAmount//one of the variables I want to edit within the snapshot
    var newResourceInfo = new UnloadedVesselClass(snapshotResourceAmount);
    listUnloadedVessels.Add(newResourceInfo);
  }
  
  OnUpdate()
  {
    foreach(civilianVessel in listUnloadedVessels){
    //Apply logic to increment resources here; i.e. civilianVesel.resourceAmount++
  }
}

I've tried using the "ref" keyword, but it does not seem to work with Lists.  When I try using an intermediate variable, I store the location of *that* variable instead of the member of the list.  Does anyone have any ideas?

Link to comment
Share on other sites

12 hours ago, Cramblesquash said:

Is it possible to make an opaque Gui like the KSPedia, or Astronaut complex Gui? I want it to be open and closable via the appLauncher from the KSC. I thought it would be possible with this new KSPAsset system, but I just can't get my head round how to do it, and from what I've read, it's not easy to get gui's made in this way to be dynamic, which is something I'm looking for. In an ideal world, I'd actually like to add it to the Administration building as another tab, but I'm guessing this is impossible, or more effort that it's worth.

Any help would be greatly appreciated.

It is possible but as you said it is not easy. As for the "not easy to be dynamic" part it depends what you call dynamics. If it s mostly button and text you re fine. Design your UI, use different names for each item and then you can load it as a bundle (see here for inspiration)

Link to comment
Share on other sites

5 hours ago, sarbian said:

It is possible but as you said it is not easy. As for the "not easy to be dynamic" part it depends what you call dynamics. If it s mostly button and text you re fine. Design your UI, use different names for each item and then you can load it as a bundle (see here for inspiration)

As Sarbian says, if dynamic is just adding set piece elements at run time it's relatively easy to do (you just need multiple prefabs). I wouldn't neccesarily limit that to buttons and text either. It's all just a matter of finding the correct component (which Unity provides plenty of options to do) and setting it up the way you want / adding applicable callbacks.

tl;dr: Identify repeated components, make prefabs of them (I make a class for each prefab to make the C# side obvious)

Link to comment
Share on other sites

Where are the sound effects currently used by the game? I figured it'd be easier to change those files than the configs of every engine mod I have, but I can't find them. Are they accessible?

Link to comment
Share on other sites

29 minutes ago, String Witch said:

Where are the sound effects currently used by the game? I figured it'd be easier to change those files than the configs of every engine mod I have, but I can't find them. Are they accessible?

you mean the files located here? GameData\Squad\Sounds

Edited by wile1411
Link to comment
Share on other sites

13 hours ago, Tralfagar said:

I've tried using the "ref" keyword, but it does not seem to work with Lists.  When I try using an intermediate variable, I store the location of *that* variable instead of the member of the list.  Does anyone have any ideas?

If I understand what you're trying to do, you're trying real hard to get something you already have.  They're already pointers.

For example if you do:

List<PartResource> myres;
 
myres.Add(vessel.parts[7].Resources[0])

myres[0].amount+=1;

Then myres[0] isn't a copy of the original resource; it is the original resource.  In C terms, it's already a pointer to the original resource.  If you modify myres[0].amount, you're really modifying vessel.parts[7].Resources[0].amount.

For a simple example, SafeChute gathers all the part modules from a ship that are parachutes, and then saves them in a List, and then accesses that List to read values from the original part:

https://github.com/henrybauer/SafeChute/blob/master/SafeChute.cs#L113-L119

Closer to what you want to do, TacFuelBalancer's fuel controller builds a "map" of the ship and then caches it.  It then accesses the parts (and their resources) through that map:

Map building:

https://github.com/thewebbooth/TacFuelBalancer/blob/master/Source/FuelBalanceController.cs#L409-L562

Accessing the real parts via the map:

https://github.com/thewebbooth/TacFuelBalancer/blob/master/Source/FuelBalanceController.cs#L598-L617

 

Link to comment
Share on other sites

9 hours ago, wile1411 said:

you mean the files located here? GameData\Squad\Sounds

No; those files aren't actually used by the current build of the game. A number of filenames referenced in engine configs aren't in that folder, besides.

Link to comment
Share on other sites

2 hours ago, String Witch said:

No; those files aren't actually used by the current build of the game. A number of filenames referenced in engine configs aren't in that folder, besides.

You can't as the sounds are distributed as part of the Unity deployed game package. They are not individual files. The EULA prohibits you from touching them. To do what you want to do your only option IS to MM the sounds out of config files and replace them with others that way.

Edited by JPLRepo
Link to comment
Share on other sites

21 hours ago, sarbian said:

It is possible but as you said it is not easy. As for the "not easy to be dynamic" part it depends what you call dynamics. If it s mostly button and text you re fine. Design your UI, use different names for each item and then you can load it as a bundle (see here for inspiration)

Hmmm ok. I would definitely want some of the text to change, certainly at the press of a button, and possibly in real time, and possibly plot a graph (particularly if someone has a neat way of drawing graphs!). I'll have a go with using the asset bundle for now, but give up on that pretty soon if it gives me more grief. My understanding is I can kinda copy old GUI tutorials, just with the re-factoring outlined in the 1.1 conversion thread? Thanks for your help

Link to comment
Share on other sites

Hello, Is there a way to keep a coroutine active when the game is paused (with something like FlightDriver.setPause(True,True) or in the RnD building) ?

I've tried this...

using System.Collections;
using UnityEngine;

namespace corTest {
	[KSPAddon (KSPAddon.Startup.EveryScene, false)]
	public class corTest : MonoBehaviour {
		void Start() {
			StartCoroutine (test ());
			GameEvents.onGamePause.Add (gamePause);
			GameEvents.onGameUnpause.Add (gameUnPause);
			Debug.Log ("Start");
		}
		void OnDestroy() {
			StopCoroutine (test ());
			GameEvents.onGamePause.Remove (gamePause);
			GameEvents.onGameUnpause.Remove (gameUnPause);
			Debug.Log ("OnDestroy");
		}
		IEnumerator test() {
			int i = 0;
			while (true) {
				Debug.Log ("wait ... " + i);
				i++;
				yield return new WaitForSeconds (1);
			}
		}
		void gamePause() {
			Debug.Log ("gamePause");
		}
		void gameUnPause() {
			Debug.Log ("gameUnPause");
		}
	}
}

And the result is:

wait ... 1
wait ... 2
wait ... 3
wait ... 4
wait ... 5
wait ... 6
wait ... 7
gamePause
gameUnPause
wait ... 8
wait ... 9
wait ... 10

I just want to keep counting on my coroutine without the use of Update(). Thanks !

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