]> git.deb.at Git - rhonda/impostor.git/commitdiff
WIP
authorjs6pak <kubastaron@hotmail.com>
Wed, 10 Nov 2021 19:46:56 +0000 (20:46 +0100)
committerjs6pak <kubastaron@hotmail.com>
Wed, 10 Nov 2021 19:46:56 +0000 (20:46 +0100)
41 files changed:
src/Impostor.Api/Extensions/SpanReaderExtensions.cs
src/Impostor.Api/Innersloth/Customization/HatType.cs [deleted file]
src/Impostor.Api/Innersloth/Customization/PetType.cs [deleted file]
src/Impostor.Api/Innersloth/Customization/PlayerOutfit.cs [new file with mode: 0644]
src/Impostor.Api/Innersloth/Customization/PlayerOutfitType.cs [new file with mode: 0644]
src/Impostor.Api/Innersloth/Customization/SkinType.cs [deleted file]
src/Impostor.Api/Innersloth/GameOptionsData.cs
src/Impostor.Api/Innersloth/PlatformSpecificData.cs [new file with mode: 0644]
src/Impostor.Api/Innersloth/Platforms.cs
src/Impostor.Api/Innersloth/RoleOptionsData.cs [new file with mode: 0644]
src/Impostor.Api/Innersloth/RoleTypes.cs [new file with mode: 0644]
src/Impostor.Api/Net/Inner/Objects/IInnerPlayerControl.cs
src/Impostor.Api/Net/Inner/Objects/IInnerPlayerInfo.cs
src/Impostor.Api/Net/Inner/RpcCalls.cs
src/Impostor.Api/Net/Messages/C2S/HandshakeC2S.cs
src/Impostor.Api/Net/Messages/C2S/Message00HostGameC2S.cs
src/Impostor.Api/Net/Messages/C2S/Message01JoinGameC2S.cs
src/Impostor.Api/Net/Messages/C2S/Message16GetGameListC2S.cs
src/Impostor.Api/Net/Messages/IMessageReader.cs
src/Impostor.Api/Net/Messages/IMessageWriter.cs
src/Impostor.Api/Net/Messages/MessageFlags.cs
src/Impostor.Api/Net/Messages/Rpcs/Rpc09SetHat.cs [deleted file]
src/Impostor.Api/Net/Messages/Rpcs/Rpc10SetSkin.cs [deleted file]
src/Impostor.Api/Net/Messages/Rpcs/Rpc17SetPet.cs [deleted file]
src/Impostor.Api/Net/Messages/Rpcs/Rpc39SetHat.cs [new file with mode: 0644]
src/Impostor.Api/Net/Messages/Rpcs/Rpc40SetSkin.cs [new file with mode: 0644]
src/Impostor.Api/Net/Messages/Rpcs/Rpc41SetPet.cs [new file with mode: 0644]
src/Impostor.Api/Net/Messages/S2C/MessageDisconnect.cs
src/Impostor.Hazel
src/Impostor.Server/Net/Client.cs
src/Impostor.Server/Net/ClientBase.cs
src/Impostor.Server/Net/Factories/ClientFactory.cs
src/Impostor.Server/Net/Factories/IClientFactory.cs
src/Impostor.Server/Net/Inner/Objects/InnerMeetingHud.cs
src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.Api.cs
src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs
src/Impostor.Server/Net/Inner/Objects/InnerPlayerInfo.cs
src/Impostor.Server/Net/Manager/ClientManager.cs
src/Impostor.Server/Net/Matchmaker.cs
src/Impostor.Server/Net/Redirector/ClientRedirector.cs
src/Impostor.Server/Recorder/ClientRecorder.cs

index 05b891ab4d211e29af34ed9606d519021071f2bd..3b24c7796d3a04c2859acc221233509b49ed8c71 100644 (file)
@@ -16,6 +16,13 @@ namespace Impostor.Api
             return original[0];
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static short ReadInt16(this ref ReadOnlySpan<byte> input)
+        {
+            var original = Advance<short>(ref input);
+            return BinaryPrimitives.ReadInt16LittleEndian(original);
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static int ReadInt32(this ref ReadOnlySpan<byte> input)
         {
diff --git a/src/Impostor.Api/Innersloth/Customization/HatType.cs b/src/Impostor.Api/Innersloth/Customization/HatType.cs
deleted file mode 100644 (file)
index 28b481b..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-namespace Impostor.Api.Innersloth.Customization
-{
-    public enum HatType : uint
-    {
-        NoHat = 0,
-        Astronaut = 1,
-        BaseballCap = 2,
-        BrainSlug = 3,
-        BushHat = 4,
-        CaptainsHat = 5,
-        DoubleTopHat = 6,
-        Flowerpot = 7,
-        Goggles = 8,
-        HardHat = 9,
-        Military = 10,
-        PaperHat = 11,
-        PartyHat = 12,
-        Police = 13,
-        Stethescope = 14,
-        TopHat = 15,
-        TowelWizard = 16,
-        Ushanka = 17,
-        Viking = 18,
-        WallCap = 19,
-        Snowman = 20,
-        Reindeer = 21,
-        Lights = 22,
-        Santa = 23,
-        Tree = 24,
-        Present = 25,
-        Candycanes = 26,
-        ElfHat = 27,
-        NewYears2018 = 28,
-        WhiteHat = 29,
-        Crown = 30,
-        Eyebrows = 31,
-        HaloHat = 32,
-        HeroCap = 33,
-        PipCap = 34,
-        PlungerHat = 35,
-        ScubaHat = 36,
-        StickminHat = 37,
-        StrawHat = 38,
-        TenGallonHat = 39,
-        ThirdEyeHat = 40,
-        ToiletPaperHat = 41,
-        Toppat = 42,
-        BlackFedora = 43,
-        SkiGoggles = 44,
-        Headphones = 45,
-        MaskHat = 46,
-        PaperMask = 47,
-        Security = 48,
-        StrapHat = 49,
-        Banana = 50,
-        Beanie = 51,
-        Bear = 52,
-        Cheese = 53,
-        Cherry = 54,
-        Egg = 55,
-        GreenFedora = 56,
-        Flamingo = 57,
-        FlowerPin = 58,
-        Helmet = 59,
-        Plant = 60,
-        BatEyes = 61,
-        BatWings = 62,
-        Horns = 63,
-        Mohawk = 64,
-        Pumpkin = 65,
-        ScaryBag = 66,
-        Witch = 67,
-        Wolf = 68,
-        Pirate = 69,
-        Plague = 70,
-        Machete = 71,
-        Fred = 72,
-        MinerCap = 73,
-        WinterHat = 74,
-        Archae = 75,
-        Antenna = 76,
-        Balloon = 77,
-        BirdNest = 78,
-        BlackBelt = 79,
-        Caution = 80,
-        Chef = 81,
-        CopHat = 82,
-        DoRag = 83,
-        DumSticker = 84,
-        Fez = 85,
-        GeneralHat = 86,
-        GreyThing = 87,
-        HunterCap = 88,
-        JungleHat = 89,
-        MiniCrewmate = 90,
-        NinjaMask = 91,
-        RamHorns = 92,
-        SnowCrewmate = 93,
-        GeoffHat = 94,
-        DaveCap = 95,
-        EllieRose = 96,
-        SvenHat = 97,
-        BurtHat = 98,
-        EllryMohawk = 99,
-        ThomasMonoc = 100,
-        Wizard = 101,
-        Fredrick = 102,
-        MrMacbeth = 103,
-        ToppatHenry = 104,
-        ToppatEllie = 105,
-        GeoffreyPlumb = 106,
-        RightHandMan = 107,
-        AngryEyebrows = 108,
-        ChocolateIce = 109,
-        HeartPin = 110,
-        Ponytail = 111,
-        RubberGlove = 112,
-        UnicornHorn = 113,
-        Zipper = 114,
-    }
-}
diff --git a/src/Impostor.Api/Innersloth/Customization/PetType.cs b/src/Impostor.Api/Innersloth/Customization/PetType.cs
deleted file mode 100644 (file)
index f8753f2..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace Impostor.Api.Innersloth.Customization
-{
-    public enum PetType : uint
-    {
-        NoPet = 0,
-        Alien = 1,
-        Crewmate = 2,
-        Doggy = 3,
-        Stickmin = 4,
-        Hamster = 5,
-        Robot = 6,
-        Ufo = 7,
-        Ellie = 8,
-        Squig = 9,
-        Bedcrab = 10,
-        Glitch = 11,
-    }
-}
diff --git a/src/Impostor.Api/Innersloth/Customization/PlayerOutfit.cs b/src/Impostor.Api/Innersloth/Customization/PlayerOutfit.cs
new file mode 100644 (file)
index 0000000..a54b889
--- /dev/null
@@ -0,0 +1,56 @@
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth.Customization
+{
+    public class PlayerOutfit
+    {
+        public ColorType Color { get; internal set; } = (ColorType)(-1);
+
+        public string HatId { get; internal set; } = "missing";
+
+        public string PetId { get; internal set; } = "missing";
+
+        public string SkinId { get; internal set; } = "missing";
+
+        public string VisorId { get; internal set; } = "missing";
+
+        public string NamePlateId { get; internal set; } = "missing";
+
+        public string PlayerName { get; internal set; } = "missing";
+
+        public bool IsIncomplete
+        {
+            get
+            {
+                if (!string.IsNullOrEmpty(PlayerName) && Color != (ColorType)(-1) && HatId != "missing" && PetId != "missing" && SkinId != "missing" && VisorId != "missing")
+                {
+                    return NamePlateId == "missing";
+                }
+
+                return true;
+            }
+        }
+
+        public void Serialize(IMessageWriter writer)
+        {
+            writer.Write(PlayerName);
+            writer.WritePacked((int)Color);
+            writer.Write(HatId);
+            writer.Write(PetId);
+            writer.Write(SkinId);
+            writer.Write(VisorId);
+            writer.Write(NamePlateId);
+        }
+
+        public void Deserialize(IMessageReader reader)
+        {
+            PlayerName = reader.ReadString();
+            Color = (ColorType)reader.ReadPackedInt32();
+            HatId = reader.ReadString();
+            PetId = reader.ReadString();
+            SkinId = reader.ReadString();
+            VisorId = reader.ReadString();
+            NamePlateId = reader.ReadString();
+        }
+    }
+}
diff --git a/src/Impostor.Api/Innersloth/Customization/PlayerOutfitType.cs b/src/Impostor.Api/Innersloth/Customization/PlayerOutfitType.cs
new file mode 100644 (file)
index 0000000..55ba04c
--- /dev/null
@@ -0,0 +1,8 @@
+namespace Impostor.Api.Innersloth.Customization
+{
+    public enum PlayerOutfitType
+    {
+        Default,
+        Shapeshifted,
+    }
+}
diff --git a/src/Impostor.Api/Innersloth/Customization/SkinType.cs b/src/Impostor.Api/Innersloth/Customization/SkinType.cs
deleted file mode 100644 (file)
index efda42e..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace Impostor.Api.Innersloth.Customization
-{
-    public enum SkinType : uint
-    {
-        None = 0,
-        Astro = 1,
-        Capt = 2,
-        Mech = 3,
-        Military = 4,
-        Police = 5,
-        Science = 6,
-        SuitB = 7,
-        SuitW = 8,
-        Wall = 9,
-        Hazmat = 10,
-        Security = 11,
-        Tarmac = 12,
-        Miner = 13,
-        Winter = 14,
-        Archae = 15,
-        Prisoner = 16,
-        CCC = 17,
-        RightHandMan = 18,
-    }
-}
index 5530c8878e9a0d47c5718116ba2f7016ba950e46..3f01ca8c2bcfa72933776b9d50f0bb6a2ac3cfdd 100644 (file)
@@ -9,7 +9,7 @@ namespace Impostor.Api.Innersloth
         /// <summary>
         ///     The latest major version of the game client.
         /// </summary>
-        public const int LatestVersion = 4;
+        public const int LatestVersion = 5;
 
         /// <summary>
         ///     Gets or sets host's version of the game.
@@ -124,6 +124,11 @@ namespace Impostor.Api.Innersloth
         /// </summary>
         public TaskBarUpdate TaskBarUpdate { get; set; } = TaskBarUpdate.Always;
 
+        /// <summary>
+        ///     Gets or sets role options.
+        /// </summary>
+        public RoleOptionsData RoleOptions { get; set; } = new RoleOptionsData();
+
         /// <summary>
         ///     Gets or sets a value indicating whether the GameOptions are the default ones.
         /// </summary>
@@ -184,6 +189,11 @@ namespace Impostor.Api.Innersloth
             }
 
             if (version > 4)
+            {
+                RoleOptions.Serialize(writer);
+            }
+
+            if (version > 5)
             {
                 throw new ImpostorException($"Unknown GameOptionsData version {Version}.");
             }
@@ -246,6 +256,11 @@ namespace Impostor.Api.Innersloth
             }
 
             if (Version > 4)
+            {
+                RoleOptions = RoleOptionsData.Deserialize(bytes);
+            }
+
+            if (Version > 5)
             {
                 throw new ImpostorException($"Unknown GameOptionsData version {Version}.");
             }
diff --git a/src/Impostor.Api/Innersloth/PlatformSpecificData.cs b/src/Impostor.Api/Innersloth/PlatformSpecificData.cs
new file mode 100644 (file)
index 0000000..807f626
--- /dev/null
@@ -0,0 +1,42 @@
+using System;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth
+{
+    public class PlatformSpecificData
+    {
+        public PlatformSpecificData(IMessageReader reader)
+        {
+            Platform = (Platforms)reader.Tag;
+            PlatformName = reader.ReadString();
+
+            XboxPlatformId = Platform == Platforms.Xbox ? reader.ReadUInt64() : null;
+            PsnPlatformId = Platform == Platforms.Playstation ? reader.ReadUInt64() : null;
+        }
+
+        public Platforms Platform { get; }
+
+        public string PlatformName { get; }
+
+        public ulong? XboxPlatformId { get; }
+
+        public ulong? PsnPlatformId { get; }
+
+        public void Serialize(IMessageWriter writer)
+        {
+            writer.StartMessage((byte)Platform);
+            writer.Write(PlatformName);
+            switch (Platform)
+            {
+                case Platforms.Xbox:
+                    writer.Write(XboxPlatformId ?? throw new NullReferenceException($"{nameof(XboxPlatformId)} shouldn't be null when {nameof(Platform)} is Xbox"));
+                    break;
+                case Platforms.Playstation:
+                    writer.Write(PsnPlatformId ?? throw new NullReferenceException($"{nameof(PsnPlatformId)} shouldn't be null when {nameof(Platform)} is Playstation"));
+                    break;
+            }
+
+            writer.EndMessage();
+        }
+    }
+}
index 2388e39e5a8df6fecd614e320052feb434619f6c..c1b3b097704d76706475363cfb6c7cc17ecead12 100644 (file)
@@ -11,5 +11,7 @@ namespace Impostor.Api.Innersloth
         IPhone = 6,
         Android = 7,
         Switch = 8,
+        Xbox = 9,
+        Playstation = 10,
     }
 }
diff --git a/src/Impostor.Api/Innersloth/RoleOptionsData.cs b/src/Impostor.Api/Innersloth/RoleOptionsData.cs
new file mode 100644 (file)
index 0000000..1d07854
--- /dev/null
@@ -0,0 +1,136 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Impostor.Api.Net.Messages;
+
+namespace Impostor.Api.Innersloth
+{
+    public class RoleOptionsData
+    {
+        public bool ShapeshifterLeaveSkin { get; set; }
+
+        public float ShapeshifterCooldown { get; set; } = 10f;
+
+        public float ShapeshifterDuration { get; set; } = 30f;
+
+        public float ScientistCooldown { get; set; } = 15f;
+
+        public float ScientistBatteryCharge { get; set; } = 5f;
+
+        public float GuardianAngelCooldown { get; set; } = 60f;
+
+        public bool ImpostorsCanSeeProtect { get; set; }
+
+        public float ProtectionDurationSeconds { get; set; } = 10f;
+
+        public float EngineerCooldown { get; set; } = 30f;
+
+        public float EngineerInVentMaxTime { get; set; } = 15f;
+
+        public Dictionary<RoleTypes, RoleRate> RoleRates { get; } = new Dictionary<RoleTypes, RoleRate>();
+
+        public static RoleOptionsData Deserialize(ReadOnlySpan<byte> span)
+        {
+            var roleOptionsData = new RoleOptionsData();
+            var num = span.ReadInt32();
+            for (var i = 0; i < num; i++)
+            {
+                var key = (RoleTypes)span.ReadInt16();
+                var roleRate = new RoleRate(span.ReadByte(), span.ReadByte());
+                roleOptionsData.RoleRates[key] = roleRate;
+            }
+
+            roleOptionsData.ShapeshifterLeaveSkin = span.ReadBoolean();
+            roleOptionsData.ShapeshifterCooldown = span.ReadByte();
+            roleOptionsData.ShapeshifterDuration = span.ReadByte();
+            roleOptionsData.ScientistCooldown = span.ReadByte();
+            roleOptionsData.GuardianAngelCooldown = span.ReadByte();
+            roleOptionsData.EngineerCooldown = span.ReadByte();
+            roleOptionsData.EngineerInVentMaxTime = span.ReadByte();
+            roleOptionsData.ScientistBatteryCharge = span.ReadByte();
+            roleOptionsData.ProtectionDurationSeconds = span.ReadByte();
+            roleOptionsData.ImpostorsCanSeeProtect = span.ReadBoolean();
+            return roleOptionsData;
+        }
+
+        public static RoleOptionsData Deserialize(IMessageReader reader)
+        {
+            var roleOptionsData = new RoleOptionsData();
+            var num = reader.ReadPackedInt32();
+            for (var i = 0; i < num; i++)
+            {
+                var key = (RoleTypes)reader.ReadInt16();
+                var roleRate = new RoleRate(reader.ReadByte(), reader.ReadByte());
+                roleOptionsData.RoleRates[key] = roleRate;
+            }
+
+            roleOptionsData.ShapeshifterLeaveSkin = reader.ReadBoolean();
+            roleOptionsData.ShapeshifterCooldown = reader.ReadByte();
+            roleOptionsData.ShapeshifterDuration = reader.ReadByte();
+            roleOptionsData.ScientistCooldown = reader.ReadByte();
+            roleOptionsData.GuardianAngelCooldown = reader.ReadByte();
+            roleOptionsData.EngineerCooldown = reader.ReadByte();
+            roleOptionsData.EngineerInVentMaxTime = reader.ReadByte();
+            roleOptionsData.ScientistBatteryCharge = reader.ReadByte();
+            roleOptionsData.ProtectionDurationSeconds = reader.ReadByte();
+            roleOptionsData.ImpostorsCanSeeProtect = reader.ReadBoolean();
+            return roleOptionsData;
+        }
+
+        public void Serialize(IMessageWriter writer)
+        {
+            writer.WritePacked(RoleRates.Count);
+            foreach (var roleRate in RoleRates)
+            {
+                writer.Write((ushort)roleRate.Key);
+                writer.Write((byte)roleRate.Value.MaxCount);
+                writer.Write((byte)roleRate.Value.Chance);
+            }
+
+            writer.Write(ShapeshifterLeaveSkin);
+            writer.Write((byte)ShapeshifterCooldown);
+            writer.Write((byte)ShapeshifterDuration);
+            writer.Write((byte)ScientistCooldown);
+            writer.Write((byte)GuardianAngelCooldown);
+            writer.Write((byte)EngineerCooldown);
+            writer.Write((byte)EngineerInVentMaxTime);
+            writer.Write((byte)ScientistBatteryCharge);
+            writer.Write((byte)ProtectionDurationSeconds);
+            writer.Write(ImpostorsCanSeeProtect);
+        }
+
+        public void Serialize(BinaryWriter writer)
+        {
+            writer.Write(RoleRates.Count);
+            foreach (var roleRate in RoleRates)
+            {
+                writer.Write((ushort)roleRate.Key);
+                writer.Write((byte)roleRate.Value.MaxCount);
+                writer.Write((byte)roleRate.Value.Chance);
+            }
+
+            writer.Write(ShapeshifterLeaveSkin);
+            writer.Write((byte)ShapeshifterCooldown);
+            writer.Write((byte)ShapeshifterDuration);
+            writer.Write((byte)ScientistCooldown);
+            writer.Write((byte)GuardianAngelCooldown);
+            writer.Write((byte)EngineerCooldown);
+            writer.Write((byte)EngineerInVentMaxTime);
+            writer.Write((byte)ScientistBatteryCharge);
+            writer.Write((byte)ProtectionDurationSeconds);
+            writer.Write(ImpostorsCanSeeProtect);
+        }
+
+        public readonly struct RoleRate
+        {
+            public readonly int MaxCount;
+            public readonly int Chance;
+
+            public RoleRate(int maxCount, int chance)
+            {
+                MaxCount = maxCount;
+                Chance = chance;
+            }
+        }
+    }
+}
diff --git a/src/Impostor.Api/Innersloth/RoleTypes.cs b/src/Impostor.Api/Innersloth/RoleTypes.cs
new file mode 100644 (file)
index 0000000..01b8778
--- /dev/null
@@ -0,0 +1,12 @@
+namespace Impostor.Api.Innersloth
+{
+    public enum RoleTypes : ushort
+    {
+        Crewmate,
+        Impostor,
+        Scientist,
+        Engineer,
+        GuardianAngel,
+        Shapeshifter,
+    }
+}
index 711f878b4cba8a2033f40a4698454405010b7131..62beee00aad45f7b962be6a8a21951fc8e23bbc6 100644 (file)
@@ -45,29 +45,29 @@ namespace Impostor.Api.Net.Inner.Objects
         /// <returns>Task that must be awaited.</returns>
         ValueTask SetColorAsync(ColorType colorType);
 
-        /// <summary>
-        ///     Sets the hat of the current <see cref="IInnerPlayerControl" />.
-        ///     Visible to all players.
-        /// </summary>
-        /// <param name="hatType">An hat for the player.</param>
-        /// <returns>Task that must be awaited.</returns>
-        ValueTask SetHatAsync(HatType hatType);
-
-        /// <summary>
-        ///     Sets the pet of the current <see cref="IInnerPlayerControl" />.
-        ///     Visible to all players.
-        /// </summary>
-        /// <param name="petType">A pet for the player.</param>
-        /// <returns>Task that must be awaited.</returns>
-        ValueTask SetPetAsync(PetType petType);
-
-        /// <summary>
-        ///     Sets the skin of the current <see cref="IInnerPlayerControl" />.
-        ///     Visible to all players.
-        /// </summary>
-        /// <param name="skinType">A skin for the player.</param>
-        /// <returns>Task that must be awaited.</returns>
-        ValueTask SetSkinAsync(SkinType skinType);
+        // /// <summary>
+        // ///     Sets the hat of the current <see cref="IInnerPlayerControl" />.
+        // ///     Visible to all players.
+        // /// </summary>
+        // /// <param name="hatId">An hat for the player.</param>
+        // /// <returns>Task that must be awaited.</returns>
+        // ValueTask SetHatAsync(string hatId);
+        //
+        // /// <summary>
+        // ///     Sets the pet of the current <see cref="IInnerPlayerControl" />.
+        // ///     Visible to all players.
+        // /// </summary>
+        // /// <param name="petId">A pet for the player.</param>
+        // /// <returns>Task that must be awaited.</returns>
+        // ValueTask SetPetAsync(string petId);
+        //
+        // /// <summary>
+        // ///     Sets the skin of the current <see cref="IInnerPlayerControl" />.
+        // ///     Visible to all players.
+        // /// </summary>
+        // /// <param name="skinId">A skin for the player.</param>
+        // /// <returns>Task that must be awaited.</returns>
+        // ValueTask SetSkinAsync(string skinId);
 
         /// <summary>
         ///     Send a chat message as the current <see cref="IInnerPlayerControl" />.
index 310755ed2fdb454001db443bf5a33a57eb9a3cf1..6bf5c44ca24b64aa1be15041c836913fbb3ed4a6 100644 (file)
@@ -12,26 +12,6 @@ namespace Impostor.Api.Net.Inner.Objects
         /// </summary>
         string PlayerName { get; }
 
-        /// <summary>
-        ///     Gets the color of the player.
-        /// </summary>
-        ColorType Color { get; }
-
-        /// <summary>
-        ///     Gets the hat of the player.
-        /// </summary>
-        HatType Hat { get; }
-
-        /// <summary>
-        ///     Gets the pet of the player.
-        /// </summary>
-        PetType Pet { get; }
-
-        /// <summary>
-        ///     Gets the skin of the player.
-        /// </summary>
-        SkinType Skin { get; }
-
         /// <summary>
         ///     Gets a value indicating whether the player is an impostor.
         /// </summary>
@@ -42,6 +22,12 @@ namespace Impostor.Api.Net.Inner.Objects
         /// </summary>
         bool IsDead { get; }
 
+        Dictionary<PlayerOutfitType, PlayerOutfit> Outfits { get; }
+
+        PlayerOutfitType CurrentOutfitType { get; set; }
+
+        PlayerOutfit CurrentOutfit { get; }
+
         /// <summary>
         ///     Gets the reason why the player is dead in the current game.
         /// </summary>
index 1fe232784717a00b059feb153f6ffad2984895a8..2067a5b0f105a29e9a94b85f2fb88ea27353ee79 100644 (file)
         SetName = 6,
         CheckColor = 7,
         SetColor = 8,
-        SetHat = 9,
-        SetSkin = 10,
         ReportDeadBody = 11,
         MurderPlayer = 12,
         SendChat = 13,
         StartMeeting = 14,
         SetScanner = 15,
         SendChatNote = 16,
-        SetPet = 17,
         SetStartCounter = 18,
         EnterVent = 19,
         ExitVent = 20,
         SendQuickChat = 33,
         BootFromVent = 34,
         UpdateSystem = 35,
+        SetLevel = 38,
+        SetHat = 39,
+        SetSkin = 40,
+        SetPet = 41,
+        SetVisor = 42,
+        SetNamePlate = 43,
+        SetRole = 44,
+        ProtectPlayer = 45,
+        Shapeshift = 46,
+        CheckMurder = 47,
+        CheckProtect = 48,
     }
 }
index 83fd647a586cd50c0208a1306f419fde798a97c4..60dd4e18a59645c41926fd09e61441df1bd1e101 100644 (file)
@@ -4,18 +4,14 @@ namespace Impostor.Api.Net.Messages.C2S
 {
     public static class HandshakeC2S
     {
-        public static void Deserialize(IMessageReader reader, out int clientVersion, out string name, out uint? lastNonceReceived, out Language language, out QuickChatModes chatMode)
+        public static void Deserialize(IMessageReader reader, out int clientVersion, out string name, out Language language, out QuickChatModes chatMode, out PlatformSpecificData? platformSpecificData)
         {
             clientVersion = reader.ReadInt32();
             name = reader.ReadString();
 
             if (clientVersion >= Version.V1)
             {
-                lastNonceReceived = reader.ReadUInt32();
-            }
-            else
-            {
-                lastNonceReceived = null;
+                reader.ReadUInt32(); // lastNonceReceived, always 0 since 2021.11.9
             }
 
             if (clientVersion >= Version.V2)
@@ -28,6 +24,17 @@ namespace Impostor.Api.Net.Messages.C2S
                 language = Language.English;
                 chatMode = QuickChatModes.FreeChatOrQuickChat;
             }
+
+            if (clientVersion >= Version.V3)
+            {
+                using var platformReader = reader.ReadMessage();
+                platformSpecificData = new PlatformSpecificData(platformReader);
+                reader.ReadInt32(); // crossplayFlags, not used yet
+            }
+            else
+            {
+                platformSpecificData = null;
+            }
         }
 
         private static class Version
@@ -35,6 +42,8 @@ namespace Impostor.Api.Net.Messages.C2S
             public static readonly int V1 = GameVersion.GetVersion(2021, 4, 25);
 
             public static readonly int V2 = GameVersion.GetVersion(2021, 6, 30);
+
+            public static readonly int V3 = GameVersion.GetVersion(2021, 11, 9);
         }
     }
 }
index 2a61a1832b9fd1e1b37557d07b31d38d2ae7acc7..87f32e990af06ac7ab317a47a1715e66507ce91d 100644 (file)
@@ -8,6 +8,7 @@ namespace Impostor.Api.Net.Messages.C2S
         {
             writer.StartMessage(MessageFlags.HostGame);
             gameOptionsData.Serialize(writer);
+            writer.Write(int.MaxValue); // crossplayFlags
             writer.EndMessage();
         }
 
@@ -15,12 +16,11 @@ namespace Impostor.Api.Net.Messages.C2S
         ///     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)
+        public static GameOptionsData Deserialize(IMessageReader reader)
         {
             var gameOptionsData = GameOptionsData.DeserializeCreate(reader);
-            chatMode = (QuickChatModes)reader.ReadByte();
+            reader.ReadInt32(); // crossplayFlags, not used yet
 
             return gameOptionsData;
         }
index e7771e7c9ab99c3b647edebf899324388f9f5d7c..9c91ac12e49f1ba16ad49e2f3a4509be60fbad3f 100644 (file)
@@ -13,6 +13,7 @@ namespace Impostor.Api.Net.Messages.C2S
         public static void Deserialize(IMessageReader reader, out GameCode gameCode)
         {
             gameCode = reader.ReadInt32();
+            reader.ReadBoolean(); // no crossplay
         }
     }
 }
index 01237678d4dfb8f9618fe67557c8b9af64db70c1..13c02ae10253643335d19b434658c8a0b30d1de3 100644 (file)
@@ -12,9 +12,15 @@ namespace Impostor.Api.Net.Messages.C2S
 
         public static void Deserialize(IMessageReader reader, out GameOptionsData options, out QuickChatModes chatMode)
         {
-            reader.ReadPackedInt32(); // Hardcoded 0.
+            var version = reader.ReadPackedInt32();
+            if (version != 2)
+            {
+                throw new NotSupportedException($"Version {version} of {nameof(Message16GetGameListC2S)} is not supported");
+            }
+
             options = GameOptionsData.DeserializeCreate(reader);
             chatMode = (QuickChatModes)reader.ReadByte();
+            reader.ReadInt32(); // crossplayFlags, not used yet
         }
     }
 }
index 397ad5adbbaa1cd9d42058f646375cae02749fe9..7d6c25d7f069507b66d6157915c30662630f2b40 100644 (file)
@@ -48,6 +48,10 @@ namespace Impostor.Api.Net.Messages
 
         int ReadInt32();
 
+        ulong ReadUInt64();
+
+        long ReadInt64();
+
         float ReadSingle();
 
         string ReadString(int length);
index 346e6e475386acb9d77ba37578f4d444273a545c..9ee85d06f4740ccfdc547737804a09fd4ecd9e9a 100644 (file)
@@ -68,6 +68,18 @@ namespace Impostor.Api.Net.Messages
         /// <param name="value">Value to write.</param>
         void Write(int value);
 
+        /// <summary>
+        ///     Writes an ulong to the message.
+        /// </summary>
+        /// <param name="value">Value to write.</param>
+        public void Write(ulong value);
+
+        /// <summary>
+        ///     Writes an ulong to the message.
+        /// </summary>
+        /// <param name="value">Value to write.</param>
+        public void Write(long value);
+
         /// <summary>
         ///     Writes a float to the message.
         /// </summary>
index 2ebf475dcc1887f6ec2947717b9f495e1f2b44ce..a7f5272a5c504525fba308c43250e6b585488e22 100644 (file)
         public const byte WaitForHost = 12;
         public const byte Redirect = 13;
         public const byte ReselectServer = 14;
-        public const byte GetGameList = 9;
         public const byte GetGameListV2 = 16;
+        public const byte ReportPlayer = 17;
+        public const byte QuickMatch = 18;
+        public const byte QuickMatchHost = 19;
+        public const byte SetGameSession = 20;
+        public const byte SetActivePodType = 21;
+        public const byte QueryPlatformIds = 22;
+        public const byte QueryLobbyInfo = 23;
     }
 }
diff --git a/src/Impostor.Api/Net/Messages/Rpcs/Rpc09SetHat.cs b/src/Impostor.Api/Net/Messages/Rpcs/Rpc09SetHat.cs
deleted file mode 100644 (file)
index caa9ff7..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-using Impostor.Api.Innersloth.Customization;
-
-namespace Impostor.Api.Net.Messages.Rpcs
-{
-    public static class Rpc09SetHat
-    {
-        public static void Serialize(IMessageWriter writer, HatType hat)
-        {
-            writer.WritePacked((uint)hat);
-        }
-
-        public static void Deserialize(IMessageReader reader, out HatType hat)
-        {
-            hat = (HatType)reader.ReadPackedUInt32();
-        }
-    }
-}
diff --git a/src/Impostor.Api/Net/Messages/Rpcs/Rpc10SetSkin.cs b/src/Impostor.Api/Net/Messages/Rpcs/Rpc10SetSkin.cs
deleted file mode 100644 (file)
index e57eeff..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-using Impostor.Api.Innersloth.Customization;
-
-namespace Impostor.Api.Net.Messages.Rpcs
-{
-    public static class Rpc10SetSkin
-    {
-        public static void Serialize(IMessageWriter writer, SkinType skin)
-        {
-            writer.WritePacked((uint)skin);
-        }
-
-        public static void Deserialize(IMessageReader reader, out SkinType skin)
-        {
-            skin = (SkinType)reader.ReadPackedUInt32();
-        }
-    }
-}
diff --git a/src/Impostor.Api/Net/Messages/Rpcs/Rpc17SetPet.cs b/src/Impostor.Api/Net/Messages/Rpcs/Rpc17SetPet.cs
deleted file mode 100644 (file)
index be67df5..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-using Impostor.Api.Innersloth.Customization;
-
-namespace Impostor.Api.Net.Messages.Rpcs
-{
-    public static class Rpc17SetPet
-    {
-        public static void Serialize(IMessageWriter writer, PetType pet)
-        {
-            writer.WritePacked((uint)pet);
-        }
-
-        public static void Deserialize(IMessageReader reader, out PetType pet)
-        {
-            pet = (PetType)reader.ReadPackedUInt32();
-        }
-    }
-}
diff --git a/src/Impostor.Api/Net/Messages/Rpcs/Rpc39SetHat.cs b/src/Impostor.Api/Net/Messages/Rpcs/Rpc39SetHat.cs
new file mode 100644 (file)
index 0000000..6aa1399
--- /dev/null
@@ -0,0 +1,15 @@
+namespace Impostor.Api.Net.Messages.Rpcs
+{
+    public static class Rpc39SetHat
+    {
+        public static void Serialize(IMessageWriter writer, string hat)
+        {
+            writer.Write(hat);
+        }
+
+        public static void Deserialize(IMessageReader reader, out string hat)
+        {
+            hat = reader.ReadString();
+        }
+    }
+}
diff --git a/src/Impostor.Api/Net/Messages/Rpcs/Rpc40SetSkin.cs b/src/Impostor.Api/Net/Messages/Rpcs/Rpc40SetSkin.cs
new file mode 100644 (file)
index 0000000..929dab2
--- /dev/null
@@ -0,0 +1,15 @@
+namespace Impostor.Api.Net.Messages.Rpcs
+{
+    public static class Rpc40SetSkin
+    {
+        public static void Serialize(IMessageWriter writer, string skin)
+        {
+            writer.Write(skin);
+        }
+
+        public static void Deserialize(IMessageReader reader, out string skin)
+        {
+            skin = reader.ReadString();
+        }
+    }
+}
diff --git a/src/Impostor.Api/Net/Messages/Rpcs/Rpc41SetPet.cs b/src/Impostor.Api/Net/Messages/Rpcs/Rpc41SetPet.cs
new file mode 100644 (file)
index 0000000..a3ddde1
--- /dev/null
@@ -0,0 +1,15 @@
+namespace Impostor.Api.Net.Messages.Rpcs
+{
+    public static class Rpc41SetPet
+    {
+        public static void Serialize(IMessageWriter writer, string pet)
+        {
+            writer.Write(pet);
+        }
+
+        public static void Deserialize(IMessageReader reader, out string pet)
+        {
+            pet = reader.ReadString();
+        }
+    }
+}
index 8dd5c7f94780a88db746045ff714f7643d26b98c..3e334bc27f459f7ba6ffc1318070baa87f291f85 100644 (file)
@@ -39,7 +39,7 @@ namespace Impostor.Api.Net.Messages.S2C
 
             if (hasReason)
             {
-                var inner = reader.ReadMessage();
+                using var inner = reader.ReadMessage();
                 reason = (DisconnectReason)inner.ReadByte();
                 message = reason == DisconnectReason.Custom ? inner.ReadString() : null;
             }
index 8de027461e59af67fb9dc0d9b925a88ad4a01d26..74070b8c0798f9a8889d65d3bf5b54f2ef55f4c3 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 8de027461e59af67fb9dc0d9b925a88ad4a01d26
+Subproject commit 74070b8c0798f9a8889d65d3bf5b54f2ef55f4c3
index e755a1ccc6fa5656fbd33be56959132061e8d46a..7e42f61f460533a40b525e0a53c3c3b19baf4977 100644 (file)
@@ -24,8 +24,8 @@ namespace Impostor.Server.Net
         private readonly GameManager _gameManager;
         private readonly ICustomMessageManager<ICustomRootMessage> _customMessageManager;
 
-        public Client(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, GameManager gameManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, string name, int gameVersion, Language language, QuickChatModes chatMode, IHazelConnection connection)
-            : base(name, gameVersion, language, chatMode, connection)
+        public Client(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, GameManager gameManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, string name, int gameVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData, IHazelConnection connection)
+            : base(name, gameVersion, language, chatMode, platformSpecificData, connection)
         {
             _logger = logger;
             _antiCheatConfig = antiCheatOptions.Value;
@@ -64,7 +64,7 @@ namespace Impostor.Server.Net
                 case MessageFlags.HostGame:
                 {
                     // Read game settings.
-                    var gameInfo = Message00HostGameC2S.Deserialize(reader, out _);
+                    var gameInfo = Message00HostGameC2S.Deserialize(reader);
 
                     // Create game.
                     var game = await _gameManager.CreateAsync(this, gameInfo);
index c1a63825c69ce599497ef9302844694631a07bab..74224fcc4475bbe8d18d1b88d161f31eea110dee 100644 (file)
@@ -13,12 +13,13 @@ namespace Impostor.Server.Net
 {
     internal abstract class ClientBase : IClient
     {
-        protected ClientBase(string name, int gameVersion, Language language, QuickChatModes chatMode, IHazelConnection connection)
+        protected ClientBase(string name, int gameVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData, IHazelConnection connection)
         {
             Name = name;
             GameVersion = gameVersion;
             Language = language;
             ChatMode = chatMode;
+            PlatformSpecificData = platformSpecificData;
             Connection = connection;
             Items = new ConcurrentDictionary<object, object>();
         }
@@ -31,6 +32,8 @@ namespace Impostor.Server.Net
 
         public QuickChatModes ChatMode { get; }
 
+        public PlatformSpecificData PlatformSpecificData { get; }
+
         public int GameVersion { get; }
 
         public IHazelConnection Connection { get; }
index 09e55d998dc140feca83f81044a862503f42d5ea..5a7e66604712bc47c7811023d95c1003519d3b71 100644 (file)
@@ -15,9 +15,9 @@ namespace Impostor.Server.Net.Factories
             _serviceProvider = serviceProvider;
         }
 
-        public ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode)
+        public ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData)
         {
-            var client = ActivatorUtilities.CreateInstance<TClient>(_serviceProvider, name, clientVersion, language, chatMode, connection);
+            var client = ActivatorUtilities.CreateInstance<TClient>(_serviceProvider, name, clientVersion, language, chatMode, platformSpecificData, connection);
             connection.Client = client;
             return client;
         }
index d0d392d12e12dcb683e1b27169e32b5017ecaea9..ccccb33fbaf460c2ec6549a7928f7640608ca5e5 100644 (file)
@@ -5,6 +5,6 @@ namespace Impostor.Server.Net.Factories
 {
     internal interface IClientFactory
     {
-        ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode);
+        ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData);
     }
 }
index 86633e46342f27865349b19a3f83b596c1219934..001ecf8fdf93081e62ea9a13d966b81c9507c9e2 100644 (file)
@@ -125,6 +125,10 @@ namespace Impostor.Server.Net.Inner.Objects
                     }
 
                     Rpc23VotingComplete.Deserialize(reader, out var states, out var playerId, out var tie);
+                    foreach (var messageReader in states)
+                    {
+                        messageReader.Dispose();
+                    }
 
                     // This would be a nice place to implement an anti cheat.
                     // But for whatever reason host sends VotingComplete before sending his vote.
index 8804bee9006f3dd8d44718f00cf65902074e9106..c2fb8cec934d0d618eeb4001392d3fe5b56397df 100644 (file)
@@ -29,40 +29,13 @@ namespace Impostor.Server.Net.Inner.Objects
 
         public async ValueTask SetColorAsync(ColorType color)
         {
-            PlayerInfo.Color = color;
+            PlayerInfo.CurrentOutfit.Color = color;
 
             using var writer = Game.StartRpc(NetId, RpcCalls.SetColor);
             Rpc08SetColor.Serialize(writer, color);
             await Game.FinishRpcAsync(writer);
         }
 
-        public async ValueTask SetHatAsync(HatType hat)
-        {
-            PlayerInfo.Hat = hat;
-
-            using var writer = Game.StartRpc(NetId, RpcCalls.SetHat);
-            Rpc09SetHat.Serialize(writer, hat);
-            await Game.FinishRpcAsync(writer);
-        }
-
-        public async ValueTask SetPetAsync(PetType pet)
-        {
-            PlayerInfo.Pet = pet;
-
-            using var writer = Game.StartRpc(NetId, RpcCalls.SetPet);
-            Rpc17SetPet.Serialize(writer, pet);
-            await Game.FinishRpcAsync(writer);
-        }
-
-        public async ValueTask SetSkinAsync(SkinType skin)
-        {
-            PlayerInfo.Skin = skin;
-
-            using var writer = Game.StartRpc(NetId, RpcCalls.SetSkin);
-            Rpc10SetSkin.Serialize(writer, skin);
-            await Game.FinishRpcAsync(writer);
-        }
-
         public async ValueTask SendChatAsync(string text)
         {
             using var writer = Game.StartRpc(NetId, RpcCalls.SendChat);
index 8de79ed829af38ce1b8f61f2a24bf65bef3c788d..8969d5ef7a051b7ea371f6f62f87cfb96953bff0 100644 (file)
@@ -182,8 +182,8 @@ namespace Impostor.Server.Net.Inner.Objects
                         return false;
                     }
 
-                    Rpc09SetHat.Deserialize(reader, out var hat);
-                    return await HandleSetHat(sender, hat);
+                    Rpc39SetHat.Deserialize(reader, out var hat);
+                    return true;
                 }
 
                 case RpcCalls.SetSkin:
@@ -193,8 +193,41 @@ namespace Impostor.Server.Net.Inner.Objects
                         return false;
                     }
 
-                    Rpc10SetSkin.Deserialize(reader, out var skin);
-                    return await HandleSetSkin(sender, skin);
+                    Rpc40SetSkin.Deserialize(reader, out var skin);
+                    return true;
+                }
+
+                case RpcCalls.SetVisor:
+                {
+                    if (!await ValidateOwnership(call, sender))
+                    {
+                        return false;
+                    }
+
+                    // Rpc42SetVistor.Deserialize(reader, out var visor);
+                    return true;
+                }
+
+                case RpcCalls.SetNamePlate:
+                {
+                    if (!await ValidateOwnership(call, sender))
+                    {
+                        return false;
+                    }
+
+                    // Rpc43SetNamePlate.Deserialize(reader, out var namePlate);
+                    return true;
+                }
+
+                case RpcCalls.SetLevel:
+                {
+                    if (!await ValidateOwnership(call, sender))
+                    {
+                        return false;
+                    }
+
+                    // Rpc38SetLevel.Deserialize(reader, out var level);
+                    return true;
                 }
 
                 case RpcCalls.ReportDeadBody:
@@ -271,8 +304,9 @@ namespace Impostor.Server.Net.Inner.Objects
                         return false;
                     }
 
-                    Rpc17SetPet.Deserialize(reader, out var pet);
-                    return await HandleSetPet(sender, pet);
+                    Rpc41SetPet.Deserialize(reader, out var pet);
+                    return true;
+                    // return await HandleSetPet(sender, pet);
                 }
 
                 case RpcCalls.SetStartCounter:
@@ -343,7 +377,7 @@ namespace Impostor.Server.Net.Inner.Objects
                 var player = Game.GameNet.GameData!.GetPlayerById(infectedIds.Span[i]);
                 if (player != null)
                 {
-                    player.IsImpostor = true;
+                    // player.IsImpostor = true;
                 }
             }
 
@@ -479,7 +513,7 @@ namespace Impostor.Server.Net.Inner.Objects
 
             if (sender.IsOwner(this))
             {
-                if (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.Color == color))
+                if (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.CurrentOutfit.Color == color))
                 {
                     if (await sender.Client.ReportCheatAsync(RpcCalls.SetColor, "Client sent a color that is already used"))
                     {
@@ -497,7 +531,7 @@ namespace Impostor.Server.Net.Inner.Objects
 
                 var expected = RequestedColorId.Dequeue();
 
-                while (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.Color == expected))
+                while (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.CurrentOutfit.Color == expected))
                 {
                     expected = (ColorType)(((byte)expected + 1) % ColorsCount);
                 }
@@ -510,34 +544,34 @@ namespace Impostor.Server.Net.Inner.Objects
                 }
             }
 
-            PlayerInfo.Color = color;
-
-            return true;
-        }
-
-        private async ValueTask<bool> HandleSetHat(ClientPlayer sender, HatType hat)
-        {
-            if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetHat, "Client tried to change hat while not in lobby"))
-            {
-                return false;
-            }
-
-            PlayerInfo.Hat = hat;
+            PlayerInfo.CurrentOutfit.Color = color;
 
             return true;
         }
 
-        private async ValueTask<bool> HandleSetSkin(ClientPlayer sender, SkinType skin)
-        {
-            if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetSkin, "Client tried to change skin while not in lobby"))
-            {
-                return false;
-            }
-
-            PlayerInfo.Skin = skin;
-
-            return true;
-        }
+        // private async ValueTask<bool> HandleSetHat(ClientPlayer sender, HatType hat)
+        // {
+        //     if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetHat, "Client tried to change hat while not in lobby"))
+        //     {
+        //         return false;
+        //     }
+        //
+        //     PlayerInfo.Hat = hat;
+        //
+        //     return true;
+        // }
+        //
+        // private async ValueTask<bool> HandleSetSkin(ClientPlayer sender, SkinType skin)
+        // {
+        //     if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetSkin, "Client tried to change skin while not in lobby"))
+        //     {
+        //         return false;
+        //     }
+        //
+        //     PlayerInfo.Skin = skin;
+        //
+        //     return true;
+        // }
 
         private async ValueTask<bool> HandleMurderPlayer(ClientPlayer sender, IInnerPlayerControl? target)
         {
@@ -582,17 +616,17 @@ namespace Impostor.Server.Net.Inner.Objects
             await _eventManager.CallAsync(new PlayerStartMeetingEvent(Game, Game.GetClientPlayer(this.OwnerId)!, this, deadPlayer));
         }
 
-        private async ValueTask<bool> HandleSetPet(ClientPlayer sender, PetType pet)
-        {
-            if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetPet, "Client tried to change pet while not in lobby"))
-            {
-                return false;
-            }
-
-            PlayerInfo.Pet = pet;
-
-            return true;
-        }
+        // private async ValueTask<bool> HandleSetPet(ClientPlayer sender, PetType pet)
+        // {
+        //     if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetPet, "Client tried to change pet while not in lobby"))
+        //     {
+        //         return false;
+        //     }
+        //
+        //     PlayerInfo.Pet = pet;
+        //
+        //     return true;
+        // }
 
         private async ValueTask<bool> HandleSetStartCounter(ClientPlayer sender, int sequenceId, sbyte startCounter)
         {
index 28221cc1cd6551ab0d00f6727200edfb71076796..553dddad38c08adc7518a569b01c2c50a32dea56 100644 (file)
@@ -21,17 +21,20 @@ namespace Impostor.Server.Net.Inner.Objects
 
         public string PlayerName { get; internal set; } = string.Empty;
 
-        public ColorType Color { get; internal set; } = (ColorType)(-1);
+        public Dictionary<PlayerOutfitType, PlayerOutfit> Outfits { get; } = new()
+        {
+            [PlayerOutfitType.Default] = new PlayerOutfit(),
+        };
 
-        public HatType Hat { get; internal set; }
+        public PlayerOutfitType CurrentOutfitType { get; set; } = PlayerOutfitType.Default;
 
-        public PetType Pet { get; internal set; }
+        public PlayerOutfit CurrentOutfit => Outfits[CurrentOutfitType];
 
-        public SkinType Skin { get; internal set; }
+        public RoleTypes RoleType { get; internal set; }
 
         public bool Disconnected { get; internal set; }
 
-        public bool IsImpostor { get; internal set; }
+        public bool IsImpostor => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter;
 
         public bool IsDead { get; internal set; }
 
@@ -58,16 +61,23 @@ namespace Impostor.Server.Net.Inner.Objects
 
         public void Deserialize(IMessageReader reader)
         {
-            PlayerName = reader.ReadString();
-            Color = (ColorType)reader.ReadPackedInt32();
-            Hat = (HatType)reader.ReadPackedUInt32();
-            Pet = (PetType)reader.ReadPackedUInt32();
-            Skin = (SkinType)reader.ReadPackedUInt32();
+            Outfits.Clear();
+            var b = reader.ReadByte();
+            for (var i = 0; i < b; i++)
+            {
+                var key = (PlayerOutfitType)reader.ReadByte();
+                Outfits[key] = new PlayerOutfit();
+                Outfits[key].Deserialize(reader);
+            }
+
+            var PlayerLevel = reader.ReadPackedUInt32();
 
             var flag = reader.ReadByte();
-            Disconnected = (flag & 1) > 0;
-            IsImpostor = (flag & 2) > 0;
-            IsDead = (flag & 4) > 0;
+            Disconnected = (flag & 1) != 0;
+            IsDead = (flag & 4) != 0;
+
+            var roleType = (RoleTypes)reader.ReadUInt16();
+            // Role = roleType;
 
             var taskCount = reader.ReadByte();
 
index 63e34985d2667b7154ec192d29bf9510b3689645..0c1c2f1a66c2b0cf06378378aac6da526a602eb8 100644 (file)
@@ -22,7 +22,7 @@ namespace Impostor.Server.Net.Manager
         // NOTE: when updating this array, keep the versions ordered from old to new, otherwise the version compare logic doesn't work properly
         private static readonly int[] SupportedVersions =
         {
-            GameVersion.GetVersion(2021, 6, 30), // 2021.6.30
+            GameVersion.GetVersion(2021, 11, 9), // 2021.11.9
         };
 
         private readonly ILogger<ClientManager> _logger;
@@ -65,10 +65,10 @@ namespace Impostor.Server.Net.Manager
             return clientId;
         }
 
-        public async ValueTask RegisterConnectionAsync(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode)
+        public async ValueTask RegisterConnectionAsync(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode, PlatformSpecificData? platformSpecificData)
         {
             var versionCompare = CompareVersion(clientVersion);
-            if (versionCompare != VersionCompareResult.Compatible)
+            if (versionCompare != VersionCompareResult.Compatible || platformSpecificData == null)
             {
                 GameVersion.ParseVersion(clientVersion, out var year, out var month, out var day, out var revision);
                 _logger.LogTrace("Client connected using unsupported version: {clientVersion} ({version})", clientVersion, $"{year}.{month}.{day}{(revision == 0 ? string.Empty : "." + revision)}");
@@ -105,7 +105,7 @@ namespace Impostor.Server.Net.Manager
                 return;
             }
 
-            var client = _clientFactory.Create(connection, name, clientVersion, language, chatMode);
+            var client = _clientFactory.Create(connection, name, clientVersion, language, chatMode, platformSpecificData);
             var id = NextId();
 
             client.Id = id;
index 3e9b1d01f0f4c99efe7bb8ea8816793643d646cb..a8682bf69d447f42229a7bfc178c1dfe38f53ecd 100644 (file)
@@ -62,14 +62,14 @@ namespace Impostor.Server.Net
         private async ValueTask OnNewConnection(NewConnectionEventArgs e)
         {
             // Handshake.
-            HandshakeC2S.Deserialize(e.HandshakeData, out var clientVersion, out var name, out _, out var language, out var chatMode);
+            HandshakeC2S.Deserialize(e.HandshakeData, out var clientVersion, out var name, out var language, out var chatMode, out var platformSpecificData);
 
             var connection = new HazelConnection(e.Connection, _connectionLogger);
 
             await _eventManager.CallAsync(new ClientConnectionEvent(connection, e.HandshakeData));
 
             // Register client
-            await _clientManager.RegisterConnectionAsync(connection, name, clientVersion, language, chatMode);
+            await _clientManager.RegisterConnectionAsync(connection, name, clientVersion, language, chatMode, platformSpecificData);
         }
     }
 }
index 4503648b34bfac67d9b1b80b948524e37a353cad..a96ca733e3ee00cd625d8048df05ecdac8c5d7db 100644 (file)
@@ -24,11 +24,12 @@ namespace Impostor.Server.Net.Redirector
             int gameVersion,
             Language language,
             QuickChatModes chatMode,
+            PlatformSpecificData platformSpecificData,
             HazelConnection connection,
             ClientManager clientManager,
             INodeProvider nodeProvider,
             INodeLocator nodeLocator)
-            : base(name, gameVersion, language, chatMode, connection)
+            : base(name, gameVersion, language, chatMode, platformSpecificData, connection)
         {
             _clientManager = clientManager;
             _nodeProvider = nodeProvider;
index 20fa59e790ec5c9540edf6921c1061f9d9a5c4e9..4acf2a5921d47ab28b8d6e996ea6e9ea686ed7f2 100644 (file)
@@ -18,8 +18,8 @@ namespace Impostor.Server.Recorder
         private bool _createdGame;
         private bool _recordAfter;
 
-        public ClientRecorder(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, GameManager gameManager, string name, int gameVersion, Language language, QuickChatModes chatMode, HazelConnection connection, PacketRecorder recorder)
-            : base(logger, antiCheatOptions, clientManager, gameManager, customMessageManager, name, gameVersion, language, chatMode, connection)
+        public ClientRecorder(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, GameManager gameManager, string name, int gameVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData, HazelConnection connection, PacketRecorder recorder)
+            : base(logger, antiCheatOptions, clientManager, gameManager, customMessageManager, name, gameVersion, language, chatMode, platformSpecificData, connection)
         {
             _recorder = recorder;
             _isFirst = true;