From cf51b83cdd5999f68273f75ba558392375814af7 Mon Sep 17 00:00:00 2001 From: Amos Date: Fri, 14 Oct 2022 14:47:01 +0100 Subject: [PATCH] Fix Threading Duplicate for Mute Penalty & Added !MuteInfo & Fix PM (#269) * Resolve duplicate migration Resolve unmuting state double penalties * Change order of operation * Added MuteInfoCommand.cs * Resolve !pm and @broadcast permanently being disabled --- Plugins/Mute/Commands/MuteInfoCommand.cs | 50 ++++++++++++++++ Plugins/Mute/MuteManager.cs | 74 ++++++++++++++---------- Plugins/Mute/Plugin.cs | 12 +++- 3 files changed, 102 insertions(+), 34 deletions(-) create mode 100644 Plugins/Mute/Commands/MuteInfoCommand.cs diff --git a/Plugins/Mute/Commands/MuteInfoCommand.cs b/Plugins/Mute/Commands/MuteInfoCommand.cs new file mode 100644 index 000000000..574f0e9f5 --- /dev/null +++ b/Plugins/Mute/Commands/MuteInfoCommand.cs @@ -0,0 +1,50 @@ +using Data.Models.Client; +using Humanizer; +using SharedLibraryCore; +using SharedLibraryCore.Commands; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace Mute.Commands; + +public class MuteInfoCommand : Command +{ + public MuteInfoCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "muteinfo"; + Description = translationLookup["PLUGINS_MUTE_COMMANDS_MUTEINFO_DESC"]; + Alias = "mi"; + Permission = EFClient.Permission.Moderator; + RequiresTarget = true; + SupportedGames = Plugin.SupportedGames; + Arguments = new[] + { + new CommandArgument + { + Name = translationLookup["COMMANDS_ARGS_PLAYER"], + Required = true + } + }; + } + + public override async Task ExecuteAsync(GameEvent gameEvent) + { + var currentMuteMeta = await Plugin.MuteManager.GetCurrentMuteState(gameEvent.Target); + switch (currentMuteMeta.MuteState) + { + case MuteState.Muted when currentMuteMeta.Expiration is null: + gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_MUTEINFO_SUCCESS"] + .FormatExt(gameEvent.Target.Name, currentMuteMeta.Reason)); + return; + case MuteState.Muted when currentMuteMeta.Expiration.HasValue && currentMuteMeta.Expiration.Value > DateTime.UtcNow: + var remainingTime = (currentMuteMeta.Expiration.Value - DateTime.UtcNow).HumanizeForCurrentCulture(); + gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_MUTEINFO_TM_SUCCESS"] + .FormatExt(gameEvent.Target.Name, currentMuteMeta.Reason, remainingTime)); + return; + default: + gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_MUTEINFO_NONE"]); + break; + } + } +} diff --git a/Plugins/Mute/MuteManager.cs b/Plugins/Mute/MuteManager.cs index 3491c1be4..b3ce355a2 100644 --- a/Plugins/Mute/MuteManager.cs +++ b/Plugins/Mute/MuteManager.cs @@ -13,6 +13,7 @@ public class MuteManager private readonly IMetaServiceV2 _metaService; private readonly ITranslationLookup _translationLookup; private readonly ILogger _logger; + private readonly SemaphoreSlim _onMuteAction = new(1, 1); public MuteManager(IMetaServiceV2 metaService, ITranslationLookup translationLookup, ILogger logger) { @@ -26,38 +27,46 @@ public class MuteManager public async Task GetCurrentMuteState(EFClient client) { - var clientMuteMeta = await ReadPersistentDataV2(client); - if (clientMuteMeta is not null) return clientMuteMeta; - - // Return null if the client doesn't have old or new meta. - var muteState = await ReadPersistentDataV1(client); - clientMuteMeta = new MuteStateMeta + try { - Reason = muteState is null ? string.Empty : _translationLookup["PLUGINS_MUTE_MIGRATED"], - Expiration = muteState switch + await _onMuteAction.WaitAsync(); + var clientMuteMeta = await ReadPersistentDataV2(client); + if (clientMuteMeta is not null) return clientMuteMeta; + + // Return null if the client doesn't have old or new meta. + var muteState = await ReadPersistentDataV1(client); + clientMuteMeta = new MuteStateMeta { - null => DateTime.UtcNow, - MuteState.Muted => null, - _ => DateTime.UtcNow - }, - MuteState = muteState ?? MuteState.Unmuted, - CommandExecuted = true - }; + Reason = muteState is null ? string.Empty : _translationLookup["PLUGINS_MUTE_MIGRATED"], + Expiration = muteState switch + { + null => DateTime.UtcNow, + MuteState.Muted => null, + _ => DateTime.UtcNow + }, + MuteState = muteState ?? MuteState.Unmuted, + CommandExecuted = true + }; - // Migrate old mute meta, else, client has no state, so set a generic one, but don't write it to database. - if (muteState is not null) - { - clientMuteMeta.CommandExecuted = false; - await WritePersistentData(client, clientMuteMeta); - await CreatePenalty(muteState.Value, Utilities.IW4MAdminClient(), client, clientMuteMeta.Expiration, - clientMuteMeta.Reason); - } - else - { - client.SetAdditionalProperty(Plugin.MuteKey, clientMuteMeta); - } + // Migrate old mute meta, else, client has no state, so set a generic one, but don't write it to database. + if (muteState is not null) + { + clientMuteMeta.CommandExecuted = false; + await WritePersistentData(client, clientMuteMeta); + await CreatePenalty(muteState.Value, Utilities.IW4MAdminClient(), client, clientMuteMeta.Expiration, + clientMuteMeta.Reason); + } + else + { + client.SetAdditionalProperty(Plugin.MuteKey, clientMuteMeta); + } - return clientMuteMeta; + return clientMuteMeta; + } + finally + { + if (_onMuteAction.CurrentCount == 0) _onMuteAction.Release(); + } } public async Task Mute(Server server, EFClient origin, EFClient target, DateTime? dateTime, string reason) @@ -65,8 +74,6 @@ public class MuteManager var clientMuteMeta = await GetCurrentMuteState(target); if (clientMuteMeta.MuteState is MuteState.Muted && clientMuteMeta.CommandExecuted) return false; - await CreatePenalty(MuteState.Muted, origin, target, dateTime, reason); - clientMuteMeta = new MuteStateMeta { Expiration = dateTime, @@ -76,6 +83,8 @@ public class MuteManager }; await WritePersistentData(target, clientMuteMeta); + await CreatePenalty(MuteState.Muted, origin, target, dateTime, reason); + // Handle game command var client = server.GetClientsAsList().FirstOrDefault(client => client.NetworkId == target.NetworkId); await PerformGameCommand(server, client, clientMuteMeta); @@ -89,7 +98,10 @@ public class MuteManager if (clientMuteMeta.MuteState is MuteState.Unmuted && clientMuteMeta.CommandExecuted) return false; if (!target.IsIngame && clientMuteMeta.MuteState is MuteState.Unmuting) return false; - await CreatePenalty(MuteState.Unmuted, origin, target, DateTime.UtcNow, reason); + if (clientMuteMeta.MuteState is not MuteState.Unmuting) + { + await CreatePenalty(MuteState.Unmuted, origin, target, DateTime.UtcNow, reason); + } clientMuteMeta = new MuteStateMeta { diff --git a/Plugins/Mute/Plugin.cs b/Plugins/Mute/Plugin.cs index f5bd47dfc..04eff9a9e 100644 --- a/Plugins/Mute/Plugin.cs +++ b/Plugins/Mute/Plugin.cs @@ -38,9 +38,6 @@ public class Plugin : IPlugin switch (gameEvent.Type) { - case GameEvent.EventType.Command: - - break; case GameEvent.EventType.Join: // Check if user has any meta set, else ignore (unmuted) var muteMetaJoin = await MuteManager.GetCurrentMuteState(gameEvent.Origin); @@ -122,7 +119,16 @@ public class Plugin : IPlugin manager.CommandInterceptors.Add(gameEvent => { if (gameEvent.Extra is not Command command) + { return true; + } + + var muteMeta = MuteManager.GetCurrentMuteState(gameEvent.Origin).GetAwaiter().GetResult(); + if (muteMeta.MuteState is not MuteState.Muted) + { + return true; + } + return !DisabledCommands.Contains(command.GetType().Name) && !command.IsBroadcast; });