Jump to content

How to tell one part from another of the same part


Recommended Posts

So I was working on a GPS mod, reviving the work of @PakledHostage and one of the things I wanted to do was use the toolbar which as far as I know didn't exist when the mod was first conceived.

Work went well and I got it working as desired, including changing textures on the button depending on if the active vessel had a receiver, if it was on or off, and if there were enough sats in "view" to get a position fix (min of 4 are required in the original mod).

All was good.... until....

I introduced more then one receiver, and now the code gets crossed up a borked, breaking in various ways.  It's easy enough to put in conditional statements that as "is this the active vessel", but what I can't get a handle on is what to do if there are multiple GPS receivers on the same active vessel.  If for instance I place 2 or more in symmetry in the VAB or SPH.

If anyone who, unlike me, actually KNOWS what they are doing... the code is here https://github.com/TedThompson/KerbalGPS - I'm hoping to have the game mechanics basically use ONE of the receivers and ignore rest - somewhat like MechJeb does. I took a look at it's source, but that code is so huge and convoluted I can't make head nor tails of it...

TIA - hope here some ideas.

Link to comment
Share on other sites

For max single instance per vessel, I'd be heading for a vessel module rather than a part module. Search the vessel for your part module in Start() and run if found. (The part module basically becomes a marker)

If >1 instance is required for some reason, you want to do the same search in each Part module. Something like the following will do

function bool IsMaster()
{
  MyModule module;
  // going through the parts list in order
  foreach (Part p in vessel.parts)
  {
    // going through the modules on this part in order
    module = p.Modules.GetModule<MyModule>();
    if (module != null)
    {
      // first module found, is this me? if so, I'm the master
      return module == this;
    }
  }
}

tl;dr
The first module found in the vessel heirachy will be the master unit on the vessel. The order of the list of parts on a vessel, and the order of list of modules in a part is the same for every module, so every module finds the same first module.

Edited by Crzyrndm
Link to comment
Share on other sites

Two things to add to what @Crzyrndm posted:

1) Trusting KSP to keep parts in the same order on a vessel and same order of partModules on a part is fragile. It works most of the time but Docking/Undocking a vessel changes the root part and there is no guarantee the root part will stay the same. It generally does, but it isn't guaranteed. Crzyrndm's code example works in this case because you are not saving data, just finding a part on the vessel and since all parts will have the same data which one is found doesn't matter.

2) If the player adds or removes mods, the order of partModules changes on a part. This isn't an issue as long as you search for your partModule by name, not index number. Crzyrndm's example uses the name so it is okay for when you have a single instance of the part module on a part. If you have multiple instances of the same partModule you need to return a list and work with that. I personally use p.Modules.OfType<MyModule>() to do so, although if Squad implemented a GetModules() method that would work as well.

To directly answer your question in the first post, the Part.flightID value is a random ID number assigned to the part when it spawns in flight mode for the first time. It is explicitly persistent and can be used across save/loads to uniquely identify the part and will never change. (At least in stock, there might be a mod out there that changes it but I am not aware of any.)

D.

Edited by Diazo
Link to comment
Share on other sites

Docs suggest that foreach is to be avoided at all costs, so that and some pondering lead me to this

public class KerbalGPS : PartModule
{
		...		

		public static List<KerbalGPS> GPSReceivers = new List<KerbalGPS>();
        
        ...
        
		public override void OnStart(StartState state)
        {
            if (!GPSReceivers.Contains(this))
            {
                GPSReceivers.Add(this);
            }
            
            base.OnStart(state);
        }

        public void CleanUp()
        {
            if (GPSReceivers.Contains(this))
            {
                GPSReceivers.Remove(this);
            }
        }
}

Then I check via if (GPSReveivers.IndexOf(this) == 0) to only respond to the first part.  Works for 2 or more on one vessel, not so good for 2 vessels in the same area (like one on the pad and one on the runway) - so I still need to sort that out... 

Edited by tg626
Trying and failing to stop the forum from ****ing up the formatting of the code... >:(
Link to comment
Share on other sites

The foreach loop only needs to be avoided in Unity's scripting editor due to an issue with how the older version of Mono that Unity uses compiles.

If you use a stand alone code compiler, such as Visual Studio (Windows) or a newer version of Mono (Linux), there is no issue with foreach loops.

As for the current vessel, that's what the FlightGlobals.ActiveStatic is for, to be used as a check for that.

Citation here.

D.

Edited by Diazo
Link to comment
Share on other sites

I'm going to look at that "FlightGlobals.ActiveStatic" but meanwhile - this seems to be crashing the game, HARD, total freeze then CTD with nothing in the log.

        private bool checkMaster()
        {
            if (this.vessel.isActiveVessel)
            {
                for (int i = 0; i < GPSReceivers.Count; i = i++)
                {
                    if (GPSReceivers[i].vessel.isActiveVessel)
                    {
                        gpsMaster = i;
                        print("[KerbalGPS] Master found at index " + gpsMaster + " (" + GPSReceivers.IndexOf(GPSReceivers[i]) + ")");
                        break;
                    }
                }
                if (GPSReceivers.IndexOf(this) == gpsMaster && GPSReceivers[gpsMaster].vessel.isActiveVessel)
                {
                    print("[KerbalGPS] I *AM* the master! (" + gpsMaster + ")");
                    return true;
                }
                print("[KerbalGPS] No master found... OMGWTF?");
                return false;
            }
            print("[KerbalGPS] Dormant Vessel, so nevermind...");
            return false;
        }

 Works fine on ONE craft, but as soon as I hit "[" or "]" to switch to another craft CRASH!  Log ends with:

[LOG 14:59:37.420] [FLIGHT GLOBALS]: Switching To Vessel KC4-GPS ---------------------- 
[LOG 14:59:37.423] Packing Untitled Space Craft for orbit
[LOG 14:59:37.425] [PlanetariumCamera]: Focus: KC4-GPS
[LOG 14:59:37.433] Camera Mode: AUTO
[LOG 14:59:37.457] [CHATR] Capsule starts the exchange...
[LOG 14:59:37.485] [KerbalGPS] Dormant Vessel, so nevermind...
[LOG 14:59:37.486] [KerbalGPS] Dormant Vessel, so nevermind...

There are 3 receivers in on the ground at KSC, 2 on one craft, which is where I started, and 1 on a plane (KC4-GPS).

Link to comment
Share on other sites

In the end, that was a waste of time, however since I do use visual studio, I'll use the foreach.

That said, the MyModule module; line confuses me.  I assume MyModule is supposed to be the part module of my parts.  Which is KerbalGPS as in: 

MODULE
{
name = KerbalGPS
...
...
}
 
In which case, should it be
KerbalGPS module;. ?  Because that makes no sense (ergo, I assume I am not understanding...)

 

Link to comment
Share on other sites

Yes, you want to replace MyModule with KerbalGPS in these examples. KerbalGPS is your class TYPE (C# keyword you can google).

So what those lines of code are doing is saying is find the first INSTANCE (C# keyword) of KerbalGPS on this part (Cryrndm's example), or find all instances (my example) of KerbinGPS on this part.

That is what ModuleManager does for you, when you make a patch telling MM to add KerbalGPS to a part, KSP sees that and adds an instance of the KerbalGPS class type to that part.

Hope that clarifies things.

D.

Edited by Diazo
Link to comment
Share on other sites

It did - for some reason that statement looked backwards to me, but I came up with this:

        private bool checkMaster()
        {
            KerbalGPS module;
            if (vessel.isActiveVessel)
            {
                foreach (Part part in vessel.parts)
                {
                    module = part.Modules.GetModule<KerbalGPS>();
                    if (module != null)
                    {
                        return module == this;
                    }
                }
            }
            return false;
        }

And that seems to do the trick, so THANK YOU BOTH!!

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

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