2020-11-11 18:31:26 -05:00
using System ;
using Jint ;
2020-05-11 17:10:43 -04:00
using Jint.Native ;
using Jint.Runtime ;
2020-02-12 14:13:59 -05:00
using Microsoft.CSharp.RuntimeBinder ;
2020-02-11 17:44:06 -05:00
using SharedLibraryCore ;
2019-12-25 15:32:57 -05:00
using SharedLibraryCore.Database.Models ;
2020-05-11 17:10:43 -04:00
using SharedLibraryCore.Exceptions ;
2018-11-05 22:01:29 -05:00
using SharedLibraryCore.Interfaces ;
2020-05-11 17:10:43 -04:00
using System.Collections.Generic ;
2018-08-23 17:16:30 -04:00
using System.IO ;
using System.Linq ;
2022-10-24 19:52:44 -04:00
using System.Runtime.CompilerServices ;
2018-08-23 17:16:30 -04:00
using System.Text ;
2019-12-25 15:32:57 -05:00
using System.Threading ;
2018-08-23 17:16:30 -04:00
using System.Threading.Tasks ;
2022-10-17 10:17:43 -04:00
using IW4MAdmin.Application.Extensions ;
2022-02-07 19:43:36 -05:00
using Jint.Runtime.Interop ;
2020-11-11 18:31:26 -05:00
using Microsoft.Extensions.Logging ;
using Serilog.Context ;
using ILogger = Microsoft . Extensions . Logging . ILogger ;
2018-08-23 17:16:30 -04:00
2020-02-11 17:44:06 -05:00
namespace IW4MAdmin.Application.Misc
2018-08-23 17:16:30 -04:00
{
2020-02-11 17:44:06 -05:00
/// <summary>
/// implementation of IPlugin
/// used to proxy script plugin requests
/// </summary>
2020-01-31 21:15:07 -05:00
public class ScriptPlugin : IPlugin
2018-08-23 17:16:30 -04:00
{
public string Name { get ; set ; }
public float Version { get ; set ; }
2018-08-26 20:20:47 -04:00
public string Author { get ; set ; }
2018-08-23 17:16:30 -04:00
2020-02-17 11:05:31 -05:00
/// <summary>
/// indicates if the plugin is a parser
/// </summary>
public bool IsParser { get ; private set ; }
2022-02-07 19:43:36 -05:00
public FileSystemWatcher Watcher { get ; }
2020-02-01 13:27:14 -05:00
2020-02-02 17:21:34 -05:00
private Engine _scriptEngine ;
2020-02-01 13:27:14 -05:00
private readonly string _fileName ;
2022-02-07 19:43:36 -05:00
private readonly SemaphoreSlim _onProcessing = new ( 1 , 1 ) ;
private bool _successfullyLoaded ;
2020-05-11 17:10:43 -04:00
private readonly List < string > _registeredCommandNames ;
2020-11-11 18:31:26 -05:00
private readonly ILogger _logger ;
2018-08-23 17:16:30 -04:00
2022-02-09 15:45:28 -05:00
public ScriptPlugin ( ILogger logger , string filename , string workingDirectory = null )
2018-08-23 17:16:30 -04:00
{
2020-11-11 18:31:26 -05:00
_logger = logger ;
2020-02-01 13:27:14 -05:00
_fileName = filename ;
2022-02-07 19:43:36 -05:00
Watcher = new FileSystemWatcher
2018-08-23 17:16:30 -04:00
{
2022-02-07 19:43:36 -05:00
Path = workingDirectory ? ? $"{Utilities.OperatingDirectory}Plugins{Path.DirectorySeparatorChar}" ,
2022-10-24 19:52:44 -04:00
NotifyFilter = NotifyFilters . LastWrite ,
2020-02-01 13:27:14 -05:00
Filter = _fileName . Split ( Path . DirectorySeparatorChar ) . Last ( )
2018-08-23 17:16:30 -04:00
} ;
2020-02-01 13:27:14 -05:00
Watcher . EnableRaisingEvents = true ;
2020-05-11 17:10:43 -04:00
_registeredCommandNames = new List < string > ( ) ;
2019-11-15 15:50:20 -05:00
}
~ ScriptPlugin ( )
{
2020-02-01 13:27:14 -05:00
Watcher . Dispose ( ) ;
2020-02-02 17:21:34 -05:00
_onProcessing . Dispose ( ) ;
2018-08-23 17:16:30 -04:00
}
2022-02-07 19:43:36 -05:00
public async Task Initialize ( IManager manager , IScriptCommandFactory scriptCommandFactory ,
IScriptPluginServiceResolver serviceResolver )
2020-02-01 13:27:14 -05:00
{
try
2019-12-25 15:32:57 -05:00
{
2022-02-07 19:43:36 -05:00
await _onProcessing . WaitAsync ( ) ;
2020-02-01 13:27:14 -05:00
// for some reason we get an event trigger when the file is not finished being modified.
// this must have been a change in .NET CORE 3.x
// so if the new file is empty we can't process it yet
if ( new FileInfo ( _fileName ) . Length = = 0L )
2019-12-25 15:32:57 -05:00
{
2020-02-01 13:27:14 -05:00
return ;
2019-12-25 15:32:57 -05:00
}
2022-02-07 19:43:36 -05:00
var firstRun = _scriptEngine = = null ;
2019-12-25 15:32:57 -05:00
2020-02-01 13:27:14 -05:00
// it's been loaded before so we need to call the unload event
if ( ! firstRun )
{
await OnUnloadAsync ( ) ;
2020-05-11 17:10:43 -04:00
2022-02-07 19:43:36 -05:00
foreach ( var commandName in _registeredCommandNames )
2020-05-11 17:10:43 -04:00
{
2022-02-07 19:43:36 -05:00
_logger . LogDebug ( "Removing plugin registered command {Command}" , commandName ) ;
2020-05-11 17:10:43 -04:00
manager . RemoveCommandByName ( commandName ) ;
}
_registeredCommandNames . Clear ( ) ;
2020-02-01 13:27:14 -05:00
}
2018-08-23 17:16:30 -04:00
2022-02-07 19:43:36 -05:00
_successfullyLoaded = false ;
2020-02-01 13:27:14 -05:00
string script ;
2018-10-09 21:19:06 -04:00
2022-02-07 19:43:36 -05:00
await using ( var stream =
new FileStream ( _fileName , FileMode . Open , FileAccess . Read , FileShare . ReadWrite ) )
2018-10-09 21:19:06 -04:00
{
2020-02-01 13:27:14 -05:00
using ( var reader = new StreamReader ( stream , Encoding . Default ) )
{
script = await reader . ReadToEndAsync ( ) ;
}
2018-10-09 21:19:06 -04:00
}
2022-10-25 16:39:49 -04:00
_scriptEngine ? . Dispose ( ) ;
2020-02-02 17:21:34 -05:00
_scriptEngine = new Engine ( cfg = >
2022-10-17 11:45:42 -04:00
cfg . AddExtensionMethods ( typeof ( Utilities ) , typeof ( Enumerable ) , typeof ( Queryable ) ,
typeof ( ScriptPluginExtensions ) )
2022-10-13 14:51:34 -04:00
. AllowClr ( new [ ]
2022-02-07 19:43:36 -05:00
{
typeof ( System . Net . Http . HttpClient ) . Assembly ,
typeof ( EFClient ) . Assembly ,
typeof ( Utilities ) . Assembly ,
2022-07-13 17:10:16 -04:00
typeof ( Encoding ) . Assembly ,
2022-10-13 14:51:34 -04:00
typeof ( CancellationTokenSource ) . Assembly ,
typeof ( Data . Models . Client . EFClient ) . Assembly ,
typeof ( IW4MAdmin . Plugins . Stats . Plugin ) . Assembly
2022-02-07 19:43:36 -05:00
} )
. CatchClrExceptions ( )
. AddObjectConverter ( new PermissionLevelToStringConverter ( ) ) ) ;
_scriptEngine . Execute ( script ) ;
2020-02-02 17:21:34 -05:00
_scriptEngine . SetValue ( "_localization" , Utilities . CurrentLocalization ) ;
2020-09-26 19:13:56 -04:00
_scriptEngine . SetValue ( "_serviceResolver" , serviceResolver ) ;
2022-02-09 15:45:28 -05:00
_scriptEngine . SetValue ( "_lock" , _onProcessing ) ;
2022-02-07 19:43:36 -05:00
dynamic pluginObject = _scriptEngine . Evaluate ( "plugin" ) . ToObject ( ) ;
2018-08-23 17:16:30 -04:00
2020-02-01 13:27:14 -05:00
Author = pluginObject . author ;
Name = pluginObject . name ;
Version = ( float ) pluginObject . version ;
2019-01-26 21:33:37 -05:00
2022-02-07 19:43:36 -05:00
var commands = JsValue . Undefined ;
try
{
commands = _scriptEngine . Evaluate ( "commands" ) ;
}
catch ( JavaScriptException )
{
// ignore because commands aren't defined;
}
2020-05-11 17:10:43 -04:00
if ( commands ! = JsValue . Undefined )
{
try
{
foreach ( var command in GenerateScriptCommands ( commands , scriptCommandFactory ) )
{
2022-02-07 19:43:36 -05:00
_logger . LogDebug ( "Adding plugin registered command {CommandName}" , command . Name ) ;
2020-05-11 17:10:43 -04:00
manager . AddAdditionalCommand ( command ) ;
_registeredCommandNames . Add ( command . Name ) ;
}
}
catch ( RuntimeBinderException e )
{
2022-02-07 19:43:36 -05:00
throw new PluginException ( $"Not all required fields were found: {e.Message}" )
{ PluginFile = _fileName } ;
2020-05-11 17:10:43 -04:00
}
}
2022-10-24 19:52:44 -04:00
async Task < bool > OnLoadTask ( )
{
await OnLoadAsync ( manager ) ;
return true ;
}
var loadComplete = false ;
2020-02-01 13:27:14 -05:00
try
2019-01-27 19:54:18 -05:00
{
2020-02-01 13:27:14 -05:00
if ( pluginObject . isParser )
{
2022-10-24 19:52:44 -04:00
loadComplete = await OnLoadTask ( ) ;
2020-02-17 11:05:31 -05:00
IsParser = true ;
2022-02-07 19:43:36 -05:00
var eventParser = ( IEventParser ) _scriptEngine . Evaluate ( "eventParser" ) . ToObject ( ) ;
var rconParser = ( IRConParser ) _scriptEngine . Evaluate ( "rconParser" ) . ToObject ( ) ;
2020-02-01 13:27:14 -05:00
manager . AdditionalEventParsers . Add ( eventParser ) ;
manager . AdditionalRConParsers . Add ( rconParser ) ;
}
2019-01-27 19:54:18 -05:00
}
2022-01-28 18:28:49 -05:00
catch ( RuntimeBinderException )
{
var configWrapper = new ScriptPluginConfigurationWrapper ( Name , _scriptEngine ) ;
await configWrapper . InitializeAsync ( ) ;
2022-10-24 19:52:44 -04:00
if ( ! loadComplete )
{
_scriptEngine . SetValue ( "_configHandler" , configWrapper ) ;
loadComplete = await OnLoadTask ( ) ;
}
2022-01-28 18:28:49 -05:00
}
2019-01-27 19:54:18 -05:00
2022-10-24 19:52:44 -04:00
if ( ! firstRun & & ! loadComplete )
2020-02-01 13:27:14 -05:00
{
2022-10-24 19:52:44 -04:00
loadComplete = await OnLoadTask ( ) ;
2020-02-01 13:27:14 -05:00
}
2022-10-24 19:52:44 -04:00
_successfullyLoaded = loadComplete ;
2020-02-01 13:27:14 -05:00
}
2020-09-26 19:13:56 -04:00
catch ( JavaScriptException ex )
{
2020-11-11 18:31:26 -05:00
_logger . LogError ( ex ,
2022-10-24 19:52:44 -04:00
"Encountered JavaScript runtime error while executing {MethodName} for script plugin {Plugin} at {@LocationInfo} StackTrace={StackTrace}" ,
nameof ( Initialize ) , Path . GetFileName ( _fileName ) , ex . Location , ex . JavaScriptStackTrace ) ;
2022-02-07 19:43:36 -05:00
2020-11-11 18:31:26 -05:00
throw new PluginException ( "An error occured while initializing script plugin" ) ;
2020-09-26 19:13:56 -04:00
}
2022-02-07 19:43:36 -05:00
catch ( Exception ex ) when ( ex . InnerException is JavaScriptException jsEx )
2018-08-23 17:16:30 -04:00
{
2020-11-11 18:31:26 -05:00
_logger . LogError ( ex ,
2022-10-24 19:52:44 -04:00
"Encountered JavaScript runtime error while executing {MethodName} for script plugin {Plugin} initialization {@LocationInfo} StackTrace={StackTrace}" ,
nameof ( Initialize ) , _fileName , jsEx . Location , jsEx . JavaScriptStackTrace ) ;
2022-02-07 19:43:36 -05:00
throw new PluginException ( "An error occured while initializing script plugin" ) ;
2018-08-23 17:16:30 -04:00
}
2022-02-07 19:43:36 -05:00
catch ( Exception ex )
{
_logger . LogError ( ex ,
"Encountered JavaScript runtime error while executing {MethodName} for script plugin {Plugin}" ,
nameof ( OnLoadAsync ) , Path . GetFileName ( _fileName ) ) ;
2019-12-25 15:32:57 -05:00
2022-02-07 19:43:36 -05:00
throw new PluginException ( "An error occured while executing action for script plugin" ) ;
}
2020-02-01 13:27:14 -05:00
finally
{
2020-02-02 17:21:34 -05:00
if ( _onProcessing . CurrentCount = = 0 )
2020-02-01 13:27:14 -05:00
{
2020-02-02 17:21:34 -05:00
_onProcessing . Release ( 1 ) ;
2020-02-01 13:27:14 -05:00
}
}
2018-08-23 17:16:30 -04:00
}
2022-02-07 19:43:36 -05:00
public async Task OnEventAsync ( GameEvent gameEvent , Server server )
2018-08-23 17:16:30 -04:00
{
2022-02-13 22:38:40 -05:00
if ( ! _successfullyLoaded )
2018-09-04 13:40:29 -04:00
{
2022-02-13 22:38:40 -05:00
return ;
}
2022-10-25 16:39:49 -04:00
var shouldRelease = false ;
2022-02-13 22:38:40 -05:00
try
{
await _onProcessing . WaitAsync ( ) ;
2022-10-25 16:39:49 -04:00
shouldRelease = true ;
2022-10-24 19:52:44 -04:00
WrapJavaScriptErrorHandling ( ( ) = >
2020-02-02 17:21:34 -05:00
{
2022-10-24 19:52:44 -04:00
_scriptEngine . SetValue ( "_gameEvent" , gameEvent ) ;
_scriptEngine . SetValue ( "_server" , server ) ;
_scriptEngine . SetValue ( "_IW4MAdminClient" , Utilities . IW4MAdminClient ( server ) ) ;
return _scriptEngine . Evaluate ( "plugin.onEventAsync(_gameEvent, _server)" ) ;
} , new { EventType = gameEvent . Type } , server ) ;
2022-02-13 22:38:40 -05:00
}
finally
{
2022-10-25 16:39:49 -04:00
if ( _onProcessing . CurrentCount = = 0 & & shouldRelease )
2020-02-02 17:21:34 -05:00
{
2022-02-13 22:38:40 -05:00
_onProcessing . Release ( 1 ) ;
2020-02-02 17:21:34 -05:00
}
2018-09-04 13:40:29 -04:00
}
2022-10-24 19:52:44 -04:00
2018-08-23 17:16:30 -04:00
}
2022-10-25 16:39:49 -04:00
public Task OnLoadAsync ( IManager manager )
2018-08-23 17:16:30 -04:00
{
2022-10-24 19:52:44 -04:00
_logger . LogDebug ( "OnLoad executing for {Name}" , Name ) ;
WrapJavaScriptErrorHandling ( ( ) = >
2022-02-07 19:43:36 -05:00
{
_scriptEngine . SetValue ( "_manager" , manager ) ;
2022-06-01 12:25:11 -04:00
_scriptEngine . SetValue ( "getDvar" , BeginGetDvar ) ;
_scriptEngine . SetValue ( "setDvar" , BeginSetDvar ) ;
2022-10-24 19:52:44 -04:00
return _scriptEngine . Evaluate ( "plugin.onLoadAsync(_manager)" ) ;
} ) ;
2022-02-07 19:43:36 -05:00
2022-10-24 19:52:44 -04:00
return Task . CompletedTask ;
2018-08-23 17:16:30 -04:00
}
2022-10-24 19:52:44 -04:00
public Task OnTickAsync ( Server server )
2018-08-23 17:16:30 -04:00
{
2022-10-24 19:52:44 -04:00
WrapJavaScriptErrorHandling ( ( ) = >
{
_scriptEngine . SetValue ( "_server" , server ) ;
return _scriptEngine . Evaluate ( "plugin.onTickAsync(_server)" ) ;
} ) ;
return Task . CompletedTask ;
2018-08-23 17:16:30 -04:00
}
2022-10-24 19:52:44 -04:00
public async Task OnUnloadAsync ( )
2019-01-27 19:54:18 -05:00
{
2022-02-07 19:43:36 -05:00
if ( ! _successfullyLoaded )
2019-12-25 15:32:57 -05:00
{
2022-10-24 19:52:44 -04:00
return ;
2019-12-25 15:32:57 -05:00
}
2022-02-07 19:43:36 -05:00
try
{
2022-10-24 19:52:44 -04:00
await _onProcessing . WaitAsync ( ) ;
_logger . LogDebug ( "OnUnload executing for {Name}" , Name ) ;
2022-02-07 19:43:36 -05:00
2022-10-24 19:52:44 -04:00
WrapJavaScriptErrorHandling ( ( ) = > _scriptEngine . Evaluate ( "plugin.onUnloadAsync()" ) ) ;
2022-02-07 19:43:36 -05:00
}
2022-10-24 19:52:44 -04:00
finally
2022-02-07 19:43:36 -05:00
{
2022-10-24 19:52:44 -04:00
if ( _onProcessing . CurrentCount = = 0 )
{
_onProcessing . Release ( 1 ) ;
}
2022-02-07 19:43:36 -05:00
}
2019-01-27 19:54:18 -05:00
}
2020-05-11 17:10:43 -04:00
2022-10-24 19:52:44 -04:00
public T ExecuteAction < T > ( Delegate action , CancellationToken token , params object [ ] param )
2022-09-08 16:03:38 -04:00
{
2022-10-25 16:39:49 -04:00
var shouldRelease = false ;
2022-09-08 16:03:38 -04:00
try
{
2022-10-24 19:52:44 -04:00
using var forceTimeout = new CancellationTokenSource ( 5000 ) ;
using var combined = CancellationTokenSource . CreateLinkedTokenSource ( forceTimeout . Token , token ) ;
_onProcessing . Wait ( combined . Token ) ;
2022-10-25 16:39:49 -04:00
shouldRelease = true ;
2022-10-24 19:52:44 -04:00
_logger . LogDebug ( "Executing action for {Name}" , Name ) ;
return WrapJavaScriptErrorHandling ( T ( ) = >
{
var args = param . Select ( p = > JsValue . FromObject ( _scriptEngine , p ) ) . ToArray ( ) ;
var result = action . DynamicInvoke ( JsValue . Undefined , args ) ;
return ( T ) ( result as JsValue ) ? . ToObject ( ) ;
} ,
new
{
Params = string . Join ( ", " ,
param ? . Select ( eachParam = > $"Type={eachParam?.GetType().Name} Value={eachParam}" ) ? ?
Enumerable . Empty < string > ( ) )
} ) ;
2022-10-23 15:03:33 -04:00
}
2022-09-08 16:03:38 -04:00
finally
{
2022-10-25 16:39:49 -04:00
if ( _onProcessing . CurrentCount = = 0 & & shouldRelease )
2022-09-08 16:03:38 -04:00
{
_onProcessing . Release ( 1 ) ;
}
}
}
2022-10-17 11:45:42 -04:00
2022-10-24 19:52:44 -04:00
public T WrapDelegate < T > ( Delegate act , CancellationToken token , params object [ ] args )
2022-09-08 16:03:38 -04:00
{
2022-10-25 16:39:49 -04:00
var shouldRelease = false ;
2022-09-08 16:03:38 -04:00
try
{
2022-10-24 19:52:44 -04:00
using var forceTimeout = new CancellationTokenSource ( 5000 ) ;
using var combined = CancellationTokenSource . CreateLinkedTokenSource ( forceTimeout . Token , token ) ;
_onProcessing . Wait ( combined . Token ) ;
2022-10-25 16:39:49 -04:00
shouldRelease = true ;
2022-10-24 19:52:44 -04:00
_logger . LogDebug ( "Wrapping delegate action for {Name}" , Name ) ;
return WrapJavaScriptErrorHandling (
T ( ) = > ( T ) ( act . DynamicInvoke ( JsValue . Null ,
args . Select ( arg = > JsValue . FromObject ( _scriptEngine , arg ) ) . ToArray ( ) ) as ObjectWrapper )
? . ToObject ( ) ,
new
{
Params = string . Join ( ", " ,
args ? . Select ( eachParam = > $"Type={eachParam?.GetType().Name} Value={eachParam}" ) ? ?
Enumerable . Empty < string > ( ) )
} ) ;
2022-10-23 15:03:33 -04:00
}
2022-09-08 16:03:38 -04:00
finally
{
2022-10-25 16:39:49 -04:00
if ( _onProcessing . CurrentCount = = 0 & & shouldRelease )
2022-09-08 16:03:38 -04:00
{
_onProcessing . Release ( 1 ) ;
}
}
}
2020-05-11 17:10:43 -04:00
/// <summary>
/// finds declared script commands in the script plugin
/// </summary>
/// <param name="commands">commands value from jint parser</param>
/// <param name="scriptCommandFactory">factory to create the command from</param>
/// <returns></returns>
2022-06-01 12:25:11 -04:00
private IEnumerable < IManagerCommand > GenerateScriptCommands ( JsValue commands ,
IScriptCommandFactory scriptCommandFactory )
2020-05-11 17:10:43 -04:00
{
2022-02-07 19:43:36 -05:00
var commandList = new List < IManagerCommand > ( ) ;
2020-05-11 17:10:43 -04:00
// go through each defined command
foreach ( var command in commands . AsArray ( ) )
{
dynamic dynamicCommand = command . ToObject ( ) ;
string name = dynamicCommand . name ;
string alias = dynamicCommand . alias ;
string description = dynamicCommand . description ;
2022-10-13 14:51:34 -04:00
if ( dynamicCommand . permission is Data . Models . Client . EFClient . Permission perm )
{
dynamicCommand . permission = perm . ToString ( ) ;
}
2022-10-17 11:45:42 -04:00
2020-05-11 17:10:43 -04:00
string permission = dynamicCommand . permission ;
2022-02-07 19:43:36 -05:00
List < Server . Game > supportedGames = null ;
var targetRequired = false ;
2020-05-11 17:10:43 -04:00
2022-02-07 19:43:36 -05:00
var args = new List < ( string , bool ) > ( ) ;
2020-05-11 17:10:43 -04:00
dynamic arguments = null ;
try
{
arguments = dynamicCommand . arguments ;
}
catch ( RuntimeBinderException )
{
// arguments are optional
}
2020-09-28 21:32:53 -04:00
try
{
targetRequired = dynamicCommand . targetRequired ;
}
catch ( RuntimeBinderException )
{
// arguments are optional
}
2020-05-11 17:10:43 -04:00
if ( arguments ! = null )
{
foreach ( var arg in dynamicCommand . arguments )
{
args . Add ( ( arg . name , ( bool ) arg . required ) ) ;
}
}
2022-02-07 19:43:36 -05:00
try
{
foreach ( var game in dynamicCommand . supportedGames )
{
supportedGames ? ? = new List < Server . Game > ( ) ;
supportedGames . Add ( Enum . Parse ( typeof ( Server . Game ) , game . ToString ( ) ) ) ;
}
}
catch ( RuntimeBinderException )
2020-05-11 17:10:43 -04:00
{
2022-02-07 19:43:36 -05:00
// supported games is optional
}
2020-05-11 17:10:43 -04:00
2022-02-07 19:43:36 -05:00
async Task Execute ( GameEvent gameEvent )
{
2020-05-11 17:10:43 -04:00
try
{
2022-02-07 19:43:36 -05:00
await _onProcessing . WaitAsync ( ) ;
_scriptEngine . SetValue ( "_event" , gameEvent ) ;
var jsEventObject = _scriptEngine . Evaluate ( "_event" ) ;
2022-06-01 12:25:11 -04:00
2022-02-07 19:43:36 -05:00
dynamicCommand . execute . Target . Invoke ( _scriptEngine , jsEventObject ) ;
2020-05-11 17:10:43 -04:00
}
catch ( JavaScriptException ex )
{
2022-02-07 19:43:36 -05:00
using ( LogContext . PushProperty ( "Server" , gameEvent . Owner ? . ToString ( ) ) )
{
_logger . LogError ( ex , "Could not execute command action for {Filename} {@Location}" ,
Path . GetFileName ( _fileName ) , ex . Location ) ;
}
throw new PluginException ( "A runtime error occured while executing action for script plugin" ) ;
}
2022-06-01 12:25:11 -04:00
2022-02-07 19:43:36 -05:00
catch ( Exception ex )
{
using ( LogContext . PushProperty ( "Server" , gameEvent . Owner ? . ToString ( ) ) )
{
_logger . LogError ( ex ,
"Could not execute command action for script plugin {FileName}" ,
Path . GetFileName ( _fileName ) ) ;
}
throw new PluginException ( "An error occured while executing action for script plugin" ) ;
}
finally
{
if ( _onProcessing . CurrentCount = = 0 )
{
_onProcessing . Release ( 1 ) ;
}
2020-05-11 17:10:43 -04:00
}
}
2022-02-07 19:43:36 -05:00
commandList . Add ( scriptCommandFactory . CreateScriptCommand ( name , alias , description , permission ,
targetRequired , args , Execute , supportedGames ? . ToArray ( ) ) ) ;
2020-05-11 17:10:43 -04:00
}
return commandList ;
}
2022-03-07 20:59:34 -05:00
2022-06-01 12:25:11 -04:00
private void BeginGetDvar ( Server server , string dvarName , Delegate onCompleted )
2022-03-07 20:59:34 -05:00
{
2022-10-17 11:45:42 -04:00
var operationTimeout = TimeSpan . FromSeconds ( 5 ) ;
2022-08-26 13:07:43 -04:00
void OnComplete ( IAsyncResult result )
2022-03-07 20:59:34 -05:00
{
try
{
2022-10-17 11:45:42 -04:00
_onProcessing . Wait ( ) ;
2022-06-01 12:25:11 -04:00
var ( success , value ) = ( ValueTuple < bool , string > ) result . AsyncState ;
2022-03-07 20:59:34 -05:00
onCompleted . DynamicInvoke ( JsValue . Undefined ,
new [ ]
{
2022-06-01 12:25:11 -04:00
JsValue . FromObject ( _scriptEngine , server ) ,
2022-03-07 20:59:34 -05:00
JsValue . FromObject ( _scriptEngine , dvarName ) ,
2022-06-01 12:25:11 -04:00
JsValue . FromObject ( _scriptEngine , value ) ,
JsValue . FromObject ( _scriptEngine , success )
2022-03-07 20:59:34 -05:00
} ) ;
}
2022-08-26 13:07:43 -04:00
catch ( JavaScriptException ex )
{
using ( LogContext . PushProperty ( "Server" , server . ToString ( ) ) )
{
2022-10-17 11:45:42 -04:00
_logger . LogError ( ex , "Could not invoke BeginGetDvar callback for {Filename} {@Location}" ,
2022-08-26 13:07:43 -04:00
Path . GetFileName ( _fileName ) , ex . Location ) ;
}
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Could not complete {BeginGetDvar} for {Class}" , nameof ( BeginGetDvar ) , Name ) ;
}
2022-10-17 11:45:42 -04:00
finally
{
if ( _onProcessing . CurrentCount = = 0 )
{
_onProcessing . Release ( 1 ) ;
}
}
2022-08-26 13:07:43 -04:00
}
2022-10-17 11:45:42 -04:00
new Thread ( ( ) = >
2022-08-26 13:07:43 -04:00
{
2022-10-23 15:03:33 -04:00
if ( DateTime . Now - ( server . MatchEndTime ? ? server . MatchStartTime ) < TimeSpan . FromSeconds ( 15 ) )
{
using ( LogContext . PushProperty ( "Server" , server . ToString ( ) ) )
{
_logger . LogDebug ( "Not getting DVar because match recently ended" ) ;
}
OnComplete ( new AsyncResult
{
IsCompleted = false ,
AsyncState = ( false , ( string ) null )
} ) ;
}
2022-10-24 19:52:44 -04:00
2022-10-23 15:03:33 -04:00
using var tokenSource = new CancellationTokenSource ( ) ;
2022-10-17 11:45:42 -04:00
tokenSource . CancelAfter ( operationTimeout ) ;
2022-03-07 20:59:34 -05:00
2022-10-17 11:45:42 -04:00
server . GetDvarAsync < string > ( dvarName , token : tokenSource . Token ) . ContinueWith ( action = >
2022-03-07 20:59:34 -05:00
{
2022-10-17 11:45:42 -04:00
if ( action . IsCompletedSuccessfully )
{
OnComplete ( new AsyncResult
{
IsCompleted = true ,
AsyncState = ( true , action . Result . Value )
} ) ;
}
else
2022-03-07 20:59:34 -05:00
{
2022-10-17 11:45:42 -04:00
OnComplete ( new AsyncResult
{
IsCompleted = false ,
AsyncState = ( false , ( string ) null )
} ) ;
2022-03-07 20:59:34 -05:00
}
2022-10-17 11:45:42 -04:00
} ) ;
} ) . Start ( ) ;
2022-03-07 20:59:34 -05:00
}
2022-06-01 12:25:11 -04:00
private void BeginSetDvar ( Server server , string dvarName , string dvarValue , Delegate onCompleted )
2022-03-07 20:59:34 -05:00
{
2022-10-17 11:45:42 -04:00
var operationTimeout = TimeSpan . FromSeconds ( 5 ) ;
2022-08-26 13:07:43 -04:00
void OnComplete ( IAsyncResult result )
2022-03-07 20:59:34 -05:00
{
try
{
2022-10-17 11:45:42 -04:00
_onProcessing . Wait ( ) ;
2022-06-01 12:25:11 -04:00
var success = ( bool ) result . AsyncState ;
2022-03-07 20:59:34 -05:00
onCompleted . DynamicInvoke ( JsValue . Undefined ,
new [ ]
{
JsValue . FromObject ( _scriptEngine , server ) ,
2022-06-01 12:25:11 -04:00
JsValue . FromObject ( _scriptEngine , dvarName ) ,
2022-03-07 20:59:34 -05:00
JsValue . FromObject ( _scriptEngine , dvarValue ) ,
JsValue . FromObject ( _scriptEngine , success )
} ) ;
}
2022-08-26 13:07:43 -04:00
catch ( JavaScriptException ex )
{
using ( LogContext . PushProperty ( "Server" , server . ToString ( ) ) )
{
_logger . LogError ( ex , "Could complete BeginSetDvar for {Filename} {@Location}" ,
Path . GetFileName ( _fileName ) , ex . Location ) ;
}
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Could not complete {BeginSetDvar} for {Class}" , nameof ( BeginSetDvar ) , Name ) ;
}
2022-03-07 20:59:34 -05:00
finally
{
2022-10-17 11:45:42 -04:00
if ( _onProcessing . CurrentCount = = 0 )
2022-03-07 20:59:34 -05:00
{
2022-10-17 11:45:42 -04:00
_onProcessing . Release ( 1 ) ;
2022-03-07 20:59:34 -05:00
}
}
2022-10-17 11:45:42 -04:00
}
new Thread ( ( ) = >
{
2022-10-23 15:03:33 -04:00
if ( DateTime . Now - ( server . MatchEndTime ? ? server . MatchStartTime ) < TimeSpan . FromSeconds ( 15 ) )
{
using ( LogContext . PushProperty ( "Server" , server . ToString ( ) ) )
{
_logger . LogDebug ( "Not setting DVar because match recently ended" ) ;
}
OnComplete ( new AsyncResult
{
IsCompleted = false ,
AsyncState = false
} ) ;
}
2022-10-24 19:52:44 -04:00
2022-10-23 15:03:33 -04:00
using var tokenSource = new CancellationTokenSource ( ) ;
2022-10-17 11:45:42 -04:00
tokenSource . CancelAfter ( operationTimeout ) ;
server . SetDvarAsync ( dvarName , dvarValue , token : tokenSource . Token ) . ContinueWith ( action = >
{
if ( action . IsCompletedSuccessfully )
{
OnComplete ( new AsyncResult
{
IsCompleted = true ,
AsyncState = true
} ) ;
}
else
{
OnComplete ( new AsyncResult
{
IsCompleted = false ,
AsyncState = false
} ) ;
}
} ) ;
} ) . Start ( ) ;
2022-03-07 20:59:34 -05:00
}
2022-10-24 19:52:44 -04:00
private T WrapJavaScriptErrorHandling < T > ( Func < T > work , object additionalData = null , Server server = null ,
[CallerMemberName] string methodName = "" )
{
using ( LogContext . PushProperty ( "Server" , server ? . ToString ( ) ) )
{
try
{
return work ( ) ;
}
catch ( JavaScriptException ex )
{
_logger . LogError ( ex ,
"Encountered JavaScript runtime error while executing {MethodName} for script plugin {Plugin} at {@LocationInfo} StackTrace={StackTrace} {@AdditionalData}" ,
methodName , Path . GetFileName ( _fileName ) , ex . Location , ex . StackTrace , additionalData ) ;
throw new PluginException ( "A runtime error occured while executing action for script plugin" ) ;
}
catch ( Exception ex ) when ( ex . InnerException is JavaScriptException jsEx )
{
_logger . LogError ( ex ,
"Encountered JavaScript runtime error while executing {MethodName} for script plugin {Plugin} initialization {@LocationInfo} StackTrace={StackTrace} {@AdditionalData}" ,
methodName , _fileName , jsEx . Location , jsEx . JavaScriptStackTrace , additionalData ) ;
throw new PluginException ( "A runtime error occured while executing action for script plugin" ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex ,
"Encountered JavaScript runtime error while executing {MethodName} for script plugin {Plugin}" ,
methodName , Path . GetFileName ( _fileName ) ) ;
throw new PluginException ( "An error occured while executing action for script plugin" ) ;
}
}
}
2018-08-23 17:16:30 -04:00
}
2022-02-07 19:43:36 -05:00
public class PermissionLevelToStringConverter : IObjectConverter
{
public bool TryConvert ( Engine engine , object value , out JsValue result )
{
if ( value is Data . Models . Client . EFClient . Permission )
{
result = value . ToString ( ) ;
return true ;
}
result = JsValue . Null ;
return false ;
}
}
2018-08-23 17:16:30 -04:00
}