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:
RaidMax 2020-04-21 17:34:00 -05:00
parent 3ae2e42718
commit 9e74dac5ed
11 changed files with 554 additions and 126 deletions

View File

@ -1096,158 +1096,111 @@ namespace IW4MAdmin
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
if (Target.ClientNumber < 0)
{
var ingameClient = Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == Target.ClientId);
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);
}
targetClient = targetClient.ClientNumber < 0 ?
Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
targetClient;
var newPenalty = new EFPenalty()
{
Type = EFPenalty.PenaltyType.Warning,
Expires = DateTime.UtcNow,
Offender = Target,
Punisher = Origin,
Offense = Reason,
Link = Target.AliasLink
Offender = targetClient,
Punisher = targetOrigin,
Offense = reason,
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)
if (targetClient.Warnings >= 4)
{
var ingameClient = Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == Target.ClientId);
if (ingameClient != null)
{
await Kick(Reason, ingameClient, Origin);
targetClient.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this));
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
// await Target.CurrentServer.OnClientDisconnected(Target);
var e = new GameEvent()
{
Type = GameEvent.EventType.PreDisconnect,
Origin = Target,
Owner = this
};
string message = $"^1{loc["SERVER_WARNING"]} ^7[^3{targetClient.Warnings}^7]: ^3{targetClient.Name}^7, {reason}";
targetClient.CurrentServer.Broadcast(message);
}
}
Manager.GetEventHandler().AddEvent(e);
#endif
public override async Task Kick(string Reason, EFClient targetClient, EFClient originClient)
{
targetClient = targetClient.ClientNumber < 0 ?
Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
targetClient;
var newPenalty = new EFPenalty()
{
Type = EFPenalty.PenaltyType.Kick,
Expires = DateTime.UtcNow,
Offender = Target,
Offender = targetClient,
Offense = Reason,
Punisher = Origin,
Link = Target.AliasLink
Punisher = originClient,
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
if (Target.ClientNumber < 0)
{
var ingameClient = Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == Target.ClientId);
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
// ensure player gets kicked if command not performed on them in the same server
targetClient = targetClient.ClientNumber < 0 ?
Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
targetClient;
var newPenalty = new EFPenalty()
{
Type = EFPenalty.PenaltyType.TempBan,
Expires = DateTime.UtcNow + length,
Offender = Target,
Offender = targetClient,
Offense = Reason,
Punisher = Origin,
Link = Target.AliasLink
Punisher = originClient,
Link = targetClient.AliasLink
};
await Manager.GetPenaltyService().Create(newPenalty);
}
Logger.WriteDebug($"Creating tempban penalty for {targetClient}");
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), Manager.GetLogger(0));
override protected async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false)
if (targetClient.IsIngame)
{
// ensure player gets banned if command not performed on them in game
if (targetClient.ClientNumber < 0)
{
EFClient ingameClient = null;
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;
string formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"^7{loc["SERVER_TB_TEXT"]}- ^5{Reason}");
await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick);
}
}
else
override public async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false)
{
#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
}
// ensure player gets kicked if command not performed on them in the same server
targetClient = targetClient.ClientNumber < 0 ?
Manager.GetActiveClients()
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
targetClient;
EFPenalty newPenalty = new EFPenalty()
{
@ -1260,8 +1213,16 @@ namespace IW4MAdmin
IsEvadedOffense = isEvade
};
Logger.WriteDebug($"Creating ban penalty for {targetClient}");
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)

View File

@ -9,7 +9,7 @@ namespace IW4MAdmin.Application.RconParsers
/// generic implementation of the IRConParserConfiguration
/// allows script plugins to generate dynamic RCon configurations
/// </summary>
sealed internal class DynamicRConParserConfiguration : IRConParserConfiguration
public class DynamicRConParserConfiguration : IRConParserConfiguration
{
public CommandPrefix CommandPrefixes { get; set; }
public ParserRegex Status { get; set; }

View File

@ -82,7 +82,10 @@ namespace IW4MAdmin.Plugins.Stats.Models
KillStreak = 0;
DeathStreak = 0;
LastScore = 0;
lock (SessionScores)
{
SessionScores.Add(0);
}
Team = IW4Info.Team.None;
}
[NotMapped]

View File

@ -124,6 +124,9 @@ namespace SharedLibraryCore.Database.Models
[NotMapped]
public string IPAddressString => IPAddress.ConvertIPtoString();
[NotMapped]
public bool IsIngame => ClientNumber >= 0;
[NotMapped]
public virtual IDictionary<int, long> LinkedAccounts { get; set; }

View File

@ -121,8 +121,7 @@ namespace SharedLibraryCore
/// <param name="message">Message to be sent to all players</param>
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
Logger.WriteVerbose(message.StripColors());
#endif
@ -196,14 +195,14 @@ namespace SharedLibraryCore
/// </summary>
/// <param name="Reason">Reason for kicking</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>
/// Temporarily ban a player ( default 1 hour ) from the server
/// </summary>
/// <param name="Reason">Reason for banning the player</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>
/// Perm ban a player from the server
@ -211,9 +210,9 @@ namespace SharedLibraryCore
/// <param name="Reason">The reason for the ban</param>
/// <param name="Target">The person to ban</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>
/// Unban a player by npID / GUID
@ -307,7 +306,7 @@ namespace SharedLibraryCore
public bool CustomCallback { get; protected set; }
public string WorkingDirectory { 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 string LogPath { get; protected set; }
public bool RestartRequested { get; set; }

View File

@ -12,7 +12,7 @@ namespace SharedLibraryCore.Services
{
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())
{
@ -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())
{

View File

@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore.Storage;
#endif
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -875,6 +876,30 @@ namespace SharedLibraryCore
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;
/// <summary>

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

View 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"
}
};
}
}

View File

@ -1,12 +1,55 @@
using IW4MAdmin;
using IW4MAdmin.Application.Misc;
using SharedLibraryCore.Interfaces;
using Microsoft.Extensions.DependencyInjection;
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
{
[TestFixture]
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]
public void Test_GenerateLogPath_Basic()
{
@ -133,5 +176,337 @@ namespace ApplicationTests
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
}
}

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