]> git.deb.at Git - rhonda/impostor.git/commitdiff
Workaround for early spawn of host player components
authorAeonLucid <aeonlucid@outlook.com>
Fri, 23 Oct 2020 00:54:32 +0000 (02:54 +0200)
committerAeonLucid <aeonlucid@outlook.com>
Fri, 23 Oct 2020 00:54:32 +0000 (02:54 +0200)
src/Impostor.Api/Net/IClient.cs
src/Impostor.Server/Net/Client.cs
src/Impostor.Server/Net/ClientBase.cs
src/Impostor.Server/Net/State/ClientPlayer.cs
src/Impostor.Server/Net/State/Game.Data.cs
src/Impostor.Server/Net/State/Game.Outgoing.cs
src/Impostor.Server/Net/State/Game.State.cs

index b882d2a95a082221b3a38c144c6e50d57e480ecb..0b9b77d3eac13548ee4ac726a35185399ad317e8 100644 (file)
@@ -34,11 +34,6 @@ namespace Impostor.Api.Net
         /// </remarks>
         IHazelConnection? Connection { get; }
 
-        /// <summary>
-        ///     Gets a value indicating whether the client is a bot.
-        /// </summary>
-        bool IsBot { get; }
-
         /// <summary>
         ///     Gets a key/value collection that can be used to share data between messages.
         /// </summary>
index e1e35128273da40796215806de3623a0221c43e9..860af7ae3e4332a9898717d22e16cdafab247e2c 100644 (file)
@@ -149,21 +149,23 @@ namespace Impostor.Server.Net
                     var readerCopy = reader.Slice(reader.Position);
 
                     // TODO: Return value, either a bool (to cancel) or a writer (to cancel (null) or modify/overwrite).
-                    await Player.Game.HandleGameDataAsync(readerCopy, Player, toPlayer);
-
-                    // Broadcast packet to all other players.
-                    using (var writer = MessageWriter.Get(messageType))
+                    var verified = await Player.Game.HandleGameDataAsync(readerCopy, Player, toPlayer);
+                    if (verified)
                     {
-                        if (toPlayer)
-                        {
-                            var target = reader.ReadPackedInt32();
-                            reader.CopyTo(writer);
-                            await Player.Game.SendToAsync(writer, target);
-                        }
-                        else
+                        // Broadcast packet to all other players.
+                        using (var writer = MessageWriter.Get(messageType))
                         {
-                            reader.CopyTo(writer);
-                            await Player.Game.SendToAllExceptAsync(writer, Id);
+                            if (toPlayer)
+                            {
+                                var target = reader.ReadPackedInt32();
+                                reader.CopyTo(writer);
+                                await Player.Game.SendToAsync(writer, target);
+                            }
+                            else
+                            {
+                                reader.CopyTo(writer);
+                                await Player.Game.SendToAllExceptAsync(writer, Id);
+                            }
                         }
                     }
 
index de23a39d0dd12a55f9b520f61cc1bed85a37822e..b56d08f9611cc445a6704db2c736b382dd27d38f 100644 (file)
@@ -22,8 +22,6 @@ namespace Impostor.Server.Net
 
         public IHazelConnection Connection { get; }
 
-        public bool IsBot => false;
-
         public IDictionary<object, object> Items { get; }
 
         public ClientPlayer Player { get; set; }
index 7d54f12b5402d63383aaef5505c5cfacdf5e7c5e..be4bbe3cdc2c1d63cb3a857fd9039f39003684b5 100644 (file)
@@ -19,6 +19,8 @@ namespace Impostor.Server.Net.State
         /// <inheritdoc />
         public LimboStates Limbo { get; set; }
 
+        public bool IsHost => Game?.Host == this;
+
         /// <inheritdoc />
         public ValueTask KickAsync()
         {
index 66a331c30e555ea1253e997caf18ddf9f3b51b8c..673a74019e5c313f7dfc01ec9fa799857f30a144 100644 (file)
@@ -1,9 +1,15 @@
 using System;
 using System.Collections.Generic;
+using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading.Tasks;
+using Impostor.Api;
+using Impostor.Api.Innersloth.Data;
 using Impostor.Api.Innersloth.GameData;
 using Impostor.Api.Net.Messages;
+using Impostor.Api.Net.Messages.C2S;
+using Impostor.Api.Net.Messages.S2C;
+using Impostor.Hazel;
 using Impostor.Server.GameData;
 using Impostor.Server.GameData.Objects;
 using Microsoft.Extensions.DependencyInjection;
@@ -13,6 +19,19 @@ namespace Impostor.Server.Net.State
 {
     internal partial class Game
     {
+        private const int FakeClientId = int.MaxValue - 1;
+
+        /// <summary>
+        ///     Used for global object, spawned by the host.
+        /// </summary>
+        private const int InvalidClient = -2;
+
+        /// <summary>
+        ///     Used internally to set the OwnerId to the current ClientId.
+        ///     i.e: <code>ownerId = ownerId == -3 ? this.ClientId : ownerId;</code>
+        /// </summary>
+        private const int CurrentClient = -3;
+
         private static readonly Type[] SpawnableObjects =
         {
             typeof(InnerShipStatus), // ShipStatus
@@ -29,18 +48,43 @@ namespace Impostor.Server.Net.State
         private readonly Dictionary<uint, InnerNetObject> _allObjectsFast = new Dictionary<uint, InnerNetObject>();
 
         private int _gamedataInitialized;
+        private bool _gamedataFakeReceived;
 
-        public void InitGameData()
+        private async ValueTask InitGameDataAsync(ClientPlayer player)
         {
             if (Interlocked.Exchange(ref _gamedataInitialized, 1) != 0)
             {
                 return;
             }
 
+            /*
+             * The Among Us client on 20.9.22i spawns some components on the host side and
+             * only spawns these on other clients when someone else connects. This means that we can't
+             * parse data until someone connects because we don't know which component belongs to the NetId.
+             *
+             * We solve this by spawning a fake player and removing the player when the spawn GameData
+             * is received in HandleGameDataAsync.
+             */
+            using (var message = MessageWriter.Get(MessageType.Reliable))
+            {
+                _logger.LogInformation("Sending join..");
 
+                // Spawn a fake player.
+                Message01JoinGameS2C.SerializeJoin(message, false, Code, FakeClientId, HostId);
+
+                message.StartMessage(MessageFlags.GameData);
+                message.Write(Code);
+                message.StartMessage(GameDataTag.SceneChangeFlag);
+                message.WritePacked(FakeClientId);
+                message.Write("OnlineGame");
+                message.EndMessage();
+                message.EndMessage();
+
+                await player.Client.Connection.SendAsync(message);
+            }
         }
 
-        public async ValueTask HandleGameDataAsync(IMessageReader parent, ClientPlayer sender, bool toPlayer)
+        public async ValueTask<bool> HandleGameDataAsync(IMessageReader parent, ClientPlayer sender, bool toPlayer)
         {
             // Find target player.
             ClientPlayer target = null;
@@ -48,11 +92,25 @@ namespace Impostor.Server.Net.State
             if (toPlayer)
             {
                 var targetId = parent.ReadPackedInt32();
-                if (!TryGetPlayer(targetId, out target))
+                if (targetId == FakeClientId && !_gamedataFakeReceived && sender.IsHost)
+                {
+                    _gamedataFakeReceived = true;
+
+                    // Remove the fake client, we received the data.
+                    using (var message = MessageWriter.Get(MessageType.Reliable))
+                    {
+                        WriteRemovePlayerMessage(message, false, FakeClientId, (byte)DisconnectReason.ExitGame);
+
+                        await sender.Client.Connection.SendAsync(message);
+                    }
+                }
+                else if (!TryGetPlayer(targetId, out target))
                 {
-                    // Invalid target.
-                    return;
+                    _logger.LogWarning("Player {0} tried to send GameData to unknown player {1}.", sender.Client.Id, targetId);
+                    return false;
                 }
+
+                _logger.LogTrace("Received GameData for target {0}.", targetId);
             }
 
             // Parse GameData messages.
@@ -79,16 +137,28 @@ namespace Impostor.Server.Net.State
 
                     case GameDataTag.RpcFlag:
                     {
-                        if (_allObjectsFast.TryGetValue(reader.ReadPackedUInt32(), out var obj))
+                        var netId = reader.ReadPackedUInt32();
+                        if (_allObjectsFast.TryGetValue(netId, out var obj))
                         {
+                            // Console.WriteLine(reader.ReadByte());
                             // obj.HandleRpc(reader.ReadByte(), reader);
                         }
+                        else
+                        {
+                            _logger.LogWarning("Received RpcFlag for unregistered NetId {0}.", netId);
+                        }
 
                         break;
                     }
 
                     case GameDataTag.SpawnFlag:
                     {
+                        if (!sender.IsHost)
+                        {
+                            _logger.LogWarning("Player {0} ({1}) tried to send spawn packet as non-host.", sender.Client.Name, sender.Client.Id);
+                            return false;
+                        }
+
                         var objectId = reader.ReadPackedUInt32();
                         if (objectId < SpawnableObjects.Length)
                         {
@@ -111,6 +181,11 @@ namespace Impostor.Server.Net.State
                                 continue;
                             }
 
+                            _logger.LogTrace(
+                                "Spawning {0} components, SpawnFlags {1}",
+                                innerNetObject.GetType().Name,
+                                innerNetObject.SpawnFlags);
+
                             for (var i = 0; i < componentsCount; i++)
                             {
                                 var obj = components[i];
@@ -118,6 +193,12 @@ namespace Impostor.Server.Net.State
                                 obj.NetId = reader.ReadPackedUInt32();
                                 obj.OwnerId = id;
 
+                                _logger.LogTrace(
+                                    "- {0}, NetId {1}, OwnerId {2}",
+                                    obj.GetType().Name,
+                                    obj.NetId,
+                                    obj.OwnerId);
+
                                 if (!AddNetObject(obj))
                                 {
                                     _logger.LogTrace("Failed to AddNetObject.");
@@ -181,6 +262,8 @@ namespace Impostor.Server.Net.State
                     }
                 }
             }
+
+            return true;
         }
 
         private bool AddNetObject(InnerNetObject obj)
index 57e3a623eb0f26dad2e0990b5e95a20f6ac65473..ca77abefb2fe178a2c1c87e5d74da440998657c7 100644 (file)
@@ -1,11 +1,9 @@
-using System;
-using System.Linq;
+using System.Linq;
 using System.Threading.Tasks;
 using Impostor.Api.Innersloth.Data;
 using Impostor.Api.Net;
 using Impostor.Api.Net.Messages;
 using Impostor.Api.Net.Messages.S2C;
-using Impostor.Server.Net.Hazel;
 
 namespace Impostor.Server.Net.State
 {
index b5fcffb39a0ca74112bf6b3bffe196b3c24217ea..147a833bc3a1fa514317038201ffb05fe61799f5 100644 (file)
@@ -24,6 +24,7 @@ namespace Impostor.Server.Net.State
             if (HostId == -1)
             {
                 HostId = player.Client.Id;
+                await InitGameDataAsync(player);
             }
 
             await _eventManager.CallAsync(new PlayerJoinedGameEvent(this, player));
@@ -71,7 +72,7 @@ namespace Impostor.Server.Net.State
             // Pick the first player as new host.
             var host = _players
                 .Select(p => p.Value)
-                .FirstOrDefault(p => !p.Client.IsBot);
+                .FirstOrDefault();
 
             if (host == null)
             {