fix stat issue with concurrent threads
fix potential lost penalty if server does not response to kick request make sure that broadcast only shows one custom say name add unit tests
This commit is contained in:
parent
3ae2e42718
commit
9e74dac5ed
@ -1096,158 +1096,111 @@ namespace IW4MAdmin
|
|||||||
return logPath.FixDirectoryCharacters();
|
return logPath.FixDirectoryCharacters();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task Warn(String Reason, EFClient Target, EFClient Origin)
|
public override async Task Warn(string reason, EFClient targetClient, EFClient targetOrigin)
|
||||||
{
|
{
|
||||||
// ensure player gets warned if command not performed on them in game
|
// ensure player gets warned if command not performed on them in game
|
||||||
if (Target.ClientNumber < 0)
|
targetClient = targetClient.ClientNumber < 0 ?
|
||||||
{
|
Manager.GetActiveClients()
|
||||||
var ingameClient = Manager.GetActiveClients()
|
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
||||||
.FirstOrDefault(c => c.ClientId == Target.ClientId);
|
targetClient;
|
||||||
|
|
||||||
if (ingameClient != null)
|
|
||||||
{
|
|
||||||
await Warn(Reason, ingameClient, Origin);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Target.Warnings >= 4)
|
|
||||||
{
|
|
||||||
Target.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string message = $"^1{loc["SERVER_WARNING"]} ^7[^3{Target.Warnings}^7]: ^3{Target.Name}^7, {Reason}";
|
|
||||||
Target.CurrentServer.Broadcast(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
var newPenalty = new EFPenalty()
|
var newPenalty = new EFPenalty()
|
||||||
{
|
{
|
||||||
Type = EFPenalty.PenaltyType.Warning,
|
Type = EFPenalty.PenaltyType.Warning,
|
||||||
Expires = DateTime.UtcNow,
|
Expires = DateTime.UtcNow,
|
||||||
Offender = Target,
|
Offender = targetClient,
|
||||||
Punisher = Origin,
|
Punisher = targetOrigin,
|
||||||
Offense = Reason,
|
Offense = reason,
|
||||||
Link = Target.AliasLink
|
Link = targetClient.AliasLink
|
||||||
};
|
};
|
||||||
|
|
||||||
await Manager.GetPenaltyService().Create(newPenalty);
|
Logger.WriteDebug($"Creating warn penalty for {targetClient}");
|
||||||
}
|
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), Manager.GetLogger(0));
|
||||||
|
|
||||||
protected override async Task Kick(String Reason, EFClient Target, EFClient Origin)
|
if (targetClient.IsIngame)
|
||||||
{
|
|
||||||
// ensure player gets kicked if command not performed on them in game
|
|
||||||
if (Target.ClientNumber < 0)
|
|
||||||
{
|
{
|
||||||
var ingameClient = Manager.GetActiveClients()
|
if (targetClient.Warnings >= 4)
|
||||||
.FirstOrDefault(c => c.ClientId == Target.ClientId);
|
|
||||||
|
|
||||||
if (ingameClient != null)
|
|
||||||
{
|
{
|
||||||
await Kick(Reason, ingameClient, Origin);
|
targetClient.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#if !DEBUG
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, Target.ClientNumber, $"{loc["SERVER_KICK_TEXT"]} - ^5{Reason}^7");
|
|
||||||
await Target.CurrentServer.ExecuteCommandAsync(formattedKick);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if DEBUG
|
string message = $"^1{loc["SERVER_WARNING"]} ^7[^3{targetClient.Warnings}^7]: ^3{targetClient.Name}^7, {reason}";
|
||||||
// await Target.CurrentServer.OnClientDisconnected(Target);
|
targetClient.CurrentServer.Broadcast(message);
|
||||||
var e = new GameEvent()
|
}
|
||||||
{
|
}
|
||||||
Type = GameEvent.EventType.PreDisconnect,
|
|
||||||
Origin = Target,
|
|
||||||
Owner = this
|
|
||||||
};
|
|
||||||
|
|
||||||
Manager.GetEventHandler().AddEvent(e);
|
public override async Task Kick(string Reason, EFClient targetClient, EFClient originClient)
|
||||||
#endif
|
{
|
||||||
|
targetClient = targetClient.ClientNumber < 0 ?
|
||||||
|
Manager.GetActiveClients()
|
||||||
|
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
||||||
|
targetClient;
|
||||||
|
|
||||||
var newPenalty = new EFPenalty()
|
var newPenalty = new EFPenalty()
|
||||||
{
|
{
|
||||||
Type = EFPenalty.PenaltyType.Kick,
|
Type = EFPenalty.PenaltyType.Kick,
|
||||||
Expires = DateTime.UtcNow,
|
Expires = DateTime.UtcNow,
|
||||||
Offender = Target,
|
Offender = targetClient,
|
||||||
Offense = Reason,
|
Offense = Reason,
|
||||||
Punisher = Origin,
|
Punisher = originClient,
|
||||||
Link = Target.AliasLink
|
Link = targetClient.AliasLink
|
||||||
};
|
};
|
||||||
|
|
||||||
await Manager.GetPenaltyService().Create(newPenalty);
|
Logger.WriteDebug($"Creating kick penalty for {targetClient}");
|
||||||
|
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), Manager.GetLogger(0));
|
||||||
|
|
||||||
|
if (targetClient.IsIngame)
|
||||||
|
{
|
||||||
|
var e = new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.PreDisconnect,
|
||||||
|
Origin = targetClient,
|
||||||
|
Owner = this
|
||||||
|
};
|
||||||
|
|
||||||
|
Manager.GetEventHandler().AddEvent(e);
|
||||||
|
|
||||||
|
string formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"{loc["SERVER_KICK_TEXT"]} - ^5{Reason}^7");
|
||||||
|
await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin)
|
public override async Task TempBan(string Reason, TimeSpan length, EFClient targetClient, EFClient originClient)
|
||||||
{
|
{
|
||||||
// ensure player gets banned if command not performed on them in game
|
// ensure player gets kicked if command not performed on them in the same server
|
||||||
if (Target.ClientNumber < 0)
|
targetClient = targetClient.ClientNumber < 0 ?
|
||||||
{
|
Manager.GetActiveClients()
|
||||||
var ingameClient = Manager.GetActiveClients()
|
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
||||||
.FirstOrDefault(c => c.ClientId == Target.ClientId);
|
targetClient;
|
||||||
|
|
||||||
if (ingameClient != null)
|
|
||||||
{
|
|
||||||
await TempBan(Reason, length, ingameClient, Origin);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if !DEBUG
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string formattedKick = String.Format(RconParser.Configuration.CommandPrefixes.Kick, Target.ClientNumber, $"^7{loc["SERVER_TB_TEXT"]}- ^5{Reason}");
|
|
||||||
await Target.CurrentServer.ExecuteCommandAsync(formattedKick);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
await Target.CurrentServer.OnClientDisconnected(Target);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var newPenalty = new EFPenalty()
|
var newPenalty = new EFPenalty()
|
||||||
{
|
{
|
||||||
Type = EFPenalty.PenaltyType.TempBan,
|
Type = EFPenalty.PenaltyType.TempBan,
|
||||||
Expires = DateTime.UtcNow + length,
|
Expires = DateTime.UtcNow + length,
|
||||||
Offender = Target,
|
Offender = targetClient,
|
||||||
Offense = Reason,
|
Offense = Reason,
|
||||||
Punisher = Origin,
|
Punisher = originClient,
|
||||||
Link = Target.AliasLink
|
Link = targetClient.AliasLink
|
||||||
};
|
};
|
||||||
|
|
||||||
await Manager.GetPenaltyService().Create(newPenalty);
|
Logger.WriteDebug($"Creating tempban penalty for {targetClient}");
|
||||||
|
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), Manager.GetLogger(0));
|
||||||
|
|
||||||
|
if (targetClient.IsIngame)
|
||||||
|
{
|
||||||
|
string formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"^7{loc["SERVER_TB_TEXT"]}- ^5{Reason}");
|
||||||
|
await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false)
|
override public async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false)
|
||||||
{
|
{
|
||||||
// ensure player gets banned if command not performed on them in game
|
// ensure player gets kicked if command not performed on them in the same server
|
||||||
if (targetClient.ClientNumber < 0)
|
targetClient = targetClient.ClientNumber < 0 ?
|
||||||
{
|
Manager.GetActiveClients()
|
||||||
EFClient ingameClient = null;
|
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
||||||
|
targetClient;
|
||||||
ingameClient = Manager.GetServers()
|
|
||||||
.Select(s => s.GetClientsAsList())
|
|
||||||
.FirstOrDefault(l => l.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) != null)
|
|
||||||
?.First(c => c.ClientId == targetClient.ClientId);
|
|
||||||
|
|
||||||
if (ingameClient != null)
|
|
||||||
{
|
|
||||||
await Ban(reason, ingameClient, originClient, isEvade);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#if !DEBUG
|
|
||||||
string formattedString = string.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{reason} ^7{loc["SERVER_BAN_APPEAL"].FormatExt(Website)}^7");
|
|
||||||
await targetClient.CurrentServer.ExecuteCommandAsync(formattedString);
|
|
||||||
#else
|
|
||||||
await targetClient.CurrentServer.OnClientDisconnected(targetClient);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
EFPenalty newPenalty = new EFPenalty()
|
EFPenalty newPenalty = new EFPenalty()
|
||||||
{
|
{
|
||||||
@ -1260,8 +1213,16 @@ namespace IW4MAdmin
|
|||||||
IsEvadedOffense = isEvade
|
IsEvadedOffense = isEvade
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Logger.WriteDebug($"Creating ban penalty for {targetClient}");
|
||||||
targetClient.SetLevel(Permission.Banned, originClient);
|
targetClient.SetLevel(Permission.Banned, originClient);
|
||||||
await Manager.GetPenaltyService().Create(newPenalty);
|
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), Manager.GetLogger(0));
|
||||||
|
|
||||||
|
if (targetClient.IsIngame)
|
||||||
|
{
|
||||||
|
Logger.WriteDebug($"Attempting to kicking newly banned client {targetClient}");
|
||||||
|
string formattedString = string.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{reason} ^7{loc["SERVER_BAN_APPEAL"].FormatExt(Website)}^7");
|
||||||
|
await targetClient.CurrentServer.ExecuteCommandAsync(formattedString);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override public async Task Unban(string reason, EFClient Target, EFClient Origin)
|
override public async Task Unban(string reason, EFClient Target, EFClient Origin)
|
||||||
|
@ -9,7 +9,7 @@ namespace IW4MAdmin.Application.RconParsers
|
|||||||
/// generic implementation of the IRConParserConfiguration
|
/// generic implementation of the IRConParserConfiguration
|
||||||
/// allows script plugins to generate dynamic RCon configurations
|
/// allows script plugins to generate dynamic RCon configurations
|
||||||
/// </summary>
|
/// </summary>
|
||||||
sealed internal class DynamicRConParserConfiguration : IRConParserConfiguration
|
public class DynamicRConParserConfiguration : IRConParserConfiguration
|
||||||
{
|
{
|
||||||
public CommandPrefix CommandPrefixes { get; set; }
|
public CommandPrefix CommandPrefixes { get; set; }
|
||||||
public ParserRegex Status { get; set; }
|
public ParserRegex Status { get; set; }
|
||||||
|
@ -82,7 +82,10 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
|||||||
KillStreak = 0;
|
KillStreak = 0;
|
||||||
DeathStreak = 0;
|
DeathStreak = 0;
|
||||||
LastScore = 0;
|
LastScore = 0;
|
||||||
SessionScores.Add(0);
|
lock (SessionScores)
|
||||||
|
{
|
||||||
|
SessionScores.Add(0);
|
||||||
|
}
|
||||||
Team = IW4Info.Team.None;
|
Team = IW4Info.Team.None;
|
||||||
}
|
}
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
|
@ -124,6 +124,9 @@ namespace SharedLibraryCore.Database.Models
|
|||||||
[NotMapped]
|
[NotMapped]
|
||||||
public string IPAddressString => IPAddress.ConvertIPtoString();
|
public string IPAddressString => IPAddress.ConvertIPtoString();
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsIngame => ClientNumber >= 0;
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public virtual IDictionary<int, long> LinkedAccounts { get; set; }
|
public virtual IDictionary<int, long> LinkedAccounts { get; set; }
|
||||||
|
|
||||||
|
@ -121,8 +121,7 @@ namespace SharedLibraryCore
|
|||||||
/// <param name="message">Message to be sent to all players</param>
|
/// <param name="message">Message to be sent to all players</param>
|
||||||
public GameEvent Broadcast(string message, EFClient sender = null)
|
public GameEvent Broadcast(string message, EFClient sender = null)
|
||||||
{
|
{
|
||||||
string formattedMessage = String.Format(RconParser.Configuration.CommandPrefixes.Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{message.FixIW4ForwardSlash()}");
|
string formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Say, $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{message.FixIW4ForwardSlash()}");
|
||||||
|
|
||||||
#if DEBUG == true
|
#if DEBUG == true
|
||||||
Logger.WriteVerbose(message.StripColors());
|
Logger.WriteVerbose(message.StripColors());
|
||||||
#endif
|
#endif
|
||||||
@ -196,14 +195,14 @@ namespace SharedLibraryCore
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Reason">Reason for kicking</param>
|
/// <param name="Reason">Reason for kicking</param>
|
||||||
/// <param name="Target">EFClient to kick</param>
|
/// <param name="Target">EFClient to kick</param>
|
||||||
abstract protected Task Kick(String Reason, EFClient Target, EFClient Origin);
|
abstract public Task Kick(String Reason, EFClient Target, EFClient Origin);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Temporarily ban a player ( default 1 hour ) from the server
|
/// Temporarily ban a player ( default 1 hour ) from the server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Reason">Reason for banning the player</param>
|
/// <param name="Reason">Reason for banning the player</param>
|
||||||
/// <param name="Target">The player to ban</param>
|
/// <param name="Target">The player to ban</param>
|
||||||
abstract protected Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin);
|
abstract public Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Perm ban a player from the server
|
/// Perm ban a player from the server
|
||||||
@ -211,9 +210,9 @@ namespace SharedLibraryCore
|
|||||||
/// <param name="Reason">The reason for the ban</param>
|
/// <param name="Reason">The reason for the ban</param>
|
||||||
/// <param name="Target">The person to ban</param>
|
/// <param name="Target">The person to ban</param>
|
||||||
/// <param name="Origin">The person who banned the target</param>
|
/// <param name="Origin">The person who banned the target</param>
|
||||||
abstract protected Task Ban(String Reason, EFClient Target, EFClient Origin, bool isEvade = false);
|
abstract public Task Ban(String Reason, EFClient Target, EFClient Origin, bool isEvade = false);
|
||||||
|
|
||||||
abstract protected Task Warn(String Reason, EFClient Target, EFClient Origin);
|
abstract public Task Warn(String Reason, EFClient Target, EFClient Origin);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unban a player by npID / GUID
|
/// Unban a player by npID / GUID
|
||||||
@ -307,7 +306,7 @@ namespace SharedLibraryCore
|
|||||||
public bool CustomCallback { get; protected set; }
|
public bool CustomCallback { get; protected set; }
|
||||||
public string WorkingDirectory { get; protected set; }
|
public string WorkingDirectory { get; protected set; }
|
||||||
public IRConConnection RemoteConnection { get; protected set; }
|
public IRConConnection RemoteConnection { get; protected set; }
|
||||||
public IRConParser RconParser { get; protected set; }
|
public IRConParser RconParser { get; set; }
|
||||||
public IEventParser EventParser { get; set; }
|
public IEventParser EventParser { get; set; }
|
||||||
public string LogPath { get; protected set; }
|
public string LogPath { get; protected set; }
|
||||||
public bool RestartRequested { get; set; }
|
public bool RestartRequested { get; set; }
|
||||||
|
@ -12,7 +12,7 @@ namespace SharedLibraryCore.Services
|
|||||||
{
|
{
|
||||||
public class PenaltyService : Interfaces.IEntityService<EFPenalty>
|
public class PenaltyService : Interfaces.IEntityService<EFPenalty>
|
||||||
{
|
{
|
||||||
public async Task<EFPenalty> Create(EFPenalty newEntity)
|
public virtual async Task<EFPenalty> Create(EFPenalty newEntity)
|
||||||
{
|
{
|
||||||
using (var context = new DatabaseContext())
|
using (var context = new DatabaseContext())
|
||||||
{
|
{
|
||||||
@ -181,7 +181,7 @@ namespace SharedLibraryCore.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveActivePenalties(int aliasLinkId)
|
public virtual async Task RemoveActivePenalties(int aliasLinkId)
|
||||||
{
|
{
|
||||||
using (var context = new DatabaseContext())
|
using (var context = new DatabaseContext())
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore.Storage;
|
|||||||
#endif
|
#endif
|
||||||
using SharedLibraryCore.Database.Models;
|
using SharedLibraryCore.Database.Models;
|
||||||
using SharedLibraryCore.Helpers;
|
using SharedLibraryCore.Helpers;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@ -875,6 +876,30 @@ namespace SharedLibraryCore
|
|||||||
return new[] { deltaX, deltaY };
|
return new[] { deltaX, deltaY };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// attempts to create and persist a penalty
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="penalty"></param>
|
||||||
|
/// <param name="penaltyService"></param>
|
||||||
|
/// <param name="logger"></param>
|
||||||
|
/// <returns>true of the creat succeeds, false otherwise</returns>
|
||||||
|
public static async Task<bool> TryCreatePenalty(this EFPenalty penalty, IEntityService<EFPenalty> penaltyService, ILogger logger)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await penaltyService.Create(penalty);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.WriteWarning($"Could not create penalty of type {penalty.Type.ToString()}");
|
||||||
|
logger.WriteDebug(e.GetExceptionInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static bool ShouldHideLevel(this Permission perm) => perm == Permission.Flagged;
|
public static bool ShouldHideLevel(this Permission perm) => perm == Permission.Flagged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
24
Tests/ApplicationTests/Fixtures/ClientGenerators.cs
Normal file
24
Tests/ApplicationTests/Fixtures/ClientGenerators.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ApplicationTests.Fixtures
|
||||||
|
{
|
||||||
|
public class ClientGenerators
|
||||||
|
{
|
||||||
|
public static EFClient CreateBasicClient(Server currentServer, bool isIngame = true) => new EFClient()
|
||||||
|
{
|
||||||
|
ClientId = 1,
|
||||||
|
CurrentAlias = new EFAlias()
|
||||||
|
{
|
||||||
|
Name = "BasicClient",
|
||||||
|
IPAddress = "127.0.0.1".ConvertToIP(),
|
||||||
|
},
|
||||||
|
Level = EFClient.Permission.User,
|
||||||
|
ClientNumber = isIngame ? 0 : -1,
|
||||||
|
CurrentServer = currentServer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
22
Tests/ApplicationTests/Fixtures/ConfigurationGenerators.cs
Normal file
22
Tests/ApplicationTests/Fixtures/ConfigurationGenerators.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using IW4MAdmin.Application.RconParsers;
|
||||||
|
using SharedLibraryCore.Configuration;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ApplicationTests.Fixtures
|
||||||
|
{
|
||||||
|
public class ConfigurationGenerators
|
||||||
|
{
|
||||||
|
public static ServerConfiguration CreateServerConfiguration() => new ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 };
|
||||||
|
public static IRConParserConfiguration CreateRConParserConfiguration(IParserRegexFactory factory) => new DynamicRConParserConfiguration(factory)
|
||||||
|
{
|
||||||
|
CommandPrefixes = new SharedLibraryCore.RCon.CommandPrefix()
|
||||||
|
{
|
||||||
|
Kick = "kick",
|
||||||
|
Say = "say"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,55 @@
|
|||||||
using IW4MAdmin;
|
using IW4MAdmin;
|
||||||
using IW4MAdmin.Application.Misc;
|
using IW4MAdmin.Application.Misc;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using FakeItEasy;
|
||||||
|
using System;
|
||||||
|
using ApplicationTests.Fixtures;
|
||||||
|
using SharedLibraryCore.Services;
|
||||||
|
using SharedLibraryCore.Database.Models;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ApplicationTests.Mocks;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace ApplicationTests
|
namespace ApplicationTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class IW4MServerTests
|
public class IW4MServerTests
|
||||||
{
|
{
|
||||||
|
private IServiceProvider serviceProvider;
|
||||||
|
private ILogger fakeLogger;
|
||||||
|
private IManager fakeManager;
|
||||||
|
private IRConConnection fakeRConConnection;
|
||||||
|
private IRConParser fakeRConParser;
|
||||||
|
private MockEventHandler mockEventHandler;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
fakeLogger = A.Fake<ILogger>();
|
||||||
|
fakeManager = A.Fake<IManager>();
|
||||||
|
fakeRConConnection = A.Fake<IRConConnection>();
|
||||||
|
var rconConnectionFactory = A.Fake<IRConConnectionFactory>();
|
||||||
|
A.CallTo(() => rconConnectionFactory.CreateConnection(A<string>.Ignored, A<int>.Ignored, A<string>.Ignored))
|
||||||
|
.Returns(fakeRConConnection);
|
||||||
|
var fakeTranslationLookup = A.Fake<ITranslationLookup>();
|
||||||
|
fakeRConParser = A.Fake<IRConParser>();
|
||||||
|
A.CallTo(() => fakeRConParser.Configuration)
|
||||||
|
.Returns(ConfigurationGenerators.CreateRConParserConfiguration(A.Fake<IParserRegexFactory>()));
|
||||||
|
|
||||||
|
mockEventHandler = new MockEventHandler();
|
||||||
|
A.CallTo(() => fakeManager.GetEventHandler())
|
||||||
|
.Returns(mockEventHandler);
|
||||||
|
|
||||||
|
serviceProvider = new ServiceCollection()
|
||||||
|
.AddSingleton(new IW4MServer(fakeManager, ConfigurationGenerators.CreateServerConfiguration(), fakeTranslationLookup, rconConnectionFactory)
|
||||||
|
{
|
||||||
|
RconParser = fakeRConParser
|
||||||
|
})
|
||||||
|
.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_GenerateLogPath_Basic()
|
public void Test_GenerateLogPath_Basic()
|
||||||
{
|
{
|
||||||
@ -133,5 +176,337 @@ namespace ApplicationTests
|
|||||||
|
|
||||||
Assert.AreEqual(expected, generated);
|
Assert.AreEqual(expected, generated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region BAN
|
||||||
|
[Test]
|
||||||
|
public async Task Test_BanCreatesPenalty()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
await server.Ban("test reason", target, origin);
|
||||||
|
|
||||||
|
A.CallTo(() => fakePenaltyService.Create(A<EFPenalty>.Ignored))
|
||||||
|
.MustHaveHappenedOnceExactly();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_BanExecutesKickCommand()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
await server.Ban("test reason", target, origin);
|
||||||
|
|
||||||
|
A.CallTo(() => fakeRConParser.ExecuteCommandAsync(fakeRConConnection, "kick"))
|
||||||
|
.MustHaveHappenedOnceExactly();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_BanQueuesSetLevelEvent()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
await server.Ban("test reason", target, origin);
|
||||||
|
|
||||||
|
Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Type == SharedLibraryCore.GameEvent.EventType.ChangePermission &&
|
||||||
|
_event.Origin == origin &&
|
||||||
|
_event.Target == target &&
|
||||||
|
(EFClient.Permission)_event.Extra == EFClient.Permission.Banned));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_BanFindsIngameClientToExecuteFor()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server, isIngame: false);
|
||||||
|
var ingameTarget = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
A.CallTo(() => fakeManager.GetActiveClients())
|
||||||
|
.Returns(new[] { ingameTarget });
|
||||||
|
|
||||||
|
await server.Ban("test reason", target, origin);
|
||||||
|
|
||||||
|
Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Target == ingameTarget));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region TEMPBAN
|
||||||
|
[Test]
|
||||||
|
public async Task Test_TempBanCreatesPenalty()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
await server.TempBan("test reason", TimeSpan.Zero, target, origin);
|
||||||
|
|
||||||
|
A.CallTo(() => fakePenaltyService.Create(A<EFPenalty>.Ignored))
|
||||||
|
.MustHaveHappenedOnceExactly();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_TempBanExecutesKickCommand()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
await server.TempBan("test reason", TimeSpan.Zero, target, origin);
|
||||||
|
|
||||||
|
A.CallTo(() => fakeRConParser.ExecuteCommandAsync(fakeRConConnection, "kick"))
|
||||||
|
.MustHaveHappenedOnceExactly();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_TempBanFindsIngameClientToExecuteFor()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server, isIngame: false);
|
||||||
|
|
||||||
|
var ingameTarget = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
A.CallTo(() => fakeManager.GetActiveClients())
|
||||||
|
.Returns(new[] { ingameTarget });
|
||||||
|
|
||||||
|
await server.TempBan("test reason", TimeSpan.Zero, target, origin);
|
||||||
|
|
||||||
|
A.CallTo(() => fakeRConParser.ExecuteCommandAsync(fakeRConConnection, "kick"))
|
||||||
|
.MustHaveHappenedOnceExactly();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region KICK
|
||||||
|
[Test]
|
||||||
|
public async Task Test_KickCreatesPenalty()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
await server.Kick("test reason", target, origin);
|
||||||
|
|
||||||
|
A.CallTo(() => fakePenaltyService.Create(A<EFPenalty>.Ignored))
|
||||||
|
.MustHaveHappenedOnceExactly();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_KickExecutesKickCommand()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
await server.Kick("test reason", target, origin);
|
||||||
|
|
||||||
|
A.CallTo(() => fakeRConParser.ExecuteCommandAsync(fakeRConConnection, "kick"))
|
||||||
|
.MustHaveHappenedOnceExactly();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_KickQueuesPredisconnectEvent()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
await server.Kick("test reason", target, origin);
|
||||||
|
|
||||||
|
Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Type == SharedLibraryCore.GameEvent.EventType.PreDisconnect && _event.Origin == target));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_KickFindsIngameClientToExecuteFor()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server, isIngame: false);
|
||||||
|
|
||||||
|
var ingameTarget = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
A.CallTo(() => fakeManager.GetActiveClients())
|
||||||
|
.Returns(new[] { ingameTarget });
|
||||||
|
|
||||||
|
await server.Kick("test reason", target, origin);
|
||||||
|
|
||||||
|
// kick creates a pre disconnect event
|
||||||
|
Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Origin == ingameTarget));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region WARN
|
||||||
|
[Test]
|
||||||
|
public async Task Test_WarnCreatesPenalty()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
await server.Warn("test reason", target, origin);
|
||||||
|
|
||||||
|
A.CallTo(() => fakePenaltyService.Create(A<EFPenalty>.Ignored))
|
||||||
|
.MustHaveHappenedOnceExactly();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_WarnBroadCastMessageForIngameClient()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
await server.Warn("test reason", target, origin);
|
||||||
|
|
||||||
|
Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Type == SharedLibraryCore.GameEvent.EventType.Broadcast));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_WarnLimitReachedQueuesKickEvent()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
target.Warnings = 5;
|
||||||
|
|
||||||
|
await server.Warn("test reason", target, origin);
|
||||||
|
|
||||||
|
Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Type == SharedLibraryCore.GameEvent.EventType.Kick && _event.Target == target));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region UNBAN
|
||||||
|
[Test]
|
||||||
|
public async Task Test_UnbanQueuesSetLevelEvent()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
A.CallTo(() => fakePenaltyService.RemoveActivePenalties(A<int>.Ignored))
|
||||||
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
target.Level = EFClient.Permission.Banned;
|
||||||
|
target.AliasLink = new EFAliasLink();
|
||||||
|
|
||||||
|
await server.Unban("test reason", target, origin);
|
||||||
|
|
||||||
|
Assert.IsTrue(mockEventHandler.Events.Any(_event => _event.Type == SharedLibraryCore.GameEvent.EventType.ChangePermission && _event.Target == target));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_UnbanRemovedActivePenalties()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
A.CallTo(() => fakePenaltyService.RemoveActivePenalties(A<int>.Ignored))
|
||||||
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
target.Level = EFClient.Permission.Banned;
|
||||||
|
target.AliasLink = new EFAliasLink()
|
||||||
|
{
|
||||||
|
AliasLinkId = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
await server.Unban("test reason", target, origin);
|
||||||
|
|
||||||
|
A.CallTo(() => fakePenaltyService.RemoveActivePenalties(target.AliasLink.AliasLinkId))
|
||||||
|
.MustHaveHappened();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test_UnbanCreatesPenalty()
|
||||||
|
{
|
||||||
|
var fakePenaltyService = A.Fake<PenaltyService>();
|
||||||
|
A.CallTo(() => fakeManager.GetPenaltyService())
|
||||||
|
.Returns(fakePenaltyService);
|
||||||
|
A.CallTo(() => fakePenaltyService.RemoveActivePenalties(A<int>.Ignored))
|
||||||
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||||
|
var origin = ClientGenerators.CreateBasicClient(server);
|
||||||
|
var target = ClientGenerators.CreateBasicClient(server);
|
||||||
|
|
||||||
|
target.Level = EFClient.Permission.Banned;
|
||||||
|
target.AliasLink = new EFAliasLink()
|
||||||
|
{
|
||||||
|
AliasLinkId = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
await server.Unban("test reason", target, origin);
|
||||||
|
|
||||||
|
A.CallTo(() => fakePenaltyService.Create(A<EFPenalty>.Ignored))
|
||||||
|
.MustHaveHappened();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
Tests/ApplicationTests/Mocks/EventHandler.cs
Normal file
16
Tests/ApplicationTests/Mocks/EventHandler.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ApplicationTests.Mocks
|
||||||
|
{
|
||||||
|
class MockEventHandler : IEventHandler
|
||||||
|
{
|
||||||
|
public IList<GameEvent> Events = new List<GameEvent>();
|
||||||
|
|
||||||
|
public void AddEvent(GameEvent gameEvent)
|
||||||
|
{
|
||||||
|
Events.Add(gameEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user