Spawn PlayerInfo's if host is not authoritive

This PlayerInfo also needs to be synced over if it is server owned
This commit is contained in:
miniduikboot committed 2024-08-20 21:41:38 +02:00
1 parent b118af12be
commit 09cadcb63b
3 files changed
+110 -2

No files matched your search

@@ -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;
}
}
}
@@ -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))
@@ -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);
}
}
}