From: AeonLucid Date: Fri, 23 Oct 2020 00:54:32 +0000 (+0200) Subject: Workaround for early spawn of host player components X-Git-Tag: v1.2.2~96^2~46 X-Git-Url: https://git.deb.at/?a=commitdiff_plain;h=579eacc5219c0643db514802cc758c3dccd3095e;p=rhonda%2Fimpostor.git Workaround for early spawn of host player components --- diff --git a/src/Impostor.Api/Net/IClient.cs b/src/Impostor.Api/Net/IClient.cs index b882d2a..0b9b77d 100644 --- a/src/Impostor.Api/Net/IClient.cs +++ b/src/Impostor.Api/Net/IClient.cs @@ -34,11 +34,6 @@ namespace Impostor.Api.Net /// IHazelConnection? Connection { get; } - /// - /// Gets a value indicating whether the client is a bot. - /// - bool IsBot { get; } - /// /// Gets a key/value collection that can be used to share data between messages. /// diff --git a/src/Impostor.Server/Net/Client.cs b/src/Impostor.Server/Net/Client.cs index e1e3512..860af7a 100644 --- a/src/Impostor.Server/Net/Client.cs +++ b/src/Impostor.Server/Net/Client.cs @@ -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); + } } } diff --git a/src/Impostor.Server/Net/ClientBase.cs b/src/Impostor.Server/Net/ClientBase.cs index de23a39..b56d08f 100644 --- a/src/Impostor.Server/Net/ClientBase.cs +++ b/src/Impostor.Server/Net/ClientBase.cs @@ -22,8 +22,6 @@ namespace Impostor.Server.Net public IHazelConnection Connection { get; } - public bool IsBot => false; - public IDictionary Items { get; } public ClientPlayer Player { get; set; } diff --git a/src/Impostor.Server/Net/State/ClientPlayer.cs b/src/Impostor.Server/Net/State/ClientPlayer.cs index 7d54f12..be4bbe3 100644 --- a/src/Impostor.Server/Net/State/ClientPlayer.cs +++ b/src/Impostor.Server/Net/State/ClientPlayer.cs @@ -19,6 +19,8 @@ namespace Impostor.Server.Net.State /// public LimboStates Limbo { get; set; } + public bool IsHost => Game?.Host == this; + /// public ValueTask KickAsync() { diff --git a/src/Impostor.Server/Net/State/Game.Data.cs b/src/Impostor.Server/Net/State/Game.Data.cs index 66a331c..673a740 100644 --- a/src/Impostor.Server/Net/State/Game.Data.cs +++ b/src/Impostor.Server/Net/State/Game.Data.cs @@ -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; + + /// + /// Used for global object, spawned by the host. + /// + private const int InvalidClient = -2; + + /// + /// Used internally to set the OwnerId to the current ClientId. + /// i.e: ownerId = ownerId == -3 ? this.ClientId : ownerId; + /// + 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 _allObjectsFast = new Dictionary(); 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 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) diff --git a/src/Impostor.Server/Net/State/Game.Outgoing.cs b/src/Impostor.Server/Net/State/Game.Outgoing.cs index 57e3a62..ca77abe 100644 --- a/src/Impostor.Server/Net/State/Game.Outgoing.cs +++ b/src/Impostor.Server/Net/State/Game.Outgoing.cs @@ -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 { diff --git a/src/Impostor.Server/Net/State/Game.State.cs b/src/Impostor.Server/Net/State/Game.State.cs index b5fcffb..147a833 100644 --- a/src/Impostor.Server/Net/State/Game.State.cs +++ b/src/Impostor.Server/Net/State/Game.State.cs @@ -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) {