265 lines
8.2 KiB
C#
265 lines
8.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using ClassicAssist.Shared;
|
|
using ClassicAssist.Data.Macros.Commands;
|
|
using ClassicAssist.Shared.Resources;
|
|
using ClassicAssist.Shared.UO;
|
|
using IronPython.Hosting;
|
|
using Microsoft.Scripting;
|
|
using Microsoft.Scripting.Hosting;
|
|
|
|
namespace ClassicAssist.Data.Macros
|
|
{
|
|
public class MacroInvoker
|
|
{
|
|
public delegate void dMacroException( Exception e );
|
|
|
|
public delegate void dMacroStartStop();
|
|
|
|
private static readonly ScriptEngine _engine = Python.CreateEngine();
|
|
private static Dictionary<string, object> _importCache;
|
|
private readonly MemoryStream _memoryStream = new MemoryStream();
|
|
private CancellationTokenSource _cancellationToken;
|
|
private MacroEntry _macro;
|
|
private readonly SystemMessageTextWriter _textWriter = new SystemMessageTextWriter();
|
|
|
|
public MacroInvoker()
|
|
{
|
|
ScriptRuntime runtime = _engine.Runtime;
|
|
runtime.LoadAssembly( Assembly.GetExecutingAssembly() );
|
|
|
|
foreach ( string assembly in AssistantOptions.Assemblies ?? new string[0] )
|
|
{
|
|
try
|
|
{
|
|
runtime.LoadAssembly( Assembly.LoadFile( assembly ) );
|
|
}
|
|
catch ( Exception )
|
|
{
|
|
// ignored
|
|
}
|
|
}
|
|
|
|
if ( _importCache == null )
|
|
{
|
|
_importCache = InitializeImports( _engine );
|
|
}
|
|
|
|
_engine.Runtime.IO.SetOutput( _memoryStream, _textWriter );
|
|
_engine.Runtime.IO.SetErrorOutput( _memoryStream, _textWriter );
|
|
|
|
string modulePath = Path.Combine( Engine.StartupPath ?? Environment.CurrentDirectory, "Modules" );
|
|
ICollection<string> searchPaths = _engine.GetSearchPaths();
|
|
|
|
if ( searchPaths.Contains( modulePath ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
searchPaths.Add( modulePath );
|
|
_engine.SetSearchPaths( searchPaths );
|
|
}
|
|
|
|
public Exception Exception { get; set; }
|
|
public bool IsFaulted { get; set; }
|
|
|
|
public bool IsRunning => Thread?.IsAlive ?? false;
|
|
|
|
public Stopwatch StopWatch { get; set; } = new Stopwatch();
|
|
|
|
public Thread Thread { get; set; }
|
|
|
|
public event dMacroStartStop StartedEvent;
|
|
public event dMacroStartStop StoppedEvent;
|
|
public event dMacroException ExceptionEvent;
|
|
|
|
private static string GetScriptingImports()
|
|
{
|
|
string prepend = Assembly.GetExecutingAssembly().GetTypes()
|
|
.Where( t =>
|
|
t.Namespace != null && t.IsPublic && t.IsClass && t.Namespace.EndsWith( "Macros.Commands" ) )
|
|
.Aggregate( string.Empty, ( current, t ) => current + $"from {t.FullName} import * \n" );
|
|
|
|
foreach ( string assemblyName in AssistantOptions.Assemblies ?? new string[0] )
|
|
{
|
|
try
|
|
{
|
|
Assembly assembly = Assembly.LoadFile( assemblyName );
|
|
|
|
prepend += assembly.GetTypes()
|
|
.Where( t =>
|
|
t.Namespace != null && t.IsPublic && t.IsClass &&
|
|
t.Namespace.EndsWith( "Macros.Commands" ) ).Aggregate( string.Empty,
|
|
( current, t ) => current + $"from {t.FullName} import * \n" );
|
|
}
|
|
catch ( Exception )
|
|
{
|
|
// ignored
|
|
}
|
|
}
|
|
|
|
return prepend;
|
|
}
|
|
|
|
public static Dictionary<string, object> InitializeImports( ScriptEngine engine )
|
|
{
|
|
Dictionary<string, object> dictionary = new Dictionary<string, object>();
|
|
|
|
ScriptSource importSource =
|
|
engine.CreateScriptSourceFromString( GetScriptingImports(), SourceCodeKind.Statements );
|
|
|
|
CompiledCode importCompiled = importSource.Compile();
|
|
ScriptScope importScope = engine.CreateScope( dictionary );
|
|
importCompiled.Execute( importScope );
|
|
|
|
return dictionary;
|
|
}
|
|
|
|
public void Execute( MacroEntry macro )
|
|
{
|
|
_macro = macro;
|
|
|
|
if ( Thread != null && Thread.IsAlive )
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
MainCommands.SetQuietMode( Options.CurrentOptions.DefaultMacroQuietMode );
|
|
|
|
_cancellationToken = new CancellationTokenSource();
|
|
|
|
if ( _importCache == null )
|
|
{
|
|
_importCache = InitializeImports( _engine );
|
|
}
|
|
|
|
ScriptSource source = _engine.CreateScriptSourceFromString( _macro.Macro, SourceCodeKind.Statements );
|
|
|
|
Dictionary<string, object> importCache = new Dictionary<string, object>( _importCache );
|
|
|
|
IsFaulted = false;
|
|
|
|
Thread = new Thread( () =>
|
|
{
|
|
Thread = Thread.CurrentThread;
|
|
|
|
try
|
|
{
|
|
StartedEvent?.Invoke();
|
|
|
|
AliasCommands.SetDefaultAliases();
|
|
|
|
ScriptScope macroScope = _engine.CreateScope( importCache );
|
|
|
|
StopWatch.Reset();
|
|
StopWatch.Start();
|
|
|
|
do
|
|
{
|
|
_cancellationToken.Token.ThrowIfCancellationRequested();
|
|
|
|
source.Execute( macroScope );
|
|
|
|
StopWatch.Stop();
|
|
|
|
bool willLoop = _macro.Loop && !IsFaulted && !_cancellationToken.IsCancellationRequested;
|
|
|
|
if ( !willLoop )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( Options.CurrentOptions.Debug )
|
|
{
|
|
Shared.UO.Commands.SystemMessage( string.Format( Strings.Loop_time___0_, StopWatch.Elapsed ) );
|
|
}
|
|
|
|
int diff = 50 - (int) StopWatch.ElapsedMilliseconds;
|
|
|
|
if ( diff > 0 )
|
|
{
|
|
Thread.Sleep( diff );
|
|
}
|
|
}
|
|
while ( _macro.Loop && !IsFaulted );
|
|
}
|
|
catch ( TaskCanceledException )
|
|
{
|
|
IsFaulted = true;
|
|
}
|
|
catch ( ThreadInterruptedException )
|
|
{
|
|
IsFaulted = true;
|
|
}
|
|
catch ( ThreadAbortException )
|
|
{
|
|
IsFaulted = true;
|
|
}
|
|
catch ( Exception e )
|
|
{
|
|
IsFaulted = true;
|
|
Exception = e;
|
|
|
|
ExceptionEvent?.Invoke( e );
|
|
}
|
|
finally
|
|
{
|
|
StoppedEvent?.Invoke();
|
|
MacroManager.GetInstance().OnMacroStopped();
|
|
}
|
|
} ) { IsBackground = true };
|
|
|
|
try
|
|
{
|
|
Thread.Start();
|
|
MacroManager.GetInstance().OnMacroStarted();
|
|
}
|
|
catch ( ThreadStateException )
|
|
{
|
|
// TODO
|
|
}
|
|
catch ( ThreadStartException )
|
|
{
|
|
// TODO
|
|
}
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
_cancellationToken?.Cancel();
|
|
|
|
if ( Thread == null || !Thread.IsAlive )
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
StopWatch.Stop();
|
|
|
|
int diff = 50 - (int) StopWatch.ElapsedMilliseconds;
|
|
|
|
if ( diff > 0 )
|
|
{
|
|
Thread.Sleep( diff );
|
|
}
|
|
|
|
Thread?.Abort();
|
|
Thread?.Join( 100 );
|
|
|
|
MacroManager.GetInstance().Replay = false;
|
|
MacroManager.GetInstance().OnMacroStopped();
|
|
}
|
|
catch ( ThreadStateException e )
|
|
{
|
|
Shared.UO.Commands.SystemMessage( string.Format( Strings.Macro_error___0_, e.Message ) );
|
|
}
|
|
}
|
|
}
|
|
} |