diff --git a/EvoSC.sln b/EvoSC.sln index 95a966060..8820dabb7 100644 --- a/EvoSC.sln +++ b/EvoSC.sln @@ -138,6 +138,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameModeUiModule.Tests", "t EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiveRankingModule.Tests", "tests\Modules\LiveRankingModule.Tests\LiveRankingModule.Tests.csproj", "{4AA1890A-1423-4831-95B2-E29B9A7A58D1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamChatModule", "src/Modules/TeamChatModule/TeamChatModule.csproj", "{DDB0A249-BD1C-4556-BFE1-362B17EDA874}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamChatModule.Tests", "tests\Modules\TeamChatModule.Tests\TeamChatModule.Tests.csproj", "{7BD60D6E-7B7E-4771-87C0-7F98FC82F990}" +EndProject + @@ -417,6 +422,14 @@ Global {4AA1890A-1423-4831-95B2-E29B9A7A58D1}.Debug|Any CPU.Build.0 = Debug|Any CPU {4AA1890A-1423-4831-95B2-E29B9A7A58D1}.Release|Any CPU.ActiveCfg = Release|Any CPU {4AA1890A-1423-4831-95B2-E29B9A7A58D1}.Release|Any CPU.Build.0 = Release|Any CPU + {DDB0A249-BD1C-4556-BFE1-362B17EDA874}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DDB0A249-BD1C-4556-BFE1-362B17EDA874}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DDB0A249-BD1C-4556-BFE1-362B17EDA874}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DDB0A249-BD1C-4556-BFE1-362B17EDA874}.Release|Any CPU.Build.0 = Release|Any CPU + {7BD60D6E-7B7E-4771-87C0-7F98FC82F990}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BD60D6E-7B7E-4771-87C0-7F98FC82F990}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BD60D6E-7B7E-4771-87C0-7F98FC82F990}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BD60D6E-7B7E-4771-87C0-7F98FC82F990}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -482,5 +495,7 @@ Global {5BA1FF1B-8CB0-4FF5-B6C0-4E2E323D446E} = {DC47658A-F421-4BA4-B617-090A7DFB3900} {5B515690-0F0B-44D1-B644-3524A83A17CE} = {6D75D6A2-6ECD-4DE4-96C5-CAD7D134407A} {4AA1890A-1423-4831-95B2-E29B9A7A58D1} = {6D75D6A2-6ECD-4DE4-96C5-CAD7D134407A} + {DDB0A249-BD1C-4556-BFE1-362B17EDA874} = {DC47658A-F421-4BA4-B617-090A7DFB3900} + {7BD60D6E-7B7E-4771-87C0-7F98FC82F990} = {6D75D6A2-6ECD-4DE4-96C5-CAD7D134407A} EndGlobalSection EndGlobal diff --git a/src/EvoSC.CLI/CliStartup.cs b/src/EvoSC.CLI/CliStartup.cs index 042b7673a..c77581a88 100644 --- a/src/EvoSC.CLI/CliStartup.cs +++ b/src/EvoSC.CLI/CliStartup.cs @@ -81,6 +81,10 @@ public static void SetupBasePipeline(this IStartupPipeline pipeline, IEvoScBaseC .RegisterSingleton() , "Logging") + .Services(AppFeature.Chat, s => s + .Register(Lifestyle.Transient) + , "GbxRemoteClient", "Themes") + .Services(AppFeature.ChatCommands, s => s .AddEvoScChatCommands() , "Logging", "PlayerManager") diff --git a/src/EvoSC.Commands/Middleware/CommandsMiddleware.cs b/src/EvoSC.Commands/Middleware/CommandsMiddleware.cs index 1e26c517b..25706c7e1 100644 --- a/src/EvoSC.Commands/Middleware/CommandsMiddleware.cs +++ b/src/EvoSC.Commands/Middleware/CommandsMiddleware.cs @@ -21,7 +21,7 @@ public class CommandsMiddleware(ActionDelegate next, ILogger { private readonly ChatCommandParser _parser = new(cmdManager); - async Task HandleUserErrorsAsync(IParserResult result, string playerLogin) + async Task HandleUserErrorsAsync(IParserResult result, IPlayer player) { if (result.Exception is CommandParserException cmdParserException) { @@ -31,12 +31,12 @@ async Task HandleUserErrorsAsync(IParserResult result, string playerLogin) } var message = $"Error: {cmdParserException.Message}"; - await serverClient.SendChatMessageAsync(playerLogin, $"Error: {message}"); + await serverClient.Chat.ErrorMessageAsync($"Error: {message}", player); } if (result.Exception is PlayerNotFoundException playerNotFoundException) { - await serverClient.SendChatMessageAsync(playerLogin, $"Error: {playerNotFoundException.Message}"); + await serverClient.Chat.ErrorMessageAsync($"Error: {playerNotFoundException.Message}", player); } else { @@ -55,7 +55,7 @@ private async Task ExecuteCommandAsync(IChatCommand cmd, object[] args, ChatRout var contextServerClient = context.ServiceScope.Container.GetRequiredService(); - var playerInteractionContext = new CommandInteractionContext((IOnlinePlayer)routerContext.Player, contextServerClient, context) + var playerInteractionContext = new CommandInteractionContext((IOnlinePlayer)routerContext.Author, contextServerClient, context) { CommandExecuted = cmd }; @@ -122,7 +122,7 @@ public async Task ExecuteAsync(ChatRouterPipelineContext context) } else if (parserResult.Exception != null) { - await HandleUserErrorsAsync(parserResult, context.Player.GetLogin()); + await HandleUserErrorsAsync(parserResult, context.Author); } else { diff --git a/src/EvoSC.Commands/Middleware/CommandsPermissionMiddleware.cs b/src/EvoSC.Commands/Middleware/CommandsPermissionMiddleware.cs index ad81a7a95..62fbff58f 100644 --- a/src/EvoSC.Commands/Middleware/CommandsPermissionMiddleware.cs +++ b/src/EvoSC.Commands/Middleware/CommandsPermissionMiddleware.cs @@ -26,6 +26,6 @@ await permissions.HasPermissionAsync(cmdContext.Player, cmdContext.CommandExecut return; } - await server.SendChatMessageAsync(cmdContext.Player, "Insufficient permissions to run this command."); + await server.Chat.SendChatMessageAsync("Insufficient permissions to run this command.", cmdContext.Player); } } diff --git a/src/EvoSC.Common/Application/AppFeature.cs b/src/EvoSC.Common/Application/AppFeature.cs index 4677c6a25..7a474f3e6 100644 --- a/src/EvoSC.Common/Application/AppFeature.cs +++ b/src/EvoSC.Common/Application/AppFeature.cs @@ -122,4 +122,10 @@ public enum AppFeature /// [Identifier(NoPrefix = true)] Themes, + + /// + /// Optimized way to send chat messages to a set of players of any size. + /// + [Identifier(NoPrefix = true)] + Chat, } diff --git a/src/EvoSC.Common/Interfaces/Controllers/IPlayerInteractionContext.cs b/src/EvoSC.Common/Interfaces/Controllers/IPlayerInteractionContext.cs index df1a27ca1..0581d3094 100644 --- a/src/EvoSC.Common/Interfaces/Controllers/IPlayerInteractionContext.cs +++ b/src/EvoSC.Common/Interfaces/Controllers/IPlayerInteractionContext.cs @@ -1,4 +1,5 @@ using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; namespace EvoSC.Common.Interfaces.Controllers; @@ -6,4 +7,5 @@ public interface IPlayerInteractionContext : IGenericControllerContext { public IOnlinePlayer Player { get; } public IServerClient Server { get; } + public IChatService Chat => Server.Chat; } diff --git a/src/EvoSC.Common/Interfaces/IRemoteChatRouter.cs b/src/EvoSC.Common/Interfaces/IRemoteChatRouter.cs index b33683361..09e12f4c4 100644 --- a/src/EvoSC.Common/Interfaces/IRemoteChatRouter.cs +++ b/src/EvoSC.Common/Interfaces/IRemoteChatRouter.cs @@ -1,5 +1,10 @@ -namespace EvoSC.Common.Interfaces; +using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Remote.ChatRouter; + +namespace EvoSC.Common.Interfaces; public interface IRemoteChatRouter { + public Task SendMessageAsync(string message, IOnlinePlayer actor); + public Task SendMessageAsync(ChatRouterPipelineContext pipelineContext); } diff --git a/src/EvoSC.Common/Interfaces/IServerClient.cs b/src/EvoSC.Common/Interfaces/IServerClient.cs index 15424f491..292820532 100644 --- a/src/EvoSC.Common/Interfaces/IServerClient.cs +++ b/src/EvoSC.Common/Interfaces/IServerClient.cs @@ -1,4 +1,6 @@ using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; +using EvoSC.Common.Util.TextFormatting; using GbxRemoteNet.Interfaces; namespace EvoSC.Common.Interfaces; @@ -13,6 +15,8 @@ public interface IServerClient /// Whether the client is connected to the remote XMLRPC server or not. /// public bool Connected { get; } + + public IChatService Chat { get; } /// /// Start the client and set up a connection. @@ -29,66 +33,8 @@ public interface IServerClient public Task StopAsync(CancellationToken token); /// - /// Send an info message to the chat. - /// - /// Text to send. - /// - public Task InfoMessageAsync(string text); - - /// - /// Send an info message to a specific player. - /// - /// Player to send the message to. - /// Text to send. - /// - public Task InfoMessageAsync(IPlayer player, string text); - - /// - /// Send a success message to the chat. - /// - /// Text to send. - /// - public Task SuccessMessageAsync(string text); - - /// - /// Send a success message to a specific player. + /// Get the server's maps directory. /// - /// Player to send the message to. - /// Text to send. /// - public Task SuccessMessageAsync(IPlayer player, string text); - - /// - /// Send a warning message to the chat. - /// - /// Text to send. - /// - public Task WarningMessageAsync(string text); - - /// - /// Send a warning message to a specific player. - /// - /// Player to send the message to. - /// Text to send. - /// - public Task WarningMessageAsync(IPlayer player, string text); - - /// - /// Send a error message to the chat. - /// - /// Text to send. - /// - public Task ErrorMessageAsync(string text); - - /// - /// Send a error message to a specific player. - /// - /// Player to send the message to. - /// Text to send. - /// - public Task ErrorMessageAsync(IPlayer player, string text); - public Task GetMapsDirectoryAsync(); - - public Task FileExistsAsync(string file); } diff --git a/src/EvoSC.Common/Interfaces/Services/IChatService.cs b/src/EvoSC.Common/Interfaces/Services/IChatService.cs new file mode 100644 index 000000000..6c07551b0 --- /dev/null +++ b/src/EvoSC.Common/Interfaces/Services/IChatService.cs @@ -0,0 +1,225 @@ +using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Util.TextFormatting; + +namespace EvoSC.Common.Interfaces.Services; + +public interface IChatService +{ + /// + /// Send a chat message to all players. + /// + /// Message to send + /// + public Task SendChatMessageAsync(string message); + /// + /// Send a chat message to all players. + /// + /// Message to send + /// + public Task SendChatMessageAsync(TextFormatter message); + /// + /// Send a chat message to all players. + /// + /// Message to send + /// + public Task SendChatMessageAsync(Action message); + /// + /// Send a chat message to a set of players. + /// + /// Message to send. + /// Players to send the message to. + /// + public Task SendChatMessageAsync(string message, params string[] accountIds); + /// + /// Send a chat message to a set of players. + /// + /// Message to send. + /// Players to send the message to. + /// + public Task SendChatMessageAsync(string message, params IPlayer[] players); + /// + /// Send a chat message to a set of players. + /// + /// Message to send. + /// Players to send the message to. + /// + public Task SendChatMessageAsync(TextFormatter message, params string[] accountIds); + /// + /// Send a chat message to a set of players. + /// + /// Message to send. + /// Players to send the message to. + /// + public Task SendChatMessageAsync(TextFormatter message, params IPlayer[] players); + /// + /// Send a chat message to a set of players. + /// + /// Message to send. + /// Players to send the message to. + /// + public Task SendChatMessageAsync(Action message, params string[] accountIds); + /// + /// Send a chat message to a set of players. + /// + /// Message to send. + /// Players to send the message to. + /// + public Task SendChatMessageAsync(Action message, params IPlayer[] players); + + /// + /// Send an info-formatted message to everyone. + /// + /// Message to send. + /// + public Task InfoMessageAsync(string text); + /// + /// Send an info-formatted message to everyone. + /// + /// Message to send. + /// + public Task InfoMessageAsync(TextFormatter text); + /// + /// Send an info-formatted message to everyone. + /// + /// Message to send. + /// + public Task InfoMessageAsync(Action text); + /// + /// Send an info-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task InfoMessageAsync(string text, params IPlayer[] players); + /// + /// Send an info-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task InfoMessageAsync(TextFormatter text, params IPlayer[] players); + /// + /// Send an info-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task InfoMessageAsync(Action text, params IPlayer[] players); + + /// + /// Send a success-formatted message to everyone. + /// + /// Message to send. + /// + public Task SuccessMessageAsync(string text);/// + /// Send a success-formatted message to everyone. + /// + /// Message to send. + /// + public Task SuccessMessageAsync(TextFormatter text); + /// + /// Send a success-formatted message to everyone. + /// + /// Message to send. + /// + public Task SuccessMessageAsync(Action text); + /// + /// Send a success-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task SuccessMessageAsync(string text, params IPlayer[] players); + /// + /// Send a success-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task SuccessMessageAsync(TextFormatter text, params IPlayer[] players); + /// + /// Send a success-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task SuccessMessageAsync(Action text, params IPlayer[] players); + + /// + /// Send a warning-formatted message to everyone. + /// + /// Message to send. + /// + public Task WarningMessageAsync(string text);/// + /// Send a warning-formatted message to everyone. + /// + /// Message to send. + /// + public Task WarningMessageAsync(TextFormatter text);/// + /// Send a warning-formatted message to everyone. + /// + /// Message to send. + /// + public Task WarningMessageAsync(Action text); + /// + /// Send a warning-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task WarningMessageAsync(string text, params IPlayer[] players); + /// + /// Send a warning-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task WarningMessageAsync(TextFormatter text, params IPlayer[] players); + /// + /// Send a warning-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task WarningMessageAsync(Action text, params IPlayer[] players); + + /// + /// Send an error-formatted message to everyone. + /// + /// Message to send. + /// + public Task ErrorMessageAsync(string text); + /// + /// Send an error-formatted message to everyone. + /// + /// Message to send. + /// + public Task ErrorMessageAsync(TextFormatter text); + /// + /// Send an error-formatted message to everyone. + /// + /// Message to send. + /// + public Task ErrorMessageAsync(Action text); + /// + /// Send an error-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task ErrorMessageAsync(string text, params IPlayer[] players); + /// + /// Send an error-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task ErrorMessageAsync(TextFormatter text, params IPlayer[] players); + /// + /// Send an error-formatted message to a set of players. + /// + /// Message to send. + /// Players to send to. + /// + public Task ErrorMessageAsync(Action text, params IPlayer[] players); +} diff --git a/src/EvoSC.Common/Remote/ChatRouter/ChatRouterPipelineContext.cs b/src/EvoSC.Common/Remote/ChatRouter/ChatRouterPipelineContext.cs index 9e193fc01..c969dfa93 100644 --- a/src/EvoSC.Common/Remote/ChatRouter/ChatRouterPipelineContext.cs +++ b/src/EvoSC.Common/Remote/ChatRouter/ChatRouterPipelineContext.cs @@ -6,6 +6,8 @@ namespace EvoSC.Common.Remote.ChatRouter; public class ChatRouterPipelineContext : IPipelineContext { public required bool ForwardMessage { get; set; } - public required IPlayer Player { get; init; } + public required IOnlinePlayer Author { get; init; } public required string MessageText { get; set; } + public required List Recipients { get; set; } + public bool IsTeamMessage { get; set; } } diff --git a/src/EvoSC.Common/Remote/ChatRouter/RemoteChatRouter.cs b/src/EvoSC.Common/Remote/ChatRouter/RemoteChatRouter.cs index d7850995e..62e1a874e 100644 --- a/src/EvoSC.Common/Remote/ChatRouter/RemoteChatRouter.cs +++ b/src/EvoSC.Common/Remote/ChatRouter/RemoteChatRouter.cs @@ -1,6 +1,7 @@ using EvoSC.Common.Exceptions.PlayerExceptions; using EvoSC.Common.Interfaces; using EvoSC.Common.Interfaces.Middleware; +using EvoSC.Common.Interfaces.Models; using EvoSC.Common.Interfaces.Services; using EvoSC.Common.Middleware; using EvoSC.Common.Util; @@ -35,11 +36,39 @@ public RemoteChatRouter(ILogger logger, IServerClient server, } private async Task HandlePlayerChatRoutingAsync(object sender, PlayerChatGbxEventArgs e) + { + var actorAccountId = PlayerUtils.ConvertLoginToAccountId(e.Login); + var actor = await _players.GetOnlinePlayerAsync(actorAccountId); + var onlinePlayers = await _players.GetOnlinePlayersAsync(); + + await SendMessageAsync(new ChatRouterPipelineContext + { + ForwardMessage = true, + Author = actor, + MessageText = e.Text, + Recipients = onlinePlayers.ToList(), + IsTeamMessage = e.Options == 3 + }); + } + + public async Task SendMessageAsync(string message, IOnlinePlayer actor) + { + var onlinePlayers = await _players.GetOnlinePlayersAsync(); + await SendMessageAsync(new ChatRouterPipelineContext + { + ForwardMessage = true, + Author = actor, + MessageText = message, + Recipients = onlinePlayers.ToList(), + IsTeamMessage = false + }); + } + + public async Task SendMessageAsync(ChatRouterPipelineContext pipelineContext) { try { - var accountId = PlayerUtils.ConvertLoginToAccountId(e.Login); - var player = await _players.GetOnlinePlayerAsync(accountId); + var player = await _players.GetOnlinePlayerAsync(pipelineContext.Author.AccountId); if (player.Flags.IsServer) { @@ -53,21 +82,17 @@ private async Task HandlePlayerChatRoutingAsync(object sender, PlayerChatGbxEven { Task.Run(async () => { - var formatted = FormattingUtils.FormatPlayerChatMessage(chatContext.Player, - chatContext.MessageText); - await _server.SendChatMessageAsync(formatted); + var formatted = FormattingUtils.FormatPlayerChatMessage(chatContext.Author, + + chatContext.MessageText, chatContext.IsTeamMessage); + await _server.Chat.SendChatMessageAsync(formatted, ((IEnumerable)chatContext.Recipients).ToArray()); }); } }); - await pipelineChain(new ChatRouterPipelineContext - { - ForwardMessage = true, - Player = player, - MessageText = e.Text - }); + await pipelineChain(pipelineContext); - _logger.LogInformation("[{Name}]: {Msg}", player.StrippedNickName, e.Text); + _logger.LogInformation("[{Name}]: {Msg}", player.StrippedNickName, pipelineContext.MessageText); } catch (PlayerNotFoundException ex) { diff --git a/src/EvoSC.Common/Remote/ServerClient.cs b/src/EvoSC.Common/Remote/ServerClient.cs index 02090a70f..831afdca4 100644 --- a/src/EvoSC.Common/Remote/ServerClient.cs +++ b/src/EvoSC.Common/Remote/ServerClient.cs @@ -1,9 +1,12 @@ using EvoSC.Common.Config.Models; using EvoSC.Common.Exceptions; using EvoSC.Common.Interfaces; +using EvoSC.Common.Interfaces.Services; using EvoSC.Common.Interfaces.Themes; +using EvoSC.Common.Services; using GbxRemoteNet; using GbxRemoteNet.Interfaces; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace EvoSC.Common.Remote; @@ -20,6 +23,7 @@ public partial class ServerClient : IServerClient public IGbxRemoteClient Remote => _gbxRemote; public bool Connected => _connected; + public IChatService Chat { get; } public ServerClient(IEvoScBaseConfig config, ILogger logger, IEvoSCApplication app, IThemeManager themes) { @@ -32,6 +36,8 @@ public ServerClient(IEvoScBaseConfig config, ILogger logger, IEvoS _gbxRemote = new GbxRemoteClient(config.Server.Host, config.Server.Port, logger); _gbxRemote.OnDisconnected += OnDisconnectedAsync; + + Chat = new ChatService(this, themes); } private async Task OnDisconnectedAsync() @@ -137,11 +143,4 @@ public async Task GetMapsDirectoryAsync() return mapsDir; } - - public async Task FileExistsAsync(string file) - { - var mapsDir = await GetMapsDirectoryAsync(); - - return File.Exists(Path.Combine(mapsDir, file)); - } } diff --git a/src/EvoSC.Common/Remote/ServerClient_Responses.cs b/src/EvoSC.Common/Remote/ServerClient_Responses.cs deleted file mode 100644 index bd833a719..000000000 --- a/src/EvoSC.Common/Remote/ServerClient_Responses.cs +++ /dev/null @@ -1,57 +0,0 @@ -using EvoSC.Common.Interfaces.Models; -using EvoSC.Common.Util; -using EvoSC.Common.Util.ServerUtils; -using EvoSC.Common.Util.TextFormatting; - -namespace EvoSC.Common.Remote; - -public partial class ServerClient -{ - private TextFormatter MakeInfoMessage(string text) => - new TextFormatter() - .AddText(GameIcons.Icons.ExclamationCircle, styling => styling.WithColor(new TextColor(_themes.Theme.Chat_Info))) - .AddText(" ") - .AddText(text); - - private TextFormatter MakeSuccessMessage(string text) => - new TextFormatter() - .AddText(GameIcons.Icons.CheckCircle, styling => styling.WithColor(new TextColor(_themes.Theme.Chat_Success))) - .AddText(" ") - .AddText(text); - - private TextFormatter MakeWarningMessage(string text) => - new TextFormatter() - .AddText(GameIcons.Icons.ExclamationTriangle, styling => styling.WithColor(new TextColor(_themes.Theme.Chat_Warning))) - .AddText(" ") - .AddText(text); - - private TextFormatter MakeErrorMessage(string text) => - new TextFormatter() - .AddText("", styling => styling.WithColor(new TextColor(_themes.Theme.Chat_Danger))) - .AddText(" ") - .AddText(text); - - public Task InfoMessageAsync(string text) => - this.SendChatMessageAsync(MakeInfoMessage(text)); - - public Task InfoMessageAsync(IPlayer player, string text) => - this.SendChatMessageAsync(player, MakeInfoMessage(text)); - - public Task SuccessMessageAsync(string text) => - this.SendChatMessageAsync(MakeSuccessMessage(text)); - - public Task SuccessMessageAsync(IPlayer player, string text) => - this.SendChatMessageAsync(player, MakeSuccessMessage(text)); - - public Task WarningMessageAsync(string text) => - this.SendChatMessageAsync(MakeWarningMessage(text)); - - public Task WarningMessageAsync(IPlayer player, string text) => - this.SendChatMessageAsync(player, MakeWarningMessage(text)); - - public Task ErrorMessageAsync(string text) => - this.SendChatMessageAsync(MakeErrorMessage(text)); - - public Task ErrorMessageAsync(IPlayer player, string text) => - this.SendChatMessageAsync(player, MakeErrorMessage(text)); -} diff --git a/src/EvoSC.Common/Services/ChatService.cs b/src/EvoSC.Common/Services/ChatService.cs new file mode 100644 index 000000000..a0099a3cb --- /dev/null +++ b/src/EvoSC.Common/Services/ChatService.cs @@ -0,0 +1,149 @@ +using EvoSC.Common.Interfaces; +using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; +using EvoSC.Common.Interfaces.Themes; +using EvoSC.Common.Util; +using EvoSC.Common.Util.TextFormatting; +using GbxRemoteNet; + +namespace EvoSC.Common.Services; + +public class ChatService(IServerClient server, IThemeManager themes) : IChatService +{ + public Task SendChatMessageAsync(string message) => server.Remote.ChatSendServerMessageAsync(message); + + public Task SendChatMessageAsync(TextFormatter message) => SendChatMessageAsync(message.ToString()); + + public Task SendChatMessageAsync(Action message) + { + var text = new TextFormatter(); + message(text); + return SendChatMessageAsync(text); + } + + public async Task SendChatMessageAsync(string message, params string[] accountIds) + { + var mc = new MultiCall(); + foreach (var accountId in accountIds) + { + mc.Add("ChatSendServerMessageToLogin", message, PlayerUtils.ConvertAccountIdToLogin(accountId)); + } + + return (await server.Remote.MultiCallAsync(mc)).All(r => r != null && (bool)r); + } + + public Task SendChatMessageAsync(string message, params IPlayer[] players) => + SendChatMessageAsync(message, players.Select(p => p.AccountId).ToArray()); + + public Task SendChatMessageAsync(TextFormatter message, params string[] accountIds) => + SendChatMessageAsync(message.ToString(), accountIds); + + public Task SendChatMessageAsync(TextFormatter message, params IPlayer[] players) => + SendChatMessageAsync(message.ToString(), players.Select(p => p.AccountId).ToArray()); + + public Task SendChatMessageAsync(Action message, params string[] accountIds) + { + var text = new TextFormatter(); + message(text); + return SendChatMessageAsync(text, accountIds); + } + + public Task SendChatMessageAsync(Action message, params IPlayer[] players) => + SendChatMessageAsync(message, players.Select(p => p.AccountId).ToArray()); + + public Task InfoMessageAsync(string text) => SendChatMessageAsync(CreateInfoMessage(text)); + + public Task InfoMessageAsync(TextFormatter text) => SendChatMessageAsync(CreateInfoMessage(text.ToString())); + + public Task InfoMessageAsync(Action text) => SendChatMessageAsync(CreateInfoMessage(text)); + + public Task InfoMessageAsync(string text, params IPlayer[] players) => SendChatMessageAsync(CreateInfoMessage(text), players); + + public Task InfoMessageAsync(TextFormatter text, params IPlayer[] players) => SendChatMessageAsync(CreateInfoMessage(text.ToString()), players); + + public Task InfoMessageAsync(Action text, params IPlayer[] players) => SendChatMessageAsync(CreateInfoMessage(text), players); + + public Task SuccessMessageAsync(string text) => SendChatMessageAsync(CreateSuccessMessage(text)); + + public Task SuccessMessageAsync(TextFormatter text) => SendChatMessageAsync(CreateSuccessMessage(text.ToString())); + + public Task SuccessMessageAsync(Action text) => SendChatMessageAsync(CreateSuccessMessage(text)); + + public Task SuccessMessageAsync(string text, params IPlayer[] players) => SendChatMessageAsync(CreateSuccessMessage(text), players); + + public Task SuccessMessageAsync(TextFormatter text, params IPlayer[] players) => SendChatMessageAsync(CreateSuccessMessage(text.ToString()), players); + + public Task SuccessMessageAsync(Action text, params IPlayer[] players) => SendChatMessageAsync(CreateSuccessMessage(text), players); + + public Task WarningMessageAsync(string text) => SendChatMessageAsync(CreateWarningMessage(text)); + + public Task WarningMessageAsync(TextFormatter text) => SendChatMessageAsync(CreateWarningMessage(text.ToString())); + + public Task WarningMessageAsync(Action text) => SendChatMessageAsync(CreateWarningMessage(text)); + + public Task WarningMessageAsync(string text, params IPlayer[] players) => SendChatMessageAsync(CreateWarningMessage(text), players); + + public Task WarningMessageAsync(TextFormatter text, params IPlayer[] players) => SendChatMessageAsync(CreateWarningMessage(text.ToString()), players); + + public Task WarningMessageAsync(Action text, params IPlayer[] players) => SendChatMessageAsync(CreateWarningMessage(text), players); + + public Task ErrorMessageAsync(string text) => SendChatMessageAsync(CreateErrorMessage(text)); + + public Task ErrorMessageAsync(TextFormatter text) => SendChatMessageAsync(CreateErrorMessage(text.ToString())); + + public Task ErrorMessageAsync(Action text) => SendChatMessageAsync(CreateErrorMessage(text)); + + public Task ErrorMessageAsync(string text, params IPlayer[] players) => SendChatMessageAsync(CreateErrorMessage(text), players); + + public Task ErrorMessageAsync(TextFormatter text, params IPlayer[] players) => SendChatMessageAsync(CreateErrorMessage(text.ToString()), players); + + public Task ErrorMessageAsync(Action text, params IPlayer[] players) => SendChatMessageAsync(CreateErrorMessage(text), players); + + private string CreateInfoMessage(string message) => + new TextFormatter() + .AddText(GameIcons.Icons.ExclamationCircle, s => s + .WithColor(new TextColor(themes.Theme.Chat_Info))) + .AddText(" ") + .AddText(message) + .ToString(); + + private string CreateInfoMessage(Action message) => CreateInfoMessage(CallMessageAction(message)); + + private string CreateSuccessMessage(string message) => + new TextFormatter() + .AddText(GameIcons.Icons.CheckCircle, s => s + .WithColor(new TextColor(themes.Theme.Chat_Success))) + .AddText(" ") + .AddText(message) + .ToString(); + + private string CreateSuccessMessage(Action message) => CreateSuccessMessage(CallMessageAction(message)); + + private string CreateWarningMessage(string message) => + new TextFormatter() + .AddText(GameIcons.Icons.ExclamationTriangle, s => s + .WithColor(new TextColor(themes.Theme.Chat_Warning))) + .AddText(" ") + .AddText(message) + .ToString(); + + private string CreateWarningMessage(Action message) => CreateWarningMessage(CallMessageAction(message)); + + private string CreateErrorMessage(string message) => + new TextFormatter() + .AddText(GameIcons.Icons.TimesCircle, s => s + .WithColor(new TextColor(themes.Theme.Chat_Danger))) + .AddText(" ") + .AddText(message) + .ToString(); + + + private string CreateErrorMessage(Action message) => CreateErrorMessage(CallMessageAction(message)); + + private string CallMessageAction(Action message) + { + var text = new TextFormatter(); + message(text); + return text.ToString(); + } +} diff --git a/src/EvoSC.Common/Services/PlayerCacheService.cs b/src/EvoSC.Common/Services/PlayerCacheService.cs index f3d084079..b99f67a8f 100644 --- a/src/EvoSC.Common/Services/PlayerCacheService.cs +++ b/src/EvoSC.Common/Services/PlayerCacheService.cs @@ -9,6 +9,7 @@ using EvoSC.Common.Models.Players; using EvoSC.Common.Remote; using EvoSC.Common.Util; +using EvoSC.Common.Util.ServerUtils; using GbxRemoteNet; using GbxRemoteNet.Events; using GbxRemoteNet.Structs; diff --git a/src/EvoSC.Common/Util/FormattingUtils.cs b/src/EvoSC.Common/Util/FormattingUtils.cs index 46c5ce607..21de5eba5 100644 --- a/src/EvoSC.Common/Util/FormattingUtils.cs +++ b/src/EvoSC.Common/Util/FormattingUtils.cs @@ -46,13 +46,18 @@ public static string FormatTimeAsDelta(int milliseconds) return $"+ {s:0}.{ms:000}"; } - public static TextFormatter FormatPlayerChatMessage(IPlayer player, string message) + public static TextFormatter FormatPlayerChatMessage(IPlayer player, string message, bool teamMessage) { var formattedMessage = new TextFormatter(); + if (teamMessage) + { + formattedMessage.AddText($"({GameIcons.Icons.EyeSlash} team) ", s => s.WithColor("BBB")); + } + if (player.DisplayGroup?.Icon != null) { - formattedMessage.AddText(player.DisplayGroup.Icon, s => s.WithColor(player.DisplayGroup.Color ?? "FFF")); + formattedMessage.AddText($"{player.DisplayGroup.Icon} ", s => s.WithColor(player.DisplayGroup.Color ?? "FFF")); } formattedMessage.AddText("[") diff --git a/src/EvoSC.Common/Util/GbxRemoteExtensions.cs b/src/EvoSC.Common/Util/ServerUtils/GbxRemoteExtensions.cs similarity index 91% rename from src/EvoSC.Common/Util/GbxRemoteExtensions.cs rename to src/EvoSC.Common/Util/ServerUtils/GbxRemoteExtensions.cs index f33626027..84432d7dd 100644 --- a/src/EvoSC.Common/Util/GbxRemoteExtensions.cs +++ b/src/EvoSC.Common/Util/ServerUtils/GbxRemoteExtensions.cs @@ -1,6 +1,6 @@ using GbxRemoteNet.XmlRpc; -namespace EvoSC.Common.Util; +namespace EvoSC.Common.Util.ServerUtils; public static class GbxRemoteUtils { diff --git a/src/EvoSC.Common/Util/ServerUtils/ServerChatMessageExtensions.cs b/src/EvoSC.Common/Util/ServerUtils/ServerChatMessageExtensions.cs deleted file mode 100644 index 938d7075f..000000000 --- a/src/EvoSC.Common/Util/ServerUtils/ServerChatMessageExtensions.cs +++ /dev/null @@ -1,107 +0,0 @@ -using EvoSC.Common.Interfaces; -using EvoSC.Common.Interfaces.Models; -using EvoSC.Common.Util.TextFormatting; - -namespace EvoSC.Common.Util.ServerUtils; - -public static class ServerChatMessageExtensions -{ - /// - /// Send a chat message to all users. - /// - /// - /// The message to send. - /// - public static Task SendChatMessageAsync(this IServerClient server, string message) => - server.Remote.ChatSendServerMessageAsync(message); - - /// - /// Send a chat message to a specific user. - /// - /// - /// The message to send. - /// The player to send the message to. - /// - public static Task SendChatMessageAsync(this IServerClient server, IPlayer player, string message) => - server.Remote.ChatSendServerMessageToLoginAsync(message, PlayerUtils.ConvertAccountIdToLogin(player.AccountId)); - - /// - /// Send a chat message to a specific user by their login. - /// - /// - /// The message to send. - /// Login of the player to send the message to. - /// - public static Task SendChatMessageAsync(this IServerClient server, string login, string message) => - server.Remote.ChatSendServerMessageToLoginAsync(message, login); - - /// - /// Send formatted text to the chat for a specific login. - /// - /// - /// The text message to send. - /// - public static Task SendChatMessageAsync(this IServerClient server, TextFormatter message) => - server.SendChatMessageAsync(message.ToString()); - - /// - /// Send formatted text to the chat to a specific player. - /// - /// - /// The text message to send. - /// The player to send the message to. - /// - public static Task SendChatMessageAsync(this IServerClient server, IPlayer player, TextFormatter message) => - server.SendChatMessageAsync(player, message.ToString()); - - /// - /// Send a chat message to a specific user by their login. - /// - /// - /// The message to send. - /// Login of the player to send the message to. - /// - public static Task SendChatMessageAsync(this IServerClient server, string login, TextFormatter message) => - server.SendChatMessageAsync(login, message.ToString()); - - /// - /// Send formatted text to the chat using a builder action. - /// - /// - /// The text message to send. - /// - public static Task SendChatMessageAsync(this IServerClient server, Action messageBuilder) - { - var message = new TextFormatter(); - messageBuilder(message); - return server.SendChatMessageAsync(message.ToString()); - } - - /// - /// Send formatted text to the chat for a specific player using a builder action. - /// - /// - /// The text message to send. - /// The player to send the message to. - /// - public static Task SendChatMessageAsync(this IServerClient server, IPlayer player, Action messageBuilder) - { - var message = new TextFormatter(); - messageBuilder(message); - return server.SendChatMessageAsync(player, message.ToString()); - } - - /// - /// Send formatted text to the chat for a specific login using a builder action. - /// - /// - /// The text message to send. - /// Login of the player to send the message to. - /// - public static Task SendChatMessageAsync(this IServerClient server, string login, Action messageBuilder) - { - var message = new TextFormatter(); - messageBuilder(message); - return server.SendChatMessageAsync(login, message.ToString()); - } -} diff --git a/src/EvoSC.Manialinks/Middleware/ManialinkPermissionMiddleware.cs b/src/EvoSC.Manialinks/Middleware/ManialinkPermissionMiddleware.cs index d797fceed..2f1b38669 100644 --- a/src/EvoSC.Manialinks/Middleware/ManialinkPermissionMiddleware.cs +++ b/src/EvoSC.Manialinks/Middleware/ManialinkPermissionMiddleware.cs @@ -9,7 +9,7 @@ namespace EvoSC.Manialinks.Middleware; /// This middleware checks if a user has the permission to execute a Manialink Action. /// A message in chat is sent to the player in case of insufficient permissions. /// -public class ManialinkPermissionMiddleware(ActionDelegate next, IPermissionManager permissions, IServerClient server) +public class ManialinkPermissionMiddleware(ActionDelegate next, IPermissionManager permissions, IChatService chat) { public async Task ExecuteAsync(IControllerContext context) { @@ -25,6 +25,6 @@ public async Task ExecuteAsync(IControllerContext context) return; } - await server.ErrorMessageAsync(mlContext.Player, "Sorry, you don't have permission to do that."); + await chat.ErrorMessageAsync("Sorry, you don't have permission to do that.", mlContext.Player); } } diff --git a/src/EvoSC.Testing/Controllers/CommandInteractionControllerTestBase.cs b/src/EvoSC.Testing/Controllers/CommandInteractionControllerTestBase.cs index 58fb2231f..bfe9c929d 100644 --- a/src/EvoSC.Testing/Controllers/CommandInteractionControllerTestBase.cs +++ b/src/EvoSC.Testing/Controllers/CommandInteractionControllerTestBase.cs @@ -2,6 +2,7 @@ using EvoSC.Common.Interfaces; using EvoSC.Common.Interfaces.Controllers; using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; using GbxRemoteNet.Interfaces; using Moq; @@ -10,7 +11,7 @@ namespace EvoSC.Testing.Controllers; public class CommandInteractionControllerTestBase : ControllerMock where TController : class, IController { - protected readonly (Mock Client, Mock Remote) Server = + protected readonly (Mock Client, Mock Remote, Mock Chat) Server = Mocking.NewServerClientMock(); /// diff --git a/src/EvoSC.Testing/Controllers/ManialinkControllerTestBase.cs b/src/EvoSC.Testing/Controllers/ManialinkControllerTestBase.cs index 9a57bf91c..0a97b88ba 100644 --- a/src/EvoSC.Testing/Controllers/ManialinkControllerTestBase.cs +++ b/src/EvoSC.Testing/Controllers/ManialinkControllerTestBase.cs @@ -1,7 +1,10 @@ -using EvoSC.Common.Interfaces.Controllers; +using EvoSC.Common.Interfaces; +using EvoSC.Common.Interfaces.Controllers; using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; using EvoSC.Manialinks.Interfaces; using EvoSC.Manialinks.Interfaces.Models; +using GbxRemoteNet.Interfaces; using Moq; namespace EvoSC.Testing.Controllers; @@ -9,6 +12,9 @@ namespace EvoSC.Testing.Controllers; public class ManialinkControllerTestBase : ControllerMock where TController : class, IController { + protected readonly (Mock Client, Mock Remote, Mock Chat) Server = + Mocking.NewServerClientMock(); + private Mock _mlManager = new(); /// @@ -25,6 +31,6 @@ public class ManialinkControllerTestBase : ControllerMock SetupMock( { mock.Context.Setup(c => c.Player).Returns(actor); mock.Context.Setup(c => c.Server).Returns(serverClient.Object); + mock.Context.Setup(c => c.Chat).Returns(serverClient.Object.Chat); mock.Context.Object.AuditEvent.CausedBy(actor); return mock; @@ -65,6 +68,7 @@ public static ControllerContextMock SetupMock( { mock.Context.Setup(c => c.Player).Returns(actor); mock.Context.Setup(c => c.Server).Returns(serverClient.Object); + mock.Context.Setup(c => c.Chat).Returns(serverClient.Object.Chat); mock.Context.Object.AuditEvent.CausedBy(actor); return mock; @@ -88,12 +92,14 @@ public static ControllerContextMock /// The mocked manialink manager to use. /// public static ControllerContextMock SetupMock( - this ControllerContextMock mock, IOnlinePlayer actor, + this ControllerContextMock mock, Mock serverClient, IOnlinePlayer actor, IManialinkActionContext actionContext, IManialinkManager mlManager) { mock.Context.Setup(c => c.Player).Returns(actor); mock.Context.Setup(c => c.ManialinkAction).Returns(actionContext); mock.Context.Setup(m => m.ManialinkManager).Returns(mlManager); + mock.Context.Setup(m => m.Server).Returns(serverClient.Object); + mock.Context.Setup(m => m.Chat).Returns(serverClient.Object.Chat); mock.Context.Object.AuditEvent.CausedBy(actor); return mock; @@ -107,8 +113,8 @@ public static ControllerContextMock SetupMock( /// The mocked manialink manager to use. /// public static ControllerContextMock NewManialinkInteractionContextMock( - IOnlinePlayer actor, IManialinkActionContext actionContext, IManialinkManager mlManager) => - new ControllerContextMock().SetupMock(actor, actionContext, mlManager); + Mock serverClient, IOnlinePlayer actor, IManialinkActionContext actionContext, IManialinkManager mlManager) => + new ControllerContextMock().SetupMock(serverClient, actor, actionContext, mlManager); /// /// Create a new controller instance that will use the mocked context and services given. @@ -237,13 +243,15 @@ public static Locale NewLocaleMock(IContextService contextService) /// Create a new mock of the server client and it's GBXRemoteClient. /// /// - public static (Mock Client, Mock Remote) NewServerClientMock() + public static (Mock Client, Mock Remote, Mock Chat) NewServerClientMock() { var remote = new Mock(); var client = new Mock(); + var chat = new Mock(); client.Setup(m => m.Remote).Returns(remote.Object); + client.Setup(m => m.Chat).Returns(chat.Object); - return (client, remote); + return (client, remote, chat); } /// diff --git a/src/EvoSC/ApplicationSetup.cs b/src/EvoSC/ApplicationSetup.cs index 7b3204ff0..539bc5ad2 100644 --- a/src/EvoSC/ApplicationSetup.cs +++ b/src/EvoSC/ApplicationSetup.cs @@ -72,6 +72,10 @@ public static void SetupPipeline(this IStartupPipeline pipeline, IEvoScBaseConfi .Services(AppFeature.ServicesManager, s => s .RegisterSingleton() ) + + .Services(AppFeature.Chat, s => s + .Register(Lifestyle.Transient) + ) .Services(AppFeature.ChatCommands, s => s.AddEvoScChatCommands()) diff --git a/src/EvoSC/EvoSC.csproj b/src/EvoSC/EvoSC.csproj index 6e70faab2..12ca2d98b 100644 --- a/src/EvoSC/EvoSC.csproj +++ b/src/EvoSC/EvoSC.csproj @@ -50,6 +50,7 @@ + diff --git a/src/EvoSC/InternalModules.cs b/src/EvoSC/InternalModules.cs index 4b7f93cac..94a8df479 100644 --- a/src/EvoSC/InternalModules.cs +++ b/src/EvoSC/InternalModules.cs @@ -24,6 +24,7 @@ using EvoSC.Modules.Official.ServerManagementModule; using EvoSC.Modules.Official.SetName; using EvoSC.Modules.Official.SpectatorTargetInfoModule; +using EvoSC.Modules.Official.TeamChatModule; using EvoSC.Modules.Official.TeamInfoModule; using EvoSC.Modules.Official.TeamSettingsModule; using EvoSC.Modules.Official.WorldRecordModule; @@ -61,7 +62,8 @@ public static class InternalModules typeof(TeamSettingsModule), typeof(ServerManagementModule), typeof(TeamInfoModule), - typeof(GameModeUiModule) + typeof(GameModeUiModule), + typeof(TeamChatModule) ]; /// diff --git a/src/Modules/ExampleModule/ExampleController.cs b/src/Modules/ExampleModule/ExampleController.cs index d420a19d4..4eb1164c6 100644 --- a/src/Modules/ExampleModule/ExampleController.cs +++ b/src/Modules/ExampleModule/ExampleController.cs @@ -12,7 +12,10 @@ using EvoSC.Common.Util; using EvoSC.Common.Util.ServerUtils; using EvoSC.Manialinks.Interfaces; +using GbxRemoteNet; using GbxRemoteNet.Events; +using GbxRemoteNet.XmlRpc.ExtraTypes; +using Microsoft.Extensions.Logging; namespace EvoSC.Modules.Official.ExampleModule; @@ -30,11 +33,12 @@ public class ExampleController : EvoScController private readonly Locale _locale; private readonly IEventManager _events; private readonly IMapService _mapService; + private readonly ILogger _logger; public ExampleController(IMySettings settings, IChatCommandManager cmds, IServerClient server, IChatCommandManager chatCommands, IPermissionManager permissions, IPermissionRepository permRepo, IMapRepository mapRepo, IMatchSettingsService matchSettings, IManialinkActionManager manialinkActions, - Locale locale, IEventManager events, IMapService mapService) + Locale locale, IEventManager events, IMapService mapService, ILogger logger) { _settings = settings; _server = server; @@ -47,12 +51,13 @@ public ExampleController(IMySettings settings, IChatCommandManager cmds, IServer _locale = locale; _events = events; _mapService = mapService; + _logger = logger; } [ChatCommand("hey", "Say hey!")] public async Task TmxAddMap(string name) { - await _server.SendChatMessageAsync(Context.Player, $"hello, {name}!"); + await _server.Chat.SendChatMessageAsync($"hello, {name}!", Context.Player); } [ChatCommand("ratemap", "Rate the current map.", "test")] @@ -66,20 +71,36 @@ public async Task RateMap(int rating) { if (rating is < 0 or > 100) { - await _server.SendChatMessageAsync(Context.Player, "Rating must be between 0 and 100 inclusively."); + await _server.Chat.SendChatMessageAsync("Rating must be between 0 and 100 inclusively.", Context.Player); } else { - await _server.SendChatMessageAsync($"Your rating: {rating}"); + await _server.Chat.SendChatMessageAsync($"Your rating: {rating}"); } } [ChatCommand("test", "Some testing.")] public async Task TestCommand() { - var mode = await _matchSettings.GetCurrentModeAsync(); - - Console.WriteLine(mode); + try + { + var mc = new MultiCall(); + var n = 50000; + + for (var i = 0; i < n; i++) + { + mc.Add("GetVersion"); + } + + Console.WriteLine($"Calling {n} methods with multicall ..."); + var result = await _server.Remote.MultiCallAsync(mc); + + Console.WriteLine($"Got {result.Length} results."); + } + catch (Exception ex) + { + _logger.LogError(ex, ""); + } } [ChatCommand("rejoin", "Simulates the player joining the server.")] diff --git a/src/Modules/ForceTeamModule/Controllers/ForceTeamManialinkController.cs b/src/Modules/ForceTeamModule/Controllers/ForceTeamManialinkController.cs index a8f11c709..4761016eb 100644 --- a/src/Modules/ForceTeamModule/Controllers/ForceTeamManialinkController.cs +++ b/src/Modules/ForceTeamModule/Controllers/ForceTeamManialinkController.cs @@ -19,15 +19,14 @@ public async Task SwitchPlayerAsync(string accountId) if (player == null) { - await Context.Server.ErrorMessageAsync(Context.Player, - "Failed to find the player you are trying to switch."); + await Context.Chat.ErrorMessageAsync("Failed to find the player you are trying to switch.", Context.Player); return; } var newTeam = await forceTeamService.SwitchPlayerAsync(player); var teamInfo = await Context.Server.Remote.GetTeamInfoAsync((int)newTeam + 1); - await Context.Server.InfoMessageAsync(new TextFormatter() + await Context.Chat.InfoMessageAsync(new TextFormatter() .AddText(player.NickName) .AddText(" was forced to team ") .AddText(teamInfo.Name, s => s.WithColor(teamInfo.RGB)) diff --git a/src/Modules/LocalRecordsModule/Controllers/LocalRecordsManialinkController.cs b/src/Modules/LocalRecordsModule/Controllers/LocalRecordsManialinkController.cs index c8119c62c..9bace5ab5 100644 --- a/src/Modules/LocalRecordsModule/Controllers/LocalRecordsManialinkController.cs +++ b/src/Modules/LocalRecordsModule/Controllers/LocalRecordsManialinkController.cs @@ -8,7 +8,7 @@ namespace EvoSC.Modules.Official.LocalRecordsModule.Controllers; [Controller] -public class LocalRecordsManialinkController(ILocalRecordsService localRecordsService, IServerClient server) : ManialinkController +public class LocalRecordsManialinkController(ILocalRecordsService localRecordsService) : ManialinkController { [ManialinkRoute(Permission = LocalRecordsPermission.ResetLocals)] public async Task ConfirmResetAsync(bool confirmed) @@ -21,7 +21,7 @@ public async Task ConfirmResetAsync(bool confirmed) } Context.AuditEvent.WithEventName(AuditEvents.ResetAll); - await server.InfoMessageAsync("Resetting local records, this may take a while ..."); + await Context.Chat.InfoMessageAsync("Resetting local records, this may take a while ..."); try { @@ -29,12 +29,12 @@ public async Task ConfirmResetAsync(bool confirmed) } catch (Exception ex) { - await server.ErrorMessageAsync($"Failed to reset local records: {ex.Message}"); + await Context.Chat.ErrorMessageAsync($"Failed to reset local records: {ex.Message}"); Context.AuditEvent.Error(); throw; } - await server.SuccessMessageAsync("Local records successfully reset!"); + await Context.Chat.SuccessMessageAsync("Local records successfully reset!"); Context.AuditEvent.Success(); await localRecordsService.ShowWidgetToAllAsync(); diff --git a/src/Modules/LocalRecordsModule/Services/LocalRecordsService.cs b/src/Modules/LocalRecordsModule/Services/LocalRecordsService.cs index dbf28ac3f..a5239abc8 100644 --- a/src/Modules/LocalRecordsModule/Services/LocalRecordsService.cs +++ b/src/Modules/LocalRecordsModule/Services/LocalRecordsService.cs @@ -24,7 +24,7 @@ public class LocalRecordsService( IManialinkManager manialinkManager, ILogger logger, ILocalRecordsSettings settings, - IServerClient server, + IChatService server, IThemeManager themeManager, IPlayerRecordsRepository playerRecordsRepository) : ILocalRecordsService { diff --git a/src/Modules/MapListModule/Services/MapListService.cs b/src/Modules/MapListModule/Services/MapListService.cs index 8fa836316..aa3639bad 100644 --- a/src/Modules/MapListModule/Services/MapListService.cs +++ b/src/Modules/MapListModule/Services/MapListService.cs @@ -23,7 +23,7 @@ public class MapListService( IContextService context, IMapService mapService, ILogger logger, - IServerClient serverClient, + IChatService chat, IManialinkManager manialinkManager, IPermissionManager permissions) : IMapListService { @@ -80,11 +80,11 @@ public async Task DeleteMapAsync(IPlayer actor, string mapUid) logger.LogError(ex, "Failed to Remove map"); context.Audit().Error(); - await serverClient.ErrorMessageAsync(actor, $"Failed to remove the map '{map.Name}'"); + await chat.ErrorMessageAsync($"Failed to remove the map '{map.Name}'", actor); return; } - await serverClient.SuccessMessageAsync(actor, $"'{map.Name}' was removed from the map list."); + await chat.SuccessMessageAsync($"'{map.Name}' was removed from the map list.", actor); } public async Task ShowMapListAsync(IPlayer player) diff --git a/src/Modules/MapQueueModule/Controllers/QueueCommandsController.cs b/src/Modules/MapQueueModule/Controllers/QueueCommandsController.cs index 2cfb297bb..4c9b0af04 100644 --- a/src/Modules/MapQueueModule/Controllers/QueueCommandsController.cs +++ b/src/Modules/MapQueueModule/Controllers/QueueCommandsController.cs @@ -12,14 +12,14 @@ namespace EvoSC.Modules.Official.MapQueueModule.Controllers; [Controller] -public class QueueCommandsController(IMapQueueService mapQueue, IMapService maps, IServerClient server) : EvoScController +public class QueueCommandsController(IMapQueueService mapQueue, IMapService maps) : EvoScController { [ChatCommand("queue", "Queue a map to be played.")] public async Task QueueAsync(int id) { var map = await maps.GetMapByIdAsync(id); await mapQueue.EnqueueAsync(map); - await server.SuccessMessageAsync($"$<{Context.Player.NickName}$> queued map $<{map.Name}$>."); + await Context.Chat.SuccessMessageAsync($"$<{Context.Player.NickName}$> queued map $<{map.Name}$>."); } [ChatCommand("queuelist", "List currently queued maps.")] @@ -40,6 +40,6 @@ public async Task QueueListAsync() message.AddText(", "); } - await server.SendChatMessageAsync(Context.Player, message); + await Context.Chat.SendChatMessageAsync(message, Context.Player); } } diff --git a/src/Modules/MapQueueModule/Controllers/QueueController.cs b/src/Modules/MapQueueModule/Controllers/QueueController.cs index 4deee8716..0d75dafcf 100644 --- a/src/Modules/MapQueueModule/Controllers/QueueController.cs +++ b/src/Modules/MapQueueModule/Controllers/QueueController.cs @@ -48,7 +48,7 @@ public async Task OnMapQueuedAsync(object sender, MapQueueEventArgs args) await server.Remote.ChooseNextMapAsync(args.Map.FilePath); } - await server.InfoMessageAsync($"Map queued: {args.Map.Name}"); + await server.Chat.InfoMessageAsync($"Map queued: {args.Map.Name}"); } [Subscribe(MapQueueEvents.MapDropped)] @@ -62,7 +62,7 @@ public async Task OnMapDroppedAsync(object sender, MapQueueMapDroppedEventArgs a if (!args.WasNext) { - await server.InfoMessageAsync($"Map removed from queue: {args.Map.Name}"); + await server.Chat.InfoMessageAsync($"Map removed from queue: {args.Map.Name}"); } } } diff --git a/src/Modules/MapsModule/Controllers/MapsController.cs b/src/Modules/MapsModule/Controllers/MapsController.cs index 6f89af884..e66703c8a 100644 --- a/src/Modules/MapsModule/Controllers/MapsController.cs +++ b/src/Modules/MapsModule/Controllers/MapsController.cs @@ -15,12 +15,10 @@ namespace EvoSC.Modules.Official.MapsModule.Controllers; [Controller] public class MapsController( - ILogger logger, - IMxMapService mxMapService, - IMapService mapService, - IServerClient server, - Locale locale - ) + ILogger logger, + IMxMapService mxMapService, + IMapService mapService, + Locale locale) : EvoScController { private readonly dynamic _locale = locale; @@ -36,18 +34,18 @@ public async Task AddMapAsync(string mapId) } catch (DuplicateNameException) { - await server.ErrorMessageAsync(Context.Player, _locale.PlayerLanguage.DuplicateMap(mapId)); + await Context.Chat.ErrorMessageAsync(_locale.PlayerLanguage.DuplicateMap(mapId), Context.Player); return; } catch (Exception) { - await server.ErrorMessageAsync(Context.Player, _locale.PlayerLanguage.FailedAddingMap(mapId)); + await Context.Chat.ErrorMessageAsync(_locale.PlayerLanguage.FailedAddingMap(mapId), Context.Player); throw; } if (map == null) { - await server.ErrorMessageAsync(Context.Player, _locale.PlayerLanguage.MapIdNotFound(mapId)); + await Context.Chat.ErrorMessageAsync(_locale.PlayerLanguage.MapIdNotFound(mapId), Context.Player); return; } @@ -56,7 +54,7 @@ public async Task AddMapAsync(string mapId) .HavingProperties(new { Map = map }) .Comment(_locale.Audit_MapAdded); - await server.SuccessMessageAsync(Context.Player, _locale.PlayerLanguage.MapAddedSuccessfully(map.Name, map.Author?.NickName)); + await Context.Chat.SuccessMessageAsync(_locale.PlayerLanguage.MapAddedSuccessfully(map.Name, map.Author?.NickName), Context.Player); } [ChatCommand("removemap", "[Command.Remove]", MapsPermissions.RemoveMap)] @@ -67,7 +65,7 @@ public async Task RemoveMapAsync(long mapId) if (map == null) { - await server.ErrorMessageAsync(Context.Player, _locale.PlayerLanguage.MapIdNotFound(mapId)); + await Context.Chat.ErrorMessageAsync(_locale.PlayerLanguage.MapIdNotFound(mapId), Context.Player); return; } @@ -77,7 +75,7 @@ public async Task RemoveMapAsync(long mapId) } catch (Exception) { - await server.ErrorMessageAsync(Context.Player, _locale.PlayerLanguage.MapRemovedFailed(mapId)); + await Context.Chat.ErrorMessageAsync(_locale.PlayerLanguage.MapRemovedFailed(mapId), Context.Player); throw; } @@ -86,7 +84,7 @@ public async Task RemoveMapAsync(long mapId) .HavingProperties(new { Map = map }) .Comment(_locale.Audit_MapRemoved); - await server.SuccessMessageAsync(Context.Player, _locale.PlayerLanguage.MapRemovedSuccessfully(map.Name, map.Author.NickName)); + await Context.Chat.SuccessMessageAsync(_locale.PlayerLanguage.MapRemovedSuccessfully(map.Name, map.Author.NickName), Context.Player); logger.LogDebug("Player {PlayerId} removed map {MapName}", Context.Player.Id, map.Name); } } diff --git a/src/Modules/MatchManagerModule/Controllers/MatchCommandsController.cs b/src/Modules/MatchManagerModule/Controllers/MatchCommandsController.cs index 7df025ef1..8bc5ef449 100644 --- a/src/Modules/MatchManagerModule/Controllers/MatchCommandsController.cs +++ b/src/Modules/MatchManagerModule/Controllers/MatchCommandsController.cs @@ -12,7 +12,7 @@ namespace EvoSC.Modules.Official.MatchManagerModule.Controllers; [Controller] -public class MatchCommandsController(IMatchControlService matchControl, IServerClient server, Locale locale) +public class MatchCommandsController(IMatchControlService matchControl, Locale locale) : EvoScController { private readonly dynamic _locale = locale; @@ -42,7 +42,7 @@ public async Task EndMatchAsync() public async Task RestartMatchAsync() { await matchControl.RestartMatchAsync(); - await server.InfoMessageAsync(_locale.RestartedMatch(Context.Player.NickName)); + await Context.Chat.InfoMessageAsync(_locale.RestartedMatch(Context.Player.NickName)); Context.AuditEvent.Success() .WithEventName(AuditEvents.RestartMatch) @@ -53,7 +53,7 @@ public async Task RestartMatchAsync() public async Task EndRoundAsync() { await matchControl.EndRoundAsync(); - await server.InfoMessageAsync(_locale.ForcedRoundEnd(Context.Player.NickName)); + await Context.Chat.InfoMessageAsync(_locale.ForcedRoundEnd(Context.Player.NickName)); Context.AuditEvent.Success() .WithEventName(AuditEvents.EndRound) @@ -65,7 +65,7 @@ public async Task EndRoundAsync() public async Task SkipMapAsync() { await matchControl.SkipMapAsync(); - await server.InfoMessageAsync(_locale.SkippedToNextMap(Context.Player.NickName)); + await Context.Chat.InfoMessageAsync(_locale.SkippedToNextMap(Context.Player.NickName)); Context.AuditEvent.Success() .WithEventName(AuditEvents.SkipMap) @@ -82,7 +82,7 @@ public async Task SetRoundPointsAsync(int team, int points) .WithEventName(AuditEvents.TeamRoundPointsSet) .HavingProperties(new { Points = points, Team = playerTeam }); - await server.SuccessMessageAsync(Context.Player, $"Round points for team {playerTeam} was set to {points}."); + await Context.Chat.SuccessMessageAsync($"Round points for team {playerTeam} was set to {points}.",Context.Player); } [ChatCommand("setteammappoints", "Set the map points of a team.", MatchControlPermissions.SetTeamPoints)] @@ -95,7 +95,7 @@ public async Task SetMapPointsAsync(int team, int points) .WithEventName(AuditEvents.TeamMapPointsSet) .HavingProperties(new { Points = points, Team = playerTeam }); - await server.SuccessMessageAsync(Context.Player, $"Map points for team {playerTeam} was set to {points}."); + await Context.Chat.SuccessMessageAsync($"Map points for team {playerTeam} was set to {points}.", Context.Player); } [ChatCommand("setteammatchpoints", "Set the match points of a team.", MatchControlPermissions.SetTeamPoints)] @@ -108,7 +108,7 @@ public async Task SetMatchPointsAsync(int team, int points) .WithEventName(AuditEvents.TeamMatchPointsSet) .HavingProperties(new { Points = points, Team = playerTeam }); - await server.SuccessMessageAsync(Context.Player, $"Match points for team {playerTeam} was set to {points}."); + await Context.Chat.SuccessMessageAsync($"Match points for team {playerTeam} was set to {points}.", Context.Player); } [ChatCommand("pause", "Pause the current match.", MatchControlPermissions.PauseMatch)] diff --git a/src/Modules/MatchManagerModule/Services/MatchManagerHandlerService.cs b/src/Modules/MatchManagerModule/Services/MatchManagerHandlerService.cs index d09cfa8b0..e210d1df3 100644 --- a/src/Modules/MatchManagerModule/Services/MatchManagerHandlerService.cs +++ b/src/Modules/MatchManagerModule/Services/MatchManagerHandlerService.cs @@ -16,7 +16,7 @@ namespace EvoSC.Modules.Official.MatchManagerModule.Services; [Service(LifeStyle = ServiceLifeStyle.Scoped)] -public class MatchManagerHandlerService(ILiveModeService liveModeService, IServerClient server, +public class MatchManagerHandlerService(ILiveModeService liveModeService, IChatService chat, IMatchSettingsService matchSettings, ILogger logger, IEventManager events, IContextService context, Locale locale) : IMatchManagerHandlerService @@ -28,7 +28,7 @@ public async Task SetModeAsync(string mode, IPlayer actor) if (mode == "list") { var modes = string.Join(", ", liveModeService.GetAvailableModes()); - await server.SuccessMessageAsync(actor, _locale.PlayerLanguage.AvailableModes(modes)); + await chat.SuccessMessageAsync(_locale.PlayerLanguage.AvailableModes(modes), actor); } else { @@ -42,7 +42,7 @@ await events.RaiseAsync(MatchSettingsEvent.LiveModeSet, catch (LiveModeNotFoundException ex) { var modes = string.Join(", ", liveModeService.GetAvailableModes()); - await server.ErrorMessageAsync(actor, _locale.PlayerLanguage.LiveModeNotFound(ex.Message, modes)); + await chat.ErrorMessageAsync(_locale.PlayerLanguage.LiveModeNotFound(ex.Message, modes), actor); } } } @@ -57,18 +57,18 @@ public async Task LoadMatchSettingsAsync(string name, IPlayer actor) .WithEventName(AuditEvents.MatchSettingsLoaded) .HavingProperties(new {Name = name}); - await server.InfoMessageAsync(_locale.LoadedMatchSettings(actor.NickName, name)); + await chat.InfoMessageAsync(_locale.LoadedMatchSettings(actor.NickName, name)); await events.RaiseAsync(MatchSettingsEvent.MatchSettingsLoaded, new MatchSettingsLoadedEventArgs {Name = name}); } catch (FileNotFoundException ex) { - await server.ErrorMessageAsync(actor, _locale.PlayerLanguage.CannotFindMatchSettings(name)); + await chat.ErrorMessageAsync(_locale.PlayerLanguage.CannotFindMatchSettings(name), actor); } catch (Exception ex) { - await server.ErrorMessageAsync(actor, _locale.PlayerLanguage.UnknownErrorWhenLoadingMatchSettings); + await chat.ErrorMessageAsync(_locale.PlayerLanguage.UnknownErrorWhenLoadingMatchSettings, actor); logger.LogError(ex, "Failed to load MatchSettings"); throw; } @@ -101,21 +101,21 @@ public async Task SetScriptSettingAsync(string name, string value, IPlayer actor .HavingProperties(new {Name = name, Value = value}) .Comment(_locale.Audit_ModeScriptSettingsModified); - await server.SuccessMessageAsync(_locale.ScriptSettingsSetTo(name, value)); + await chat.SuccessMessageAsync(_locale.ScriptSettingsSetTo(name, value)); } catch (FormatException ex) { - await server.ErrorMessageAsync(actor, _locale.PlayerLanguage.WrongScriptSettingFormat); + await chat.ErrorMessageAsync(_locale.PlayerLanguage.WrongScriptSettingFormat, actor); logger.LogError(ex, "Wrong format while setting script setting value"); } catch (XmlRpcFaultException ex) { - await server.ErrorMessageAsync(actor, _locale.PlayerLanguageFailedSettingScriptSetting(name, ex.Fault.FaultString)); + await chat.ErrorMessageAsync(_locale.PlayerLanguageFailedSettingScriptSetting(name, ex.Fault.FaultString), actor); logger.LogError(ex, "XMLRPC fault while setting script setting"); } catch (Exception ex) { - await server.ErrorMessageAsync(actor, _locale.PlayerLanguage.ErrorOccuredWhenSettingScriptSetting(ex.Message)); + await chat.ErrorMessageAsync(_locale.PlayerLanguage.ErrorOccuredWhenSettingScriptSetting(ex.Message), actor); logger.LogError(ex, "Failed to set script setting value"); throw; } diff --git a/src/Modules/MatchReadyModule/Services/PlayerReadyService.cs b/src/Modules/MatchReadyModule/Services/PlayerReadyService.cs index 8e6a41c68..e96c9c9d3 100644 --- a/src/Modules/MatchReadyModule/Services/PlayerReadyService.cs +++ b/src/Modules/MatchReadyModule/Services/PlayerReadyService.cs @@ -10,7 +10,7 @@ namespace EvoSC.Modules.Official.MatchReadyModule.Services; [Service(LifeStyle = ServiceLifeStyle.Singleton)] public class PlayerReadyService(IPlayerReadyTrackerService playerReadyTrackerService, IManialinkManager manialinks, - IServerClient server, IPlayerManagerService players) + IChatService chat, IPlayerManagerService players) : IPlayerReadyService { private bool _widgetEnabled; @@ -29,11 +29,11 @@ public async Task SetPlayerReadyStatusAsync(IPlayer player, bool isReady) if (isReady) { - await server.InfoMessageAsync($"$<{player.NickName}$> is ready."); + await chat.InfoMessageAsync($"$<{player.NickName}$> is ready."); } else { - await server.InfoMessageAsync($"$<{player.NickName}$> is no longer ready."); + await chat.InfoMessageAsync($"$<{player.NickName}$> is no longer ready."); } await UpdateWidgetAsync(); diff --git a/src/Modules/ModuleManagerModule/Services/ModuleManagerService.cs b/src/Modules/ModuleManagerModule/Services/ModuleManagerService.cs index 294a4413b..ccee62a2a 100644 --- a/src/Modules/ModuleManagerModule/Services/ModuleManagerService.cs +++ b/src/Modules/ModuleManagerModule/Services/ModuleManagerService.cs @@ -3,6 +3,7 @@ using EvoSC.Common.Interfaces.Controllers; using EvoSC.Common.Interfaces.Localization; using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; using EvoSC.Common.Services.Attributes; using EvoSC.Common.Util.TextFormatting; using EvoSC.Modules.Interfaces; @@ -13,7 +14,7 @@ namespace EvoSC.Modules.Official.ModuleManagerModule.Services; [Service] -public class ModuleManagerService(IContextService context, IModuleManager modules, IServerClient server, Locale locale) +public class ModuleManagerService(IContextService context, IModuleManager modules, IChatService chat, Locale locale) : IModuleManagerService { private readonly dynamic _locale = locale; @@ -34,7 +35,7 @@ public async Task EnableModuleAsync(IModuleLoadContext module) if (actor != null) { - await server.SuccessMessageAsync(actor, _locale.PlayerLanguage.ModuleWasEnabled(module.ModuleInfo.Name)); + await chat.SuccessMessageAsync(_locale.PlayerLanguage.ModuleWasEnabled(module.ModuleInfo.Name), actor); } } catch (Exception ex) @@ -43,7 +44,7 @@ public async Task EnableModuleAsync(IModuleLoadContext module) if (actor != null) { - await server.ErrorMessageAsync(actor, _locale.PlayerLanguage.FailedEnablingModule(ex.Message)); + await chat.ErrorMessageAsync(_locale.PlayerLanguage.FailedEnablingModule(ex.Message), actor); } throw; @@ -66,7 +67,7 @@ public async Task DisableModuleAsync(IModuleLoadContext module) if (actor != null) { - await server.SuccessMessageAsync(actor, _locale.PlayerLanguage.ModuleWasDisabled(module.ModuleInfo.Name)); + await chat.SuccessMessageAsync(_locale.PlayerLanguage.ModuleWasDisabled(module.ModuleInfo.Name), actor); } } catch (Exception ex) @@ -75,7 +76,7 @@ public async Task DisableModuleAsync(IModuleLoadContext module) if (actor != null) { - await server.ErrorMessageAsync(actor, _locale.PlayerLanguage.FailedDisablingModule(ex.Message)); + await chat.ErrorMessageAsync(_locale.PlayerLanguage.FailedDisablingModule(ex.Message), actor); } throw; @@ -96,6 +97,6 @@ public Task ListModulesAsync(IPlayer actor) message.AddText(", "); } - return server.InfoMessageAsync(actor, message.ToString()); + return chat.InfoMessageAsync(message.ToString(), actor); } } diff --git a/src/Modules/NextMapModule/Controllers/NextMapChatController.cs b/src/Modules/NextMapModule/Controllers/NextMapChatController.cs index c503c51f8..3c8ad8e14 100644 --- a/src/Modules/NextMapModule/Controllers/NextMapChatController.cs +++ b/src/Modules/NextMapModule/Controllers/NextMapChatController.cs @@ -9,7 +9,7 @@ namespace EvoSC.Modules.Official.NextMapModule.Controllers; [Controller] -public class NextMapChatController(INextMapService nextMapService, IServerClient server, Locale locale) +public class NextMapChatController(INextMapService nextMapService, Locale locale) : EvoScController { private readonly dynamic _locale = locale; @@ -18,6 +18,6 @@ public class NextMapChatController(INextMapService nextMapService, IServerClient public async Task GetNextMapAsync() { var nextMap = await nextMapService.GetNextMapAsync(); - await server.InfoMessageAsync(_locale.PlayerLanguage.NextMap(nextMap.Name, nextMap.Author?.NickName)); + await Context.Chat.InfoMessageAsync(_locale.PlayerLanguage.NextMap(nextMap.Name, nextMap.Author?.NickName)); } } diff --git a/src/Modules/OpenPlanetModule/Services/OpenPlanetControlService.cs b/src/Modules/OpenPlanetModule/Services/OpenPlanetControlService.cs index 3169c3a44..6d98929db 100644 --- a/src/Modules/OpenPlanetModule/Services/OpenPlanetControlService.cs +++ b/src/Modules/OpenPlanetModule/Services/OpenPlanetControlService.cs @@ -117,13 +117,13 @@ await auditService.NewEvent(OpAuditEvents.PlayerJailed) switch (reason) { case OpJailReason.InvalidVersion: - await server.ErrorMessageAsync(player, _locale.PlayerLanguage.ProhibitedVersion); + await server.Chat.ErrorMessageAsync(_locale.PlayerLanguage.ProhibitedVersion, player); break; case OpJailReason.InvalidSignatureMode: - await server.ErrorMessageAsync(player, _locale.PlayerLanguage.ProhibitedSignatureMode); + await server.Chat.ErrorMessageAsync(_locale.PlayerLanguage.ProhibitedSignatureMode, player); break; case OpJailReason.OpenPlanetNotAllowed: - await server.ErrorMessageAsync(player, _locale.PlayerLanguage.OpenPlanetProhibited); + await server.Chat.ErrorMessageAsync(_locale.PlayerLanguage.OpenPlanetProhibited, player); break; } } @@ -138,6 +138,6 @@ private async Task ReleasePlayerAsync(IPlayer player) scheduler.UnScheduleKickPlayer(player); await manialinks.HideManialinkAsync("OpenPlanetModule.WarningWindow"); await server.Remote.ForceSpectatorAsync(player.GetLogin(), 0); - await server.SuccessMessageAsync(player, _locale.PlayerLanguage.CorrectSignatureMode); + await server.Chat.SuccessMessageAsync(_locale.PlayerLanguage.CorrectSignatureMode, player); } } diff --git a/src/Modules/Player/Controllers/PlayerCommandsController.cs b/src/Modules/Player/Controllers/PlayerCommandsController.cs index 4cca24037..ad3ddb8b0 100644 --- a/src/Modules/Player/Controllers/PlayerCommandsController.cs +++ b/src/Modules/Player/Controllers/PlayerCommandsController.cs @@ -10,7 +10,7 @@ namespace EvoSC.Modules.Official.Player.Controllers; [Controller] -public class PlayerCommandsController(IPlayerService players, IServerClient server) : EvoScController +public class PlayerCommandsController(IPlayerService players) : EvoScController { [ChatCommand("kick", "[Command.Kick]", ModPermissions.KickPlayer)] public Task KickPlayerAsync(IOnlinePlayer player) => players.KickAsync(player, Context.Player); @@ -34,6 +34,6 @@ public async Task ForceSpectatorAsync(IOnlinePlayer player) await players.ForceSpectatorAsync(player); Context.AuditEvent.Success().WithEventName(AuditEvents.PlayerForcedToSpectator); - await server.SuccessMessageAsync(Context.Player, $"$<{player.NickName}$> was forced to spectator."); + await Context.Chat.SuccessMessageAsync($"$<{player.NickName}$> was forced to spectator.", Context.Player); } } diff --git a/src/Modules/Player/Controllers/PlayerEventController.cs b/src/Modules/Player/Controllers/PlayerEventController.cs index 97e967b83..5abc2e32a 100644 --- a/src/Modules/Player/Controllers/PlayerEventController.cs +++ b/src/Modules/Player/Controllers/PlayerEventController.cs @@ -6,13 +6,14 @@ using EvoSC.Common.Interfaces; using EvoSC.Common.Interfaces.Controllers; using EvoSC.Common.Interfaces.Localization; +using EvoSC.Common.Interfaces.Services; using EvoSC.Modules.Official.Player.Interfaces; using Microsoft.Extensions.Logging; namespace EvoSC.Modules.Official.Player.Controllers; [Controller] -public class PlayerEventController(IPlayerService playerService, Locale locale, IServerClient server) : EvoScController +public class PlayerEventController(IPlayerService playerService, Locale locale, IChatService chat) : EvoScController { private readonly dynamic _locale = locale; @@ -21,7 +22,7 @@ public async Task OnPlayerJoinedAsync(object sender, PlayerJoinedEventArgs args) { if (args.IsNewPlayer) { - await server.InfoMessageAsync(_locale.PlayerFirstJoined(args.Player.NickName)); + await chat.InfoMessageAsync(_locale.PlayerFirstJoined(args.Player.NickName)); return; } diff --git a/src/Modules/Player/Services/PlayerService.cs b/src/Modules/Player/Services/PlayerService.cs index 4d09f2d10..806ff9211 100644 --- a/src/Modules/Player/Services/PlayerService.cs +++ b/src/Modules/Player/Services/PlayerService.cs @@ -22,7 +22,7 @@ public class PlayerService(IPlayerManagerService playerManager, IServerClient se public async Task GreetPlayerAsync(IPlayer player) { - await server.InfoMessageAsync(_locale.PlayerJoined(player.NickName)); + await server.Chat.InfoMessageAsync(_locale.PlayerJoined(player.NickName)); await playerManager.UpdateLastVisitAsync(player); } @@ -45,11 +45,11 @@ public async Task KickAsync(IPlayer player, IPlayer actor) .HavingProperties(new {Player = player}) .Comment(_locale.Audit_Kicked); - await server.SuccessMessageAsync(actor, _locale.PlayerLanguage.PlayerKicked(player.NickName)); + await server.Chat.SuccessMessageAsync(_locale.PlayerLanguage.PlayerKicked(player.NickName), actor); } else { - await server.ErrorMessageAsync(actor, _locale.PlayerLanguage.PlayerKickingFailed); + await server.Chat.ErrorMessageAsync(_locale.PlayerLanguage.PlayerKickingFailed, actor); } } @@ -62,12 +62,12 @@ public async Task MuteAsync(IPlayer player, IPlayer actor) .HavingProperties(new {Player = player}) .Comment(_locale.Audit_Muted); - await server.WarningMessageAsync(player, _locale.PlayerLanguage.YouWereMuted); - await server.SuccessMessageAsync(actor, _locale.PlayerLanguage.PlayerMuted(player.NickName)); + await server.Chat.WarningMessageAsync(_locale.PlayerLanguage.YouWereMuted, player); + await server.Chat.SuccessMessageAsync(_locale.PlayerLanguage.PlayerMuted(player.NickName), actor); } else { - await server.ErrorMessageAsync(actor, _locale.PlayerMutingFailed); + await server.Chat.ErrorMessageAsync(_locale.PlayerMutingFailed, actor); } } @@ -80,12 +80,12 @@ public async Task UnmuteAsync(IPlayer player, IPlayer actor) .HavingProperties(new {Player = player}) .Comment(_locale.Audit_Unmuted); - await server.InfoMessageAsync(player, _locale.PlayerLanguage.YouGotUnmuted); - await server.SuccessMessageAsync(actor, _locale.PlayerLanguage.PlayerUnmuted(player.NickName)); + await server.Chat.InfoMessageAsync(_locale.PlayerLanguage.YouGotUnmuted, player); + await server.Chat.SuccessMessageAsync(_locale.PlayerLanguage.PlayerUnmuted(player.NickName), actor); } else { - await server.ErrorMessageAsync(actor, _locale.PlayerLanguage.PlayerUnmutingFailed); + await server.Chat.ErrorMessageAsync(_locale.PlayerLanguage.PlayerUnmutingFailed, actor); } } @@ -108,7 +108,7 @@ public async Task BanAsync(IPlayer player, IPlayer actor) .HavingProperties(new {Player = player}) .Comment(_locale.Audit_Banned); - await server.SuccessMessageAsync(actor, _locale.PlayerLanguage.PlayerBanned(player.NickName)); + await server.Chat.SuccessMessageAsync(_locale.PlayerLanguage.PlayerBanned(player.NickName), actor); } public async Task UnbanAsync(string login, IPlayer actor) @@ -122,13 +122,13 @@ public async Task UnbanAsync(string login, IPlayer actor) .HavingProperties(new {PlayerLogin = login}) .Comment(_locale.Audit_Unbanned); - await server.SuccessMessageAsync(actor, _locale.PlayerLanguage.PlayerUnbanned(login)); + await server.Chat.SuccessMessageAsync(_locale.PlayerLanguage.PlayerUnbanned(login), actor); } } catch (Exception ex) { logger.LogError(ex, "Failed to unban player {Login}", login); - await server.ErrorMessageAsync(actor, _locale.PlayerLanguage.PlayerUnbanningFailed(login)); + await server.Chat.ErrorMessageAsync(_locale.PlayerLanguage.PlayerUnbanningFailed(login), actor); } try @@ -140,13 +140,13 @@ public async Task UnbanAsync(string login, IPlayer actor) .HavingProperties(new {PlayerLogin = login}) .Comment(_locale.Audit_Unblacklisted); - await server.SuccessMessageAsync(actor, _locale.PlayerLanguage.PlayerUnblacklisted(login)); + await server.Chat.SuccessMessageAsync(_locale.PlayerLanguage.PlayerUnblacklisted(login), actor); } } catch (Exception ex) { logger.LogError(ex, "Failed to un-blacklist player {Login}", login); - await server.ErrorMessageAsync(actor, _locale.PlayerLanguage.PlayerUnblacklistingFailed(login)); + await server.Chat.ErrorMessageAsync(_locale.PlayerLanguage.PlayerUnblacklistingFailed(login), actor); } } diff --git a/src/Modules/PlayerRecords/Services/PlayerRecordHandlerService.cs b/src/Modules/PlayerRecords/Services/PlayerRecordHandlerService.cs index cc31a8350..64564f900 100644 --- a/src/Modules/PlayerRecords/Services/PlayerRecordHandlerService.cs +++ b/src/Modules/PlayerRecords/Services/PlayerRecordHandlerService.cs @@ -16,7 +16,7 @@ namespace EvoSC.Modules.Official.PlayerRecords.Services; [Service(LifeStyle = ServiceLifeStyle.Singleton)] public class PlayerRecordHandlerService(IPlayerRecordsService playerRecords, IPlayerManagerService players, - IEventManager events, IPlayerRecordSettings recordOptions, IServerClient server, IMapService maps, + IEventManager events, IPlayerRecordSettings recordOptions, IChatService chat, IMapService maps, Locale locale) : IPlayerRecordHandlerService { @@ -45,9 +45,9 @@ public async Task CheckWaypointAsync(WayPointEventArgs waypoint) public Task SendRecordUpdateToChatAsync(IPlayerRecord record) => recordOptions.EchoPb switch { - EchoOptions.All => server.InfoMessageAsync( + EchoOptions.All => chat.InfoMessageAsync( _locale.PlayerGotANewPb(record.Player.NickName, FormattingUtils.FormatTime(record.Score))), - EchoOptions.Player => server.InfoMessageAsync( + EchoOptions.Player => chat.InfoMessageAsync( _locale.PlayerLanguage.YouGotANewPb(record.Player, FormattingUtils.FormatTime(record.Score))), _ => Task.CompletedTask }; @@ -59,7 +59,7 @@ public async Task ShowCurrentPlayerPbAsync(IPlayer player) if (pb == null) { - await server.InfoMessageAsync(player, _locale.PlayerLanguage.YouHaveNotSetATime); + await chat.InfoMessageAsync(_locale.PlayerLanguage.YouHaveNotSetATime, player); return; } @@ -68,6 +68,6 @@ public async Task ShowCurrentPlayerPbAsync(IPlayer player) var m = pb.Score / 1000 / 60; var formattedTime = $"{(m > 0 ? m + ":" : "")}{s:00}.{ms:000}"; - await server.InfoMessageAsync(player, _locale.PlayerLanguage.YourCurrentPbIs(formattedTime)); + await chat.InfoMessageAsync(_locale.PlayerLanguage.YourCurrentPbIs(formattedTime), player); } } diff --git a/src/Modules/ServerManagementModule/Controllers/ServerCommandsController.cs b/src/Modules/ServerManagementModule/Controllers/ServerCommandsController.cs index e4192f5a2..90cc2e723 100644 --- a/src/Modules/ServerManagementModule/Controllers/ServerCommandsController.cs +++ b/src/Modules/ServerManagementModule/Controllers/ServerCommandsController.cs @@ -23,7 +23,7 @@ public async Task SetServerPasswordAsync(string password) .HavingProperties(new { password }) .Success(); - await Context.Server.SuccessMessageAsync(Context.Player, "The password was changed."); + await Context.Chat.SuccessMessageAsync("The password was changed.", Context.Player); } [ChatCommand("clearpassword", "Clear the server password.", @@ -38,7 +38,7 @@ public async Task ClearServerPasswordAsync() .HavingProperties(new { password = "", cleared = true }) .Success(); - await Context.Server.SuccessMessageAsync(Context.Player, "The password was cleared and removed."); + await Context.Chat.SuccessMessageAsync("The password was cleared and removed.", Context.Player); } [ChatCommand("setmaxplayers", "Set maximum number of players that can join the server.", @@ -53,7 +53,7 @@ public async Task SetMaxPlayersAsync(int maxPlayers) .HavingProperties(new { maxPlayers }) .Success(); - await Context.Server.SuccessMessageAsync(Context.Player, $"Max players set to {maxPlayers}"); + await Context.Chat.SuccessMessageAsync($"Max players set to {maxPlayers}", Context.Player); } [ChatCommand("setmaxspectators", "Set the maximum number of spectators that can join the server.", @@ -68,6 +68,6 @@ public async Task SetMaxSpectatorsAsync(int maxSpectators) .HavingProperties(new { maxSpectators }) .Success(); - await Context.Server.SuccessMessageAsync(Context.Player, $"Max spectators set to {maxSpectators}"); + await Context.Chat.SuccessMessageAsync($"Max spectators set to {maxSpectators}", Context.Player); } } diff --git a/src/Modules/SetName/Services/SetNameService.cs b/src/Modules/SetName/Services/SetNameService.cs index 88fa71848..2da9b4ce2 100644 --- a/src/Modules/SetName/Services/SetNameService.cs +++ b/src/Modules/SetName/Services/SetNameService.cs @@ -11,7 +11,7 @@ namespace EvoSC.Modules.Official.SetName.Services; [Service(LifeStyle = ServiceLifeStyle.Scoped)] -public class SetNameService(IServerClient server, IPlayerRepository playerRepository, IPlayerCacheService playerCache, +public class SetNameService(IChatService chat, IPlayerRepository playerRepository, IPlayerCacheService playerCache, IEventManager events, Locale locale) : ISetNameService { @@ -21,15 +21,15 @@ public async Task SetNicknameAsync(IPlayer player, string newName) { if (player.NickName.Equals(newName, StringComparison.Ordinal)) { - await server.ErrorMessageAsync(player, _locale.PlayerLanguage.DidNotChangeName); + await chat.ErrorMessageAsync(_locale.PlayerLanguage.DidNotChangeName, player); return; } await playerRepository.UpdateNicknameAsync(player, newName); await playerCache.UpdatePlayerAsync(player); - await server.SuccessMessageAsync(player, _locale.PlayerLanguage.NameSuccessfullySet(newName)); - await server.InfoMessageAsync(_locale.PlayerChangedTheirName(player.NickName, newName)); + await chat.SuccessMessageAsync(_locale.PlayerLanguage.NameSuccessfullySet(newName), player); + await chat.InfoMessageAsync(_locale.PlayerChangedTheirName(player.NickName, newName)); await events.RaiseAsync(SetNameEvents.NicknameUpdated, new NicknameUpdatedEventArgs { diff --git a/src/Modules/TeamChatModule/Config/ITeamChatSettings.cs b/src/Modules/TeamChatModule/Config/ITeamChatSettings.cs new file mode 100644 index 000000000..ee9d5c2c3 --- /dev/null +++ b/src/Modules/TeamChatModule/Config/ITeamChatSettings.cs @@ -0,0 +1,15 @@ +using System.ComponentModel; +using Config.Net; +using EvoSC.Modules.Attributes; + +namespace EvoSC.Modules.Official.TeamChatModule.Config; + +[Settings] +public interface ITeamChatSettings +{ + [Option(DefaultValue = 1L), Description("Show team chat to players in this group, even if they are not in the team. Has higher priority than ExcludeGroup.")] + public long IncludeGroup { get; } + + [Option(DefaultValue = 0L), Description("Do not show team chat to players in this group, even if they are in the team.")] + public long ExcludeGroup { get; } +} diff --git a/src/Modules/TeamChatModule/Localization.resx b/src/Modules/TeamChatModule/Localization.resx new file mode 100644 index 000000000..10e3157c7 --- /dev/null +++ b/src/Modules/TeamChatModule/Localization.resx @@ -0,0 +1,19 @@ + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/src/Modules/TeamChatModule/Middlewares/TeamChatMiddleware.cs b/src/Modules/TeamChatModule/Middlewares/TeamChatMiddleware.cs new file mode 100644 index 000000000..d173d6dd5 --- /dev/null +++ b/src/Modules/TeamChatModule/Middlewares/TeamChatMiddleware.cs @@ -0,0 +1,35 @@ +using EvoSC.Common.Middleware; +using EvoSC.Common.Middleware.Attributes; +using EvoSC.Common.Remote.ChatRouter; +using EvoSC.Modules.Official.TeamChatModule.Config; + +namespace EvoSC.Modules.Official.TeamChatModule.Middlewares; + +[Middleware(For = PipelineType.ChatRouter)] +public class TeamChatMiddleware(ActionDelegate next, ITeamChatSettings settings) +{ + public Task ExecuteAsync(ChatRouterPipelineContext context) + { + if (context.IsTeamMessage) + { + context.Recipients = context.Recipients.Where(p => + { + // show message to players in the include group + if (p.Groups.Any(g => g.Id == settings.IncludeGroup)) + { + return true; + } + + // do not show message to players in exclude group + if (p.Groups.Any(g => g.Id == settings.ExcludeGroup)) + { + return false; + } + + return p.Team == context.Author.Team; + }).ToList(); + } + + return next(context); + } +} diff --git a/src/Modules/TeamChatModule/TeamChatModule.cs b/src/Modules/TeamChatModule/TeamChatModule.cs new file mode 100644 index 000000000..308be4ace --- /dev/null +++ b/src/Modules/TeamChatModule/TeamChatModule.cs @@ -0,0 +1,6 @@ +using EvoSC.Modules.Attributes; + +namespace EvoSC.Modules.Official.TeamChatModule; + +[Module(IsInternal = true)] +public class TeamChatModule : EvoScModule; diff --git a/src/Modules/TeamChatModule/TeamChatModule.csproj b/src/Modules/TeamChatModule/TeamChatModule.csproj new file mode 100644 index 000000000..4dd03ed37 --- /dev/null +++ b/src/Modules/TeamChatModule/TeamChatModule.csproj @@ -0,0 +1,25 @@ + + + net8.0 + enable + enable + EvoSC.Modules.Official.TeamChatModule + false + TeamChatModule + Team Chat + Functionality for teams to only send messages to themselves. + 1.0.0 + Evo + + + + + + + + + + + + + diff --git a/src/Modules/TeamChatModule/info.toml b/src/Modules/TeamChatModule/info.toml new file mode 100644 index 000000000..225fb0fd8 --- /dev/null +++ b/src/Modules/TeamChatModule/info.toml @@ -0,0 +1,11 @@ +[info] +# A unique name for this module, this is used as a identifier +name = "TeamChatModule" +# The title of the module +title = "Team Chat" +# A short description of what the module is and does +summary = "Functionality for teams to only send messages to themselves." +# The current version of this module, using SEMVER +version = "1.0.0" +# The name of the author that created this module +author = "Evo" diff --git a/tests/EvoSC.CLI.Tests/RequiredFeaturesAttributeTests.cs b/tests/EvoSC.CLI.Tests/RequiredFeaturesAttributeTests.cs index b5caeeca6..5350fa85b 100644 --- a/tests/EvoSC.CLI.Tests/RequiredFeaturesAttributeTests.cs +++ b/tests/EvoSC.CLI.Tests/RequiredFeaturesAttributeTests.cs @@ -52,7 +52,8 @@ public void All_Features_Enum_Adds_All_Features() AppFeature.ActionPipelines, AppFeature.Permissions, AppFeature.Manialinks, - AppFeature.Themes + AppFeature.Themes, + AppFeature.Chat, }, attr.Features); } } diff --git a/tests/EvoSC.Common.Tests/Services/MapServiceTests.cs b/tests/EvoSC.Common.Tests/Services/MapServiceTests.cs index fc2ca4cbe..5e28c88eb 100644 --- a/tests/EvoSC.Common.Tests/Services/MapServiceTests.cs +++ b/tests/EvoSC.Common.Tests/Services/MapServiceTests.cs @@ -31,7 +31,7 @@ public class MapServiceTests private readonly Mock _config = new(); private readonly Mock _playerService = new(); - private readonly (Mock Client, Mock Remote) + private readonly (Mock Client, Mock Remote, Mock Chat) _server = Mocking.NewServerClientMock(); private readonly MapService _mapService; diff --git a/tests/EvoSC.Testing.Tests/MockingTests.cs b/tests/EvoSC.Testing.Tests/MockingTests.cs index 62aaa5abb..417ae7d41 100644 --- a/tests/EvoSC.Testing.Tests/MockingTests.cs +++ b/tests/EvoSC.Testing.Tests/MockingTests.cs @@ -78,7 +78,8 @@ public void SetupMock_For_ManialinkInteraction_Sets_Up_Correctly() var player = new Mock(); var mlActionContext = new Mock(); var mlManager = new Mock(); - mock.SetupMock(player.Object, mlActionContext.Object, mlManager.Object); + var server = Mocking.NewServerClientMock(); + mock.SetupMock(server.Client, player.Object, mlActionContext.Object, mlManager.Object); Assert.Equal(player.Object, mock.Context.Object.Player); Assert.Equal(mlActionContext.Object, mock.Context.Object.ManialinkAction); @@ -92,7 +93,8 @@ public void NewManialinkInteractionContextMock_Returns_Correct_Mock() var player = new Mock(); var mlActionContext = new Mock(); var mlManager = new Mock(); - var mock = Mocking.NewManialinkInteractionContextMock(player.Object, mlActionContext.Object, mlManager.Object); + var server = Mocking.NewServerClientMock(); + var mock = Mocking.NewManialinkInteractionContextMock(server.Client, player.Object, mlActionContext.Object, mlManager.Object); Assert.Equal(player.Object, mock.Context.Object.Player); Assert.Equal(mlActionContext.Object, mock.Context.Object.ManialinkAction); diff --git a/tests/Modules/ASayModule.Tests/ASayServiceTest.cs b/tests/Modules/ASayModule.Tests/ASayServiceTest.cs index 9e7507a92..a762698d8 100644 --- a/tests/Modules/ASayModule.Tests/ASayServiceTest.cs +++ b/tests/Modules/ASayModule.Tests/ASayServiceTest.cs @@ -25,7 +25,7 @@ public class ASayServiceTest private readonly Mock _manialinkManager = new(); private ControllerContextMock _commandContext; - private readonly (Mock Client, Mock Remote) _serverClient = + private readonly (Mock Client, Mock Remote, Mock Chat) _serverClient = Mocking.NewServerClientMock(); public ASayServiceTest() @@ -40,7 +40,7 @@ public ASayServiceTest() IASayService ASayService, Mock ContextService, Mock PlayerManager, - (Mock Client, Mock Remote) Server, + (Mock Client, Mock Remote, Mock Chat) Server, Mock Player, Mock Actor, Mock Audit diff --git a/tests/Modules/ForceTeamModule.Tests/Controllers/ForceTeamManialinkControllerTests.cs b/tests/Modules/ForceTeamModule.Tests/Controllers/ForceTeamManialinkControllerTests.cs index bd103a9f7..da4a5ef4b 100644 --- a/tests/Modules/ForceTeamModule.Tests/Controllers/ForceTeamManialinkControllerTests.cs +++ b/tests/Modules/ForceTeamModule.Tests/Controllers/ForceTeamManialinkControllerTests.cs @@ -51,11 +51,9 @@ public async Task Player_Is_Switched_And_Audited() public async Task Player_Not_Found_Sends_Error() { _playerManagerService.Setup(m => m.GetOnlinePlayerAsync(PlayerAccountId)).ReturnsAsync((IOnlinePlayer?)null); - var serverMock = Mocking.NewServerClientMock(); - Context.Setup(m => m.Server).Returns(serverMock.Client.Object); await Controller.SwitchPlayerAsync(PlayerAccountId); - - serverMock.Client.Verify(m => m.ErrorMessageAsync(_player.Object, It.IsAny())); + + Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny(), _player.Object)); } } diff --git a/tests/Modules/ForceTeamModule.Tests/Services/ForceTeamServiceTests.cs b/tests/Modules/ForceTeamModule.Tests/Services/ForceTeamServiceTests.cs index b57f50052..01f2097f1 100644 --- a/tests/Modules/ForceTeamModule.Tests/Services/ForceTeamServiceTests.cs +++ b/tests/Modules/ForceTeamModule.Tests/Services/ForceTeamServiceTests.cs @@ -16,7 +16,7 @@ public class ForceTeamServiceTests private ( IForceTeamService ForceTeamService, Mock ManialinkManager, - (Mock Client, Mock Remote) Server, + (Mock Client, Mock Remote, Mock Chat) Server, Locale locale ) NewForceTeamServiceMock() { diff --git a/tests/Modules/GameModeUiModule.Tests/Services/GameModeUiModuleServiceTests.cs b/tests/Modules/GameModeUiModule.Tests/Services/GameModeUiModuleServiceTests.cs index 4d762dd37..0f2fadff6 100644 --- a/tests/Modules/GameModeUiModule.Tests/Services/GameModeUiModuleServiceTests.cs +++ b/tests/Modules/GameModeUiModule.Tests/Services/GameModeUiModuleServiceTests.cs @@ -1,4 +1,5 @@ using EvoSC.Common.Interfaces; +using EvoSC.Common.Interfaces.Services; using EvoSC.Modules.Official.GameModeUiModule.Config; using EvoSC.Modules.Official.GameModeUiModule.Interfaces; using EvoSC.Modules.Official.GameModeUiModule.Services; @@ -14,7 +15,7 @@ public class GameModeUiModuleServiceTests { private readonly Mock _settings = new(); - private readonly (Mock Client, Mock Remote) + private readonly (Mock Client, Mock Remote, Mock Chat) _server = Mocking.NewServerClientMock(); private IGameModeUiModuleService UiModuleServiceMock() diff --git a/tests/Modules/LiveRankingModule.Tests/Services/LiveRankingServiceTests.cs b/tests/Modules/LiveRankingModule.Tests/Services/LiveRankingServiceTests.cs index 105177865..31c9e7776 100644 --- a/tests/Modules/LiveRankingModule.Tests/Services/LiveRankingServiceTests.cs +++ b/tests/Modules/LiveRankingModule.Tests/Services/LiveRankingServiceTests.cs @@ -25,7 +25,7 @@ public class LiveRankingServiceTests private readonly Mock _playerManagerService = new(); private readonly Mock _matchSettingsService = new(); - private readonly (Mock Client, Mock Remote) + private readonly (Mock Client, Mock Remote, Mock Chat) _server = Mocking.NewServerClientMock(); private ILiveRankingService LiveRankingServiceMock() diff --git a/tests/Modules/LocalRecordsModule.Tests/Controllers/LocalRecordsManialinkControllerTests.cs b/tests/Modules/LocalRecordsModule.Tests/Controllers/LocalRecordsManialinkControllerTests.cs index b9c6ee6b6..92c2419a2 100644 --- a/tests/Modules/LocalRecordsModule.Tests/Controllers/LocalRecordsManialinkControllerTests.cs +++ b/tests/Modules/LocalRecordsModule.Tests/Controllers/LocalRecordsManialinkControllerTests.cs @@ -1,5 +1,6 @@ using EvoSC.Common.Interfaces; using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; using EvoSC.Manialinks.Interfaces.Models; using EvoSC.Modules.Official.LocalRecordsModule.Controllers; using EvoSC.Modules.Official.LocalRecordsModule.Interfaces.Services; @@ -15,12 +16,10 @@ public class LocalRecordsManialinkControllerTests : ManialinkControllerTestBase< private readonly Mock _manialinkActionContext = new(); private readonly Mock _actor = new(); private readonly Mock _localRecordsService = new(); - private readonly (Mock Server, Mock Remote) - _server = Mocking.NewServerClientMock(); public LocalRecordsManialinkControllerTests() { - InitMock(_actor.Object, _manialinkActionContext.Object, _localRecordsService, _server.Server); + InitMock(_actor.Object, _manialinkActionContext.Object, _localRecordsService); } [Fact] @@ -50,7 +49,7 @@ public async Task Reset_Records_Error_Is_Reported() _localRecordsService.Setup(m => m.ResetLocalRecordsAsync()).Throws(); await Assert.ThrowsAsync(() => Controller.ConfirmResetAsync(true)); - _server.Server.Verify(m => m.ErrorMessageAsync(It.IsAny())); + Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny())); AuditEventBuilder.Verify(m => m.Error()); } } diff --git a/tests/Modules/LocalRecordsModule.Tests/Services/LocalRecordsServiceTests.cs b/tests/Modules/LocalRecordsModule.Tests/Services/LocalRecordsServiceTests.cs index e96008945..d21380c3a 100644 --- a/tests/Modules/LocalRecordsModule.Tests/Services/LocalRecordsServiceTests.cs +++ b/tests/Modules/LocalRecordsModule.Tests/Services/LocalRecordsServiceTests.cs @@ -36,7 +36,7 @@ namespace LocalRecordsModule.Tests.Services; Mock ManialinkManager, ILogger Logger, Mock Settings, - (Mock Client, Mock Remote) Server, + (Mock Client, Mock Remote, Mock Chat) Server, Mock ThemeManager, Mock PlayerRecordsRepository); @@ -64,7 +64,7 @@ private MockData NewLocalRecordsServiceMock() manialinkManager.Object, logger, settings.Object, - server.Client.Object, + server.Chat.Object, themeManager.Object, playerRecordsRepository.Object ); @@ -236,7 +236,7 @@ public async Task New_Pb_Is_Not_Good_Enough() await mock.Service.UpdatePbAsync(newPb); - mock.Server.Client.Verify(m => m.InfoMessageAsync(It.IsAny()), Times.Never); + mock.Server.Chat.Verify(m => m.InfoMessageAsync(It.IsAny()), Times.Never); } [Fact] @@ -260,7 +260,7 @@ public async Task New_Pb_Is_New_Local_Record() await mock.Service.UpdatePbAsync(newPb); - mock.Server.Client.Verify(m => m.InfoMessageAsync(It.Is(s => s.Contains("gained the"))), Times.Once); + mock.Server.Chat.Verify(m => m.InfoMessageAsync(It.Is(s => s.Contains("gained the"))), Times.Once); } [Fact] @@ -289,7 +289,7 @@ public async Task New_Pb_Is_Improved_From_Old_Record() await mock.Service.UpdatePbAsync(newPb); - mock.Server.Client.Verify(m => m.InfoMessageAsync(It.Is(s => s.Contains("improved the"))), Times.Once); + mock.Server.Chat.Verify(m => m.InfoMessageAsync(It.Is(s => s.Contains("improved the"))), Times.Once); } [Fact] @@ -313,6 +313,6 @@ public async Task New_Pb_Is_Equaled_To_Old_Record() await mock.Service.UpdatePbAsync(newPb); - mock.Server.Client.Verify(m => m.InfoMessageAsync(It.Is(s => s.Contains("equaled their"))), Times.Once); + mock.Server.Chat.Verify(m => m.InfoMessageAsync(It.Is(s => s.Contains("equaled their"))), Times.Once); } } diff --git a/tests/Modules/MapListModule.Tests/Services/MapListServiceTests.cs b/tests/Modules/MapListModule.Tests/Services/MapListServiceTests.cs index 85124b8dc..c7226aa35 100644 --- a/tests/Modules/MapListModule.Tests/Services/MapListServiceTests.cs +++ b/tests/Modules/MapListModule.Tests/Services/MapListServiceTests.cs @@ -31,7 +31,7 @@ public class MapListServiceTests ControllerContextMock ControllerContext, Mock Player, ILogger Logger, - (Mock Client, Mock Remote) Server, + (Mock Client, Mock Remote, Mock Chat) Server, Mock ManialinkManager, Mock PermissionManager ) NewMapListServiceMock() @@ -51,7 +51,7 @@ Mock PermissionManager context.ContextService.Object, mapService.Object, logger, - server.Client.Object, + server.Chat.Object, manialinkManagerService.Object, permissionManagerService.Object ); diff --git a/tests/Modules/MapQueueModuleTests/Controllers/QueueCommandsControllerTests.cs b/tests/Modules/MapQueueModuleTests/Controllers/QueueCommandsControllerTests.cs index 593308bb1..b9c429543 100644 --- a/tests/Modules/MapQueueModuleTests/Controllers/QueueCommandsControllerTests.cs +++ b/tests/Modules/MapQueueModuleTests/Controllers/QueueCommandsControllerTests.cs @@ -16,12 +16,10 @@ public class QueueCommandsControllerTests : CommandInteractionControllerTestBase private Mock _player = new(); private readonly Mock _mapQueueServiceMock = new(); private readonly Mock _mapServiceMock = new(); - private (Mock Client, Mock Remote) _server = Mocking.NewServerClientMock(); - public QueueCommandsControllerTests() { _player.Setup(m => m.AccountId).Returns("a467a996-eba5-44bf-9e2b-8543b50f39ae"); - InitMock(_player.Object, _mapQueueServiceMock, _mapServiceMock, _server.Client); + InitMock(_player.Object, _mapQueueServiceMock, _mapServiceMock); } [Fact] diff --git a/tests/Modules/MapQueueModuleTests/Controllers/QueueControllerTests.cs b/tests/Modules/MapQueueModuleTests/Controllers/QueueControllerTests.cs index 2932a3669..6fde8a282 100644 --- a/tests/Modules/MapQueueModuleTests/Controllers/QueueControllerTests.cs +++ b/tests/Modules/MapQueueModuleTests/Controllers/QueueControllerTests.cs @@ -19,7 +19,7 @@ public class QueueControllerTests : EventControllerTestBase { private Mock _mapQueueServiceMock = new(); private Mock _mapServiceMock = new(); - private (Mock Client, Mock Remote) _server = Mocking.NewServerClientMock(); + private (Mock Client, Mock Remote, Mock Chat) _server = Mocking.NewServerClientMock(); public QueueControllerTests() { diff --git a/tests/Modules/MapsModule.Tests/Controllers/MapsControllerTests.cs b/tests/Modules/MapsModule.Tests/Controllers/MapsControllerTests.cs index adb0a1759..df312c5c7 100644 --- a/tests/Modules/MapsModule.Tests/Controllers/MapsControllerTests.cs +++ b/tests/Modules/MapsModule.Tests/Controllers/MapsControllerTests.cs @@ -21,13 +21,12 @@ public class MapsControllerTests : CommandInteractionControllerTestBase _mxMapService = new(); private Mock _mapService = new(); private Locale _locale; - private (Mock Client, Mock Remote) _server = Mocking.NewServerClientMock(); private Mock _map; public MapsControllerTests() { _locale = Mocking.NewLocaleMock(ContextService.Object); - InitMock(_actor.Object, _logger, _mxMapService, _mapService, _server.Client, _locale); + InitMock(_actor.Object, _logger, _mxMapService, _mapService, _locale); _map = new Mock(); _map.Setup(m => m.Name).Returns("MyMap"); @@ -45,7 +44,7 @@ public async Task Map_Is_Added() _mxMapService.Verify(m => m.FindAndDownloadMapAsync(123, null, _actor.Object), Times.Once); AuditEventBuilder.Verify(m => m.Success(), Times.Once); AuditEventBuilder.Verify(m => m.WithEventName(AuditEvents.MapAdded), Times.Once); - _server.Client.Verify(m => m.SuccessMessageAsync(_actor.Object, It.IsAny()), Times.Once); + Server.Chat.Verify(m => m.SuccessMessageAsync(It.IsAny(), _actor.Object), Times.Once); } [Fact] @@ -57,7 +56,7 @@ public async Task Adding_Map_Failing_With_Exception_Is_Logged() await Assert.ThrowsAsync(() => Controller.AddMapAsync("123")); - _server.Client.Verify(m => m.ErrorMessageAsync(_actor.Object, It.IsAny()), Times.Once); + Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny(), _actor.Object), Times.Once); } [Fact] @@ -69,7 +68,7 @@ public async Task Adding_Duplicate_Map_Returns_Error() await Assert.ThrowsAsync(() => Controller.AddMapAsync("123")); - _server.Client.Verify(m => m.ErrorMessageAsync(_actor.Object, It.IsAny()), Times.Once); + Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny(), _actor.Object), Times.Once); } [Fact] @@ -80,7 +79,7 @@ public async Task Adding_Map_Failing_With_Null_Return_Is_Logged() await Controller.AddMapAsync("123"); - _server.Client.Verify(m => m.ErrorMessageAsync(_actor.Object, It.IsAny()), Times.Once); + Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny(), _actor.Object), Times.Once); } [Fact] @@ -94,8 +93,8 @@ public async Task Map_Is_Removed() _mapService.Verify(m => m.RemoveMapAsync(123), Times.Once); AuditEventBuilder.Verify(m => m.Success(), Times.Once); AuditEventBuilder.Verify(m => m.WithEventName(AuditEvents.MapRemoved), Times.Once); - _server.Client.Verify(m => m.SuccessMessageAsync(_actor.Object, It.IsAny()), Times.Once); - _server.Client.Verify(m => m.ErrorMessageAsync(_actor.Object, It.IsAny()), Times.Never); + Server.Chat.Verify(m => m.SuccessMessageAsync(It.IsAny(), _actor.Object), Times.Once); + Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny(), _actor.Object), Times.Never); _logger.Verify(LogLevel.Debug, null, null, Times.Once()); } @@ -107,8 +106,8 @@ public async Task Map_Removal_Failed_Is_Reported() await Controller.RemoveMapAsync(123); _mapService.Verify(m => m.RemoveMapAsync(123), Times.Never); - _server.Client.Verify(m => m.SuccessMessageAsync(_actor.Object, It.IsAny()), Times.Never); - _server.Client.Verify(m => m.ErrorMessageAsync(_actor.Object, It.IsAny()), Times.Once); + Server.Chat.Verify(m => m.SuccessMessageAsync(It.IsAny(), _actor.Object), Times.Never); + Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny(), _actor.Object), Times.Once); AuditEventBuilder.Verify(m => m.Success(), Times.Never); } } diff --git a/tests/Modules/MatchManagerModule.Tests/Controllers/MatchCommandsControllerTests.cs b/tests/Modules/MatchManagerModule.Tests/Controllers/MatchCommandsControllerTests.cs index 2bfaaf14a..0b3b65e63 100644 --- a/tests/Modules/MatchManagerModule.Tests/Controllers/MatchCommandsControllerTests.cs +++ b/tests/Modules/MatchManagerModule.Tests/Controllers/MatchCommandsControllerTests.cs @@ -1,6 +1,7 @@ using EvoSC.Common.Interfaces; using EvoSC.Common.Interfaces.Localization; using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; using EvoSC.Modules.Official.MatchManagerModule.Controllers; using EvoSC.Modules.Official.MatchManagerModule.Events; using EvoSC.Modules.Official.MatchManagerModule.Interfaces; @@ -15,13 +16,12 @@ public class MatchCommandsControllerTests : CommandInteractionControllerTestBase { private Mock _player = new(); private Mock _matchControl = new(); - private (Mock Client, Mock Remote) _server = Mocking.NewServerClientMock(); private Locale _locale; public MatchCommandsControllerTests() { _locale = Mocking.NewLocaleMock(this.ContextService.Object); - InitMock(_player.Object, _matchControl, _server.Client, _locale); + InitMock(_player.Object, _matchControl, _locale); } [Fact] diff --git a/tests/Modules/MatchManagerModule.Tests/Services/MatchControlServiceTests.cs b/tests/Modules/MatchManagerModule.Tests/Services/MatchControlServiceTests.cs index f37b5787e..0e9cd8982 100644 --- a/tests/Modules/MatchManagerModule.Tests/Services/MatchControlServiceTests.cs +++ b/tests/Modules/MatchManagerModule.Tests/Services/MatchControlServiceTests.cs @@ -1,5 +1,6 @@ using EvoSC.Common.Interfaces; using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; using EvoSC.Modules.Official.MatchManagerModule.Interfaces; using EvoSC.Modules.Official.MatchManagerModule.Services; using EvoSC.Testing; @@ -12,7 +13,7 @@ public class MatchControlServiceTests { private ( IMatchControlService MatchControlService, - (Mock Client, Mock Remote) Server, + (Mock Client, Mock Remote, Mock Chat) Server, Mock EventManager ) NewMatchControlServiceMock() { diff --git a/tests/Modules/NextMapModule.Tests/Controllers/NextMapChatControllerTests.cs b/tests/Modules/NextMapModule.Tests/Controllers/NextMapChatControllerTests.cs index 43ee16525..4c971ae21 100644 --- a/tests/Modules/NextMapModule.Tests/Controllers/NextMapChatControllerTests.cs +++ b/tests/Modules/NextMapModule.Tests/Controllers/NextMapChatControllerTests.cs @@ -2,6 +2,7 @@ using EvoSC.Common.Interfaces; using EvoSC.Common.Interfaces.Localization; using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; using EvoSC.Modules.Official.NextMapModule.Controllers; using EvoSC.Modules.Official.NextMapModule.Interfaces; using EvoSC.Testing; @@ -16,15 +17,13 @@ public class NextMapChatControllerTests : CommandInteractionControllerTestBase _actor = new(); private readonly Mock _nextMapService = new(); - private readonly (Mock Client, Mock Remote) - _server = Mocking.NewServerClientMock(); private readonly Locale _locale; public NextMapChatControllerTests() { _locale = Mocking.NewLocaleMock(ContextService.Object); - InitMock(_actor.Object, _nextMapService, _server.Client, _locale); + InitMock(_actor.Object, _nextMapService, _locale); } [Fact] @@ -43,6 +42,6 @@ public async Task Get_Next_Map_Sends_Chat_Message() await Controller.GetNextMapAsync(); - _server.Client.Verify(c => c.InfoMessageAsync(It.IsAny())); + Server.Chat.Verify(c => c.InfoMessageAsync(It.IsAny())); } } diff --git a/tests/Modules/OpenPlanetModule.Tests/Controllers/OpenPlanetControlManialinkControllerTests.cs b/tests/Modules/OpenPlanetModule.Tests/Controllers/OpenPlanetControlManialinkControllerTests.cs index 43539d215..ab2add3de 100644 --- a/tests/Modules/OpenPlanetModule.Tests/Controllers/OpenPlanetControlManialinkControllerTests.cs +++ b/tests/Modules/OpenPlanetModule.Tests/Controllers/OpenPlanetControlManialinkControllerTests.cs @@ -1,5 +1,6 @@ using EvoSC.Common.Interfaces; using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; using EvoSC.Manialinks.Interfaces.Models; using EvoSC.Modules.Official.OpenPlanetModule.Controllers; using EvoSC.Modules.Official.OpenPlanetModule.Interfaces; @@ -18,7 +19,7 @@ public class OpenPlanetControlManialinkControllerTests : ManialinkControllerTest private readonly Mock _player = new(); private readonly Mock _actionContext = new(); private readonly Mock _controlService = new(); - private readonly (Mock Server, Mock Client) _server = Mocking.NewServerClientMock(); + private readonly (Mock Server, Mock Client, Mock Chat) _server = Mocking.NewServerClientMock(); private readonly Mock _trackerService = new(); private readonly Mock _scheduler = new(); diff --git a/tests/Modules/OpenPlanetModule.Tests/Controllers/OpenPlanetEventControllerTests.cs b/tests/Modules/OpenPlanetModule.Tests/Controllers/OpenPlanetEventControllerTests.cs index fafa63c3c..d205b8bd6 100644 --- a/tests/Modules/OpenPlanetModule.Tests/Controllers/OpenPlanetEventControllerTests.cs +++ b/tests/Modules/OpenPlanetModule.Tests/Controllers/OpenPlanetEventControllerTests.cs @@ -18,7 +18,7 @@ public class OpenPlanetEventControllerTests : EventControllerTestBase _trackerService = new(); - private readonly (Mock Server, Mock Client) _server = Mocking.NewServerClientMock(); + private readonly (Mock Server, Mock Client, Mock Chat) _server = Mocking.NewServerClientMock(); private readonly Mock _playerService = new(); private Mock _player = new(); diff --git a/tests/Modules/OpenPlanetModule.Tests/Services/OpenPlanetControlServiceTests.cs b/tests/Modules/OpenPlanetModule.Tests/Services/OpenPlanetControlServiceTests.cs index 35ee47a69..807f81be7 100644 --- a/tests/Modules/OpenPlanetModule.Tests/Services/OpenPlanetControlServiceTests.cs +++ b/tests/Modules/OpenPlanetModule.Tests/Services/OpenPlanetControlServiceTests.cs @@ -28,7 +28,7 @@ public class OpenPlanetControlServiceTests Mock Permissions, Mock Settings, Mock Manialinks, - (Mock Client, Mock Remote) Server, + (Mock Client, Mock Remote, Mock Chat) Server, Mock Scheduler, Mock AuditService, ControllerContextMock Context @@ -45,7 +45,7 @@ ControllerContextMock Context var actionContext = new Mock(); var context = - Mocking.NewManialinkInteractionContextMock(player.Object, actionContext.Object, manialinks.Object); + Mocking.NewManialinkInteractionContextMock(server.Client, player.Object, actionContext.Object, manialinks.Object); var contextService = Mocking.NewContextServiceMock(context.Context.Object, player.Object); var locale = Mocking.NewLocaleMock(contextService.Object); diff --git a/tests/Modules/Player.Tests/PlayerCommandsControllerTests.cs b/tests/Modules/Player.Tests/PlayerCommandsControllerTests.cs index 2ce41ff4e..fe95a8740 100644 --- a/tests/Modules/Player.Tests/PlayerCommandsControllerTests.cs +++ b/tests/Modules/Player.Tests/PlayerCommandsControllerTests.cs @@ -1,5 +1,6 @@ using EvoSC.Common.Interfaces; using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; using EvoSC.Modules.Official.Player.Controllers; using EvoSC.Modules.Official.Player.Events; using EvoSC.Modules.Official.Player.Interfaces; @@ -14,11 +15,9 @@ public class PlayerCommandsControllerTests : CommandInteractionControllerTestBas { private Mock _playerService = new(); private Mock _player = new(); - private (Mock Client, Mock Remote) _server = Mocking.NewServerClientMock(); - public PlayerCommandsControllerTests() { - InitMock(_player.Object, _playerService, _server.Client); + InitMock(_player.Object, _playerService); } [Fact] diff --git a/tests/Modules/Player.Tests/PlayerEventControllerTests.cs b/tests/Modules/Player.Tests/PlayerEventControllerTests.cs index 8491e14f4..2b08b8dc3 100644 --- a/tests/Modules/Player.Tests/PlayerEventControllerTests.cs +++ b/tests/Modules/Player.Tests/PlayerEventControllerTests.cs @@ -1,6 +1,7 @@ using EvoSC.Common.Events.Arguments; using EvoSC.Common.Interfaces.Controllers; using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; using EvoSC.Modules.Official.Player.Controllers; using EvoSC.Modules.Official.Player.Interfaces; using EvoSC.Testing; @@ -17,8 +18,8 @@ public class PlayerEventControllerTests : ControllerMock(); + InitMock(_playerService, locale, chatService); } [Fact] diff --git a/tests/Modules/Player.Tests/PlayerServiceTests.cs b/tests/Modules/Player.Tests/PlayerServiceTests.cs index c6f38943c..a7c6dd7d5 100644 --- a/tests/Modules/Player.Tests/PlayerServiceTests.cs +++ b/tests/Modules/Player.Tests/PlayerServiceTests.cs @@ -26,7 +26,7 @@ public class PlayerServiceTests private Mock _actor = new(); private ControllerContextMock _eventContext; private ControllerContextMock _commandContext; - private readonly (Mock Client, Mock Remote) _serverClient = + private readonly (Mock Client, Mock Remote, Mock Chat) _serverClient = Mocking.NewServerClientMock(); public PlayerServiceTests() @@ -48,7 +48,7 @@ public PlayerServiceTests() Locale Locale, Mock PlayerManager, Mock> Logger, - (Mock Client, Mock Remote) Server, + (Mock Client, Mock Remote, Mock Chat) Server, Mock Player, Mock Actor, Mock Audit, @@ -112,7 +112,7 @@ public async Task Player_Is_Greeted_When_Already_Exists() await mock.PlayerService.GreetPlayerAsync(mock.Player.Object); mock.PlayerManager.Verify(m => m.UpdateLastVisitAsync(mock.Player.Object), Times.Once); - mock.Server.Client.Verify(m => m.InfoMessageAsync(It.IsAny())); + mock.Server.Chat.Verify(m => m.InfoMessageAsync(It.IsAny())); } [Fact] @@ -125,7 +125,7 @@ public async Task Player_Is_Kicked_And_Audited() mock.Audit.Verify(m => m.Success(), Times.Once); mock.Server.Remote.Verify(m => m.KickAsync(PlayerLogin,""), Times.Once); - mock.Server.Client.Verify(m => m.SuccessMessageAsync(mock.Actor.Object, It.IsAny()), Times.Once); + mock.Server.Chat.Verify(m => m.SuccessMessageAsync(It.IsAny(), mock.Actor.Object), Times.Once); } [Fact] @@ -136,7 +136,7 @@ public async Task Player_Kicking_Failed_Sends_Error_Message() await mock.PlayerService.KickAsync(mock.Player.Object, mock.Actor.Object); mock.ContextService.Verify(m => m.Audit(), Times.Never); - mock.Server.Client.Verify(m => m.ErrorMessageAsync(mock.Actor.Object, It.IsAny()), Times.Once); + mock.Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny(), mock.Actor.Object), Times.Once); } [Fact] @@ -149,8 +149,8 @@ public async Task Player_Is_Muted_And_Audited() mock.Audit.Verify(m => m.Success(), Times.Once); mock.Server.Remote.Verify(m => m.IgnoreAsync(PlayerLogin), Times.Once); - mock.Server.Client.Verify(m => m.WarningMessageAsync(mock.Player.Object, It.IsAny()), Times.Once); - mock.Server.Client.Verify(m => m.SuccessMessageAsync(mock.Actor.Object, It.IsAny()), Times.Once); + mock.Server.Chat.Verify(m => m.WarningMessageAsync(It.IsAny(), mock.Player.Object), Times.Once); + mock.Server.Chat.Verify(m => m.SuccessMessageAsync(It.IsAny(), mock.Actor.Object), Times.Once); } [Fact] @@ -161,7 +161,7 @@ public async Task Player_Is_Muting_Failed_Sends_Error_Message() await mock.PlayerService.MuteAsync(mock.Player.Object, mock.Actor.Object); mock.ContextService.Verify(m => m.Audit(), Times.Never); - mock.Server.Client.Verify(m => m.ErrorMessageAsync(mock.Actor.Object, It.IsAny()), Times.Once); + mock.Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny(), mock.Actor.Object), Times.Once); } [Fact] @@ -174,8 +174,8 @@ public async Task Player_Is_UnMuted_And_Audited() mock.Audit.Verify(m => m.Success(), Times.Once); mock.Server.Remote.Verify(m => m.UnIgnoreAsync(PlayerLogin), Times.Once); - mock.Server.Client.Verify(m => m.InfoMessageAsync(mock.Player.Object, It.IsAny()), Times.Once); - mock.Server.Client.Verify(m => m.SuccessMessageAsync(mock.Actor.Object, It.IsAny()), Times.Once); + mock.Server.Chat.Verify(m => m.InfoMessageAsync(It.IsAny(), mock.Player.Object), Times.Once); + mock.Server.Chat.Verify(m => m.SuccessMessageAsync(It.IsAny(), mock.Actor.Object), Times.Once); } [Fact] @@ -186,7 +186,7 @@ public async Task Player_Is_UnMuting_Failed_Sends_Error_Message() await mock.PlayerService.MuteAsync(mock.Player.Object, mock.Actor.Object); mock.ContextService.Verify(m => m.Audit(), Times.Never); - mock.Server.Client.Verify(m => m.ErrorMessageAsync(mock.Actor.Object, It.IsAny()), Times.Once); + mock.Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny(), mock.Actor.Object), Times.Once); } [Fact] @@ -199,7 +199,7 @@ public async Task Player_Is_Banned_Blacklisted_And_Audited() mock.Server.Remote.Verify(m => m.BanAsync(PlayerLogin), Times.Once); mock.Server.Remote.Verify(m => m.BlackListAsync(PlayerLogin), Times.Once); mock.Audit.Verify(m => m.Success(), Times.Once); - mock.Server.Client.Verify(m => m.SuccessMessageAsync(mock.Actor.Object, It.IsAny()), Times.Once); + mock.Server.Chat.Verify(m => m.SuccessMessageAsync(It.IsAny(), mock.Actor.Object), Times.Once); } [Fact] @@ -215,7 +215,7 @@ public async Task Player_Banning_Failed_Still_Blacklists_And_Logs_Trace() mock.Logger.Verify(LogLevel.Trace, ex, null, Times.Once()); mock.Server.Remote.Verify(m => m.BlackListAsync(PlayerLogin), Times.Once); mock.Audit.Verify(m => m.Success(), Times.Once); - mock.Server.Client.Verify(m => m.SuccessMessageAsync(mock.Actor.Object, It.IsAny()), Times.Once); + mock.Server.Chat.Verify(m => m.SuccessMessageAsync(It.IsAny(), mock.Actor.Object), Times.Once); } [Fact] @@ -229,7 +229,7 @@ public async Task Player_Is_Unbanned_And_Audited() mock.Server.Remote.Verify(m => m.UnBanAsync(PlayerLogin), Times.Once); mock.Server.Remote.Verify(m => m.UnBlackListAsync(PlayerLogin), Times.Once); - mock.Server.Client.Verify(m => m.SuccessMessageAsync(_actor.Object, It.IsAny()), Times.Exactly(2)); + mock.Server.Chat.Verify(m => m.SuccessMessageAsync(It.IsAny(), mock.Actor.Object), Times.Exactly(2)); mock.Audit.Verify(m => m.Success(), Times.Exactly(2)); } @@ -245,7 +245,7 @@ public async Task Player_Unban_Failed_Is_Logged_And_Still_Unblacklisted() mock.Server.Remote.Verify(m => m.UnBanAsync(PlayerLogin), Times.Once); mock.Server.Remote.Verify(m => m.UnBlackListAsync(PlayerLogin), Times.Once); - mock.Server.Client.Verify(m => m.ErrorMessageAsync(_actor.Object, It.IsAny()), Times.Once); + mock.Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny(), mock.Actor.Object), Times.Once); mock.Audit.Verify(m => m.Success(), Times.Once); mock.Logger.Verify(LogLevel.Error, ex, null, Times.Once()); } @@ -262,7 +262,7 @@ public async Task Player_Unblacklist_Failed_Sends_ErrorMsg_And_ErrorLog() mock.Server.Remote.Verify(m => m.UnBanAsync(PlayerLogin), Times.Once); mock.Server.Remote.Verify(m => m.UnBlackListAsync(PlayerLogin), Times.Once); - mock.Server.Client.Verify(m => m.ErrorMessageAsync(_actor.Object, It.IsAny()), Times.Once); + mock.Server.Chat.Verify(m => m.ErrorMessageAsync(It.IsAny(), mock.Actor.Object), Times.Once); mock.Audit.Verify(m => m.Success(), Times.Once); mock.Logger.Verify(LogLevel.Error, ex, null, Times.Once()); } diff --git a/tests/Modules/ServerManagementModule.Tests/Services/ServerManagementServiceTests.cs b/tests/Modules/ServerManagementModule.Tests/Services/ServerManagementServiceTests.cs index 8640ad96b..4c1d4ff25 100644 --- a/tests/Modules/ServerManagementModule.Tests/Services/ServerManagementServiceTests.cs +++ b/tests/Modules/ServerManagementModule.Tests/Services/ServerManagementServiceTests.cs @@ -1,4 +1,5 @@ using EvoSC.Common.Interfaces; +using EvoSC.Common.Interfaces.Services; using EvoSC.Modules.Official.ServerManagementModule.Interfaces; using EvoSC.Modules.Official.ServerManagementModule.Services; using EvoSC.Testing; @@ -11,7 +12,7 @@ public class ServerManagementServiceTests { public ( IServerManagementService ServerManagementService, - (Mock Client, Mock Remote) Server, + (Mock Client, Mock Remote, Mock Chat) Server, Mock EventManager ) NewServiceMock() { diff --git a/tests/Modules/SetName.Tests/SetNameServiceTests.cs b/tests/Modules/SetName.Tests/SetNameServiceTests.cs index 44b1a9c9a..41e3cb144 100644 --- a/tests/Modules/SetName.Tests/SetNameServiceTests.cs +++ b/tests/Modules/SetName.Tests/SetNameServiceTests.cs @@ -20,15 +20,15 @@ public async Task Name_Is_Set_And_Updated_In_Caches() player.Setup(m => m.NickName).Returns("OldName"); var mlAction = new Mock(); var mlManager = new Mock(); - var context = Mocking.NewManialinkInteractionContextMock(player.Object, mlAction.Object, mlManager.Object); - var contextService = Mocking.NewContextServiceMock(context.Context.Object, null); var server = Mocking.NewServerClientMock(); + var context = Mocking.NewManialinkInteractionContextMock(server.Client, player.Object, mlAction.Object, mlManager.Object); + var contextService = Mocking.NewContextServiceMock(context.Context.Object, null); var playerRepository = new Mock(); var playerCache = new Mock(); var eventManager = new Mock(); var locale = Mocking.NewLocaleMock(contextService.Object); - var service = new SetNameService(server.Client.Object, playerRepository.Object, playerCache.Object, + var service = new SetNameService(server.Chat.Object, playerRepository.Object, playerCache.Object, eventManager.Object, locale); await service.SetNicknameAsync(player.Object, "NewName"); @@ -45,13 +45,13 @@ public async Task Name_Equals_Old_Name_Wont_Update() player.Setup(m => m.NickName).Returns("OldName"); var mlAction = new Mock(); var mlManager = new Mock(); - var context = Mocking.NewManialinkInteractionContextMock(player.Object, mlAction.Object, mlManager.Object); + var server = Mocking.NewServerClientMock(); + var context = Mocking.NewManialinkInteractionContextMock(server.Client, player.Object, mlAction.Object, mlManager.Object); var contextService = Mocking.NewContextServiceMock(context.Context.Object, null); var playerRepository = new Mock(); var locale = Mocking.NewLocaleMock(contextService.Object); - var server = Mocking.NewServerClientMock(); - var service = new SetNameService(server.Client.Object, playerRepository.Object, null, null, locale); + var service = new SetNameService(server.Chat.Object, playerRepository.Object, null, null, locale); await service.SetNicknameAsync(player.Object, "OldName"); diff --git a/tests/Modules/TeamChatModule.Tests/GlobalUsings.cs b/tests/Modules/TeamChatModule.Tests/GlobalUsings.cs new file mode 100644 index 000000000..c802f4480 --- /dev/null +++ b/tests/Modules/TeamChatModule.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using Xunit; diff --git a/tests/Modules/TeamChatModule.Tests/Middlewares/TeamChatMiddlewareTests.cs b/tests/Modules/TeamChatModule.Tests/Middlewares/TeamChatMiddlewareTests.cs new file mode 100644 index 000000000..c1a867563 --- /dev/null +++ b/tests/Modules/TeamChatModule.Tests/Middlewares/TeamChatMiddlewareTests.cs @@ -0,0 +1,109 @@ +using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Models.Enums; +using EvoSC.Common.Middleware; +using EvoSC.Common.Models.Players; +using EvoSC.Common.Permissions.Models; +using EvoSC.Common.Remote.ChatRouter; +using EvoSC.Modules.Official.TeamChatModule.Config; +using EvoSC.Modules.Official.TeamChatModule.Middlewares; +using Moq; + +namespace TeamChatModule.Tests.Middlewares; + +public class TeamChatMiddlewareTests +{ + (Mock Settings, TeamChatMiddleware Middleware, ChatRouterPipelineContext Context, Mock Actor) NewMock() + { + var action = new Mock(); + var settings = new Mock(); + var player = new Mock(); + var context = new ChatRouterPipelineContext + { + ForwardMessage = false, + Author = player.Object, + MessageText = "", + Recipients = [], + IsTeamMessage = false + }; + var middleware = new TeamChatMiddleware(action.Object, settings.Object); + + return (settings, middleware, context, player); + } + + [Theory] + [InlineData(PlayerTeam.Team1, 4)] + [InlineData(PlayerTeam.Team2, 3)] + public async Task Team_Chat_Enabled_Only_Includes_Team(PlayerTeam team, int count) + { + var mock = NewMock(); + + mock.Actor.Setup(m => m.Team).Returns(team); + mock.Context.IsTeamMessage = true; + mock.Context.Recipients = + [ + new OnlinePlayer { AccountId = "1", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "2", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "3", State = PlayerState.Playing, Team = PlayerTeam.Team2,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "4", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "5", State = PlayerState.Playing, Team = PlayerTeam.Team2,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "6", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "7", State = PlayerState.Playing, Team = PlayerTeam.Team2,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + ]; + + await mock.Middleware.ExecuteAsync(mock.Context); + + Assert.Equal(count, mock.Context.Recipients.Count); + } + + [Theory] + [InlineData(PlayerTeam.Team1, 5)] + [InlineData(PlayerTeam.Team2, 3)] + public async Task Players_In_Included_Group_Is_Always_Included(PlayerTeam team, int count) + { + var mock = NewMock(); + + mock.Settings.Setup(m => m.IncludeGroup).Returns(2); + mock.Actor.Setup(m => m.Team).Returns(team); + mock.Context.IsTeamMessage = true; + mock.Context.Recipients = + [ + new OnlinePlayer { AccountId = "1", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "2", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "3", State = PlayerState.Playing, Team = PlayerTeam.Team2,Groups = new []{new Group{Id = 2, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "4", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "5", State = PlayerState.Playing, Team = PlayerTeam.Team2,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "6", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "7", State = PlayerState.Playing, Team = PlayerTeam.Team2,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + ]; + + await mock.Middleware.ExecuteAsync(mock.Context); + + Assert.Equal(count, mock.Context.Recipients.Count); + } + + [Theory] + [InlineData(PlayerTeam.Team1, 3)] + [InlineData(PlayerTeam.Team2, 2)] + public async Task Players_In_Excluded_Group_Is_Never_Included(PlayerTeam team, int count) + { + var mock = NewMock(); + + mock.Settings.Setup(m => m.ExcludeGroup).Returns(2); + mock.Actor.Setup(m => m.Team).Returns(team); + mock.Context.IsTeamMessage = true; + mock.Context.Recipients = + [ + new OnlinePlayer { AccountId = "1", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "2", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 2, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "3", State = PlayerState.Playing, Team = PlayerTeam.Team2,Groups = new []{new Group{Id = 2, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "4", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "5", State = PlayerState.Playing, Team = PlayerTeam.Team2,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "6", State = PlayerState.Playing, Team = PlayerTeam.Team1,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + new OnlinePlayer { AccountId = "7", State = PlayerState.Playing, Team = PlayerTeam.Team2,Groups = new []{new Group{Id = 1, Title = "", Description = ""}}}, + ]; + + await mock.Middleware.ExecuteAsync(mock.Context); + + Assert.Equal(count, mock.Context.Recipients.Count); + } +} diff --git a/tests/Modules/TeamChatModule.Tests/TeamChatModule.Tests.csproj b/tests/Modules/TeamChatModule.Tests/TeamChatModule.Tests.csproj new file mode 100644 index 000000000..5532a94f2 --- /dev/null +++ b/tests/Modules/TeamChatModule.Tests/TeamChatModule.Tests.csproj @@ -0,0 +1,35 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + ..\..\..\..\..\..\.nuget\packages\moq\4.20.70\lib\net6.0\Moq.dll + + + + diff --git a/tests/Modules/TeamInfoModule.Tests/Services/TeamInfoServiceTests.cs b/tests/Modules/TeamInfoModule.Tests/Services/TeamInfoServiceTests.cs index 9511d4311..4974dc89f 100644 --- a/tests/Modules/TeamInfoModule.Tests/Services/TeamInfoServiceTests.cs +++ b/tests/Modules/TeamInfoModule.Tests/Services/TeamInfoServiceTests.cs @@ -1,4 +1,5 @@ using EvoSC.Common.Interfaces; +using EvoSC.Common.Interfaces.Services; using EvoSC.Common.Util.MatchSettings.Models.ModeScriptSettingsModels; using EvoSC.Manialinks.Interfaces; using EvoSC.Modules.Official.TeamInfoModule.Config; @@ -18,7 +19,7 @@ public class TeamInfoServiceTests private readonly Mock _manialinkManager = new(); private readonly Mock _settings = new(); - private readonly (Mock Client, Mock Remote) + private readonly (Mock Client, Mock Remote, Mock Chat) _server = Mocking.NewServerClientMock(); private ITeamInfoService TeamInfoServiceMock() diff --git a/tests/Modules/TeamSettingsModule.Tests/Services/TeamSettingsServiceTests.cs b/tests/Modules/TeamSettingsModule.Tests/Services/TeamSettingsServiceTests.cs index 7037a03e8..2fad17063 100644 --- a/tests/Modules/TeamSettingsModule.Tests/Services/TeamSettingsServiceTests.cs +++ b/tests/Modules/TeamSettingsModule.Tests/Services/TeamSettingsServiceTests.cs @@ -1,6 +1,7 @@ using System.Collections.Specialized; using EvoSC.Common.Interfaces; using EvoSC.Common.Interfaces.Models; +using EvoSC.Common.Interfaces.Services; using EvoSC.Manialinks.Interfaces; using EvoSC.Manialinks.Interfaces.Models; using EvoSC.Modules.Official.TeamSettingsModule.Events; @@ -22,14 +23,15 @@ public class TeamSettingsServiceTests private readonly Mock _manialinkManager = new(); private readonly Mock _events = new(); - private readonly (Mock Client, Mock Remote) + private readonly (Mock Client, Mock Remote, Mock Chat) _server = Mocking.NewServerClientMock(); private ITeamSettingsService TeamSettingsServiceMock() { var mlAction = new Mock(); + var server = Mocking.NewServerClientMock(); var context = - Mocking.NewManialinkInteractionContextMock(_player.Object, mlAction.Object, _manialinkManager.Object); + Mocking.NewManialinkInteractionContextMock(server.Client, _player.Object, mlAction.Object, _manialinkManager.Object); var contextService = Mocking.NewContextServiceMock(context.Context.Object, null); var locale = Mocking.NewLocaleMock(contextService.Object);