From 104237b439bcb9afab78b574e6c55d6b47502e25 Mon Sep 17 00:00:00 2001 From: js6pak Date: Sun, 12 Nov 2023 21:33:54 +0100 Subject: [PATCH] Fix host authoritive murder --- .../Extensions/MurderResultFlagsExtensions.cs | 11 +++ .../Inner/Objects/InnerPlayerControl.Api.cs | 2 +- .../Net/Inner/Objects/InnerPlayerControl.cs | 86 ++++++++++--------- 3 files changed, 56 insertions(+), 43 deletions(-) create mode 100644 src/Impostor.Api/Extensions/MurderResultFlagsExtensions.cs diff --git a/src/Impostor.Api/Extensions/MurderResultFlagsExtensions.cs b/src/Impostor.Api/Extensions/MurderResultFlagsExtensions.cs new file mode 100644 index 0000000..7565915 --- /dev/null +++ b/src/Impostor.Api/Extensions/MurderResultFlagsExtensions.cs @@ -0,0 +1,11 @@ +using Impostor.Api.Innersloth; + +namespace Impostor.Api; + +public static class MurderResultFlagsExtensions +{ + public static bool IsFailed(this MurderResultFlags value) + { + return (value & (MurderResultFlags.FailedError | MurderResultFlags.FailedProtected)) != 0; + } +} diff --git a/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.Api.cs b/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.Api.cs index 0718400..3a785bf 100644 --- a/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.Api.cs +++ b/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.Api.cs @@ -99,7 +99,7 @@ namespace Impostor.Server.Net.Inner.Objects throw new ImpostorProtocolException("Tried to murder a player, but target was not alive."); } - if ((result & (MurderResultFlags.FailedError | MurderResultFlags.FailedProtected)) == 0) + if (!result.IsFailed()) { ((InnerPlayerControl)target).Die(DeathReason.Kill); } diff --git a/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs b/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs index 01da1f6..21344cf 100644 --- a/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs +++ b/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs @@ -71,6 +71,22 @@ namespace Impostor.Server.Net.Inner.Objects internal IInnerPlayerControl? ProtectedBy { get; set; } + internal bool IsProtected + { + get + { + // HnS doesn't have guardian angels + if (Game.Options is NormalGameOptions normalGameOptions && ProtectedOn != null) + { + var guardianAngelOptions = (GuardianAngelRoleOptions)normalGameOptions.RoleOptions.Roles[RoleTypes.GuardianAngel].RoleOptions; + var protectionExpiresAt = ProtectedOn.Value.AddSeconds(guardianAngelOptions.ProtectionDurationSeconds); + return protectionExpiresAt >= _dateTimeProvider.UtcNow; + } + + return false; + } + } + public override ValueTask SerializeAsync(IMessageWriter writer, bool initialState) { throw new NotImplementedException(); @@ -260,7 +276,7 @@ namespace Impostor.Server.Net.Inner.Objects } Rpc12MurderPlayer.Deserialize(reader, Game, out var murdered, out var result); - return await HandleMurderPlayer(sender, murdered, result); + return await HandleMurderPlayer(sender, (InnerPlayerControl?)murdered, result); } case RpcCalls.SendChat: @@ -377,8 +393,8 @@ namespace Impostor.Server.Net.Inner.Objects return false; } - Rpc45ProtectPlayer.Deserialize(reader, Game, out _, out _); - break; + Rpc45ProtectPlayer.Deserialize(reader, Game, out var protectTarget, out _); + return await HandleProtectPlayer(sender, protectTarget); } case RpcCalls.Shapeshift: @@ -401,7 +417,7 @@ namespace Impostor.Server.Net.Inner.Objects } Rpc47CheckMurder.Deserialize(reader, Game, out var murdered); - return await HandleCheckMurder(sender, murdered); + return await HandleCheckMurder(sender, (InnerPlayerControl?)murdered); } case RpcCalls.CheckProtect: @@ -411,8 +427,8 @@ namespace Impostor.Server.Net.Inner.Objects return false; } - Rpc48CheckProtect.Deserialize(reader, Game, out var protectTarget); - return await HandleCheckProtect(sender, protectTarget); + Rpc48CheckProtect.Deserialize(reader, Game, out _); + break; } default: @@ -435,23 +451,6 @@ namespace Impostor.Server.Net.Inner.Objects ProtectedBy = guardianAngel; } - internal bool IsProtected() - { - // HnS doesn't have guardian angels - if (Game.Options.GameMode == GameModes.Normal && ProtectedOn != null) - { - var opts = (NormalGameOptions)Game.Options; - var guardianAngelOpts = (GuardianAngelRoleOptions)opts.RoleOptions.Roles[RoleTypes.GuardianAngel].RoleOptions; - var duration = guardianAngelOpts.ProtectionDurationSeconds; - var protectionExpiresAt = ProtectedOn.Value.AddSeconds(duration); - return protectionExpiresAt >= _dateTimeProvider.UtcNow; - } - else - { - return false; - } - } - private async ValueTask HandleCompleteTask(ClientPlayer sender, uint taskId) { var task = PlayerInfo.Tasks.ElementAtOrDefault((int)taskId); @@ -674,7 +673,7 @@ namespace Impostor.Server.Net.Inner.Objects return true; } - private async ValueTask HandleCheckMurder(ClientPlayer sender, IInnerPlayerControl? target) + private async ValueTask HandleCheckMurder(ClientPlayer sender, InnerPlayerControl? target) { if (!PlayerInfo.CanMurder(Game, _dateTimeProvider)) { @@ -704,15 +703,14 @@ namespace Impostor.Server.Net.Inner.Objects if (target != null) { - var tgt = (InnerPlayerControl)target; - var result = tgt.IsProtected() ? MurderResultFlags.FailedProtected : MurderResultFlags.Succeeded; + var result = target.IsProtected ? MurderResultFlags.FailedProtected : MurderResultFlags.Succeeded; var evt = new PlayerCheckMurderEvent(Game, sender, this, target, result); await _eventManager.CallAsync(evt); if (!evt.IsCancelled) { - tgt.ProtectedOn = null; // Clear GA protection in all cases + target.ProtectedOn = null; // Clear GA protection in all cases await MurderPlayerAsync(target, evt.Result); } } @@ -720,7 +718,7 @@ namespace Impostor.Server.Net.Inner.Objects return false; } - private async ValueTask HandleMurderPlayer(ClientPlayer sender, IInnerPlayerControl? target, MurderResultFlags result) + private async ValueTask HandleMurderPlayer(ClientPlayer sender, InnerPlayerControl? target, MurderResultFlags result) { if (!_game.IsHostAuthoritive) { @@ -749,9 +747,19 @@ namespace Impostor.Server.Net.Inner.Objects if (target != null && !target.PlayerInfo.IsDead) { - if ((result & (MurderResultFlags.FailedError | MurderResultFlags.FailedProtected)) == 0) + // In host authoritive mode every client has to figure out if the kill was prevented by guardian protection on it's own + if ((result & MurderResultFlags.Succeeded) != 0 && target.IsProtected) { - ((InnerPlayerControl)target).Die(DeathReason.Kill); + result = (result & ~MurderResultFlags.Succeeded) | MurderResultFlags.FailedProtected; + } + + if (!result.IsFailed()) + { + target.Die(DeathReason.Kill); + } + else if ((result & MurderResultFlags.FailedProtected) != 0) + { + target.ProtectedOn = null; } await _eventManager.CallAsync(new PlayerMurderEvent(Game, sender, this, target, result)); @@ -762,7 +770,7 @@ namespace Impostor.Server.Net.Inner.Objects return true; } - private async ValueTask HandleCheckProtect(ClientPlayer sender, IInnerPlayerControl? target) + private async ValueTask HandleProtectPlayer(ClientPlayer sender, IInnerPlayerControl? target) { if (target == null) { @@ -774,23 +782,17 @@ namespace Impostor.Server.Net.Inner.Objects return true; } - if (PlayerInfo.RoleType == RoleTypes.GuardianAngel) + if (PlayerInfo.RoleType != RoleTypes.GuardianAngel) { - if (await sender.Client.ReportCheatAsync(RpcCalls.CheckProtect, "Sender tried to protect target it couldn't protect")) + if (await sender.Client.ReportCheatAsync(RpcCalls.CheckProtect, "Client tried to protect but it wasn't a guardian angel")) { return false; } } - if (_game.IsHostAuthoritive) - { - return true; - } - else - { - await ProtectPlayerAsync(target); - return false; - } + ((InnerPlayerControl)target).Protect(this); + + return true; } private async ValueTask HandleSendChat(ClientPlayer sender, string message) -- 2.39.5