Files
Beyhan Oğur d5f52cc3d3 first commit
2026-09-03 04:45:36 +03:00

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 ) );
}
}
}
}