Modernizing FlightGear Scripting
|This article is a stub. You can help the wiki by|
|Memory Management (GC)|
See Nasal success stories for the main article about this subject.
FlightGear has built-in scripting capabilities using a custom ECMAScript-based C-like language called Nasal.
FlightGear needed a built-in scripting language, and it has one. A compact, clean, elegant and fast one. In total there are at the moment more than 170.000 lines of Nasal in *.nas files and a few thousands embedded in joystick drivers, dialog description files, model animation files, keyboard.xml, mice.xml and several other files. Extension functions interface perfectly to the property tree, the event manager, the built-in XML parser etc. Nasal is very tightly integrated in fgfs and used all over the place. 
Over the last couple of years, we've seen some recurring debates in response to requests to provide support for additional scripting languages, possibly to replace Nasal scripting in its entirety sooner or later, i.e. "ditch" Nasal in favor of a more established, more mainstream, language like Python or Lua, with better tooling, support and documentation available for end-users, developers and other contributors.
Certainly a lot of people have become pretty familiar with Nasal's features in FlightGear, but this alone doesn't automatically imply that it's the best _possible_ solution. At the time when Nasal was introduced into FlightGear it certainly had been the best solution _available_. But hey, if Andy Ross or someone else would have had provided the close integration of any different, cleverly designed scripting language XY into FlightGear, then everyone would now happily script in XY and nobody would care about Nasal. This is what the discussion is about - or at least was meant to be. Currently there is no choice, if you'd like to script in FlightGear, then you'll have to use Nasal. It remains to be seen how many people are going to jump onto another scripting language if they had the choice to experience the difference.
thinking purely on language syntax + interpreter engine footprint + seamless integration with flightgear + language capability/completeness: nasal may still be the overall winner. Nasal is brilliant in a number of ways. But to be fair, those are not the only considerations. Don't be too quick to discount the community of knowledge, the documentation, the library support, ease of installing new libs with new functionality ... there are many reasons to consider a choice. For example: I might take a slightly longer way home from work so I don't have to take a left turn at an uncontrolled intersection during a busy time at rush hour. Technically the first route is shorter, maybe even on average quicker, but perhaps I also consider stress levels and safety as part of the overall equation. That isn't a direct analogy to nasal vs. python, just pointing out that a person's final decision may weigh additional more subjective factors, and that a normal thing that we humans do.
Adding another language wouldn't be that hard. Actually, we had another one before nasal and beside nasal for a while. It was called PSL (plib scripting language), and we ripped it out because Nasal was/is just better and because offering and maintaining two languages it utterly pointless . 
Nasal is *very* well designed, compact, and efficient. It is used heavily throughout many areas of FlightGear. So most core developers can't imagine any scenario where we would switch to some new scripting language unless a lot key developers were convinced that it was every so much better that that benefit would substantially outweigh the cost. And that scenario is hard to imagine.
But all that said, FlightGear is intended to be a developers sandbox, so please feel welcome to play and learn and ask questions. 
There's really nothing (other than cosmetics) wrong with Lua. As you mention, it's grown into a large community with lots of documentation and libraries and professional-looking trappings. None of that was true in 2003/4 when Nasal was in its infancy, but it is now, and I can see why it would be attractive. If you want to do the integration work and maintain it (remember, there's a ton of code outside the interpreter you need to write to be able to do useful things inside the simulator), feel free.
This article is intended to help summarize some of the key points made by various contributors and to help provide a reference (pointers) for people interested in the corresponding debates.
It is also a question of resource optimiation. Mainstream scripting languages have lots of resources online, call it libraries, documentation, or best practices / know how in general. Nasal is naked in this regard. Any improvement to Nasal, either the language or the framework has to come from within the FG project itself, so in practice, it receives marginal attention.
Reinventing the wheel in a project that already relies so much on third party libraries simply makes no sense from an outsider's perspective, as it takes away precious and spare manpower from the crucial bits of the system.
Nasal is de-facto unmaintained and has been unmaintained for several years meanwhile, Nasal is a niche language that is really only used by the FlightGear project, long-standing issues such as its garbage collector, which isn't suitable for a semi-realtime application/simulation like FlightGear, haven't been fixed in years.
In general, there are other, much better maintained, options for embedded scripting these days, such as e.g.:
In addition, documentation and end-user support for more established/mainstream languages is obviously much better than anything we have for the custom FlightGear/Nasal interpreter.
Many people argue that a more standard scripting language (like e.g. Python) would also attract more potential contributors to FlightGear, also the sizable community with 3rd party modules, means that new functionality can be much more easily implemented ("batteries included", and "no yak shaving" needed):
- Python has much better, and much more, end-user documentation (in dozens of languages), which we don't have to write/maintain ourselves
- Python has much better tooling to help with troubleshooting, debugging and profiling code (think IDEs, integrated editors for writing/debuggnig code)
- Python also is being actively developed/maintained by an enormous community of contributors, which is to say we don't have to bother updating it, or fixing bugs in the runtime
- Python is, and will continue to remain, relevant in the software engingeering industry even 30+ years from now (aka, it is more future-safe, and its success/availability is not tied to a single entity/use-case or application)
- alternate/different GC implementations
- JIT compilation/optimization opportunities
- excellent support for threading and running code asynchronously (well, the GIL is there)
- much better accessibility, and much lower barrier to entry: if I need help with Nasal, it's kinda problematic to expect the other party to have the runtime environment installed - whereas Python is a rather common thing these days, and it is kinda hard to imagine systems without a suitable Python implementation
- FlightGear adopting industry solutions, and standards, is likely to become more relevant to professional players/companies/contributors (think being multi-platform, using OpenGL instead of DirectX, striving for OSG/HLA or CIGI adoption)
- FlightGear supporting Python for embedded scripting/extension purposes is more likely to be appealing to educational setups already working with Python (think universities, college, CS/SE courses or aerospace programs)
- With Python, when compared to Nasal, there is an "infinite" supply of people familiar with Python who use it professionally at their day jobs, many of whom are quite willing to use a language that is used in the real world, whereas learning a "niche" language is unlikely to be too appealing to people who need to make a living coding, simply because putting "Nasal experience" on your CV is unlikely to impress anyone (unless you happen to be an ENT doctor...)
Thus, one of the key questions commonly posed is "How coupled are Nasal and the scripting glue in FGFS ? Is there a clean break, or if not, can it be refactored without too much pain into something that would allow end dev users to use whatever scripting language they prefer, or have many modules already writen in, etc. If the coupling is not of the hair pulling type, it might be conceivable to integrate another scripting language alongside Nasal for starters, and in time, completely replace it if one is so inclined "
This wiki article is going to try to provide answers to some of the more commonly asked questions and provide the background to appreciate the challenges given the current situation.
- dependency on a huge number of external libs is considered a bad thing (TM) in FG, hence even if you convince people to implement language XY, you don't get to use all the libs in anything that goes to the repository
- hence you're left with finding an algorithm that does what you want it to, and implementing that algorithm is the thing that takes a long while, not the week spent learning how Python syntax differs from Nasal syntax
- but adding more scripting languages to FG means more complexity and more breaking points - so the benefit for whatever you add should outweigh the problems
Do we want two similar languages embedded in fgfs side by side, so that people can choose? Hell, no! This is just needless bloat ("re-invention of the wheel" is an understatement!) and it would be a source of confusion. On which basis would people decide for one or the other? Would we expect them to evaluate both languages and to find out which works better for them? Or just take a random pick? What would be the advantage? That people who don't like Nasal can have something that's quite similar? Doesn't sound smart. So, having both in FlightGear is clearly not desirable. 
As discussed at https://wiki.python.org/moin/SandboxedPython , it's hard to embed Python without giving the scripts it runs full access to your system; hence, I wouldn't make this available to aircraft/scenery. (Nasal avoids this security problem by having its I/O functions only allow access to a limited range of files.) It could still be good to have available for local experimentation, but should be clearly labelled as insecure.
It is hard to see a benefit having a binary integration of Python in the flightgear core. Using python to get/set properties and run fg-commands is already possible without any c++ integration.
One major concern with the FGPythonSys integration is that it introduces an unpredictable (w.r.t. timing) step in the main loop getting us on step further away from constant frame rates.
Do we in the end want a scripting language that is more widely used just > because it is more widely used (which is a disadvantage if you think of > malicious code), because we hope to re-use other code, because there's > less transition time learning a slightly differently syntax?
If FlightGear is a sandbox on the playground, how do we dictate which toys are allowed in? If we debate this on purely technical merits, then the argument will literally go on forever in the same sense of Mac vs. PC or iphone vs. android. If we look at it from the standpoint of what toys do people wish to bring into the sandbox to play with, then some of these technical arguments and demanding answers to one's personal level of satisfaction seem a little silly. Prove your truck can haul more sand than the one we already have? Prove your shovel and bucket are better than mine? Do you like yours only because it's red plastic and not yellow?
bringing in the Python toy is potentially harmful for the reaons already stated.
Instead of adding just-another-feature we need to strip it down to getting a fast and constand fps rendering engine. Everything else needs to run outside the main loop and has to interact with the core by, say HLA/RTI or whatever IPC we have.
FG intentionally should *not* have a proliferation of addons like FSX - we aim to merge into mainline what is reasonable to aviod having a plethora of mutually incompatible stuff like for other sims. So pointing out that there's no proliferation of addons is not a meaningful measure of success of failure.
porting several hundred thousand lines of Nasal to something else, even if it is automated to a high degree, doesn't sound very attractive..
The paradigm of binding a C/C++ core to a scripting language has its own disadvantages, and that makes it very difficult to switch.
let's keep in mind that despite the current situation with Nasal, many core developers are already concerned that scripting is being used too widely - regardless of Nasal being de-facto under-documented, and hardly maintained - this situation would only get worse once/if a real mainstream language is supported. In summary, it's not like Nasal's syntax is esoteric in any way - it is far more standard than Fortran, Forth, LISP or Scheme - despite those being definitely "more mainstream" than Nasal - and it being close enough to C, C++ and GLSL, also means that people doing FlightGear scripting get a feeling for the syntax that will come in handy later, when looking at other, non-nasal, FlightGear source code. 
FlightGear can't just ditch Nasal. Bringing another scripting language into the fold would certainly be a big undertaking.
It is really important to note that in a small application which is probably 1/10 or 1/100th the sie of FlightGear, this (supporting Python) was a truly massive undertaking to make these changes ... on the order of 2-3 months of pretty much non-stop effort. For FlightGear it would be almost insurmountable. I think it's an interesting topic to discuss in theory. We can point to examples and all the good things that python could bring to the table. However, we can't get around the fact that this could be a multi-year effort to do the conversion in a way that truly modernizes FlightGear and truly leverages all the good aspects of Python ... and during that 'under construction' period of a year or more, most of FlightGear would be broken. Edward came up with an alternative scheme where he built a python interface to the existing C++ property tree and some hooks to run python scripts, but still ... dropping nasal and converting to python requries porting AND carefully testing all the scripts though all their code paths and edge cases. Just that would be a massive undertaking, and it wouldn't be fair to just port the core nasal, and hang all the aircraft and scenery developers out to flap in the wind to figure it out for themselves.
The balance is that Nasal is used a little in the core program, GUI, advanced weather, etc.. and a very lot in aircraft models. Nasal works well enough and removing it would be a massive undertaking. Although many people don't particularly like Nasal it is reasonably efficient and is very adapted to operations within FlightGear. Most contributors simply have yet to see a compelling reason to change; concurrency isn't even relevant until we have a properly threaded core, and garbage collection is not something commonly seen as a problem.
Removing Nasal will break a lot of aircraft models so it's not an option that can be seriously considered without a migration path. Some core developers would argue that there is overuse of Nasal aircraft side but it is what it is and we can't change that.
On replacing Nasal
As far as replacing nasal, that project would be very hard. There are many vital parts of flightgear currently coded in nasal. There are also random bits of nasal code scattered around in joystick configurations, instrument and aircraft models, scenery models... everywhere. It would, in effect, require starting the whole data project over again.
Most developers would prefer we have /one/, and only one scripting language in Flightgear and would be be delighted for that to be a better supported, more widely used language than Nasal, but we don’t want to end up with multiple scripting languages co-existing. I.e if we’re going down the Python route, we need a coherent plan, even if it takes two years, to phase out Nasal. 
You can really only make progress on the Python front if you accept that Nasal is there to stay until Python works reasonably well without causing tons of regressions, absent that, there will be (at least) two co-existing scripting solutions, which is likely to complicate the situation even further - especially in the light of the mess that $FG_ROOT/Nasal and aircraft-side scripts have become, despite Nasal being an unpopular niche language.
Otherwise, supporting Python would only make the shortcomings in FlightGear's legacy architecture even more prominent, quite possibly within just a few months, because Python has a much lower barrier to entry, so that people will be able to create all sorts of functionality, and in the process continue to clutter the main loop and make it even more non-deterministic than it is already. The first step really is coming up with a mode to entirely disable nasal using something like --disable-nasal, and the next step is to rework the Nasal initialiation sequence, to move away from the hard-coded assumption that the whole system is up and running and that all modules are always loaded - at that point, a non-Nasal mode can make actual progress, because FlightGear would no longer necessarily depend on Nasal - sooner or later, FGNasalSys could be reworked to support multiple instances, so that aircraft scripts can be treated, and executed, separately - i.e. separate from other simulator scripts (think GUI, scenery), and it would become much more straightforward to run certain parts on helper threads, or even introduce private property trees for certain purposes (think one private property tree for the FDM/AP, one for Canvas textures (MFDs)). All of this could greatly benefit from the Phi/mongoose groundwork that Torsten completed, because it would provide for a sane mechanism to get/set properties and run fgcommands, without requiring Nasal inevitably. However, we really don't want to have another scripting interpreter running inside the main loop/thread - that would only add even more work to our todo lists.
The current situation
- If* you want embedded scripting with hooks inside flightgear, and you don't want nasal, then you are probably on your own with that. When we discussed this previously we decided that we didn't want to encourage multiple optional script engines because of the chances people will create aircraft with dependencies that no one else has and also the extra complexity of maintaining multiple script engines in the core code.
We have a single SGSubsystemMgr based FGNasalSys class inherited from SGSubsystem, which implements support for FlightGear scripting (add references/links below):
For aircraft specific functions and for some special cases, it might be necessary to run in sync with the FDM iteration. But almost every system should be able to run completely independently of the frame rate. What of course would be mandatory is to sync properties at well defined time stamps, this is what the RTI takes care of.
Coming up with some kind of unifying platform: A modern launcher UI (maybe even as a webapp) which can launch FlightGear, configure certain stuff (during FG running) and arrange communications with third-party processes implemented in Python or other languages. These would then manage scenarios, AI, maybe even input devices and aircraft systems. Of course, this would need a better integration of remote protocols into FG. The user wouldn't notice anything of the hidden processes, because the launcher/manager takes care of everything, even the spawning and killing of the auxiliary processes.
The http interface that Phi uses is where I'd see a unified API growing from; but someone's got to be interested enough to do this. The main problem with a unifying platform IME is that it sounds ground but really doesn't mean much. Concrete proposals and offers of development assistance are what's needed; but adding another scripting language into the run time isn't a great idea; if anything we need to be able to provide a fully featured API available over HTTP that can be called via any platform. Switching to use Phi for more is probably a good idea; and certainly exposing more of the API is going to permit more things to be done there; but a launcher webapp would obviously require a standalone HTTP server running all the time; anyone fancy writing a FlightGear apache module -)
at the end of the day, the main bottleneck is not the language/interpreter supported by FlightGear, but the way it is integrated - and the legacy SGSubsystem/Mgr approach is simply not suitable for large-scale scripting, certainly not the kind of scripting that would definitely be boosted by supporting a more mainstream language like Lua or Python. Thus, there are things that need to be addressed first, which is why it makes sense to review Richard's postings in this thread, and explore using Torsten's mongoose/Phi approach, i.e. remote properties and async fgcommands to use those as the foundation for a scripting subsystem that runs outside the fgfs main loop.
There is work that lies ahead that would benefit FGPythonSys and FGNasalSys at the same time - such as a strong IPC mechanism, like HLA, or even just Emesary via remote properties (asynchronous remote properties and fgcommands, as per Torsten's mongoose/Phi work) - Torsten basically proved that you don't need to use HLA to come up with async modules that can interface with the rest of FG - if this, his, approach were to be formalied, standardized and extened, many other subsystems could be using this, none of which would be facing the challenges that Nasal/Python are currently facing (or in fact any other SGSubsystem not using SGthread, too).
And I think that's really what Richard is hinting at when he refers to Phi, i.e. its back-end, not the front-end part - and the back-end could, and arguably should, be also shared/used by other UIs, including possibly PUI and/or a Canvas-based GUI. Unfortunately, for the time being, providing an alternative to Nasal, or even replacing it in its entirety would provide you withero benefits, due to the legacy FlightGear architecture, which would require major re-architecting to make this a worthwhile goal.
FGPythonSys could benefit FGNasalSys and vice versa - but for most of this to happen, many implicit dependencies need to be detangled and cleaned up - which is why being able to entirely disable Nasal, and only init some bits of it, is a crucial part of the equation - even if you never again want to run a single line of Nasal code in your fgfs process, you need to come up with a way to still "boot" into a mostly functional fgfs environment.
For the time being, FGPythonSys remains the strongest option to explore supporting other scripting languages - but that should definitely not be using the SGSubsystem/Mgr and FGNasalSys approach, but instead be built on something along the lines of Richard's IPC comments regarding mongoose/Phi, otherwise all the people wanting python would immediately learn that all the Nasal folks were right about the bottleneck being elsewhere, and Nasal not being the culprit - just imagine for a second, FGPythonSys would be available right now, and provide what FGNasalSys provides, using the same implementation strategy: We'd see all sorts of 3rd party modules using all sorts of platform/OS specific modules, quite possibly with certain aircraft, instruments and scenery requiring different versions of certain modules to be installed.
we could definitely come up with a reimplementation of a subset of FGNasalSys and FGPythonSys to have an abstract FGScriptingSys that would be designed to be async from the get-go, and like Richard said, it sure seems like the Phi approach (remote properties and remote RPC via fgcommands) could be a straightforward to make that happen, without having to wait for the HLA/Qt5 folks to retire so that they can actually scratch off some items from their virtual todo lists.
We should probably implement multi-processing or multi-threading using message passing, and mirrors of the property tree. (And hence no concerns about locking at all!)
Rough design is:
- main thread has existing property tree
- other threads have read only copy (mirror) of their parts property tree (subscription model, so only the parts they have requested)
- new code synchronies property changes from the master to the copies once per frame or some other interval
- other threads can ONLY interact with the main thread via the command API (which can be serialised into messages, and queued to be passed back to the main thread).
(so on a non-main thread, when you try and do ‘setValue’ on a property, we queue a ‘property-assign’ command and send it to the main thread)
The good thing about this is it works with multi-threading but also with multi-processing (using shared memory, named pipes or Unix domain sockets) so we could e.g. have the main simulation logic run as a separate process from multiple viewers. But we can run as a single process with threads using local memory to implement the message-passing of commands and have very little overhead. (Even though overhead of shared memory or Unix sockets will be virtually zero also). And of course if we use TCP sockets this is really distributable over a LAN (but not a WAN I guess) James intends to prototype this using some of the existing Network/IO protocols, eg the telnet one and a binary equivalent.
James mentioned it can be done with only small changes to SGPropertyNode, initially. What he would then suggest is, we implement scripting options such as Python by implementing a small library in that language, which talks the message-passing protocol.
Again for a proof-of-concept this could be done using the telnet or props protocols, and as more performance is needed we using a binary protocol and shared memory.
We still need the discussion about which language is mandatory but using a different language for an optional component won’t impact the core code at all.
Note this is totally orthogonal to the HLA work - HLA is a much higher level abstraction, we cannot use it to mirror the property tree. James' goal above is to explore a low-cost route to exploiting multi-core CPUs with the current codebase, in a way which keeps existing code working and fixes the locking / race bugs we already have with OSG touching properties at the same time as the main thread. So, in terms of any scripting system, James would suggest to think about it along these lines, and especially any problems you can foresee, because it’s much better that we agree what is practical and move together, than have three different attempts to fix or work-around the lack of locking in the property tree.