aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs256
1 files changed, 202 insertions, 54 deletions
diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
index 771db0c..26850c4 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
@@ -94,6 +94,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
94 private UUID m_CurrentStateHash; 94 private UUID m_CurrentStateHash;
95 private UUID m_RegionID; 95 private UUID m_RegionID;
96 96
97 public int DebugLevel { get; set; }
98
97 public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> LineMap { get; set; } 99 public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> LineMap { get; set; }
98 100
99 private Dictionary<string,IScriptApi> m_Apis = new Dictionary<string,IScriptApi>(); 101 private Dictionary<string,IScriptApi> m_Apis = new Dictionary<string,IScriptApi>();
@@ -156,6 +158,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
156 158
157 public UUID AppDomain { get; set; } 159 public UUID AppDomain { get; set; }
158 160
161 public SceneObjectPart Part { get; private set; }
162
159 public string PrimName { get; private set; } 163 public string PrimName { get; private set; }
160 164
161 public string ScriptName { get; private set; } 165 public string ScriptName { get; private set; }
@@ -174,6 +178,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
174 178
175 public Queue EventQueue { get; private set; } 179 public Queue EventQueue { get; private set; }
176 180
181 public long EventsQueued
182 {
183 get
184 {
185 lock (EventQueue)
186 return EventQueue.Count;
187 }
188 }
189
190 public long EventsProcessed { get; private set; }
191
177 public int StartParam { get; set; } 192 public int StartParam { get; set; }
178 193
179 public TaskInventoryItem ScriptTask { get; private set; } 194 public TaskInventoryItem ScriptTask { get; private set; }
@@ -186,66 +201,124 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
186 201
187 public static readonly long MaxMeasurementPeriod = 30 * TimeSpan.TicksPerMinute; 202 public static readonly long MaxMeasurementPeriod = 30 * TimeSpan.TicksPerMinute;
188 203
204 private bool m_coopTermination;
205
206 private EventWaitHandle m_coopSleepHandle;
207
189 public void ClearQueue() 208 public void ClearQueue()
190 { 209 {
191 m_TimerQueued = false; 210 m_TimerQueued = false;
192 EventQueue.Clear(); 211 EventQueue.Clear();
193 } 212 }
194 213
195 public ScriptInstance(IScriptEngine engine, SceneObjectPart part, 214 public ScriptInstance(
196 UUID itemID, UUID assetID, string assembly, 215 IScriptEngine engine, SceneObjectPart part, TaskInventoryItem item,
197 AppDomain dom, string primName, string scriptName, 216 int startParam, bool postOnRez,
198 int startParam, bool postOnRez, StateSource stateSource, 217 int maxScriptQueue)
199 int maxScriptQueue)
200 { 218 {
201 State = "default"; 219 State = "default";
202 EventQueue = new Queue(32); 220 EventQueue = new Queue(32);
203 221
204 Engine = engine; 222 Engine = engine;
205 LocalID = part.LocalId; 223 Part = part;
206 ObjectID = part.UUID; 224 ScriptTask = item;
207 RootLocalID = part.ParentGroup.LocalId; 225
208 RootObjectID = part.ParentGroup.UUID; 226 // This is currently only here to allow regression tests to get away without specifying any inventory
209 ItemID = itemID; 227 // item when they are testing script logic that doesn't require an item.
210 AssetID = assetID; 228 if (ScriptTask != null)
211 PrimName = primName; 229 {
212 ScriptName = scriptName; 230 ScriptName = ScriptTask.Name;
213 m_Assembly = assembly; 231 ItemID = ScriptTask.ItemID;
232 AssetID = ScriptTask.AssetID;
233 }
234
235 PrimName = part.ParentGroup.Name;
214 StartParam = startParam; 236 StartParam = startParam;
215 m_MaxScriptQueue = maxScriptQueue; 237 m_MaxScriptQueue = maxScriptQueue;
216 m_stateSource = stateSource;
217 m_postOnRez = postOnRez; 238 m_postOnRez = postOnRez;
218 m_AttachedAvatar = part.ParentGroup.AttachedAvatar; 239 m_AttachedAvatar = part.ParentGroup.AttachedAvatar;
219 m_RegionID = part.ParentGroup.Scene.RegionInfo.RegionID; 240 m_RegionID = part.ParentGroup.Scene.RegionInfo.RegionID;
220 241
221 if (part != null) 242 if (Engine.Config.GetString("ScriptStopStrategy", "abort") == "co-op")
222 { 243 {
223 part.TaskInventory.LockItemsForRead(true); 244 m_coopTermination = true;
224 if (part.TaskInventory.ContainsKey(ItemID)) 245 m_coopSleepHandle = new AutoResetEvent(false);
225 {
226 ScriptTask = part.TaskInventory[ItemID];
227 }
228 part.TaskInventory.LockItemsForRead(false);
229 } 246 }
247 }
248
249 /// <summary>
250 /// Load the script from an assembly into an AppDomain.
251 /// </summary>
252 /// <param name='dom'></param>
253 /// <param name='assembly'></param>
254 /// <param name='stateSource'></param>
255 /// <returns>false if load failed, true if suceeded</returns>
256 public bool Load(AppDomain dom, string assembly, StateSource stateSource)
257 {
258 m_Assembly = assembly;
259 m_stateSource = stateSource;
230 260
231 ApiManager am = new ApiManager(); 261 ApiManager am = new ApiManager();
232 262
233 foreach (string api in am.GetApis()) 263 foreach (string api in am.GetApis())
234 { 264 {
235 m_Apis[api] = am.CreateApi(api); 265 m_Apis[api] = am.CreateApi(api);
236 m_Apis[api].Initialize(engine, part, ScriptTask); 266 m_Apis[api].Initialize(Engine, Part, ScriptTask, m_coopSleepHandle);
237 } 267 }
238 268
239 try 269 try
240 { 270 {
271 object[] constructorParams;
272
273 Assembly scriptAssembly = dom.Load(Path.GetFileNameWithoutExtension(assembly));
274 Type scriptType = scriptAssembly.GetType("SecondLife.XEngineScript");
275
276 if (scriptType != null)
277 {
278 constructorParams = new object[] { m_coopSleepHandle };
279 }
280 else if (!m_coopTermination)
281 {
282 scriptType = scriptAssembly.GetType("SecondLife.Script");
283 constructorParams = null;
284 }
285 else
286 {
287 m_log.ErrorFormat(
288 "[SCRIPT INSTANCE]: Not starting script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}. You must remove all existing {6}* script DLL files before using enabling co-op termination"
289 + ", either by setting DeleteScriptsOnStartup = true in [XEngine] for one run"
290 + " or by deleting these files manually.",
291 ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, assembly);
292
293 return false;
294 }
295
296// m_log.DebugFormat(
297// "[SCRIPT INSTANCE]: Looking to load {0} from assembly {1} in {2}",
298// scriptType.FullName, Path.GetFileNameWithoutExtension(assembly), Engine.World.Name);
299
241 if (dom != System.AppDomain.CurrentDomain) 300 if (dom != System.AppDomain.CurrentDomain)
242 m_Script = (IScript)dom.CreateInstanceAndUnwrap( 301 m_Script
302 = (IScript)dom.CreateInstanceAndUnwrap(
243 Path.GetFileNameWithoutExtension(assembly), 303 Path.GetFileNameWithoutExtension(assembly),
244 "SecondLife.Script"); 304 scriptType.FullName,
305 false,
306 BindingFlags.Default,
307 null,
308 constructorParams,
309 null,
310 null,
311 null);
245 else 312 else
246 m_Script = (IScript)Assembly.Load( 313 m_Script
247 Path.GetFileNameWithoutExtension(assembly)).CreateInstance( 314 = (IScript)scriptAssembly.CreateInstance(
248 "SecondLife.Script"); 315 scriptType.FullName,
316 false,
317 BindingFlags.Default,
318 null,
319 constructorParams,
320 null,
321 null);
249 322
250 //ILease lease = (ILease)RemotingServices.GetLifetimeService(m_Script as ScriptBaseClass); 323 //ILease lease = (ILease)RemotingServices.GetLifetimeService(m_Script as ScriptBaseClass);
251 //RemotingServices.GetLifetimeService(m_Script as ScriptBaseClass); 324 //RemotingServices.GetLifetimeService(m_Script as ScriptBaseClass);
@@ -254,8 +327,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
254 catch (Exception e) 327 catch (Exception e)
255 { 328 {
256 m_log.ErrorFormat( 329 m_log.ErrorFormat(
257 "[SCRIPT INSTANCE]: Error loading assembly {0}. Exception {1}{2}", 330 "[SCRIPT INSTANCE]: Not starting script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}. Error loading assembly {6}. Exception {7}{8}",
258 assembly, e.Message, e.StackTrace); 331 ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, assembly, e.Message, e.StackTrace);
332
333 return false;
259 } 334 }
260 335
261 try 336 try
@@ -267,16 +342,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
267 342
268// // m_log.Debug("[Script] Script instance created"); 343// // m_log.Debug("[Script] Script instance created");
269 344
270 part.SetScriptEvents(ItemID, 345 Part.SetScriptEvents(ItemID,
271 (int)m_Script.GetStateEventFlags(State)); 346 (int)m_Script.GetStateEventFlags(State));
272 } 347 }
273 catch (Exception e) 348 catch (Exception e)
274 { 349 {
275 m_log.ErrorFormat( 350 m_log.ErrorFormat(
276 "[SCRIPT INSTANCE]: Error loading script instance from assembly {0}. Exception {1}{2}", 351 "[SCRIPT INSTANCE]: Not starting script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}. Error initializing script instance. Exception {6}{7}",
277 assembly, e.Message, e.StackTrace); 352 ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, e.Message, e.StackTrace);
278 353
279 return; 354 return false;
280 } 355 }
281 356
282 m_SaveState = true; 357 m_SaveState = true;
@@ -309,7 +384,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
309 384
310// m_log.DebugFormat("[Script] Successfully retrieved state for script {0}.{1}", PrimName, m_ScriptName); 385// m_log.DebugFormat("[Script] Successfully retrieved state for script {0}.{1}", PrimName, m_ScriptName);
311 386
312 part.SetScriptEvents(ItemID, 387 Part.SetScriptEvents(ItemID,
313 (int)m_Script.GetStateEventFlags(State)); 388 (int)m_Script.GetStateEventFlags(State));
314 389
315 if (!Running) 390 if (!Running)
@@ -329,15 +404,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
329 else 404 else
330 { 405 {
331 m_log.WarnFormat( 406 m_log.WarnFormat(
332 "[SCRIPT INSTANCE]: Unable to load script state file {0} for script {1} {2} in {3} {4} (assembly {5}). Memory limit exceeded", 407 "[SCRIPT INSTANCE]: Not starting script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}. Unable to load script state file {6}. Memory limit exceeded.",
333 savedState, ScriptName, ItemID, PrimName, ObjectID, assembly); 408 ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, savedState);
334 } 409 }
335 } 410 }
336 catch (Exception e) 411 catch (Exception e)
337 { 412 {
338 m_log.ErrorFormat( 413 m_log.ErrorFormat(
339 "[SCRIPT INSTANCE]: Unable to load script state file {0} for script {1} {2} in {3} {4} (assembly {5}). XML is {6}. Exception {7}{8}", 414 "[SCRIPT INSTANCE]: Not starting script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}. Unable to load script state file {6}. XML is {7}. Exception {8}{9}",
340 savedState, ScriptName, ItemID, PrimName, ObjectID, assembly, xml, e.Message, e.StackTrace); 415 ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, savedState, xml, e.Message, e.StackTrace);
341 } 416 }
342 } 417 }
343// else 418// else
@@ -348,6 +423,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
348// presence.ControllingClient.SendAgentAlertMessage("Compile successful", false); 423// presence.ControllingClient.SendAgentAlertMessage("Compile successful", false);
349 424
350// } 425// }
426
427 return true;
351 } 428 }
352 429
353 public void Init() 430 public void Init()
@@ -521,9 +598,36 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
521 } 598 }
522 599
523 // Wait for the current event to complete. 600 // Wait for the current event to complete.
524 if (!m_InSelfDelete && workItem.Wait(new TimeSpan((long)timeout * 100000))) 601 if (!m_InSelfDelete)
525 { 602 {
526 return true; 603 if (!m_coopTermination)
604 {
605 // If we're not co-operative terminating then try and wait for the event to complete before stopping
606 if (workItem.Wait(timeout))
607 return true;
608 }
609 else
610 {
611 if (DebugLevel >= 1)
612 m_log.DebugFormat(
613 "[SCRIPT INSTANCE]: Co-operatively stopping script {0} {1} in {2} {3}",
614 ScriptName, ItemID, PrimName, ObjectID);
615
616 // This will terminate the event on next handle check by the script.
617 m_coopSleepHandle.Set();
618
619 // For now, we will wait forever since the event should always cleanly terminate once LSL loop
620 // checking is implemented. May want to allow a shorter timeout option later.
621 if (workItem.Wait(Timeout.Infinite))
622 {
623 if (DebugLevel >= 1)
624 m_log.DebugFormat(
625 "[SCRIPT INSTANCE]: Co-operatively stopped script {0} {1} in {2} {3}",
626 ScriptName, ItemID, PrimName, ObjectID);
627
628 return true;
629 }
630 }
527 } 631 }
528 632
529 lock (EventQueue) 633 lock (EventQueue)
@@ -536,11 +640,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
536 640
537 // If the event still hasn't stopped and we the stop isn't the result of script or object removal, then 641 // If the event still hasn't stopped and we the stop isn't the result of script or object removal, then
538 // forcibly abort the work item (this aborts the underlying thread). 642 // forcibly abort the work item (this aborts the underlying thread).
643 // Co-operative termination should never reach this point.
539 if (!m_InSelfDelete) 644 if (!m_InSelfDelete)
540 { 645 {
541// m_log.ErrorFormat( 646 m_log.DebugFormat(
542// "[SCRIPT INSTANCE]: Aborting script {0} {1} in prim {2} {3} {4} {5}", 647 "[SCRIPT INSTANCE]: Aborting unstopped script {0} {1} in prim {2}, localID {3}, timeout was {4} ms",
543// ScriptName, ItemID, PrimName, ObjectID, m_InSelfDelete, DateTime.Now.Ticks); 648 ScriptName, ItemID, PrimName, LocalID, timeout);
544 649
545 workItem.Abort(); 650 workItem.Abort();
546 } 651 }
@@ -696,19 +801,41 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
696 { 801 {
697 802
698// m_log.DebugFormat("[XEngine]: Processing event {0} for {1}", data.EventName, this); 803// m_log.DebugFormat("[XEngine]: Processing event {0} for {1}", data.EventName, this);
804 SceneObjectPart part = Engine.World.GetSceneObjectPart(LocalID);
805
806 if (DebugLevel >= 2)
807 m_log.DebugFormat(
808 "[SCRIPT INSTANCE]: Processing event {0} for {1}/{2}({3})/{4}({5}) @ {6}/{7}",
809 data.EventName,
810 ScriptName,
811 part.Name,
812 part.LocalId,
813 part.ParentGroup.Name,
814 part.ParentGroup.UUID,
815 part.AbsolutePosition,
816 part.ParentGroup.Scene.Name);
699 817
700 m_DetectParams = data.DetectParams; 818 m_DetectParams = data.DetectParams;
701 819
702 if (data.EventName == "state") // Hardcoded state change 820 if (data.EventName == "state") // Hardcoded state change
703 { 821 {
704 // m_log.DebugFormat("[Script] Script {0}.{1} state set to {2}",
705 // PrimName, ScriptName, data.Params[0].ToString());
706 State = data.Params[0].ToString(); 822 State = data.Params[0].ToString();
823
824 if (DebugLevel >= 1)
825 m_log.DebugFormat(
826 "[SCRIPT INSTANCE]: Changing state to {0} for {1}/{2}({3})/{4}({5}) @ {6}/{7}",
827 State,
828 ScriptName,
829 part.Name,
830 part.LocalId,
831 part.ParentGroup.Name,
832 part.ParentGroup.UUID,
833 part.AbsolutePosition,
834 part.ParentGroup.Scene.Name);
835
707 AsyncCommandManager.RemoveScript(Engine, 836 AsyncCommandManager.RemoveScript(Engine,
708 LocalID, ItemID); 837 LocalID, ItemID);
709 838
710 SceneObjectPart part = Engine.World.GetSceneObjectPart(
711 LocalID);
712 if (part != null) 839 if (part != null)
713 { 840 {
714 part.SetScriptEvents(ItemID, 841 part.SetScriptEvents(ItemID,
@@ -720,8 +847,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
720 if (Engine.World.PipeEventsForScript(LocalID) || 847 if (Engine.World.PipeEventsForScript(LocalID) ||
721 data.EventName == "control") // Don't freeze avies! 848 data.EventName == "control") // Don't freeze avies!
722 { 849 {
723 SceneObjectPart part = Engine.World.GetSceneObjectPart(
724 LocalID);
725 // m_log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}", 850 // m_log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}",
726 // PrimName, ScriptName, data.EventName, State); 851 // PrimName, ScriptName, data.EventName, State);
727 852
@@ -763,7 +888,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
763 m_InEvent = false; 888 m_InEvent = false;
764 m_CurrentEvent = String.Empty; 889 m_CurrentEvent = String.Empty;
765 890
766 if ((!(e is TargetInvocationException) || (!(e.InnerException is SelfDeleteException) && !(e.InnerException is ScriptDeleteException))) && !(e is ThreadAbortException)) 891 if ((!(e is TargetInvocationException)
892 || (!(e.InnerException is SelfDeleteException)
893 && !(e.InnerException is ScriptDeleteException)
894 && !(e.InnerException is ScriptCoopStopException)))
895 && !(e is ThreadAbortException))
767 { 896 {
768 try 897 try
769 { 898 {
@@ -776,6 +905,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
776 ChatTypeEnum.DebugChannel, 2147483647, 905 ChatTypeEnum.DebugChannel, 2147483647,
777 part.AbsolutePosition, 906 part.AbsolutePosition,
778 part.Name, part.UUID, false); 907 part.Name, part.UUID, false);
908
909
910 m_log.DebugFormat(
911 "[SCRIPT INSTANCE]: Runtime error in script {0}, part {1} {2} at {3} in {4}, displayed error {5}, actual exception {6}",
912 ScriptName,
913 PrimName,
914 part.UUID,
915 part.AbsolutePosition,
916 part.ParentGroup.Scene.Name,
917 text.Replace("\n", "\\n"),
918 e.InnerException);
779 } 919 }
780 catch (Exception) 920 catch (Exception)
781 { 921 {
@@ -802,6 +942,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
802 if (part != null) 942 if (part != null)
803 part.Inventory.RemoveInventoryItem(ItemID); 943 part.Inventory.RemoveInventoryItem(ItemID);
804 } 944 }
945 else if ((e is TargetInvocationException) && (e.InnerException is ScriptCoopStopException))
946 {
947 if (DebugLevel >= 1)
948 m_log.DebugFormat(
949 "[SCRIPT INSTANCE]: Script {0}.{1} in event {2}, state {3} stopped co-operatively.",
950 PrimName, ScriptName, data.EventName, State);
951 }
805 } 952 }
806 } 953 }
807 } 954 }
@@ -810,6 +957,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
810 // script engine to run the next event. 957 // script engine to run the next event.
811 lock (EventQueue) 958 lock (EventQueue)
812 { 959 {
960 EventsProcessed++;
961
813 if (EventQueue.Count > 0 && Running && !ShuttingDown) 962 if (EventQueue.Count > 0 && Running && !ShuttingDown)
814 { 963 {
815 m_CurrentWorkItem = Engine.QueueEventHandler(this); 964 m_CurrentWorkItem = Engine.QueueEventHandler(this);
@@ -834,7 +983,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
834 return (DateTime.Now - m_EventStart).Seconds; 983 return (DateTime.Now - m_EventStart).Seconds;
835 } 984 }
836 985
837 public void ResetScript() 986 public void ResetScript(int timeout)
838 { 987 {
839 if (m_Script == null) 988 if (m_Script == null)
840 return; 989 return;
@@ -844,7 +993,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
844 RemoveState(); 993 RemoveState();
845 ReleaseControls(); 994 ReleaseControls();
846 995
847 Stop(0); 996 Stop(timeout);
848 SceneObjectPart part = Engine.World.GetSceneObjectPart(LocalID); 997 SceneObjectPart part = Engine.World.GetSceneObjectPart(LocalID);
849 part.Inventory.GetInventoryItem(ItemID).PermsMask = 0; 998 part.Inventory.GetInventoryItem(ItemID).PermsMask = 0;
850 part.Inventory.GetInventoryItem(ItemID).PermsGranter = UUID.Zero; 999 part.Inventory.GetInventoryItem(ItemID).PermsGranter = UUID.Zero;
@@ -1015,7 +1164,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
1015 "({0}): {1}", scriptLine - 1, 1164 "({0}): {1}", scriptLine - 1,
1016 e.InnerException.Message); 1165 e.InnerException.Message);
1017 1166
1018 System.Console.WriteLine(e.ToString()+"\n");
1019 return message; 1167 return message;
1020 } 1168 }
1021 } 1169 }