diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs | 619 |
1 files changed, 619 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs new file mode 100644 index 0000000..833211f --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs | |||
@@ -0,0 +1,619 @@ | |||
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.Yengine | ||
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 | // Say how long to sleep. | ||
253 | m_SleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds(ms); | ||
254 | |||
255 | // Don't wake on any events. | ||
256 | m_SleepEventMask1 = 0; | ||
257 | m_SleepEventMask2 = 0; | ||
258 | } | ||
259 | |||
260 | // The compiler follows all calls to llSleep() with a call to CheckRun(). | ||
261 | // So tell CheckRun() to suspend the microthread. | ||
262 | suspendOnCheckRunTemp = true; | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * Block script execution until an event is queued or a timeout is reached. | ||
267 | * @param timeout = maximum number of seconds to wait | ||
268 | * @param returnMask = if event is queued that matches these mask bits, | ||
269 | * the script is woken, that event is dequeued and | ||
270 | * returned to the caller. The event handler is not | ||
271 | * executed. | ||
272 | * @param backgroundMask = if any of these events are queued while waiting, | ||
273 | * execute their event handlers. When any such event | ||
274 | * handler exits, continue waiting for events or the | ||
275 | * timeout. | ||
276 | * @returns empty list: no event was queued that matched returnMask and the timeout was reached | ||
277 | * or a background event handler changed state (eg, via 'state' statement) | ||
278 | * else: list giving parameters of the event: | ||
279 | * [0] = event code (integer) | ||
280 | * [1..n] = call parameters to the event, if any | ||
281 | * Notes: | ||
282 | * 1) Scrips should use XMREVENTMASKn_<eventname> symbols for the mask arguments, | ||
283 | * where n is 1 or 2 for mask1 or mask2 arguments. | ||
284 | * The list[0] return argument can be decoded by using XMREVENTCODE_<eventname> symbols. | ||
285 | * 2) If all masks are zero, the call ends up acting like llSleep. | ||
286 | * 3) If an event is enabled in both returnMask and backgroundMask, the returnMask bit | ||
287 | * action takes precedence, ie, the event is returned. This allows a simple specification | ||
288 | * of -1 for both backgroundMask arguments to indicate that all events not listed in | ||
289 | * the returnMask argumetns should be handled in the background. | ||
290 | * 4) Any events not listed in either returnMask or backgroundMask arguments will be | ||
291 | * queued for later processing (subject to normal queue limits). | ||
292 | * 5) Background event handlers execute as calls from within xmrEventDequeue, they do | ||
293 | * not execute as separate threads. Thus any background event handlers must return | ||
294 | * before the call to xmrEventDequeue will return. | ||
295 | * 6) If a background event handler changes state (eg, via 'state' statement), the state | ||
296 | * is immediately changed and the script-level xmrEventDequeue call does not return. | ||
297 | * 7) For returned events, the detect parameters are overwritten by the returned event. | ||
298 | * For background events, the detect parameters are saved and restored. | ||
299 | * 8) Scripts must contain dummy event handler definitions for any event types that may | ||
300 | * be returned by xmrEventDequeue, to let the runtime know that the script is capable | ||
301 | * of processing that event type. Otherwise, the event may not be queued to the script. | ||
302 | */ | ||
303 | private static LSL_List emptyList = new LSL_List(new object[0]); | ||
304 | |||
305 | public override LSL_List xmrEventDequeue(double timeout, int returnMask1, int returnMask2, | ||
306 | int backgroundMask1, int backgroundMask2) | ||
307 | { | ||
308 | DateTime sleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds(timeout * 1000.0); | ||
309 | EventParams evt = null; | ||
310 | int callNo, evc2; | ||
311 | int evc1 = 0; | ||
312 | int mask1 = returnMask1 | backgroundMask1; // codes 00..31 | ||
313 | int mask2 = returnMask2 | backgroundMask2; // codes 32..63 | ||
314 | LinkedListNode<EventParams> lln = null; | ||
315 | object[] sv; | ||
316 | ScriptEventCode evc = ScriptEventCode.None; | ||
317 | |||
318 | callNo = -1; | ||
319 | try | ||
320 | { | ||
321 | if(callMode == CallMode_NORMAL) | ||
322 | goto findevent; | ||
323 | |||
324 | // Stack frame is being restored as saved via CheckRun...(). | ||
325 | // Restore necessary values then jump to __call<n> label to resume processing. | ||
326 | sv = RestoreStackFrame("xmrEventDequeue", out callNo); | ||
327 | sleepUntil = DateTime.Parse((string)sv[0]); | ||
328 | returnMask1 = (int)sv[1]; | ||
329 | returnMask2 = (int)sv[2]; | ||
330 | mask1 = (int)sv[3]; | ||
331 | mask2 = (int)sv[4]; | ||
332 | switch(callNo) | ||
333 | { | ||
334 | case 0: | ||
335 | goto __call0; | ||
336 | case 1: | ||
337 | { | ||
338 | evc1 = (int)sv[5]; | ||
339 | evc = (ScriptEventCode)(int)sv[6]; | ||
340 | DetectParams[] detprms = ObjArrToDetPrms((object[])sv[7]); | ||
341 | object[] ehargs = (object[])sv[8]; | ||
342 | evt = new EventParams(evc.ToString(), ehargs, detprms); | ||
343 | goto __call1; | ||
344 | } | ||
345 | } | ||
346 | throw new ScriptBadCallNoException(callNo); | ||
347 | |||
348 | // Find first event that matches either the return or background masks. | ||
349 | findevent: | ||
350 | Monitor.Enter(m_QueueLock); | ||
351 | for(lln = m_EventQueue.First; lln != null; lln = lln.Next) | ||
352 | { | ||
353 | evt = lln.Value; | ||
354 | evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt.EventName); | ||
355 | evc1 = (int)evc; | ||
356 | evc2 = evc1 - 32; | ||
357 | if((((uint)evc1 < (uint)32) && (((mask1 >> evc1) & 1) != 0)) || | ||
358 | (((uint)evc2 < (uint)32) && (((mask2 >> evc2) & 1) != 0))) | ||
359 | goto remfromq; | ||
360 | } | ||
361 | |||
362 | // Nothing found, sleep while one comes in. | ||
363 | m_SleepUntil = sleepUntil; | ||
364 | m_SleepEventMask1 = mask1; | ||
365 | m_SleepEventMask2 = mask2; | ||
366 | Monitor.Exit(m_QueueLock); | ||
367 | suspendOnCheckRunTemp = true; | ||
368 | callNo = 0; | ||
369 | __call0: | ||
370 | CheckRunQuick(); | ||
371 | goto checktmo; | ||
372 | |||
373 | // Found one, remove it from queue. | ||
374 | remfromq: | ||
375 | m_EventQueue.Remove(lln); | ||
376 | if((uint)evc1 < (uint)m_EventCounts.Length) | ||
377 | m_EventCounts[evc1]--; | ||
378 | |||
379 | Monitor.Exit(m_QueueLock); | ||
380 | m_InstEHEvent++; | ||
381 | |||
382 | // See if returnable or background event. | ||
383 | if((((uint)evc1 < (uint)32) && (((returnMask1 >> evc1) & 1) != 0)) || | ||
384 | (((uint)evc2 < (uint)32) && (((returnMask2 >> evc2) & 1) != 0))) | ||
385 | { | ||
386 | // Returnable event, return its parameters in a list. | ||
387 | // Also set the detect parameters to what the event has. | ||
388 | int plen = evt.Params.Length; | ||
389 | object[] plist = new object[plen + 1]; | ||
390 | plist[0] = (LSL_Integer)evc1; | ||
391 | for(int i = 0; i < plen;) | ||
392 | { | ||
393 | object ob = evt.Params[i]; | ||
394 | if(ob is int) | ||
395 | ob = (LSL_Integer)(int)ob; | ||
396 | else if(ob is double) | ||
397 | ob = (LSL_Float)(double)ob; | ||
398 | else if(ob is string) | ||
399 | ob = (LSL_String)(string)ob; | ||
400 | plist[++i] = ob; | ||
401 | } | ||
402 | m_DetectParams = evt.DetectParams; | ||
403 | return new LSL_List(plist); | ||
404 | } | ||
405 | |||
406 | // It is a background event, simply call its event handler, | ||
407 | // then check event queue again. | ||
408 | callNo = 1; | ||
409 | __call1: | ||
410 | ScriptEventHandler seh = m_ObjCode.scriptEventHandlerTable[stateCode, evc1]; | ||
411 | if(seh == null) | ||
412 | goto checktmo; | ||
413 | |||
414 | DetectParams[] saveDetParams = this.m_DetectParams; | ||
415 | object[] saveEHArgs = this.ehArgs; | ||
416 | ScriptEventCode saveEventCode = this.eventCode; | ||
417 | |||
418 | this.m_DetectParams = evt.DetectParams; | ||
419 | this.ehArgs = evt.Params; | ||
420 | this.eventCode = evc; | ||
421 | |||
422 | try | ||
423 | { | ||
424 | seh(this); | ||
425 | } | ||
426 | finally | ||
427 | { | ||
428 | this.m_DetectParams = saveDetParams; | ||
429 | this.ehArgs = saveEHArgs; | ||
430 | this.eventCode = saveEventCode; | ||
431 | } | ||
432 | |||
433 | // Keep waiting until we find a returnable event or timeout. | ||
434 | checktmo: | ||
435 | if(DateTime.UtcNow < sleepUntil) | ||
436 | goto findevent; | ||
437 | |||
438 | // We timed out, return an empty list. | ||
439 | return emptyList; | ||
440 | } | ||
441 | finally | ||
442 | { | ||
443 | if(callMode != CallMode_NORMAL) | ||
444 | { | ||
445 | // Stack frame is being saved by CheckRun...(). | ||
446 | // Save everything we need at the __call<n> labels so we can restore it | ||
447 | // when we need to. | ||
448 | sv = CaptureStackFrame("xmrEventDequeue", callNo, 9); | ||
449 | sv[0] = sleepUntil.ToString(); // needed at __call0,__call1 | ||
450 | sv[1] = returnMask1; // needed at __call0,__call1 | ||
451 | sv[2] = returnMask2; // needed at __call0,__call1 | ||
452 | sv[3] = mask1; // needed at __call0,__call1 | ||
453 | sv[4] = mask2; // needed at __call0,__call1 | ||
454 | if(callNo == 1) | ||
455 | { | ||
456 | sv[5] = evc1; // needed at __call1 | ||
457 | sv[6] = (int)evc; // needed at __call1 | ||
458 | sv[7] = DetPrmsToObjArr(evt.DetectParams); // needed at __call1 | ||
459 | sv[8] = evt.Params; // needed at __call1 | ||
460 | } | ||
461 | } | ||
462 | } | ||
463 | } | ||
464 | |||
465 | /** | ||
466 | * @brief Enqueue an event | ||
467 | * @param ev = as returned by xmrEventDequeue saying which event type to queue | ||
468 | * and what argument list to pass to it. The llDetect...() parameters | ||
469 | * are as currently set for the script (use xmrEventLoadDets to set how | ||
470 | * you want them to be different). | ||
471 | */ | ||
472 | public override void xmrEventEnqueue(LSL_List ev) | ||
473 | { | ||
474 | object[] data = ev.Data; | ||
475 | ScriptEventCode evc = (ScriptEventCode)ListInt(data[0]); | ||
476 | |||
477 | int nargs = data.Length - 1; | ||
478 | object[] args = new object[nargs]; | ||
479 | Array.Copy(data, 1, args, 0, nargs); | ||
480 | |||
481 | PostEvent(new EventParams(evc.ToString(), args, m_DetectParams)); | ||
482 | } | ||
483 | |||
484 | /** | ||
485 | * @brief Save current detect params into a list | ||
486 | * @returns a list containing current detect param values | ||
487 | */ | ||
488 | private const int saveDPVer = 1; | ||
489 | |||
490 | public override LSL_List xmrEventSaveDets() | ||
491 | { | ||
492 | object[] obs = DetPrmsToObjArr(m_DetectParams); | ||
493 | return new LSL_List(obs); | ||
494 | } | ||
495 | |||
496 | private static object[] DetPrmsToObjArr(DetectParams[] dps) | ||
497 | { | ||
498 | int len = dps.Length; | ||
499 | object[] obs = new object[len * 16 + 1]; | ||
500 | int j = 0; | ||
501 | obs[j++] = (LSL_Integer)saveDPVer; | ||
502 | for(int i = 0; i < len; i++) | ||
503 | { | ||
504 | DetectParams dp = dps[i]; | ||
505 | obs[j++] = (LSL_String)dp.Key.ToString(); // UUID | ||
506 | obs[j++] = dp.OffsetPos; // vector | ||
507 | obs[j++] = (LSL_Integer)dp.LinkNum; // integer | ||
508 | obs[j++] = (LSL_String)dp.Group.ToString(); // UUID | ||
509 | obs[j++] = (LSL_String)dp.Name; // string | ||
510 | obs[j++] = (LSL_String)dp.Owner.ToString(); // UUID | ||
511 | obs[j++] = dp.Position; // vector | ||
512 | obs[j++] = dp.Rotation; // rotation | ||
513 | obs[j++] = (LSL_Integer)dp.Type; // integer | ||
514 | obs[j++] = dp.Velocity; // vector | ||
515 | obs[j++] = dp.TouchST; // vector | ||
516 | obs[j++] = dp.TouchNormal; // vector | ||
517 | obs[j++] = dp.TouchBinormal; // vector | ||
518 | obs[j++] = dp.TouchPos; // vector | ||
519 | obs[j++] = dp.TouchUV; // vector | ||
520 | obs[j++] = (LSL_Integer)dp.TouchFace; // integer | ||
521 | } | ||
522 | return obs; | ||
523 | } | ||
524 | |||
525 | /** | ||
526 | * @brief Load current detect params from a list | ||
527 | * @param dpList = as returned by xmrEventSaveDets() | ||
528 | */ | ||
529 | public override void xmrEventLoadDets(LSL_List dpList) | ||
530 | { | ||
531 | m_DetectParams = ObjArrToDetPrms(dpList.Data); | ||
532 | } | ||
533 | |||
534 | private static DetectParams[] ObjArrToDetPrms(object[] objs) | ||
535 | { | ||
536 | int j = 0; | ||
537 | if((objs.Length % 16 != 1) || (ListInt(objs[j++]) != saveDPVer)) | ||
538 | throw new Exception("invalid detect param format"); | ||
539 | |||
540 | int len = objs.Length / 16; | ||
541 | DetectParams[] dps = new DetectParams[len]; | ||
542 | |||
543 | for(int i = 0; i < len; i++) | ||
544 | { | ||
545 | DetectParams dp = new DetectParams(); | ||
546 | |||
547 | dp.Key = new UUID(ListStr(objs[j++])); | ||
548 | dp.OffsetPos = (LSL_Vector)objs[j++]; | ||
549 | dp.LinkNum = ListInt(objs[j++]); | ||
550 | dp.Group = new UUID(ListStr(objs[j++])); | ||
551 | dp.Name = ListStr(objs[j++]); | ||
552 | dp.Owner = new UUID(ListStr(objs[j++])); | ||
553 | dp.Position = (LSL_Vector)objs[j++]; | ||
554 | dp.Rotation = (LSL_Rotation)objs[j++]; | ||
555 | dp.Type = ListInt(objs[j++]); | ||
556 | dp.Velocity = (LSL_Vector)objs[j++]; | ||
557 | |||
558 | SurfaceTouchEventArgs stea = new SurfaceTouchEventArgs(); | ||
559 | |||
560 | stea.STCoord = LSLVec2OMVec((LSL_Vector)objs[j++]); | ||
561 | stea.Normal = LSLVec2OMVec((LSL_Vector)objs[j++]); | ||
562 | stea.Binormal = LSLVec2OMVec((LSL_Vector)objs[j++]); | ||
563 | stea.Position = LSLVec2OMVec((LSL_Vector)objs[j++]); | ||
564 | stea.UVCoord = LSLVec2OMVec((LSL_Vector)objs[j++]); | ||
565 | stea.FaceIndex = ListInt(objs[j++]); | ||
566 | |||
567 | dp.SurfaceTouchArgs = stea; | ||
568 | |||
569 | dps[i] = dp; | ||
570 | } | ||
571 | return dps; | ||
572 | } | ||
573 | |||
574 | /** | ||
575 | * @brief The script is executing a 'state <newState>;' command. | ||
576 | * Tell outer layers to cancel any event triggers, like llListen(), | ||
577 | * then tell outer layers which events the new state has handlers for. | ||
578 | * We also clear the event queue as per http://wiki.secondlife.com/wiki/State | ||
579 | */ | ||
580 | public override void StateChange() | ||
581 | { | ||
582 | // Cancel any llListen()s etc. | ||
583 | // But llSetTimerEvent() should persist. | ||
584 | object[] timers = m_XMRLSLApi.acm.TimerPlugin.GetSerializationData(m_ItemID); | ||
585 | AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID); | ||
586 | m_XMRLSLApi.acm.TimerPlugin.CreateFromData(m_LocalID, m_ItemID, UUID.Zero, timers); | ||
587 | |||
588 | // Tell whoever cares which event handlers the new state has. | ||
589 | m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(stateCode)); | ||
590 | |||
591 | // Clear out any old events from the queue. | ||
592 | lock(m_QueueLock) | ||
593 | { | ||
594 | m_EventQueue.Clear(); | ||
595 | for(int i = m_EventCounts.Length; --i >= 0;) | ||
596 | m_EventCounts[i] = 0; | ||
597 | } | ||
598 | } | ||
599 | } | ||
600 | |||
601 | /** | ||
602 | * @brief Thrown by things like llResetScript() to unconditionally | ||
603 | * unwind as script and reset it to the default state_entry | ||
604 | * handler. We don't want script-level try/catch to intercept | ||
605 | * these so scripts can't interfere with the behavior. | ||
606 | */ | ||
607 | public class ScriptResetException: Exception, IXMRUncatchable | ||
608 | { | ||
609 | } | ||
610 | |||
611 | /** | ||
612 | * @brief Thrown by things like llDie() to unconditionally unwind as | ||
613 | * script. We don't want script-level try/catch to intercept | ||
614 | * these so scripts can't interfere with the behavior. | ||
615 | */ | ||
616 | public class ScriptDieException: Exception, IXMRUncatchable | ||
617 | { | ||
618 | } | ||
619 | } | ||