diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs new file mode 100644 index 0000000..a24036a --- /dev/null +++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstBackend.cs | |||
@@ -0,0 +1,657 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Threading; | ||
30 | using System.Collections.Generic; | ||
31 | using OpenMetaverse; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.ScriptEngine.Shared; | ||
34 | using OpenSim.Region.ScriptEngine.Shared.Api; | ||
35 | using log4net; | ||
36 | |||
37 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
38 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
39 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
40 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
41 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
42 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
43 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
44 | |||
45 | namespace OpenSim.Region.ScriptEngine.XMREngine | ||
46 | { | ||
47 | /****************************************************\ | ||
48 | * This file contains routines called by scripts. * | ||
49 | \****************************************************/ | ||
50 | |||
51 | public class XMRLSL_Api : LSL_Api | ||
52 | { | ||
53 | public AsyncCommandManager acm; | ||
54 | private XMRInstance inst; | ||
55 | |||
56 | public void InitXMRLSLApi(XMRInstance i) | ||
57 | { | ||
58 | acm = AsyncCommands; | ||
59 | inst = i; | ||
60 | } | ||
61 | |||
62 | protected override void ScriptSleep(int ms) | ||
63 | { | ||
64 | inst.Sleep(ms); | ||
65 | } | ||
66 | |||
67 | public override void llSleep(double sec) | ||
68 | { | ||
69 | inst.Sleep((int)(sec * 1000.0)); | ||
70 | } | ||
71 | |||
72 | public override void llDie() | ||
73 | { | ||
74 | inst.Die(); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * @brief Seat avatar on prim. | ||
79 | * @param owner = true: owner of prim script is running in | ||
80 | * false: avatar that has given ANIMATION permission on the prim | ||
81 | * @returns 0: successful | ||
82 | * -1: no permission to animate | ||
83 | * -2: no av granted perms | ||
84 | * -3: av not in region | ||
85 | */ | ||
86 | /* engines should not have own API | ||
87 | public int xmrSeatAvatar (bool owner) | ||
88 | { | ||
89 | // Get avatar to be seated and make sure they have given us ANIMATION permission | ||
90 | |||
91 | UUID avuuid; | ||
92 | if (owner) { | ||
93 | avuuid = inst.m_Part.OwnerID; | ||
94 | } else { | ||
95 | if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION) == 0) { | ||
96 | return -1; | ||
97 | } | ||
98 | avuuid = m_item.PermsGranter; | ||
99 | } | ||
100 | if (avuuid == UUID.Zero) { | ||
101 | return -2; | ||
102 | } | ||
103 | |||
104 | ScenePresence presence = World.GetScenePresence (avuuid); | ||
105 | if (presence == null) { | ||
106 | return -3; | ||
107 | } | ||
108 | |||
109 | // remoteClient = not used by ScenePresence.HandleAgentRequestSit() | ||
110 | // agentID = not used by ScenePresence.HandleAgentRequestSit() | ||
111 | // targetID = UUID of prim to sit on | ||
112 | // offset = offset of sitting position | ||
113 | |||
114 | presence.HandleAgentRequestSit (null, UUID.Zero, m_host.UUID, OpenMetaverse.Vector3.Zero); | ||
115 | return 0; | ||
116 | } | ||
117 | */ | ||
118 | /** | ||
119 | * @brief llTeleportAgent() is broken in that if you pass it a landmark, | ||
120 | * it still subjects the position to spawn points, as it always | ||
121 | * calls RequestTeleportLocation() with TeleportFlags.ViaLocation. | ||
122 | * See llTeleportAgent() and CheckAndAdjustTelehub(). | ||
123 | * | ||
124 | * @param agent = what agent to teleport | ||
125 | * @param landmark = inventory name or UUID of a landmark object | ||
126 | * @param lookat = looking direction after teleport | ||
127 | */ | ||
128 | /* engines should not have own API | ||
129 | public void xmrTeleportAgent2Landmark (string agent, string landmark, LSL_Vector lookat) | ||
130 | { | ||
131 | // find out about agent to be teleported | ||
132 | UUID agentId; | ||
133 | if (!UUID.TryParse (agent, out agentId)) throw new ApplicationException ("bad agent uuid"); | ||
134 | |||
135 | ScenePresence presence = World.GetScenePresence (agentId); | ||
136 | if (presence == null) throw new ApplicationException ("agent not present in scene"); | ||
137 | if (presence.IsNPC) throw new ApplicationException ("agent is an NPC"); | ||
138 | if (presence.IsGod) throw new ApplicationException ("agent is a god"); | ||
139 | |||
140 | // prim must be owned by land owner or prim must be attached to agent | ||
141 | if (m_host.ParentGroup.AttachmentPoint == 0) { | ||
142 | if (m_host.OwnerID != World.LandChannel.GetLandObject (presence.AbsolutePosition).LandData.OwnerID) { | ||
143 | throw new ApplicationException ("prim not owned by land's owner"); | ||
144 | } | ||
145 | } else { | ||
146 | if (m_host.OwnerID != presence.UUID) throw new ApplicationException ("prim not attached to agent"); | ||
147 | } | ||
148 | |||
149 | // find landmark in inventory or by UUID | ||
150 | UUID assetID = ScriptUtils.GetAssetIdFromKeyOrItemName (m_host, landmark); | ||
151 | if (assetID == UUID.Zero) throw new ApplicationException ("no such landmark"); | ||
152 | |||
153 | // read it in and make sure it is a landmark | ||
154 | AssetBase lma = World.AssetService.Get (assetID.ToString ()); | ||
155 | if ((lma == null) || (lma.Type != (sbyte)AssetType.Landmark)) throw new ApplicationException ("not a landmark"); | ||
156 | |||
157 | // parse the record | ||
158 | AssetLandmark lm = new AssetLandmark (lma); | ||
159 | |||
160 | // the regionhandle (based on region's world X,Y) might be out of date | ||
161 | // re-read the handle so we can pass it to RequestTeleportLocation() | ||
162 | var region = World.GridService.GetRegionByUUID (World.RegionInfo.ScopeID, lm.RegionID); | ||
163 | if (region == null) throw new ApplicationException ("no such region"); | ||
164 | |||
165 | // finally ready to teleport | ||
166 | World.RequestTeleportLocation (presence.ControllingClient, | ||
167 | region.RegionHandle, | ||
168 | lm.Position, | ||
169 | lookat, | ||
170 | (uint)TeleportFlags.ViaLandmark); | ||
171 | } | ||
172 | */ | ||
173 | /** | ||
174 | * @brief Allow any member of group given by config SetParcelMusicURLGroup to set music URL. | ||
175 | * Code modelled after llSetParcelMusicURL(). | ||
176 | * @param newurl = new URL to set (or "" to leave it alone) | ||
177 | * @returns previous URL string | ||
178 | */ | ||
179 | /* engines should not have own API | ||
180 | public string xmrSetParcelMusicURLGroup (string newurl) | ||
181 | { | ||
182 | string groupname = m_ScriptEngine.Config.GetString ("SetParcelMusicURLGroup", ""); | ||
183 | if (groupname == "") throw new ApplicationException ("no SetParcelMusicURLGroup config param set"); | ||
184 | |||
185 | IGroupsModule igm = World.RequestModuleInterface<IGroupsModule> (); | ||
186 | if (igm == null) throw new ApplicationException ("no GroupsModule loaded"); | ||
187 | |||
188 | GroupRecord grouprec = igm.GetGroupRecord (groupname); | ||
189 | if (grouprec == null) throw new ApplicationException ("no such group " + groupname); | ||
190 | |||
191 | GroupMembershipData gmd = igm.GetMembershipData (grouprec.GroupID, m_host.OwnerID); | ||
192 | if (gmd == null) throw new ApplicationException ("not a member of group " + groupname); | ||
193 | |||
194 | ILandObject land = World.LandChannel.GetLandObject (m_host.AbsolutePosition); | ||
195 | if (land == null) throw new ApplicationException ("no land at " + m_host.AbsolutePosition.ToString ()); | ||
196 | string oldurl = land.GetMusicUrl (); | ||
197 | if (oldurl == null) oldurl = ""; | ||
198 | if ((newurl != null) && (newurl != "")) land.SetMusicUrl (newurl); | ||
199 | return oldurl; | ||
200 | } | ||
201 | */ | ||
202 | } | ||
203 | |||
204 | public partial class XMRInstance | ||
205 | { | ||
206 | /** | ||
207 | * @brief The script is calling llReset(). | ||
208 | * We throw an exception to unwind the script out to its main | ||
209 | * causing all the finally's to execute and it will also set | ||
210 | * eventCode = None to indicate event handler has completed. | ||
211 | */ | ||
212 | public void ApiReset() | ||
213 | { | ||
214 | ClearQueueExceptLinkMessages(); | ||
215 | throw new ScriptResetException(); | ||
216 | } | ||
217 | |||
218 | /** | ||
219 | * @brief The script is calling one of the llDetected...(int number) | ||
220 | * functions. Return corresponding DetectParams pointer. | ||
221 | */ | ||
222 | public DetectParams GetDetectParams(int number) | ||
223 | { | ||
224 | DetectParams dp = null; | ||
225 | if ((number >= 0) && (m_DetectParams != null) && (number < m_DetectParams.Length)) { | ||
226 | dp = m_DetectParams[number]; | ||
227 | } | ||
228 | return dp; | ||
229 | } | ||
230 | |||
231 | /** | ||
232 | * @brief Script is calling llDie, so flag the run loop to delete script | ||
233 | * once we are off the microthread stack, and throw an exception | ||
234 | * to unwind the stack asap. | ||
235 | */ | ||
236 | public void Die() | ||
237 | { | ||
238 | // llDie doesn't work in attachments! | ||
239 | if (m_Part.ParentGroup.IsAttachment || m_DetachQuantum > 0) | ||
240 | return; | ||
241 | |||
242 | throw new ScriptDieException(); | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * @brief Called by script to sleep for the given number of milliseconds. | ||
247 | */ | ||
248 | public void Sleep(int ms) | ||
249 | { | ||
250 | lock (m_QueueLock) { | ||
251 | |||
252 | /* | ||
253 | * Say how long to sleep. | ||
254 | */ | ||
255 | m_SleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds(ms); | ||
256 | |||
257 | /* | ||
258 | * Don't wake on any events. | ||
259 | */ | ||
260 | m_SleepEventMask1 = 0; | ||
261 | m_SleepEventMask2 = 0; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * The compiler follows all calls to llSleep() with a call to CheckRun(). | ||
266 | * So tell CheckRun() to suspend the microthread. | ||
267 | */ | ||
268 | suspendOnCheckRunTemp = true; | ||
269 | } | ||
270 | |||
271 | /** | ||
272 | * Block script execution until an event is queued or a timeout is reached. | ||
273 | * @param timeout = maximum number of seconds to wait | ||
274 | * @param returnMask = if event is queued that matches these mask bits, | ||
275 | * the script is woken, that event is dequeued and | ||
276 | * returned to the caller. The event handler is not | ||
277 | * executed. | ||
278 | * @param backgroundMask = if any of these events are queued while waiting, | ||
279 | * execute their event handlers. When any such event | ||
280 | * handler exits, continue waiting for events or the | ||
281 | * timeout. | ||
282 | * @returns empty list: no event was queued that matched returnMask and the timeout was reached | ||
283 | * or a background event handler changed state (eg, via 'state' statement) | ||
284 | * else: list giving parameters of the event: | ||
285 | * [0] = event code (integer) | ||
286 | * [1..n] = call parameters to the event, if any | ||
287 | * Notes: | ||
288 | * 1) Scrips should use XMREVENTMASKn_<eventname> symbols for the mask arguments, | ||
289 | * where n is 1 or 2 for mask1 or mask2 arguments. | ||
290 | * The list[0] return argument can be decoded by using XMREVENTCODE_<eventname> symbols. | ||
291 | * 2) If all masks are zero, the call ends up acting like llSleep. | ||
292 | * 3) If an event is enabled in both returnMask and backgroundMask, the returnMask bit | ||
293 | * action takes precedence, ie, the event is returned. This allows a simple specification | ||
294 | * of -1 for both backgroundMask arguments to indicate that all events not listed in | ||
295 | * the returnMask argumetns should be handled in the background. | ||
296 | * 4) Any events not listed in either returnMask or backgroundMask arguments will be | ||
297 | * queued for later processing (subject to normal queue limits). | ||
298 | * 5) Background event handlers execute as calls from within xmrEventDequeue, they do | ||
299 | * not execute as separate threads. Thus any background event handlers must return | ||
300 | * before the call to xmrEventDequeue will return. | ||
301 | * 6) If a background event handler changes state (eg, via 'state' statement), the state | ||
302 | * is immediately changed and the script-level xmrEventDequeue call does not return. | ||
303 | * 7) For returned events, the detect parameters are overwritten by the returned event. | ||
304 | * For background events, the detect parameters are saved and restored. | ||
305 | * 8) Scripts must contain dummy event handler definitions for any event types that may | ||
306 | * be returned by xmrEventDequeue, to let the runtime know that the script is capable | ||
307 | * of processing that event type. Otherwise, the event may not be queued to the script. | ||
308 | */ | ||
309 | private static LSL_List emptyList = new LSL_List (new object[0]); | ||
310 | |||
311 | public override LSL_List xmrEventDequeue (double timeout, int returnMask1, int returnMask2, | ||
312 | int backgroundMask1, int backgroundMask2) | ||
313 | { | ||
314 | DateTime sleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds (timeout * 1000.0); | ||
315 | EventParams evt = null; | ||
316 | int callNo, evc2; | ||
317 | int evc1 = 0; | ||
318 | int mask1 = returnMask1 | backgroundMask1; // codes 00..31 | ||
319 | int mask2 = returnMask2 | backgroundMask2; // codes 32..63 | ||
320 | LinkedListNode<EventParams> lln = null; | ||
321 | object[] sv; | ||
322 | ScriptEventCode evc = ScriptEventCode.None; | ||
323 | |||
324 | callNo = -1; | ||
325 | try | ||
326 | { | ||
327 | if (callMode == CallMode_NORMAL) goto findevent; | ||
328 | |||
329 | /* | ||
330 | * Stack frame is being restored as saved via CheckRun...(). | ||
331 | * Restore necessary values then jump to __call<n> label to resume processing. | ||
332 | */ | ||
333 | sv = RestoreStackFrame ("xmrEventDequeue", out callNo); | ||
334 | sleepUntil = DateTime.Parse ((string)sv[0]); | ||
335 | returnMask1 = (int)sv[1]; | ||
336 | returnMask2 = (int)sv[2]; | ||
337 | mask1 = (int)sv[3]; | ||
338 | mask2 = (int)sv[4]; | ||
339 | switch (callNo) | ||
340 | { | ||
341 | case 0: goto __call0; | ||
342 | case 1: | ||
343 | { | ||
344 | evc1 = (int)sv[5]; | ||
345 | evc = (ScriptEventCode)(int)sv[6]; | ||
346 | DetectParams[] detprms = ObjArrToDetPrms ((object[])sv[7]); | ||
347 | object[] ehargs = (object[])sv[8]; | ||
348 | evt = new EventParams (evc.ToString (), ehargs, detprms); | ||
349 | goto __call1; | ||
350 | } | ||
351 | } | ||
352 | throw new ScriptBadCallNoException (callNo); | ||
353 | |||
354 | /* | ||
355 | * Find first event that matches either the return or background masks. | ||
356 | */ | ||
357 | findevent: | ||
358 | Monitor.Enter (m_QueueLock); | ||
359 | for (lln = m_EventQueue.First; lln != null; lln = lln.Next) | ||
360 | { | ||
361 | evt = lln.Value; | ||
362 | evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode), evt.EventName); | ||
363 | evc1 = (int)evc; | ||
364 | evc2 = evc1 - 32; | ||
365 | if ((((uint)evc1 < (uint)32) && (((mask1 >> evc1) & 1) != 0)) || | ||
366 | (((uint)evc2 < (uint)32) && (((mask2 >> evc2) & 1) != 0))) | ||
367 | goto remfromq; | ||
368 | } | ||
369 | |||
370 | /* | ||
371 | * Nothing found, sleep while one comes in. | ||
372 | */ | ||
373 | m_SleepUntil = sleepUntil; | ||
374 | m_SleepEventMask1 = mask1; | ||
375 | m_SleepEventMask2 = mask2; | ||
376 | Monitor.Exit (m_QueueLock); | ||
377 | suspendOnCheckRunTemp = true; | ||
378 | callNo = 0; | ||
379 | __call0: | ||
380 | CheckRunQuick (); | ||
381 | goto checktmo; | ||
382 | |||
383 | /* | ||
384 | * Found one, remove it from queue. | ||
385 | */ | ||
386 | remfromq: | ||
387 | m_EventQueue.Remove (lln); | ||
388 | if ((uint)evc1 < (uint)m_EventCounts.Length) | ||
389 | m_EventCounts[evc1] --; | ||
390 | |||
391 | Monitor.Exit (m_QueueLock); | ||
392 | m_InstEHEvent ++; | ||
393 | |||
394 | /* | ||
395 | * See if returnable or background event. | ||
396 | */ | ||
397 | if ((((uint)evc1 < (uint)32) && (((returnMask1 >> evc1) & 1) != 0)) || | ||
398 | (((uint)evc2 < (uint)32) && (((returnMask2 >> evc2) & 1) != 0))) | ||
399 | { | ||
400 | |||
401 | /* | ||
402 | * Returnable event, return its parameters in a list. | ||
403 | * Also set the detect parameters to what the event has. | ||
404 | */ | ||
405 | int plen = evt.Params.Length; | ||
406 | object[] plist = new object[plen+1]; | ||
407 | plist[0] = (LSL_Integer)evc1; | ||
408 | for (int i = 0; i < plen;) | ||
409 | { | ||
410 | object ob = evt.Params[i]; | ||
411 | if (ob is int) | ||
412 | ob = (LSL_Integer)(int)ob; | ||
413 | else if (ob is double) | ||
414 | ob = (LSL_Float)(double)ob; | ||
415 | else if (ob is string) | ||
416 | ob = (LSL_String)(string)ob; | ||
417 | plist[++i] = ob; | ||
418 | } | ||
419 | m_DetectParams = evt.DetectParams; | ||
420 | return new LSL_List (plist); | ||
421 | } | ||
422 | |||
423 | /* | ||
424 | * It is a background event, simply call its event handler, | ||
425 | * then check event queue again. | ||
426 | */ | ||
427 | callNo = 1; | ||
428 | __call1: | ||
429 | ScriptEventHandler seh = m_ObjCode.scriptEventHandlerTable[stateCode,evc1]; | ||
430 | if (seh == null) | ||
431 | goto checktmo; | ||
432 | |||
433 | DetectParams[] saveDetParams = this.m_DetectParams; | ||
434 | object[] saveEHArgs = this.ehArgs; | ||
435 | ScriptEventCode saveEventCode = this.eventCode; | ||
436 | |||
437 | this.m_DetectParams = evt.DetectParams; | ||
438 | this.ehArgs = evt.Params; | ||
439 | this.eventCode = evc; | ||
440 | |||
441 | try | ||
442 | { | ||
443 | seh (this); | ||
444 | } | ||
445 | finally | ||
446 | { | ||
447 | m_DetectParams = saveDetParams; | ||
448 | ehArgs = saveEHArgs; | ||
449 | eventCode = saveEventCode; | ||
450 | } | ||
451 | |||
452 | /* | ||
453 | * Keep waiting until we find a returnable event or timeout. | ||
454 | */ | ||
455 | checktmo: | ||
456 | if (DateTime.UtcNow < sleepUntil) | ||
457 | goto findevent; | ||
458 | |||
459 | /* | ||
460 | * We timed out, return an empty list. | ||
461 | */ | ||
462 | return emptyList; | ||
463 | } | ||
464 | finally | ||
465 | { | ||
466 | if (callMode != CallMode_NORMAL) | ||
467 | { | ||
468 | |||
469 | /* | ||
470 | * Stack frame is being saved by CheckRun...(). | ||
471 | * Save everything we need at the __call<n> labels so we can restore it | ||
472 | * when we need to. | ||
473 | */ | ||
474 | sv = CaptureStackFrame ("xmrEventDequeue", callNo, 9); | ||
475 | sv[0] = sleepUntil.ToString (); // needed at __call0,__call1 | ||
476 | sv[1] = returnMask1; // needed at __call0,__call1 | ||
477 | sv[2] = returnMask2; // needed at __call0,__call1 | ||
478 | sv[3] = mask1; // needed at __call0,__call1 | ||
479 | sv[4] = mask2; // needed at __call0,__call1 | ||
480 | if (callNo == 1) | ||
481 | { | ||
482 | sv[5] = evc1; // needed at __call1 | ||
483 | sv[6] = (int)evc; // needed at __call1 | ||
484 | sv[7] = DetPrmsToObjArr (evt.DetectParams); // needed at __call1 | ||
485 | sv[8] = evt.Params; // needed at __call1 | ||
486 | } | ||
487 | } | ||
488 | } | ||
489 | } | ||
490 | |||
491 | /** | ||
492 | * @brief Enqueue an event | ||
493 | * @param ev = as returned by xmrEventDequeue saying which event type to queue | ||
494 | * and what argument list to pass to it. The llDetect...() parameters | ||
495 | * are as currently set for the script (use xmrEventLoadDets to set how | ||
496 | * you want them to be different). | ||
497 | */ | ||
498 | public override void xmrEventEnqueue (LSL_List ev) | ||
499 | { | ||
500 | object[] data = ev.Data; | ||
501 | ScriptEventCode evc = (ScriptEventCode)ListInt (data[0]); | ||
502 | |||
503 | int nargs = data.Length - 1; | ||
504 | object[] args = new object[nargs]; | ||
505 | Array.Copy (data, 1, args, 0, nargs); | ||
506 | |||
507 | PostEvent (new EventParams (evc.ToString (), args, m_DetectParams)); | ||
508 | } | ||
509 | |||
510 | /** | ||
511 | * @brief Save current detect params into a list | ||
512 | * @returns a list containing current detect param values | ||
513 | */ | ||
514 | private const int saveDPVer = 1; | ||
515 | |||
516 | public override LSL_List xmrEventSaveDets () | ||
517 | { | ||
518 | object[] obs = DetPrmsToObjArr (m_DetectParams); | ||
519 | return new LSL_List (obs); | ||
520 | } | ||
521 | |||
522 | private static object[] DetPrmsToObjArr (DetectParams[] dps) | ||
523 | { | ||
524 | int len = dps.Length; | ||
525 | object[] obs = new object[len*16+1]; | ||
526 | int j = 0; | ||
527 | obs[j++] = (LSL_Integer)saveDPVer; | ||
528 | for (int i = 0; i < len; i ++) | ||
529 | { | ||
530 | DetectParams dp = dps[i]; | ||
531 | obs[j++] = (LSL_String)dp.Key.ToString(); // UUID | ||
532 | obs[j++] = dp.OffsetPos; // vector | ||
533 | obs[j++] = (LSL_Integer)dp.LinkNum; // integer | ||
534 | obs[j++] = (LSL_String)dp.Group.ToString(); // UUID | ||
535 | obs[j++] = (LSL_String)dp.Name; // string | ||
536 | obs[j++] = (LSL_String)dp.Owner.ToString(); // UUID | ||
537 | obs[j++] = dp.Position; // vector | ||
538 | obs[j++] = dp.Rotation; // rotation | ||
539 | obs[j++] = (LSL_Integer)dp.Type; // integer | ||
540 | obs[j++] = dp.Velocity; // vector | ||
541 | obs[j++] = dp.TouchST; // vector | ||
542 | obs[j++] = dp.TouchNormal; // vector | ||
543 | obs[j++] = dp.TouchBinormal; // vector | ||
544 | obs[j++] = dp.TouchPos; // vector | ||
545 | obs[j++] = dp.TouchUV; // vector | ||
546 | obs[j++] = (LSL_Integer)dp.TouchFace; // integer | ||
547 | } | ||
548 | return obs; | ||
549 | } | ||
550 | |||
551 | |||
552 | /** | ||
553 | * @brief Load current detect params from a list | ||
554 | * @param dpList = as returned by xmrEventSaveDets() | ||
555 | */ | ||
556 | public override void xmrEventLoadDets (LSL_List dpList) | ||
557 | { | ||
558 | m_DetectParams = ObjArrToDetPrms (dpList.Data); | ||
559 | } | ||
560 | |||
561 | private static DetectParams[] ObjArrToDetPrms (object[] objs) | ||
562 | { | ||
563 | int j = 0; | ||
564 | if ((objs.Length % 16 != 1) || (ListInt (objs[j++]) != saveDPVer)) | ||
565 | throw new Exception ("invalid detect param format"); | ||
566 | |||
567 | int len = objs.Length / 16; | ||
568 | DetectParams[] dps = new DetectParams[len]; | ||
569 | |||
570 | for (int i = 0; i < len; i ++) | ||
571 | { | ||
572 | DetectParams dp = new DetectParams (); | ||
573 | |||
574 | dp.Key = new UUID (ListStr (objs[j++])); | ||
575 | dp.OffsetPos = (LSL_Vector)objs[j++]; | ||
576 | dp.LinkNum = ListInt (objs[j++]); | ||
577 | dp.Group = new UUID (ListStr (objs[j++])); | ||
578 | dp.Name = ListStr (objs[j++]); | ||
579 | dp.Owner = new UUID (ListStr (objs[j++])); | ||
580 | dp.Position = (LSL_Vector)objs[j++]; | ||
581 | dp.Rotation = (LSL_Rotation)objs[j++]; | ||
582 | dp.Type = ListInt (objs[j++]); | ||
583 | dp.Velocity = (LSL_Vector)objs[j++]; | ||
584 | |||
585 | SurfaceTouchEventArgs stea = new SurfaceTouchEventArgs (); | ||
586 | |||
587 | stea.STCoord = LSLVec2OMVec ((LSL_Vector)objs[j++]); | ||
588 | stea.Normal = LSLVec2OMVec ((LSL_Vector)objs[j++]); | ||
589 | stea.Binormal = LSLVec2OMVec ((LSL_Vector)objs[j++]); | ||
590 | stea.Position = LSLVec2OMVec ((LSL_Vector)objs[j++]); | ||
591 | stea.UVCoord = LSLVec2OMVec ((LSL_Vector)objs[j++]); | ||
592 | stea.FaceIndex = ListInt (objs[j++]); | ||
593 | |||
594 | dp.SurfaceTouchArgs = stea; | ||
595 | |||
596 | dps[i] = dp; | ||
597 | } | ||
598 | |||
599 | return dps; | ||
600 | } | ||
601 | |||
602 | /** | ||
603 | * @brief The script is executing a 'state <newState>;' command. | ||
604 | * Tell outer layers to cancel any event triggers, like llListen(), | ||
605 | * then tell outer layers which events the new state has handlers for. | ||
606 | * We also clear the event queue as per http://wiki.secondlife.com/wiki/State | ||
607 | */ | ||
608 | public override void StateChange() | ||
609 | { | ||
610 | /* | ||
611 | * Cancel any llListen()s etc. | ||
612 | * But llSetTimerEvent() should persist. | ||
613 | */ | ||
614 | object[] timers = m_XMRLSLApi.acm.TimerPlugin.GetSerializationData(m_ItemID); | ||
615 | AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID); | ||
616 | m_XMRLSLApi.acm.TimerPlugin.CreateFromData(m_LocalID, m_ItemID, UUID.Zero, timers); | ||
617 | |||
618 | /* | ||
619 | * Tell whoever cares which event handlers the new state has. | ||
620 | */ | ||
621 | m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(stateCode)); | ||
622 | |||
623 | /* | ||
624 | * Clear out any old events from the queue. | ||
625 | */ | ||
626 | lock (m_QueueLock) | ||
627 | { | ||
628 | m_EventQueue.Clear(); | ||
629 | for (int i = m_EventCounts.Length; -- i >= 0;) | ||
630 | m_EventCounts[i] = 0; | ||
631 | } | ||
632 | } | ||
633 | |||
634 | /** | ||
635 | * @brief Script is calling xmrStackLeft(). | ||
636 | */ | ||
637 | public override int xmrStackLeft () | ||
638 | { | ||
639 | return microthread.StackLeft (); | ||
640 | } | ||
641 | } | ||
642 | |||
643 | /** | ||
644 | * @brief Thrown by things like llResetScript() to unconditionally | ||
645 | * unwind as script and reset it to the default state_entry | ||
646 | * handler. We don't want script-level try/catch to intercept | ||
647 | * these so scripts can't interfere with the behavior. | ||
648 | */ | ||
649 | public class ScriptResetException : Exception, IXMRUncatchable { } | ||
650 | |||
651 | /** | ||
652 | * @brief Thrown by things like llDie() to unconditionally unwind as | ||
653 | * script. We don't want script-level try/catch to intercept | ||
654 | * these so scripts can't interfere with the behavior. | ||
655 | */ | ||
656 | public class ScriptDieException : Exception, IXMRUncatchable { } | ||
657 | } | ||