Page 1 of 1

JS scope /callng context / multiple instances of 1 oxp

Posted: Sun Aug 10, 2014 11:19 pm
by Lone_Wolf
Below is a copy of a post i made in scripter's Cove in june 2012, https://bb.oolite.space/viewtopic.php?f= ... 91#p175491
I don't see any responses to it, so it may have been overlooked.

The reason i found the problem then may very well be that ShieldCycler appears to be rather unique codingwise and uses 3 types of event handlers (equipment events, shipscript events & worldscript events) .
If you only use 1 of those, you may never encounter the problem.

a few questions :

- have other oxp makers encountered this problem ?

- are there better ways to solve this then my workaround describe below ?

- does current Oolite source-code (1.80 or trunk) still have this problem ?

If the problem still exists, should it be handled in Oolite sourcecode or in OXP code ?

LVV

Lone_Wolf wrote:
The discussion about global namespace
meant that CCL was updated, and that in turns meant i had to update shieldcycler.
A comment (and a pm) from Commander McLane made me look back at older shieldcycler versions.

Before version 0.21.2 i used this.sc_foo for storing/changing values, but found they somehow didn't stick.
It looked like there were multiple instances of those values instead of just 1.
In 0.21.2 & 0.21.3 i tried to solve this by using global variables that were stored in global namespace.
This however didn't solve all problems, so i looked deeper.

Reading up on scope and JS calling context, i realised that when a function was called the caller determined whether a new context was created or an existing one was used.
Looking into oolite code, it appeared that there were different contexts created for :
equipment script events, ship script events and worldscript events.

Even though the handlers for these events were all in the same script, they didn't share the same context.

I found that calling a function as a method is the easiest way to ensure what the execution context will be at runtime.
In ShieldCycler 0.30.1 i created dummy event handlers like this in a separate script :

Code: Select all

this.activated = function()
  {
    worldScripts["Shield Cycler"].sc_activated();
  };
This effectively means all event handlers shieldcycler uses are now worldscript events and share the same context, thus solving the problems.


The reason i encountered these problems is likely the mix of equipment, shipscript and worldscript events shieldcycler needs to do its job.

I posted this here hoping my findings will be useful for other coders.

Re: JS scope /callng context / multiple instances of 1 oxp

Posted: Mon Aug 11, 2014 6:44 am
by cim
If you're mixing world and equipment scripts then yes, it may often be easiest to have the equipment scripts call worldscript methods and do very little themselves, if the two scripts need to share state. Most of my equipment OXPs do it that way.

Multiple instances of the same script source file don't share state or variables: a single ship script may be independently used on hundreds of entities, each doing their own thing. Using the same JS source file as a worldscript and as a ship script, or an equipment script and a condition script is legal, and technically works in that it will compile twice and get the appropriate context each time, but is likely to end up confusing: much better to have separate files in Scripts for each case.

Worldscripts are able to act as the player's - and only the player's - ship script. Any event that the player's ship script receives, all worldscripts also receive. This means ship scripts attached to the player ship are very rarely needed: only if there's something specific to that exact ship class that needs scripting. If you're dealing with shields, then you shouldn't usually need a ship script at all (since NPCs don't have them).

Re: JS scope /callng context / multiple instances of 1 oxp

Posted: Mon Aug 11, 2014 11:54 am
by Lone_Wolf
Thanks, Cim .

That clarifies it's not in a problem in the sourceode, but a consequence of how js works.

The shipscript events i use are currently used only for player ships of any type that have a SC device installed.
In a future version (don't hold your breath) i hope to be able to give npcs Shield Cycler devices.

With current setup I do need some settings to share state between scripts, but there may be another way to achieve that.

let's say i have these scripts :
A - worldscript that does all the work and holds the 'shared state' objects
W - worldscript that handles worldscript events
S - deals with shipscript events
E - deals with equipment events
C - condition script

W, S, E & C could then access the 'shared state' objects in A directly, or call specific functions in A using parameters to set them.

Looks like it's time to analyse the structure of my code.

Re: JS scope /callng context / multiple instances of 1 oxp

Posted: Mon Aug 11, 2014 4:11 pm
by Lone_Wolf
I've looked at things and what i described above should be doable.

I've got a related question to which i can't find the answer :

suppose i got 2 scripts in the Scripts folder :
x.js & y.js

x.js is mentioned in worldscripts.plist , y.js isn't.

In x.js i want to call a function in y.js .

Is this possible with oolite JS ?

If not possible, are there drawbacks to having lots of worldscripts in an oxp ?

(i expect future versions of SC may have a dozen or more script files)

Re: JS scope /callng context / multiple instances of 1 oxp

Posted: Mon Aug 11, 2014 4:53 pm
by cim
Lone_Wolf wrote:
W, S, E & C could then access the 'shared state' objects in A directly, or call specific functions in A using parameters to set them.
Yes. In this particular case A and W could probably be the same world script without particular problems.
Lone_Wolf wrote:
In x.js i want to call a function in y.js .

Is this possible with oolite JS ?
It depends what sort of script y.js is. If it's just a loose file in the folder not referenced anywhere else, no. If it's a ship script, and you know which ship it's attached to - let's say you have it in the thatShip variable, then you can do thatShip.script.theFunction(). If it's an equipment or condition script, you can call the functions from a world script in theory, but in practice it's a bit fiddly to do and you probably should find another approach.
Lone_Wolf wrote:
If not possible, are there drawbacks to having lots of worldscripts in an oxp ?
Every time there's a world script event, or a ship script event affecting the player ship, Oolite will check to see if every world script has the relevant event handler defined, and if so call those event handlers in some order.

So ... drawback 1: it's very slightly less efficient (but only slightly: the time taken to not call a non-existent event handler is not worth worrying about). Drawback 2: if you have the same event handler defined in both world scripts, you can't control which order the things in them happen in - whereas if you'd put the code into a single handler in one world script, you could ... but if the order of execution matters, they probably shouldn't be independent world scripts.

In theory, you very rarely absolutely need more than one world script in any OXP - there are a few things where you get one X per world script and might need two or more at once, but they're rare - but in practice splitting distinct components up can make your code a lot more maintainable (e.g. Oolite has one world script for each built-in mission, not one world script handling all the missions, even though the way the missions are set up a single world script could theoretically do the lot)

Re: JS scope /callng context / multiple instances of 1 oxp

Posted: Tue Aug 12, 2014 10:20 am
by Lone_Wolf
Thanks for the explanation, Cim.

My JS code is slowly beginning to look more like the modular OOP code i wrote in Delphi/Pascal in the 90's .

Re: JS scope /callng context / multiple instances of 1 oxp

Posted: Tue Aug 12, 2014 10:29 pm
by Tricky
Lone_Wolf wrote:
Thanks for the explanation, Cim.

My JS code is slowly beginning to look more like the modular OOP code i wrote in Delphi/Pascal in the 90's .
Heh! My identifiers for OXZs resemble Java package naming schemes since I started making plugins for Minecraft/Bukkit, rather than the example ‘oolite.oxp.Username.OXPName’

As for scope, say no more... :P

Re: JS scope /callng context / multiple instances of 1 oxp

Posted: Wed Aug 13, 2014 10:11 am
by Switeck
I have encountered a similar problem with my unfinished OXPs that attempt to pass variable values around between script sections.

I wanted to use this.variable_name format for the variables, but found many ended up being NAN.
This was the case even though everything was in the same .js script file.

So I resorted to using global variable names instead, though I did minimize how many variables I used as such.

this.ship type variables, which contain details for an npc ship or even a ship group, are particularly tricky for me -- since there might be multiple instances on that ship or ship group in the same system at once. A worldscript may see them as undefined even if it created them on a previous call to its script.

I also encountered this problem as well with a beta version of Thargoid's Swarm OXP:
http://wiki.alioth.net/index.php/Swarm_OXP
https://bb.oolite.space/viewtopic.php?f=4&t=11452

Re: JS scope /callng context / multiple instances of 1 oxp

Posted: Wed Aug 13, 2014 1:17 pm
by Lone_Wolf
Switeck, i skimmed that thread, and it does like it may be the same problem i had.

A simple way to determine if different execution context is the problem cause might be to log the value of this
in several places, along with a short description that clarifies which part of the code is doing the logging.

Re: JS scope /callng context / multiple instances of 1 oxp

Posted: Thu Aug 14, 2014 2:58 am
by Switeck
I've added 20 logging lines to catch 1 elusive bug...only to figure out the problem via some other means. :oops:

One probably GOOD programming habit I've gotten thanks to Oolite OXPs is adding this.name to the logging, like so:
log(this.name,"Precheck variable count: "+x,y,z);

That will print in the Latest.log the script.js filename of the OXP...usually enough info to track it down.

It's very easy to have an obnoxious amount of logging just due to a particular script firing off many times per minute, so usually I remark out some of the logging while checking other things.