fix alias command sending message to origin instead of target
(hopefully) fix an issue with banned players causing exception if they create events before they are kicked out fix issues with sometimes wrong error message for timeout show most recent IP address at top of alias list optimization to some sql queries
This commit is contained in:
parent
cb9119ac58
commit
161b27e2f2
@ -33,8 +33,6 @@ namespace IW4MAdmin.Application
|
||||
public ILogger Logger => GetLogger(0);
|
||||
public bool Running { get; private set; }
|
||||
public bool IsInitialized { get; private set; }
|
||||
// expose the event handler so we can execute the events
|
||||
public OnServerEventEventHandler OnServerEvent { get; set; }
|
||||
public DateTime StartTime { get; private set; }
|
||||
public string Version => Assembly.GetEntryAssembly().GetName().Version.ToString();
|
||||
|
||||
@ -73,21 +71,19 @@ namespace IW4MAdmin.Application
|
||||
PageList = new PageList();
|
||||
AdditionalEventParsers = new List<IEventParser>();
|
||||
AdditionalRConParsers = new List<IRConParser>();
|
||||
OnServerEvent += OnGameEvent;
|
||||
OnServerEvent += EventApi.OnGameEvent;
|
||||
//OnServerEvent += OnGameEvent;
|
||||
//OnServerEvent += EventApi.OnGameEvent;
|
||||
TokenAuthenticator = new TokenAuthentication();
|
||||
_metaService = new MetaService();
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private async void OnGameEvent(object sender, GameEventArgs args)
|
||||
public async Task ExecuteEvent(GameEvent newEvent)
|
||||
{
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"Entering event process for {args.Event.Id}");
|
||||
Logger.WriteDebug($"Entering event process for {newEvent.Id}");
|
||||
#endif
|
||||
|
||||
var newEvent = args.Event;
|
||||
|
||||
// the event has failed already
|
||||
if (newEvent.Failed)
|
||||
{
|
||||
@ -96,12 +92,11 @@ namespace IW4MAdmin.Application
|
||||
|
||||
try
|
||||
{
|
||||
await newEvent.Owner.EventProcessing.WaitAsync(CancellationToken);
|
||||
await newEvent.Owner.ExecuteEvent(newEvent);
|
||||
|
||||
// save the event info to the database
|
||||
var changeHistorySvc = new ChangeHistoryService();
|
||||
await changeHistorySvc.Add(args.Event);
|
||||
await changeHistorySvc.Add(newEvent);
|
||||
|
||||
#if DEBUG
|
||||
Logger.WriteDebug($"Processed event with id {newEvent.Id}");
|
||||
@ -145,22 +140,12 @@ namespace IW4MAdmin.Application
|
||||
Logger.WriteDebug(ex.GetExceptionInfo());
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
if (newEvent.Owner.EventProcessing.CurrentCount == 0)
|
||||
{
|
||||
newEvent.Owner.EventProcessing.Release(1);
|
||||
}
|
||||
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"Exiting event process for {args.Event.Id}");
|
||||
#endif
|
||||
}
|
||||
|
||||
skip:
|
||||
|
||||
// tell anyone waiting for the output that we're done
|
||||
newEvent.OnProcessed.Set();
|
||||
newEvent.Complete();
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"Exiting event process for {newEvent.Id}");
|
||||
#endif
|
||||
}
|
||||
|
||||
public IList<Server> GetServers()
|
||||
|
@ -256,6 +256,7 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
// this is a custom event printed out by _customcallbacks.gsc (used for anticheat)
|
||||
if (eventType == "ScriptKill")
|
||||
{
|
||||
|
||||
long originId = lineSplit[1].ConvertGuidToLong(1);
|
||||
long targetId = lineSplit[2].ConvertGuidToLong(1);
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
using SharedLibraryCore;
|
||||
using IW4MAdmin.Application.Misc;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Events;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
@ -9,7 +11,11 @@ namespace IW4MAdmin.Application
|
||||
class GameEventHandler : IEventHandler
|
||||
{
|
||||
readonly ApplicationManager Manager;
|
||||
private static GameEvent.EventType[] overrideEvents = new[]
|
||||
private readonly EventProfiler _profiler;
|
||||
private delegate void GameEventAddedEventHandler(object sender, GameEventArgs args);
|
||||
private event GameEventAddedEventHandler GameEventAdded;
|
||||
|
||||
private static readonly GameEvent.EventType[] overrideEvents = new[]
|
||||
{
|
||||
GameEvent.EventType.Connect,
|
||||
GameEvent.EventType.Disconnect,
|
||||
@ -20,6 +26,17 @@ namespace IW4MAdmin.Application
|
||||
public GameEventHandler(IManager mgr)
|
||||
{
|
||||
Manager = (ApplicationManager)mgr;
|
||||
_profiler = new EventProfiler(mgr.GetLogger(0));
|
||||
GameEventAdded += GameEventHandler_GameEventAdded;
|
||||
}
|
||||
|
||||
private async void GameEventHandler_GameEventAdded(object sender, GameEventArgs args)
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
await Manager.ExecuteEvent(args.Event);
|
||||
#if DEBUG
|
||||
_profiler.Profile(start, DateTime.Now, args.Event);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void AddEvent(GameEvent gameEvent)
|
||||
@ -35,7 +52,7 @@ namespace IW4MAdmin.Application
|
||||
#if DEBUG
|
||||
gameEvent.Owner.Logger.WriteDebug($"Adding event with id {gameEvent.Id}");
|
||||
#endif
|
||||
Manager.OnServerEvent?.Invoke(gameEvent.Owner, new GameEventArgs(null, false, gameEvent));
|
||||
GameEventAdded?.Invoke(this, new GameEventArgs(null, false, gameEvent));
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
|
@ -76,7 +76,6 @@ namespace IW4MAdmin.Application.IO
|
||||
#if DEBUG
|
||||
_server.Logger.WriteVerbose(gameEvent.Data);
|
||||
#endif
|
||||
|
||||
// we don't want to add the event if ignoreBots is on and the event comes from a bot
|
||||
if (!_ignoreBots || (_ignoreBots && !((gameEvent.Origin?.IsBot ?? false) || (gameEvent.Target?.IsBot ?? false))))
|
||||
{
|
||||
@ -103,11 +102,6 @@ namespace IW4MAdmin.Application.IO
|
||||
}
|
||||
|
||||
_server.Manager.GetEventHandler().AddEvent(gameEvent);
|
||||
|
||||
if (gameEvent.IsBlocking)
|
||||
{
|
||||
await gameEvent.WaitAsync(Utilities.DefaultCommandTimeout, _server.Manager.CancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
}
|
||||
|
||||
override public async Task OnClientConnected(EFClient clientFromLog)
|
||||
override public async Task<EFClient> OnClientConnected(EFClient clientFromLog)
|
||||
{
|
||||
Logger.WriteDebug($"Client slot #{clientFromLog.ClientNumber} now reserved");
|
||||
|
||||
@ -57,6 +57,7 @@ namespace IW4MAdmin
|
||||
Logger.WriteInfo($"Client {client} connected...");
|
||||
|
||||
// Do the player specific stuff
|
||||
client.ProcessingEvent = clientFromLog.ProcessingEvent;
|
||||
client.ClientNumber = clientFromLog.ClientNumber;
|
||||
client.Score = clientFromLog.Score;
|
||||
client.Ping = clientFromLog.Ping;
|
||||
@ -73,9 +74,8 @@ namespace IW4MAdmin
|
||||
Type = GameEvent.EventType.Connect
|
||||
};
|
||||
|
||||
await client.OnJoin(client.IPAddress);
|
||||
client.State = ClientState.Connected;
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
return client;
|
||||
}
|
||||
|
||||
override public async Task OnClientDisconnected(EFClient client)
|
||||
@ -103,55 +103,85 @@ namespace IW4MAdmin
|
||||
|
||||
public override async Task ExecuteEvent(GameEvent E)
|
||||
{
|
||||
bool canExecuteCommand = true;
|
||||
|
||||
if (!await ProcessEvent(E))
|
||||
if (E == null)
|
||||
{
|
||||
Logger.WriteError("Received NULL event");
|
||||
return;
|
||||
}
|
||||
|
||||
Command C = null;
|
||||
if (E.Type == GameEvent.EventType.Command)
|
||||
if (E.IsBlocking)
|
||||
{
|
||||
try
|
||||
await E.Origin?.Lock();
|
||||
}
|
||||
|
||||
bool canExecuteCommand = true;
|
||||
Exception lastException = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (!await ProcessEvent(E))
|
||||
{
|
||||
C = await SharedLibraryCore.Commands.CommandProcessing.ValidateCommand(E);
|
||||
return;
|
||||
}
|
||||
|
||||
catch (CommandException e)
|
||||
Command C = null;
|
||||
if (E.Type == GameEvent.EventType.Command)
|
||||
{
|
||||
Logger.WriteInfo(e.Message);
|
||||
try
|
||||
{
|
||||
C = await SharedLibraryCore.Commands.CommandProcessing.ValidateCommand(E);
|
||||
}
|
||||
|
||||
catch (CommandException e)
|
||||
{
|
||||
Logger.WriteInfo(e.Message);
|
||||
}
|
||||
|
||||
if (C != null)
|
||||
{
|
||||
E.Extra = C;
|
||||
}
|
||||
}
|
||||
|
||||
if (C != null)
|
||||
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
||||
{
|
||||
E.Extra = C;
|
||||
try
|
||||
{
|
||||
await plugin.OnEventAsync(E, this);
|
||||
}
|
||||
catch (AuthorizationException e)
|
||||
{
|
||||
E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
|
||||
canExecuteCommand = false;
|
||||
}
|
||||
catch (Exception Except)
|
||||
{
|
||||
Logger.WriteError($"{loc["SERVER_PLUGIN_ERROR"]} [{plugin.Name}]");
|
||||
Logger.WriteDebug(Except.GetExceptionInfo());
|
||||
}
|
||||
}
|
||||
|
||||
// hack: this prevents commands from getting executing that 'shouldn't' be
|
||||
if (E.Type == GameEvent.EventType.Command && E.Extra is Command command &&
|
||||
(canExecuteCommand || E.Origin?.Level == Permission.Console))
|
||||
{
|
||||
await command.ExecuteAsync(E);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
||||
catch (Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
await plugin.OnEventAsync(E, this);
|
||||
}
|
||||
catch (AuthorizationException e)
|
||||
{
|
||||
E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
|
||||
canExecuteCommand = false;
|
||||
}
|
||||
catch (Exception Except)
|
||||
{
|
||||
Logger.WriteError($"{loc["SERVER_PLUGIN_ERROR"]} [{plugin.Name}]");
|
||||
Logger.WriteDebug(Except.GetExceptionInfo());
|
||||
}
|
||||
lastException = e;
|
||||
}
|
||||
|
||||
// hack: this prevents commands from getting executing that 'shouldn't' be
|
||||
if (E.Type == GameEvent.EventType.Command && E.Extra is Command command &&
|
||||
(canExecuteCommand || E.Origin?.Level == Permission.Console))
|
||||
finally
|
||||
{
|
||||
await command.ExecuteAsync(E);
|
||||
E.Origin?.Unlock();
|
||||
|
||||
if (lastException != null)
|
||||
{
|
||||
throw lastException;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,6 +225,25 @@ namespace IW4MAdmin
|
||||
await Manager.GetClientService().UpdateLevel(newPermission, E.Target, E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Connect)
|
||||
{
|
||||
if (E.Origin.State != ClientState.Connected)
|
||||
{
|
||||
E.Origin.State = ClientState.Connected;
|
||||
E.Origin.LastConnection = DateTime.UtcNow;
|
||||
E.Origin.Connections += 1;
|
||||
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
{
|
||||
Name = E.Origin.Name,
|
||||
Message = "CONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await E.Origin.OnJoin(E.Origin.IPAddress);
|
||||
}
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.PreConnect)
|
||||
{
|
||||
// we don't want to track bots in the database at all if ignore bots is requested
|
||||
@ -230,7 +279,8 @@ namespace IW4MAdmin
|
||||
Clients[E.Origin.ClientNumber] = E.Origin;
|
||||
try
|
||||
{
|
||||
await OnClientConnected(E.Origin);
|
||||
E.Origin = await OnClientConnected(E.Origin);
|
||||
E.Target = E.Origin;
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
@ -242,13 +292,6 @@ namespace IW4MAdmin
|
||||
return false;
|
||||
}
|
||||
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
{
|
||||
Name = E.Origin.Name,
|
||||
Message = "CONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
|
||||
if (E.Origin.Level > EFClient.Permission.Moderator)
|
||||
{
|
||||
E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
|
||||
@ -624,7 +667,6 @@ namespace IW4MAdmin
|
||||
#endif
|
||||
|
||||
var polledClients = await PollPlayersAsync();
|
||||
var waiterList = new List<GameEvent>();
|
||||
|
||||
foreach (var disconnectingClient in polledClients[1])
|
||||
{
|
||||
@ -641,18 +683,9 @@ namespace IW4MAdmin
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
// wait until the disconnect event is complete
|
||||
// because we don't want to try to fill up a slot that's not empty yet
|
||||
waiterList.Add(e);
|
||||
await e.WaitAsync(Utilities.DefaultCommandTimeout, Manager.CancellationToken);
|
||||
}
|
||||
|
||||
// wait for all the disconnect tasks to finish
|
||||
foreach (var waiter in waiterList)
|
||||
{
|
||||
waiter.Wait();
|
||||
}
|
||||
|
||||
waiterList.Clear();
|
||||
// this are our new connecting clients
|
||||
foreach (var client in polledClients[0])
|
||||
{
|
||||
@ -671,16 +704,9 @@ namespace IW4MAdmin
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
waiterList.Add(e);
|
||||
await e.WaitAsync(Utilities.DefaultCommandTimeout, Manager.CancellationToken);
|
||||
}
|
||||
|
||||
// wait for all the connect tasks to finish
|
||||
foreach (var waiter in waiterList)
|
||||
{
|
||||
waiter.Wait();
|
||||
}
|
||||
|
||||
waiterList.Clear();
|
||||
// these are the clients that have updated
|
||||
foreach (var client in polledClients[2])
|
||||
{
|
||||
@ -692,12 +718,6 @@ namespace IW4MAdmin
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
waiterList.Add(e);
|
||||
}
|
||||
|
||||
foreach (var waiter in waiterList)
|
||||
{
|
||||
waiter.Wait();
|
||||
}
|
||||
|
||||
if (ConnectionErrors > 0)
|
||||
|
63
Application/Misc/EventProfiler.cs
Normal file
63
Application/Misc/EventProfiler.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace IW4MAdmin.Application.Misc
|
||||
{
|
||||
internal class EventPerformance
|
||||
{
|
||||
public long ExecutionTime { get; set; }
|
||||
public GameEvent Event { get; set; }
|
||||
public string EventInfo => $"{Event.Type}, {Event.FailReason}, {Event.IsBlocking}, {Event.Data}, {Event.Message}, {Event.Extra}";
|
||||
}
|
||||
|
||||
public class DuplicateKeyComparer<TKey> : IComparer<TKey> where TKey : IComparable
|
||||
{
|
||||
public int Compare(TKey x, TKey y)
|
||||
{
|
||||
int result = x.CompareTo(y);
|
||||
|
||||
if (result == 0)
|
||||
return 1;
|
||||
else
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
internal class EventProfiler
|
||||
{
|
||||
public double AverageEventTime { get; private set; }
|
||||
public double MaxEventTime => Events.Values.Last().ExecutionTime;
|
||||
public double MinEventTime => Events.Values[0].ExecutionTime;
|
||||
public int TotalEventCount => Events.Count;
|
||||
public SortedList<long, EventPerformance> Events { get; private set; } = new SortedList<long, EventPerformance>(new DuplicateKeyComparer<long>());
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public EventProfiler(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Profile(DateTime start, DateTime end, GameEvent gameEvent)
|
||||
{
|
||||
_logger.WriteDebug($"Starting profile of event {gameEvent.Id}");
|
||||
long executionTime = (long)Math.Round((end - start).TotalMilliseconds);
|
||||
|
||||
var perf = new EventPerformance()
|
||||
{
|
||||
Event = gameEvent,
|
||||
ExecutionTime = executionTime
|
||||
};
|
||||
|
||||
lock (Events)
|
||||
{
|
||||
Events.Add(executionTime, perf);
|
||||
}
|
||||
|
||||
AverageEventTime = (AverageEventTime * (TotalEventCount - 1) + executionTime) / TotalEventCount;
|
||||
_logger.WriteDebug($"Finished profile of event {gameEvent.Id}");
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IW4MAdmin.Application
|
||||
{
|
||||
@ -20,16 +19,21 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
|
||||
readonly string FileName;
|
||||
readonly SemaphoreSlim OnLogWriting;
|
||||
readonly ReaderWriterLockSlim WritingLock;
|
||||
static readonly short MAX_LOG_FILES = 10;
|
||||
|
||||
public Logger(string fn)
|
||||
{
|
||||
FileName = Path.Join(Utilities.OperatingDirectory, "Log", $"{fn}.log");
|
||||
OnLogWriting = new SemaphoreSlim(1, 1);
|
||||
WritingLock = new ReaderWriterLockSlim();
|
||||
RotateLogs();
|
||||
}
|
||||
|
||||
~Logger()
|
||||
{
|
||||
WritingLock.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// rotates logs when log is initialized
|
||||
/// </summary>
|
||||
@ -56,7 +60,7 @@ namespace IW4MAdmin.Application
|
||||
|
||||
void Write(string msg, LogType type)
|
||||
{
|
||||
OnLogWriting.Wait();
|
||||
WritingLock.EnterWriteLock();
|
||||
|
||||
string stringType = type.ToString();
|
||||
msg = msg.StripColors();
|
||||
@ -74,7 +78,7 @@ namespace IW4MAdmin.Application
|
||||
#if DEBUG
|
||||
// lets keep it simple and dispose of everything quickly as logging wont be that much (relatively)
|
||||
Console.WriteLine(LogLine);
|
||||
File.AppendAllText(FileName, $"{LogLine}{Environment.NewLine}");
|
||||
//File.AppendAllText(FileName, $"{LogLine}{Environment.NewLine}");
|
||||
//Debug.WriteLine(msg);
|
||||
#else
|
||||
if (type == LogType.Error || type == LogType.Verbose)
|
||||
@ -91,7 +95,7 @@ namespace IW4MAdmin.Application
|
||||
Console.WriteLine(ex.GetExceptionInfo());
|
||||
}
|
||||
|
||||
OnLogWriting.Release(1);
|
||||
WritingLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
public void WriteVerbose(string msg)
|
||||
|
@ -38,7 +38,7 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
},
|
||||
};
|
||||
|
||||
Configuration.Status.Pattern = @"^ *([0-9]+) +-?([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$";
|
||||
Configuration.Status.Pattern = @"^ *([0-9]+) +-?([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback|unknown) +(-*[0-9]+) +([0-9]+) *$";
|
||||
Configuration.Status.AddMapping(ParserRegex.GroupType.RConClientNumber, 1);
|
||||
Configuration.Status.AddMapping(ParserRegex.GroupType.RConScore, 2);
|
||||
Configuration.Status.AddMapping(ParserRegex.GroupType.RConPing, 3);
|
||||
|
@ -9,4 +9,4 @@ pip==10.0.1
|
||||
pytz==2018.9
|
||||
setuptools==39.0.1
|
||||
six==1.12.0
|
||||
Werkzeug==0.15.2
|
||||
Werkzeug==0.16.0
|
||||
|
@ -6,6 +6,7 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
@ -16,6 +17,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
public EFServer Server { get; private set; }
|
||||
private readonly Server _server;
|
||||
public bool IsTeamBased { get; set; }
|
||||
public SemaphoreSlim OnSaving { get; private set; }
|
||||
|
||||
public ServerStats(EFServer sv, EFServerStatistics st, Server server)
|
||||
{
|
||||
@ -23,6 +25,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
ServerStatistics = st;
|
||||
Server = sv;
|
||||
_server = server;
|
||||
OnSaving = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
~ServerStats()
|
||||
{
|
||||
OnSaving.Dispose();
|
||||
}
|
||||
|
||||
public int TeamCount(IW4Info.Team teamName)
|
||||
|
@ -410,6 +410,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
Vector3 vDeathOrigin = null;
|
||||
Vector3 vKillOrigin = null;
|
||||
Vector3 vViewAngles = null;
|
||||
var snapshotAngles = new List<Vector3>();
|
||||
SemaphoreSlim waiter = null;
|
||||
|
||||
try
|
||||
@ -419,20 +420,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
vDeathOrigin = Vector3.Parse(deathOrigin);
|
||||
vKillOrigin = Vector3.Parse(killOrigin);
|
||||
vViewAngles = Vector3.Parse(viewAngles).FixIW4Angles();
|
||||
}
|
||||
|
||||
catch (FormatException)
|
||||
{
|
||||
_log.WriteWarning("Could not parse kill or death origin or viewangle vectors");
|
||||
_log.WriteDebug($"Kill - {killOrigin} Death - {deathOrigin} ViewAngle - {viewAngles}");
|
||||
await AddStandardKill(attacker, victim);
|
||||
return;
|
||||
}
|
||||
|
||||
var snapshotAngles = new List<Vector3>();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (string angle in snapAngles.Split(':', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
snapshotAngles.Add(Vector3.Parse(angle).FixIW4Angles());
|
||||
@ -441,7 +429,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
catch (FormatException)
|
||||
{
|
||||
_log.WriteWarning("Could not parse snapshot angles");
|
||||
_log.WriteError("Could not parse vector data from hit");
|
||||
_log.WriteDebug($"Kill - {killOrigin} Death - {deathOrigin} ViewAngle - {viewAngles} Snapshot - {string.Join(",", snapshotAngles.Select(_a => _a.ToString()))}");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -478,7 +467,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
var clientStats = attacker.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY);
|
||||
|
||||
waiter = clientStats.ProcessingHit;
|
||||
await waiter.WaitAsync();
|
||||
await waiter.WaitAsync(Utilities.DefaultCommandTimeout, Plugin.ServerManager.CancellationToken);
|
||||
|
||||
// increment their hit count
|
||||
if (hit.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET ||
|
||||
@ -495,12 +484,31 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
if (Plugin.Config.Configuration().StoreClientKills)
|
||||
{
|
||||
var cache = _servers[serverId].HitCache;
|
||||
cache.Add(hit);
|
||||
|
||||
if (cache.Count > MAX_CACHED_HITS)
|
||||
var serverWaiter = _servers[serverId].OnSaving;
|
||||
try
|
||||
{
|
||||
await SaveHitCache(serverId);
|
||||
await serverWaiter.WaitAsync();
|
||||
var cache = _servers[serverId].HitCache;
|
||||
cache.Add(hit);
|
||||
|
||||
if (cache.Count > MAX_CACHED_HITS)
|
||||
{
|
||||
await SaveHitCache(serverId);
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.WriteError("Could not store client kills");
|
||||
_log.WriteDebug(e.GetExceptionInfo());
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
if (serverWaiter.CurrentCount == 0)
|
||||
{
|
||||
serverWaiter.Release(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -538,9 +546,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
await Sync(attacker.CurrentServer);
|
||||
#endif
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
@ -552,7 +557,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
finally
|
||||
{
|
||||
waiter?.Release(1);
|
||||
if (waiter?.CurrentCount == 0)
|
||||
{
|
||||
waiter.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -561,7 +569,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
using (var ctx = new DatabaseContext(true))
|
||||
{
|
||||
var server = _servers[serverId];
|
||||
ctx.AddRange(server.HitCache);
|
||||
ctx.AddRange(server.HitCache.ToList());
|
||||
await ctx.SaveChangesAsync();
|
||||
server.HitCache.Clear();
|
||||
}
|
||||
@ -1110,21 +1118,41 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
long serverId = GetIdForServer(sv);
|
||||
|
||||
using (var ctx = new DatabaseContext())
|
||||
var waiter = _servers[serverId].OnSaving;
|
||||
try
|
||||
{
|
||||
var serverStatsSet = ctx.Set<EFServerStatistics>();
|
||||
serverStatsSet.Update(_servers[serverId].ServerStatistics);
|
||||
await ctx.SaveChangesAsync();
|
||||
await waiter.WaitAsync();
|
||||
|
||||
using (var ctx = new DatabaseContext())
|
||||
{
|
||||
var serverStatsSet = ctx.Set<EFServerStatistics>();
|
||||
serverStatsSet.Update(_servers[serverId].ServerStatistics);
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
foreach (var stats in sv.GetClientsAsList()
|
||||
.Select(_client => _client.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY))
|
||||
.Where(_stats => _stats != null))
|
||||
{
|
||||
await SaveClientStats(stats);
|
||||
}
|
||||
|
||||
await SaveHitCache(serverId);
|
||||
}
|
||||
|
||||
foreach (var stats in sv.GetClientsAsList()
|
||||
.Select(_client => _client.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY))
|
||||
.Where(_stats => _stats != null))
|
||||
catch (Exception e)
|
||||
{
|
||||
await SaveClientStats(stats);
|
||||
_log.WriteError("There was a probably syncing server stats");
|
||||
_log.WriteDebug(e.GetExceptionInfo());
|
||||
}
|
||||
|
||||
await SaveHitCache(serverId);
|
||||
finally
|
||||
{
|
||||
if (waiter.CurrentCount == 0)
|
||||
{
|
||||
waiter.Release(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTeamBased(long serverId, bool isTeamBased)
|
||||
|
@ -43,7 +43,7 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
break;
|
||||
case GameEvent.EventType.Stop:
|
||||
break;
|
||||
case GameEvent.EventType.Connect:
|
||||
case GameEvent.EventType.PreConnect:
|
||||
await Manager.AddPlayer(E.Origin);
|
||||
break;
|
||||
case GameEvent.EventType.Disconnect:
|
||||
|
@ -73,7 +73,7 @@ namespace Tests
|
||||
};
|
||||
|
||||
_manager.GetEventHandler().AddEvent(e);
|
||||
e.OnProcessed.Wait();
|
||||
e.Complete();
|
||||
|
||||
e = new GameEvent()
|
||||
{
|
||||
@ -92,7 +92,7 @@ namespace Tests
|
||||
};
|
||||
|
||||
_manager.GetEventHandler().AddEvent(e);
|
||||
e.OnProcessed.Wait();
|
||||
e.Complete();
|
||||
|
||||
e = new GameEvent()
|
||||
{
|
||||
@ -111,7 +111,7 @@ namespace Tests
|
||||
};
|
||||
|
||||
_manager.GetEventHandler().AddEvent(e);
|
||||
e.OnProcessed.Wait();
|
||||
e.Complete();
|
||||
|
||||
}
|
||||
|
||||
@ -126,13 +126,13 @@ namespace Tests
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
_manager.OnServerEvent += (sender, eventArgs) =>
|
||||
{
|
||||
if (eventArgs.Event.Type == GameEvent.EventType.Connect)
|
||||
{
|
||||
onJoined.Set();
|
||||
}
|
||||
};
|
||||
//_manager.OnServerEvent += (sender, eventArgs) =>
|
||||
//{
|
||||
// if (eventArgs.Event.Type == GameEvent.EventType.Connect)
|
||||
// {
|
||||
// onJoined.Set();
|
||||
// }
|
||||
//};
|
||||
|
||||
server.EmulateClientJoinLog();
|
||||
onJoined.Wait();
|
||||
@ -140,25 +140,25 @@ namespace Tests
|
||||
var client = server.Clients[0];
|
||||
|
||||
var warnEvent = client.Warn("test warn", Utilities.IW4MAdminClient(server));
|
||||
warnEvent.OnProcessed.Wait(5000);
|
||||
warnEvent.WaitAsync(new TimeSpan(0, 0, 10), new CancellationToken()).Wait();
|
||||
|
||||
Assert.False(warnEvent.Failed);
|
||||
|
||||
warnEvent = client.Warn("test warn", new EFClient() { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
warnEvent.OnProcessed.Wait(5000);
|
||||
warnEvent.WaitAsync(new TimeSpan(0, 0, 10), new CancellationToken()).Wait();
|
||||
|
||||
Assert.True(warnEvent.FailReason == GameEvent.EventFailReason.Permission &&
|
||||
client.Warnings == 1, "warning was applied without proper permissions");
|
||||
|
||||
// warn clear
|
||||
var warnClearEvent = client.WarnClear(new EFClient { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
warnClearEvent.OnProcessed.Wait(5000);
|
||||
warnClearEvent.WaitAsync(new TimeSpan(0, 0, 10), new CancellationToken()).Wait();
|
||||
|
||||
Assert.True(warnClearEvent.FailReason == GameEvent.EventFailReason.Permission &&
|
||||
client.Warnings == 1, "warning was removed without proper permissions");
|
||||
|
||||
warnClearEvent = client.WarnClear(Utilities.IW4MAdminClient(server));
|
||||
warnClearEvent.OnProcessed.Wait(5000);
|
||||
warnClearEvent.WaitAsync(new TimeSpan(0, 0, 10), new CancellationToken()).Wait();
|
||||
|
||||
Assert.True(!warnClearEvent.Failed && client.Warnings == 0, "warning was not cleared");
|
||||
}
|
||||
@ -178,14 +178,14 @@ namespace Tests
|
||||
var player = new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer };
|
||||
player.SetAdditionalProperty("_reportCount", 3);
|
||||
var reportEvent = client.Report("test report", player);
|
||||
reportEvent.OnProcessed.Wait(TestTimeout);
|
||||
reportEvent.WaitAsync(new TimeSpan(0, 0, 10), new CancellationToken()).Wait();
|
||||
|
||||
Assert.True(reportEvent.FailReason == GameEvent.EventFailReason.Throttle &
|
||||
client.CurrentServer.Reports.Count(r => r.Target.NetworkId == client.NetworkId) == 0, $"too many reports were applied [{reportEvent.FailReason.ToString()}]");
|
||||
|
||||
// succeed
|
||||
reportEvent = client.Report("test report", new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
reportEvent.OnProcessed.Wait(TestTimeout);
|
||||
reportEvent.WaitAsync(new TimeSpan(0, 0, 10), new CancellationToken()).Wait();
|
||||
|
||||
Assert.True(!reportEvent.Failed &&
|
||||
client.CurrentServer.Reports.Count(r => r.Target.NetworkId == client.NetworkId) == 1, $"report was not applied [{reportEvent.FailReason.ToString()}]");
|
||||
@ -222,7 +222,7 @@ namespace Tests
|
||||
Assert.False(client == null, "no client found to flag");
|
||||
|
||||
var flagEvent = client.Flag("test flag", new EFClient { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
flagEvent.OnProcessed.Wait();
|
||||
flagEvent.Complete();
|
||||
|
||||
// succeed
|
||||
Assert.True(!flagEvent.Failed &&
|
||||
@ -230,31 +230,31 @@ namespace Tests
|
||||
Assert.False(client.ReceivedPenalties.FirstOrDefault(p => p.Offense == "test flag") == null, "flag was not applied");
|
||||
|
||||
flagEvent = client.Flag("test flag", new EFClient { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
flagEvent.OnProcessed.Wait();
|
||||
flagEvent.Complete();
|
||||
|
||||
// fail
|
||||
Assert.True(client.ReceivedPenalties.Count == 1, "flag was applied without permisions");
|
||||
|
||||
flagEvent = client.Flag("test flag", new EFClient { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
flagEvent.OnProcessed.Wait();
|
||||
flagEvent.Complete();
|
||||
|
||||
// fail
|
||||
Assert.True(client.ReceivedPenalties.Count == 1, "duplicate flag was applied");
|
||||
|
||||
var unflagEvent = client.Unflag("test unflag", new EFClient { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
unflagEvent.OnProcessed.Wait();
|
||||
unflagEvent.Complete();
|
||||
|
||||
// fail
|
||||
Assert.False(client.Level == EFClient.Permission.User, "user was unflagged without permissions");
|
||||
|
||||
unflagEvent = client.Unflag("test unflag", new EFClient { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
unflagEvent.OnProcessed.Wait();
|
||||
unflagEvent.Complete();
|
||||
|
||||
// succeed
|
||||
Assert.True(client.Level == EFClient.Permission.User, "user was not unflagged");
|
||||
|
||||
unflagEvent = client.Unflag("test unflag", new EFClient { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
unflagEvent.OnProcessed.Wait();
|
||||
unflagEvent.Complete();
|
||||
|
||||
// succeed
|
||||
Assert.True(unflagEvent.FailReason == GameEvent.EventFailReason.Invalid, "user was not flagged");
|
||||
@ -272,12 +272,12 @@ namespace Tests
|
||||
Assert.False(client == null, "no client found to kick");
|
||||
|
||||
var kickEvent = client.Kick("test kick", new EFClient() { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
kickEvent.OnProcessed.Wait();
|
||||
kickEvent.Complete();
|
||||
|
||||
Assert.True(kickEvent.FailReason == GameEvent.EventFailReason.Permission, "client was kicked without permission");
|
||||
|
||||
kickEvent = client.Kick("test kick", new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
kickEvent.OnProcessed.Wait();
|
||||
kickEvent.Complete();
|
||||
|
||||
Assert.True(_manager.Servers.First().GetClientsAsList().FirstOrDefault(c => c.NetworkId == client.NetworkId) == null, "client was not kicked");
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ namespace Tests
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
e.OnProcessed.Wait();
|
||||
e.Complete();
|
||||
|
||||
var client = Manager.GetServers()[0].Clients[0];
|
||||
|
||||
@ -44,7 +44,7 @@ namespace Tests
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
e.OnProcessed.Wait();
|
||||
e.Complete();
|
||||
|
||||
Assert.True(client.Warnings == 1, "client wasn't warned for objectional language");
|
||||
}
|
||||
|
@ -28,11 +28,11 @@ namespace Tests
|
||||
var currentClientCount = server.ClientNum;
|
||||
int eventsProcessed = 0;
|
||||
|
||||
_manager.OnServerEvent += (sender, eventArgs) =>
|
||||
/*_manager.OnServerEvent += (sender, eventArgs) =>
|
||||
{
|
||||
if (eventArgs.Event.Type == GameEvent.EventType.Connect)
|
||||
{
|
||||
eventArgs.Event.OnProcessed.Wait();
|
||||
eventArgs.Event.Complete();
|
||||
Assert.False(eventArgs.Event.Failed, "connect event was not processed");
|
||||
Assert.True(server.ClientNum == currentClientCount + 1, "client count was not incremented");
|
||||
eventsProcessed++;
|
||||
@ -41,13 +41,13 @@ namespace Tests
|
||||
|
||||
if (eventArgs.Event.Type == GameEvent.EventType.Disconnect)
|
||||
{
|
||||
eventArgs.Event.OnProcessed.Wait();
|
||||
eventArgs.Event.Complete();
|
||||
Assert.False(eventArgs.Event.Failed, "disconnect event was not processed");
|
||||
Assert.True(server.ClientNum == currentClientCount, "client count was not decremented");
|
||||
eventsProcessed++;
|
||||
resetEvent.Set();
|
||||
}
|
||||
};
|
||||
};*/
|
||||
|
||||
server.EmulateClientJoinLog();
|
||||
|
||||
@ -73,11 +73,11 @@ namespace Tests
|
||||
int eventsProcessed = 0;
|
||||
|
||||
_manager.GetApplicationSettings().Configuration().RConPollRate = 5000;
|
||||
_manager.OnServerEvent += (sender, eventArgs) =>
|
||||
/*_manager.OnServerEvent += (sender, eventArgs) =>
|
||||
{
|
||||
if (eventArgs.Event.Type == GameEvent.EventType.Connect)
|
||||
{
|
||||
eventArgs.Event.OnProcessed.Wait();
|
||||
eventArgs.Event.Complete();
|
||||
Assert.False(eventArgs.Event.Failed, "connect event was not processed");
|
||||
Assert.True(server.ClientNum == currentClientCount + 1, "client count was not incremented");
|
||||
eventsProcessed++;
|
||||
@ -86,13 +86,13 @@ namespace Tests
|
||||
|
||||
if (eventArgs.Event.Type == GameEvent.EventType.Disconnect)
|
||||
{
|
||||
eventArgs.Event.OnProcessed.Wait();
|
||||
eventArgs.Event.Complete();
|
||||
Assert.False(eventArgs.Event.Failed, "disconnect event was not processed");
|
||||
Assert.True(server.ClientNum == currentClientCount, "client count was not decremented");
|
||||
eventsProcessed++;
|
||||
resetEvent.Set();
|
||||
}
|
||||
};
|
||||
};*/
|
||||
|
||||
(server.RconParser as TestRconParser).FakeClientCount = 1;
|
||||
|
||||
|
@ -998,7 +998,7 @@ namespace SharedLibraryCore.Commands
|
||||
var names = new List<string>(E.Target.AliasLink.Children.Select(a => a.Name));
|
||||
var IPs = new List<string>(E.Target.AliasLink.Children.Select(a => a.IPAddress.ConvertIPtoString()).Distinct());
|
||||
|
||||
E.Target.Tell($"[^3{E.Target}^7]");
|
||||
E.Origin.Tell($"[^3{E.Target}^7]");
|
||||
|
||||
message.Append($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ALIAS_ALIASES"]}: ");
|
||||
message.Append(String.Join(" | ", names));
|
||||
|
@ -22,40 +22,26 @@ namespace SharedLibraryCore.Database
|
||||
public DbSet<EFMeta> EFMeta { get; set; }
|
||||
public DbSet<EFChangeHistory> EFChangeHistory { get; set; }
|
||||
|
||||
|
||||
//[Obsolete]
|
||||
//private static readonly ILoggerFactory _loggerFactory = new LoggerFactory(new[] {
|
||||
// new ConsoleLoggerProvider((category, level) => level == LogLevel.Information, true)
|
||||
//});
|
||||
|
||||
static string _ConnectionString;
|
||||
static string _provider;
|
||||
private static readonly string _migrationPluginDirectory = @"X:\IW4MAdmin\BUILD\Plugins";
|
||||
private static int activeContextCount;
|
||||
private static readonly ILoggerFactory _loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.AddConsole()
|
||||
.AddDebug()
|
||||
.AddFilter((category, level) => true);
|
||||
});
|
||||
|
||||
public DatabaseContext(DbContextOptions<DatabaseContext> opt) : base(opt)
|
||||
{
|
||||
#if DEBUG == true
|
||||
activeContextCount++;
|
||||
//Console.WriteLine($"Initialized DB Context #{activeContextCount}");
|
||||
#endif
|
||||
}
|
||||
|
||||
public DatabaseContext()
|
||||
{
|
||||
#if DEBUG == true
|
||||
activeContextCount++;
|
||||
//Console.WriteLine($"Initialized DB Context #{activeContextCount}");
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
#if DEBUG == true
|
||||
|
||||
//Console.WriteLine($"Disposed DB Context #{activeContextCount}");
|
||||
activeContextCount--;
|
||||
#endif
|
||||
}
|
||||
|
||||
public DatabaseContext(bool disableTracking) : this()
|
||||
@ -83,6 +69,9 @@ namespace SharedLibraryCore.Database
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
// optionsBuilder.UseLoggerFactory(_loggerFactory)
|
||||
// .EnableSensitiveDataLogging();
|
||||
|
||||
if (string.IsNullOrEmpty(_ConnectionString))
|
||||
{
|
||||
string currentPath = Utilities.OperatingDirectory;
|
||||
@ -153,6 +142,7 @@ namespace SharedLibraryCore.Database
|
||||
ent.HasIndex(a => a.Name);
|
||||
ent.Property(_alias => _alias.SearchableName).HasMaxLength(24);
|
||||
ent.HasIndex(_alias => _alias.SearchableName);
|
||||
ent.HasIndex(_alias => new { _alias.Name, _alias.IPAddress }).IsUnique();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<EFMeta>(ent =>
|
||||
|
@ -8,9 +8,6 @@ namespace SharedLibraryCore
|
||||
{
|
||||
public class GameEvent
|
||||
{
|
||||
// define what the delagate function looks like
|
||||
public delegate void OnServerEventEventHandler(object sender, GameEventArgs e);
|
||||
|
||||
public enum EventFailReason
|
||||
{
|
||||
/// <summary>
|
||||
@ -205,11 +202,17 @@ namespace SharedLibraryCore
|
||||
|
||||
public GameEvent()
|
||||
{
|
||||
OnProcessed = new ManualResetEventSlim(false);
|
||||
_eventFinishedWaiter = new ManualResetEvent(false);
|
||||
Time = DateTime.UtcNow;
|
||||
Id = GetNextEventId();
|
||||
}
|
||||
|
||||
~GameEvent()
|
||||
{
|
||||
_eventFinishedWaiter.Set();
|
||||
_eventFinishedWaiter.Dispose();
|
||||
}
|
||||
|
||||
public EventType Type;
|
||||
public EventRequiredEntity RequiredEntity { get; set; }
|
||||
public string Data; // Data is usually the message sent by player
|
||||
@ -219,34 +222,56 @@ namespace SharedLibraryCore
|
||||
public Server Owner;
|
||||
public bool IsRemote { get; set; } = false;
|
||||
public object Extra { get; set; }
|
||||
public ManualResetEventSlim OnProcessed { get; set; }
|
||||
private readonly ManualResetEvent _eventFinishedWaiter;
|
||||
public DateTime Time { get; set; }
|
||||
public long Id { get; private set; }
|
||||
public EventFailReason FailReason { get; set; }
|
||||
public bool Failed => FailReason != EventFailReason.None;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the event should block until it is complete
|
||||
/// </summary>
|
||||
public bool IsBlocking { get; set; }
|
||||
|
||||
public void Complete()
|
||||
{
|
||||
_eventFinishedWaiter.Set();
|
||||
#if DEBUG
|
||||
Owner?.Logger.WriteDebug($"Completed internal for event {Id}");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// asynchronously wait for GameEvent to be processed
|
||||
/// </summary>
|
||||
/// <returns>waitable task </returns>
|
||||
public Task<GameEvent> WaitAsync(TimeSpan timeSpan, CancellationToken token)
|
||||
public async Task<GameEvent> WaitAsync(TimeSpan timeSpan, CancellationToken token)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
bool processed = OnProcessed.Wait(timeSpan, token);
|
||||
// this let's us know if the the action timed out
|
||||
FailReason = FailReason == EventFailReason.None & !processed ? EventFailReason.Timeout : FailReason;
|
||||
return this;
|
||||
});
|
||||
}
|
||||
bool processed = false;
|
||||
|
||||
public GameEvent Wait()
|
||||
{
|
||||
OnProcessed.Wait();
|
||||
#if DEBUG
|
||||
Owner?.Logger.WriteDebug($"Begin wait for event {Id}");
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
processed = await Task.Run(() => _eventFinishedWaiter.WaitOne(timeSpan), token);
|
||||
}
|
||||
catch { }
|
||||
|
||||
|
||||
if (!processed)
|
||||
{
|
||||
#if DEBUG
|
||||
//throw new Exception();
|
||||
#endif
|
||||
Owner?.Logger.WriteError("Waiting for event to complete timed out");
|
||||
Owner?.Logger.WriteDebug($"{Id}, {Type}, {Data}, {Extra}, {FailReason.ToString()}, {Message}, {Origin}, {Target}");
|
||||
}
|
||||
|
||||
|
||||
// this lets us know if the the action timed out
|
||||
FailReason = FailReason == EventFailReason.None && !processed ? EventFailReason.Timeout : FailReason;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ namespace SharedLibraryCore.Interfaces
|
||||
string ExternalIPAddress { get; }
|
||||
CancellationToken CancellationToken { get; }
|
||||
bool IsRestartRequested { get; }
|
||||
OnServerEventEventHandler OnServerEvent { get; set; }
|
||||
//OnServerEventEventHandler OnServerEvent { get; set; }
|
||||
Task ExecuteEvent(GameEvent gameEvent);
|
||||
}
|
||||
}
|
||||
|
909
SharedLibraryCore/Migrations/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs
generated
Normal file
909
SharedLibraryCore/Migrations/20191030000713_EnforceUniqueIndexForEFAliasIPName.Designer.cs
generated
Normal file
@ -0,0 +1,909 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SharedLibraryCore.Database;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20191030000713_EnforceUniqueIndexForEFAliasIPName")]
|
||||
partial class EnforceUniqueIndexForEFAliasIPName
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.0.0");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("SnapshotId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("CurrentSessionLength")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("CurrentStrain")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("CurrentViewAngleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Deaths")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("Distance")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("EloRating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("HitDestinationId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HitLocation")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HitOriginId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HitType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Hits")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Kills")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LastStrainAngleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("RecoilOffset")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("SessionAngleOffset")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("SessionAverageSnapValue")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("SessionSPM")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("SessionScore")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SessionSnapHits")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("StrainAngleBetween")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("TimeSinceLastEvent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("WeaponId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("SnapshotId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CurrentViewAngleId");
|
||||
|
||||
b.HasIndex("HitDestinationId");
|
||||
|
||||
b.HasIndex("HitOriginId");
|
||||
|
||||
b.HasIndex("LastStrainAngleId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshotVector3", b =>
|
||||
{
|
||||
b.Property<int>("ACSnapshotVector3Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SnapshotId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Vector3Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ACSnapshotVector3Id");
|
||||
|
||||
b.HasIndex("SnapshotId");
|
||||
|
||||
b.HasIndex("Vector3Id");
|
||||
|
||||
b.ToTable("EFACSnapshotVector3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AttackerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Damage")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("DeathOriginVector3Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DeathType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("Fraction")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("HitLoc")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsKill")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("KillOriginVector3Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Map")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("VictimId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("ViewAnglesVector3Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("VisibilityPercentage")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Weapon")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("KillId");
|
||||
|
||||
b.HasIndex("AttackerId");
|
||||
|
||||
b.HasIndex("DeathOriginVector3Id");
|
||||
|
||||
b.HasIndex("KillOriginVector3Id");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("VictimId");
|
||||
|
||||
b.HasIndex("ViewAnglesVector3Id");
|
||||
|
||||
b.ToTable("EFClientKills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.Property<long>("MessageId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("TimeSent")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("MessageId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("TimeSent");
|
||||
|
||||
b.ToTable("EFClientMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFClientRatingHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("AverageRecoilOffset")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("AverageSnapValue")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Deaths")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("EloRating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Kills")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("MaxStrain")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("RollingWeightedKDR")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("SPM")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("Skill")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("SnapHitCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TimePlayed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("VisionAverage")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.HasKey("ClientId", "ServerId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.Property<int>("HitLocationCountId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EFClientStatisticsClientId")
|
||||
.HasColumnName("EFClientStatisticsClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("EFClientStatisticsServerId")
|
||||
.HasColumnName("EFClientStatisticsServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HitCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<float>("HitOffsetAverage")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Location")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<float>("MaxAngleDistance")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.HasKey("HitLocationCountId");
|
||||
|
||||
b.HasIndex("EFClientStatisticsServerId");
|
||||
|
||||
b.HasIndex("EFClientStatisticsClientId", "EFClientStatisticsServerId");
|
||||
|
||||
b.ToTable("EFHitLocationCounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.Property<int>("RatingId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ActivityAmount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Newest")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("Performance")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Ranking")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("RatingId");
|
||||
|
||||
b.HasIndex("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("Performance", "Ranking", "When");
|
||||
|
||||
b.ToTable("EFRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
|
||||
{
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("EndPoint")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("GameName")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Port")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ServerId");
|
||||
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("TotalKills")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("TotalPlayTime")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("StatisticId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.Property<int>("AliasId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("DateAdded")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("IPAddress")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LinkId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(24);
|
||||
|
||||
b.Property<string>("SearchableName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(24);
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("SearchableName");
|
||||
|
||||
b.HasIndex("Name", "IPAddress")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Property<int>("AliasLinkId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("AliasLinkId");
|
||||
|
||||
b.ToTable("EFAliasLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
|
||||
{
|
||||
b.Property<int>("ChangeHistoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("CurrentValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("OriginEntityId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("PreviousValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TargetEntityId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("TimeChanged")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TypeOfChange")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ChangeHistoryId");
|
||||
|
||||
b.ToTable("EFChangeHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AliasLinkId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Connections")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("CurrentAliasId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("FirstConnection")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastConnection")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Masked")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("NetworkId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordSalt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TotalConnectionTime")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ClientId");
|
||||
|
||||
b.HasIndex("AliasLinkId");
|
||||
|
||||
b.HasIndex("CurrentAliasId");
|
||||
|
||||
b.HasIndex("NetworkId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.Property<int>("MetaId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Extra")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(32);
|
||||
|
||||
b.Property<DateTime>("Updated")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("MetaId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("Key");
|
||||
|
||||
b.ToTable("EFMeta");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.Property<int>("PenaltyId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AutomatedOffense")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("Expires")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsEvadedOffense")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LinkId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("OffenderId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Offense")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PunisherId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("PenaltyId");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("OffenderId");
|
||||
|
||||
b.HasIndex("PunisherId");
|
||||
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.Property<int>("Vector3Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<float>("X")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<float>("Y")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<float>("Z")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentViewAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitDestinationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitOriginId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("LastStrainAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshotVector3", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", "Snapshot")
|
||||
.WithMany("PredictedViewAngles")
|
||||
.HasForeignKey("SnapshotId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "Vector")
|
||||
.WithMany()
|
||||
.HasForeignKey("Vector3Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttackerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeathOriginVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("KillOriginVector3Id");
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
|
||||
.WithMany()
|
||||
.HasForeignKey("VictimId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
|
||||
.WithMany()
|
||||
.HasForeignKey("ViewAnglesVector3Id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("EFClientStatisticsClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("EFClientStatisticsServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", null)
|
||||
.WithMany("HitLocations")
|
||||
.HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("RatingHistoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
|
||||
.WithMany()
|
||||
.HasForeignKey("AliasLinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentAliasId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany("Meta")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("OffenderId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
|
||||
.WithMany("AdministeredPenalties")
|
||||
.HasForeignKey("PunisherId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
public partial class EnforceUniqueIndexForEFAliasIPName : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.Sqlite")
|
||||
{
|
||||
migrationBuilder.Sql(@"DELETE FROM EFAlias WHERE AliasId IN (
|
||||
SELECT AliasId from (
|
||||
SELECT AliasId, Name, Min(DateAdded), IPAddress FROM EFAlias where (IPAddress, Name) in (SELECT DISTINCT IPAddress, Name FROM EFAlias GROUP BY EFAlias.IPAddress, Name HAVING count(IPAddress) > 1 AND count(Name) > 1)
|
||||
GROUP BY IPAddress ORDER BY IPAddress))", true);
|
||||
migrationBuilder.Sql(@"CREATE UNIQUE INDEX IX_EFAlias_Name_IPAddress ON EFAlias ( IPAddress, Name );", true);
|
||||
return;
|
||||
}
|
||||
|
||||
else if (migrationBuilder.ActiveProvider == "Pomelo.EntityFrameworkCore.MySql")
|
||||
{
|
||||
migrationBuilder.Sql(@"CREATE TEMPORARY TABLE DUPLICATE_ALIASES
|
||||
SELECT
|
||||
MIN(`AliasId`) `MIN`,
|
||||
MAX(`AliasId`) `MAX`,
|
||||
`LinkId`
|
||||
FROM
|
||||
`EFAlias`
|
||||
WHERE
|
||||
(`IPAddress`, `NAME`) IN(
|
||||
SELECT DISTINCT
|
||||
`IPAddress`,
|
||||
`NAME`
|
||||
FROM
|
||||
`EFAlias`
|
||||
GROUP BY
|
||||
`EFAlias`.`IPAddress`,
|
||||
`NAME`
|
||||
HAVING
|
||||
COUNT(`IPAddress`) > 1 AND COUNT(`NAME`) > 1
|
||||
)
|
||||
GROUP BY
|
||||
`IPAddress`
|
||||
ORDER BY
|
||||
`IPAddress`;
|
||||
SET
|
||||
SQL_SAFE_UPDATES = 0;
|
||||
UPDATE
|
||||
`EFClients` AS `Client`
|
||||
JOIN
|
||||
DUPLICATE_ALIASES `Duplicate`
|
||||
ON
|
||||
`Client`.CurrentAliasId = `Duplicate`.`MIN`
|
||||
SET
|
||||
`Client`.CurrentAliasId = `Duplicate`.`MAX`
|
||||
WHERE
|
||||
`Client`.`CurrentAliasId` IN(
|
||||
SELECT
|
||||
`MIN`
|
||||
FROM
|
||||
DUPLICATE_ALIASES
|
||||
);
|
||||
DELETE
|
||||
FROM
|
||||
`EFAlias`
|
||||
WHERE
|
||||
`AliasId` IN(
|
||||
SELECT
|
||||
`MIN`
|
||||
FROM
|
||||
DUPLICATE_ALIASES
|
||||
);
|
||||
SET
|
||||
SQL_SAFE_UPDATES = 1;
|
||||
DROP TABLE
|
||||
DUPLICATE_ALIASES;");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
migrationBuilder.Sql(@"DELETE
|
||||
FROM ""EFAlias""
|
||||
WHERE ""AliasId""
|
||||
IN
|
||||
(
|
||||
SELECT MIN(""AliasId"") AliasId
|
||||
|
||||
FROM ""EFAlias"" WHERE(""IPAddress"", ""Name"")
|
||||
|
||||
IN
|
||||
(
|
||||
SELECT DISTINCT ""IPAddress"", ""Name""
|
||||
|
||||
FROM ""EFAlias""
|
||||
|
||||
GROUP BY ""EFAlias"".""IPAddress"", ""Name""
|
||||
|
||||
HAVING COUNT(""IPAddress"") > 1 AND COUNT(""Name"") > 1
|
||||
)
|
||||
|
||||
GROUP BY ""IPAddress""
|
||||
|
||||
ORDER BY ""IPAddress""
|
||||
)", true);
|
||||
}
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFAlias_Name_IPAddress",
|
||||
table: "EFAlias",
|
||||
columns: new[] { "Name", "IPAddress" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFAlias_Name_IPAddress",
|
||||
table: "EFAlias");
|
||||
}
|
||||
}
|
||||
}
|
@ -464,14 +464,13 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
b.HasIndex("IPAddress");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.HasIndex("SearchableName");
|
||||
|
||||
b.HasIndex("Name", "IPAddress")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
});
|
||||
|
||||
|
@ -3,8 +3,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Database.Models
|
||||
@ -82,6 +82,12 @@ namespace SharedLibraryCore.Database.Models
|
||||
{ "_reportCount", 0 }
|
||||
};
|
||||
ReceivedPenalties = new List<EFPenalty>();
|
||||
ProcessingEvent = new SemaphoreSlim(1, 1);
|
||||
}
|
||||
|
||||
~EFClient()
|
||||
{
|
||||
ProcessingEvent.Dispose();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
@ -108,6 +114,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
|
||||
[NotMapped]
|
||||
public string IPAddressString => IPAddress.ConvertIPtoString();
|
||||
|
||||
[NotMapped]
|
||||
public virtual IDictionary<int, long> LinkedAccounts { get; set; }
|
||||
|
||||
@ -452,13 +459,10 @@ namespace SharedLibraryCore.Database.Models
|
||||
/// <summary>
|
||||
/// Handles any client related logic on connection
|
||||
/// </summary>
|
||||
public bool OnConnect()
|
||||
public bool IsAbleToConnectSimple()
|
||||
{
|
||||
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
|
||||
LastConnection = DateTime.UtcNow;
|
||||
Connections += 1;
|
||||
|
||||
string strippedName = Name.StripColors();
|
||||
if (string.IsNullOrWhiteSpace(Name) || strippedName.Replace(" ", "").Length < 3)
|
||||
{
|
||||
@ -524,27 +528,29 @@ namespace SharedLibraryCore.Database.Models
|
||||
{
|
||||
IPAddress = ipAddress;
|
||||
await CurrentServer.Manager.GetClientService().UpdateAlias(this);
|
||||
CurrentServer.Logger.WriteDebug($"Updated alias for {this}");
|
||||
await CurrentServer.Manager.GetClientService().Update(this);
|
||||
CurrentServer.Logger.WriteDebug($"Updated client for {this}");
|
||||
|
||||
bool canConnect = await CanConnect(ipAddress);
|
||||
|
||||
if (canConnect)
|
||||
if (!canConnect)
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Client {this} is not allowed to join the server");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Join,
|
||||
Origin = this,
|
||||
Target = this,
|
||||
Owner = CurrentServer
|
||||
Owner = CurrentServer,
|
||||
};
|
||||
|
||||
CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Client {this} is not allowed to join the server");
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
@ -560,6 +566,13 @@ namespace SharedLibraryCore.Database.Models
|
||||
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
var autoKickClient = Utilities.IW4MAdminClient(CurrentServer);
|
||||
|
||||
bool isAbleToConnectSimple = IsAbleToConnectSimple();
|
||||
|
||||
if (!isAbleToConnectSimple)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// we want to get any penalties that are tied to their IP or AliasLink (but not necessarily their GUID)
|
||||
var activePenalties = await CurrentServer.Manager.GetPenaltyService().GetActivePenaltiesAsync(AliasLinkId, ipAddress);
|
||||
|
||||
@ -609,7 +622,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
Unflag(Utilities.CurrentLocalization.LocalizationIndex["SERVER_AUTOFLAG_UNFLAG"], autoKickClient);
|
||||
}
|
||||
|
||||
return OnConnect();
|
||||
return true;
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
@ -661,6 +674,29 @@ namespace SharedLibraryCore.Database.Models
|
||||
.LocalizationIndex[$"GLOBAL_PERMISSION_{Level.ToString().ToUpper()}"]
|
||||
};
|
||||
|
||||
[NotMapped]
|
||||
public SemaphoreSlim ProcessingEvent;
|
||||
|
||||
public async Task Lock()
|
||||
{
|
||||
bool result = await ProcessingEvent.WaitAsync(Utilities.DefaultCommandTimeout);
|
||||
|
||||
#if DEBUG
|
||||
if (!result)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
if (ProcessingEvent.CurrentCount == 0)
|
||||
{
|
||||
ProcessingEvent.Release(1);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return ((EFClient)obj).NetworkId == this.NetworkId;
|
||||
|
@ -19,19 +19,25 @@ namespace SharedLibraryCore
|
||||
private Jint.Engine ScriptEngine;
|
||||
private readonly string FileName;
|
||||
private IManager Manager;
|
||||
private readonly FileSystemWatcher _watcher;
|
||||
|
||||
public ScriptPlugin(string fileName)
|
||||
{
|
||||
FileName = fileName;
|
||||
var watcher = new FileSystemWatcher()
|
||||
_watcher = new FileSystemWatcher()
|
||||
{
|
||||
Path = $"{Utilities.OperatingDirectory}Plugins{Path.DirectorySeparatorChar}",
|
||||
NotifyFilter = NotifyFilters.Size,
|
||||
Filter = fileName.Split(Path.DirectorySeparatorChar).Last()
|
||||
};
|
||||
|
||||
watcher.Changed += Watcher_Changed;
|
||||
watcher.EnableRaisingEvents = true;
|
||||
_watcher.Changed += Watcher_Changed;
|
||||
_watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
~ScriptPlugin()
|
||||
{
|
||||
_watcher.Dispose();
|
||||
}
|
||||
|
||||
private async void Watcher_Changed(object sender, FileSystemEventArgs e)
|
||||
|
@ -67,7 +67,7 @@ namespace SharedLibraryCore
|
||||
/// </summary>
|
||||
/// <param name="P">EFClient pulled from memory reading</param>
|
||||
/// <returns>True if player added sucessfully, false otherwise</returns>
|
||||
public abstract Task OnClientConnected(EFClient P);
|
||||
public abstract Task<EFClient> OnClientConnected(EFClient P);
|
||||
|
||||
/// <summary>
|
||||
/// Remove player by client number
|
||||
|
@ -21,19 +21,23 @@ namespace SharedLibraryCore.Services
|
||||
|
||||
if (entity.IPAddress != null)
|
||||
{
|
||||
var existingAlias = await context.Aliases
|
||||
var existingAliases = await context.Aliases
|
||||
.Select(_alias => new { _alias.AliasId, _alias.LinkId, _alias.IPAddress, _alias.Name })
|
||||
.FirstOrDefaultAsync(_alias => _alias.IPAddress == entity.IPAddress);
|
||||
.Where(_alias => _alias.IPAddress == entity.IPAddress)
|
||||
.ToListAsync();
|
||||
|
||||
if (existingAlias != null)
|
||||
if (existingAliases.Count > 0)
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"[create] client with new GUID {entity} has existing link {existingAlias.LinkId}");
|
||||
linkId = existingAliases.First().LinkId;
|
||||
|
||||
linkId = existingAlias.LinkId;
|
||||
if (existingAlias.Name == entity.Name)
|
||||
entity.CurrentServer.Logger.WriteDebug($"[create] client with new GUID {entity} has existing link {linkId}");
|
||||
|
||||
var existingExactAlias = existingAliases.FirstOrDefault(_alias => _alias.Name == entity.Name);
|
||||
|
||||
if (existingExactAlias != null)
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"[create] client with new GUID {entity} has existing alias {existingAlias.AliasId}");
|
||||
aliasId = existingAlias.AliasId;
|
||||
entity.CurrentServer.Logger.WriteDebug($"[create] client with new GUID {entity} has existing alias {existingExactAlias.AliasId}");
|
||||
aliasId = existingExactAlias.AliasId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,6 +103,8 @@ namespace SharedLibraryCore.Services
|
||||
|
||||
private async Task UpdateAlias(string name, int? ip, EFClient entity, DatabaseContext context)
|
||||
{
|
||||
entity.CurrentServer.Manager.GetLogger(0).WriteDebug($"Begin update alias for {entity}");
|
||||
|
||||
// entity is the tracked db context item
|
||||
// get all aliases by IP address and LinkId
|
||||
var iqAliases = context.Aliases
|
||||
@ -106,11 +112,33 @@ namespace SharedLibraryCore.Services
|
||||
// we only want alias that have the same IP address or share a link
|
||||
.Where(_alias => _alias.IPAddress == ip || (_alias.LinkId == entity.AliasLinkId));
|
||||
|
||||
#if DEBUG == true
|
||||
var aliasSql = iqAliases.ToSql();
|
||||
#endif
|
||||
var aliases = await iqAliases.ToListAsync();
|
||||
|
||||
//// update each of the aliases where this is no IP but the name is identical
|
||||
//foreach (var alias in aliases.Where(_alias => (_alias.IPAddress == null || _alias.IPAddress == 0)))
|
||||
//{
|
||||
// alias.IPAddress = ip;
|
||||
//}
|
||||
|
||||
//// remove any possible duplicates after updating
|
||||
//foreach (var aliasGroup in aliases.GroupBy(_alias => new { _alias.IPAddress, _alias.Name })
|
||||
// .Where(_group => _group.Count() > 1))
|
||||
//{
|
||||
// var oldestDuplicateAlias = aliasGroup.OrderBy(_alias => _alias.DateAdded).First();
|
||||
|
||||
// entity.CurrentServer.Manager.GetLogger(0).WriteDebug($"Oldest duplicate is {oldestDuplicateAlias.AliasId}");
|
||||
|
||||
// await context.Clients.Where(_client => aliasGroup.Select(_grp => _grp.AliasId).Contains(_client.CurrentAliasId))
|
||||
// .ForEachAsync(_client => _client.CurrentAliasId = oldestDuplicateAlias.AliasId);
|
||||
|
||||
// var duplicateAliases = aliasGroup.Where(_alias => _alias.AliasId != oldestDuplicateAlias.AliasId);
|
||||
// context.RemoveRange(duplicateAliases);
|
||||
|
||||
// await context.SaveChangesAsync();
|
||||
|
||||
// entity.CurrentServer.Manager.GetLogger(0).WriteDebug($"Removed duplicate aliases {string.Join(",", duplicateAliases.Select(_alias => _alias.AliasId))}");
|
||||
//}
|
||||
|
||||
// see if they have a matching IP + Name but new NetworkId
|
||||
var existingExactAlias = aliases.FirstOrDefault(a => a.Name == name && a.IPAddress == ip);
|
||||
bool hasExactAliasMatch = existingExactAlias != null;
|
||||
@ -125,12 +153,6 @@ namespace SharedLibraryCore.Services
|
||||
bool hasExistingAlias = aliases.Count > 0;
|
||||
bool isAliasLinkUpdated = newAliasLink.AliasLinkId != entity.AliasLink.AliasLinkId;
|
||||
|
||||
// update each of the aliases where this is no IP but the name is identical
|
||||
foreach (var alias in aliases.Where(_alias => (_alias.IPAddress == null || _alias.IPAddress == 0)))
|
||||
{
|
||||
alias.IPAddress = ip;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
// this happens when the link we found is different than the one we create before adding an IP
|
||||
@ -191,9 +213,12 @@ namespace SharedLibraryCore.Services
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
entity.CurrentServer.Logger.WriteDebug($"[updatealias] {entity} has exact alias match, so we're going to try to remove aliasId {oldAlias.AliasId} with linkId {oldAlias.AliasId}");
|
||||
context.Aliases.Remove(oldAlias);
|
||||
await context.SaveChangesAsync();
|
||||
if (context.Entry(oldAlias).State != EntityState.Deleted)
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"[updatealias] {entity} has exact alias match, so we're going to try to remove aliasId {oldAlias.AliasId} with linkId {oldAlias.AliasId}");
|
||||
context.Aliases.Remove(oldAlias);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,6 +241,8 @@ namespace SharedLibraryCore.Services
|
||||
entity.CurrentAliasId = 0;
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
entity.CurrentServer.Manager.GetLogger(0).WriteDebug($"End update alias for {entity}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -286,18 +313,41 @@ namespace SharedLibraryCore.Services
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<EFClient> Get(int entityID)
|
||||
public async Task<EFClient> Get(int entityId)
|
||||
{
|
||||
// todo: this needs to be optimized for large linked accounts
|
||||
using (var context = new DatabaseContext(true))
|
||||
{
|
||||
var iqClient = from _c in context.Clients
|
||||
.Include(c => c.CurrentAlias)
|
||||
.Include(c => c.AliasLink.Children)
|
||||
.Include(c => c.Meta)
|
||||
where _c.ClientId == entityID
|
||||
select _c;
|
||||
|
||||
var client = await iqClient.FirstOrDefaultAsync();
|
||||
var client = context.Clients
|
||||
.Select(_client => new EFClient()
|
||||
{
|
||||
ClientId = _client.ClientId,
|
||||
AliasLinkId = _client.AliasLinkId,
|
||||
Level = _client.Level,
|
||||
Connections = _client.Connections,
|
||||
FirstConnection = _client.FirstConnection,
|
||||
LastConnection = _client.LastConnection,
|
||||
Masked = _client.Masked,
|
||||
NetworkId = _client.NetworkId,
|
||||
CurrentAlias = new EFAlias()
|
||||
{
|
||||
Name = _client.CurrentAlias.Name,
|
||||
IPAddress = _client.CurrentAlias.IPAddress
|
||||
}
|
||||
})
|
||||
.FirstOrDefault(_client => _client.ClientId == entityId);
|
||||
|
||||
client.AliasLink = new EFAliasLink()
|
||||
{
|
||||
Children = await context.Aliases
|
||||
.Where(_alias => _alias.LinkId == client.AliasLinkId)
|
||||
.Select(_alias => new EFAlias()
|
||||
{
|
||||
Name = _alias.Name,
|
||||
IPAddress = _alias.IPAddress
|
||||
}).ToListAsync()
|
||||
};
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
@ -337,8 +387,19 @@ namespace SharedLibraryCore.Services
|
||||
EF.CompileAsyncQuery((DatabaseContext context, long networkId) =>
|
||||
context.Clients
|
||||
.Include(c => c.CurrentAlias)
|
||||
.Include(c => c.AliasLink.Children)
|
||||
.Include(c => c.ReceivedPenalties)
|
||||
//.Include(c => c.AliasLink.Children)
|
||||
//.Include(c => c.ReceivedPenalties)
|
||||
.Select(_client => new EFClient()
|
||||
{
|
||||
ClientId = _client.ClientId,
|
||||
AliasLinkId = _client.AliasLinkId,
|
||||
Level = _client.Level,
|
||||
Connections = _client.Connections,
|
||||
FirstConnection = _client.FirstConnection,
|
||||
LastConnection = _client.LastConnection,
|
||||
Masked = _client.Masked,
|
||||
NetworkId = _client.NetworkId
|
||||
})
|
||||
.FirstOrDefault(c => c.NetworkId == networkId)
|
||||
);
|
||||
|
||||
|
@ -74,6 +74,13 @@ namespace SharedLibraryCore.Services
|
||||
return await ctx.EFMeta
|
||||
.Where(_meta => _meta.Key == metaKey)
|
||||
.Where(_meta => _meta.ClientId == client.ClientId)
|
||||
.Select(_meta => new EFMeta()
|
||||
{
|
||||
MetaId = _meta.MetaId,
|
||||
Key = _meta.Key,
|
||||
ClientId = _meta.ClientId,
|
||||
Value = _meta.Value
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="Npgsql" Version="4.1.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.1" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.0.0-rc1.final" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.0.0-rc3.final" />
|
||||
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -31,7 +31,7 @@ namespace SharedLibraryCore
|
||||
#endif
|
||||
public static Encoding EncodingType;
|
||||
public static Localization.Layout CurrentLocalization = new Localization.Layout(new Dictionary<string, string>());
|
||||
public static TimeSpan DefaultCommandTimeout = new TimeSpan(0, 0, 10);
|
||||
public static TimeSpan DefaultCommandTimeout = new TimeSpan(0, 0, 25);
|
||||
|
||||
public static EFClient IW4MAdminClient(Server server = null)
|
||||
{
|
||||
|
@ -45,17 +45,17 @@ namespace WebfrontCore.Controllers
|
||||
NetworkId = client.NetworkId,
|
||||
Meta = new List<ProfileMeta>(),
|
||||
Aliases = client.AliasLink.Children
|
||||
.Where(a => a.Name != client.Name)
|
||||
.Select(a => a.Name)
|
||||
.Distinct()
|
||||
.Prepend(client.Name)
|
||||
.OrderBy(a => a)
|
||||
.Distinct()
|
||||
.ToList(),
|
||||
IPs = client.AliasLink.Children
|
||||
.Where(i => i.IPAddress != null)
|
||||
.OrderByDescending(i => i.DateAdded)
|
||||
.Select(i => i.IPAddress.ConvertIPtoString())
|
||||
.Union(new List<string>() { client.CurrentAlias.IPAddress.ConvertIPtoString() })
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Prepend(client.CurrentAlias.IPAddress.ConvertIPtoString())
|
||||
.Distinct()
|
||||
.OrderBy(i => i)
|
||||
.ToList(),
|
||||
HasActivePenalty = activePenalties.Count() > 0,
|
||||
ActivePenaltyType = activePenalties.Count() > 0 ? activePenalties.First().Type.ToString() : null,
|
||||
@ -139,7 +139,7 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
var clientsDto = await Manager.GetClientService().FindClientsByIdentifier(clientName);
|
||||
|
||||
foreach(var client in clientsDto)
|
||||
foreach (var client in clientsDto)
|
||||
{
|
||||
if (!Authorized && ((Permission)client.LevelInt).ShouldHideLevel())
|
||||
{
|
||||
@ -152,7 +152,7 @@ namespace WebfrontCore.Controllers
|
||||
return View("Find/Index", clientsDto);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Meta(int id, int count, int offset, DateTime? startAt)
|
||||
public async Task<IActionResult> GetMeta(int id, int count, int offset, DateTime? startAt)
|
||||
{
|
||||
IEnumerable<ProfileMeta> meta = await MetaService.GetRuntimeMeta(id, startAt == null ? offset : 0, count, startAt ?? DateTime.UtcNow);
|
||||
|
||||
|
@ -23,7 +23,7 @@ namespace WebfrontCore.Middleware
|
||||
public ClaimsPermissionRemoval(RequestDelegate nextRequest, IManager manager)
|
||||
{
|
||||
_manager = manager;
|
||||
_manager.OnServerEvent += OnGameEvent;
|
||||
//_manager.OnServerEvent += OnGameEvent;
|
||||
_privilegedClientIds = new List<int>();
|
||||
_nextRequest = nextRequest;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user