OpenSim.Region.Environment into a "framework" part and a modules only
part. This first changeset refactors OpenSim.Region.Environment.Scenes,
OpenSim.Region.Environment.Interfaces, and OpenSim.Region.Interfaces
into OpenSim.Region.Framework.{Interfaces,Scenes} leaving only region
modules in OpenSim.Region.Environment.
The next step will be to move region modules up from
OpenSim.Region.Environment.Modules to OpenSim.Region.CoreModules and
then sort out which modules are really core modules and which should
move out to forge.
I've been very careful to NOT BREAK anything. i hope i've
succeeded. as this is the work of a whole week i hope i managed to
keep track with the applied patches of the last week --- could any of
you that did check in stuff have a look at whether it survived? thx!
949 lines
32 KiB
C#
949 lines
32 KiB
C#
/*
|
|
* Copyright (c) Contributors, http://opensimulator.org/
|
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of the OpenSim Project nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Runtime.Remoting;
|
|
using System.Runtime.Remoting.Lifetime;
|
|
using System.Threading;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Security.Policy;
|
|
using System.Reflection;
|
|
using System.Globalization;
|
|
using System.Xml;
|
|
using OpenMetaverse;
|
|
using log4net;
|
|
using Nini.Config;
|
|
using Amib.Threading;
|
|
using OpenSim.Framework;
|
|
using OpenSim.Region.Environment;
|
|
using OpenSim.Region.Framework.Scenes;
|
|
using OpenSim.Region.Framework.Interfaces;
|
|
using OpenSim.Region.ScriptEngine.Shared;
|
|
using OpenSim.Region.ScriptEngine.Shared.Api;
|
|
using OpenSim.Region.ScriptEngine.Shared.Api.Runtime;
|
|
using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
|
|
using OpenSim.Region.ScriptEngine.Shared.CodeTools;
|
|
using OpenSim.Region.ScriptEngine.Interfaces;
|
|
|
|
namespace OpenSim.Region.ScriptEngine.Shared.Instance
|
|
{
|
|
public class ScriptInstance : IScriptInstance
|
|
{
|
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
private IScriptEngine m_Engine;
|
|
private IScriptWorkItem m_CurrentResult = null;
|
|
private Queue m_EventQueue = new Queue(32);
|
|
private bool m_RunEvents = false;
|
|
private UUID m_ItemID;
|
|
private uint m_LocalID;
|
|
private UUID m_ObjectID;
|
|
private UUID m_AssetID;
|
|
private IScript m_Script;
|
|
private UUID m_AppDomain;
|
|
private DetectParams[] m_DetectParams;
|
|
private bool m_TimerQueued;
|
|
private DateTime m_EventStart;
|
|
private bool m_InEvent;
|
|
private string m_PrimName;
|
|
private string m_ScriptName;
|
|
private string m_Assembly;
|
|
private int m_StartParam = 0;
|
|
private string m_CurrentEvent = String.Empty;
|
|
private bool m_InSelfDelete = false;
|
|
private int m_MaxScriptQueue;
|
|
private bool m_SaveState = true;
|
|
private bool m_ShuttingDown = false;
|
|
private int m_ControlEventsInQueue = 0;
|
|
private int m_LastControlLevel = 0;
|
|
private bool m_CollisionInQueue = false;
|
|
private TaskInventoryItem m_thisScriptTask;
|
|
// The following is for setting a minimum delay between events
|
|
private double m_minEventDelay = 0;
|
|
private long m_eventDelayTicks = 0;
|
|
private long m_nextEventTimeTicks = 0;
|
|
private bool m_startOnInit = true;
|
|
private StateSource m_stateSource;
|
|
private bool m_postOnRez;
|
|
private bool m_startedFromSavedState = false;
|
|
|
|
//private ISponsor m_ScriptSponsor;
|
|
private Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>
|
|
m_LineMap;
|
|
|
|
public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>
|
|
LineMap
|
|
{
|
|
get { return m_LineMap; }
|
|
set { m_LineMap = value; }
|
|
}
|
|
|
|
private Dictionary<string,IScriptApi> m_Apis = new Dictionary<string,IScriptApi>();
|
|
|
|
// Script state
|
|
private string m_State="default";
|
|
|
|
public Object[] PluginData = new Object[0];
|
|
|
|
/// <summary>
|
|
/// Used by llMinEventDelay to suppress events happening any faster than this speed.
|
|
/// This currently restricts all events in one go. Not sure if each event type has
|
|
/// its own check so take the simple route first.
|
|
/// </summary>
|
|
public double MinEventDelay
|
|
{
|
|
get { return m_minEventDelay; }
|
|
set
|
|
{
|
|
if (value > 0.001)
|
|
m_minEventDelay = value;
|
|
else
|
|
m_minEventDelay = 0.0;
|
|
m_eventDelayTicks = (long)(m_minEventDelay * 10000000L);
|
|
m_nextEventTimeTicks = DateTime.Now.Ticks;
|
|
}
|
|
}
|
|
|
|
public bool Running
|
|
{
|
|
get { return m_RunEvents; }
|
|
set { m_RunEvents = value; }
|
|
}
|
|
|
|
public bool ShuttingDown
|
|
{
|
|
get { return m_ShuttingDown; }
|
|
set { m_ShuttingDown = value; }
|
|
}
|
|
|
|
public string State
|
|
{
|
|
get { return m_State; }
|
|
set { m_State = value; }
|
|
}
|
|
|
|
public IScriptEngine Engine
|
|
{
|
|
get { return m_Engine; }
|
|
}
|
|
|
|
public UUID AppDomain
|
|
{
|
|
get { return m_AppDomain; }
|
|
set { m_AppDomain = value; }
|
|
}
|
|
|
|
public string PrimName
|
|
{
|
|
get { return m_PrimName; }
|
|
}
|
|
|
|
public string ScriptName
|
|
{
|
|
get { return m_ScriptName; }
|
|
}
|
|
|
|
public UUID ItemID
|
|
{
|
|
get { return m_ItemID; }
|
|
}
|
|
|
|
public UUID ObjectID
|
|
{
|
|
get { return m_ObjectID; }
|
|
}
|
|
|
|
public uint LocalID
|
|
{
|
|
get { return m_LocalID; }
|
|
}
|
|
|
|
public UUID AssetID
|
|
{
|
|
get { return m_AssetID; }
|
|
}
|
|
|
|
public Queue EventQueue
|
|
{
|
|
get { return m_EventQueue; }
|
|
}
|
|
|
|
public void ClearQueue()
|
|
{
|
|
m_TimerQueued = false;
|
|
m_EventQueue.Clear();
|
|
}
|
|
|
|
public int StartParam
|
|
{
|
|
get { return m_StartParam; }
|
|
set { m_StartParam = value; }
|
|
}
|
|
|
|
public TaskInventoryItem ScriptTask
|
|
{
|
|
get { return m_thisScriptTask; }
|
|
}
|
|
|
|
public ScriptInstance(IScriptEngine engine, SceneObjectPart part,
|
|
UUID itemID, UUID assetID, string assembly,
|
|
AppDomain dom, string primName, string scriptName,
|
|
int startParam, bool postOnRez, StateSource stateSource,
|
|
int maxScriptQueue)
|
|
{
|
|
m_Engine = engine;
|
|
|
|
m_LocalID = part.LocalId;
|
|
m_ObjectID = part.UUID;
|
|
m_ItemID = itemID;
|
|
m_AssetID = assetID;
|
|
m_PrimName = primName;
|
|
m_ScriptName = scriptName;
|
|
m_Assembly = assembly;
|
|
m_StartParam = startParam;
|
|
m_MaxScriptQueue = maxScriptQueue;
|
|
m_stateSource = stateSource;
|
|
m_postOnRez = postOnRez;
|
|
|
|
if (part != null && part.TaskInventory.ContainsKey(m_ItemID))
|
|
{
|
|
m_thisScriptTask = part.TaskInventory[m_ItemID];
|
|
}
|
|
|
|
ApiManager am = new ApiManager();
|
|
|
|
foreach (string api in am.GetApis())
|
|
{
|
|
m_Apis[api] = am.CreateApi(api);
|
|
m_Apis[api].Initialize(engine, part, m_LocalID, itemID);
|
|
}
|
|
|
|
try
|
|
{
|
|
m_Script = (IScript)dom.CreateInstanceAndUnwrap(
|
|
Path.GetFileNameWithoutExtension(assembly),
|
|
"SecondLife.Script");
|
|
|
|
// Add a sponsor to the script
|
|
// ISponsor scriptSponsor = new ScriptSponsor();
|
|
// ILease lease = (ILease)RemotingServices.GetLifetimeService(m_Script as MarshalByRefObject);
|
|
// lease.Register(scriptSponsor);
|
|
//m_ScriptSponsor = scriptSponsor;
|
|
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
m_Engine.Log.ErrorFormat("[Script] Error loading assembly {0}\n"+e.ToString(), assembly);
|
|
}
|
|
|
|
try
|
|
{
|
|
foreach (KeyValuePair<string,IScriptApi> kv in m_Apis)
|
|
{
|
|
m_Script.InitApi(kv.Key, kv.Value);
|
|
}
|
|
|
|
// m_Engine.Log.Debug("[Script] Script instance created");
|
|
|
|
part.SetScriptEvents(m_ItemID,
|
|
(int)m_Script.GetStateEventFlags(State));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
m_Engine.Log.Error("[Script] Error loading script instance\n"+e.ToString());
|
|
return;
|
|
}
|
|
|
|
m_SaveState = true;
|
|
|
|
string savedState = Path.Combine(Path.GetDirectoryName(assembly),
|
|
m_ItemID.ToString() + ".state");
|
|
if (File.Exists(savedState))
|
|
{
|
|
string xml = String.Empty;
|
|
|
|
try
|
|
{
|
|
FileInfo fi = new FileInfo(savedState);
|
|
int size=(int)fi.Length;
|
|
if (size < 512000)
|
|
{
|
|
using (FileStream fs = File.Open(savedState,
|
|
FileMode.Open, FileAccess.Read, FileShare.None))
|
|
{
|
|
System.Text.ASCIIEncoding enc =
|
|
new System.Text.ASCIIEncoding();
|
|
|
|
Byte[] data = new Byte[size];
|
|
fs.Read(data, 0, size);
|
|
|
|
xml = enc.GetString(data);
|
|
|
|
ScriptSerializer.Deserialize(xml, this);
|
|
|
|
AsyncCommandManager.CreateFromData(m_Engine,
|
|
m_LocalID, m_ItemID, m_ObjectID,
|
|
PluginData);
|
|
|
|
// m_Engine.Log.DebugFormat("[Script] Successfully retrieved state for script {0}.{1}", m_PrimName, m_ScriptName);
|
|
|
|
part.SetScriptEvents(m_ItemID,
|
|
(int)m_Script.GetStateEventFlags(State));
|
|
|
|
if (m_RunEvents && (!m_ShuttingDown))
|
|
{
|
|
m_RunEvents = false;
|
|
}
|
|
else
|
|
{
|
|
m_RunEvents = false;
|
|
m_startOnInit = false;
|
|
}
|
|
|
|
// we get new rez events on sim restart, too
|
|
// but if there is state, then we fire the change
|
|
// event
|
|
|
|
// We loaded state, don't force a re-save
|
|
m_SaveState = false;
|
|
m_startedFromSavedState = true;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_Engine.Log.Error("[Script] Unable to load script state: Memory limit exceeded");
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
m_Engine.Log.ErrorFormat("[Script] Unable to load script state from xml: {0}\n"+e.ToString(), xml);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ScenePresence presence = m_Engine.World.GetScenePresence(part.OwnerID);
|
|
|
|
if (presence != null && (!postOnRez))
|
|
presence.ControllingClient.SendAgentAlertMessage("Compile successful", false);
|
|
|
|
// m_Engine.Log.ErrorFormat("[Script] Unable to load script state, file not found");
|
|
}
|
|
}
|
|
|
|
public void Init()
|
|
{
|
|
if (!m_startOnInit) return;
|
|
|
|
if (m_startedFromSavedState)
|
|
{
|
|
Start();
|
|
if (m_postOnRez)
|
|
{
|
|
PostEvent(new EventParams("on_rez",
|
|
new Object[] {new LSL_Types.LSLInteger(m_StartParam)}, new DetectParams[0]));
|
|
}
|
|
|
|
if (m_stateSource == StateSource.NewRez)
|
|
{
|
|
// m_Engine.Log.Debug("[Script] Posted changed(CHANGED_REGION_RESTART) to script");
|
|
PostEvent(new EventParams("changed",
|
|
new Object[] {new LSL_Types.LSLInteger(256)}, new DetectParams[0]));
|
|
}
|
|
else if (m_stateSource == StateSource.PrimCrossing)
|
|
{
|
|
// CHANGED_REGION
|
|
PostEvent(new EventParams("changed",
|
|
new Object[] {new LSL_Types.LSLInteger(512)}, new DetectParams[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Start();
|
|
PostEvent(new EventParams("state_entry",
|
|
new Object[0], new DetectParams[0]));
|
|
if (m_postOnRez)
|
|
{
|
|
PostEvent(new EventParams("on_rez",
|
|
new Object[] {new LSL_Types.LSLInteger(m_StartParam)}, new DetectParams[0]));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private void ReleaseControls()
|
|
{
|
|
SceneObjectPart part=m_Engine.World.GetSceneObjectPart(m_LocalID);
|
|
if (part != null && part.TaskInventory.ContainsKey(m_ItemID))
|
|
{
|
|
UUID permsGranter = part.TaskInventory[m_ItemID].PermsGranter;
|
|
int permsMask = part.TaskInventory[m_ItemID].PermsMask;
|
|
|
|
if ((permsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)
|
|
{
|
|
|
|
ScenePresence presence = m_Engine.World.GetScenePresence(permsGranter);
|
|
if (presence != null)
|
|
presence.UnRegisterControlEventsToScript(m_LocalID, m_ItemID);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void DestroyScriptInstance()
|
|
{
|
|
ReleaseControls();
|
|
AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
|
|
}
|
|
|
|
public void RemoveState()
|
|
{
|
|
string savedState = Path.Combine(Path.GetDirectoryName(m_Assembly),
|
|
m_ItemID.ToString() + ".state");
|
|
|
|
try
|
|
{
|
|
File.Delete(savedState);
|
|
}
|
|
catch(Exception)
|
|
{
|
|
}
|
|
}
|
|
|
|
public void VarDump(Dictionary<string, object> vars)
|
|
{
|
|
Console.WriteLine("Variable dump for script {0}", m_ItemID.ToString());
|
|
foreach (KeyValuePair<string, object> v in vars)
|
|
{
|
|
Console.WriteLine("Variable: {0} = '{1}'", v. Key,
|
|
v.Value.ToString());
|
|
}
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
lock (m_EventQueue)
|
|
{
|
|
if (Running)
|
|
return;
|
|
|
|
m_RunEvents = true;
|
|
|
|
if (m_EventQueue.Count > 0)
|
|
{
|
|
if (m_CurrentResult == null)
|
|
m_CurrentResult = m_Engine.QueueEventHandler(this);
|
|
else
|
|
m_Engine.Log.Error("[Script] Tried to start a script that was already queued");
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool Stop(int timeout)
|
|
{
|
|
IScriptWorkItem result;
|
|
|
|
lock (m_EventQueue)
|
|
{
|
|
if (!Running)
|
|
return true;
|
|
|
|
if (m_CurrentResult == null)
|
|
{
|
|
m_RunEvents = false;
|
|
return true;
|
|
}
|
|
|
|
if (m_CurrentResult.Cancel())
|
|
{
|
|
m_CurrentResult = null;
|
|
m_RunEvents = false;
|
|
return true;
|
|
}
|
|
|
|
result = m_CurrentResult;
|
|
m_RunEvents = false;
|
|
}
|
|
|
|
if (result.Wait(new TimeSpan((long)timeout * 100000)))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
lock (m_EventQueue)
|
|
{
|
|
result = m_CurrentResult;
|
|
}
|
|
|
|
if (result == null)
|
|
return true;
|
|
|
|
if (!m_InSelfDelete)
|
|
result.Abort();
|
|
|
|
lock (m_EventQueue)
|
|
{
|
|
m_CurrentResult = null;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void SetState(string state)
|
|
{
|
|
if (state == State)
|
|
return;
|
|
|
|
PostEvent(new EventParams("state_exit", new Object[0],
|
|
new DetectParams[0]));
|
|
PostEvent(new EventParams("state", new Object[] { state },
|
|
new DetectParams[0]));
|
|
PostEvent(new EventParams("state_entry", new Object[0],
|
|
new DetectParams[0]));
|
|
|
|
throw new EventAbortException();
|
|
}
|
|
|
|
public void PostEvent(EventParams data)
|
|
{
|
|
// m_Engine.Log.DebugFormat("[Script] Posted event {2} in state {3} to {0}.{1}",
|
|
// m_PrimName, m_ScriptName, data.EventName, m_State);
|
|
|
|
if (!Running)
|
|
return;
|
|
|
|
// If min event delay is set then ignore any events untill the time has expired
|
|
// This currently only allows 1 event of any type in the given time period.
|
|
// This may need extending to allow for a time for each individual event type.
|
|
if (m_eventDelayTicks != 0)
|
|
{
|
|
if (DateTime.Now.Ticks < m_nextEventTimeTicks)
|
|
return;
|
|
m_nextEventTimeTicks = DateTime.Now.Ticks + m_eventDelayTicks;
|
|
}
|
|
|
|
lock (m_EventQueue)
|
|
{
|
|
if (m_EventQueue.Count >= m_MaxScriptQueue)
|
|
return;
|
|
|
|
if (data.EventName == "timer")
|
|
{
|
|
if (m_TimerQueued)
|
|
return;
|
|
m_TimerQueued = true;
|
|
}
|
|
|
|
if (data.EventName == "control")
|
|
{
|
|
int held = ((LSL_Types.LSLInteger)data.Params[1]).value;
|
|
// int changed = ((LSL_Types.LSLInteger)data.Params[2]).value;
|
|
|
|
// If the last message was a 0 (nothing held)
|
|
// and this one is also nothing held, drop it
|
|
//
|
|
if (m_LastControlLevel == held && held == 0)
|
|
return;
|
|
|
|
// If there is one or more queued, then queue
|
|
// only changed ones, else queue unconditionally
|
|
//
|
|
if (m_ControlEventsInQueue > 0)
|
|
{
|
|
if (m_LastControlLevel == held)
|
|
return;
|
|
}
|
|
|
|
m_LastControlLevel = held;
|
|
m_ControlEventsInQueue++;
|
|
}
|
|
|
|
if (data.EventName == "collision")
|
|
{
|
|
if (m_CollisionInQueue)
|
|
return;
|
|
if (data.DetectParams == null)
|
|
return;
|
|
|
|
m_CollisionInQueue = true;
|
|
}
|
|
|
|
m_EventQueue.Enqueue(data);
|
|
|
|
if (m_CurrentResult == null)
|
|
{
|
|
m_CurrentResult = m_Engine.QueueEventHandler(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Process the next event queued for this script
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public object EventProcessor()
|
|
{
|
|
lock (m_Script)
|
|
{
|
|
EventParams data = null;
|
|
|
|
lock (m_EventQueue)
|
|
{
|
|
data = (EventParams) m_EventQueue.Dequeue();
|
|
if (data == null) // Shouldn't happen
|
|
{
|
|
if ((m_EventQueue.Count > 0) && m_RunEvents && (!m_ShuttingDown))
|
|
{
|
|
m_CurrentResult = m_Engine.QueueEventHandler(this);
|
|
}
|
|
else
|
|
{
|
|
m_CurrentResult = null;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (data.EventName == "timer")
|
|
m_TimerQueued = false;
|
|
if (data.EventName == "control")
|
|
{
|
|
if (m_ControlEventsInQueue > 0)
|
|
m_ControlEventsInQueue--;
|
|
}
|
|
if (data.EventName == "collision")
|
|
m_CollisionInQueue = false;
|
|
}
|
|
|
|
//m_log.DebugFormat("[XENGINE]: Processing event {0} for {1}", data.EventName, this);
|
|
|
|
m_DetectParams = data.DetectParams;
|
|
|
|
if (data.EventName == "state") // Hardcoded state change
|
|
{
|
|
// m_Engine.Log.DebugFormat("[Script] Script {0}.{1} state set to {2}",
|
|
// m_PrimName, m_ScriptName, data.Params[0].ToString());
|
|
m_State=data.Params[0].ToString();
|
|
AsyncCommandManager.RemoveScript(m_Engine,
|
|
m_LocalID, m_ItemID);
|
|
|
|
SceneObjectPart part = m_Engine.World.GetSceneObjectPart(
|
|
m_LocalID);
|
|
if (part != null)
|
|
{
|
|
part.SetScriptEvents(m_ItemID,
|
|
(int)m_Script.GetStateEventFlags(State));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_Engine.World.PipeEventsForScript(m_LocalID) ||
|
|
data.EventName == "control") // Don't freeze avies!
|
|
{
|
|
SceneObjectPart part = m_Engine.World.GetSceneObjectPart(
|
|
m_LocalID);
|
|
// m_Engine.Log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}",
|
|
// m_PrimName, m_ScriptName, data.EventName, m_State);
|
|
|
|
try
|
|
{
|
|
m_CurrentEvent = data.EventName;
|
|
m_EventStart = DateTime.Now;
|
|
m_InEvent = true;
|
|
|
|
m_Script.ExecuteEvent(State, data.EventName, data.Params);
|
|
|
|
m_InEvent = false;
|
|
m_CurrentEvent = String.Empty;
|
|
|
|
if (m_SaveState)
|
|
{
|
|
// This will be the very first event we deliver
|
|
// (state_entry) in defualt state
|
|
//
|
|
|
|
SaveState(m_Assembly);
|
|
|
|
m_SaveState = false;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
m_InEvent = false;
|
|
m_CurrentEvent = String.Empty;
|
|
|
|
if ((!(e is TargetInvocationException) || !(e.InnerException is SelfDeleteException)) && (!(e is ThreadAbortException)))
|
|
{
|
|
try
|
|
{
|
|
// DISPLAY ERROR INWORLD
|
|
string text = FormatException(e);
|
|
|
|
if (text.Length > 1000)
|
|
text = text.Substring(0, 1000);
|
|
m_Engine.World.SimChat(Utils.StringToBytes(text),
|
|
ChatTypeEnum.DebugChannel, 2147483647,
|
|
part.AbsolutePosition,
|
|
part.Name, part.UUID, false);
|
|
}
|
|
catch (Exception e2) // LEGIT: User Scripting
|
|
{
|
|
m_Engine.Log.Error("[Script]: "+
|
|
"Error displaying error in-world: " +
|
|
e2.ToString());
|
|
m_Engine.Log.Error("[Script]: " +
|
|
"Errormessage: Error compiling script:\r\n" +
|
|
e.ToString());
|
|
}
|
|
}
|
|
else if ((e is TargetInvocationException) && (e.InnerException is SelfDeleteException))
|
|
{
|
|
m_InSelfDelete = true;
|
|
if (part != null && part.ParentGroup != null)
|
|
m_Engine.World.DeleteSceneObject(part.ParentGroup, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lock (m_EventQueue)
|
|
{
|
|
if ((m_EventQueue.Count > 0) && m_RunEvents && (!m_ShuttingDown))
|
|
{
|
|
m_CurrentResult = m_Engine.QueueEventHandler(this);
|
|
}
|
|
else
|
|
{
|
|
m_CurrentResult = null;
|
|
}
|
|
}
|
|
|
|
m_DetectParams = null;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public int EventTime()
|
|
{
|
|
if (!m_InEvent)
|
|
return 0;
|
|
|
|
return (DateTime.Now - m_EventStart).Seconds;
|
|
}
|
|
|
|
public void ResetScript()
|
|
{
|
|
if (m_Script == null)
|
|
return;
|
|
|
|
bool running = Running;
|
|
|
|
RemoveState();
|
|
ReleaseControls();
|
|
|
|
Stop(0);
|
|
SceneObjectPart part=m_Engine.World.GetSceneObjectPart(m_LocalID);
|
|
part.Inventory.GetInventoryItem(m_ItemID).PermsMask = 0;
|
|
part.Inventory.GetInventoryItem(m_ItemID).PermsGranter = UUID.Zero;
|
|
AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
|
|
m_EventQueue.Clear();
|
|
m_Script.ResetVars();
|
|
m_State = "default";
|
|
|
|
part.SetScriptEvents(m_ItemID,
|
|
(int)m_Script.GetStateEventFlags(State));
|
|
if (running)
|
|
Start();
|
|
m_SaveState = true;
|
|
PostEvent(new EventParams("state_entry",
|
|
new Object[0], new DetectParams[0]));
|
|
}
|
|
|
|
public void ApiResetScript()
|
|
{
|
|
// bool running = Running;
|
|
|
|
RemoveState();
|
|
ReleaseControls();
|
|
|
|
m_Script.ResetVars();
|
|
SceneObjectPart part=m_Engine.World.GetSceneObjectPart(m_LocalID);
|
|
part.Inventory.GetInventoryItem(m_ItemID).PermsMask = 0;
|
|
part.Inventory.GetInventoryItem(m_ItemID).PermsGranter = UUID.Zero;
|
|
AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
|
|
|
|
m_EventQueue.Clear();
|
|
m_Script.ResetVars();
|
|
m_State = "default";
|
|
|
|
part.SetScriptEvents(m_ItemID,
|
|
(int)m_Script.GetStateEventFlags(State));
|
|
|
|
if (m_CurrentEvent != "state_entry")
|
|
{
|
|
m_SaveState = true;
|
|
PostEvent(new EventParams("state_entry",
|
|
new Object[0], new DetectParams[0]));
|
|
throw new EventAbortException();
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, object> GetVars()
|
|
{
|
|
return m_Script.GetVars();
|
|
}
|
|
|
|
public void SetVars(Dictionary<string, object> vars)
|
|
{
|
|
m_Script.SetVars(vars);
|
|
}
|
|
|
|
public DetectParams GetDetectParams(int idx)
|
|
{
|
|
if (m_DetectParams == null)
|
|
return null;
|
|
if (idx < 0 || idx >= m_DetectParams.Length)
|
|
return null;
|
|
|
|
return m_DetectParams[idx];
|
|
}
|
|
|
|
public UUID GetDetectID(int idx)
|
|
{
|
|
if (m_DetectParams == null)
|
|
return UUID.Zero;
|
|
if (idx < 0 || idx >= m_DetectParams.Length)
|
|
return UUID.Zero;
|
|
|
|
return m_DetectParams[idx].Key;
|
|
}
|
|
|
|
public void SaveState(string assembly)
|
|
{
|
|
// If we're currently in an event, just tell it to save upon return
|
|
//
|
|
if (m_InEvent)
|
|
{
|
|
m_SaveState = true;
|
|
return;
|
|
}
|
|
|
|
PluginData = AsyncCommandManager.GetSerializationData(m_Engine, m_ItemID);
|
|
|
|
string xml = ScriptSerializer.Serialize(this);
|
|
|
|
try
|
|
{
|
|
FileStream fs = File.Create(Path.Combine(Path.GetDirectoryName(assembly), m_ItemID.ToString() + ".state"));
|
|
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
|
|
Byte[] buf = enc.GetBytes(xml);
|
|
fs.Write(buf, 0, buf.Length);
|
|
fs.Close();
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
Console.WriteLine("Unable to save xml\n"+e.ToString());
|
|
}
|
|
if (!File.Exists(Path.Combine(Path.GetDirectoryName(assembly), m_ItemID.ToString() + ".state")))
|
|
{
|
|
throw new Exception("Completed persistence save, but no file was created");
|
|
}
|
|
}
|
|
|
|
public IScriptApi GetApi(string name)
|
|
{
|
|
if (m_Apis.ContainsKey(name))
|
|
return m_Apis[name];
|
|
return null;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return String.Format("{0} {1} on {2}", m_ScriptName, m_ItemID, m_PrimName);
|
|
}
|
|
|
|
string FormatException(Exception e)
|
|
{
|
|
if (e.InnerException == null) // Not a normal runtime error
|
|
return e.ToString();
|
|
|
|
string message = "Runtime error:\n" + e.InnerException.StackTrace;
|
|
string[] lines = message.Split(new char[] {'\n'});
|
|
|
|
foreach (string line in lines)
|
|
{
|
|
if (line.Contains("SecondLife.Script"))
|
|
{
|
|
int idx = line.IndexOf(':');
|
|
if (idx != -1)
|
|
{
|
|
string val = line.Substring(idx+1);
|
|
int lineNum = 0;
|
|
if (int.TryParse(val, out lineNum))
|
|
{
|
|
KeyValuePair<int, int> pos =
|
|
Compiler.FindErrorPosition(
|
|
lineNum, 0, LineMap);
|
|
|
|
int scriptLine = pos.Key;
|
|
int col = pos.Value;
|
|
if (scriptLine == 0)
|
|
scriptLine++;
|
|
if (col == 0)
|
|
col++;
|
|
message = string.Format("Runtime error:\n" +
|
|
"Line ({0}): {1}", scriptLine - 1,
|
|
e.InnerException.Message);
|
|
|
|
return message;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_log.ErrorFormat("Scripting exception:");
|
|
m_log.ErrorFormat(e.ToString());
|
|
|
|
return e.ToString();
|
|
}
|
|
|
|
public string GetAssemblyName()
|
|
{
|
|
return m_Assembly;
|
|
}
|
|
|
|
public string GetXMLState()
|
|
{
|
|
bool run = Running;
|
|
Stop(100);
|
|
Running = run;
|
|
return ScriptSerializer.Serialize(this);
|
|
}
|
|
}
|
|
}
|