예제 #1
0
        public void Deserialize(IMessageReader reader)
        {
            PlayerName = reader.ReadString();
            Color      = (ColorType)reader.ReadPackedInt32();
            Hat        = (HatType)reader.ReadPackedUInt32();
            Pet        = (PetType)reader.ReadPackedUInt32();
            Skin       = (SkinType)reader.ReadPackedUInt32();

            var flag = reader.ReadByte();

            Disconnected = (flag & 1) > 0;
            IsImpostor   = (flag & 2) > 0;
            IsDead       = (flag & 4) > 0;

            var taskCount = reader.ReadByte();

            if (Tasks.Count != taskCount)
            {
                Tasks = new List <InnerGameData.TaskInfo>(taskCount);
            }

            for (var i = 0; i < taskCount; i++)
            {
                Tasks[i] ??= new InnerGameData.TaskInfo();
                Tasks[i].Deserialize(reader);
            }
        }
예제 #2
0
        public Vector3 ReadByteAngles(IMessageReader reader)
        {
            float angleX = AngleByteToFloat(reader.ReadByte());
            float angleY = (reader.ReadByte());

            return new Vector3(angleX, angleY, 0);
        }
예제 #3
0
        public async override ValueTask HandleRpc(ClientPlayer sender, ClientPlayer?target, RpcCalls call, IMessageReader reader)
        {
            switch (call)
            {
            case RpcCalls.CloseDoorsOfType:
            {
                if (target == null || !target.IsHost)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CloseDoorsOfType)} to wrong destinition, must be host");
                }

                if (!sender.Character.PlayerInfo.IsImpostor)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CloseDoorsOfType)} as crewmate");
                }

                var systemType = (SystemTypes)reader.ReadByte();
                break;
            }

            case RpcCalls.RepairSystem:
            {
                if (target == null || !target.IsHost)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.RepairSystem)} to wrong destinition, must be host");
                }

                var systemType = (SystemTypes)reader.ReadByte();
                if (systemType == SystemTypes.Sabotage && !sender.Character.PlayerInfo.IsImpostor)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.RepairSystem)} for {systemType} as crewmate");
                }

                var player = reader.ReadNetObject <InnerPlayerControl>(_game);
                var amount = reader.ReadByte();
                if (amount == 7 || amount == 3 || amount == 14 || systemType == SystemTypes.Sabotage)
                {
                    await _eventManager.CallAsync(new PlayerSabotageEvent(_game, _game.GetClientPlayer(player.OwnerId), player, amount));
                }

                // TODO: Modify data (?)
                break;
            }

            default:
            {
                _logger.LogWarning("{0}: Unknown rpc call {1}", nameof(InnerShipStatus), call);
                break;
            }
            }

            return;
        }
예제 #4
0
        public void Deserialize(IMessageReader reader, bool initialState)
        {
            Countdown = reader.ReadSingle();
            UserConsolePairs.Clear(); // TODO: Thread safety

            int count = reader.ReadPackedInt32();

            for (int i = 0; i < count; i++)
            {
                UserConsolePairs.Add(new Tuple <byte, byte>(reader.ReadByte(), reader.ReadByte()));
            }
        }
예제 #5
0
        private static void HandleToServer(string source, IMessageReader packet)
        {
            var tagName = TagMap.ContainsKey(packet.Tag) ? TagMap[packet.Tag] : "Unknown";

            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine($"{source,-15} Server received: {packet.Tag,-2} {tagName}");

            switch (packet.Tag)
            {
            case 0:
                Console.WriteLine("- GameInfo length " + packet.ReadBytesAndSize().Length);
                break;

            case 1:
                Console.WriteLine("- GameCode        " + packet.ReadInt32());
                Console.WriteLine("- Unknown         " + packet.ReadByte());
                break;

            case 5:
            case 6:
                Console.WriteLine("- GameCode        " + packet.ReadInt32());
                Console.WriteLine(HexUtils.HexDump(packet.Buffer.ToArray().Take(packet.Length).ToArray()));
                // packet.Position = packet.Length;
                break;
            }
        }
예제 #6
0
        public override async ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer?target, IMessageReader reader, bool initialState)
        {
            if (!await ValidateHost(CheatContext.Deserialize, sender))
            {
                return;
            }

            var votes   = _votes;
            var unknown = reader.ReadByte();

            if (unknown != 0)
            {
                for (var i = 0; i < unknown; i++)
                {
                    var v4 = reader.ReadInt32();
                    if (v4 == 0)
                    {
                        break;
                    }

                    if (!votes.TryGetValue(v4, out var v12))
                    {
                        v12       = new int[3];
                        votes[v4] = v12;
                    }

                    for (var j = 0; j < 3; j++)
                    {
                        v12[j] = reader.ReadPackedInt32();
                    }
                }
            }
        }
 public static void Deserialize(IMessageReader reader, out uint netId, out string id, out string version, out PluginSide side)
 {
     netId   = reader.ReadPackedUInt32();
     id      = reader.ReadString();
     version = reader.ReadString();
     side    = (PluginSide)reader.ReadByte();
 }
예제 #8
0
        public override async ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer?target, IMessageReader reader, bool initialState)
        {
            if (!await ValidateHost(CheatContext.Deserialize, sender))
            {
                return;
            }

            if (initialState)
            {
                var num = reader.ReadPackedInt32();

                for (var i = 0; i < num; i++)
                {
                    var playerId   = reader.ReadByte();
                    var playerInfo = new InnerPlayerInfo(playerId);

                    playerInfo.Deserialize(reader);

                    if (!_allPlayers.TryAdd(playerInfo.PlayerId, playerInfo))
                    {
                        throw new ImpostorException("Failed to add player to InnerGameData.");
                    }
                }
            }
            else
            {
                throw new NotImplementedException("This shouldn't happen, according to Among Us disassembly.");
            }
        }
예제 #9
0
    /// <summary>
    /// Reads the equipment data from a message reader
    /// </summary>
    /// <param name="reader"></param>
    protected override void readData(IMessageReader reader)
    {
        int numArmorSlots = reader.ReadInt();

        for (int i = 0; i < numArmorSlots; i++)
        {
            ArmorType type = (ArmorType)reader.ReadByte();

            string frameId = reader.ReadString();
            Frame  frame   = Frame.FindFrameById(frameId);

            if (frame != null)
            {
                StartCoroutine(equipAmorDeferred(frame));
            }
        }

        int numCombatItemSlots = reader.ReadInt();

        for (int i = 0; i < numCombatItemSlots; i++)
        {
            int   index = reader.ReadInt();
            Frame frame = Frame.FindFrameById(reader.ReadString());

            if (frame != null)
            {
                StartCoroutine(equipCombatItemDeferred(index, frame));
            }
        }
    }
예제 #10
0
 public static void Deserialize(IMessageReader reader, out int announcementVersion, out int id, out Language language)
 {
     reader.ReadByte(); // SendOption header, probably added by accident
     announcementVersion = reader.ReadPackedInt32();
     id       = reader.ReadPackedInt32();
     language = (Language)reader.ReadPackedInt32();
 }
예제 #11
0
        /// <summary>
        ///     Deserialize a packet.
        /// </summary>
        /// <param name="reader"><see cref="IMessageReader" /> with <see cref="IMessageReader.Tag" /> 0.</param>
        /// <param name="chatType">The chat type selected in the client of the player.</param>
        /// <returns>Deserialized <see cref="GameOptionsData" />.</returns>
        public static GameOptionsData Deserialize(IMessageReader reader, out ChatType chatType)
        {
            var gameOptionsData = GameOptionsData.DeserializeCreate(reader);

            chatType = (ChatType)reader.ReadByte();

            return(gameOptionsData);
        }
예제 #12
0
        public void Deserialize(IMessageReader reader, bool initialState)
        {
            byte num = reader.ReadByte();

            for (int i = 0; i < num; i++)
            {
                SystemTypes systemType = (SystemTypes)reader.ReadByte();
                float       value      = reader.ReadSingle();

                _timers[systemType] = value;
            }

            for (int j = 0; j < _doors.Count; j++)
            {
                _doors[j] = reader.ReadBoolean();
            }
        }
예제 #13
0
        /// <summary>
        ///     Deserialize a packet.
        /// </summary>
        /// <param name="reader"><see cref="IMessageReader" /> with <see cref="IMessageReader.Tag" /> 0.</param>
        /// <param name="chatMode">The chat type selected in the client of the player.</param>
        /// <returns>Deserialized <see cref="GameOptionsData" />.</returns>
        public static GameOptionsData Deserialize(IMessageReader reader, out QuickChatModes chatMode)
        {
            var gameOptionsData = GameOptionsData.DeserializeCreate(reader);

            chatMode = (QuickChatModes)reader.ReadByte();

            return(gameOptionsData);
        }
예제 #14
0
            public void Deserialize(IMessageReader reader)
            {
                var num = reader.ReadByte();

                VotedFor  = (sbyte)((num & VoteMask) - 1);
                IsDead    = (num & DeadBit) > 0;
                DidVote   = (num & VotedBit) > 0;
                DidReport = (num & ReportedBit) > 0;
            }
예제 #15
0
        public void Deserialize(IMessageReader reader)
        {
            PlayerName = reader.ReadString();
            ColorId    = reader.ReadByte();
            HatId      = reader.ReadPackedUInt32();
            PetId      = reader.ReadPackedUInt32();
            SkinId     = reader.ReadPackedUInt32();
            var flag = reader.ReadByte();

            Disconnected = (flag & 1) > 0;
            IsImpostor   = (flag & 2) > 0;
            IsDead       = (flag & 4) > 0;
            var taskCount = reader.ReadByte();

            for (var i = 0; i < taskCount; i++)
            {
                Tasks[i] ??= new InnerGameData.TaskInfo();
                Tasks[i].Deserialize(reader);
            }
        }
예제 #16
0
        public void Deserialize(IMessageReader reader, bool initialState)
        {
            UsersList.Clear();

            var num = reader.ReadPackedInt32();

            for (var i = 0; i < num; i++)
            {
                UsersList.Add(reader.ReadByte());
            }
        }
        public void Deserialize(IMessageReader reader, bool initialState)
        {
            Countdown = reader.ReadSingle();
            Timer     = reader.ReadSingle();
            ActiveConsoles.Clear();    // TODO: Thread safety
            CompletedConsoles.Clear(); // TODO: Thread safety

            uint activeCount = reader.ReadPackedUInt32();

            for (int i = 0; i < activeCount; i++)
            {
                ActiveConsoles.Add(new Tuple <byte, byte>(reader.ReadByte(), reader.ReadByte()));
            }

            uint completedCount = reader.ReadPackedUInt32();

            for (int i = 0; i < completedCount; i++)
            {
                CompletedConsoles.Add(reader.ReadByte());
            }
        }
예제 #18
0
        public PlayerInfo(IMessageReader reader)
        {
            Name  = reader.ReadString();
            Color = reader.ReadByte();
            Hat   = reader.ReadPackedUInt32();
            Pet   = reader.ReadPackedUInt32();
            Skin  = reader.ReadPackedUInt32();
            var flags = reader.ReadByte();

            Disconnected = (flags & 1) != 0;
            IsImpostor   = (flags & 2) != 0;
            IsDead       = (flags & 4) != 0;
            var capacity = reader.ReadByte();

            for (var i = 0; i < capacity; i++)
            {
                var id = reader.ReadPackedUInt32();
                //var typeId = reader.ReadByte(); // idk if this is written
                var complete = reader.ReadBoolean();
            }
        }
예제 #19
0
        public override void Deserialize(IClientPlayer sender, IClientPlayer?target, IMessageReader reader, bool initialState)
        {
            if (!sender.IsHost)
            {
                // throw new ImpostorCheatException($"Client attempted to send data for {nameof(InnerPlayerControl)} as non-host");
            }

            if (initialState)
            {
                IsNew = reader.ReadBoolean();
            }

            PlayerId = reader.ReadByte();
        }
        public override async ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer?target, IMessageReader reader, bool initialState)
        {
            if (!await ValidateHost(CheatContext.Deserialize, sender))
            {
                return;
            }

            if (initialState)
            {
                IsNew = reader.ReadBoolean();
            }

            PlayerId = reader.ReadByte();
        }
예제 #21
0
        public static void Deserialize(IMessageReader reader, out ISet <Mod> mods)
        {
            var length = reader.ReadPackedInt32();

            mods = new HashSet <Mod>(length);

            for (var i = 0; i < length; i++)
            {
                var id         = reader.ReadString();
                var version    = reader.ReadString();
                var pluginSide = (PluginSide)reader.ReadByte();

                mods.Add(new Mod(id, version, pluginSide));
            }
        }
예제 #22
0
        public static bool Deserialize(IMessageReader reader, [NotNullWhen(true)] out ReactorProtocolVersion?version, [NotNullWhen(true)] out int?modCount)
        {
            if (reader.Length > reader.Position)
            {
                version  = (ReactorProtocolVersion)reader.ReadByte();
                modCount = reader.ReadPackedInt32();

                return(true);
            }

            version  = null;
            modCount = null;

            return(false);
        }
예제 #23
0
        public override async ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer?target, IMessageReader reader, bool initialState)
        {
            if (!await ValidateHost(CheatContext.Deserialize, sender))
            {
                return;
            }

            if (initialState)
            {
                var num = reader.ReadPackedInt32();

                for (var i = 0; i < num; i++)
                {
                    var playerId   = reader.ReadByte();
                    var playerInfo = new InnerPlayerInfo(playerId);

                    playerInfo.Deserialize(reader);

                    if (!_allPlayers.TryAdd(playerInfo.PlayerId, playerInfo))
                    {
                        throw new ImpostorException("Failed to add player to InnerGameData.");
                    }
                }
            }
            else
            {
                while (reader.Position < reader.Length)
                {
                    var inner      = reader.ReadMessage();
                    var playerInfo = this.GetPlayerById(inner.Tag);
                    if (playerInfo != null)
                    {
                        playerInfo.Deserialize(inner);
                    }
                    else
                    {
                        playerInfo = new InnerPlayerInfo(inner.Tag);
                        playerInfo.Deserialize(inner);

                        if (!_allPlayers.TryAdd(playerInfo.PlayerId, playerInfo))
                        {
                            throw new ImpostorException("Failed to add player to InnerGameData.");
                        }
                    }
                }
            }
        }
예제 #24
0
        public override async ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer?target, IMessageReader reader, bool initialState)
        {
            if (!sender.IsHost)
            {
                if (await sender.Client.ReportCheatAsync(CheatContext.Deserialize, $"Client attempted to send data for {nameof(InnerPlayerControl)} as non-host"))
                {
                    return;
                }
            }

            if (initialState)
            {
                IsNew = reader.ReadBoolean();
            }

            PlayerId = reader.ReadByte();
        }
예제 #25
0
        public override async ValueTask DeserializeAsync(IClientPlayer sender, IClientPlayer?target, IMessageReader reader, bool initialState)
        {
            if (!sender.IsHost)
            {
                if (await sender.Client.ReportCheatAsync(CheatContext.Deserialize, $"Client attempted to send data for {nameof(InnerShipStatus)} as non-host"))
                {
                    return;
                }
            }

            var votes   = _votes;
            var unknown = reader.ReadByte();

            if (unknown != 0)
            {
                for (var i = 0; i < unknown; i++)
                {
                    var v4 = reader.ReadInt32();
                    if (v4 == 0)
                    {
                        break;
                    }

                    if (!votes.TryGetValue(v4, out var v12))
                    {
                        v12       = new int[3];
                        votes[v4] = v12;
                    }

                    for (var j = 0; j < 3; j++)
                    {
                        v12[j] = reader.ReadPackedInt32();
                    }
                }
            }
        }
예제 #26
0
 public static void Deserialize(IMessageReader reader, out bool on, out byte scannerCount)
 {
     on           = reader.ReadBoolean();
     scannerCount = reader.ReadByte();
 }
 public void Deserialize(IMessageReader reader, bool initialState, IEventManager eventManager)
 {
     InUse = reader.ReadByte();
 }
예제 #28
0
        public override async ValueTask HandleRpc(ClientPlayer sender, ClientPlayer?target, RpcCalls call, IMessageReader reader)
        {
            switch (call)
            {
            case RpcCalls.Close:
            {
                if (!sender.IsHost)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.Close)} but was not a host");
                }

                if (target != null)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.Close)} to a specific player instead of broadcast");
                }

                break;
            }

            case RpcCalls.VotingComplete:
            {
                if (!sender.IsHost)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.VotingComplete)} but was not a host");
                }

                if (target != null)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.VotingComplete)} to a specific player instead of broadcast");
                }

                var states   = reader.ReadBytesAndSize();
                var playerId = reader.ReadByte();
                var tie      = reader.ReadBoolean();

                if (playerId != byte.MaxValue)
                {
                    var player = _game.GameNet.GameData.GetPlayerById(playerId);
                    if (player != null)
                    {
                        player.Controller.Die(DeathReason.Exile);
                        await _eventManager.CallAsync(new PlayerExileEvent(_game, sender, player.Controller));
                    }
                }

                await _eventManager.CallAsync(new MeetingEndedEvent(_game, this));

                break;
            }

            case RpcCalls.CastVote:
            {
                var srcPlayerId = reader.ReadByte();
                if (srcPlayerId != sender.Character.PlayerId)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CastVote)} to an unowned {nameof(InnerPlayerControl)}");
                }

                // Host broadcasts vote to others.
                if (sender.IsHost && target != null)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CastVote)} to a specific player instead of broadcast");
                }

                // Player sends vote to host.
                if (target == null || !target.IsHost)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CastVote)} to wrong destinition, must be host");
                }

                var targetPlayerId = reader.ReadByte();
                break;
            }

            default:
            {
                _logger.LogWarning("{0}: Unknown rpc call {1}", nameof(InnerMeetingHud), call);
                break;
            }
            }
        }
예제 #29
0
        public override ValueTask HandleRpc(ClientPlayer sender, ClientPlayer?target, RpcCalls call, IMessageReader reader)
        {
            switch (call)
            {
            case RpcCalls.SetTasks:
            {
                if (!sender.IsHost)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetTasks)} but was not a host");
                }

                if (target != null)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetTasks)} to a specific player instead of broadcast");
                }

                var playerId    = reader.ReadByte();
                var taskTypeIds = reader.ReadBytesAndSize();

                SetTasks(playerId, taskTypeIds);
                break;
            }

            case RpcCalls.UpdateGameData:
            {
                if (!sender.IsHost)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetTasks)} but was not a host");
                }

                if (target != null)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.SetTasks)} to a specific player instead of broadcast");
                }

                while (reader.Position < reader.Length)
                {
                    using var message = reader.ReadMessage();
                    var player = GetPlayerById(message.Tag);
                    if (player != null)
                    {
                        player.Deserialize(message);
                    }
                    else
                    {
                        var playerInfo = new InnerPlayerInfo(message.Tag);

                        playerInfo.Deserialize(reader);

                        if (!_allPlayers.TryAdd(playerInfo.PlayerId, playerInfo))
                        {
                            throw new ImpostorException("Failed to add player to InnerGameData.");
                        }
                    }
                }

                break;
            }

            default:
            {
                _logger.LogWarning("{0}: Unknown rpc call {1}", nameof(InnerGameData), call);
                break;
            }
            }

            return(default);
예제 #30
0
 public static void Deserialize(IMessageReader reader, out int clientVersion, out Platforms platform, out string clientId)
 {
     clientVersion = reader.ReadInt32();
     platform      = (Platforms)reader.ReadByte();
     clientId      = reader.ReadString();
 }
예제 #31
0
        public override async ValueTask HandleRpc(ClientPlayer sender, ClientPlayer?target, RpcCalls call, IMessageReader reader)
        {
            switch (call)
            {
            // Play an animation.
            case RpcCalls.PlayAnimation:
            {
                if (!sender.IsOwner(this))
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.PlayAnimation)} to an unowned {nameof(InnerPlayerControl)}");
                }

                if (target != null)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.PlayAnimation)} to a specific player instead of broadcast");
                }

                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)}");
                }

                if (target != null)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.CompleteTask)} to a specific player instead of broadcast");
                }

                var taskId = reader.ReadPackedUInt32();
                var task   = PlayerInfo.Tasks[(int)taskId];
                if (task == null)
                {
                    _logger.LogWarning($"Client sent {nameof(RpcCalls.CompleteTask)} with a taskIndex that is not in their {nameof(InnerPlayerInfo)}");
                }
                else
                {
                    task.Complete = true;
                    await _eventManager.CallAsync(new PlayerCompletedTaskEvent(_game, sender, this, task));
                }

                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());
                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++)
                {
                    var playerId = reader.ReadByte();
                    var player   = _game.GameNet.GameData.GetPlayerById(playerId);
                    if (player != null)
                    {
                        player.IsImpostor = true;
                    }
                }

                if (_game.GameState == GameStates.Starting)
                {
                    await _game.StartedAsync();
                }

                break;
            }

            // Player was voted out.
            case RpcCalls.Exiled:
            {
                if (!sender.IsHost)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.Exiled)} but was not a host");
                }

                if (target != null)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.Exiled)} to a specific player instead of broadcast");
                }

                // TODO: Not hit?
                Die(DeathReason.Exile);

                await _eventManager.CallAsync(new PlayerExileEvent(_game, sender, this));

                break;
            }

            // Validates the player name at the host.
            case RpcCalls.CheckName:
            {
                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?
            // only called by a non-host player on to start meeting
            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();
                // deadBodyPlayerId == byte.MaxValue -- means emergency call by button

                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)
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.MurderPlayer)} as crewmate");
                }

                if (!sender.Character.PlayerInfo.CanMurder(_game))
                {
                    throw new ImpostorCheatException($"Client sent {nameof(RpcCalls.MurderPlayer)} too fast");
                }

                sender.Character.PlayerInfo.LastMurder = DateTimeOffset.UtcNow;

                var player = reader.ReadNetObject <InnerPlayerControl>(_game);
                if (!player.PlayerInfo.IsDead)
                {
                    player.Die(DeathReason.Kill);
                    await _eventManager.CallAsync(new PlayerMurderEvent(_game, sender, this, player));
                }

                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();

                await _eventManager.CallAsync(new PlayerChatEvent(_game, sender, this, chat));

                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");
                }

                // deadBodyPlayerId == byte.MaxValue -- means emergency call by button
                var deadBodyPlayerId = reader.ReadByte();
                var deadPlayer       = deadBodyPlayerId != byte.MaxValue
                        ? _game.GameNet.GameData.GetPlayerById(deadBodyPlayerId)?.Controller
                        : null;

                await _eventManager.CallAsync(new PlayerStartMeetingEvent(_game, _game.GetClientPlayer(this.OwnerId), this, deadPlayer));

                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();
                if (secondsLeft < byte.MaxValue)
                {
                    await _eventManager.CallAsync(new PlayerSetStartCounterEvent(_game, sender, this, secondsLeft));
                }

                break;
            }

            default:
            {
                _logger.LogWarning("{0}: Unknown rpc call {1}", nameof(InnerPlayerControl), call);
                break;
            }
            }
        }