246
edits
m (→setMainFile()) |
|||
(14 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
= Nasal runtime loadable modules = | = Nasal runtime re-loadable modules = | ||
== Introduction and motivation == | == Introduction and motivation == | ||
Nasal (Flightgears integrated scripting language) comes with a limited number of [[Nasal library|core functions]]. | Nasal (Flightgears integrated scripting language) comes with a limited number of [[Nasal library|core functions]]. | ||
Line 6: | Line 5: | ||
The latter are stored in the [[Fgdata]] repository under /Nasal and loaded automatically by FlightGear. | The latter are stored in the [[Fgdata]] repository under /Nasal and loaded automatically by FlightGear. | ||
Optional code, which is loaded only on demand, can be handled either by the legacy module system implemented in C++ or by modules.nas if run-time re-load support is required. | |||
For more information on how and when these files are loaded see [[Nasal Initialization]]. | For more information on how and when these files are loaded see [[Nasal Initialization]]. | ||
== Differences between add-ons and modules == | |||
While there are many similarities between add-ons and modules, some words on the differences: | While there are many similarities between add-ons and modules, some words on the differences: | ||
Line 26: | Line 18: | ||
Add-ons are selected by the user before launching FlightGear, thus they may or may not be available at runtime. | Add-ons are selected by the user before launching FlightGear, thus they may or may not be available at runtime. | ||
A module must contain at least the file '''main.nas''' (default, other filename is possible) | = HowTo / Examples = | ||
First some examples, the API documentation follows below. | |||
== Nasal modules in an aircraft (e.g. for development) == | |||
Nasal code for an aircraft is usually loaded by a XML declaration in the <aircraft>-set.xml file like this: | |||
<syntaxhighlight lang="xml"> | |||
<PropertyList> | |||
<nasal> | |||
<foo> <!-- Nasal namespace foo --> | |||
<file>Nasal/foo.nas</file> | |||
</foo> | |||
<bar> <!-- Nasal namespace bar --> | |||
<file>Aircraft/Generic/foo.nas</file> | |||
</bar> | |||
<myAircraft> <!-- Nasal namespace myAircraft--> | |||
<file>Nasal/efis_module.nas</file> | |||
</myAircraft> | |||
</nasal> | |||
</PropertyList> | |||
</syntaxhighlight> | |||
This is fine unless you are developing a Nasal subsystem for the aircraft and have to restart FlightGear every time you edit a few lines. | |||
For this case you could consider to use the Module class (defined in FGDATA/Nasal/modules.nas) and make your code re-loadable at runtime, | |||
which means you do not have to restart whole FlightGear just for a few lines of edited Nasal code. | |||
'''Example Nasal/efis_module.nas:''' | |||
<syntaxhighlight lang="nasal"> | |||
#-- load EFIS as reloadable module | |||
var my_efis = modules.Module.new("myAircraft_EFIS"); # Module name | |||
my_efis.setDebug(1); # 0=(mostly) silent; 1=print setlistener and maketimer calls to console; 2=print also each listener hit, be very careful with this! | |||
my_efis.setFilePath(getprop("/sim/aircraft-dir")~"/Nasal/EFIS"); | |||
my_efis.setMainFile("myAircraft-efis.nas"); | |||
my_efis.load(); | |||
</syntaxhighlight> | |||
Now add a menu item for easy reloading like this: | |||
{{note| | |||
The module name in the property path must match the name passed to modules.Module.new() | |||
|margin=10px |width=50%}} | |||
<syntaxhighlight lang="xml"> | |||
<menubar> | |||
<default> | |||
<menu n=100> | |||
<!-- normal aircraft menu --> | |||
</menu> | |||
<menu n=101> | |||
<label>Aircraft Development</label> | |||
<item> | |||
<label>Reload EFIS</label> | |||
<binding> | |||
<command>property-assign</command> | |||
<property>/nasal/modules/myAircraft_EFIS/reload</property> | |||
<value>1</value> | |||
</binding> | |||
</item> | |||
</menu> | |||
</default> | |||
</menubar> | |||
</syntaxhighlight> | |||
{{tip| | |||
You can also call {{code| myAircraft.my_efis.reload();}} to trigger the reload. myAircraft is the namespace given in the <aircraft>-set.xml file, my_efis is the module object (see above). | |||
|margin=10px |width=50%}} | |||
= Structure of Nasal reloadable modules = | |||
A module must contain at least the file '''main.nas''' (default, other filename is possible) and should contain the functions {{code|main()}} and {{code|unload()}}. | |||
=== main() function === | === main() function === | ||
The main.nas file shall contain a function | The main.nas file shall contain a function {{code|main()}} that will be called on load with either the module object as parameter or the parameters passed to the {{code|module.load()}} function. | ||
=== unload() function === | === unload() function === | ||
If you want the module to be re-/un-loadable, you must make sure to track resources and remove them on onload. | If you want the module to be re-/un-loadable, you must make sure to track resources and remove them on onload. | ||
For this the main.nas shall contain a function | For this the main.nas shall contain a function {{code|unload()}} that removes any resources which were created by the module. | ||
'''Example:''' any canvas created by the module should have called its {{code|del()}} method here. | |||
{{note|Calls to {{code|setlistener()}} and {{code|maketimer()}} are automatically tracked by the Module class for you, timers will be stopped automatically, listeners will be removed automatically on {{code|unload()}}.}} | |||
=== Rules for Module names === | === Rules for Module names === | ||
Line 47: | Line 101: | ||
# The name must contain at least one letter so it cannot be confused with a number | # The name must contain at least one letter so it cannot be confused with a number | ||
# The name shall not match any existing .nas file or directory in FGDATA/Nasal to avoid namespace clashes | # The name shall not match any existing .nas file or directory in FGDATA/Nasal to avoid namespace clashes | ||
{{note| | |||
Reloadable modules shipped with FlightGear are stored in subdirectories of FGDATA/Nasal/modules/ as these subdirectories will not be loaded automatically by Flightgear on start. | |||
However, modules.nas will scan this directory to create a list of available modules (list of subdirectories). | |||
Each module correspondes to one subdirectory and the direcory name is also the module name. | |||
}} | |||
= modules.nas = | |||
In ''modules.nas'' the class ''Module'' is defined. | |||
A module object holds information about the path and filename of the Nasal script and supports unloading and reloading the code at runtime | |||
(e.g. without restarting Flightgear as a whole) by tracking some critical resources like [[Listeners|listeners]] and [[Timers|timers]]. | |||
{{note|Parts of this functionality were added to the [[Addons|addons]] manager earlier and have now been extracted to avoid code duplication.}} | |||
{{caution| Within a module {{code|setlistener()}} is overloaded and the default value for the 4th argument (runtime) is changed to 0, so the listener will run only if the property value has changed. | |||
}} | |||
== library functions == | == library functions == | ||
Line 68: | Line 138: | ||
|param1text = The name of the module (=subdirectory in Nasal/modules) to load. | |param1text = The name of the module (=subdirectory in Nasal/modules) to load. | ||
|param2 = debug | |param2 = debug | ||
|param2text = Defaults to 1 (true), use 0 to disable debug. | |param2text = Defaults to 1 (true), use 0 to disable debug. If debug > 1, each listener hit will be printed, be careful not to listen to rapidly changing props, do not set runtime=1 in setlistener(). | ||
|example1 = | |example1 = | ||
var debug = 1; | var debug = 1; | ||
Line 151: | Line 221: | ||
}} | }} | ||
= Existing modules = | |||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- |
edits