Celestia/Celx Scripting/Development Suggestions
||A reader has identified this chapter as an undeveloped draft or outline.
You can help to develop the work, or you can ask for assistance in the project room.
Once you've progressed beyond writing the simplest CELX "Hello World" scripts, development of your software can be a problem. Lua programming is just as complex as programming in any other language, and can be more challenging than many. Since Lua is an interpreted language with a runtime compiler, finding subtle bugs is often an issue. Here are a few suggestions to help make it easier to develop and debug Lua modules for Celestia.
Celestia is an amazing application in many ways, not the least of which is its extensibility in the form of Lua, its built-in programming language. Lua is fast and powerful and can augment the Celestia experience in ways that are only limited by the imagination. But as Uncle Ben once said to Peter Parker, with great power comes responsibility. Celestia’s Lua doesn’t hold your hand. It has an almost pathological adherence to letting you get away with almost anything without complaining. It’s like having a girl friend who’s so sweet she never complains about anything. Then one day she’s gone and you find a nasty note about never picking up your socks. Celestia’s Lua is like that. You can get away with almost anything, but there’s a heavy price to pay. And Lua doesn’t even leave a note. One day Lua will abandon you and you’ll have no idea why. But it’s not like Lua won’t tell you her problems if you ask. You just have to ask. (Good idea with your girlfriend too.)
The first thing you need to do is to get well acquainted with a Lua function called "pcall" Like all of Lua’s functions you can get a complete description in the "Lua Reference Manual" available either as a book or online, available at http://www.lua.org/manual/5.0/
One of the first functions you should write is something like
error_pause = 20 result, error = pcall( f ) if(result == false) then celestia:print(error, error_pause) wait(error_pause) end
where "f" is a function you’ve written. When you start programming you should get in the habit of calling all of your functions with pcall. Once your program is debugged, you can remove them if desired, but you should probably keep a copy of the program with the pcall statements in. Chances are, you’ll probably want to modify your baby.
What you’re essentially doing is duplicating the error reporting that most other languages have built in. The advantage here is you can remove the overhead of error detection when you’re through.
The next thing you should do, if you haven’t already, is to read Harald Schmidt’s online description of Lua/CELX scripting at http://celestia.h-schmidt.net/celx-summary-latest.html This gives all the available Celestia functionality with some tips on Lua, but I’d recommend also reading the Lua manual in conjunction with Harold’s description.
Programming Style is a matter of taste, of course, but if you going to write anything large, I’d recommend taking advantage of Lua’s ability to simply read in new code when it’s needed. For example, dofile below is an example.
function dofile(filename) s = assert(loadfile( filename ), "unable to load " .. filename) return s end
This function will compile the code in the file and issue an error if it fails.
You can take advantage of Lua’s garbage collector in this case. Lua does periodic garbage collection on any memory that your program no longer has a link to. "dofile" above could overwrite links and Lua would automatically (eventually) dispose of the memory, making your code almost infinitely extensible if you desire. This also applies to any text you might read in. Overwriting the links to strings frees up the old memory.
Simplicity is King
Lua follows the KISS principle: "Keep it simple, stupid." There are only eight types of data in Lua: nil, boolean, number, string, function, userdata, thread and table where "userdata" is just a block of undefined memory.
And, believe me, simplicity’s a good thing, for no other reason than it’s so much easier to design than to remember. The data type you’ll probably be using most is the table which is really an array that can be indexed by almost anything except nil.
If you’re programming on a Mac, be aware there are some subtle differences from the PC version, the main one being how comments are handled. The manual says comments can start with a "[[" but that doesn’t work on a Mac. The comment form I use on my Mac is
--[[ this is a coment ]]
Note the "--" precedes the brackets. With multiline comments I use
--[[ The following code is the most magnificent piece of ... --]]
Note: this seems to be caused by Mac text editors acting as word processors. They assume that when you type two dashes followed by a space you really want an em-dash (a single, double-wide dash), and replace those two dashes by the single binary code for a double-wide dash. Unfortunately, this breaks the Lua commenting convention, which requires two separate dash characters. Fortunately, if you follow two dashes by anything other than a space or tab, Mac editors don't make the substitution.
Celestia v1.5.0 cannot detect when a ScriptedOrbit or ScriptedRotation calls obj:getposition() or obj:getorientation() for an object which will have its position or orientation modified as a side effect of the ScriptedOrbit or ScriptedRotation.
As a result, one must not call obj:getposition() ( or obj:getorientation() ) from a Scripted function if the position (or orientation) of "obj" is determined, even indirectly, by that Scripted function. Doing so results in that getposition() calling routines which call the Scripted function which calls getposition()... ad infinitum. This quickly exhausts the stack space and crashes Celestia.
Suggestion: the Scripted code might simply return the previous position or orientation rather than trying to calculate a new one.
It is hoped that this situation will be detected in a future major release of Celestia.
Requiring a lua file in a celx script
When using the Lua 'require' function in a celx script, Lua uses the celestia base directory to look for Lua files. Then, one can define a different path in the celx script using:
package.path = "mypath/?.lua;"
To define the directory of the celx script as the default path, one just need to add the following line at the beginning of the script:
package.path = celestia:getscriptpath().."/../?.lua;"