Jump to content

PartModule - initialization sequence and order of events, interlinking modules


Recommended Posts

Hi all,

I am attempting to find the best way to utilize order of operations to cleanly link two partModules together. Most of the time this relies on a two-pass operation; everything is created/initialized/added during the first pass, and during the second-pass things are linked together. However, in KSP, there is only a single (inconsistent) loading pass. You are never guaranteed to have both OnLoad and OnStart called on your part at all initialization places -- often only one or the other is called (or perhaps onStart is always called, and OnLoad only inconsistently), and even then they are both called consecutively rather than after all other parts have the first pass done.

It seems my problem mostly stems from the way that KSP initializes partModules. It goes through and -fully- loads each module before proceeding to the next. There is no second pass /method call to the modules once they have all been loaded -- which is exactly what I need.

My current solution is to declare controlled modules prior to the modules that control them. E.g. an externally controlled texture-switch module needs to be listed -before- the module that controls it int he part .cfg file. However this is ugly in the config (requires modules listed in very specific order), error prone, and nigh-impossible to patch properly. So, I'm investigating 'proper' solutions.

I need to know when -all- modules for a part have been loaded and fully initialized so that my various PartModules can go and look for whatever other module they are supposed to be interacting with and do their initialization/data synching. Is this information available somewhere? Are their Unity lifecycle methods that I could use? What other methods are available to know when a part has had all its modules added but is not yet started (e.g. has not yet received update ticks)?.

I cannot wait until first update tick, as I need everything to be setup and fully initialized by then (as other PartModules that are not under my control will start doing their thing immediately and may be called before the first tick on my modules, I need to ensure that everything is setup -before- they start doing their things/receive their first update tick).

Is anyone aware of possible solutions or have I overlooked something, or is this just not cleanly workable in KSP?

Really, I just need proper order-independent loading of interlinked modules. E.g ModuleA controls ModuleB, but moduleA is defined first in the config. Currently if I try this, moduleB will be null when moduleA is doing its OnStart/OnLoad code (as the part loads them one at a time, moduleB does not exist yet), and the whole world implodes.

My last resort (and its a bit of a hack...) is to add a dummy PartModule to the end of the config file whose sole purpose is to inform all other partModules (that have a specific interface) when the loading sequence is done. However I have not yet tried this out, and it seems like it might suffer some of the same probems (order of modules determining if things explode/crash, lack of easy patchability). I am also investigating various GameEvents, and perhaps a custom solution using message passing (PartMessage? have not played with it yet...). Edit -- PartMessage is apparently a KSPAPIExtensions thing; so no-go for my purposes.

Other lifecycle events -

Awake - Unity lifecycle - no clue when it is called for the PartModule; but from Unity's charts it looks like it would be called -before- the OnStart/etc methods; so called too early to be useful

OnAwake - KSP PartModule method. Called directly from the unity method. So, same problems.

Start - Unity says it is called before the first FixedUpdate/Update call. But then it goes and tells me that no guarantees are made when instantiated during run-time. Also no idea if it is called on each script as it is instantiated or called on all 'uninitialized' scripts prior to FixedUpdate.

More Edit --

Further investigation tells me that many of the stock 'interlinked' modules (such as ModuleSurfaceFX) load their inter-linking in the Unity derived Start() method. I have not personally investigated this to see when/how it is called (and cannot for awhile, as I'm stuck at work for most of the rest of the day). So, there might be some hope there... will continue looking around.

Edited by Shadowmage
Link to comment
Share on other sites

Okay, first my understanding of the Methods:

Awake/OnAwake: Called upon partModule creation somewhere early in the load process. KSP actually uses one of these (Awake I think?) and overriding it causes issues. As a rule I don't use these.

Start/OnStart: Called when an instance of a partModule is created on a part. While this happens on flight scene start for the FlightGlobals.ActiveVessel, note that it can happen anytime during the flight scene when a non-focus vessel loads upon coming into physics range. My preferred place for setup type code to run.

Now, the description of your issue sounds like it might be worth looking at the VesselModule class. While you can't save/load data to it easily, it sounds exactly like what you need in terms of a vessel over-arching class reference.

The bonus is that because it is not a partModule, the VesselModule will always be created before the partModules load, or always created afterwards depending on which order Squad programmed things in. (A quick test would tell you the order.)

If the VesselModule is created first, it will exist in order for the PartModules to reference it, if the partModules load first you can foreach the part.Modules list in the VesselModule OnStart() method to do your linking.

D.

Link to comment
Share on other sites

Diazo, brilliant as always :)

I had not thought about a VesselModule (in fact, I had completely forgotten these even existed).

From a quick look at things, it appears that it might just fill the need that I was looking for. As near as I can tell they are created and added to the -vessel- after it is fully initialized (e.g. all of the parts have had their OnLoad/OnStart methods called already). So that fills the exact function I was looking for, even if it is a bit of a workaround.

As I've never used these before, I'm not quite sure how they should be registered/added to the vessel. My code-exploring though tells me that merely having the class present in the a loaded assembly should cause the VesselModuleManager to instantiate that class and add it to the vessel (as long as it derives from VesselModule). Does that seem about right?

Seems kind of silly that any mod that needs this kind of callback functionality would need to add their own vesselModule for it; but I'm unsure as to the best way to approach the Squad devs to get the modding API improved. Kind of seems like most of KAE should also be merged into the stock API; so much stuff there that could be useful for everyone... sadly it is limited to an external hard-dependency, which limits/prevents many mods from using it (such as mine).

Thanks again for the ideas and insight

Link to comment
Share on other sites

You are correct, any class that inherits from VesselModule will have a single copy of that class added to every vessel in the game.

I'm pretty sure it works the same as the Vessel.id, so every time a new vessel spawns (such as an undocking), a new instance of the VesselModule class is created and added to that vessel.

Now, referencing that VesselModule is tricky, I've never found a reference to a list of VesselModules on a vessel in the Vessel class so I've had to find the GameObject directly using Unity's functions.

Actually, this is worth a read http://forum.kerbalspaceprogram.com/threads/126229-Vessel-Modules-Discussion

It's from when VesselModules first came out and we were looking into their capabilities.

D.

Link to comment
Share on other sites

Just for reference, here's the full life cycle of a PartModule:

  • Part prefab is created and PartModule is added to it
  • OnAwake() is called upon creation
  • OnLoad() is called with data from GameDatabase
  • Part prefab is instantiated to create an actual part - this is done by serializing and deserializing
  • OnAwake() is called
  • If part is being loaded from a vessel, OnLoad() is called with data from save or craft file
  • OnStart() is called on all PartModules for a particular part
  • Start() is called - OnStart() is called first because it's trigged by the Start() method of the part itself
  • Usual update loop happens. Unity functions Update(), FixedUpdate(), LateUpdate() are called if active=true. OnUpdate() and OnFixedUpdate() are called by the part if isEnabled=true
  • Part is removed and OnDestroy() is called

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