2008-08-27 22:38:36 +00:00
/ *
* 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 .
2009-06-01 06:37:14 +00:00
* * Neither the name of the OpenSimulator Project nor the
2008-08-27 22:38:36 +00:00
* 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 ;
2012-07-11 21:54:22 +00:00
using System.Collections ;
using System.Collections.Generic ;
using System.Globalization ;
2008-08-27 22:38:36 +00:00
using System.IO ;
2012-07-11 21:54:22 +00:00
using System.Reflection ;
2008-11-26 11:12:57 +00:00
using System.Runtime.Remoting ;
using System.Runtime.Remoting.Lifetime ;
2008-08-27 22:38:36 +00:00
using System.Security.Policy ;
2012-07-11 21:54:22 +00:00
using System.Text ;
using System.Threading ;
2008-08-27 22:38:36 +00:00
using System.Xml ;
2008-09-06 07:52:41 +00:00
using OpenMetaverse ;
2008-10-11 16:32:31 +00:00
using log4net ;
2008-08-27 22:38:36 +00:00
using Nini.Config ;
using Amib.Threading ;
using OpenSim.Framework ;
2009-02-10 13:10:57 +00:00
using OpenSim.Region.CoreModules ;
2009-02-06 16:55:34 +00:00
using OpenSim.Region.Framework.Scenes ;
using OpenSim.Region.Framework.Interfaces ;
2008-08-27 22:38:36 +00:00
using OpenSim.Region.ScriptEngine.Shared ;
using OpenSim.Region.ScriptEngine.Shared.Api ;
2008-11-26 11:12:57 +00:00
using OpenSim.Region.ScriptEngine.Shared.Api.Runtime ;
2008-08-27 22:38:36 +00:00
using OpenSim.Region.ScriptEngine.Shared.ScriptBase ;
using OpenSim.Region.ScriptEngine.Shared.CodeTools ;
using OpenSim.Region.ScriptEngine.Interfaces ;
2015-09-08 11:43:54 +00:00
using System.Diagnostics ; //for [DebuggerNonUserCode]
2008-08-27 22:38:36 +00:00
namespace OpenSim.Region.ScriptEngine.Shared.Instance
{
2009-08-14 13:18:56 +00:00
public class ScriptInstance : MarshalByRefObject , IScriptInstance
2008-08-27 22:38:36 +00:00
{
2012-01-26 00:28:51 +00:00
private static readonly ILog m_log = LogManager . GetLogger ( MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
2012-03-14 00:29:36 +00:00
2015-01-16 22:44:54 +00:00
public bool StatePersistedHere { get { return m_AttachedAvatar = = UUID . Zero ; } }
2012-03-14 00:29:36 +00:00
/// <summary>
/// The current work item if an event for this script is running or waiting to run,
/// </summary>
/// <remarks>
2012-03-15 02:02:31 +00:00
/// Null if there is no running or waiting to run event. Must be changed only under an EventQueue lock.
2012-03-14 00:29:36 +00:00
/// </remarks>
private IScriptWorkItem m_CurrentWorkItem ;
2008-08-27 22:38:36 +00:00
private IScript m_Script ;
private DetectParams [ ] m_DetectParams ;
private bool m_TimerQueued ;
private DateTime m_EventStart ;
private bool m_InEvent ;
2014-12-10 00:25:27 +00:00
private string m_assemblyPath ;
private string m_dataPath ;
2008-08-27 22:38:36 +00:00
private string m_CurrentEvent = String . Empty ;
2009-10-29 13:42:40 +00:00
private bool m_InSelfDelete ;
2008-08-27 22:53:58 +00:00
private int m_MaxScriptQueue ;
2015-01-16 22:44:54 +00:00
private bool m_SaveState ;
2009-10-29 13:42:40 +00:00
private int m_ControlEventsInQueue ;
private int m_LastControlLevel ;
private bool m_CollisionInQueue ;
2015-02-18 14:01:13 +00:00
private bool m_StateChangeInProgress ;
2012-03-15 02:02:31 +00:00
2008-12-21 19:04:06 +00:00
// The following is for setting a minimum delay between events
2009-10-29 13:42:40 +00:00
private double m_minEventDelay ;
2017-01-05 19:07:37 +00:00
2009-10-29 13:42:40 +00:00
private long m_eventDelayTicks ;
private long m_nextEventTimeTicks ;
2009-01-28 09:52:09 +00:00
private bool m_startOnInit = true ;
2009-10-29 13:42:40 +00:00
private UUID m_AttachedAvatar ;
2009-01-28 09:52:09 +00:00
private StateSource m_stateSource ;
private bool m_postOnRez ;
2009-10-29 13:42:40 +00:00
private bool m_startedFromSavedState ;
2009-10-29 12:56:37 +00:00
private UUID m_CurrentStateHash ;
2009-10-29 13:42:40 +00:00
private UUID m_RegionID ;
2008-12-21 13:28:51 +00:00
2012-12-12 23:13:34 +00:00
public int DebugLevel { get ; set ; }
2015-08-17 19:46:30 +00:00
public WaitHandle CoopWaitHandle { get ; private set ; }
public Stopwatch ExecutionTimer { get ; private set ; }
2012-03-15 02:02:31 +00:00
public Dictionary < KeyValuePair < int , int > , KeyValuePair < int , int > > LineMap { get ; set ; }
2008-08-27 22:38:36 +00:00
private Dictionary < string , IScriptApi > m_Apis = new Dictionary < string , IScriptApi > ( ) ;
public Object [ ] PluginData = new Object [ 0 ] ;
2008-12-21 19:04:06 +00:00
/// <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 ;
2017-01-05 19:07:37 +00:00
else
2008-12-21 19:04:06 +00:00
m_minEventDelay = 0.0 ;
2012-01-26 00:28:51 +00:00
2008-12-21 19:04:06 +00:00
m_eventDelayTicks = ( long ) ( m_minEventDelay * 10000000L ) ;
m_nextEventTimeTicks = DateTime . Now . Ticks ;
}
}
2015-08-07 13:35:32 +00:00
public bool Running
{
get { return m_running ; }
set
{
m_running = value ;
if ( m_running )
StayStopped = false ;
}
}
private bool m_running ;
2008-08-27 22:38:36 +00:00
2011-10-19 20:30:37 +00:00
public bool Suspended
{
get { return m_Suspended ; }
set
{
// Need to do this inside a lock in order to avoid races with EventProcessor()
lock ( m_Script )
{
bool wasSuspended = m_Suspended ;
m_Suspended = value ;
2017-01-05 19:07:37 +00:00
2011-10-19 20:30:37 +00:00
if ( wasSuspended & & ! m_Suspended )
{
2012-03-15 02:02:31 +00:00
lock ( EventQueue )
2011-10-19 20:30:37 +00:00
{
// Need to place ourselves back in a work item if there are events to process
2012-03-15 02:02:31 +00:00
if ( EventQueue . Count > 0 & & Running & & ! ShuttingDown )
m_CurrentWorkItem = Engine . QueueEventHandler ( this ) ;
2011-10-19 20:30:37 +00:00
}
}
}
}
}
private bool m_Suspended ;
2011-10-19 19:24:07 +00:00
2012-03-15 02:02:31 +00:00
public bool ShuttingDown { get ; set ; }
2008-09-08 02:40:20 +00:00
2012-03-15 02:02:31 +00:00
public string State { get ; set ; }
2008-08-27 22:38:36 +00:00
2015-08-07 13:35:32 +00:00
public bool StayStopped { get ; set ; }
2012-03-15 02:02:31 +00:00
public IScriptEngine Engine { get ; private set ; }
2008-08-27 22:38:36 +00:00
2012-03-15 02:02:31 +00:00
public UUID AppDomain { get ; set ; }
2008-08-27 22:38:36 +00:00
2013-01-14 23:19:47 +00:00
public SceneObjectPart Part { get ; private set ; }
2012-03-15 02:02:31 +00:00
public string PrimName { get ; private set ; }
2008-08-27 22:38:36 +00:00
2012-03-15 02:02:31 +00:00
public string ScriptName { get ; private set ; }
2008-08-27 22:38:36 +00:00
2012-03-15 02:02:31 +00:00
public UUID ItemID { get ; private set ; }
2008-08-27 22:38:36 +00:00
2015-09-08 11:43:54 +00:00
public UUID ObjectID { get { return Part . UUID ; } }
2008-08-27 22:38:36 +00:00
2015-09-08 11:43:54 +00:00
public uint LocalID { get { return Part . LocalId ; } }
2008-08-27 22:38:36 +00:00
2015-09-08 11:43:54 +00:00
public UUID RootObjectID { get { return Part . ParentGroup . UUID ; } }
2012-03-16 01:31:53 +00:00
2015-09-08 11:43:54 +00:00
public uint RootLocalID { get { return Part . ParentGroup . LocalId ; } }
2012-03-16 01:31:53 +00:00
2012-03-15 02:02:31 +00:00
public UUID AssetID { get ; private set ; }
2008-08-27 22:38:36 +00:00
2012-03-15 02:02:31 +00:00
public Queue EventQueue { get ; private set ; }
2008-08-27 22:38:36 +00:00
2012-12-05 23:33:48 +00:00
public long EventsQueued
{
2017-01-05 19:07:37 +00:00
get
2012-12-05 23:33:48 +00:00
{
lock ( EventQueue )
return EventQueue . Count ;
2017-01-05 19:07:37 +00:00
}
2012-12-05 23:33:48 +00:00
}
2012-12-05 22:33:28 +00:00
public long EventsProcessed { get ; private set ; }
2012-03-15 02:02:31 +00:00
public int StartParam { get ; set ; }
2008-08-27 22:38:36 +00:00
2012-03-15 02:02:31 +00:00
public TaskInventoryItem ScriptTask { get ; private set ; }
2008-08-27 22:38:36 +00:00
2012-03-16 00:34:30 +00:00
public DateTime TimeStarted { get ; private set ; }
2015-07-27 09:16:21 +00:00
public MetricsCollectorTime ExecutionTime { get ; private set ; }
2012-03-16 00:34:30 +00:00
2015-07-27 09:16:21 +00:00
private static readonly int MeasurementWindow = 30 * 1000 ; // show the *recent* time used by the script, to find currently active scripts
2012-03-16 00:34:30 +00:00
2013-01-16 02:07:43 +00:00
private bool m_coopTermination ;
2017-01-05 19:07:37 +00:00
2013-01-16 02:07:43 +00:00
private EventWaitHandle m_coopSleepHandle ;
2012-03-15 02:02:31 +00:00
public void ClearQueue ( )
2008-12-21 13:28:51 +00:00
{
2012-03-15 02:02:31 +00:00
m_TimerQueued = false ;
2015-02-18 14:01:13 +00:00
m_StateChangeInProgress = false ;
2012-03-15 02:02:31 +00:00
EventQueue . Clear ( ) ;
2008-12-21 13:28:51 +00:00
}
2013-01-15 21:13:22 +00:00
public ScriptInstance (
IScriptEngine engine , SceneObjectPart part , TaskInventoryItem item ,
int startParam , bool postOnRez ,
int maxScriptQueue )
2008-08-27 22:38:36 +00:00
{
2012-03-15 02:02:31 +00:00
State = "default" ;
EventQueue = new Queue ( 32 ) ;
2015-08-17 19:46:30 +00:00
ExecutionTimer = new Stopwatch ( ) ;
2012-03-15 02:02:31 +00:00
Engine = engine ;
2013-01-14 23:19:47 +00:00
Part = part ;
2013-01-15 21:13:22 +00:00
ScriptTask = item ;
// This is currently only here to allow regression tests to get away without specifying any inventory
// item when they are testing script logic that doesn't require an item.
if ( ScriptTask ! = null )
{
ScriptName = ScriptTask . Name ;
ItemID = ScriptTask . ItemID ;
AssetID = ScriptTask . AssetID ;
}
PrimName = part . ParentGroup . Name ;
2012-03-15 02:02:31 +00:00
StartParam = startParam ;
2008-08-27 22:53:58 +00:00
m_MaxScriptQueue = maxScriptQueue ;
2009-01-28 09:52:09 +00:00
m_postOnRez = postOnRez ;
2011-08-26 22:06:41 +00:00
m_AttachedAvatar = part . ParentGroup . AttachedAvatar ;
2009-04-15 18:51:17 +00:00
m_RegionID = part . ParentGroup . Scene . RegionInfo . RegionID ;
2015-01-16 22:44:54 +00:00
m_SaveState = StatePersistedHere ;
2015-07-27 09:16:21 +00:00
ExecutionTime = new MetricsCollectorTime ( MeasurementWindow , 10 ) ;
2015-01-16 22:44:54 +00:00
// m_log.DebugFormat(
// "[SCRIPT INSTANCE]: Instantiated script instance {0} (id {1}) in part {2} (id {3}) in object {4} attached avatar {5} in {6}",
// ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, m_AttachedAvatar, Engine.World.Name);
2013-01-15 21:13:22 +00:00
}
2008-08-27 22:38:36 +00:00
2013-01-15 21:13:22 +00:00
/// <summary>
/// Load the script from an assembly into an AppDomain.
/// </summary>
/// <param name='dom'></param>
/// <param name='assembly'></param>
2014-12-10 00:25:27 +00:00
/// <param name='dataPath'>
/// Path for all script associated data (state, etc.). In a multi-region set up
/// with all scripts loading into the same AppDomain this may not be the same place as the DLL itself.
/// </param>
2013-01-15 21:13:22 +00:00
/// <param name='stateSource'></param>
2013-01-23 02:28:27 +00:00
/// <returns>false if load failed, true if suceeded</returns>
2015-01-26 23:16:06 +00:00
public bool Load (
2017-01-05 19:07:37 +00:00
IScript script , EventWaitHandle coopSleepHandle , string assemblyPath ,
2015-01-26 23:16:06 +00:00
string dataPath , StateSource stateSource , bool coopTermination )
2013-01-15 21:13:22 +00:00
{
2015-01-26 23:16:06 +00:00
m_Script = script ;
m_coopSleepHandle = coopSleepHandle ;
m_assemblyPath = assemblyPath ;
2014-12-10 00:25:27 +00:00
m_dataPath = dataPath ;
2013-01-15 21:13:22 +00:00
m_stateSource = stateSource ;
2015-01-26 23:16:06 +00:00
m_coopTermination = coopTermination ;
2008-08-27 22:38:36 +00:00
2015-08-17 19:46:30 +00:00
if ( m_coopTermination )
CoopWaitHandle = coopSleepHandle ;
else
CoopWaitHandle = null ;
2014-07-10 23:03:02 +00:00
ApiManager am = new ApiManager ( ) ;
foreach ( string api in am . GetApis ( ) )
{
m_Apis [ api ] = am . CreateApi ( api ) ;
2015-08-17 19:46:30 +00:00
m_Apis [ api ] . Initialize ( Engine , Part , ScriptTask ) ;
2014-07-10 23:03:02 +00:00
}
2008-08-27 22:38:36 +00:00
try
{
foreach ( KeyValuePair < string , IScriptApi > kv in m_Apis )
{
m_Script . InitApi ( kv . Key , kv . Value ) ;
}
2015-01-26 23:16:06 +00:00
// // m_log.Debug("[Script] Script instance created");
2008-08-27 22:38:36 +00:00
2014-07-10 23:03:02 +00:00
Part . SetScriptEvents ( ItemID , ( int ) m_Script . GetStateEventFlags ( State ) ) ;
2008-08-27 22:38:36 +00:00
}
2012-01-26 00:28:51 +00:00
catch ( Exception e )
2008-08-27 22:38:36 +00:00
{
2012-01-26 00:28:51 +00:00
m_log . ErrorFormat (
2013-01-23 23:34:15 +00:00
"[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}" ,
ScriptTask . Name , ScriptTask . ItemID , Part . Name , Part . UUID , Part . ParentGroup . Name , Engine . World . Name , e . Message , e . StackTrace ) ;
2012-01-26 00:28:51 +00:00
2013-01-23 02:28:27 +00:00
return false ;
2008-08-27 22:38:36 +00:00
}
2017-01-05 19:07:37 +00:00
// For attachments, XEngine saves the state into a .state file when XEngine.SetXMLState() is called.
2014-12-10 00:25:27 +00:00
string savedState = Path . Combine ( m_dataPath , ItemID . ToString ( ) + ".state" ) ;
2014-07-10 23:03:02 +00:00
2008-08-27 22:38:36 +00:00
if ( File . Exists ( savedState ) )
{
2015-01-26 23:16:06 +00:00
// m_log.DebugFormat(
2017-01-05 19:07:37 +00:00
// "[SCRIPT INSTANCE]: Found state for script {0} for {1} ({2}) at {3} in {4}",
2015-01-26 23:16:06 +00:00
// ItemID, savedState, Part.Name, Part.ParentGroup.Name, Part.ParentGroup.Scene.Name);
2014-12-10 00:25:27 +00:00
2008-08-27 22:38:36 +00:00
string xml = String . Empty ;
try
{
FileInfo fi = new FileInfo ( savedState ) ;
2009-09-14 14:14:59 +00:00
int size = ( int ) fi . Length ;
2008-08-27 22:38:36 +00:00
if ( size < 512000 )
{
using ( FileStream fs = File . Open ( savedState ,
FileMode . Open , FileAccess . Read , FileShare . None ) )
{
Byte [ ] data = new Byte [ size ] ;
fs . Read ( data , 0 , size ) ;
2012-07-11 21:54:22 +00:00
xml = Encoding . UTF8 . GetString ( data ) ;
2008-08-27 22:38:36 +00:00
ScriptSerializer . Deserialize ( xml , this ) ;
2012-03-15 02:02:31 +00:00
AsyncCommandManager . CreateFromData ( Engine ,
2015-01-26 23:16:06 +00:00
LocalID , ItemID , ObjectID ,
PluginData ) ;
2008-08-27 22:38:36 +00:00
2015-01-26 23:16:06 +00:00
// m_log.DebugFormat("[Script] Successfully retrieved state for script {0}.{1}", PrimName, m_ScriptName);
2008-08-27 22:38:36 +00:00
2013-01-14 23:19:47 +00:00
Part . SetScriptEvents ( ItemID ,
2015-01-26 23:16:06 +00:00
( int ) m_Script . GetStateEventFlags ( State ) ) ;
2008-09-08 22:19:06 +00:00
2012-07-17 13:00:42 +00:00
if ( ! Running )
2009-01-28 09:52:09 +00:00
m_startOnInit = false ;
2008-08-27 22:38:36 +00:00
2012-07-17 13:00:42 +00:00
Running = false ;
2008-08-27 22:38:36 +00:00
// we get new rez events on sim restart, too
// but if there is state, then we fire the change
// event
2008-09-07 22:01:25 +00:00
// We loaded state, don't force a re-save
m_SaveState = false ;
2009-01-28 09:52:09 +00:00
m_startedFromSavedState = true ;
2008-08-27 22:38:36 +00:00
}
2015-01-16 22:44:54 +00:00
// If this script is in an attachment then we no longer need the state file.
if ( ! StatePersistedHere )
RemoveState ( ) ;
2008-08-27 22:38:36 +00:00
}
2015-01-26 23:16:06 +00:00
// else
// {
// m_log.WarnFormat(
// "[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.",
// ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, savedState);
// }
2008-08-27 22:38:36 +00:00
}
2012-01-26 00:28:51 +00:00
catch ( Exception e )
2008-08-27 22:38:36 +00:00
{
2015-01-26 23:16:06 +00:00
m_log . ErrorFormat (
"[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}" ,
ScriptTask . Name , ScriptTask . ItemID , Part . Name , Part . UUID , Part . ParentGroup . Name , Engine . World . Name , savedState , xml , e . Message , e . StackTrace ) ;
2008-08-27 22:38:36 +00:00
}
}
2015-01-26 23:16:06 +00:00
// else
// {
// m_log.DebugFormat(
2017-01-05 19:07:37 +00:00
// "[SCRIPT INSTANCE]: Did not find state for script {0} for {1} ({2}) at {3} in {4}",
2015-01-26 23:16:06 +00:00
// ItemID, savedState, Part.Name, Part.ParentGroup.Name, Part.ParentGroup.Scene.Name);
// }
2013-01-23 02:28:27 +00:00
return true ;
2009-01-28 09:52:09 +00:00
}
public void Init ( )
{
2012-07-17 13:00:42 +00:00
if ( ShuttingDown )
2012-06-21 01:13:03 +00:00
return ;
2009-01-28 09:52:09 +00:00
2017-01-05 19:07:37 +00:00
if ( m_startedFromSavedState )
2009-01-28 09:52:09 +00:00
{
2012-07-17 13:00:42 +00:00
if ( m_startOnInit )
Start ( ) ;
2017-01-05 19:07:37 +00:00
if ( m_postOnRez )
2009-01-28 09:52:09 +00:00
{
PostEvent ( new EventParams ( "on_rez" ,
2012-03-15 02:02:31 +00:00
new Object [ ] { new LSL_Types . LSLInteger ( StartParam ) } , new DetectParams [ 0 ] ) ) ;
2009-01-28 09:52:09 +00:00
}
2009-08-04 03:07:12 +00:00
if ( m_stateSource = = StateSource . AttachedRez )
2009-08-04 01:43:08 +00:00
{
PostEvent ( new EventParams ( "attach" ,
2009-08-04 03:07:12 +00:00
new object [ ] { new LSL_Types . LSLString ( m_AttachedAvatar . ToString ( ) ) } , new DetectParams [ 0 ] ) ) ;
2009-08-04 01:43:08 +00:00
}
2010-08-06 13:33:22 +00:00
else if ( m_stateSource = = StateSource . RegionStart )
2009-01-28 09:52:09 +00:00
{
2010-11-26 21:46:48 +00:00
//m_log.Debug("[Script] Posted changed(CHANGED_REGION_RESTART) to script");
2009-01-28 09:52:09 +00:00
PostEvent ( new EventParams ( "changed" ,
2010-11-26 21:46:48 +00:00
new Object [ ] { new LSL_Types . LSLInteger ( ( int ) Changed . REGION_RESTART ) } , new DetectParams [ 0 ] ) ) ;
2009-01-28 09:52:09 +00:00
}
2010-11-26 21:46:48 +00:00
else if ( m_stateSource = = StateSource . PrimCrossing | | m_stateSource = = StateSource . Teleporting )
2009-01-28 09:52:09 +00:00
{
// CHANGED_REGION
PostEvent ( new EventParams ( "changed" ,
2010-11-26 21:46:48 +00:00
new Object [ ] { new LSL_Types . LSLInteger ( ( int ) Changed . REGION ) } , new DetectParams [ 0 ] ) ) ;
// CHANGED_TELEPORT
if ( m_stateSource = = StateSource . Teleporting )
PostEvent ( new EventParams ( "changed" ,
new Object [ ] { new LSL_Types . LSLInteger ( ( int ) Changed . TELEPORT ) } , new DetectParams [ 0 ] ) ) ;
2009-01-28 09:52:09 +00:00
}
2010-11-26 21:46:48 +00:00
}
2017-01-05 19:07:37 +00:00
else
2009-01-28 09:52:09 +00:00
{
2012-07-17 13:00:42 +00:00
if ( m_startOnInit )
Start ( ) ;
2009-01-28 09:52:09 +00:00
PostEvent ( new EventParams ( "state_entry" ,
new Object [ 0 ] , new DetectParams [ 0 ] ) ) ;
2017-01-05 19:07:37 +00:00
if ( m_postOnRez )
2009-01-28 09:52:09 +00:00
{
2008-08-27 22:38:36 +00:00
PostEvent ( new EventParams ( "on_rez" ,
2012-03-15 02:02:31 +00:00
new Object [ ] { new LSL_Types . LSLInteger ( StartParam ) } , new DetectParams [ 0 ] ) ) ;
2009-01-28 09:52:09 +00:00
}
2009-08-04 03:07:12 +00:00
if ( m_stateSource = = StateSource . AttachedRez )
2009-08-04 01:43:08 +00:00
{
PostEvent ( new EventParams ( "attach" ,
2009-08-04 03:07:12 +00:00
new object [ ] { new LSL_Types . LSLString ( m_AttachedAvatar . ToString ( ) ) } , new DetectParams [ 0 ] ) ) ;
2009-08-04 01:43:08 +00:00
}
2008-08-27 22:38:36 +00:00
}
}
2008-09-17 22:00:56 +00:00
private void ReleaseControls ( )
{
2012-03-15 02:02:31 +00:00
SceneObjectPart part = Engine . World . GetSceneObjectPart ( LocalID ) ;
2017-01-05 19:07:37 +00:00
2009-02-20 14:04:29 +00:00
if ( part ! = null )
2008-09-17 22:00:56 +00:00
{
2009-02-20 14:04:29 +00:00
int permsMask ;
UUID permsGranter ;
2009-11-24 17:02:12 +00:00
part . TaskInventory . LockItemsForRead ( true ) ;
2012-03-15 10:55:44 +00:00
if ( ! part . TaskInventory . ContainsKey ( ItemID ) )
2009-02-20 14:04:29 +00:00
{
2009-11-24 17:02:12 +00:00
part . TaskInventory . LockItemsForRead ( false ) ;
2013-01-14 23:19:47 +00:00
return ;
2009-09-30 16:00:09 +00:00
}
2012-03-15 10:55:44 +00:00
permsGranter = part . TaskInventory [ ItemID ] . PermsGranter ;
permsMask = part . TaskInventory [ ItemID ] . PermsMask ;
2009-11-24 17:02:12 +00:00
part . TaskInventory . LockItemsForRead ( false ) ;
2009-02-20 14:04:29 +00:00
2008-09-17 22:00:56 +00:00
if ( ( permsMask & ScriptBaseClass . PERMISSION_TAKE_CONTROLS ) ! = 0 )
{
2012-03-15 02:02:31 +00:00
ScenePresence presence = Engine . World . GetScenePresence ( permsGranter ) ;
2008-09-17 22:00:56 +00:00
if ( presence ! = null )
2012-03-15 02:02:31 +00:00
presence . UnRegisterControlEventsToScript ( LocalID , ItemID ) ;
2008-09-17 22:00:56 +00:00
}
}
}
public void DestroyScriptInstance ( )
{
ReleaseControls ( ) ;
2012-03-15 02:02:31 +00:00
AsyncCommandManager . RemoveScript ( Engine , LocalID , ItemID ) ;
2008-09-17 22:00:56 +00:00
}
2008-08-27 22:38:36 +00:00
public void RemoveState ( )
{
2014-12-10 00:25:27 +00:00
string savedState = Path . Combine ( m_dataPath , ItemID . ToString ( ) + ".state" ) ;
// m_log.DebugFormat(
// "[SCRIPT INSTANCE]: Deleting state {0} for script {1} (id {2}) in part {3} (id {4}) in object {5} in {6}.",
// savedState, ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name);
2008-08-27 22:38:36 +00:00
try
{
File . Delete ( savedState ) ;
}
2013-04-09 22:02:11 +00:00
catch ( Exception e )
2008-08-27 22:38:36 +00:00
{
2013-04-09 22:02:11 +00:00
m_log . Warn (
string . Format (
2017-01-05 19:07:37 +00:00
"[SCRIPT INSTANCE]: Could not delete script state {0} for script {1} (id {2}) in part {3} (id {4}) in object {5} in {6}. Exception " ,
savedState , ScriptTask . Name , ScriptTask . ItemID , Part . Name , Part . UUID , Part . ParentGroup . Name , Engine . World . Name ) ,
2013-04-09 22:02:11 +00:00
e ) ;
2008-08-27 22:38:36 +00:00
}
}
public void VarDump ( Dictionary < string , object > vars )
{
2012-03-15 02:02:31 +00:00
// m_log.Info("Variable dump for script "+ ItemID.ToString());
2009-05-22 14:57:00 +00:00
// foreach (KeyValuePair<string, object> v in vars)
// {
2009-05-18 15:32:06 +00:00
// m_log.Info("Variable: "+v.Key+" = "+v.Value.ToString());
2009-05-22 14:57:00 +00:00
// }
2008-08-27 22:38:36 +00:00
}
public void Start ( )
{
2012-03-15 02:02:31 +00:00
lock ( EventQueue )
2008-08-27 22:38:36 +00:00
{
if ( Running )
return ;
2012-03-15 00:48:19 +00:00
Running = true ;
2008-08-27 22:38:36 +00:00
2012-03-16 00:34:30 +00:00
TimeStarted = DateTime . Now ;
2015-07-27 09:16:21 +00:00
// Note: we don't reset ExecutionTime. The reason is that runaway scripts are stopped and restarted
// automatically, and we *do* want to show that they had high CPU in that case. If we had reset
// ExecutionTime here then runaway scripts, paradoxically, would never show up in the "Top Scripts" dialog.
2012-03-16 00:34:30 +00:00
2012-03-15 02:02:31 +00:00
if ( EventQueue . Count > 0 )
2008-08-27 22:38:36 +00:00
{
2012-03-14 00:29:36 +00:00
if ( m_CurrentWorkItem = = null )
2012-03-15 02:02:31 +00:00
m_CurrentWorkItem = Engine . QueueEventHandler ( this ) ;
2009-05-18 15:32:06 +00:00
// else
// m_log.Error("[Script] Tried to start a script that was already queued");
2008-08-27 22:38:36 +00:00
}
}
}
2015-01-16 23:55:00 +00:00
public bool Stop ( int timeout , bool clearEventQueue = false )
2008-08-27 22:38:36 +00:00
{
2013-05-01 18:01:43 +00:00
if ( DebugLevel > = 1 )
m_log . DebugFormat (
"[SCRIPT INSTANCE]: Stopping script {0} {1} in {2} {3} with timeout {4} {5} {6}" ,
ScriptName , ItemID , PrimName , ObjectID , timeout , m_InSelfDelete , DateTime . Now . Ticks ) ;
2012-02-09 00:10:45 +00:00
2012-03-14 00:29:36 +00:00
IScriptWorkItem workItem ;
2008-08-27 22:38:36 +00:00
2012-03-15 02:02:31 +00:00
lock ( EventQueue )
2008-08-27 22:38:36 +00:00
{
2015-01-16 23:55:00 +00:00
if ( clearEventQueue )
ClearQueue ( ) ;
2008-08-27 22:38:36 +00:00
if ( ! Running )
return true ;
2012-03-14 00:29:36 +00:00
// If we're not running or waiting to run an event then we can safely stop.
if ( m_CurrentWorkItem = = null )
2008-08-27 22:38:36 +00:00
{
2012-03-15 00:48:19 +00:00
Running = false ;
2008-08-27 22:38:36 +00:00
return true ;
}
2012-03-14 00:29:36 +00:00
// If we are waiting to run an event then we can try to cancel it.
if ( m_CurrentWorkItem . Cancel ( ) )
2008-08-27 22:38:36 +00:00
{
2012-03-14 00:29:36 +00:00
m_CurrentWorkItem = null ;
2012-03-15 00:48:19 +00:00
Running = false ;
2008-08-27 22:38:36 +00:00
return true ;
}
2012-03-14 00:29:36 +00:00
workItem = m_CurrentWorkItem ;
2012-03-15 00:48:19 +00:00
Running = false ;
2008-08-27 22:38:36 +00:00
}
2012-03-14 00:29:36 +00:00
// Wait for the current event to complete.
2013-01-16 02:07:43 +00:00
if ( ! m_InSelfDelete )
2008-08-27 22:38:36 +00:00
{
2013-01-16 02:07:43 +00:00
if ( ! m_coopTermination )
{
// If we're not co-operative terminating then try and wait for the event to complete before stopping
2013-02-12 21:34:12 +00:00
if ( workItem . Wait ( timeout ) )
2013-01-16 02:07:43 +00:00
return true ;
}
else
{
2013-01-23 23:34:15 +00:00
if ( DebugLevel > = 1 )
m_log . DebugFormat (
"[SCRIPT INSTANCE]: Co-operatively stopping script {0} {1} in {2} {3}" ,
ScriptName , ItemID , PrimName , ObjectID ) ;
2013-01-16 02:07:43 +00:00
// This will terminate the event on next handle check by the script.
m_coopSleepHandle . Set ( ) ;
// For now, we will wait forever since the event should always cleanly terminate once LSL loop
// checking is implemented. May want to allow a shorter timeout option later.
2013-02-12 21:34:12 +00:00
if ( workItem . Wait ( Timeout . Infinite ) )
2013-01-16 02:07:43 +00:00
{
2013-01-23 23:34:15 +00:00
if ( DebugLevel > = 1 )
m_log . DebugFormat (
"[SCRIPT INSTANCE]: Co-operatively stopped script {0} {1} in {2} {3}" ,
ScriptName , ItemID , PrimName , ObjectID ) ;
2013-01-16 02:07:43 +00:00
return true ;
}
}
2008-08-27 22:38:36 +00:00
}
2012-03-15 02:02:31 +00:00
lock ( EventQueue )
2008-08-27 22:38:36 +00:00
{
2012-03-14 00:29:36 +00:00
workItem = m_CurrentWorkItem ;
2008-08-27 22:38:36 +00:00
}
2012-03-14 00:29:36 +00:00
if ( workItem = = null )
2008-08-27 22:38:36 +00:00
return true ;
2012-03-14 00:29:36 +00:00
// If the event still hasn't stopped and we the stop isn't the result of script or object removal, then
// forcibly abort the work item (this aborts the underlying thread).
2013-01-16 02:07:43 +00:00
// Co-operative termination should never reach this point.
2008-08-27 22:38:36 +00:00
if ( ! m_InSelfDelete )
2012-03-12 21:16:05 +00:00
{
2012-12-12 23:30:26 +00:00
m_log . DebugFormat (
2017-01-05 19:07:37 +00:00
"[SCRIPT INSTANCE]: Aborting unstopped script {0} {1} in prim {2}, localID {3}, timeout was {4} ms" ,
2012-12-12 23:30:26 +00:00
ScriptName , ItemID , PrimName , LocalID , timeout ) ;
2012-03-14 00:29:36 +00:00
workItem . Abort ( ) ;
2012-03-12 21:16:05 +00:00
}
2008-08-27 22:38:36 +00:00
2012-03-15 02:02:31 +00:00
lock ( EventQueue )
2008-08-27 22:38:36 +00:00
{
2012-03-14 00:29:36 +00:00
m_CurrentWorkItem = null ;
2008-08-27 22:38:36 +00:00
}
return true ;
}
2009-11-23 15:02:05 +00:00
[DebuggerNonUserCode] //Prevents the debugger from farting in this function
2008-08-27 22:38:36 +00:00
public void SetState ( string state )
{
2008-09-09 01:24:58 +00:00
if ( state = = State )
return ;
2015-03-12 00:07:01 +00:00
2015-02-18 14:01:13 +00:00
EventParams lastTimerEv = null ;
2015-03-12 00:07:01 +00:00
lock ( EventQueue )
2015-02-18 14:01:13 +00:00
{
2015-03-12 00:07:01 +00:00
// Remove all queued events, remembering the last timer event
while ( EventQueue . Count > 0 )
{
EventParams tempv = ( EventParams ) EventQueue . Dequeue ( ) ;
if ( tempv . EventName = = "timer" ) lastTimerEv = tempv ;
}
// Post events
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 ] ) ) ;
// Requeue the timer event after the state changing events
if ( lastTimerEv ! = null ) EventQueue . Enqueue ( lastTimerEv ) ;
// This will stop events from being queued and processed
// until the new state is started
m_StateChangeInProgress = true ;
2015-02-18 14:01:13 +00:00
}
2008-09-26 02:51:00 +00:00
throw new EventAbortException ( ) ;
2008-08-27 22:38:36 +00:00
}
2012-03-14 00:29:36 +00:00
/// <summary>
/// Post an event to this script instance.
/// </summary>
/// <remarks>
/// The request to run the event is sent
/// </remarks>
/// <param name="data"></param>
2008-08-27 22:38:36 +00:00
public void PostEvent ( EventParams data )
{
2009-02-22 01:26:18 +00:00
// m_log.DebugFormat("[Script] Posted event {2} in state {3} to {0}.{1}",
2012-03-15 02:02:31 +00:00
// PrimName, ScriptName, data.EventName, State);
2008-08-27 22:38:36 +00:00
if ( ! Running )
return ;
2008-12-21 19:04:06 +00:00
// 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.
2019-02-20 04:54:32 +00:00
if ( m_eventDelayTicks ! = 0 & &
data . EventName ! = "state" & & data . EventName ! = "state_entry" & & data . EventName ! = "state_exit"
& & data . EventName ! = "run_time_permissions" & & data . EventName ! = "http_request" & & data . EventName ! = "link_message" )
2008-12-21 19:04:06 +00:00
{
if ( DateTime . Now . Ticks < m_nextEventTimeTicks )
return ;
m_nextEventTimeTicks = DateTime . Now . Ticks + m_eventDelayTicks ;
}
2012-03-15 02:02:31 +00:00
lock ( EventQueue )
2008-08-27 22:38:36 +00:00
{
2015-03-12 00:14:30 +00:00
// The only events that persist across state changes are timers
2017-01-05 19:07:37 +00:00
if ( m_StateChangeInProgress & & data . EventName ! = "timer" )
2015-03-12 00:14:30 +00:00
return ;
2012-03-15 02:02:31 +00:00
if ( EventQueue . Count > = m_MaxScriptQueue )
2008-08-27 22:38:36 +00:00
return ;
if ( data . EventName = = "timer" )
{
if ( m_TimerQueued )
return ;
m_TimerQueued = true ;
}
2008-09-17 22:00:56 +00:00
if ( data . EventName = = "control" )
{
int held = ( ( LSL_Types . LSLInteger ) data . Params [ 1 ] ) . value ;
2008-09-18 16:53:26 +00:00
// int changed = ((LSL_Types.LSLInteger)data.Params[2]).value;
2008-09-17 22:00:56 +00:00
// 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 + + ;
}
2008-09-18 18:50:39 +00:00
if ( data . EventName = = "collision" )
{
if ( m_CollisionInQueue )
return ;
if ( data . DetectParams = = null )
return ;
m_CollisionInQueue = true ;
}
2012-03-15 02:02:31 +00:00
EventQueue . Enqueue ( data ) ;
2008-08-27 22:38:36 +00:00
2012-03-14 00:29:36 +00:00
if ( m_CurrentWorkItem = = null )
2008-08-27 22:38:36 +00:00
{
2012-03-15 02:02:31 +00:00
m_CurrentWorkItem = Engine . QueueEventHandler ( this ) ;
2008-08-27 22:38:36 +00:00
}
}
}
2008-09-16 16:37:16 +00:00
/// <summary>
/// Process the next event queued for this script
/// </summary>
2009-09-30 16:00:09 +00:00
/// <returns></returns>
2008-08-27 22:38:36 +00:00
public object EventProcessor ( )
{
2012-03-15 00:06:52 +00:00
// We check here as the thread stopping this instance from running may itself hold the m_Script lock.
if ( ! Running )
return 0 ;
2015-09-02 18:54:53 +00:00
lock ( m_Script )
{
2011-10-19 20:30:37 +00:00
// m_log.DebugFormat("[XEngine]: EventProcessor() invoked for {0}.{1}", PrimName, ScriptName);
if ( Suspended )
return 0 ;
2015-08-17 19:46:30 +00:00
ExecutionTimer . Restart ( ) ;
2017-01-05 19:07:37 +00:00
2015-07-27 09:34:09 +00:00
try
{
return EventProcessorInt ( ) ;
}
finally
{
2015-08-17 19:46:30 +00:00
ExecutionTimer . Stop ( ) ;
ExecutionTime . AddSample ( ExecutionTimer ) ;
Part . ParentGroup . Scene . AddScriptExecutionTime ( ExecutionTimer . ElapsedTicks ) ;
2015-07-27 09:34:09 +00:00
}
}
}
2008-09-18 18:50:39 +00:00
2015-07-27 09:34:09 +00:00
private object EventProcessorInt ( )
{
EventParams data = null ;
lock ( EventQueue )
{
data = ( EventParams ) EventQueue . Dequeue ( ) ;
2016-08-19 13:17:57 +00:00
if ( data = = null )
2008-09-17 22:00:56 +00:00
{
2016-08-19 13:17:57 +00:00
// check if a null event was enqueued or if its really empty
if ( EventQueue . Count > 0 & & Running & & ! ShuttingDown & & ! m_InSelfDelete )
2008-09-18 18:50:39 +00:00
{
2015-07-27 09:34:09 +00:00
m_CurrentWorkItem = Engine . QueueEventHandler ( this ) ;
2008-09-18 18:50:39 +00:00
}
2015-07-27 09:34:09 +00:00
else
2008-09-18 18:50:39 +00:00
{
2015-07-27 09:34:09 +00:00
m_CurrentWorkItem = null ;
2008-09-18 18:50:39 +00:00
}
2015-07-27 09:34:09 +00:00
return 0 ;
2008-09-17 22:00:56 +00:00
}
2012-12-12 23:13:34 +00:00
2015-07-27 09:34:09 +00:00
if ( data . EventName = = "timer" )
m_TimerQueued = false ;
if ( data . EventName = = "control" )
{
if ( m_ControlEventsInQueue > 0 )
m_ControlEventsInQueue - - ;
}
if ( data . EventName = = "collision" )
m_CollisionInQueue = false ;
}
if ( DebugLevel > = 2 )
m_log . DebugFormat (
"[SCRIPT INSTANCE]: Processing event {0} for {1}/{2}({3})/{4}({5}) @ {6}/{7}" ,
data . EventName ,
ScriptName ,
Part . Name ,
Part . LocalId ,
Part . ParentGroup . Name ,
Part . ParentGroup . UUID ,
Part . AbsolutePosition ,
Part . ParentGroup . Scene . Name ) ;
m_DetectParams = data . DetectParams ;
if ( data . EventName = = "state" ) // Hardcoded state change
{
State = data . Params [ 0 ] . ToString ( ) ;
if ( DebugLevel > = 1 )
2012-12-12 23:13:34 +00:00
m_log . DebugFormat (
2015-07-27 09:34:09 +00:00
"[SCRIPT INSTANCE]: Changing state to {0} for {1}/{2}({3})/{4}({5}) @ {6}/{7}" ,
State ,
ScriptName ,
Part . Name ,
Part . LocalId ,
Part . ParentGroup . Name ,
Part . ParentGroup . UUID ,
Part . AbsolutePosition ,
2013-01-14 23:19:47 +00:00
Part . ParentGroup . Scene . Name ) ;
2015-07-27 09:34:09 +00:00
AsyncCommandManager . StateChange ( Engine ,
LocalID , ItemID ) ;
// we are effectively in the new state now, so we can resume queueing
// and processing other non-timer events
m_StateChangeInProgress = false ;
2008-08-27 22:38:36 +00:00
2015-07-27 09:34:09 +00:00
Part . SetScriptEvents ( ItemID , ( int ) m_Script . GetStateEventFlags ( State ) ) ;
}
else
{
2016-08-19 15:14:08 +00:00
Exception e = null ;
2015-07-27 09:34:09 +00:00
if ( Engine . World . PipeEventsForScript ( LocalID ) | |
data . EventName = = "control" ) // Don't freeze avies!
2008-08-27 22:38:36 +00:00
{
2015-07-27 09:34:09 +00:00
// m_log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}",
// PrimName, ScriptName, data.EventName, State);
2012-12-12 23:13:34 +00:00
2015-07-27 09:34:09 +00:00
try
2008-09-18 18:50:39 +00:00
{
2015-07-27 09:34:09 +00:00
m_CurrentEvent = data . EventName ;
2016-11-22 05:18:06 +00:00
m_EventStart = DateTime . UtcNow ;
2015-07-27 09:34:09 +00:00
m_InEvent = true ;
2008-08-27 22:38:36 +00:00
2008-09-25 17:26:32 +00:00
try
{
m_Script . ExecuteEvent ( State , data . EventName , data . Params ) ;
2015-07-27 09:34:09 +00:00
}
finally
{
2008-09-25 17:26:32 +00:00
m_InEvent = false ;
m_CurrentEvent = String . Empty ;
2016-08-21 21:44:07 +00:00
lock ( EventQueue )
m_CurrentWorkItem = null ; // no longer in a event that can be canceled
2015-07-27 09:34:09 +00:00
}
2008-09-07 22:01:25 +00:00
2015-07-27 09:34:09 +00:00
if ( m_SaveState )
{
// This will be the very first event we deliver
// (state_entry) in default state
/ /
SaveState ( ) ;
2008-09-18 18:50:39 +00:00
2015-07-27 09:34:09 +00:00
m_SaveState = false ;
2008-09-25 17:26:32 +00:00
}
2015-07-27 09:34:09 +00:00
}
2016-08-19 15:14:08 +00:00
catch ( Exception exx )
{
e = exx ;
}
if ( e ! = null )
2015-07-27 09:34:09 +00:00
{
// m_log.DebugFormat(
// "[SCRIPT] Exception in script {0} {1}: {2}{3}",
// ScriptName, ItemID, e.Message, e.StackTrace);
if ( ( ! ( e is TargetInvocationException )
| | ( ! ( e . InnerException is SelfDeleteException )
& & ! ( e . InnerException is ScriptDeleteException )
& & ! ( e . InnerException is ScriptCoopStopException ) ) )
& & ! ( e is ThreadAbortException ) )
2008-08-27 22:38:36 +00:00
{
2015-07-27 09:34:09 +00:00
try
2009-04-12 12:03:07 +00:00
{
2016-11-08 13:39:49 +00:00
if ( e . InnerException ! = null & & e . InnerException is ScriptException )
{
2017-01-05 19:07:37 +00:00
string text = e . InnerException . Message +
2016-11-08 13:39:49 +00:00
"(script: " + ScriptName +
" event: " + data . EventName +
2018-11-26 19:57:42 +00:00
" primID:" + Part . UUID . ToString ( ) +
2016-11-08 13:39:49 +00:00
" at " + Part . AbsolutePosition + ")" ;
if ( text . Length > 1000 )
text = text . Substring ( 0 , 1000 ) ;
Engine . World . SimChat ( Utils . StringToBytes ( text ) ,
ChatTypeEnum . DebugChannel , 2147483647 ,
Part . AbsolutePosition ,
Part . Name , Part . UUID , false ) ;
m_log . Debug ( string . Format (
2018-11-26 20:20:28 +00:00
"[SCRIPT ERROR]: {0} (at event {1}, part {2} {3} at {4} in {5}" ,
2016-11-08 13:39:49 +00:00
e . InnerException . Message ,
data . EventName ,
PrimName ,
Part . UUID ,
Part . AbsolutePosition ,
Part . ParentGroup . Scene . Name ) ) ;
2017-01-05 19:07:37 +00:00
}
2016-11-08 13:39:49 +00:00
else
2017-01-05 19:07:37 +00:00
{
2016-11-08 13:39:49 +00:00
// DISPLAY ERROR INWORLD
string text = FormatException ( e ) ;
if ( text . Length > 1000 )
text = text . Substring ( 0 , 1000 ) ;
Engine . World . SimChat ( Utils . StringToBytes ( text ) ,
ChatTypeEnum . DebugChannel , 2147483647 ,
Part . AbsolutePosition ,
Part . Name , Part . UUID , false ) ;
m_log . Debug ( string . Format (
2018-11-26 20:20:28 +00:00
"[SCRIPT ERROR]: Runtime error in script {0} (event {1}), part {2} {3} at {4} in {5} " ,
2016-11-08 13:39:49 +00:00
ScriptName ,
data . EventName ,
PrimName ,
Part . UUID ,
Part . AbsolutePosition ,
Part . ParentGroup . Scene . Name ) ,
e ) ;
}
2009-04-12 12:03:07 +00:00
}
2015-07-27 09:34:09 +00:00
catch ( Exception )
2013-01-16 02:07:43 +00:00
{
}
2015-07-27 09:34:09 +00:00
}
else if ( ( e is TargetInvocationException ) & & ( e . InnerException is SelfDeleteException ) )
{
m_InSelfDelete = true ;
Engine . World . DeleteSceneObject ( Part . ParentGroup , false ) ;
}
else if ( ( e is TargetInvocationException ) & & ( e . InnerException is ScriptDeleteException ) )
{
m_InSelfDelete = true ;
Part . Inventory . RemoveInventoryItem ( ItemID ) ;
}
else if ( ( e is TargetInvocationException ) & & ( e . InnerException is ScriptCoopStopException ) )
{
if ( DebugLevel > = 1 )
m_log . DebugFormat (
"[SCRIPT INSTANCE]: Script {0}.{1} in event {2}, state {3} stopped co-operatively." ,
PrimName , ScriptName , data . EventName , State ) ;
2008-08-27 22:38:36 +00:00
}
}
}
2015-07-27 09:34:09 +00:00
}
2008-08-27 22:38:36 +00:00
2015-07-27 09:34:09 +00:00
// If there are more events and we are currently running and not shutting down, then ask the
// script engine to run the next event.
lock ( EventQueue )
{
2015-08-17 16:05:39 +00:00
// Increase processed events counter and prevent wrap;
if ( + + EventsProcessed = = 1000000 )
EventsProcessed = 100000 ;
2012-12-05 22:33:28 +00:00
2015-08-17 16:05:39 +00:00
if ( ( EventsProcessed % 100000 ) = = 0 & & DebugLevel > 0 )
2015-07-27 09:34:09 +00:00
{
m_log . DebugFormat ( "[SCRIPT INSTANCE]: Script \"{0}\" (Object \"{1}\" {2} @ {3}.{4}, Item ID {5}, Asset {6}) in event {7}: processed {8:n0} script events" ,
ScriptTask . Name ,
Part . ParentGroup . Name , Part . ParentGroup . UUID , Part . ParentGroup . AbsolutePosition , Part . ParentGroup . Scene . Name ,
ScriptTask . ItemID , ScriptTask . AssetID , data . EventName , EventsProcessed ) ;
}
2015-07-17 13:27:40 +00:00
2016-08-19 13:17:57 +00:00
if ( EventQueue . Count > 0 & & Running & & ! ShuttingDown & & ! m_InSelfDelete )
2015-07-27 09:34:09 +00:00
{
m_CurrentWorkItem = Engine . QueueEventHandler ( this ) ;
2008-08-27 22:38:36 +00:00
}
2015-07-27 09:34:09 +00:00
else
{
m_CurrentWorkItem = null ;
}
}
2008-08-27 22:38:36 +00:00
2015-07-27 09:34:09 +00:00
m_DetectParams = null ;
2008-08-27 22:38:36 +00:00
2015-07-27 09:34:09 +00:00
return 0 ;
2008-08-27 22:38:36 +00:00
}
public int EventTime ( )
{
if ( ! m_InEvent )
return 0 ;
2016-11-22 05:18:06 +00:00
return ( DateTime . UtcNow - m_EventStart ) . Seconds ;
2008-08-27 22:38:36 +00:00
}
2012-12-17 21:37:02 +00:00
public void ResetScript ( int timeout )
2008-08-27 22:38:36 +00:00
{
2008-09-03 11:58:36 +00:00
if ( m_Script = = null )
2008-09-03 10:53:25 +00:00
return ;
2008-09-03 11:58:36 +00:00
2008-08-27 22:38:36 +00:00
bool running = Running ;
RemoveState ( ) ;
2008-09-17 22:00:56 +00:00
ReleaseControls ( ) ;
2008-08-27 22:38:36 +00:00
2012-12-17 21:37:02 +00:00
Stop ( timeout ) ;
2012-03-15 02:02:31 +00:00
SceneObjectPart part = Engine . World . GetSceneObjectPart ( LocalID ) ;
part . Inventory . GetInventoryItem ( ItemID ) . PermsMask = 0 ;
part . Inventory . GetInventoryItem ( ItemID ) . PermsGranter = UUID . Zero ;
2012-05-16 11:27:49 +00:00
part . CollisionSound = UUID . Zero ;
2012-03-15 02:02:31 +00:00
AsyncCommandManager . RemoveScript ( Engine , LocalID , ItemID ) ;
2015-10-01 04:20:04 +00:00
m_TimerQueued = false ;
m_StateChangeInProgress = false ;
2012-03-15 02:02:31 +00:00
EventQueue . Clear ( ) ;
2015-10-01 04:20:04 +00:00
2008-08-27 22:38:36 +00:00
m_Script . ResetVars ( ) ;
2015-03-02 23:31:54 +00:00
StartParam = 0 ;
2012-03-15 02:02:31 +00:00
State = "default" ;
2008-09-08 22:19:06 +00:00
2015-10-01 04:20:04 +00:00
2012-03-15 02:02:31 +00:00
part . SetScriptEvents ( ItemID ,
2008-09-08 22:19:06 +00:00
( int ) m_Script . GetStateEventFlags ( State ) ) ;
2008-08-27 22:38:36 +00:00
if ( running )
Start ( ) ;
2015-01-16 22:44:54 +00:00
m_SaveState = StatePersistedHere ;
2008-08-27 22:38:36 +00:00
PostEvent ( new EventParams ( "state_entry" ,
new Object [ 0 ] , new DetectParams [ 0 ] ) ) ;
}
2009-11-23 15:02:05 +00:00
[DebuggerNonUserCode] //Stops the VS debugger from farting in this function
2008-08-27 22:38:36 +00:00
public void ApiResetScript ( )
{
// bool running = Running;
RemoveState ( ) ;
2008-09-17 22:00:56 +00:00
ReleaseControls ( ) ;
2008-08-27 22:38:36 +00:00
m_Script . ResetVars ( ) ;
2012-03-15 02:02:31 +00:00
SceneObjectPart part = Engine . World . GetSceneObjectPart ( LocalID ) ;
part . Inventory . GetInventoryItem ( ItemID ) . PermsMask = 0 ;
part . Inventory . GetInventoryItem ( ItemID ) . PermsGranter = UUID . Zero ;
2012-05-16 11:27:49 +00:00
part . CollisionSound = UUID . Zero ;
2012-03-15 02:02:31 +00:00
AsyncCommandManager . RemoveScript ( Engine , LocalID , ItemID ) ;
2008-09-08 22:19:06 +00:00
2015-10-01 04:20:04 +00:00
m_TimerQueued = false ;
m_StateChangeInProgress = false ;
2012-03-15 02:02:31 +00:00
EventQueue . Clear ( ) ;
2008-09-08 22:19:06 +00:00
m_Script . ResetVars ( ) ;
2015-03-02 19:59:02 +00:00
string oldState = State ;
2015-03-02 23:31:54 +00:00
StartParam = 0 ;
2012-03-15 02:02:31 +00:00
State = "default" ;
2008-09-08 22:19:06 +00:00
2012-03-15 02:02:31 +00:00
part . SetScriptEvents ( ItemID ,
2008-09-08 22:19:06 +00:00
( int ) m_Script . GetStateEventFlags ( State ) ) ;
2015-03-02 19:59:02 +00:00
if ( m_CurrentEvent ! = "state_entry" | | oldState ! = "default" )
2008-08-27 22:38:36 +00:00
{
2015-01-16 22:44:54 +00:00
m_SaveState = StatePersistedHere ;
2008-08-27 22:38:36 +00:00
PostEvent ( new EventParams ( "state_entry" ,
new Object [ 0 ] , new DetectParams [ 0 ] ) ) ;
2008-09-26 02:51:00 +00:00
throw new EventAbortException ( ) ;
2008-08-27 22:38:36 +00:00
}
}
public Dictionary < string , object > GetVars ( )
{
2015-09-08 11:43:54 +00:00
if ( m_Script ! = null )
return m_Script . GetVars ( ) ;
else
return new Dictionary < string , object > ( ) ;
2008-08-27 22:38:36 +00:00
}
public void SetVars ( Dictionary < string , object > vars )
{
2015-01-22 23:12:10 +00:00
// foreach (KeyValuePair<string, object> kvp in vars)
// m_log.DebugFormat("[SCRIPT INSTANCE]: Setting var {0}={1}", kvp.Key, kvp.Value);
2008-08-27 22:38:36 +00:00
m_Script . SetVars ( vars ) ;
}
public DetectParams GetDetectParams ( int idx )
{
2008-09-18 18:50:39 +00:00
if ( m_DetectParams = = null )
return null ;
2008-08-27 22:38:36 +00:00
if ( idx < 0 | | idx > = m_DetectParams . Length )
return null ;
return m_DetectParams [ idx ] ;
}
2008-09-06 07:52:41 +00:00
public UUID GetDetectID ( int idx )
2008-08-27 22:38:36 +00:00
{
2008-09-18 18:50:39 +00:00
if ( m_DetectParams = = null )
return UUID . Zero ;
2008-08-27 22:38:36 +00:00
if ( idx < 0 | | idx > = m_DetectParams . Length )
2008-09-06 07:52:41 +00:00
return UUID . Zero ;
2008-08-27 22:38:36 +00:00
return m_DetectParams [ idx ] . Key ;
}
2014-12-10 00:25:27 +00:00
public void SaveState ( )
2008-08-27 22:38:36 +00:00
{
2015-08-07 13:35:32 +00:00
if ( ! Running & & ! StayStopped )
2015-01-29 17:55:08 +00:00
return ;
// We cannot call this inside the EventQueue lock since it will currently take AsyncCommandManager.staticLock.
// This may already be held by AsyncCommandManager.DoOneCmdHandlerPass() which in turn can take EventQueue
// lock via ScriptInstance.PostEvent().
PluginData = AsyncCommandManager . GetSerializationData ( Engine , ItemID ) ;
2015-01-16 23:55:00 +00:00
// We need to lock here to avoid any race with a thread that is removing this script.
lock ( EventQueue )
2008-09-07 22:01:25 +00:00
{
2015-01-29 17:55:08 +00:00
// Check again to avoid a race with a thread in Stop()
2015-08-07 13:35:32 +00:00
if ( ! Running & & ! StayStopped )
2015-01-16 23:55:00 +00:00
return ;
2008-09-07 22:01:25 +00:00
2015-01-16 23:55:00 +00:00
// If we're currently in an event, just tell it to save upon return
/ /
if ( m_InEvent )
{
m_SaveState = true ;
return ;
}
2015-01-16 22:44:54 +00:00
2015-01-16 23:55:00 +00:00
// m_log.DebugFormat(
// "[SCRIPT INSTANCE]: Saving state for script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}",
// ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name);
2008-08-27 22:38:36 +00:00
2015-01-16 23:55:00 +00:00
string xml = ScriptSerializer . Serialize ( this ) ;
2009-10-29 12:56:37 +00:00
2015-01-16 23:55:00 +00:00
// Compare hash of the state we just just created with the state last written to disk
// If the state is different, update the disk file.
UUID hash = UUID . Parse ( Utils . MD5String ( xml ) ) ;
if ( hash ! = m_CurrentStateHash )
2009-03-26 14:28:00 +00:00
{
2015-01-16 23:55:00 +00:00
try
2014-12-03 18:58:55 +00:00
{
2015-01-16 23:55:00 +00:00
using ( FileStream fs = File . Create ( Path . Combine ( m_dataPath , ItemID . ToString ( ) + ".state" ) ) )
{
Byte [ ] buf = Util . UTF8NoBomEncoding . GetBytes ( xml ) ;
fs . Write ( buf , 0 , buf . Length ) ;
}
2014-12-03 18:58:55 +00:00
}
2015-01-16 23:55:00 +00:00
catch ( Exception )
{
// m_log.Error("Unable to save xml\n"+e.ToString());
}
//if (!File.Exists(Path.Combine(Path.GetDirectoryName(assembly), ItemID.ToString() + ".state")))
//{
// throw new Exception("Completed persistence save, but no file was created");
//}
m_CurrentStateHash = hash ;
2009-03-26 14:28:00 +00:00
}
2015-08-07 13:35:32 +00:00
StayStopped = false ;
2008-08-27 22:38:36 +00:00
}
}
2008-09-13 14:40:42 +00:00
public IScriptApi GetApi ( string name )
{
if ( m_Apis . ContainsKey ( name ) )
2012-04-23 23:03:57 +00:00
{
// m_log.DebugFormat("[SCRIPT INSTANCE]: Found api {0} in {1}@{2}", name, ScriptName, PrimName);
2008-09-13 14:40:42 +00:00
return m_Apis [ name ] ;
2012-04-23 23:03:57 +00:00
}
// m_log.DebugFormat("[SCRIPT INSTANCE]: Did not find api {0} in {1}@{2}", name, ScriptName, PrimName);
2008-09-13 14:40:42 +00:00
return null ;
}
2017-01-05 19:07:37 +00:00
2008-09-16 16:37:16 +00:00
public override string ToString ( )
{
2012-03-15 02:02:31 +00:00
return String . Format ( "{0} {1} on {2}" , ScriptName , ItemID , PrimName ) ;
2008-09-16 16:37:16 +00:00
}
2008-09-27 05:31:43 +00:00
string FormatException ( Exception e )
{
2008-09-29 10:30:01 +00:00
if ( e . InnerException = = null ) // Not a normal runtime error
return e . ToString ( ) ;
2008-09-27 05:31:43 +00:00
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" +
2009-12-22 09:24:01 +00:00
"({0}): {1}" , scriptLine - 1 ,
2008-09-27 05:31:43 +00:00
e . InnerException . Message ) ;
return message ;
}
}
}
}
2009-05-18 15:32:06 +00:00
// m_log.ErrorFormat("Scripting exception:");
// m_log.ErrorFormat(e.ToString());
2008-10-11 16:32:31 +00:00
return e . ToString ( ) ;
2008-09-27 05:31:43 +00:00
}
2008-11-08 02:24:34 +00:00
public string GetAssemblyName ( )
{
2014-12-10 00:25:27 +00:00
return m_assemblyPath ;
2008-11-08 02:24:34 +00:00
}
public string GetXMLState ( )
{
2008-11-09 19:30:40 +00:00
bool run = Running ;
2008-11-08 02:24:34 +00:00
Stop ( 100 ) ;
2008-11-09 19:30:40 +00:00
Running = run ;
2009-02-19 05:16:25 +00:00
// We should not be doing this, but since we are about to
// dispose this, it really doesn't make a difference
// This is meant to work around a Windows only race
/ /
m_InEvent = false ;
2009-02-19 05:31:17 +00:00
// Force an update of the in-memory plugin data
/ /
2012-03-15 02:02:31 +00:00
PluginData = AsyncCommandManager . GetSerializationData ( Engine , ItemID ) ;
2009-02-19 05:31:17 +00:00
2008-11-08 02:24:34 +00:00
return ScriptSerializer . Serialize ( this ) ;
}
2009-04-15 18:51:17 +00:00
public UUID RegionID
{
get { return m_RegionID ; }
}
2010-04-19 05:29:26 +00:00
public void Suspend ( )
{
2011-10-19 19:24:07 +00:00
Suspended = true ;
2010-04-19 05:29:26 +00:00
}
public void Resume ( )
{
2011-10-19 19:24:07 +00:00
Suspended = false ;
2010-04-19 05:29:26 +00:00
}
2008-08-27 22:38:36 +00:00
}
2013-07-12 17:53:27 +00:00
/// <summary>
/// Xengine event wait handle.
/// </summary>
/// <remarks>
/// This class exists becase XEngineScriptBase gets a reference to this wait handle. We need to make sure that
/// when scripts are running in different AppDomains the lease does not expire.
/// FIXME: Like LSL_Api, etc., this effectively leaks memory since the GC will never collect it. To avoid this,
/// proper remoting sponsorship needs to be implemented across the board.
/// </remarks>
public class XEngineEventWaitHandle : EventWaitHandle
{
public XEngineEventWaitHandle ( bool initialState , EventResetMode mode ) : base ( initialState , mode ) { }
public override Object InitializeLifetimeService ( )
{
return null ;
}
}
2013-10-30 13:10:29 +00:00
}