aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/LuaSL/LuaSL_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/LuaSL/LuaSL_main.c')
-rw-r--r--src/LuaSL/LuaSL_main.c708
1 files changed, 708 insertions, 0 deletions
diff --git a/src/LuaSL/LuaSL_main.c b/src/LuaSL/LuaSL_main.c
new file mode 100644
index 0000000..0a40712
--- /dev/null
+++ b/src/LuaSL/LuaSL_main.c
@@ -0,0 +1,708 @@
1
2#include "LuaSL.h"
3
4
5static int CPUs = 4;
6static Eina_Strbuf *clientStream;
7
8
9static Eina_Bool _sleep_timer_cb(void *data)
10{
11 script *script = data;
12 gameGlobals *ourGlobals = script->game;
13
14 PD("Waking up %s", script->SID);
15 sendToChannel(ourGlobals, script->SID, "return 0.0");
16 return ECORE_CALLBACK_CANCEL;
17}
18
19static Eina_Bool _timer_timer_cb(void *data)
20{
21 script *script = data;
22 gameGlobals *ourGlobals = script->game;
23
24 PD("Timer for %s", script->SID);
25 sendToChannel(ourGlobals, script->SID, "events.timer()");
26 return ECORE_CALLBACK_RENEW;
27}
28
29static script *findThem(gameGlobals *ourGlobals, const char *base, const char *text)
30{
31 char name[PATH_MAX];
32 char *temp;
33
34 strncpy(name, base, PATH_MAX);
35 if ((temp = rindex(name, '/')))
36 temp[1] = '\0';
37 strcat(name, text);
38 if ((temp = rindex(name, '"')))
39 temp[0] = '\0';
40 strcat(name, ".lsl");
41 return eina_hash_find(ourGlobals->names, name);
42}
43
44static void resetScript(script *victim)
45{
46 gameGlobals *ourGlobals = victim->game;
47
48 PD("Resetting %s", victim->fileName);
49 // TODO - now what?
50}
51
52void scriptSendBack(void * data)
53{
54 scriptMessage *message = data;
55 gameGlobals *ourGlobals = message->script->game;
56
57 if (0 == strncmp(message->message, "llSleep(", 8))
58 ecore_timer_add(atof(&(message->message)[8]), _sleep_timer_cb, message->script);
59 else if (0 == strncmp(message->message, "llSetTimerEvent(", 16))
60 {
61 message->script->timerTime = atof(&(message->message)[16]);
62 if (0.0 == message->script->timerTime)
63 {
64 if (message->script->timer)
65 ecore_timer_del(message->script->timer);
66 message->script->timer = NULL;
67 }
68 else
69 message->script->timer = ecore_timer_add(message->script->timerTime, _timer_timer_cb, message->script);
70 }
71 else if (0 == strncmp(message->message, "llSetScriptState(", 17))
72 {
73 script *them;
74
75 if ((them = findThem(ourGlobals, message->script->fileName, &(message->message[18]))))
76 {
77 char *temp = rindex(&(message->message[18]), ',');
78
79 if (temp)
80 {
81 temp++;
82 while (isspace(*temp))
83 temp++;
84 if ('1' == *temp)
85 sendToChannel(ourGlobals, them->SID, "start()");
86 else
87 sendToChannel(ourGlobals, them->SID, "stop()");
88 PD("Stopped %s", them->fileName);
89 }
90 else
91 PE("Missing script state in llSetScriptState(%s, )", them->fileName);
92 }
93 else
94 {
95 char *temp = rindex(&(message->message[18]), '"');
96
97 if (temp)
98 *temp = '\0';
99 PE("Can't stop script, can't find %s", &(message->message[18]));
100 }
101 }
102 else if (0 == strncmp(message->message, "llResetOtherScript(", 19))
103 {
104 script *them;
105
106 if ((them = findThem(ourGlobals, message->script->fileName, &(message->message[20]))))
107 resetScript(them);
108 }
109 else if (0 == strncmp(message->message, "llResetScript(", 14))
110 resetScript(message->script);
111 else
112 sendBack(ourGlobals, message->script->client, message->script->SID, message->message);
113 free(message);
114}
115
116static Eina_Bool _add(void *data, int type __UNUSED__, Ecore_Con_Event_Client_Add *ev)
117{
118 ecore_con_client_timeout_set(ev->client, 0);
119 return ECORE_CALLBACK_RENEW;
120}
121
122static Eina_Bool _data(void *data, int type __UNUSED__, Ecore_Con_Event_Client_Data *ev)
123{
124 gameGlobals *ourGlobals = data;
125 char buf[PATH_MAX];
126 char SID[PATH_MAX];
127 const char *command;
128 char *ext;
129
130 eina_strbuf_append_length(clientStream, ev->data, ev->size);
131 command = eina_strbuf_string_get(clientStream);
132 while ((ext = index(command, '\n')))
133 {
134 int length = ext - command;
135
136 strncpy(SID, command, length + 1);
137 SID[length] = '\0';
138 eina_strbuf_remove(clientStream, 0, length + 1);
139 ext = index(SID, '.');
140 if (ext)
141 {
142 ext[0] = '\0';
143 command = ext + 1;
144 if (0 == strncmp(command, "compile(", 8))
145 {
146 char *temp;
147 char *file;
148
149 strcpy(buf, &command[8]);
150 temp = buf;
151 file = temp;
152 while (')' != temp[0])
153 temp++;
154 temp[0] = '\0';
155
156 PD("Compiling %s, %s.", SID, file);
157 if (compileLSL(ourGlobals, ev->client, SID, file, FALSE))
158 {
159 script *me = calloc(1, sizeof(script));
160
161 gettimeofday(&me->startTime, NULL);
162 strncpy(me->SID, SID, sizeof(me->SID));
163 strncpy(me->fileName, file, sizeof(me->fileName));
164 me->game = ourGlobals;
165 me->client = ev->client;
166 eina_hash_add(ourGlobals->scripts, me->SID, me);
167 eina_hash_add(ourGlobals->names, me->fileName, me);
168 sendBack(ourGlobals, ev->client, SID, "compiled(true)");
169 }
170 else
171 sendBack(ourGlobals, ev->client, SID, "compiled(false)");
172 }
173 else if (0 == strcmp(command, "run()"))
174 {
175 script *me;
176 char buf[PATH_MAX];
177
178 me = eina_hash_find(ourGlobals->scripts, SID);
179 if (me)
180 {
181 sprintf(buf, "%s.lua.out", me->fileName);
182 newProc(buf, TRUE, me);
183 }
184 }
185 else if (0 == strcmp(command, "exit()"))
186 {
187 PD("Told to exit.");
188 ecore_main_loop_quit();
189 }
190 else
191 {
192 const char *status = NULL;
193
194 status = sendToChannel(ourGlobals, SID, command);
195 if (status)
196 PE("Error sending command %s to script %s : %s", command, SID, status);
197 }
198 }
199
200 // Get the next blob to check it.
201 command = eina_strbuf_string_get(clientStream);
202 }
203
204 return ECORE_CALLBACK_RENEW;
205}
206
207static Eina_Bool _del(void *data, int type __UNUSED__, Ecore_Con_Event_Client_Del *ev)
208{
209 gameGlobals *ourGlobals = data;
210
211 if (ev->client)
212 {
213 PD("No more clients, exiting.");
214 ecore_con_client_del(ev->client);
215 ecore_main_loop_quit();
216 }
217 return ECORE_CALLBACK_RENEW;
218}
219
220int main(int argc, char **argv)
221{
222 gameGlobals ourGlobals;
223 int result = EXIT_FAILURE;
224
225 memset(&ourGlobals, 0, sizeof(gameGlobals));
226 ourGlobals.address = "127.0.0.1";
227 ourGlobals.port = 8211;
228
229 if (eina_init())
230 {
231 ourGlobals.logDom = loggingStartup("LuaSL", ourGlobals.logDom);
232 ourGlobals.scripts = eina_hash_string_superfast_new(NULL);
233 ourGlobals.names = eina_hash_string_superfast_new(NULL);
234 if (ecore_con_init())
235 {
236 if (ecore_con_init())
237 {
238 if ((ourGlobals.server = ecore_con_server_add(ECORE_CON_REMOTE_TCP, ourGlobals.address, ourGlobals.port, &ourGlobals)))
239 {
240 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_ADD, (Ecore_Event_Handler_Cb) _add, &ourGlobals);
241 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DATA, (Ecore_Event_Handler_Cb) _data, &ourGlobals);
242 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DEL, (Ecore_Event_Handler_Cb) _del, &ourGlobals);
243 ecore_con_server_timeout_set(ourGlobals.server, 0);
244 ecore_con_server_client_limit_set(ourGlobals.server, -1, 0);
245 ecore_con_server_timeout_set(ourGlobals.server, 10);
246 ecore_con_server_client_limit_set(ourGlobals.server, 3, 0);
247 clientStream = eina_strbuf_new();
248
249 if (edje_init())
250 {
251 int i;
252
253 result = 0;
254 compilerSetup(&ourGlobals);
255 luaprocInit();
256 for (i = 0; i < CPUs; i++)
257 {
258 if ( sched_create_worker( ) != LUAPROC_SCHED_OK )
259 PEm("Error creating luaproc worker thread.");
260 }
261 ecore_main_loop_begin();
262
263 // TODO - this is what hangs the system, should change from raw pthreads to ecore threads.
264 sched_join_workerthreads();
265 edje_shutdown();
266 }
267 else
268 PCm("Failed to init edje!");
269 }
270 else
271 PCm("Failed to add server!");
272 ecore_con_shutdown();
273 }
274 else
275 PCm("Failed to init ecore_con!");
276 ecore_shutdown();
277 }
278 else
279 PCm("Failed to init ecore!");
280 }
281 else
282 fprintf(stderr, "Failed to init eina!");
283
284 return result;
285}
286
287
288/* Stuff to be merged in later.
289
290#ifdef _WIN32
291# define FMT_SIZE_T "%Iu"
292#else
293# define FMT_SIZE_T "%zu"
294#endif
295
296#define MAX_LUA_MEM (64 * 1024)) // LSL Mono is 64KB, edje Lua is 4MB. (4 * (1024 * 1024))
297
298#define _edje_lua2_error(L, err_code) _edje_lua2_error_full(__FILE__, __FUNCTION__, __LINE__, L, err_code)
299
300
301typedef struct _Edje_Lua_Alloc Edje_Lua_Alloc;
302
303struct _Edje_Lua_Alloc
304{
305 size_t max, cur;
306};
307
308static void *
309_elua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
310{
311 Edje_Lua_Alloc *ela = ud;
312 void *ptr2 = NULL;
313
314 if (ela)
315 {
316 ela->cur += nsize - osize;
317 if (ela->cur > ela->max)
318 {
319 printf("Lua memory limit of " FMT_SIZE_T " bytes reached (" FMT_SIZE_T " allocated)", ela->max, ela->cur);
320 }
321 else if (nsize == 0)
322 {
323 free(ptr);
324 }
325 else
326 {
327 ptr2 = realloc(ptr, nsize);
328 if (NULL == ptr2)
329 printf("Lua cannot re-allocate " FMT_SIZE_T " bytes", nsize);
330 }
331 }
332 else
333 printf("Lua cannoct allocate memory, no Edje_Lua_Alloc");
334
335 return ptr2;
336}
337
338static int panics = 0;
339static int
340_elua_custom_panic(lua_State *L) // Stack usage [-0, +0, m]
341{
342 // If we somehow manage to have multiple panics, it's likely due to being out
343 // of memory in the following lua_tostring() call.
344 panics++;
345 if (panics)
346 {
347 printf("Lua PANICS!!!!!");
348 }
349 else
350 {
351 printf("Lua PANIC!!!!!: %s", lua_tostring(L, -1)); // Stack usage [-0, +0, m]
352 }
353 // The docs say that this will cause an exit(EXIT_FAILURE) if we return,
354 // and that we we should long jump some where to avoid that. This is only
355 // called for things not called from a protected environment. We always
356 // use pcalls though, except for the library load calls. If we can't load
357 // the standard libraries, then perhaps a crash is the right thing.
358 return 0;
359}
360
361static void
362_edje_lua2_error_full(const char *file, const char *fnc, int line,
363 lua_State *L, int err_code) // Stack usage [-0, +0, m]
364{
365 const char *err_type;
366
367 switch (err_code)
368 {
369 case LUA_ERRRUN:
370 err_type = "runtime";
371 break;
372 case LUA_ERRSYNTAX:
373 err_type = "syntax";
374 break;
375 case LUA_ERRMEM:
376 err_type = "memory allocation";
377 break;
378 case LUA_ERRERR:
379 err_type = "error handler";
380 break;
381 default:
382 err_type = "unknown";
383 break;
384 }
385 printf("Lua %s error: %s\n", err_type, lua_tostring(L, -1)); // Stack usage [-0, +0, m]
386}
387
388static int errFunc(lua_State *L)
389{
390 int i = 0;
391 lua_Debug ar;
392
393 while (lua_getstack(L, i++, &ar))
394 {
395 if (lua_getinfo(L, "nSlu", &ar))
396 {
397 if (NULL == ar.name)
398 ar.name = "DUNNO";
399 printf("Lua error in the %s %s %s @ line %d in %s\n%s!", ar.what, ar.namewhat, ar.name, ar.currentline, ar.short_src, ar.source);
400 }
401 }
402 return 0;
403}
404
405void runLuaFile(gameGlobals *ourGlobals, const char *filename)
406{
407 PD("Starting %s", filename);
408 newProc(filename, TRUE);
409
410// TODO, should set up our panic and errfunc as below. Plus the other TODO stuff.
411
412
413// TODO - hack up LuaJIT so that we can limit memory per state.
414// lua_setallocf(L, _elua_alloc, &ela); // LuaJIT uses a heavily hacked up dlmalloc. Seems that standard realloc is not so thread safe?
415 lua_atpanic(L, _elua_custom_panic);
416// TODO - Sandbox out what this opens. See lib_init.c from LuaJIT.
417// Just noticed this in the LuaJIT docs - "To change or extend the list of standard libraries to load, copy src/lib_init.c to your project and modify it accordingly. Make sure the jit library is loaded or the JIT compiler will not be activated."
418 luaL_openlibs(L);
419
420 lua_pushcfunction(L, errFunc);
421 ...
422 if ((err = lua_pcall(L, 0, 0, -2)))
423 _edje_lua2_error(L, err);
424}
425*/
426
427
428
429
430/* What we need to do, and how we might do it.
431 *
432 * Get configuration info.
433 *
434 * Some of the configuration info we need is tucked away in OpenSim .ini files. So we have to read that.
435 * Eet is probably not so useful here, as we would have to write a UI tool, and there's no UI otherwise.
436 *
437 * Multi task!
438 *
439 * Luaproc starts up X worker threads. Each one takes the next ready Lua state and runs it until it blocks waiting for a channel message, or yields.
440 * The current mainloop waits for events and commands from the message channel, executes them, then goes back to waiting.
441 * So that is fine in general, so long as the LSL event handlers actually return in a reasonable time.
442 * We need to be able to yield at suitable places, or be able to force a yield. Both without slowing things down too much.
443 *
444 * Watchdog thread.
445 *
446 * It's easy enough to have a watchdog thread wake up every now and then to check if any given Lua state has been hogging it's CPU for too long.
447 * The hard part is forcing Lua states to yield cleanly, without slowing performance too much.
448 *
449 * Identifying scripts. - OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
450 *
451 * We need to be able to identify scripts so that messages can get to the right ones. Also the objects they are in.
452 * And do it all in a way that OpenSim is happy with.
453 * Copies of the same script in the same prim would have the same asset UUID, but different name, though they would run independently.
454 * Copies of the same script in different prims could have the same asset UUID AND the same name.
455 * OpenSim seems to be using a UUID to identify single scripts, and a uint to identify single prims, when sending events for either case.
456 * The UUID is from the script item in the prims inventory.
457 * There is also an asset UUID (the one printed out on the console at script startup time) that points to the source code in the prim.
458 * Which will be identical to the asset UUID for the multiple copies of the same script.
459 *
460 * script assetID
461 * UUID of the script source in the grids asset database, also the script source in the prim.
462 *
463 * script itemID
464 * UUID of this instance of the running script.
465 * UUID of the scripts binary in the prims inventory.
466 * This is the one used to identify the running script.
467 *
468 * prim uint localID
469 * Some sort of number representing the prim the script is running in.
470 * Events are sometimes sent to this.
471 *
472 * path/filename
473 * An invention of LuaSL, coz we store stuff as files.
474 *
475 * OpenSim says "compile this assetID for this itemID, in this prim uint"
476 * Current infrastructure does not allow easy sending of the script source, but we don't have ROBUST code to get it either.
477 * ROBUST is the way to go though, coz we can sneakily start to suck other stuff, like prim contents across when needed.
478 * Though that sort of thing needs access to the local sim databases to lookup the prim and it's other contents. sigh
479 * I think that new script and notecard contents get new assetIDs anyway, so keeping an eye on assets.create_time or asset_access_time wont help much.
480 *
481 * OpenSim says "start / stop this itemID"
482 * Already catered for.
483 *
484 * What does OpenSim REALLY do?
485 *
486 * Region/Framework/Scenes/Scene.Inventory.cs - CapsUpdateTaskInventoryScriptAsset(IClientAPI remoteClient, UUID itemId, UUID primId, bool isScriptRunning, byte[] data)
487 * remoteClient
488 * itemID - UUID of the script source.
489 * primID - UUID of the prim it is in.
490 * isScriptRunning
491 * data - the script source code.
492 * Called when a user saves the script. itemID stays the same, but we get a new assetID, for the new source code asset.
493 * Looks up the item in the prim.
494 * AssetBase asset = CreateAsset(item.Name, item.Description, (sbyte)AssetType.LSLText, data, remoteClient.AgentId);
495 * AssetService.Store(asset);
496 * stashes the new assetID in the item
497 * updates the item in the prim
498 * if (isScriptRunning)
499 * part.Inventory.RemoveScriptInstance(item.ItemID, false);
500 * part.Inventory.CreateScriptInstance(item.ItemID, 0, false, DefaultScriptEngine, 0);
501 * errors = part.Inventory.GetScriptErrors(item.ItemID);
502 *
503 * CreateScriptInstance() is generally called to start scripts, part.ParentGroup.ResumeScripts(); is usually called after CreateScriptInstance()
504 *
505 * Region/Framework/Scenes/SceneObjectPartInventory.cs - CreateScriptInstance(UUID itemId, int startParam, bool postOnRez, string engine, int stateSource)
506 * looks up the itemID, then calls the real one -
507 * Region/Framework/Scenes/SceneObjectPartInventory.cs - CreateScriptInstance(TaskInventoryItem item, int startParam, bool postOnRez, string engine, int stateSource)
508 * get the asset from the asset database using the items assetID
509 * restores script state if needed
510 * converts asset.data to a string called script
511 * m_part.ParentGroup.Scene.EventManager.TriggerRezScript(m_part.LocalId, item.ItemID, script, startParam, postOnRez, engine, stateSource);
512 * QUIRK - if it's a sim border crossing, TriggerRezScript() gets called with empty script source.
513 *
514 * Region/ScriptEngine/XEngine/XEngine.cs - AddRegion(Scene scene)
515 * m_log.InfoFormat("[XEngine] Initializing scripts in region {0}", scene.RegionInfo.RegionName);
516 * gets the script config info, which is the same damn stuff for each sim. Pffft
517 * Think it relies on the scenes event manager to call OnRezScript() -
518 * m_Scene.EventManager.OnRezScript += OnRezScript;
519 *
520 * Region/Framework/Scenes/EventManager.cs - TriggerRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource)
521 * Loops through Scene.EventManager.OnRezScript calling them.
522 *
523 * Region/ScriptEngine/XEngine/XEngine.cs - OnRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource)
524 * Looks at the script source to figure out if it's an XEngine script.
525 * Either queues the script for later, or does it direct.
526 * Region/ScriptEngine/XEngine/XEngine.cs - DoOnRezScript() is passed an array holding -
527 * localID is a uint that represents the containing prim in the current scene
528 * itemID is the UUID of the script in the prims contents
529 * script is the script source code.
530 * startParam is the scripts startParam
531 * postOnRez
532 * stateSource is an integer saying how we where started, used to trigger the appropriate startup events.
533 * uses localID to look up the prim in the scene, then looks inside that for the itemID to find the assetID.
534 * m_Compiler.PerformScriptCompile(script, assetID.ToString(), item.OwnerID, out assembly, out linemap);
535 * Which is in Region/ScriptEngine/Shared/CodeTools/Compiler.cs
536 * instance = new ScriptInstance(this, part, itemID, assetID, assembly, m_AppDomains[appDomain], part.ParentGroup.RootPart.Name, item.Name, startParam, postOnRez, stateSource, m_MaxScriptQueue);
537 * Region/ScriptEngine/Shared/Instance/ScriptInstance.cs - ScriptInstance(IScriptEngine engine, SceneObjectPart part, UUID itemID, UUID assetID, string assembly, AppDomain dom, string primName, string scriptName, int startParam, bool postOnRez, StateSource stateSource, int maxScriptQueue)
538 * inits all the APIs
539 * loads in any saved state if it can find one
540 * m_log.DebugFormat("[XEngine] Loaded script {0}.{1}, script UUID {2}, prim UUID {3} @ {4}.{5}", part.ParentGroup.RootPart.Name, item.Name, assetID, part.UUID, part.ParentGroup.RootPart.AbsolutePosition, part.ParentGroup.Scene.RegionInfo.RegionName);
541 *
542 * Soooo, when a script is saved -
543 * the new source is saved in the asset database
544 * The script item in the prim gets the new assetID
545 * if the script is running -
546 * remove the old script instance (item.ItemID)
547 * create a new one (item.ItemID)
548 * get the source code from the asset database (item.assetID)
549 * restore script state
550 * TriggerRezOnScript()
551 * Loop through all those that are interested, incuding XEngine.onRezScript()
552 *** check the first line to see if it's an XEngine script
553 * sooner or later passes it to XEngine.DoOnRezScript()
554 * looks up localID to get the prim
555 * looks inside prim to get the script from itemID
556 * gets the assetID from the script item
557 * compiles the script
558 * creates the script instance
559 * loads up the APIs
560 * restores any script state
561 * calls instance.Init() which is Region/ScriptEngine/Shared/Instance/ScriptInstance.cs - Init()
562 * passes the usual startup events to the script.
563 * part.ParentGroup.ResumeScripts()
564 *
565 * At the *** marked point, LuaSL.onRezScript should -
566 * check the first line to see if it's an LuaSL script
567 * looks up localID to get the prim
568 * looks inside prim to get the script from itemID
569 * gets the assetID from the script item
570 * filename encode the sim name, object name, and script name
571 * replace anything less than 0x21, DEL " * / : < > ? \ | + [ ] - , . ( ) $ % # @ from - http://en.wikipedia.org/wiki/Filename plus a few more
572 * THEN reduce to 254 characters
573 * NOTE the object names might be identical, disambiguate them.
574 * write the script to a file - /script/engine/path/sim_name/objects/object_name/script_name
575 * send the itemID.compile(/script/engine/path/sim_name/objects/object_name/script_name) message to the script engine's socket
576 *
577 *
578 * Object inventory "cache".
579 *
580 * This code currently pretends that there is a local file based sim object store available.
581 * I think it would be a good idea to abuse the OpenSim cache system to produce that file based object store.
582 * It will help with the "damn OpenSim's asset database has to be a bottomless pit" monster design flaw.
583 * Prim contents must all be unique names anyway, and there are SOME constraints on contents names, so probably don't have to do much to convert an item name to a legal file name.
584 * Oops, names can have directory slashes in them. lol
585 * On the other hand, sim objects CAN have the same name.
586 *
587 * So we got sim directories, with an objects directory inside it, with object directories inside that. The object directories have object files in them. This is all like the test setup that is here.
588 * We need metadata. Sim metadata, object metadata, and object contents metadata. That can be done with a "foo.omg" file at each level.
589 * sim/index.omg - the list of object name.UUIDs, their X,Y,Z location, size, and rotation.
590 * sim/objects/objectName_UUID/index.omg - the list of contents names, item UUIDs, asset UUIDs, and types.
591 * sim/objects/objectName/subObjectName - the list of ITS contents names, item UUIDs, asset UUIDs, and types.
592 *
593 * Script start, stop, reset. - OpenSim/Region/ScriptEngine/Interfaces/IScriptEngine.cs
594 *
595 * Scripts and users have to be able to start, stop, and reset scripts.
596 * Users have to be able to see the start / stopped status of scripts from the viewer.
597 * Which means if the script does it, we have to tell OpenSim.
598 * Naturally, if OpenSim does it, it has to tell us.
599 * Should be able to do both by passing textual Lua function calls between OpenSim and LuaSL.
600 *
601 * Event handling, llDetect*() functions.
602 *
603 * OpenSim will generate events at random times and send us relevant information for llDetect*() functions and the handler calls.
604 * These should come through the scripts main loop eventually.
605 *
606 * Send messages from / to OpenSim and ROBUST.
607 *
608 * ROBUST uses HTTP for the communications, and some sort of XML, probably XML-RPC.
609 * OpenSim has some sort of generic mechanism for talking to script engines in C#. I want to turn that into a socket based, pass textual Lua function calls, type system.
610 * That assumes C# has some sort of semi decent introspection or reflection system.
611 * After a minimum of research, I have come to the conclusion that C# has suitable introspection, and will go ahead with my plans, leaving that problem to the C# coders.
612 * Looks like OpenSim is already using a bit of C# introspection for ll*() functions anyway.
613 * The scripts main loop can already deal with incoming commands as a string with a Lua function call in it.
614 *
615 * Send messages from / to Lua or C, and wait or not wait.
616 *
617 * Luaproc channels are distinct from Lua states, but some Lua state has to create them before they can be used.
618 * On the other hand, looks like broadcasting messages is not really catered for, it's first come first served.
619 * luaproc.send() can send multiple messages to a single channel. It blocks if no one is listening.
620 * This was done to simplify things for the luaproc programmers, who suggest creating more Lua states to deal with asynchronous message sending.
621 * luaprog.receive() gets a channel message. It can block waiting, or not block.
622 * I already hacked up C code to send and not block. I might have broken the luaproc.send() ability to send multiple messages.
623 * Before I hacked it up, actual message sending was done by copying the contents of the sending Lua states stack to the receiver states stack.
624 * This is the simple method for the luaproc programmers, as both states are in the context of a luaproc call, so both stacks are available.
625 * My hacked up version either takes one message from the sender, or is passed one from C. The C call just returns if there is no one waiting on that channel.
626 * luaproc.send() calls that C function after taking a single message from the stack, and block waits as usual if the C call cannot deliver.
627 * Don't think there is C to receive messages, luaproc seems to be lacking entirely in C side API.
628 * NOTE - Sending from C means that the message goes nowhere if no one is waiting for it.
629 * SOOOO, we may need to queue messages to.
630 * Just chuck them in a FIFO per channel, and destroy the FIFO when the channel get's destroyed.
631 * See if I can create the SID channel in C before I start the Lua state running.
632 * Edje messages might have to be used instead, or some hybrid.
633 *
634 * Main loop is waiting on messages, and that's the main driver. Luaproc is fine with that. Good for events.
635 * End of event handler -
636 * just wait for the next event.
637 * Stop a script from LSL -
638 * gotta find it's SID from it's name, and the prim UUID
639 * send the message
640 * wait for it to get the message - BUT we don't really want to wait.
641 * Stop a script from OpenSim -
642 * we should have it's SID from OpenSim, just send the message from C, no need to wait.
643 * Start a script -
644 * if it's stopped, it's already waiting for the message.
645 * if it's not stopped, then we don't care. BUT then we might be waiting for it to get the message if starting it from LSL.
646 * Reset a script -
647 * probably should be done from C anyway, and can reuse the libraries like luaproc likes to do.
648 * ask C to reset it.
649 * LSL calls a function we have to hand to OpenSim -
650 * send the message to C, wait.
651 * C eventually sends a message back.
652 * Sleep -
653 * tell C it's waiting for the wake up message.
654 * wait for the wake up message.
655 *
656 * C needs -
657 * Lua call for stop script.
658 * get the SID from the name, and the prim UUID.
659 * send the stop message to the SID.
660 * send something to OpenSim so it knows.
661 * return to Lua.
662 * Lua call for start script.
663 * get the SID from the name, and the prim UUID.
664 * send the start message to the SID.
665 * send something to OpenSim so it knows.
666 * return to Lua.
667 * Lua call for reset other script.
668 * get the SID from the name, and the prim UUID.
669 * figure out which Lua state it is.
670 * fall through to "reset this script", only with the script set to the found one.
671 * Lua call for reset this script.
672 * get luaproc to close this Lua state
673 * reload the script file
674 * start it again, reusing the previous Lua state, or which ever one luaproc wants to use.
675 * Lua call for sending a function to OpenSim.
676 * Lua first strings up the function call and args, with SID.
677 * C packs it off to opensim.
678 * C puts Lua state on the "waiting for message" queue if a return value is needed.
679 * OpenSim sends back the return value, business as usual.
680 * Lua call for sleep.
681 * setup an ecore timer callback
682 * put the Lua state into "waiting for message" queue.
683 * ecore timer callback sends the wake up message.
684 *
685 * Time and timers, plus deal with time dilation.
686 *
687 * Various LSL functions deal with time, that's no problem.
688 * Some might deal with time dilation, which means dealing with that info through OpenSim.
689 * There is a timer event, which should be done through ecore timers and whatever message system we end up with.
690 * Finally, a sleep call, which can be done with ecore timer and messages to.
691 *
692 * Deal directly with MySQL and SQLite databases.
693 *
694 * The script engine can run on the same computer as the sim server, that's how OpenSim does stuff. So we can directly access the database the sim server has, which gives us access to sim object metadata.
695 * Changing that metadata might require us to inform OpenSim about the changes. It's entirely possible that different changes do or do not need that.
696 * Esskyuehl may be suitable, though it's still in the prototype stage.
697 *
698 * Serialise the script state, send it somewhere.
699 *
700 * Lua can generally serialise itself as as a string of code to be executed at the destination. There might be some C side state that needs to be take care of as well. We shall see.
701 *
702 * Email, HTTP, XML-RPC?
703 *
704 * LSL has functions for using these as communications methods. We should implement them ourselves eventually, but use the existing OpenSim methods for now.
705 * Note that doing it ourselves may cause issues with OpenSim doing it for some other script engine.
706 * Azy might be suitable, but it's also in prototype.
707 *
708*/