Added class for "long commands" (command that returns as event) with dedicated thread for processing. Added support for llSetTimerEvent(). Deleting old compiled scripts before new compile is attempted (avoids loading wrong script on compile error).

afrisby
Tedd Hansen 2007-08-25 19:08:15 +00:00
parent 53be4774b3
commit b75c1b2191
9 changed files with 261 additions and 79 deletions

View File

@ -23,7 +23,7 @@ namespace OpenSim.Region.ScriptEngine.Common
// Object never expires // Object never expires
public override Object InitializeLifetimeService() public override Object InitializeLifetimeService()
{ {
Console.WriteLine("Executor: InitializeLifetimeService()"); //Console.WriteLine("Executor: InitializeLifetimeService()");
// return null; // return null;
ILease lease = (ILease)base.InitializeLifetimeService(); ILease lease = (ILease)base.InitializeLifetimeService();
@ -45,54 +45,60 @@ namespace OpenSim.Region.ScriptEngine.Common
{ {
// IMPORTANT: Types and MemberInfo-derived objects require a LOT of memory. // IMPORTANT: Types and MemberInfo-derived objects require a LOT of memory.
// Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead! // Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead!
try
if (m_Running == false)
{ {
// Script is inactive, do not execute! if (m_Running == false)
return; {
} // Script is inactive, do not execute!
return;
}
string EventName = m_Script.State() + "_event_" + FunctionName; string EventName = m_Script.State() + "_event_" + FunctionName;
//type.InvokeMember(EventName, BindingFlags.InvokeMethod, null, m_Script, args); //type.InvokeMember(EventName, BindingFlags.InvokeMethod, null, m_Script, args);
Console.WriteLine("ScriptEngine Executor.ExecuteEvent: \"" + EventName + "\""); Console.WriteLine("ScriptEngine Executor.ExecuteEvent: \"" + EventName + "\"");
if (Events.ContainsKey(EventName) == false) if (Events.ContainsKey(EventName) == false)
{ {
// Not found, create // Not found, create
Type type = m_Script.GetType(); Type type = m_Script.GetType();
try
{
MethodInfo mi = type.GetMethod(EventName);
Events.Add(EventName, mi);
}
catch (Exception e)
{
// Event name not found, cache it as not found
Events.Add(EventName, null);
}
}
// Get event
MethodInfo ev = null;
Events.TryGetValue(EventName, out ev);
if (ev == null) // No event by that name!
{
Console.WriteLine("ScriptEngine Can not find any event named: \"" + EventName + "\"");
return;
}
// Found
try try
{ {
MethodInfo mi = type.GetMethod(EventName); // Invoke it
Events.Add(EventName, mi); ev.Invoke(m_Script, args);
} }
catch (Exception e) catch (Exception e)
{ {
// Event name not found, cache it as not found // TODO: Send to correct place
Events.Add(EventName, null); Console.WriteLine("ScriptEngine Exception attempting to executing script function: " + e.ToString());
} }
} }
catch { }
// Get event
MethodInfo ev = null;
Events.TryGetValue(EventName, out ev);
if (ev == null) // No event by that name!
return;
// Found
try
{
// Invoke it
ev.Invoke(m_Script, args);
}
catch (Exception e)
{
// TODO: Send to correct place
Console.WriteLine("ScriptEngine Exception attempting to executing script function: " + e.ToString());
}
} }

View File

@ -51,6 +51,14 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL
// Output assembly name // Output assembly name
ScriptCompileCounter++; ScriptCompileCounter++;
string OutFile = Path.Combine("ScriptEngines", "Script_" + ScriptCompileCounter + ".dll"); string OutFile = Path.Combine("ScriptEngines", "Script_" + ScriptCompileCounter + ".dll");
try
{
System.IO.File.Delete(OutFile);
}
catch (Exception e)
{
Console.WriteLine("Exception attempting to delete old compiled script: " + e.ToString());
}
//string OutFile = Path.Combine("ScriptEngines", "SecondLife.Script.dll"); //string OutFile = Path.Combine("ScriptEngines", "SecondLife.Script.dll");
// DEBUG - write source to disk // DEBUG - write source to disk

View File

@ -15,7 +15,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL
// Object never expires // Object never expires
public override Object InitializeLifetimeService() public override Object InitializeLifetimeService()
{ {
Console.WriteLine("LSL_BaseClass: InitializeLifetimeService()"); //Console.WriteLine("LSL_BaseClass: InitializeLifetimeService()");
// return null; // return null;
ILease lease = (ILease)base.InitializeLifetimeService(); ILease lease = (ILease)base.InitializeLifetimeService();

View File

@ -19,15 +19,20 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler
{ {
private System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); private System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
private ScriptManager m_manager; private ScriptEngine m_ScriptEngine;
private IScriptHost m_host; private IScriptHost m_host;
private uint m_localID;
private LLUUID m_itemID;
public LSL_BuiltIn_Commands(ScriptManager manager, IScriptHost host) public LSL_BuiltIn_Commands(ScriptEngine ScriptEngine, IScriptHost host, uint localID, LLUUID itemID)
{ {
m_manager = manager; m_ScriptEngine = ScriptEngine;
m_host = host; m_host = host;
m_localID = localID;
m_itemID = itemID;
MainLog.Instance.Notice("ScriptEngine", "LSL_BaseClass.Start() called. Hosted by [" + m_host.Name + ":" + m_host.UUID + "@" + m_host.AbsolutePosition + "]");
//MainLog.Instance.Notice("ScriptEngine", "LSL_BaseClass.Start() called. Hosted by [" + m_host.Name + ":" + m_host.UUID + "@" + m_host.AbsolutePosition + "]");
} }
@ -39,7 +44,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler
// Object never expires // Object never expires
public override Object InitializeLifetimeService() public override Object InitializeLifetimeService()
{ {
Console.WriteLine("LSL_BuiltIn_Commands: InitializeLifetimeService()"); //Console.WriteLine("LSL_BuiltIn_Commands: InitializeLifetimeService()");
// return null; // return null;
ILease lease = (ILease)base.InitializeLifetimeService(); ILease lease = (ILease)base.InitializeLifetimeService();
@ -55,7 +60,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler
public Scene World public Scene World
{ {
get { return m_manager.World; } get { return m_ScriptEngine.World; }
} }
//These are the implementations of the various ll-functions used by the LSL scripts. //These are the implementations of the various ll-functions used by the LSL scripts.
@ -258,7 +263,11 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler
public void llRezObject(string inventory, LSL_Types.Vector3 pos, LSL_Types.Quaternion rot, int param) { } public void llRezObject(string inventory, LSL_Types.Vector3 pos, LSL_Types.Quaternion rot, int param) { }
public void llLookAt(LSL_Types.Vector3 target, double strength, double damping) { } public void llLookAt(LSL_Types.Vector3 target, double strength, double damping) { }
public void llStopLookAt() { } public void llStopLookAt() { }
public void llSetTimerEvent(double sec) { } public void llSetTimerEvent(double sec)
{
// Setting timer repeat
m_ScriptEngine.myLSLLongCmdHandler.SetTimerEvent(m_localID, m_itemID, sec);
}
public void llSleep(double sec) { System.Threading.Thread.Sleep((int)(sec * 1000)); } public void llSleep(double sec) { System.Threading.Thread.Sleep((int)(sec * 1000)); }
public double llGetMass() { return 0; } public double llGetMass() { return 0; }
public void llCollisionFilter(string name, string id, int accept) { } public void llCollisionFilter(string name, string id, int accept) { }
@ -416,7 +425,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler
public LSL_Types.Vector3 llGroundContour(LSL_Types.Vector3 offset) { return new LSL_Types.Vector3(); } public LSL_Types.Vector3 llGroundContour(LSL_Types.Vector3 offset) { return new LSL_Types.Vector3(); }
public int llGetAttached() { return 0; } public int llGetAttached() { return 0; }
public int llGetFreeMemory() { return 0; } public int llGetFreeMemory() { return 0; }
public string llGetRegionName() { return m_manager.RegionName; } public string llGetRegionName() { return World.RegionInfo.RegionName; }
public double llGetRegionTimeDilation() { return 1.0f; } public double llGetRegionTimeDilation() { return 1.0f; }
public double llGetRegionFPS() { return 10.0f; } public double llGetRegionFPS() { return 10.0f; }
public void llParticleSystem(List<Object> rules) { } public void llParticleSystem(List<Object> rules) { }

View File

@ -77,7 +77,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
//); //);
Console.WriteLine("OnRezScript localID: " + localID + " LLUID: " + itemID.ToString() + " Size: " + script.Length); Console.WriteLine("OnRezScript localID: " + localID + " LLUID: " + itemID.ToString() + " Size: " + script.Length);
myScriptEngine.myScriptManager.StartScript(localID, itemID, script); myScriptEngine.myScriptManager.StartScript(localID, itemID, script);
myScriptEngine.myEventQueueManager.AddToObjectQueue(localID, "state_entry", new object[] { });
} }
public void OnRemoveScript(uint localID, LLUUID itemID) public void OnRemoveScript(uint localID, LLUUID itemID)
{ {

View File

@ -226,6 +226,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
} }
} }
/// <summary> /// <summary>
/// Add event to event execution queue /// Add event to event execution queue
/// </summary> /// </summary>
@ -237,35 +238,45 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
// Determine all scripts in Object and add to their queue // Determine all scripts in Object and add to their queue
//myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName); //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName);
lock (QueueLock)
// Do we have any scripts in this object at all? If not, return
if (myScriptEngine.myScriptManager.Scripts.ContainsKey(localID) == false)
{ {
//Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID.");
return;
}
// Do we have any scripts in this object at all? If not, return foreach (LLUUID itemID in new System.Collections.ArrayList(myScriptEngine.myScriptManager.GetScriptKeys(localID)))
if (myScriptEngine.myScriptManager.Scripts.ContainsKey(localID) == false) {
{ // Add to each script in that object
//Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID."); // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter?
return; AddToScriptQueue(localID, itemID, FunctionName, param);
}
foreach (LLUUID itemID in myScriptEngine.myScriptManager.GetScriptKeys(localID))
{
// Add to each script in that object
// TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter?
// Create a structure and add data
QueueItemStruct QIS = new QueueItemStruct();
QIS.localID = localID;
QIS.itemID = itemID;
QIS.FunctionName = FunctionName;
QIS.param = param;
// Add it to queue
EventQueue.Enqueue(QIS);
}
} }
} }
/// <summary>
/// Add event to event execution queue
/// </summary>
/// <param name="localID"></param>
/// <param name="itemID"></param>
/// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
/// <param name="param">Array of parameters to match event mask</param>
public void AddToScriptQueue(uint localID, LLUUID itemID, string FunctionName, object[] param)
{
lock (QueueLock)
{
// Create a structure and add data
QueueItemStruct QIS = new QueueItemStruct();
QIS.localID = localID;
QIS.itemID = itemID;
QIS.FunctionName = FunctionName;
QIS.param = param;
// Add it to queue
EventQueue.Enqueue(QIS);
}
}
} }
} }

View File

@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using libsecondlife;
namespace OpenSim.Region.ScriptEngine.DotNetEngine
{
/// <summary>
/// Handles LSL commands that takes long time and returns an event, for example timers, HTTP requests, etc.
/// </summary>
class LSLLongCmdHandler
{
private Thread CmdHandlerThread;
private int CmdHandlerThreadCycleSleepms = 100;
private ScriptEngine myScriptEngine;
public LSLLongCmdHandler(ScriptEngine _ScriptEngine)
{
myScriptEngine = _ScriptEngine;
// Start the thread that will be doing the work
CmdHandlerThread = new Thread(CmdHandlerThreadLoop);
CmdHandlerThread.Name = "CmdHandlerThread";
CmdHandlerThread.Priority = ThreadPriority.BelowNormal;
CmdHandlerThread.IsBackground = true;
CmdHandlerThread.Start();
}
~LSLLongCmdHandler()
{
// Shut down thread
try
{
if (CmdHandlerThread != null)
{
if (CmdHandlerThread.IsAlive == true)
{
CmdHandlerThread.Abort();
CmdHandlerThread.Join();
}
}
}
catch { }
}
private void CmdHandlerThreadLoop()
{
while (true)
{
// Check timers
CheckTimerEvents();
// Sleep before next cycle
Thread.Sleep(CmdHandlerThreadCycleSleepms);
}
}
/// <summary>
/// Remove a specific script (and all its pending commands)
/// </summary>
/// <param name="m_localID"></param>
/// <param name="m_itemID"></param>
public void RemoveScript(uint m_localID, LLUUID m_itemID)
{
// Remove a specific script
// Remove from: Timers
UnSetTimerEvents(m_localID, m_itemID);
}
//
// TIMER
//
private class TimerClass
{
public uint localID;
public LLUUID itemID;
public double interval;
public DateTime next;
}
private List<TimerClass> Timers = new List<TimerClass>();
private object ListLock = new object();
public void SetTimerEvent(uint m_localID, LLUUID m_itemID, double sec)
{
Console.WriteLine("SetTimerEvent");
// Always remove first, in case this is a re-set
UnSetTimerEvents(m_localID, m_itemID);
if (sec == 0) // Disabling timer
return;
// Add to timer
TimerClass ts = new TimerClass();
ts.localID = m_localID;
ts.itemID = m_itemID;
ts.interval = sec;
ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
lock (ListLock)
{
Timers.Add(ts);
}
}
public void UnSetTimerEvents(uint m_localID, LLUUID m_itemID)
{
// Remove from timer
lock (ListLock)
{
List<TimerClass> NewTimers = new List<TimerClass>();
foreach (TimerClass ts in Timers)
{
if (ts.localID != m_localID && ts.itemID != m_itemID)
{
NewTimers.Add(ts);
}
}
Timers.Clear();
Timers = NewTimers;
}
}
public void CheckTimerEvents()
{
// Nothing to do here?
if (Timers.Count == 0)
return;
// Go through all timers
foreach (TimerClass ts in Timers)
{
// Time has passed?
if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime())
{
// Add it to queue
//Console.WriteLine("Enqueue timer event: " + ts.next.ToUniversalTime().ToString("HH:mm:ss") + " > " + DateTime.Now.ToUniversalTime().ToString("HH:mm:ss"));
myScriptEngine.myEventQueueManager.AddToScriptQueue(ts.localID, ts.itemID, "timer", new object[] { });
// set next interval
ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
}
}
}
}
}

View File

@ -48,6 +48,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
internal EventQueueManager myEventQueueManager; // Executes events internal EventQueueManager myEventQueueManager; // Executes events
internal ScriptManager myScriptManager; // Load, unload and execute scripts internal ScriptManager myScriptManager; // Load, unload and execute scripts
internal AppDomainManager myAppDomainManager; internal AppDomainManager myAppDomainManager;
internal LSLLongCmdHandler myLSLLongCmdHandler;
private OpenSim.Framework.Console.LogBase m_log; private OpenSim.Framework.Console.LogBase m_log;
@ -77,6 +78,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
myEventManager = new EventManager(this); myEventManager = new EventManager(this);
myScriptManager = new ScriptManager(this); myScriptManager = new ScriptManager(this);
myAppDomainManager = new AppDomainManager(); myAppDomainManager = new AppDomainManager();
myLSLLongCmdHandler = new LSLLongCmdHandler(this);
// Should we iterate the region for scripts that needs starting? // Should we iterate the region for scripts that needs starting?
// Or can we assume we are loaded before anything else so we can use proper events? // Or can we assume we are loaded before anything else so we can use proper events?

View File

@ -249,6 +249,8 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
{ {
// Create a new instance of the compiler (currently we don't want reuse) // Create a new instance of the compiler (currently we don't want reuse)
OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.Compiler LSLCompiler = new OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.Compiler(); OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.Compiler LSLCompiler = new OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.Compiler();
// Compile (We assume LSL) // Compile (We assume LSL)
@ -272,11 +274,15 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
// We need to give (untrusted) assembly a private instance of BuiltIns // We need to give (untrusted) assembly a private instance of BuiltIns
// this private copy will contain Read-Only FullitemID so that it can bring that on to the server whenever needed. // this private copy will contain Read-Only FullitemID so that it can bring that on to the server whenever needed.
LSL_BuiltIn_Commands LSLB = new LSL_BuiltIn_Commands(this, World.GetSceneObjectPart(localID)); LSL_BuiltIn_Commands LSLB = new LSL_BuiltIn_Commands(m_scriptEngine, World.GetSceneObjectPart(localID), localID, itemID);
// Start the script - giving it BuiltIns // Start the script - giving it BuiltIns
CompiledScript.Start(LSLB); CompiledScript.Start(LSLB);
// Fire the first start-event
m_scriptEngine.myEventQueueManager.AddToObjectQueue(localID, "state_entry", new object[] { });
} }
catch (Exception e) catch (Exception e)
{ {
@ -291,6 +297,9 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
// Stop script // Stop script
Console.WriteLine("Stop script localID: " + localID + " LLUID: " + itemID.ToString()); Console.WriteLine("Stop script localID: " + localID + " LLUID: " + itemID.ToString());
// Stop long command on script
m_scriptEngine.myLSLLongCmdHandler.RemoveScript(localID, itemID);
// Get AppDomain // Get AppDomain
AppDomain ad = GetScript(localID, itemID).Exec.GetAppDomain(); AppDomain ad = GetScript(localID, itemID).Exec.GetAppDomain();
// Tell script not to accept new requests // Tell script not to accept new requests
@ -328,12 +337,5 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
} }
public string RegionName
{
get
{
return World.RegionInfo.RegionName;
}
}
} }
} }