]> git.deb.at Git - rhonda/impostor.git/commitdiff
Implement new MurderPlayer logic
authorminiduikboot <mini@duikbo.at>
Mon, 22 Nov 2021 21:12:34 +0000 (22:12 +0100)
committerminiduikboot <mini@duikbo.at>
Mon, 22 Nov 2021 21:12:34 +0000 (22:12 +0100)
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.

src/Impostor.Api/Net/Messages/Rpcs/Rpc47CheckMurder.cs [new file with mode: 0644]
src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.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 (file)
index 0000000..229e369
--- /dev/null
@@ -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<IInnerPlayerControl>(game);
+        }
+    }
+}
index 7c2a9889f25376cdcda2ec3d8ed505c1dd5e8bf5..bbea32446d186ed881097c5353024de1831ea45a 100644 (file)
@@ -61,6 +61,9 @@ namespace Impostor.Server.Net.Inner.Objects
 
         internal Queue<ColorType> RequestedColorId { get; } = new Queue<ColorType>();
 
+        /// <summary> Gets or sets target that was set by the last CheckMurder RPC. </summary>
+        internal IInnerPlayerControl? IsMurdering { get; set; } = null;
+
         public override ValueTask<bool> 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<bool> HandleMurderPlayer(ClientPlayer sender, IInnerPlayerControl? target)
+        private async ValueTask<bool> 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<bool> 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;
         }