refactor a good bit of stuff for better dependency injection

fix regular expression for T6 log parsing
This commit is contained in:
RaidMax
2020-02-11 16:44:06 -06:00
parent ec053eb854
commit c3c21a7749
56 changed files with 820 additions and 322 deletions

View File

@ -500,18 +500,18 @@ namespace SharedLibraryCore.Commands
public override Task ExecuteAsync(GameEvent E)
{
String cmd = E.Data.Trim();
string cmd = E.Data.Trim();
if (cmd.Length > 2)
{
bool found = false;
foreach (Command C in E.Owner.Manager.GetCommands())
foreach (var command in E.Owner.Manager.GetCommands())
{
if (C.Name == cmd.ToLower() ||
C.Alias == cmd.ToLower())
if (command.Name == cmd.ToLower() ||
command.Alias == cmd.ToLower())
{
E.Origin.Tell($"[^3{C.Name}^7] {C.Description}");
E.Origin.Tell(C.Syntax);
E.Origin.Tell($"[^3{command.Name}^7] {command.Description}");
E.Origin.Tell(command.Syntax);
found = true;
}
}

View File

@ -56,11 +56,11 @@ namespace SharedLibraryCore.Configuration
{
var loc = Utilities.CurrentLocalization.LocalizationIndex;
var parserVersions = rconParsers.Select(_parser => _parser.Name).ToArray();
var selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_RCON_PARSER_VERSION"]} ({IPAddress}:{Port})", $"{loc["SETUP_PROMPT_DEFAULT"]} (Call of Duty)", null, parserVersions);
var selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_RCON_PARSER_VERSION"]} ({IPAddress}:{Port})", parserVersions[0], null, parserVersions);
if (selection.Item1 >= 0)
{
RConParserVersion = rconParsers.First(_parser => _parser.Name == selection.Item2).Version;
RConParserVersion = rconParsers.FirstOrDefault(_parser => _parser.Name == selection.Item2)?.Version;
if (selection.Item1 > 0 && !rconParsers[selection.Item1 - 1].CanGenerateLogPath)
{
@ -70,11 +70,11 @@ namespace SharedLibraryCore.Configuration
}
parserVersions = eventParsers.Select(_parser => _parser.Name).ToArray();
selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_EVENT_PARSER_VERSION"]} ({IPAddress}:{Port})", $"{loc["SETUP_PROMPT_DEFAULT"]} (Call of Duty)", null, parserVersions);
selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_EVENT_PARSER_VERSION"]} ({IPAddress}:{Port})", parserVersions[0], null, parserVersions);
if (selection.Item1 >= 0)
{
EventParserVersion = eventParsers.First(_parser => _parser.Name == selection.Item2).Version;
EventParserVersion = eventParsers.FirstOrDefault(_parser => _parser.Name == selection.Item2)?.Version;
}
}

View File

@ -26,12 +26,6 @@ namespace SharedLibraryCore.Configuration.Validation
RuleForEach(_server => _server.AutoMessages)
.NotEmpty();
RuleFor(_server => _server.RConParserVersion)
.NotEmpty();
RuleFor(_server => _server.EventParserVersion)
.NotEmpty();
RuleFor(_server => _server.ReservedSlotNumber)
.InclusiveBetween(0, 32);
}

View File

@ -1,67 +0,0 @@
using Newtonsoft.Json;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Interfaces;
using System;
using System.IO;
using System.Threading.Tasks;
namespace SharedLibraryCore.Configuration
{
public class BaseConfigurationHandler<T> : IConfigurationHandler<T> where T : IBaseConfiguration
{
T _configuration;
public BaseConfigurationHandler(string fn)
{
FileName = Path.Join(Utilities.OperatingDirectory, "Configuration", $"{fn}.json");
Build();
}
public string FileName { get; }
public void Build()
{
try
{
var configContent = File.ReadAllText(FileName);
_configuration = JsonConvert.DeserializeObject<T>(configContent);
}
catch (FileNotFoundException)
{
_configuration = default;
}
catch (Exception e)
{
throw new ConfigurationException("MANAGER_CONFIGURATION_ERROR")
{
Errors = new[] { e.Message },
ConfigurationFileName = FileName
};
}
}
public Task Save()
{
var settings = new JsonSerializerSettings()
{
Formatting = Formatting.Indented
};
settings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
var appConfigJSON = JsonConvert.SerializeObject(_configuration, settings);
return File.WriteAllTextAsync(FileName, appConfigJSON);
}
public T Configuration()
{
return _configuration;
}
public void Set(T config)
{
_configuration = config;
}
}
}

View File

@ -0,0 +1,14 @@
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// defines the capabilities for providing a base path
/// unused as of now, will be used later during refactorying
/// </summary>
public interface IBasePathProvider
{
/// <summary>
/// working directory of IW4MAdmin
/// </summary>
string BasePath { get; }
}
}

View File

@ -0,0 +1,17 @@
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// defines the capabilities of the configuration handler factory
/// used to generate new instance of configuration handlers
/// </summary>
public interface IConfigurationHandlerFactory
{
/// <summary>
/// generates a new configuration handler
/// </summary>
/// <typeparam name="T">base configuration type</typeparam>
/// <param name="name">file name of configuration</param>
/// <returns>new configuration handler instance</returns>
IConfigurationHandler<T> GetConfigurationHandler<T>(string name) where T : IBaseConfiguration;
}
}

View File

@ -0,0 +1,18 @@
using SharedLibraryCore.Configuration;
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// defines the capabilities of game server instance factory
/// </summary>
public interface IGameServerInstanceFactory
{
/// <summary>
/// creates the instance of a game server
/// </summary>
/// <param name="config">server configuration</param>
/// <param name="manager">application manager</param>
/// <returns></returns>
Server CreateServer(ServerConfiguration config, IManager manager);
}
}

View File

@ -2,7 +2,6 @@
using System.Threading.Tasks;
using SharedLibraryCore.Services;
using SharedLibraryCore.Configuration;
using System.Reflection;
using SharedLibraryCore.Database.Models;
using System.Threading;
using System.Collections;
@ -29,7 +28,10 @@ namespace SharedLibraryCore.Interfaces
/// </summary>
/// <returns>EventHandler for the manager</returns>
IEventHandler GetEventHandler();
IList<Assembly> GetPluginAssemblies();
/// <summary>
/// enumerates the registered plugin instances
/// </summary>
IEnumerable<IPlugin> Plugins { get; }
/// <summary>
/// provides a page list to add and remove from
/// </summary>

View File

@ -1,32 +1,23 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// Defines the capabilities of the plugin importer
/// defines the capabilities of the plugin importer
/// </summary>
public interface IPluginImporter
{
{
/// <summary>
/// Command types that are defined in plugin assemblies
/// discovers C# assembly plugin and command types
/// </summary>
IList<Type> CommandTypes { get; }
/// <returns>tuple of IPlugin implementation type definitions, and IManagerCommand type definitions</returns>
(IEnumerable<Type>, IEnumerable<Type>) DiscoverAssemblyPluginImplementations();
/// <summary>
/// The loaded plugins from plugin assemblies
/// discovers the script plugins
/// </summary>
IList<IPlugin> ActivePlugins { get; }
/// <summary>
/// Assemblies that contain plugins
/// </summary>
IList<Assembly> PluginAssemblies { get; }
/// <summary>
/// All assemblies in the plugin folder
/// </summary>
IList<Assembly> Assemblies { get; }
/// <returns>initialized script plugin collection</returns>
IEnumerable<IPlugin> DiscoverScriptPlugins();
}
}

View File

@ -0,0 +1,25 @@
using SharedLibraryCore.RCon;
using System.Threading.Tasks;
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// defines the capabilities of an RCon connection
/// </summary>
public interface IRConConnection
{
/// <summary>
/// sends a query with the instance of the rcon connection
/// </summary>
/// <param name="type">type of RCon query to perform</param>
/// <param name="parameters">optional parameter list</param>
/// <returns></returns>
Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "");
/// <summary>
/// sets the rcon parser configuration
/// </summary>
/// <param name="config">parser config</param>
void SetConfiguration(IRConParserConfiguration config);
}
}

View File

@ -0,0 +1,17 @@
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// defines the capabilities of an RCon connection factory
/// </summary>
public interface IRConConnectionFactory
{
/// <summary>
/// creates an rcon connection instance
/// </summary>
/// <param name="ipAddress">ip address of the server</param>
/// <param name="port">port of the server</param>
/// <param name="password"> password of the server</param>
/// <returns>instance of rcon connection</returns>
IRConConnection CreateConnection(string ipAddress, int port, string password);
}
}

View File

@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.RCon;
using static SharedLibraryCore.Server;
namespace SharedLibraryCore.Interfaces
@ -15,7 +14,7 @@ namespace SharedLibraryCore.Interfaces
/// <param name="connection">RCon connection to retrieve with</param>
/// <param name="dvarName">name of DVAR</param>
/// <returns></returns>
Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName);
Task<Dvar<T>> GetDvarAsync<T>(IRConConnection connection, string dvarName);
/// <summary>
/// set value of DVAR by name
@ -24,7 +23,7 @@ namespace SharedLibraryCore.Interfaces
/// <param name="dvarName">name of DVAR to set</param>
/// <param name="dvarValue">value to set DVAR to</param>
/// <returns></returns>
Task<bool> SetDvarAsync(Connection connection, string dvarName, object dvarValue);
Task<bool> SetDvarAsync(IRConConnection connection, string dvarName, object dvarValue);
/// <summary>
/// executes a console command on the server
@ -32,14 +31,14 @@ namespace SharedLibraryCore.Interfaces
/// <param name="connection">RCon connection to use</param>
/// <param name="command">console command to execute</param>
/// <returns></returns>
Task<string[]> ExecuteCommandAsync(Connection connection, string command);
Task<string[]> ExecuteCommandAsync(IRConConnection connection, string command);
/// <summary>
/// get the list of connected clients from status response
/// </summary>
/// <param name="connection">RCon connection to use</param>
/// <returns>list of clients, and current map</returns>
Task<(List<EFClient>, string)> GetStatusAsync(Connection connection);
Task<(List<EFClient>, string)> GetStatusAsync(IRConConnection connection);
/// <summary>
/// stores the RCon configuration

View File

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SharedLibraryCore.RCon
namespace SharedLibraryCore.RCon
{
public class CommandPrefix
{

View File

@ -1,285 +0,0 @@
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SharedLibraryCore.RCon
{
class ConnectionState
{
~ConnectionState()
{
OnComplete.Dispose();
OnSentData.Dispose();
OnReceivedData.Dispose();
}
public int ConnectionAttempts { get; set; }
const int BufferSize = 4096;
public readonly byte[] ReceiveBuffer = new byte[BufferSize];
public readonly SemaphoreSlim OnComplete = new SemaphoreSlim(1, 1);
public readonly ManualResetEventSlim OnSentData = new ManualResetEventSlim(false);
public readonly ManualResetEventSlim OnReceivedData = new ManualResetEventSlim(false);
public SocketAsyncEventArgs SendEventArgs { get; set; } = new SocketAsyncEventArgs();
public SocketAsyncEventArgs ReceiveEventArgs { get; set; } = new SocketAsyncEventArgs();
public DateTime LastQuery { get; set; } = DateTime.Now;
}
public class Connection
{
static readonly ConcurrentDictionary<EndPoint, ConnectionState> ActiveQueries = new ConcurrentDictionary<EndPoint, ConnectionState>();
public IPEndPoint Endpoint { get; private set; }
public string RConPassword { get; private set; }
private readonly ILogger Log;
private IRConParserConfiguration Config;
private readonly Encoding defaultEncoding;
public Connection(string ipAddress, int port, string password, ILogger log, IRConParserConfiguration config)
{
Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
defaultEncoding = Encoding.GetEncoding("windows-1252");
RConPassword = password;
Log = log;
Config = config;
}
public void SetConfiguration(IRConParserConfiguration config)
{
Config = config;
}
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "")
{
if (!ActiveQueries.ContainsKey(this.Endpoint))
{
ActiveQueries.TryAdd(this.Endpoint, new ConnectionState());
}
var connectionState = ActiveQueries[this.Endpoint];
#if DEBUG == true
Log.WriteDebug($"Waiting for semaphore to be released [{this.Endpoint}]");
#endif
// enter the semaphore so only one query is sent at a time per server.
await connectionState.OnComplete.WaitAsync();
var timeSinceLastQuery = (DateTime.Now - connectionState.LastQuery).TotalMilliseconds;
if (timeSinceLastQuery < StaticHelpers.FloodProtectionInterval)
{
await Task.Delay(StaticHelpers.FloodProtectionInterval - (int)timeSinceLastQuery);
}
connectionState.LastQuery = DateTime.Now;
#if DEBUG == true
Log.WriteDebug($"Semaphore has been released [{this.Endpoint}]");
Log.WriteDebug($"Query [{this.Endpoint},{type.ToString()},{parameters}]");
#endif
byte[] payload = null;
bool waitForResponse = Config.WaitForResponse;
string convertEncoding(string text)
{
byte[] convertedBytes = Utilities.EncodingType.GetBytes(text);
return defaultEncoding.GetString(convertedBytes);
}
try
{
string convertedRConPassword = convertEncoding(RConPassword);
string convertedParameters = convertEncoding(parameters);
switch (type)
{
case StaticHelpers.QueryType.GET_DVAR:
waitForResponse |= true;
payload = string.Format(Config.CommandPrefixes.RConGetDvar, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
break;
case StaticHelpers.QueryType.SET_DVAR:
payload = string.Format(Config.CommandPrefixes.RConSetDvar, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
break;
case StaticHelpers.QueryType.COMMAND:
payload = string.Format(Config.CommandPrefixes.RConCommand, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
break;
case StaticHelpers.QueryType.GET_STATUS:
waitForResponse |= true;
payload = (Config.CommandPrefixes.RConGetStatus + '\0').Select(Convert.ToByte).ToArray();
break;
case StaticHelpers.QueryType.GET_INFO:
waitForResponse |= true;
payload = (Config.CommandPrefixes.RConGetInfo + '\0').Select(Convert.ToByte).ToArray();
break;
case StaticHelpers.QueryType.COMMAND_STATUS:
waitForResponse |= true;
payload = string.Format(Config.CommandPrefixes.RConCommand, convertedRConPassword, "status\0").Select(Convert.ToByte).ToArray();
break;
}
}
// this happens when someone tries to send something that can't be converted into a 7 bit character set
// e.g: emoji -> windows-1252
catch (OverflowException)
{
connectionState.OnComplete.Release(1);
throw new NetworkException($"Invalid character encountered when converting encodings - {parameters}");
}
byte[] response = null;
retrySend:
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
{
DontFragment = true,
Ttl = 100,
ExclusiveAddressUse = true,
})
{
connectionState.SendEventArgs.UserToken = socket;
connectionState.OnSentData.Reset();
connectionState.OnReceivedData.Reset();
connectionState.ConnectionAttempts++;
#if DEBUG == true
Log.WriteDebug($"Sending {payload.Length} bytes to [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
#endif
try
{
response = await SendPayloadAsync(payload, waitForResponse);
if (response.Length == 0 && waitForResponse)
{
throw new NetworkException("Expected response but got 0 bytes back");
}
connectionState.ConnectionAttempts = 0;
}
catch
{
if (connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
{
await Task.Delay(StaticHelpers.FloodProtectionInterval);
goto retrySend;
}
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"].FormatExt(Endpoint));
}
finally
{
if (connectionState.OnComplete.CurrentCount == 0)
{
connectionState.OnComplete.Release(1);
}
}
}
string responseString = defaultEncoding.GetString(response, 0, response.Length) + '\n';
// note: not all games respond if the pasword is wrong or not set
if (responseString.Contains("Invalid password") || responseString.Contains("rconpassword"))
{
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_INVALID"]);
}
if (responseString.Contains("rcon_password"))
{
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_NOTSET"]);
}
if (responseString.Contains(Config.ServerNotRunningResponse))
{
throw new ServerException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_NOT_RUNNING"].FormatExt(Endpoint.ToString()));
}
string[] splitResponse = responseString.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim())
.ToArray();
return splitResponse;
}
private async Task<byte[]> SendPayloadAsync(byte[] payload, bool waitForResponse)
{
var connectionState = ActiveQueries[this.Endpoint];
var rconSocket = (Socket)connectionState.SendEventArgs.UserToken;
if (connectionState.ReceiveEventArgs.RemoteEndPoint == null &&
connectionState.SendEventArgs.RemoteEndPoint == null)
{
// setup the event handlers only once because we're reusing the event args
connectionState.SendEventArgs.Completed += OnDataSent;
connectionState.ReceiveEventArgs.Completed += OnDataReceived;
connectionState.SendEventArgs.RemoteEndPoint = this.Endpoint;
connectionState.ReceiveEventArgs.RemoteEndPoint = this.Endpoint;
connectionState.ReceiveEventArgs.DisconnectReuseSocket = true;
connectionState.SendEventArgs.DisconnectReuseSocket = true;
}
connectionState.SendEventArgs.SetBuffer(payload);
// send the data to the server
bool sendDataPending = rconSocket.SendToAsync(connectionState.SendEventArgs);
if (sendDataPending)
{
// the send has not been completed asyncronously
if (!await Task.Run(() => connectionState.OnSentData.Wait(StaticHelpers.SocketTimeout)))
{
rconSocket.Close();
throw new NetworkException("Timed out sending data", rconSocket);
}
}
if (!waitForResponse)
{
return new byte[0];
}
connectionState.ReceiveEventArgs.SetBuffer(connectionState.ReceiveBuffer);
// get our response back
bool receiveDataPending = rconSocket.ReceiveFromAsync(connectionState.ReceiveEventArgs);
if (receiveDataPending)
{
if (!await Task.Run(() => connectionState.OnReceivedData.Wait(StaticHelpers.SocketTimeout)))
{
rconSocket.Close();
throw new NetworkException("Timed out waiting for response", rconSocket);
}
}
rconSocket.Close();
byte[] response = connectionState.ReceiveBuffer
.Take(connectionState.ReceiveEventArgs.BytesTransferred)
.ToArray();
return response;
}
private void OnDataReceived(object sender, SocketAsyncEventArgs e)
{
#if DEBUG == true
Log.WriteDebug($"Read {e.BytesTransferred} bytes from {e.RemoteEndPoint.ToString()}");
#endif
ActiveQueries[this.Endpoint].OnReceivedData.Set();
}
private void OnDataSent(object sender, SocketAsyncEventArgs e)
{
#if DEBUG == true
Log.WriteDebug($"Sent {e.Buffer?.Length} bytes to {e.ConnectSocket?.RemoteEndPoint?.ToString()}");
#endif
ActiveQueries[this.Endpoint].OnSentData.Set();
}
}
}

View File

@ -1,186 +0,0 @@
using Jint;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SharedLibraryCore
{
public class ScriptPlugin : IPlugin
{
public string Name { get; set; }
public float Version { get; set; }
public string Author { get; set; }
public FileSystemWatcher Watcher { get; private set; }
private Engine _scriptEngine;
private readonly string _fileName;
private readonly SemaphoreSlim _onProcessing;
private bool successfullyLoaded;
public ScriptPlugin(string filename)
{
_fileName = filename;
Watcher = new FileSystemWatcher()
{
Path = $"{Utilities.OperatingDirectory}Plugins{Path.DirectorySeparatorChar}",
NotifyFilter = NotifyFilters.Size,
Filter = _fileName.Split(Path.DirectorySeparatorChar).Last()
};
Watcher.EnableRaisingEvents = true;
_onProcessing = new SemaphoreSlim(1, 1);
}
~ScriptPlugin()
{
Watcher.Dispose();
_onProcessing.Dispose();
}
public async Task Initialize(IManager manager)
{
await _onProcessing.WaitAsync();
try
{
// 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)
{
return;
}
bool firstRun = _scriptEngine == null;
// it's been loaded before so we need to call the unload event
if (!firstRun)
{
await OnUnloadAsync();
}
successfullyLoaded = false;
string script;
using (var stream = new FileStream(_fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var reader = new StreamReader(stream, Encoding.Default))
{
script = await reader.ReadToEndAsync();
}
}
_scriptEngine = new Engine(cfg =>
cfg.AllowClr(new[]
{
typeof(System.Net.Http.HttpClient).Assembly,
typeof(EFClient).Assembly,
typeof(Utilities).Assembly,
typeof(Encoding).Assembly
})
.CatchClrExceptions());
_scriptEngine.Execute(script);
_scriptEngine.SetValue("_localization", Utilities.CurrentLocalization);
dynamic pluginObject = _scriptEngine.GetValue("plugin").ToObject();
Author = pluginObject.author;
Name = pluginObject.name;
Version = (float)pluginObject.version;
try
{
if (pluginObject.isParser)
{
await OnLoadAsync(manager);
IEventParser eventParser = (IEventParser)_scriptEngine.GetValue("eventParser").ToObject();
IRConParser rconParser = (IRConParser)_scriptEngine.GetValue("rconParser").ToObject();
manager.AdditionalEventParsers.Add(eventParser);
manager.AdditionalRConParsers.Add(rconParser);
}
}
catch { }
if (!firstRun)
{
await OnLoadAsync(manager);
}
successfullyLoaded = true;
}
catch
{
throw;
}
finally
{
if (_onProcessing.CurrentCount == 0)
{
_onProcessing.Release(1);
}
}
}
public async Task OnEventAsync(GameEvent E, Server S)
{
if (successfullyLoaded)
{
await _onProcessing.WaitAsync();
try
{
_scriptEngine.SetValue("_gameEvent", E);
_scriptEngine.SetValue("_server", S);
_scriptEngine.SetValue("_IW4MAdminClient", Utilities.IW4MAdminClient(S));
await Task.FromResult(_scriptEngine.Execute("plugin.onEventAsync(_gameEvent, _server)").GetCompletionValue());
}
catch
{
throw;
}
finally
{
if (_onProcessing.CurrentCount == 0)
{
_onProcessing.Release(1);
}
}
}
}
public Task OnLoadAsync(IManager manager)
{
manager.GetLogger(0).WriteDebug($"OnLoad executing for {Name}");
_scriptEngine.SetValue("_manager", manager);
return Task.FromResult(_scriptEngine.Execute("plugin.onLoadAsync(_manager)").GetCompletionValue());
}
public Task OnTickAsync(Server S)
{
_scriptEngine.SetValue("_server", S);
return Task.FromResult(_scriptEngine.Execute("plugin.onTickAsync(_server)").GetCompletionValue());
}
public async Task OnUnloadAsync()
{
if (successfullyLoaded)
{
await Task.FromResult(_scriptEngine.Execute("plugin.onUnloadAsync()").GetCompletionValue());
}
}
}
}

View File

@ -28,7 +28,7 @@ namespace SharedLibraryCore
T7 = 8
}
public Server(IManager mgr, ServerConfiguration config)
public Server(IManager mgr, IRConConnectionFactory rconConnectionFactory, ServerConfiguration config)
{
Password = config.Password;
IP = config.IPAddress;
@ -37,8 +37,7 @@ namespace SharedLibraryCore
Logger = Manager.GetLogger(this.EndPoint);
Logger.WriteInfo(this.ToString());
ServerConfig = config;
RemoteConnection = new RCon.Connection(IP, Port, Password, Logger, null);
RemoteConnection = rconConnectionFactory.CreateConnection(IP, Port, Password);
EventProcessing = new SemaphoreSlim(1, 1);
Clients = new List<EFClient>(new EFClient[18]);
Reports = new List<Report>();
@ -283,7 +282,7 @@ namespace SharedLibraryCore
public IManager Manager { get; protected set; }
public ILogger Logger { get; private set; }
public ServerConfiguration ServerConfig { get; private set; }
public List<Map> Maps { get; protected set; }
public List<Map> Maps { get; protected set; } = new List<Map>();
public List<Report> Reports { get; set; }
public List<ChatInfo> ChatHistory { get; protected set; }
public Queue<PlayerHistory> ClientHistory { get; private set; }
@ -307,7 +306,7 @@ namespace SharedLibraryCore
public bool Throttled { get; protected set; }
public bool CustomCallback { get; protected set; }
public string WorkingDirectory { get; protected set; }
public RCon.Connection RemoteConnection { get; protected set; }
public IRConConnection RemoteConnection { get; protected set; }
public IRConParser RconParser { get; protected set; }
public IEventParser EventParser { get; set; }
public string LogPath { get; protected set; }

View File

@ -6,7 +6,7 @@
<ApplicationIcon />
<StartupObject />
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
<Version>2.2.6</Version>
<Version>2.2.7</Version>
<Authors>RaidMax</Authors>
<Company>Forever None</Company>
<Configurations>Debug;Release;Prerelease</Configurations>
@ -20,8 +20,8 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Description>Shared Library for IW4MAdmin</Description>
<AssemblyVersion>2.2.6.0</AssemblyVersion>
<FileVersion>2.2.6.0</FileVersion>
<AssemblyVersion>2.2.7.0</AssemblyVersion>
<FileVersion>2.2.7.0</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Prerelease|AnyCPU'">
@ -68,6 +68,10 @@
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.0" />
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug'">
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.1" />
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="if not exist &quot;$(ProjectDir)..\BUILD&quot; (&#xD;&#xA;if $(ConfigurationName) == Debug (&#xD;&#xA;md &quot;$(ProjectDir)..\BUILD&quot;&#xD;&#xA;)&#xD;&#xA;)&#xD;&#xA;if not exist &quot;$(ProjectDir)..\BUILD\Plugins&quot; (&#xD;&#xA;if $(ConfigurationName) == Debug (&#xD;&#xA;md &quot;$(ProjectDir)..\BUILD\Plugins&quot;&#xD;&#xA;)&#xD;&#xA;)" />