Difference between revisions of "Handling OXP Dependencies with JavaScript"
Line 8: | Line 8: | ||
== Solution == |
== Solution == |
||
− | Oolite 1.74 removed the automatic calling of OXPs' reset functions (making this.reset redundant) and reloads all functions from the cache when the player dies before calling all the OXPs' startUp functions. |
+ | Oolite 1.74 removed the automatic calling of OXPs' reset functions (making this.reset redundant) and reloads all functions from the cache when the player dies before calling all the OXPs' startUp functions. |
+ | |||
+ | This allows for an OXP to call a dependency OXP's startUp function before it does its own initialising, provided it hasn't been run yet. Then have that startUp function deleted so it can't be called again until the player dies. (Please note a JavaScript function deleted whilst it is in the middle of being called, <i>that includes subfunctions</i>, will still be fully executed.) This method then works through the dependency chain until all dependencies are initialised. |
||
+ | |||
+ | As an example if OXP_A depends on OXP_B which depends on OXP_C it doesn't matter what order the OXPs get called in: |
||
Order: OXP_A, OXP_C, OXP_B.<br/> |
Order: OXP_A, OXP_C, OXP_B.<br/> |
||
Line 21: | Line 21: | ||
The result is that the three OXP's have been initialised in the right order. If you tried this with different OXP order the results will be the same. |
The result is that the three OXP's have been initialised in the right order. If you tried this with different OXP order the results will be the same. |
||
− | If the dependency OXP is not using this method you must delete that's startUp function yourself after you have called its startUp function. This may have a subtle knock on effect if that dependency OXP has it own dependencies that are handled in some other way. If that is the case may find that you need to include those OXPs as dependencies of your OXP and call them first. |
+ | If the dependency OXP is not using this method you must delete that's startUp function yourself after you have called its startUp function. This may have a subtle knock on effect if that dependency OXP has it own dependencies that are handled in some other way. If that is the case may find that you need to include those OXPs as dependencies of your OXP and call them first. |
Well that's the theory, so here is what a OXP template using this idea should looks like in practice: |
Well that's the theory, so here is what a OXP template using this idea should looks like in practice: |
Revision as of 02:53, 6 November 2010
The Issue
One of the common issues with OXP development is that many OXPs are dependent on the existence of other OXPs. If they only need to exist side by side this isn't a big problem and the player only has to make sure all necessary OXPs are installed. If you need another OXP to be initialised first (have it's startUp function called first) you will run into a problem. An example of this would be if say your OXP randomly added a station above some moons in some systems. If your OXP ran before the OXP(s) that created the moons then it may place the stations differently or not at all compared to if it ran later or last.
This problem arises because Oolite does not guarantee the order in which the OXPs are load. Some version do load in alphabetical order but that isn't true across the board. There have been solution to this problem by running the initialisation code from a timer instead of the this.startUp function but this method is not good for multiple dependency issues. Since version 1.74 of Oolite there has been a much simpler solution.
Solution
Oolite 1.74 removed the automatic calling of OXPs' reset functions (making this.reset redundant) and reloads all functions from the cache when the player dies before calling all the OXPs' startUp functions.
This allows for an OXP to call a dependency OXP's startUp function before it does its own initialising, provided it hasn't been run yet. Then have that startUp function deleted so it can't be called again until the player dies. (Please note a JavaScript function deleted whilst it is in the middle of being called, that includes subfunctions, will still be fully executed.) This method then works through the dependency chain until all dependencies are initialised.
As an example if OXP_A depends on OXP_B which depends on OXP_C it doesn't matter what order the OXPs get called in:
Order: OXP_A, OXP_C, OXP_B.
- OXP_A's startUp is called first.
- It deletes itself and then looks for the existence of OXP_B's startUp function and as it exists calls it.
- OXP_B's startUp now it has been called first deletes itself and then looks for the existence of OXP_C's startUp. As it exists and it in turn is called.
- OXP_C first deletes itself and then executes its initialisation code and returns execution back to OXP_B's startUp.
- OXP_B now executes its initialisation code and returns execution back to OXP_A's startUp where it gets to execute its initialisation code. This was all within the call to OXP_A.
- Now Oolite will want to call OXP_C's startUp as it is next in its list. As it has already been called and deleted it won't find it and will skip it.
- Finally Oolite will try to call OXP_B's startUp function which also has been deleted and will also therefore be skipped.
The result is that the three OXP's have been initialised in the right order. If you tried this with different OXP order the results will be the same.
If the dependency OXP is not using this method you must delete that's startUp function yourself after you have called its startUp function. This may have a subtle knock on effect if that dependency OXP has it own dependencies that are handled in some other way. If that is the case may find that you need to include those OXPs as dependencies of your OXP and call them first.
Well that's the theory, so here is what a OXP template using this idea should looks like in practice:
this.name = "Oxp_A"; this.author = "Author"; this.copyright = "license"; this.description = "Some Description"; this.version = "1.0 alpha 1"; this.startUp = function() { delete this.startUp; // Must be done straight away to prevent loops. It doesn't stop this function from working. // For handling oxps that are written using this template: if (worldScripts.Oxp_B.startUp) worldScripts.Oxp_B.startUp(); // Calls Oxp_B.startUp as it is required to load before your Oxp_A. // Repeat above for each OXP that this OXP depends on. // For handling old oxps that are NOT written using this template: if (worldScripts.Oxp_Old.startUp) { worldScripts.Oxp_Old.startUp(); // Calls Oxp_Old.startUp as it is required to load before your Oxp_A. delete worldScripts.Oxp_Old.startUp; // You do this as Oxp_Old can't. } // Repeat above for each old OXP that this OXP depends on. // Test for existence of an OXP that isn't required but effects the way this OXP works. if (worldScripts["Oxp_X"]) { this.Oxp_X_Exists = true; } else { this.Oxp_X_Exists = false; } // Repeat above for other similar OXP checks. // Do initialisation stuff here as required log(this.name + " " + this.version +" loaded."); // This goes last so the load messages appear the sensible order. } // The rest of OXP goes here
Replace the OXP names, repeat the sections and fill the rest of the code as needed.