aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/docs/SledjHamr/LuaSL-New-scripting-engine.html
diff options
context:
space:
mode:
Diffstat (limited to 'docs/SledjHamr/LuaSL-New-scripting-engine.html')
-rw-r--r--docs/SledjHamr/LuaSL-New-scripting-engine.html917
1 files changed, 917 insertions, 0 deletions
diff --git a/docs/SledjHamr/LuaSL-New-scripting-engine.html b/docs/SledjHamr/LuaSL-New-scripting-engine.html
new file mode 100644
index 0000000..9ceb7f8
--- /dev/null
+++ b/docs/SledjHamr/LuaSL-New-scripting-engine.html
@@ -0,0 +1,917 @@
1<html>
2<title>LuaSL New scripting engine</title>
3<head>
4</head>
5<body bgcolor="black" text="white" alink="red" link="blue" vlink="purple">
6<p>LuaSL is a Lua based LSL scripting engine that will aim for LSL
7compatibility first, then adding Lua extensions. It aims to replace the
8woeful XEngine from OpenSim, and at a later stage, be the basis for a
9client side scripting engine.</p>
10<p>To compile this, you will need Enlightenment Foundation Libraries (EFL)
11installed in either /opt/e17 or /usr. These are typical places it get's
12installed in. You will also need flex. The rest of the dependencies
13are in the ../libraries directory.</p>
14<h1> write our own </h1>
15<p>I had considered in the <a href="../SledjHamr.html">SledjHamr</a> document writing a new script engine from scratch in Lua. This is what I wrote -</p>
16<p>"I'd love to write a Lua implementation of LSL, I'm sure Alice would want to write a Scheme one."</p>
17<p>"I've been thinking of trying out Lua as a backend for LSL, and trying some micro threading style experiments."</p>
18<p>That's about the sum total of my plans and thoughts though. lol</p>
19<p>Here are some more random thoughts.</p>
20<p>Writing an entire scripting engine in a new language is a big job.</p>
21<p>Lua is meant to be embedded into other things as an internal scripting language. It has great features that let it be a meta language, you can make it look like other languages, or add other language concepts. Plus it's tiny. Being used by online games like WoW means it's probably got what it takes. These are reasons I chose it for both server side and client side scripting.</p>
22<p>My own personal plan was to cut my teeth on Lua by using <a href="http://www.enlightenment.org/">EFL</a> for a RL contract I'm working on, then add Lua scripting to the meta-impy viewer. It turns out that the EFL Lua support was not complete, but I managed to use it in that project anyway. My next Lua plans are to implement in EFL those things that project needed. I did not plan on working on a Lua based server side scripting engine until after I had implemented some of the <a href="../index.html">OMG</a> plans. In particular, I want to do that stuff in C, and C is the natural partner for Lua. Adding Lua to .NET / mono is a whole can of worms I personally don't want to get stuck in.</p>
23<p>I have successfully completed my plans to implement EFL Lua things that my RL contract needed. That's in the current release of the EFL libraries, so I can move onto my next plans now.</p>
24<p>On the other hand, perhaps it's worthwhile starting on our own scripting engine now? It's a big job, so lets break it down.</p>
25<p>&nbsp;</p>
26<h2> Lua to .NET bindings. </h2>
27<p>OpenSim is written in C#, a language I don't know much about. C# is one of the languages that Microsoft's .NET (and the unix port mono) can support. There seems to be a few projects that have done this for us. Some of these might be duplicated, I've not actually read them, just a quick search.</p>
28<p><a href="http://www.lua.inf.puc-rio.br/post/9">Some details about Lua.NET, also mentions some others, but with broken links.</a></p>
29<p><a href="http://www.gamedev.net/page/resources/_/technical/game-programming/using-lua-with-c-r2275">LuaInterface</a></p>
30<p><a href="http://ttuxen.wordpress.com/2009/11/03/embedding-lua-in-dotnet/">Details how to integrate the two from Visual Studio, so really only useful for Windows.</a></p>
31<p><a href="http://luaforge.net/projects/luanet/">Lua.NET</a></p>
32<p><a href="http://www.codeproject.com/KB/mcpp/luanetwrapper.aspx">Another roll your own example.</a></p>
33<p>&nbsp;</p>
34<h2> Interfacing to the virtual world </h2>
35<p>Some parts of a scripting engine need to interface with the virtual world. Changing prims, detecting touches, playing sounds, starting animations, etc. The existing script engine has C# functions that map LSL script functions to what's needed to do these things in OpenSim. Lua, being an embedded scripting language, has methods of making Lua functions that can call the under laying systems functions. So it should not be that hard to just map pre existing C# functions to Lua functions in the same way that they are now mapped to LSL functions.</p>
36<p>&nbsp;</p>
37<h2> Compiler </h2>
38<p>LSL, C#, and Lua are all compiled. I think LSL is compiled to .NET bytecode, C# certainly is. Lua is compiled to it's own bytecode, but perhaps one of the bindings I mentioned before would compile it to .NET bytecode? Compilation is now done server side. It used to be done viewer side, but that changed when LL moved to a mono backend for LSL. OpenSim being .NET / mono in the first place, could just take advantage of that and work in the same way. OpenSim does not support the old LSL script engine. I don't think we need to either.</p>
39<p>&nbsp;</p>
40<h2> Language </h2>
41<p>OpenSim allows people to write in world scripts in C# as well as LSL. Personally, I've NEVER seen any actual in world examples being used, only some theoretical examples on web pages. I suspect that people want their scripts to be more or less compatible with SL, so they stick to LSL. Certainly there is a great amount of scripts that came from SL, so they are LSL anyway.</p>
42<p>It's possible we would want people to be able to write scripts in Lua as well as LSL. That might actually get more traction than C#, as we might attract scripters from WoW and other popular online games that use Lua for scripting. I think it has advantages also for when Lua scripting makes it into the client. Server and client side scripting should be compatible. I don't think LSL is a good language for client side scripting, as it's just not made with that in mind.</p>
43<p>&nbsp;</p>
44<h2> Design </h2>
45<p>There are a number of ways we can go about this. Do we write the entire scripting engine in Lua, and only interface with C# for those things we really need to in order to get OpenSim to do in world stuff? Do we write a LSL to Lua translation layer that then compiles the Lua? The OpenSim engine I understand does that, translates LSL to C# then compiles that to .NET. Could we start with a higher level of Lua that interfaces with the existing LSL support functions in OpenSim? Can we even rely on those LSL support functions being a stable API? Should we start by experimenting with Lua's meta language features, see how close we can get it to look like LSL syntax? Perhaps concentrate on those parts of the job that don't require interfacing to OpenSim, and hope that SledjHamr can meet it half way to avoid the entire .NET thing?</p>
46<p>I choose to do those last two things - see how far I can get Lua to look like LSL, and concentrate on the parts that don't require interfacing to OpenSim, hoping that I can avoid the entire .NET thing.</p>
47
48<p>The basic design will be made up as I go along, but so far I have this -</p>
49
50<p>A parser parses an LSL script, validating it and reporting errors.</p>
51
52<p>A translator takes the result of the parse, and converts it into Lua
53source. Each LSL script becomes a Lua state. LSL states are handled as
54Lua tables, with each LSL state function being a table function in a
55common metatable. LL and OS functions are likely to be C or Lua
56functions. Careful testing should be done with LuaJIT FFI, sandboxing,
57and performance testing.</p>
58
59<p>The Lua source is compiled by the Lua compiler.</p>
60
61<p>LuaJIT is used as the Lua compiler, library, and runtime.</p>
62
63<p>Luaproc is used to start up operating system threads and hand Lua states
64between them. Luaproc messaging is also being used, but might need to
65change to edje messaging. Note - luaproc has been extensively rewritten
66for this project, mostly converting it to use EFL. That rewrite
67substantially shrunk the source code. Then it was all rewritten again
68to use EFL threads, and cooperative multitasking.</p>
69
70<p>THIS IS WHERE WE ARE RIGHT NOW.</p>
71
72<p>Should implement embedded Lua somehow. Probably the best thing to do is
73to have comments like -</p>
74
75<p>//Lua: local t = {1, 3, 42, x='something', 'something else}</p>
76/*Lua: print(t.x) */</p>
77
78<p>The LSL parser picks these up and stores them in the AST as Lua
79snippets, then the compiler output functions just inserts them in the
80Lua code it is generating. Obviously these Lua snippets can access the
81rest of the generated Lua code. They should also be able to access
82skang and thus do proper GUI stuff on viewers that support skang.</p>
83
84<p>Nails will be used to pump commands in and out of the LuaSL system.
85Incoming commands invoke LSL events via the LuaSL state metatable. LL
86and OS functions that impact the world will be converted to nails
87commands sent to the command pump.</p>
88
89<p>Initially, since this is the first thing being written, a nails command
90pump client needs to be installed into OpenSim's C# stuff. Though it
91might be possible to talk directly to ROBUST instead. Think I'll try
92the ROBUST route, see how far I can get. That's the general principle
93applying in all of this - try to avoid C# and see how for we can get.
94lol</p>
95
96<p>On the other hand, might be better to leverage the existing C#
97implementations of LSL functions, just to get things up and running
98quickly. To that end, a protocol involving exchanging snippets of Lua
99over a network socket has been developed, and the next step is to write
100the C# side. sigh</p>
101
102<p>A watchdog thread should be used to make sure no LuaSL script spends
103forever processing any event.</p>
104
105<p>Some form of serialisation will need to be created for saving script
106state during shutdowns, passing script state to other threads /
107processes / computers. Apparently Lua is good at this.</p>
108
109<p>There will have to be a MySQL (and maybe SQLite) client in the system,
110so we can talk directly to the local sim database. Esskyuehl may be
111suitable, though it's still in the prototype stage.</p>
112
113<p>Email, HTTP, and XML-RPC might need to be dealt with by us. A ROBUST
114client will be needed to. Azy might be suitable, but it's also in
115prototype.</p>
116
117<p>An object is a file system directory, full of LSL scripts as text files,
118notecards as text files, animations as BVH (or later BVJ) files, etc.
119There will be some sort of metadata in place. This could be created by
120our own OpenSim compatible cache module.</p>
121
122
123<p>Test harness.</p>
124-------------</p>
125
126<p>I'll build a test harness. It will be based on EFL Edje Lua, with
127buttons for triggering LSL events, SL style dialogues, and other goodies.</p>
128
129<p>The initial goal will be to run standard MLP scripts. They have minimal
130interface to the world, and exercise quite a bit of the rest of LSL.
131They are also quite common, and sometimes responsible for a lot of the
132script running load.</p>
133
134<p>Later I should add stock standard OpenCollar scripts from SL. They are
135a bitch to get working under OpenSim, so would be good compatibility
136tests.</p>
137
138<p>Various eina logging domains might be used to handle whisper, say, shout,
139etc.</p>
140
141<p>Performance testing will have to be done on 5000 scripts, to see how
142that compares against XEngine.</p>
143
144<p>The test harness became the love world server.</p>
145
146
147<p>TODO</p>
148<p>----</p>
149
150<p>Useful for limiting the amount of time scripts use -
151<p>https://groups.google.com/forum/#!topic/lua-alchemy-dev/3bDPk2aQ8FE</p>
152<p>http://stackoverflow.com/questions/862256/how-can-i-end-a-lua-thread-cleanly</p>
153
154
155
156<p>&nbsp;</p>
157<h1> onefangs implementation ideas </h1>
158<p>I'm gonna write an LSL script engine in Lua and C. At least initially, I'll pretend I can use SledjHamr instead of OpenSim, and see how far I get. The source is at <a href="https://github.com/onefang/SledjHamr">https://github.com/onefang/SledjHamr</a> on the experimental branch.</p>
159<p>&nbsp;</p>
160<h2> You're in a maze of twisty little quirks, all different. </h2>
161<p>LSL is known for being more quirks than features. Some of the quirks are just limitations that we can get rid of. Some we will have to replicate just to be compatible. OpenSim adds it's own quirks on top of those, but one of the points of doing this is to avoid that particular set of quirks. I'll create two variations, using the first line comment hack OpenSim invented to choose between them. The default is to use the quirky one, where an effort is made to replicate the full quirkiness of LSL. The other choice has no quirks at all, and even lets Lua features be mixed in. This Lua flavoured LSL will be the first one to work on, as it will be a lot easier.</p>
162<p>&nbsp;</p>
163<h2> Making Lua look like LSL </h2>
164<p>There are syntactic differences between LSL and Lua. Although Lua is good as a metalanguage, those syntax differences wont go away by themselves. I think some sort of preprocessor would be needed to massage LSL into Lua as a first step in compiling.</p>
165<p>The preprocessor would have to start by parsing the LSL code into some sort of useful structure. Since the whole point of this exercise as that the OpenSim Xengine sucks, and it's written in C# anyway, don't want to use that. The Aurora script engine likely sucks less, but is still C#. The standard viewer source code includes an LSL parser written using flex and bison. It looks like C code, with C++ wrappers to wedge it nicely into the rest of the viewer code, but it generates C++ code full of LL classes.</p>
166<p>A test harness could be constructed using EFL Edje Lua to provide some push buttons that can trigger LSL events, provide dialogues, and display various state info. I think a good start is to put the MLP scripts and their notecards / animations into a directory, call that directory an Object, perhaps even implement some of the rest of SledjHamr with some object meta data (MLP will need access to the objects description). MLP is a good test subject, it tends to soak up a lot of sim resources, it's interface to the world is minimal, and it would exercise a lot of the non world interfacing stuff.</p>
167<p>For reference, here is <a href="http://www.lua.org/manual">Lua reference manual</a>, <a href="http://www.lua.org/pil/">Lua PIL (for Lua 5.0)</a>, <a href="http://luajit.org/index.html">LuaJIT</a>, <a href="http://lslwiki.net/lslwiki/wakka.php?wakka=HomePage">LSL Wiki</a>, and <a href="http://wiki.secondlife.com/wiki/LSL_Portal">SL LSL portal</a>.</p>
168<p>&nbsp;</p>
169<h3> comments and line endings </h3>
170<p>Well, comments get stripped out as part of the compile, so probably should not worry about that. Though the preprocessor will need to understand LSL style comments. LSL uses C++ // style comments, every thing from the // to the end of the line is ignored, as well as C style comments, everything between /* and */ is ignored.</p>
171<p>In LSL, statements need to end in a semicolon. In Lua, they are optional. So we can just leave them in. Just needed to say that somewhere.</p>
172<p>&nbsp;</p>
173<h3> types </h3>
174<p>LSL has fixed type per variable. Lua is dynamically typed, and variables can have any type at any given time.</p>
175<p>The basic LSL types are -</p>
176<table border="1"><caption>&nbsp;</caption>
177<tbody>
178<tr><th>LSL type</th><th>LSL details</th><th>Lua type</th><th>Notes</th></tr>
179<tr>
180<td>integer</td>
181<td>A signed 32 bit integer, can use hex (integer hex = 0xff;).</td>
182<td>number</td>
183<td>Lua numbers are C double-precision floating-point IEEE 754 numbers, though other types can be used when compiling Lua. This will be a problem, they wont be stored faithfully.</td>
184</tr>
185<tr>
186<td>float</td>
187<td>An IEEE-754 32-bit floating point value.</td>
188<td>number</td>
189<td>Perfect match, if Lua is compiled as default.</td>
190</tr>
191<tr>
192<td>vector</td>
193<td>Three floats in the form &lt; x , y , z &gt;. Usually a position, colour, or Euler rotation.</td>
194<td>&nbsp;</td>
195<td>Use a table and metatable, or a userdata.</td>
196</tr>
197<tr>
198<td>rotation</td>
199<td>A quaternion rotation, made up of 4 floats, &lt; x , y , z , s &gt;.</td>
200<td>&nbsp;</td>
201<td>Use a table and metatable, or a usedata.</td>
202</tr>
203<tr>
204<td>key</td>
205<td>A UUID, specialised string in the same format as UUIDs everywhere.</td>
206<td>&nbsp;</td>
207<td>Can use a string, though perhaps a metatable or userdata would help? While it is true that it's just a string representation of a 32 bit integer, Lua has no way of faithfully representing 32 bit integers.</td>
208</tr>
209<tr>
210<td>string</td>
211<td>A sequence of UTF-8 characters. They support a few backslash escapes at compile time.</td>
212<td>string</td>
213<td>Lua string represents an immutable sequences of bytes. Lua is 8-bit clean: strings can contain any 8-bit value, including embedded zeros ('\0').</td>
214</tr>
215<tr>
216<td>list</td>
217<td>A heterogeneous list of the other data types.</td>
218<td>&nbsp;</td>
219<td>Use a table with number keys.</td>
220</tr>
221</tbody>
222</table>
223<p>&nbsp;</p>
224<h3> bit operations </h3>
225<p>LSL relies on bit operations, especially for some of it's functions. Lua only grew bit operations in 5.2, which I have not looked at yet. A complication is that Lua numbers are floats by default, so these might not be efficient. LuaJIT on the other hand, has the bit operations built in.</p>
226<p>&nbsp;</p>
227<h3> scope </h3>
228<p>This is one of the reasons why we are writing our own script engine. The OpenSim XEngine's scope system is very broken. So we gotta do better than that at the very least. Wont be hard. B-)</p>
229<p>&nbsp;</p>
230<h3> Brackets, parenthesis, and braces; oh my. </h3>
231<p>LSL uses -</p>
232<table border="1"><caption>&nbsp;</caption>
233<tbody>
234<tr><th>LSL</th><th>Meaning</th><th>Lua</th><th>Notes</th></tr>
235</tbody>
236<caption>&nbsp;</caption>
237<tbody>
238<tr>
239<td>()</td>
240<td>Expression re-ordering.</td>
241<td>()</td>
242<td>Exact match.</td>
243</tr>
244</tbody>
245<caption>&nbsp;</caption>
246<tbody>
247<tr>
248<td>{}</td>
249<td>Code block.</td>
250<td>do statements end</td>
251<td>See the flow control section for other uses.</td>
252</tr>
253</tbody>
254<caption>&nbsp;</caption>
255<tbody>
256<tr>
257<td>[]</td>
258<td>List creation.</td>
259<td>someList = { "a", varB, 3, functionF(foo) }</td>
260<td>&nbsp;</td>
261</tr>
262</tbody>
263<caption>&nbsp;</caption>
264<tbody>
265<tr>
266<td>&lt;&gt;</td>
267<td>Vector and rotation creation.</td>
268<td>&nbsp;</td>
269<td>Well, since we are doing these as a table, we can use table creating functions.</td>
270</tr>
271</tbody>
272</table>
273<p>&nbsp;</p>
274<h3> flow control </h3>
275<p>LSL uses C style flow control, Lua does not. "LSL conditions are evaluated left to right, instead of right to left as in most programming languages." They are also not short circuited. There is no break or continue in LSL.</p>
276<table border="1"><caption>&nbsp;</caption>
277<tbody>
278<tr><th>LSL</th><th>Lua</th><th>Notes</th></tr>
279</tbody>
280<caption>&nbsp;</caption>
281<tbody>
282<tr>
283<td>
284<pre>do
285 {
286 statements;
287 } while (condition);
288</pre>
289</td>
290<td>
291<pre>repeat
292 statements
293until condition
294
295</pre>
296</td>
297<td>&nbsp;</td>
298</tr>
299</tbody>
300<caption>&nbsp;</caption>
301<tbody>
302<tr>
303<td>
304<pre>for (initialisation; condition; update)
305{
306 statements;
307}
308</pre>
309</td>
310<td>
311<pre>for variable = e1, e2, e3 do
312 statements
313end
314</pre>
315<pre>for variableList in explist do
316 statements
317end
318
319</pre>
320</td>
321<td>In the first Lua form, variable starts from e1, gets e3 added through each loop, and stops at e2.
322<p>The second form is complicated, see the lua reference manual. It can be rewritten as -</p>
323<pre>do
324 local f, s, var = explist
325 while true do
326 local var_1, ···, var_n = f(s, var)
327 if var_1 == nil then break end
328 var = var_1
329 statements
330 end
331end
332
333</pre>
334<p>Neither is a good match against LSL. To make things worse, the for variables in Lua are all local to the for loop, and it's not safe to change them in the loop. So we can't use Lua for loops to implement LSL for loops.</p>
335<p>A LSL for loop could be rewritten as -</p>
336<pre>initialisation
337while (condition)
338{
339 statements;
340 update;
341}
342</pre>
343<p>Which can be implemented by a Lua while statement.</p>
344</td>
345</tr>
346</tbody>
347<caption>&nbsp;</caption>
348<tbody>
349<tr>
350<td>
351<pre>if (condition)
352{
353 statements;
354}
355else if (condition)
356{
357 statements;
358}
359else
360{
361 statements;
362}
363</pre>
364</td>
365<td>
366<pre>if condition then
367 statements
368elseif condition then
369 statements
370else
371 statements
372end
373
374</pre>
375</td>
376<td>&nbsp;</td>
377</tr>
378</tbody>
379<caption>&nbsp;</caption>
380<tbody>
381<tr>
382<td>
383<pre>jump Label;
384statements;
385@Label;
386</pre>
387</td>
388<td>
389<pre>goto Label
390statements
391::Label::
392
393</pre>
394</td>
395<td>
396<p>Think this is only in Lua 5.2, and there might be good reasons to not use Lua 5.2, especially since we are using LuaJIT. <a href="http://lua-users.org/lists/lua-l/2009-11/msg00061.html">http://lua-users.org/lists/lua-l/2009-11/msg00061.html</a> might help to explain why.</p>
397</td>
398</tr>
399</tbody>
400<caption>&nbsp;</caption>
401<tbody>
402<tr>
403<td>
404<pre>while (condition)
405{
406 statements;
407}
408</pre>
409</td>
410<td>
411<pre>while condition do
412 statements
413end
414
415</pre>
416</td>
417<td>&nbsp;</td>
418</tr>
419</tbody>
420</table>
421<p>&nbsp;</p>
422<h3> count from 0 / 1 </h3>
423<p>LSL counts list entries from 0. Negative numbers can also be used to count backwards in a list. Therefore, the last element has index -1. The first element would also have an index of (-llGetListLength(myList))</p>
424<p>LSL string indices start at 0. Using negative numbers for start and/or end causes the index to count backwards from the length of the string, so 0, -1 would be the entire string. If start is larger than end the substring is the exclusion of the entries, so 6, 4 would be the entire string except for the 5th character. If you wish to include the last character in a string, use -1, -1 , the last two, use -2, -1 , etc. Except for llInsertString().</p>
425<p>Lua counts tables with a sequence of consecutive integer keys from 1 to the length of the sequence as a special table type with syntax sugar and functions to deal with them.</p>
426<p>Lua string indices also start at 1. Indices are allowed to be negative and are interpreted as indexing backwards, from the end of the string. Thus, the last character is at position -1, and so on.</p>
427<p>For strings (and utf8 while we are at it) just create a count from 0 strg library that otherwise duplicates the string library. For tables, do the same with a tbl library, make 0 a first class table index like everything else, then I think we are only left with table constructors like x = ["a", "b", "c"].</p>
428<p>&nbsp;</p>
429<h3> functions </h3>
430<p>LSL functions have an optional type (in which case it must return that type), and optional typed parameters. Lua is all dynamically typed, so we can just leave out the types, but check them at compile time.</p>
431<p>LSL passes function parameters by value. Lua passes by value as well, I think. Tables may be passed by reference.</p>
432<p>&nbsp;</p>
433<h3> states </h3>
434<p>LSL states can be dealt with as tables of functions, one per state, with all the usual LSL events stubbed out. So a single metatable will help. A currentState table will point to the current LSL state.</p>
435<p>&nbsp;</p>
436<h2> Efficiently running thousands of scripts </h2>
437<p>(As a data point, Anarchadia has 3315 scripts running.)</p>
438<p>LSL scripts seem to be a good match for Lua states. Each script/state is independent, with no global data shared between them except for what is explicitly sent via communications calls, or calls to the system it's embedded in (the world interfacing). Lua scripts can be run in separate OS threads, which lets us make use of multi core CPUs. It's theoretically not too hard to serialise Lua, so running Lua states can be stopped, sent to some other computer, then restarted (good for attachment scripts when TPing).</p>
439<p>"luaproc is a concurrent programming library for Lua. It implements an approach, geared towards massive concurrency support, which uses multiple independent lua_States as lightweight user threads ("Lua processes") and kernel threads as workers." Sounds like a good match, except it seems to be more an experiment for an academic paper than something useful. It is on github, with recent changes, well, recently added. <a href="https://github.com/askyrme/luaproc">https://github.com/askyrme/luaproc</a></p>
440<p>Lua has cooperative multitasking, but not pre-emptive. LSL is event driven, and no event processing should take forever. However, we would still need to deal with badly written scripts with infinite loops in them.</p>
441<ul>
442<li>Just let them run, continuing to keep that thread busy.</li>
443<li>Use a watchdog thread.
444<ul>
445<li>Reduce that threads priority.</li>
446<li>Kill the run away thread with fire.</li>
447<li>Force it to yield.
448<ul>
449<li>Requires a change to the Lua source code.</li>
450</ul>
451</li>
452<li>Force it to yield, then recompile that bad boy with a yield() call at the end of every loop.</li>
453<li>All of the above. Make them pay for their bad coding, not every one else.</li>
454</ul>
455</li>
456</ul>
457<p>I just had a thought. It might be worthwhile doing some typical compiler optimisations. Should see if doing that to the LSL helps. The Lua compiler might do that for us anyway, but certainly worth investigating. On the other hand, LuaJIT probably does most of that for us anyway. Might not be worthwhile.</p>
458<p>&nbsp;</p>
459<h2> hacking up Lua source </h2>
460<p>I was hoping to avoid it, but I think we may have to hack up Lua source and not use any system supplied Lua library. The main reason is integers. LSL scripters expect integers to behave like 32 bit signed integers, not like 32 bit floats. So that's gonna cause no end of problems unless we have a a native 32 bit signed integer type in Lua. We can't just compile Lua to use 32 bit signed integers for it's number type, as then LSL floats get broken.</p>
461<p>Things we should hack up Lua source for -</p>
462<ul>
463<li>32 bit signed integers AND 32 bit floats.
464<ul>
465<li>The <a href="http://lua-users.org/wiki/LuaPowerPatches">power patch</a> "LNUM - number mode patch ("integer patch"), revised to Lua 5.1.3" may help</li>
466<li><a href="http://lua-users.org/wiki/FloatingPoint">http://lua-users.org/wiki/FloatingPoint</a>says that the solution to the problem is to use 64 bit floats, since they can safely be used to represent 32 bit integers. I still think that would use up too much space, but buy the rest of the arguments ONLY if we are considering 64 bit servers.
467<ul>
468<li>Either way STILL means compiling our own lua, so still should hack it up while we are at it.</li>
469</ul>
470</li>
471<li><a href="http://stackoverflow.com/questions/4484437/lua-integer-type">http://stackoverflow.com/questions/4484437/lua-integer-type</a> is pointing in the right direction.</li>
472</ul>
473</li>
474<li>Forcing a yield in run away event processing.
475<ul>
476<li>The <a href="http://lua-users.org/wiki/LuaPowerPatches">power patch</a> "Interpreter Bailout Flag for Lua 5.1.3" might do the trick.</li>
477</ul>
478</li>
479<li>Start counting at 0 dammit!
480<ul>
481<li><a href="http://lua-users.org/wiki/GeneralizedPairsAndIpairs">http://lua-users.org/wiki/GeneralizedPairsAndIpairs</a> may be useful. Parts of this made it into Lua 5.2.</li>
482</ul>
483</li>
484<li>UTF-8 strings.
485<ul>
486<li><a href="http://lua-users.org/wiki/LuaUnicode">http://lua-users.org/wiki/LuaUnicode</a> and <a href="http://lua-users.org/lists/lua-l/2005-05/msg00317.html">http://lua-users.org/lists/lua-l/2005-05/msg00317.html</a> look like good starting points for research</li>
487<li>The <a href="http://lua-users.org/wiki/LuaPowerPatches">power patch</a> "Literals (hex, UTF-8) patch, revised to Lua 5.1 (beta)" may help.</li>
488</ul>
489</li>
490</ul>
491<p><br /> Things we could hack up the Lua source for if we are gonna do it anyway -</p>
492<ul>
493<li>Script state persistence might be more efficient if we add stuff to support that to Lua.</li>
494</ul>
495<p>Looks like LuaJIT gets us part of the way there, and it's supposed to be the fastest scripting language around, not much slower than C. Some of the above hacking wont be needed. It's a drop in replacement for Lua 5.1, but it has extras as well, some from 5.2, some already mentioned above in the hacks we night need to do. It has FFI, which also speeds up linking to C code, but that's very dangerous low level code. Should see if we can use it, THEN sandbox it away. See this link about sandboxing - <a href="http://osdir.com/ml/general/2011-02/msg23395.html">http://osdir.com/ml/general/2011-02/msg23395.html</a></p>
496<p>&nbsp;</p>
497<h2> Hooking it up to OpenSim </h2>
498<p>OpenSim has a mechanism for each script to choose the script engine it will run under, and even the language used. The first line of the script is interpreted by OpenSim if it's a comment. If it's not proper, OpenSim bitches about not being able to load a non existent script engine. Some examples of existing supported first lines -</p>
499<ul>
500<li>//XEngine:</li>
501<li>//XEngine:lsl</li>
502<li>//XEngine:c#</li>
503<li>//XEngine:vb</li>
504<li>//DotNetEngine:</li>
505</ul>
506<p>If no language is added after the colon, LSL is assumed. We will use that, and add -</p>
507<ul>
508<li>//LuaSL:</li>
509<li>//LuaSL:LSL</li>
510<li>//LuaSL:Lua</li>
511<li>//LuaSL:LuaSL</li>
512</ul>
513<p>As before, if that line is not there, then the default OpenSim script engine and language is used, which these days is XEngine and LSL. The default language for LuaSL will be LuaSL, which is a hybrid of LSL and what ever Lua syntax that can't be mistaken for LSL syntax. In other words, it's LSL, but any Lua code that the LSL parser does not barf on simply gets passed through to the Lua compiler. //LuaSL:Lua means the script is purely Lua code. Though perhaps that should be --LuaSL:Lua, to be compatible with Lua? //LuaSL:lsl means that the script is purely LSL code, no Lua will be tolerated.</p>
514<p>//LuaSL:LuaSL is what I'm writing to start off with, as it's the simplest thing to do. Well, OK, Pure Lua would be simpler, coz I could leave out the LSL parser stage, but that bit is half done anyway. Either way, the big part of the job is writing all those LSL functions, especially those that deal with the world.</p>
515<p>Ewww, will have to write C# stubs for OpenSim interfacing. Using a nails command pump as the intermediary sounds like a sane approach, as we will have to end up with one of those anyway. LuaSL will be a separate process, running scripts in threads, with a base control thread. The base control thread will handle our end of the nails command pump.</p>
516<p>&nbsp;</p>
517<h3> non world interfacing functions </h3>
518<p>Some LSL functions don't need to actually interface with the world, we can do them in the script engine without needing to bother OpenSim. Things like list handling functions, strings, maths, etc.</p>
519<p>&nbsp;</p>
520<h3> stepping outside the world </h3>
521<p>LSL has functions for dealing with email, HTTP, and XML-RPC. Now we could implement those systems ourselves, but to start with might be easier to just use the OpenSim implementation. Doing it ourselves may screw with internal state of OpenSim if it's doing those things for non LuaSL using scripts. Or they might fight over open ports and such.</p>
522<p>&nbsp;</p>
523<h3> getting world events </h3>
524<p>What ever 'orrible method OpenSim uses to get in world events to scripts, we will have to capture and send to our shiny new script engine.</p>
525<p>&nbsp;</p>
526<h3> changing the world </h3>
527<p>When our scripts want to change the world, we will have to convince OpenSim to do that for us. If we are really lucky, we can talk directly to the asset server. Might be able to just talk to the sims local database, but that gets tricky if the script engine is NOT running on the sim server, which is a possibility we want to keep open.</p>
528<p>For the functions that get and set prim properties, we should use wrappers around llSetPrimitiveParams() and friends. The <a href="../common/Nails.html">Nails</a> protocol is partly based on those functions, so this will work out well for the future, when we move to a Nails command pump.</p>
529<p>&nbsp;</p>
530<h3> Lifestyles of the rich and infamous... er I mean life cycle of a script, and communications with the engine. </h3>
531<p>Scripts start life currently in OpenSim, will get sent to the script engine to be compiled, than started or stopped, eventually might get deleted. While they are running, the script engine requests in world services, and responds to events. Each of these things needs OpenSim and the script engine to refer to specific scripts, can use script UUIDs for that. My basic idea is to run the script engine as a separate process, communicating over a socket to the OpenSim processes. Initially, just for ease of implementation, I'm thinking of sending function calls and parameters as Lua function calls, and getting the results back as Lua values. We can use Lua table syntax to provide the script UUID, which will be called "SID" in the following discussion.</p>
532<p>It's really quite arbitrary whether OpenSim or LuaSL will be the server end. On the one hand, when we are using nails, the central nails command pump is currently being talked about as if it's a server, with every thing else, including the script engine, being clients. On the other hand, the script engine might be better off as a ROBUST service. which implies it's a server, though a server hiding behind the ROBUST proxy. I guess you could look at it as OpenSim is using the script engine as a server that runs scripts, and the script engine using OpenSim as a server to run certain functions is only a temporary measure. Have to make a decision one way or another - Deciding to have LuaSL run as the server end, OpenSim as the client end. Later I'll add the ability to read the OpenSim config files to LuaSL, but for now the default hard coded port will be 8211.</p>
533<p>Note that the final goal is to move as much of the OpenSim script functionality to the script Engine as possible. Later we will be replacing other bits of OpenSim as well, this is just the first part. So some of these communications might be inefficient, and might stay that way, but still could speed things up if that's not too hard. This is just a preliminary suggested protocol to get things up and running quickly. From my experiments, looks like it might be best to restrict this temporary protocol to function calls and returned values.</p>
534<table border="1"><caption>&nbsp;</caption>
535<tbody>
536<tr><th>Life stage</th><th>OpenSim</th><th>LuaSL script engine</th></tr>
537</tbody>
538<caption>&nbsp;</caption>
539<tbody>
540<tr>
541<td>save script</td>
542<td>
543<pre>SID.save([=[
544default
545{
546 state_entry()
547 {
548 llSay(0, "Saluton Mondon.");
549 }
550}
551]=])
552</pre>
553<p>The [=[ ... ]=] syntax is Lua code for a multi line string. Actually, this is too delicate. What if the script happens to have the end string delimiter in it? Have OpenSim just write the file itself, it already has the script text in memory anyway. Much more robust. Also, later, our special asset "cache" will write them for us anyway.</p>
554</td>
555<td>&nbsp;</td>
556</tr>
557</tbody>
558<caption>&nbsp;</caption>
559<tbody>
560<tr>
561<td>compile script</td>
562<td>
563<pre>SID.compile(/path/to/script/source)
564</pre>
565<p>The filename can be a URL, or a FILE:// URL, or just a file name.</p>
566</td>
567<td>&nbsp;</td>
568</tr>
569</tbody>
570<caption>&nbsp;</caption>
571<tbody>
572<tr>
573<td>&nbsp;</td>
574<td>&nbsp;</td>
575<td>Compiles the script.
576<pre>SID.compilerError(42,10,"Something icky here!")
577SID.compilerWarning(123,38,"Eww, you did not mean that, surely?")
578SID.compiled(false)
579
580</pre>
581<p>That's line number, column number, and error message in the first two. true or false in the last one to show if it finish OK, or gave up.</p>
582</td>
583</tr>
584</tbody>
585<caption>&nbsp;</caption>
586<tbody>
587<tr>
588<td>start script</td>
589<td>
590<pre>SID.start()
591</pre>
592</td>
593<td>&nbsp;</td>
594</tr>
595</tbody>
596<caption>&nbsp;</caption>
597<tbody>
598<tr>
599<td>&nbsp;</td>
600<td>&nbsp;</td>
601<td>Starts / resumes the script.</td>
602</tr>
603</tbody>
604<caption>&nbsp;</caption>
605<tbody>
606<tr>
607<td>script calls in world ll*() function</td>
608<td>&nbsp;</td>
609<td>
610<pre>SID.llSay(0,"Hello World")
611</pre>
612</td>
613</tr>
614</tbody>
615<caption>&nbsp;</caption>
616<tbody>
617<tr>
618<td>&nbsp;</td>
619<td>Sends the text to channel 0.</td>
620<td>&nbsp;</td>
621</tr>
622</tbody>
623<caption>&nbsp;</caption>
624<tbody>
625<tr>
626<td>&nbsp;</td>
627<td>&nbsp;</td>
628<td>
629<pre>SID.llUnsit("66864f3c-e095-d9c8-058d-d6575e6ed1b8")
630</pre>
631</td>
632</tr>
633</tbody>
634<caption>&nbsp;</caption>
635<tbody>
636<tr>
637<td>&nbsp;</td>
638<td>Avatar stands up.</td>
639<td>&nbsp;</td>
640</tr>
641</tbody>
642<caption>&nbsp;</caption>
643<tbody>
644<tr>
645<td>&nbsp;</td>
646<td>&nbsp;</td>
647<td>
648<pre>SID.llGetAgentSize("66864f3c-e095-d9c8-058d-d6575e6ed1b8")
649</pre>
650</td>
651</tr>
652</tbody>
653<caption>&nbsp;</caption>
654<tbody>
655<tr>
656<td>&nbsp;</td>
657<td>
658<pre>SID.return {x=0.45, y=0.6, z=1.8}
659</pre>
660<p>OpenSim sends back a vector.</p>
661</td>
662<td>&nbsp;</td>
663</tr>
664</tbody>
665<caption>&nbsp;</caption>
666<tbody>
667<tr>
668<td>in world event</td>
669<td>
670<pre>SID.events.detectedNames(
671{
672 "kelly rocket",
673 "onefang rejected",
674})
675SID.events.detectedKeys(
676{
677 "01234567-89ab-cdef-0123-456789abcdef",
678 "66864f3c-e095-d9c8-058d-d6575e6ed1b8",
679})
680SID.events.touch_start(2)
681
682</pre>
683<p>Sent as three separate lines, one per function call.</p>
684</td>
685<td>&nbsp;</td>
686</tr>
687</tbody>
688<caption>&nbsp;</caption>
689<tbody>
690<tr>
691<td>&nbsp;</td>
692<td>&nbsp;</td>
693<td>Calls that event handler in the current state, making sure that calls to llDetected*() return the proper results.</td>
694</tr>
695</tbody>
696<caption>&nbsp;</caption>
697<tbody>
698<tr>
699<td>stop script</td>
700<td>
701<pre>SID.stop()
702</pre>
703</td>
704<td>&nbsp;</td>
705</tr>
706</tbody>
707<caption>&nbsp;</caption>
708<tbody>
709<tr>
710<td>&nbsp;</td>
711<td>&nbsp;</td>
712<td>Pauses (yields) the script</td>
713</tr>
714</tbody>
715<caption>&nbsp;</caption>
716<tbody>
717<tr>
718<td>delete script</td>
719<td>
720<pre>SID.delete()
721</pre>
722</td>
723<td>&nbsp;</td>
724</tr>
725</tbody>
726<caption>&nbsp;</caption>
727<tbody>
728<tr>
729<td>&nbsp;</td>
730<td>&nbsp;</td>
731<td>Deletes it's local copy of the script, and it's compiled version.</td>
732</tr>
733</tbody>
734</table>
735<p>&nbsp;</p>
736<h2> XMRE? </h2>
737<p>A lot of us came from Meta 7, and some of us liked the Meta 7 extensions to LSL that where part of XMRE. I don't think it's a good idea to just clone XMRE, for a few reasons. Would be much better to just provide similar functionality. Some of it comes for free from Lua anyway, just with a different syntax.</p>
738<p>This script engine is only gonna be source code compatible, as we are not using Mono like every one else, so no such thing as binary compatibility can be provided. Source code is how scripts travel between grids anyway.</p>
739<p>There are many scripts from the millions of SL users, going back almost a decade of SL life. This means there are lots of scripts that originated from SL floating around the OpenSim community. Some are open source, some people brought scripts with them from SL that they wrote themselves, some are being used outside of SL with the permission of the scripts authors. It's theoretically impossible to steal script source code from SL, but it can probably be done through social engineering or some such. So I expect there are some illegal copies of SL scripts out there to. The point is, there's LOTS of scripts from SL. This is why SL compatibility is important. There is a huge pool of available scripts from SL.</p>
740<p>OpenSim added some extensions to LSL, and they are available on all the OpenSim grids, though some might be disabled. There is a smaller pool of scripts available from OpenSim. Scripters in OpenSim expect the those extensions to exist. Being compatible with those extensions would be important, and would help other OpenSim grids adopt this script engine if it turns out to be any good.</p>
741<p>Meta 7 was much smaller, not around for so long, and most scripts there either came from the SL pool of available scripts, the OpenSim pool of available scripts, or where written in Meta 7 by LSL scripters. I don't think the Meta 7 specific pool of scripts is anything other than small, the pool of scripts that need XMRE extensions is probably minuscule. OpenSim scripters don't expect those extensions to be available. So I don't think that being strictly compatible with XMRE is needed. Those small number of scripts written to use XMRE extensions can probably be converted to what I'm about to propose.</p>
742<p>I was lucky that kelly managed to save the reference pages from Meta 7 that covered their extensions in detail. I was able to go over them and figure out what to do. The summary is this - we should be able to provide similar functionality as XMRE, but not an exact clone. Lua already has powerful table stuff that is better than the XMRE array stuff, with a similar syntax for those parts that they share. No need to reinvent that wheel. Switch, continue, and exception handling could be treated as just adding things from other C like languages to the LSL C like language. On the other hand, it's gonna be simpler to just let people use Lua style stuff for these things. Writing an LSL scripting engine is already a huge job, the functionality is there in Lua, we could skip implementing the exact C like syntax and get more important things done. We can add in the C style syntax later if there is much call for it. The event stuff I would already be one third of the way there based on my current design. The other two thirds we could get just by designing the rest of that subsystem to suit. After all, not much difference if we store those structures in C or Lua, since it all has to go to Lua anyway. Might as well do it in Lua, and give the scripters access.</p>
743<p>This XMRE type stuff would be using the //LuaSL:LuaSL engine. Pure //LuaSL:LSL would not have it, and pure //LuaSL:Lua would not need it. So by writing the LuaSL variation first, we get some parts of XMRE like extensions for free, mostly the Lua table stuff that is similar to XMRE arrays, only better.</p>
744<p>&nbsp;</p>
745<h3> flow control - break, case, constant, continue, default, switch </h3>
746<p>Lua has no switch (so no case or default either), but since you can store functions in tables, you can fake it easily enough. We can add the "case" part as a set of anonymous functions stored in a switch table; index the switch table with the value of the switch statement to find the correct function to call; use a metatable to detect when a switch value is missing to call the default anonymous function of the switch; have a switch.fallthrough(x) function that just calls the X case function in the switch table. There, done. Though this does not cater for XMRE case ranges. Case ranges are not normally a part of C like syntax. On the other hand, Lua tables can be indexed by any Lua type, so perhaps case ranges can be dealt with in some way. I'm not sure it's important enough to worry about for now, so leaving it off until someone wants it. Hopefully people will be to distracted by the fact they can use ANY type in case statements, and even mixed types, to miss case ranges. Actually, we could use the same mechanism we use for default, it just checks any case ranges that where registered in the switch table before calling default. That's just my quick and dirty idea, there are more here - <a href="http://lua-users.org/wiki/SwitchStatement">http://lua-users.org/wiki/SwitchStatement</a></p>
747<p>Constant is not really needed, as it's only there to support a limitation of the XMRE case statement. The above implementation has no such limitation, so we can leave it off.</p>
748<p>There is break in Lua, but no continue. Continue can be done by a jump to a label anyway, though that's a Lua 5.2 addition, and there might be good reasons to not use Lua 5.2, especially since we are using LuaJIT. <a href="http://lua-users.org/lists/lua-l/2009-11/msg00061.html">http://lua-users.org/lists/lua-l/2009-11/msg00061.html</a> might help to explain why.</p>
749<p><a href="http://lua-users.org/wiki/ContinueProposa">http://lua-users.org/wiki/ContinueProposa</a> would be of interest.</p>
750<p>&nbsp;</p>
751<h3> arrays </h3>
752<p>Lua tables have similar functionality to XMRE arrays, but I think are more powerful. So might as well just use them.</p>
753<p>LSL is statically typed, while Lua is dynamically typed. Which means we don't need to deal with the XMRE array type detection and conversion stuff. Everything is a first class value, and can be stored as table elements, or used as indexes, except the special value nil, which is similar to the XMRE value undef. XMRE arrays use lists for multi dimensional arrays, but Lua does not really have that concept. It's easy enough to store tables in tables though, so sparse matrices can be done. Lua has proper arrays, so long as you don't mind counting from 1, though I think I'll fix that. So for example, you might have a list, and since I'm converting that to a Lua table, you could use Lua table syntax with it -</p>
754<pre>myList[42] = "Life, the universe, and everything";
755llOwnerSay(myList[42]);
756myList[foo + bar] = 42;
757myOtherList["foo"] = myList;
758myOtherList[myList] = 42.0; // Using a Lua table as an index.
759
760</pre>
761<p>That last one is different from what it means in XMRE. XMRE uses lists as array indexes to support multi dimension arrays, Lua just uses it to index the single dimension table element that happens to have a table as the index.</p>
762<p>Since they are Lua tables, we can do this sort of thing to -</p>
763<pre>myOtherList["myFunc"] = someFunction; // Yes, this is storing the function, not the return value of the function.
764myOtherList["myFunc"](x, y); // Calling the function we just stored.
765myOtherList.myFunc(x, y); // Same as the last one.
766
767</pre>
768<p>That last one uses a Lua syntactic sugar short cut. It works for table indexes that are strings with no spaces in them. This sort of thing essentially comes for free, since my script engine converts to Lua before compiling that. People that know Lua already know all those fun things you can do with Lua tables, they are quite powerful.</p>
769<p>Lua table initialisation is a little different, but the LSL parser can handle that -</p>
770<pre>a = {[f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45}
771
772</pre>
773<p>I'll leave it as an exercise for the reader to look up the Lua manual (which that example is taken from) to see what that line does. lol</p>
774<p>OK, commas and semi colons in that line are interchangeable, they just separate array elements. [30] = 23 means that the table element with the index of 30 is assigned the value 23. x = 1 is shorthand for ["x"] = 1, the table element with the string index of "x" is assigned the value 1. [f(1)] = g means that what ever value that the function call f(1) returns is used as an index, it's value is assigned the value of the variable g (no matter what type it is). The rest don't include any index, so are assigned to sequentially numbered indexes starting from 1. Apart from being more powerful, the only real difference with LSL is the use of {} instead of [] to contain the list initialisers.</p>
775<p>&nbsp;</p>
776<h3> exception handling </h3>
777<p>The XMRE exception handling I could not do a clone of anyway, the system exceptions are not specified, except for the divide by zero, and even then I'm not so sure.</p>
778<p>Lua has it's own sorta exception system, it can probably be bent into this sort of shape, but why bother? The alternative is to add stuff to the LSL parser to turn something like normal C++ / Java style exception handling into the Lua system. Not sure it's worth it, so not coming up with a proposal right now. Could be added later if people want it. <a href="http://lua-users.org/wiki/ErrorHandling">http://lua-users.org/wiki/ErrorHandling</a> might be of some interest.</p>
779<p>&nbsp;</p>
780<h3> event handling </h3>
781<p>Event stuff is basically - wait for an event in the middle of some function, save and restore the llDetect*() information for event nesting, call an event handler directly, and put an event you got from the first thing back on the event queue. Note, all LSL functions are called from event handlers, or are the event handlers themselves. Lists are used to store the llDetect*() info, and the events returned / put on the queue, as well as invoking the handler direct. I'm sure it's possible to fake these Lists.</p>
782<p>The method I'll be using to represent LSL states in Lua (as tables with the event handlers stored as functions) already allows direct calling of event handlers anyway. The others are just direct access to the event queue, and to the source of the llDetect*() information so that we may molest them. Sticking the event queue in a table sounds feasible. It would be a good idea to have the llDetect*() stuff in a table with a metatable, where the various llDetect*() functions are functions provided by that metatable. Then they can just stash this table away safely while calling other event handlers direct, passing on a faked up table, or a copy of the original.</p>
783<pre>float timeout = 4.2;
784myEvents = events.wait(timeout, link_message, listen, touch_start); // Functions are first class citizens, so just pass them to the wait function, which has variable arguments.
785myDetects = events.copyDetects();
786default.link_message(LINK_ROOT, 42, "So long, and thanks for all the fish.", llGetOwner());
787events.detects(myDetects);
788events.queue(myEvents);
789
790</pre>
791<p>Or something like that might be feasible.</p>
792<p>XMRE can both wait for events, or have them called in the background, then continue waiting for the events it's waiting for. The above events.wait does not do that. We could pass two tables like this -</p>
793<pre>list waitEvents = [link_message, listen];
794list backgroundEvents = [touch_start, touch_end];
795myEvents = events.wait(timeout, waitEvents, backgroundEvents);
796
797</pre>
798<p>&nbsp;</p>
799<h2> an example </h2>
800<p>This is the current result of compiling the MLP ~pos script (white space adjusted for better readability) -</p>
801<pre>--// Generated code goes here.
802
803local _bit = require("bit")
804local _LSL = require("LSL")
805
806--[[integer]] MAX_BALLS = 6;
807--[[integer]] ch = 0;
808--[[integer]] swap = 0;
809--[[integer]] BallCount = 0;
810--[[string]] pr1 = "";
811--[[string]] pr2 = "";
812--[[integer]] Zoffset = 0;
813--[[vector]] RefPos = _LSL.ZERO_VECTOR;
814--[[rotation]] RefRot = _LSL.ZERO_ROTATION;
815.
816
817function getRefPos()
818 RefPos=_LSL.llGetPos();
819 RefRot=_LSL.llGetRot();
820 Zoffset= _LSL.integerTypecast(_LSL.llGetObjectDesc());
821 RefPos.z --[[+=]] = RefPos.z + --[[float]] Zoffset / 100.;
822end
823
824--[[list]] Pdata = {};
825
826function getPosNew( --[[string]] pdata)
827 Pdata = _LSL.llParseString2List(pdata, {" ", }, {});
828end
829
830function setPos()
831 pr1 = --[[string]] ( --[[vector]] _LSL.llList2String(Pdata, 0) * RefRot + RefPos);
832 pr2 = --[[string]] ( --[[vector]] _LSL.llList2String(Pdata, 2) * RefRot + RefPos);
833 pr1 --[[+=]] = pr1 + --[[string]] (_LSL.llEuler2Rot( --[[vector]] _LSL.llList2String(Pdata, 1) * _LSL.DEG_TO_RAD) * RefRot);
834 pr2 --[[+=]] = pr2 + --[[string]] (_LSL.llEuler2Rot( --[[vector]] _LSL.llList2String(Pdata, 3) * _LSL.DEG_TO_RAD) * RefRot);
835 if (BallCount&gt;1) then
836 _LSL.llSay(ch + swap, pr1);
837 _LSL.llSay(ch + not swap, pr2);
838 else
839 _LSL.llSay(ch, pr1);
840 end
841 local --[[integer]] ix = 0;
842 local function _preIncrement_ix() ix = ix + 1; return ix; end
843 ix = 2;
844 while (ix&lt;BallCount) do
845 _LSL.llSay(ch + ix, --[[string]] ( --[[vector]] _LSL.llList2String(Pdata, 2 * ix) * RefRot + RefPos) .. --[[string]] (_LSL.llEuler2Rot( --[[vector]] _LSL.llList2String(Pdata, 2 * ix + 1) * _LSL.DEG_TO_RAD) * RefRot));
846 _preIncrement_ix();
847 end
848end
849
850function getChan()
851 ch= _LSL.integerTypecast(("0x" .. _LSL.llGetSubString( --[[string]] _LSL.llGetKey(), -4, -1)));
852end.
853
854--[[state]] _defaultState = {};.
855
856_defaultState.state_entry = function()
857 getRefPos();
858 getChan();
859end
860
861_defaultState.on_rez = function( --[[integer]] arg)
862 getRefPos();
863 getChan();
864end
865
866_defaultState.link_message = function( --[[integer]] from, --[[integer]] num, --[[string]] cmd, --[[key]] pkey)
867 if (cmd == "PRIMTOUCH") then
868 return;
869 end
870 if (num == 1 and cmd == "STOP") then
871 swap = 0;
872 return;
873 end
874 if (num) then
875 return;
876 end
877 if (cmd == "POSE") then
878 local --[[list]] parms=_LSL.llCSV2List( --[[string]] pkey);
879 BallCount=_LSL.llList2Integer(parms, 1);
880 return;
881 elseif (cmd == "POSEPOS") then
882 getPosNew( --[[string]] pkey);
883 setPos();
884 elseif (cmd == "SWAP") then
885 swap= _bit.band( _LSL.integerTypecast(( --[[string]] pkey)) , 1) ;
886 _LSL.llSay(ch + swap, pr1);
887 _LSL.llSay(ch + not swap, pr2);
888 elseif (cmd == "REPOS") then
889 getRefPos();
890 elseif (_LSL.llGetSubString(cmd, 0, 0) == "Z") then
891 local --[[integer]] change = 0;
892 if (_LSL.llGetSubString(cmd, 1, 1) == "+") then
893 change = _LSL.integerTypecast(_LSL.llGetSubString(cmd, 2, 10)) ;
894 else
895 change = _LSL.integerTypecast(_LSL.llGetSubString(cmd, 1, 10)) ;
896 end
897 Zoffset --[[+=]] = Zoffset + change;
898 RefPos.z --[[+=]] = RefPos.z + --[[float]] change / 100.;
899 setPos();
900 _LSL.llOwnerSay("Height Adjustment: change by " .. --[[string]] change .. "cm, new offset: " .. --[[string]] Zoffset .. "cm");
901 _LSL.llSetObjectDesc( --[[string]] Zoffset);
902 elseif (cmd == "GETREFPOS") then
903 _LSL.llMessageLinked(_LSL.LINK_THIS, 8, --[[string]] RefPos, --[[string]] RefRot);
904 end
905end.
906
907_LSL.stateChange(_defaultState)
908
909--// End of generated code.
910
911</pre>
912
913<p>Here's a wild idea - streaming source code. Start feeding the .lua files to the compiler when you start downloading it, so it compiles as it's downloading. Dunno yet if that would help, might just get stuck early on waiting for the entire file.</p>
914
915<p><br /> This work is licensed under a <a href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>.</p>
916</body>
917</html>