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).
parent
53be4774b3
commit
b75c1b2191
|
@ -23,7 +23,7 @@ namespace OpenSim.Region.ScriptEngine.Common
|
|||
// Object never expires
|
||||
public override Object InitializeLifetimeService()
|
||||
{
|
||||
Console.WriteLine("Executor: InitializeLifetimeService()");
|
||||
//Console.WriteLine("Executor: InitializeLifetimeService()");
|
||||
// return null;
|
||||
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.
|
||||
// Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead!
|
||||
|
||||
if (m_Running == false)
|
||||
try
|
||||
{
|
||||
// Script is inactive, do not execute!
|
||||
return;
|
||||
}
|
||||
if (m_Running == false)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// Not found, create
|
||||
Type type = m_Script.GetType();
|
||||
if (Events.ContainsKey(EventName) == false)
|
||||
{
|
||||
// Not found, create
|
||||
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
|
||||
{
|
||||
MethodInfo mi = type.GetMethod(EventName);
|
||||
Events.Add(EventName, mi);
|
||||
// Invoke it
|
||||
ev.Invoke(m_Script, args);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Event name not found, cache it as not found
|
||||
Events.Add(EventName, null);
|
||||
// TODO: Send to correct place
|
||||
Console.WriteLine("ScriptEngine Exception attempting to executing script function: " + e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -51,6 +51,14 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL
|
|||
// Output assembly name
|
||||
ScriptCompileCounter++;
|
||||
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");
|
||||
|
||||
// DEBUG - write source to disk
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL
|
|||
// Object never expires
|
||||
public override Object InitializeLifetimeService()
|
||||
{
|
||||
Console.WriteLine("LSL_BaseClass: InitializeLifetimeService()");
|
||||
//Console.WriteLine("LSL_BaseClass: InitializeLifetimeService()");
|
||||
// return null;
|
||||
ILease lease = (ILease)base.InitializeLifetimeService();
|
||||
|
||||
|
|
|
@ -19,15 +19,20 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler
|
|||
{
|
||||
|
||||
private System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
|
||||
private ScriptManager m_manager;
|
||||
private ScriptEngine m_ScriptEngine;
|
||||
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_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
|
||||
public override Object InitializeLifetimeService()
|
||||
{
|
||||
Console.WriteLine("LSL_BuiltIn_Commands: InitializeLifetimeService()");
|
||||
//Console.WriteLine("LSL_BuiltIn_Commands: InitializeLifetimeService()");
|
||||
// return null;
|
||||
ILease lease = (ILease)base.InitializeLifetimeService();
|
||||
|
||||
|
@ -55,7 +60,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler
|
|||
|
||||
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.
|
||||
|
@ -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 llLookAt(LSL_Types.Vector3 target, double strength, double damping) { }
|
||||
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 double llGetMass() { return 0; }
|
||||
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 int llGetAttached() { 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 llGetRegionFPS() { return 10.0f; }
|
||||
public void llParticleSystem(List<Object> rules) { }
|
||||
|
|
|
@ -77,7 +77,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
|
|||
//);
|
||||
Console.WriteLine("OnRezScript localID: " + localID + " LLUID: " + itemID.ToString() + " Size: " + script.Length);
|
||||
myScriptEngine.myScriptManager.StartScript(localID, itemID, script);
|
||||
myScriptEngine.myEventQueueManager.AddToObjectQueue(localID, "state_entry", new object[] { });
|
||||
}
|
||||
public void OnRemoveScript(uint localID, LLUUID itemID)
|
||||
{
|
||||
|
|
|
@ -226,6 +226,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Add event to event execution queue
|
||||
/// </summary>
|
||||
|
@ -237,35 +238,45 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
|
|||
// Determine all scripts in Object and add to their queue
|
||||
//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
|
||||
if (myScriptEngine.myScriptManager.Scripts.ContainsKey(localID) == false)
|
||||
{
|
||||
//Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID.");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
foreach (LLUUID itemID in new System.Collections.ArrayList(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?
|
||||
AddToScriptQueue(localID, itemID, FunctionName, param);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -48,6 +48,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
|
|||
internal EventQueueManager myEventQueueManager; // Executes events
|
||||
internal ScriptManager myScriptManager; // Load, unload and execute scripts
|
||||
internal AppDomainManager myAppDomainManager;
|
||||
internal LSLLongCmdHandler myLSLLongCmdHandler;
|
||||
|
||||
private OpenSim.Framework.Console.LogBase m_log;
|
||||
|
||||
|
@ -77,6 +78,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
|
|||
myEventManager = new EventManager(this);
|
||||
myScriptManager = new ScriptManager(this);
|
||||
myAppDomainManager = new AppDomainManager();
|
||||
myLSLLongCmdHandler = new LSLLongCmdHandler(this);
|
||||
|
||||
// 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?
|
||||
|
|
|
@ -249,6 +249,8 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
|
|||
{
|
||||
|
||||
|
||||
|
||||
|
||||
// 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();
|
||||
// Compile (We assume LSL)
|
||||
|
@ -272,11 +274,15 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
|
|||
|
||||
// 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.
|
||||
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
|
||||
CompiledScript.Start(LSLB);
|
||||
|
||||
// Fire the first start-event
|
||||
m_scriptEngine.myEventQueueManager.AddToObjectQueue(localID, "state_entry", new object[] { });
|
||||
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -291,6 +297,9 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
|
|||
// Stop script
|
||||
Console.WriteLine("Stop script localID: " + localID + " LLUID: " + itemID.ToString());
|
||||
|
||||
// Stop long command on script
|
||||
m_scriptEngine.myLSLLongCmdHandler.RemoveScript(localID, itemID);
|
||||
|
||||
// Get AppDomain
|
||||
AppDomain ad = GetScript(localID, itemID).Exec.GetAppDomain();
|
||||
// Tell script not to accept new requests
|
||||
|
@ -328,12 +337,5 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
|
|||
|
||||
}
|
||||
|
||||
public string RegionName
|
||||
{
|
||||
get
|
||||
{
|
||||
return World.RegionInfo.RegionName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue