]> git.deb.at Git - rhonda/impostor.git/commitdiff
Finish up PlayerControl RPCs
authorAeonLucid <aeonlucid@outlook.com>
Fri, 23 Oct 2020 20:27:57 +0000 (22:27 +0200)
committerAeonLucid <aeonlucid@outlook.com>
Fri, 23 Oct 2020 20:27:57 +0000 (22:27 +0200)
15 files changed:
src/Impostor.Api/Exceptions/ImpostorCheatException.cs [new file with mode: 0644]
src/Impostor.Api/Extensions/MessageReaderExtensions.cs [new file with mode: 0644]
src/Impostor.Api/Impostor.Api.csproj
src/Impostor.Api/Innersloth/Data/ChatNoteType.cs [new file with mode: 0644]
src/Impostor.Api/Innersloth/Net/InnerNetObject.cs
src/Impostor.Api/Innersloth/Net/Objects/Components/InnerCustomNetworkTransform.cs
src/Impostor.Api/Innersloth/Net/Objects/Components/InnerPlayerPhysics.cs
src/Impostor.Api/Innersloth/Net/Objects/Components/InnerVoteBanSystem.cs
src/Impostor.Api/Innersloth/Net/Objects/InnerGameData.cs
src/Impostor.Api/Innersloth/Net/Objects/InnerLobbyBehaviour.cs
src/Impostor.Api/Innersloth/Net/Objects/InnerMeetingHud.cs
src/Impostor.Api/Innersloth/Net/Objects/InnerPlayerControl.cs
src/Impostor.Api/Innersloth/Net/Objects/InnerShipStatus.cs
src/Impostor.Api/Net/IClientPlayer.cs
src/Impostor.Server/Net/State/ClientPlayer.cs

diff --git a/src/Impostor.Api/Exceptions/ImpostorCheatException.cs b/src/Impostor.Api/Exceptions/ImpostorCheatException.cs
new file mode 100644 (file)
index 0000000..8eb72f8
--- /dev/null
@@ -0,0 +1,24 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace Impostor.Api
+{
+    public class ImpostorCheatException : ImpostorException
+    {
+        public ImpostorCheatException()
+        {
+        }
+
+        protected ImpostorCheatException(SerializationInfo info, StreamingContext context) : base(info, context)
+        {
+        }
+
+        public ImpostorCheatException(string? message) : base(message)
+        {
+        }
+
+        public ImpostorCheatException(string? message, Exception? innerException) : base(message, innerException)
+        {
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Impostor.Api/Extensions/MessageReaderExtensions.cs b/src/Impostor.Api/Extensions/MessageReaderExtensions.cs
new file mode 100644 (file)
index 0000000..94e7d99
--- /dev/null
@@ -0,0 +1,15 @@
+using Impostor.Api.Games;
+using Impostor.Api.Innersloth.Net;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api
+{
+    public static class MessageReaderExtensions
+    {
+        public static T ReadNetObject<T>(this IMessageReader reader, IGame game)
+            where T : InnerNetObject
+        {
+            return game.FindObjectByNetId<T>(reader.ReadPackedUInt32());
+        }
+    }
+}
\ No newline at end of file
index 973c5acd85eff3cf30f742c67dd2f0134defdf63..4d3a0fb87f79cd5beaa92427ee28789ee420e795 100644 (file)
@@ -6,6 +6,7 @@
         <CodeAnalysisRuleSet>ProjectRules.ruleset</CodeAnalysisRuleSet>
         <LangVersion>9</LangVersion>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+        <Nullable>enable</Nullable>
     </PropertyGroup>
 
     <ItemGroup>
diff --git a/src/Impostor.Api/Innersloth/Data/ChatNoteType.cs b/src/Impostor.Api/Innersloth/Data/ChatNoteType.cs
new file mode 100644 (file)
index 0000000..04c3278
--- /dev/null
@@ -0,0 +1,7 @@
+namespace Impostor.Api.Innersloth.Data
+{
+    public enum ChatNoteType : byte
+    {
+        DidVote = 0,
+    }
+}
\ No newline at end of file
index 7981051a25b1445d5e9ba78ea4880e31dd9d3ca5..dc3458790d41b37f2821a836d83fd18b401cee71 100644 (file)
@@ -11,7 +11,8 @@ namespace Impostor.Api.Innersloth.Net
 
         public SpawnFlags SpawnFlags { get; internal set; }
 
-        public abstract void HandleRpc(IClientPlayer sender, IClientPlayer target, RpcCalls call, IMessageReader reader);
+        public abstract void HandleRpc(IClientPlayer sender, IClientPlayer? target, RpcCalls call,
+            IMessageReader reader);
 
         public abstract bool Serialize(IMessageWriter writer, bool initialState);
 
index 4567ae7a398467e7c2c06b49653ec22b6cfc0c9f..8270e32c1cbebbd0f88f892c697d3a551699849c 100644 (file)
@@ -44,9 +44,9 @@ namespace Impostor.Api.Innersloth.Net.Objects.Components
             return new Vector2(XRange.Lerp(v1), YRange.Lerp(v2));
         }
 
-        public override void HandleRpc(IClientPlayer sender, IClientPlayer target, RpcCalls call, IMessageReader reader)
+        public override void HandleRpc(IClientPlayer sender, IClientPlayer? target, RpcCalls call, IMessageReader reader)
         {
-            if (call == 0)
+            if (call == RpcCalls.SnapTo)
             {
                 SnapTo(ReadVector2(reader), reader.ReadUInt16());
             }
index 6ecfe63c1d81981945cc88258aba7b6bea407e49..71281389a84ede8c2204d76e6a8fc7266f4f1bee 100644 (file)
@@ -5,7 +5,8 @@ namespace Impostor.Api.Innersloth.Net.Objects.Components
 {
     public class InnerPlayerPhysics : InnerNetObject
     {
-        public override void HandleRpc(IClientPlayer sender, IClientPlayer target, RpcCalls call, IMessageReader reader)
+        public override void HandleRpc(IClientPlayer sender, IClientPlayer? target, RpcCalls call,
+            IMessageReader reader)
         {
             throw new System.NotImplementedException();
         }
index 7930853ed5627f0d7ef3e161c18c7a79c4d9d869..a9590df8c9d47c5b91ffff7c57ac510eed12bd30 100644 (file)
@@ -14,7 +14,8 @@ namespace Impostor.Api.Innersloth.Net.Objects.Components
             _votes = new Dictionary<int, int[]>();
         }
 
-        public override void HandleRpc(IClientPlayer sender, IClientPlayer target, RpcCalls call, IMessageReader reader)
+        public override void HandleRpc(IClientPlayer sender, IClientPlayer? target, RpcCalls call,
+            IMessageReader reader)
         {
             throw new NotImplementedException();
         }
index a4e05ef3c11f623ac895ee69b26878abe0625dbd..e7fafabc18249b7aede38d47bdab7b090595196b 100644 (file)
@@ -37,12 +37,17 @@ namespace Impostor.Api.Innersloth.Net.Objects
             }
         }
 
-        public PlayerInfo GetPlayerById(byte id)
+        public PlayerInfo? GetPlayerById(byte id)
         {
+            if (id == byte.MaxValue)
+            {
+                return null;
+            }
+
             return _allPlayers.TryGetValue(id, out var player) ? player : null;
         }
 
-        public override void HandleRpc(IClientPlayer sender, IClientPlayer target, RpcCalls call, IMessageReader reader)
+        public override void HandleRpc(IClientPlayer sender, IClientPlayer? target, RpcCalls call, IMessageReader reader)
         {
             throw new NotImplementedException();
         }
index 7a3f8a9d285f6298028dd0c7dbf7fb1acdea35bf..e0661f89bb9213ac04afb21158156c0c945c1bd3 100644 (file)
@@ -15,7 +15,8 @@ namespace Impostor.Api.Innersloth.Net.Objects
             Components.Add(this);
         }
 
-        public override void HandleRpc(IClientPlayer sender, IClientPlayer target, RpcCalls call, IMessageReader reader)
+        public override void HandleRpc(IClientPlayer sender, IClientPlayer? target, RpcCalls call,
+            IMessageReader reader)
         {
             throw new System.NotImplementedException();
         }
index 8496c65a8f8ef4296aa471ccdee6db692f9a673a..19e636e6f98bfbbc1ff618267728776325516f3a 100644 (file)
@@ -36,7 +36,8 @@ namespace Impostor.Api.Innersloth.Net.Objects
                 .ToArray();
         }
 
-        public override void HandleRpc(IClientPlayer sender, IClientPlayer target, RpcCalls call, IMessageReader reader)
+        public override void HandleRpc(IClientPlayer sender, IClientPlayer? target, RpcCalls call,
+            IMessageReader reader)
         {
             throw new NotImplementedException();
         }
index a6c44fbb66d530c1cb7f15078e8ba6e3456a72a0..998fc4f1c1b400c9bccfd262688ef2c7339647af 100644 (file)
@@ -32,38 +32,49 @@ namespace Impostor.Api.Innersloth.Net.Objects
 
         public InnerGameData.PlayerInfo PlayerInfo { get; internal set; }
 
-        private void Die(DeathReason reason)
-        {
-            PlayerInfo.IsDead = true;
-            PlayerInfo.LastDeathReason = reason;
-        }
-
-        public override void HandleRpc(IClientPlayer sender, IClientPlayer target, RpcCalls call, IMessageReader reader)
+        public override void HandleRpc(IClientPlayer sender, IClientPlayer? target, RpcCalls call, IMessageReader reader)
         {
             switch (call)
             {
                 case RpcCalls.PlayAnimation:
                 {
+                    // TODO: Figure out checks
                     var animation = reader.ReadByte();
                     break;
                 }
 
+                // Complete a task.
                 case RpcCalls.CompleteTask:
                 {
+                    if (!sender.IsOwner(this))
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CompleteTask)} to an unowned {nameof(InnerPlayerControl)}.");
+                    }
+
                     var index = reader.ReadPackedUInt32();
                     break;
                 }
 
+                // Update GameOptions.
                 case RpcCalls.SyncSettings:
                 {
+                    if (!sender.IsHost)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SyncSettings)} but was not a host.");
+                    }
+
                     _game.Options.Deserialize(reader.ReadBytesAndSize());
-                    Console.WriteLine(_game.Options.PlayerSpeedMod);
                     break;
                 }
 
                 // Set Impostors.
                 case RpcCalls.SetInfected:
                 {
+                    if (!sender.IsHost)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetInfected)} but was not a host.");
+                    }
+
                     var length = reader.ReadPackedInt32();
 
                     for (var i = 0; i < length; i++)
@@ -80,27 +91,260 @@ namespace Impostor.Api.Innersloth.Net.Objects
                 // Player was voted out.
                 case RpcCalls.Exiled:
                 {
+                    if (!sender.IsHost)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetInfected)} but was not a host.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetInfected)} to a specific player instead of broadcast.");
+                    }
+
                     Console.WriteLine(PlayerInfo.PlayerName + " was voted out.");
+
+                    Die(DeathReason.Exile);
                     break;
                 }
 
                 // Validates the player name at the host.
                 case RpcCalls.CheckName:
                 {
-                    if (!target.IsHost)
+                    if (target == null || !target.IsHost)
                     {
-
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CheckName)} to the wrong player.");
                     }
+
                     var name = reader.ReadString();
                     break;
                 }
 
+                // Update the name of a player.
                 case RpcCalls.SetName:
                 {
+                    if (!sender.IsHost)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetName)} but was not a host.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetName)} to a specific player instead of broadcast.");
+                    }
+
                     PlayerInfo.PlayerName = reader.ReadString();
                     break;
                 }
 
+                // Validates the color at the host.
+                case RpcCalls.CheckColor:
+                {
+                    if (target == null || !target.IsHost)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CheckColor)} to the wrong player.");
+                    }
+
+                    var color = reader.ReadByte();
+                    break;
+                }
+
+                // Update the color of a player.
+                case RpcCalls.SetColor:
+                {
+                    if (!sender.IsHost)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetColor)} but was not a host.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetColor)} to a specific player instead of broadcast.");
+                    }
+
+                    PlayerInfo.ColorId = reader.ReadByte();
+                    break;
+                }
+
+                // Update the hat of a player.
+                case RpcCalls.SetHat:
+                {
+                    if (!sender.IsOwner(this))
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetHat)} to an unowned {nameof(InnerPlayerControl)}.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetHat)} to a specific player instead of broadcast.");
+                    }
+
+                    PlayerInfo.HatId = reader.ReadPackedUInt32();
+                    break;
+                }
+
+                case RpcCalls.SetSkin:
+                {
+                    if (!sender.IsOwner(this))
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetSkin)} to an unowned {nameof(InnerPlayerControl)}.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetHat)} to a specific player instead of broadcast.");
+                    }
+
+                    PlayerInfo.SkinId = reader.ReadPackedUInt32();
+                    break;
+                }
+
+                // TODO: (ANTICHEAT) Location check?
+                case RpcCalls.ReportDeadBody:
+                {
+                    if (!sender.IsOwner(this))
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.ReportDeadBody)} to an unowned {nameof(InnerPlayerControl)}.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.ReportDeadBody)} to a specific player instead of broadcast.");
+                    }
+
+                    var deadBodyPlayerId = reader.ReadByte();
+                    break;
+                }
+
+                // TODO: (ANTICHEAT) Cooldown check?
+                case RpcCalls.MurderPlayer:
+                {
+                    if (!sender.IsOwner(this))
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.MurderPlayer)} to an unowned {nameof(InnerPlayerControl)}.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.MurderPlayer)} to a specific player instead of broadcast.");
+                    }
+
+                    if (!sender.Character.PlayerInfo.IsImpostor)
+                    {
+                        // TODO: Uncomment
+                        // throw new ImpostorHackException($"Client sent {nameof(RpcCalls.MurderPlayer)} as crewmate.");
+                    }
+
+                    var player = reader.ReadNetObject<InnerPlayerControl>(_game);
+                    break;
+                }
+
+                case RpcCalls.SendChat:
+                {
+                    if (!sender.IsOwner(this))
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SendChat)} to an unowned {nameof(InnerPlayerControl)}.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SendChat)} to a specific player instead of broadcast.");
+                    }
+
+                    var chat = reader.ReadString();
+                    break;
+                }
+
+                case RpcCalls.StartMeeting:
+                {
+                    if (!sender.IsHost)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.StartMeeting)} but was not a host.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.StartMeeting)} to a specific player instead of broadcast.");
+                    }
+
+                    var playerId = reader.ReadByte();
+                    var player = _game.GameNet.GameData.GetPlayerById(playerId);
+
+                    // Meeting started by "player", can also be null.
+                    Console.WriteLine("ads");
+                    break;
+                }
+
+                case RpcCalls.SetScanner:
+                {
+                    if (!sender.IsOwner(this))
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetScanner)} to an unowned {nameof(InnerPlayerControl)}.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetScanner)} to a specific player instead of broadcast.");
+                    }
+
+                    var on = reader.ReadBoolean();
+                    var count = reader.ReadByte();
+                    break;
+                }
+
+                case RpcCalls.SendChatNote:
+                {
+                    if (!sender.IsOwner(this))
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SendChatNote)} to an unowned {nameof(InnerPlayerControl)}.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SendChatNote)} to a specific player instead of broadcast.");
+                    }
+
+                    var playerId = reader.ReadByte();
+                    var chatNote = (ChatNoteType)reader.ReadByte();
+                    break;
+                }
+
+                case RpcCalls.SetPet:
+                {
+                    if (!sender.IsOwner(this))
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetPet)} to an unowned {nameof(InnerPlayerControl)}.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetPet)} to a specific player instead of broadcast.");
+                    }
+
+                    PlayerInfo.PetId = reader.ReadPackedUInt32();
+                    break;
+                }
+
+                // TODO: Understand this RPC
+                case RpcCalls.SetStartCounter:
+                {
+                    if (!sender.IsOwner(this))
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetStartCounter)} to an unowned {nameof(InnerPlayerControl)}.");
+                    }
+
+                    if (target != null)
+                    {
+                        throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetStartCounter)} to a specific player instead of broadcast.");
+                    }
+
+                    // Used to compare with LastStartCounter.
+                    var startCounter = reader.ReadPackedUInt32();
+
+                    // Is either start countdown or byte.MaxValue
+                    var secondsLeft = reader.ReadByte();
+                    break;
+                }
+
                 default:
                     _logger.LogWarning("InnerPlayerControl: Unknown rpc call {0}", call);
                     break;
@@ -121,5 +365,11 @@ namespace Impostor.Api.Innersloth.Net.Objects
 
             PlayerId = reader.ReadByte();
         }
+
+        private void Die(DeathReason reason)
+        {
+            PlayerInfo.IsDead = true;
+            PlayerInfo.LastDeathReason = reason;
+        }
     }
 }
\ No newline at end of file
index 2aeffecb25ae6f07bb9601ab897442b3842c6b61..1992552b72c927e09023a7539f0f4b2330402094 100644 (file)
@@ -39,7 +39,8 @@ namespace Impostor.Api.Innersloth.Net.Objects
             Components.Add(this);
         }
 
-        public override void HandleRpc(IClientPlayer sender, IClientPlayer target, RpcCalls call, IMessageReader reader)
+        public override void HandleRpc(IClientPlayer sender, IClientPlayer? target, RpcCalls call,
+            IMessageReader reader)
         {
             switch (call)
             {
@@ -56,7 +57,7 @@ namespace Impostor.Api.Innersloth.Net.Objects
                 case RpcCalls.RepairSystem:
                 {
                     var systemType = (SystemTypes)reader.ReadByte();
-                    var player = _game.FindObjectByNetId<InnerPlayerControl>(reader.ReadPackedUInt32());
+                    var player = reader.ReadNetObject<InnerPlayerControl>(_game);
                     var amount = reader.ReadByte();
 
                     if (systemType == SystemTypes.Sabotage && !player.PlayerInfo.IsImpostor)
index 528d2ecd9cb07b31ad555094e19ed8b189143cce..dcb8fc432725d908c7cef041c66ec8050b48263b 100644 (file)
@@ -1,5 +1,6 @@
 using System.Threading.Tasks;
 using Impostor.Api.Games;
+using Impostor.Api.Innersloth.Net;
 using Impostor.Api.Innersloth.Net.Objects;
 
 namespace Impostor.Api.Net
@@ -28,6 +29,13 @@ namespace Impostor.Api.Net
 
         public bool IsHost { get; }
 
+        /// <summary>
+        ///     Checks if the specified <see cref="InnerNetObject"/> is owned by <see cref="IClientPlayer"/>.
+        /// </summary>
+        /// <param name="netObject">The <see cref="InnerNetObject"/>.</param>
+        /// <returns>Returns true if owned by <see cref="IClientPlayer"/>.</returns>
+        bool IsOwner(InnerNetObject netObject);
+
         ValueTask KickAsync();
 
         ValueTask BanAsync();
index f677c20eac73d41e44a8fdca223649da8f57c07e..30412a1bd64307d800044ff742ae5ada19774a05 100644 (file)
@@ -1,4 +1,5 @@
 using System.Threading.Tasks;
+using Impostor.Api.Innersloth.Net;
 using Impostor.Api.Innersloth.Net.Objects;
 using Impostor.Api.Net;
 
@@ -26,6 +27,12 @@ namespace Impostor.Server.Net.State
 
         public string Scene { get; internal set; }
 
+        /// <inheritdoc />
+        public bool IsOwner(InnerNetObject netObject)
+        {
+            return Client.Id == netObject.OwnerId;
+        }
+
         /// <inheritdoc />
         public ValueTask KickAsync()
         {