]> git.deb.at Git - rhonda/impostor.git/commitdiff
Spawn PlayerInfo's if host is not authoritive
authorminiduikboot <mini@duikbo.at>
Thu, 20 Jun 2024 16:14:02 +0000 (18:14 +0200)
committerminiduikboot <mini@duikbo.at>
Tue, 20 Aug 2024 19:41:38 +0000 (21:41 +0200)
This PlayerInfo also needs to be synced over if it is server owned

src/Impostor.Server/Net/Inner/Objects/InnerGameData.cs
src/Impostor.Server/Net/State/Game.Data.cs
src/Impostor.Server/Net/State/Game.Outgoing.cs

index 26ec23bc94e6823a8d2d0b6a45a61631c9aa9106..b1796f15dc4c36ff00860ed6e8661512318b2304 100644 (file)
@@ -7,11 +7,14 @@ namespace Impostor.Server.Net.Inner.Objects
     internal partial class InnerGameData : IInnerGameData
     {
         private readonly ConcurrentDictionary<byte, InnerPlayerInfo> _allPlayers = new();
+        private readonly ConcurrentDictionary<int, InnerPlayerInfo> _allPlayersByClientId = new();
 
         public int PlayerCount => _allPlayers.Count;
 
         public IReadOnlyDictionary<byte, InnerPlayerInfo> Players => _allPlayers;
 
+        internal IReadOnlyDictionary<int, InnerPlayerInfo> PlayersByClientId => _allPlayersByClientId;
+
         public InnerPlayerInfo? GetPlayerById(byte id)
         {
             if (id == byte.MaxValue)
@@ -24,12 +27,31 @@ namespace Impostor.Server.Net.Inner.Objects
 
         internal bool AddPlayer(InnerPlayerInfo playerInfo)
         {
-            return _allPlayers.TryAdd(playerInfo.PlayerId, playerInfo);
+            return _allPlayers.TryAdd(playerInfo.PlayerId, playerInfo) &&
+                _allPlayersByClientId.TryAdd(playerInfo.ClientId, playerInfo);
         }
 
         internal void RemovePlayer(byte playerId)
         {
-            _allPlayers.TryRemove(playerId, out _);
+            _allPlayers.TryRemove(playerId, out var player);
+
+            if (player != null)
+            {
+                _allPlayersByClientId.TryRemove(player.ClientId, out _);
+            }
+        }
+
+        internal byte GetNextAvailablePlayerId()
+        {
+            for (byte i = 0; i < byte.MaxValue; i++)
+            {
+                if (!Players.ContainsKey(i))
+                {
+                    return i;
+                }
+            }
+
+            return byte.MaxValue;
         }
     }
 }
index 4e429fa3e89ddf93d3d74bb1715a9272508e0cda..1ae179ca105ed8914b881128af39bbf7a3ca46a2 100644 (file)
@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Threading.Tasks;
 using Impostor.Api;
 using Impostor.Api.Innersloth;
@@ -30,6 +31,16 @@ namespace Impostor.Server.Net.State
         /// </summary>
         private const int CurrentClient = -3;
 
+        /// <summary>
+        ///     Used to list objects that are managed by the game server.
+        /// </summary>
+        private const int ServerOwned = -4;
+
+        /// <summary>
+        ///     The first NetId that is considered as a server owned Network ID that the client will not allocate by default.
+        /// </summary>
+        private const int MinServerNetId = 100000;
+
         private static readonly Dictionary<uint, Type> SpawnableObjects = new()
         {
             [0] = typeof(InnerSkeldShipStatus),
@@ -47,10 +58,14 @@ namespace Impostor.Server.Net.State
             [13] = typeof(InnerFungleShipStatus),
         };
 
+        private static readonly Dictionary<Type, uint> SpawnableObjectIds = SpawnableObjects.ToDictionary((i) => i.Value, (i) => i.Key);
+
         private readonly List<InnerNetObject> _allObjects = new List<InnerNetObject>();
 
         private readonly Dictionary<uint, InnerNetObject> _allObjectsFast = new Dictionary<uint, InnerNetObject>();
 
+        private uint _nextNetId = MinServerNetId;
+
         public T? FindObjectByNetId<T>(uint netId)
             where T : IInnerNetObject
         {
@@ -256,6 +271,10 @@ namespace Impostor.Server.Net.State
                         sender.Scene = scene;
 
                         _logger.LogTrace("> Scene {0} to {1}", clientId, sender.Scene);
+
+                        await SyncServerObjectsAsync(sender);
+                        await SpawnPlayerInfoAsync(sender);
+
                         break;
                     }
 
@@ -447,6 +466,51 @@ namespace Impostor.Server.Net.State
             }
         }
 
+        private async ValueTask SyncServerObjectsAsync(ClientPlayer sender)
+        {
+            foreach (var obj in _allObjectsFast.Values)
+            {
+                if (obj.OwnerId == ServerOwned)
+                {
+                    _logger.LogTrace("Syncing {Type} {NetId}", obj.GetType(), obj.NetId);
+                    await SendObjectSpawnAsync(obj, sender.Client.Id);
+                }
+            }
+        }
+
+        private async ValueTask SpawnPlayerInfoAsync(ClientPlayer sender)
+        {
+            // Hosts spawn PlayerInfo objects if they requested authority
+            if (IsHostAuthoritive)
+            {
+                return;
+            }
+
+            // Only spawn a new PlayerInfo if one has not yet been spawned
+            if (GameNet.GameData.PlayersByClientId.ContainsKey(sender.Client.Id))
+            {
+                return;
+            }
+
+            var playerInfo = (InnerPlayerInfo)ActivatorUtilities.CreateInstance(_serviceProvider, typeof(InnerPlayerInfo), this);
+            playerInfo.SpawnFlags = SpawnFlags.None;
+            playerInfo.NetId = _nextNetId++;
+            playerInfo.OwnerId = ServerOwned;
+            playerInfo.ClientId = sender.Client.Id;
+            playerInfo.PlayerId = GameNet.GameData.GetNextAvailablePlayerId();
+
+            if (!AddNetObject(playerInfo))
+            {
+                _logger.LogError("Couldn't spawn PlayerInfo for {Name} ({ClientId})", sender.Client.Name, sender.Client.Id);
+                playerInfo.NetId = uint.MaxValue;
+                return;
+            }
+
+            _logger.LogTrace("Spawning PlayerInfo (netId {Netid})", playerInfo.NetId);
+            await OnSpawnAsync(sender, playerInfo);
+            await SendObjectSpawnAsync(playerInfo);
+        }
+
         private bool AddNetObject(InnerNetObject obj)
         {
             if (_allObjectsFast.ContainsKey(obj.NetId))
index 4c2f31fc5972e2141b1ea332945dbcbd31dac188..1ab0086e6c80bb33340060db3449eaaccc1c7553 100644 (file)
@@ -112,5 +112,27 @@ namespace Impostor.Server.Net.State
         {
             Message12WaitForHostS2C.Serialize(message, clear, Code, player.Client.Id);
         }
+
+        private async ValueTask SendObjectSpawnAsync(InnerNetObject obj, int? targetClientId = null)
+        {
+            using var writer = StartGameData(targetClientId);
+            writer.StartMessage(GameDataTag.SpawnFlag);
+            writer.WritePacked(SpawnableObjectIds[obj.GetType()]);
+            writer.WritePacked(obj.OwnerId);
+            writer.Write((byte)obj.SpawnFlags);
+
+            var components = obj.GetComponentsInChildren<InnerNetObject>();
+            writer.WritePacked(components.Count);
+            foreach (var component in components)
+            {
+                writer.WritePacked(obj.NetId);
+                writer.StartMessage(1);
+                await component.SerializeAsync(writer, true);
+                writer.EndMessage();
+            }
+
+            writer.EndMessage();
+            await FinishGameDataAsync(writer, targetClientId);
+        }
     }
 }