Modules.nas: Difference between revisions

Jump to navigation Jump to search
3,047 bytes added ,  24 February 2020
m
(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]].


To avoid loading files needlessly modules.nas was written to allow run-time (re-)loadable Nasal modules, that are loaded on demand only.
== Differences between add-ons and modules ==
 
== 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]].
 
Parts of this functionality were added to the [[Addons|addons]] manager earlier and have now been extracted to avoid code duplication.
 
=== 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.


== Nasal modules ==
Loadable modules 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.


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 '''main''' that will be called on load with either the module object as parameter or the parameters passed to the module.load() 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 '''unload''' that removes any resources which were created by the module.
For this the main.nas shall contain a function {{code|unload()}} that removes any resources which were created by the module.
Exception: setlistener and maketimer are automatically tracked by Module class for you, timers will be stopped automatically, listeners will be removed automatically on unload().
 
'''Example:''' any canvas created by the module should have called its {{code|del()}} method here.


Example: any canvas created by the module should have called its 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:
}}
}}


== HowTo / Examples ==
= Existing modules =
to be written
 
== Existing modules ==
{| class="wikitable"
{| class="wikitable"
|-
|-
246

edits

Navigation menu