From: miniduikboot Date: Thu, 20 Jun 2024 16:14:02 +0000 (+0200) Subject: Spawn PlayerInfo's if host is not authoritive X-Git-Tag: v1.10.0~7^2~9 X-Git-Url: https://git.deb.at/?a=commitdiff_plain;h=09cadcb63b3b38455dc93d66afa9b76273d3c91e;p=rhonda%2Fimpostor.git Spawn PlayerInfo's if host is not authoritive This PlayerInfo also needs to be synced over if it is server owned --- diff --git a/src/Impostor.Server/Net/Inner/Objects/InnerGameData.cs b/src/Impostor.Server/Net/Inner/Objects/InnerGameData.cs index 26ec23b..b1796f1 100644 --- a/src/Impostor.Server/Net/Inner/Objects/InnerGameData.cs +++ b/src/Impostor.Server/Net/Inner/Objects/InnerGameData.cs @@ -7,11 +7,14 @@ namespace Impostor.Server.Net.Inner.Objects internal partial class InnerGameData : IInnerGameData { private readonly ConcurrentDictionary _allPlayers = new(); + private readonly ConcurrentDictionary _allPlayersByClientId = new(); public int PlayerCount => _allPlayers.Count; public IReadOnlyDictionary Players => _allPlayers; + internal IReadOnlyDictionary 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; } } } diff --git a/src/Impostor.Server/Net/State/Game.Data.cs b/src/Impostor.Server/Net/State/Game.Data.cs index 4e429fa..1ae179c 100644 --- a/src/Impostor.Server/Net/State/Game.Data.cs +++ b/src/Impostor.Server/Net/State/Game.Data.cs @@ -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 /// private const int CurrentClient = -3; + /// + /// Used to list objects that are managed by the game server. + /// + private const int ServerOwned = -4; + + /// + /// The first NetId that is considered as a server owned Network ID that the client will not allocate by default. + /// + private const int MinServerNetId = 100000; + private static readonly Dictionary SpawnableObjects = new() { [0] = typeof(InnerSkeldShipStatus), @@ -47,10 +58,14 @@ namespace Impostor.Server.Net.State [13] = typeof(InnerFungleShipStatus), }; + private static readonly Dictionary SpawnableObjectIds = SpawnableObjects.ToDictionary((i) => i.Value, (i) => i.Key); + private readonly List _allObjects = new List(); private readonly Dictionary _allObjectsFast = new Dictionary(); + private uint _nextNetId = MinServerNetId; + public T? FindObjectByNetId(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)) diff --git a/src/Impostor.Server/Net/State/Game.Outgoing.cs b/src/Impostor.Server/Net/State/Game.Outgoing.cs index 4c2f31f..1ab0086 100644 --- a/src/Impostor.Server/Net/State/Game.Outgoing.cs +++ b/src/Impostor.Server/Net/State/Game.Outgoing.cs @@ -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(); + 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); + } } }