From aeaffc849ac8669c77e2ac9cf2eca1555891c937 Mon Sep 17 00:00:00 2001 From: miniduikboot Date: Mon, 22 Nov 2021 22:12:34 +0100 Subject: [PATCH] Implement new MurderPlayer logic In 2021.11.9, the host now uses MurderPlayer instead of impostors. Impostor now use CheckMurder to request the host to kill a player. This allows the host to prevent the target of the Guardian Angel from dying. However, if the impostor that is checking the kill is also the host, AU internally processes the CheckMurder RPC and sends the MurderPlayer RPC first, *then* it sends the CheckMurder RPC. --- .../Net/Messages/Rpcs/Rpc47CheckMurder.cs | 18 +++++++ .../Net/Inner/Objects/InnerPlayerControl.cs | 48 +++++++++++++++++-- 2 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 src/Impostor.Api/Net/Messages/Rpcs/Rpc47CheckMurder.cs diff --git a/src/Impostor.Api/Net/Messages/Rpcs/Rpc47CheckMurder.cs b/src/Impostor.Api/Net/Messages/Rpcs/Rpc47CheckMurder.cs new file mode 100644 index 0000000..229e369 --- /dev/null +++ b/src/Impostor.Api/Net/Messages/Rpcs/Rpc47CheckMurder.cs @@ -0,0 +1,18 @@ +using Impostor.Api.Games; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Api.Net.Messages.Rpcs +{ + public static class Rpc47CheckMurder + { + public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl) + { + writer.Write(playerControl.NetId); + } + + public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl) + { + playerControl = reader.ReadNetObject(game); + } + } +} diff --git a/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs b/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs index 7c2a988..bbea324 100644 --- a/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs +++ b/src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs @@ -61,6 +61,9 @@ namespace Impostor.Server.Net.Inner.Objects internal Queue RequestedColorId { get; } = new Queue(); + /// Gets or sets target that was set by the last CheckMurder RPC. + internal IInnerPlayerControl? IsMurdering { get; set; } = null; + public override ValueTask SerializeAsync(IMessageWriter writer, bool initialState) { throw new NotImplementedException(); @@ -243,7 +246,7 @@ namespace Impostor.Server.Net.Inner.Objects case RpcCalls.MurderPlayer: { - if (!await ValidateOwnership(call, sender) || !await ValidateImpostor(call, sender, PlayerInfo)) + if (!await ValidateHost(call, sender)) { return false; } @@ -367,6 +370,17 @@ namespace Impostor.Server.Net.Inner.Objects break; } + case RpcCalls.CheckMurder: + { + if (!await ValidateImpostor(call, sender, PlayerInfo)) + { + return false; + } + + Rpc47CheckMurder.Deserialize(reader, Game, out var murdered); + return await HandleCheckMurder(sender, murdered); + } + default: return await base.HandleRpcAsync(sender, target, call, reader); } @@ -598,16 +612,33 @@ namespace Impostor.Server.Net.Inner.Objects // return true; // } - private async ValueTask HandleMurderPlayer(ClientPlayer sender, IInnerPlayerControl? target) + private async ValueTask HandleCheckMurder(ClientPlayer sender, IInnerPlayerControl? target) { if (!PlayerInfo.CanMurder(Game, _dateTimeProvider)) { - if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Client tried to murder too fast")) + if (await sender.Client.ReportCheatAsync(RpcCalls.CheckMurder, "Client tried to murder too fast")) + { + return false; + } + } + + PlayerInfo.LastMurder = _dateTimeProvider.UtcNow - TimeSpan.FromMilliseconds(sender.Client.Connection.AveragePing); + + if (target == null || target.PlayerInfo.IsImpostor) + { + if (await sender.Client.ReportCheatAsync(RpcCalls.CheckMurder, "Client tried to murder invalid target")) { return false; } } + IsMurdering = target; + + return true; + } + + private async ValueTask HandleMurderPlayer(ClientPlayer sender, IInnerPlayerControl? target) + { if (target == null || target.PlayerInfo.IsImpostor) { if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Client tried to murder invalid target")) @@ -616,7 +647,14 @@ namespace Impostor.Server.Net.Inner.Objects } } - PlayerInfo.LastMurder = _dateTimeProvider.UtcNow - TimeSpan.FromMilliseconds(sender.Client.Connection.AveragePing); + // If the host is also the impostor that committed the murder, CheckMurder is actually sent *after* the MurderPlayer RPC + if (sender.Character != this && target != IsMurdering) + { + if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Host tried to murder incorrect target")) + { + return false; + } + } if (target != null && !target.PlayerInfo.IsDead) { @@ -624,6 +662,8 @@ namespace Impostor.Server.Net.Inner.Objects await _eventManager.CallAsync(new PlayerMurderEvent(Game, sender, this, target)); } + IsMurdering = null; + return true; } -- 2.39.5