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