From: AeonLucid Date: Fri, 23 Oct 2020 20:27:57 +0000 (+0200) Subject: Finish up PlayerControl RPCs X-Git-Tag: v1.2.2~96^2~38 X-Git-Url: https://git.deb.at/?a=commitdiff_plain;h=1b776bfce93ea00ed807a0fa92aaa14b4fa17928;p=rhonda%2Fimpostor.git Finish up PlayerControl RPCs --- diff --git a/src/Impostor.Api/Exceptions/ImpostorCheatException.cs b/src/Impostor.Api/Exceptions/ImpostorCheatException.cs new file mode 100644 index 0000000..8eb72f8 --- /dev/null +++ b/src/Impostor.Api/Exceptions/ImpostorCheatException.cs @@ -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 index 0000000..94e7d99 --- /dev/null +++ b/src/Impostor.Api/Extensions/MessageReaderExtensions.cs @@ -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(this IMessageReader reader, IGame game) + where T : InnerNetObject + { + return game.FindObjectByNetId(reader.ReadPackedUInt32()); + } + } +} \ No newline at end of file diff --git a/src/Impostor.Api/Impostor.Api.csproj b/src/Impostor.Api/Impostor.Api.csproj index 973c5ac..4d3a0fb 100644 --- a/src/Impostor.Api/Impostor.Api.csproj +++ b/src/Impostor.Api/Impostor.Api.csproj @@ -6,6 +6,7 @@ ProjectRules.ruleset 9 true + enable diff --git a/src/Impostor.Api/Innersloth/Data/ChatNoteType.cs b/src/Impostor.Api/Innersloth/Data/ChatNoteType.cs new file mode 100644 index 0000000..04c3278 --- /dev/null +++ b/src/Impostor.Api/Innersloth/Data/ChatNoteType.cs @@ -0,0 +1,7 @@ +namespace Impostor.Api.Innersloth.Data +{ + public enum ChatNoteType : byte + { + DidVote = 0, + } +} \ No newline at end of file diff --git a/src/Impostor.Api/Innersloth/Net/InnerNetObject.cs b/src/Impostor.Api/Innersloth/Net/InnerNetObject.cs index 7981051..dc34587 100644 --- a/src/Impostor.Api/Innersloth/Net/InnerNetObject.cs +++ b/src/Impostor.Api/Innersloth/Net/InnerNetObject.cs @@ -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); diff --git a/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerCustomNetworkTransform.cs b/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerCustomNetworkTransform.cs index 4567ae7..8270e32 100644 --- a/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerCustomNetworkTransform.cs +++ b/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerCustomNetworkTransform.cs @@ -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()); } diff --git a/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerPlayerPhysics.cs b/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerPlayerPhysics.cs index 6ecfe63..7128138 100644 --- a/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerPlayerPhysics.cs +++ b/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerPlayerPhysics.cs @@ -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(); } diff --git a/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerVoteBanSystem.cs b/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerVoteBanSystem.cs index 7930853..a9590df 100644 --- a/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerVoteBanSystem.cs +++ b/src/Impostor.Api/Innersloth/Net/Objects/Components/InnerVoteBanSystem.cs @@ -14,7 +14,8 @@ namespace Impostor.Api.Innersloth.Net.Objects.Components _votes = new Dictionary(); } - 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(); } diff --git a/src/Impostor.Api/Innersloth/Net/Objects/InnerGameData.cs b/src/Impostor.Api/Innersloth/Net/Objects/InnerGameData.cs index a4e05ef..e7fafab 100644 --- a/src/Impostor.Api/Innersloth/Net/Objects/InnerGameData.cs +++ b/src/Impostor.Api/Innersloth/Net/Objects/InnerGameData.cs @@ -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(); } diff --git a/src/Impostor.Api/Innersloth/Net/Objects/InnerLobbyBehaviour.cs b/src/Impostor.Api/Innersloth/Net/Objects/InnerLobbyBehaviour.cs index 7a3f8a9..e0661f8 100644 --- a/src/Impostor.Api/Innersloth/Net/Objects/InnerLobbyBehaviour.cs +++ b/src/Impostor.Api/Innersloth/Net/Objects/InnerLobbyBehaviour.cs @@ -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(); } diff --git a/src/Impostor.Api/Innersloth/Net/Objects/InnerMeetingHud.cs b/src/Impostor.Api/Innersloth/Net/Objects/InnerMeetingHud.cs index 8496c65..19e636e 100644 --- a/src/Impostor.Api/Innersloth/Net/Objects/InnerMeetingHud.cs +++ b/src/Impostor.Api/Innersloth/Net/Objects/InnerMeetingHud.cs @@ -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(); } diff --git a/src/Impostor.Api/Innersloth/Net/Objects/InnerPlayerControl.cs b/src/Impostor.Api/Innersloth/Net/Objects/InnerPlayerControl.cs index a6c44fb..998fc4f 100644 --- a/src/Impostor.Api/Innersloth/Net/Objects/InnerPlayerControl.cs +++ b/src/Impostor.Api/Innersloth/Net/Objects/InnerPlayerControl.cs @@ -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(_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 diff --git a/src/Impostor.Api/Innersloth/Net/Objects/InnerShipStatus.cs b/src/Impostor.Api/Innersloth/Net/Objects/InnerShipStatus.cs index 2aeffec..1992552 100644 --- a/src/Impostor.Api/Innersloth/Net/Objects/InnerShipStatus.cs +++ b/src/Impostor.Api/Innersloth/Net/Objects/InnerShipStatus.cs @@ -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(reader.ReadPackedUInt32()); + var player = reader.ReadNetObject(_game); var amount = reader.ReadByte(); if (systemType == SystemTypes.Sabotage && !player.PlayerInfo.IsImpostor) diff --git a/src/Impostor.Api/Net/IClientPlayer.cs b/src/Impostor.Api/Net/IClientPlayer.cs index 528d2ec..dcb8fc4 100644 --- a/src/Impostor.Api/Net/IClientPlayer.cs +++ b/src/Impostor.Api/Net/IClientPlayer.cs @@ -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; } + /// + /// Checks if the specified is owned by . + /// + /// The . + /// Returns true if owned by . + bool IsOwner(InnerNetObject netObject); + ValueTask KickAsync(); ValueTask BanAsync(); diff --git a/src/Impostor.Server/Net/State/ClientPlayer.cs b/src/Impostor.Server/Net/State/ClientPlayer.cs index f677c20..30412a1 100644 --- a/src/Impostor.Server/Net/State/ClientPlayer.cs +++ b/src/Impostor.Server/Net/State/ClientPlayer.cs @@ -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; } + /// + public bool IsOwner(InnerNetObject netObject) + { + return Client.Id == netObject.OwnerId; + } + /// public ValueTask KickAsync() {