Merge branches 'feature/phantom', 'feature/sync-tasks', 'feature/despawn-playerinfos', 'feature/move-playername-to-outfit', 'feature/debug-logs', 'fix/setlevel-serverside' and 'fix/scenechange-logging' into feature/2024.6.18-octopus

This commit is contained in:
15 files changed
+357 -95

No files matched your search

@@ -4,15 +4,13 @@ namespace Impostor.Api.Net.Messages.Rpcs
{ {
public static class Rpc29SetTasks public static class Rpc29SetTasks
{ {
public static void Serialize(IMessageWriter writer, byte playerId, ReadOnlyMemory<byte> taskTypeIds) public static void Serialize(IMessageWriter writer, ReadOnlyMemory<byte> taskTypeIds)
{ {
writer.Write(playerId);
writer.Write(taskTypeIds); writer.Write(taskTypeIds);
} }
public static void Deserialize(IMessageReader reader, out byte playerId, out ReadOnlyMemory<byte> taskTypeIds) public static void Deserialize(IMessageReader reader, out ReadOnlyMemory<byte> taskTypeIds)
{ {
playerId = reader.ReadByte();
taskTypeIds = reader.ReadBytesAndSize(); taskTypeIds = reader.ReadBytesAndSize();
} }
} }
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc62CheckVanish
{
public static void Serialize(IMessageWriter writer, float maxDuration = 0f)
{
writer.Write(maxDuration);
}
public static void Deserialize(IMessageReader reader, out float maxDuration)
{
maxDuration = reader.ReadSingle();
}
}
}
@@ -0,0 +1,13 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc63StartVanish
{
public static void Serialize(IMessageWriter writer)
{
}
public static void Deserialize(IMessageReader reader)
{
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc64CheckAppear
{
public static void Serialize(IMessageWriter writer, bool shouldAnimate)
{
writer.Write(shouldAnimate);
}
public static void Deserialize(IMessageReader reader, out bool shouldAnimate)
{
shouldAnimate = reader.ReadBoolean();
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc65StartAppear
{
public static void Serialize(IMessageWriter writer, bool shouldAnimate)
{
writer.Write(shouldAnimate);
}
public static void Deserialize(IMessageReader reader, out bool shouldAnimate)
{
shouldAnimate = reader.ReadBoolean();
}
}
}
@@ -1,39 +0,0 @@
using System.Threading.Tasks;
using Impostor.Api;
using Impostor.Api.Net.Inner;
using Impostor.Api.Net.Messages.Rpcs;
using Impostor.Server.Events.Player;
namespace Impostor.Server.Net.Inner.Objects
{
internal partial class InnerGameData
{
public partial class TaskInfo
{
public async ValueTask CompleteAsync()
{
if (_playerInfo.Controller == null)
{
throw new ImpostorException("Can't complete a task that doesn't have a player assigned");
}
var player = _playerInfo.Controller;
if (Complete)
{
throw new ImpostorException("Can't complete a task that is already completed");
}
Complete = true;
// Send RPC.
using var writer = player.Game.StartRpc(player.NetId, RpcCalls.CompleteTask);
Rpc01CompleteTask.Serialize(writer, Id);
await player.Game.FinishRpcAsync(writer);
// Notify plugins.
await _eventManager.CallAsync(new PlayerCompletedTaskEvent(player.Game, player.Game.GetClientPlayer(player.OwnerId)!, player, this));
}
}
}
}
@@ -1,41 +0,0 @@
using Impostor.Api.Events.Managers;
using Impostor.Api.Innersloth.Maps;
using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Server.Net.Inner.Objects
{
internal partial class InnerGameData
{
public partial class TaskInfo : ITaskInfo
{
private readonly InnerPlayerInfo _playerInfo;
private readonly IEventManager _eventManager;
public TaskInfo(InnerPlayerInfo playerInfo, IEventManager eventManager, uint id, TaskData? task)
{
_playerInfo = playerInfo;
_eventManager = eventManager;
Id = id;
Task = task;
}
public uint Id { get; internal set; }
public TaskData? Task { get; internal set; }
public bool Complete { get; internal set; }
public void Serialize(IMessageWriter writer)
{
writer.WritePacked(Id);
writer.Write(Complete);
}
public void Deserialize(IMessageReader reader)
{
Id = reader.ReadPackedUInt32();
Complete = reader.ReadBoolean();
}
}
}
}
@@ -20,7 +20,7 @@ namespace Impostor.Server.Net.Inner.Objects
public async ValueTask SetNameAsync(string name) public async ValueTask SetNameAsync(string name)
{ {
PlayerInfo.PlayerName = name; PlayerInfo.CurrentOutfit.PlayerName = name;
using var writer = Game.StartRpc(NetId, RpcCalls.SetName); using var writer = Game.StartRpc(NetId, RpcCalls.SetName);
writer.Write(name); writer.Write(name);
@@ -148,5 +148,18 @@ namespace Impostor.Server.Net.Inner.Objects
// Notify plugins. // Notify plugins.
await _eventManager.CallAsync(new PlayerExileEvent(Game, Game.GetClientPlayer(OwnerId)!, this)); await _eventManager.CallAsync(new PlayerExileEvent(Game, Game.GetClientPlayer(OwnerId)!, this));
} }
public async ValueTask StartVanishAsync()
{
using var writer = Game.StartRpc(NetId, RpcCalls.StartVanish);
await Game.FinishRpcAsync(writer);
}
public async ValueTask StartAppearAsync(bool shouldAnimate)
{
using var writer = Game.StartRpc(NetId, RpcCalls.StartAppear);
writer.Write(shouldAnimate);
await Game.FinishRpcAsync(writer);
}
} }
} }
@@ -251,7 +251,7 @@ namespace Impostor.Server.Net.Inner.Objects
} }
Rpc38SetLevel.Deserialize(reader, out var level); Rpc38SetLevel.Deserialize(reader, out var level);
return true; return await HandleSetLevel(sender, level);
} }
case RpcCalls.ReportDeadBody: case RpcCalls.ReportDeadBody:
@@ -511,7 +511,7 @@ namespace Impostor.Server.Net.Inner.Objects
return false; return false;
} }
Rpc55CheckShapeshift.Deserialize(reader, Game, out var playerControl, out var shouldAnimate); Rpc55CheckShapeshift.Deserialize(reader, Game, out var playerControl, out _);
break; break;
} }
@@ -526,6 +526,56 @@ namespace Impostor.Server.Net.Inner.Objects
break; break;
} }
case RpcCalls.CheckVanish:
{
if (!await ValidateOwnership(call, sender) ||
!await ValidateRole(call, sender, PlayerInfo, RoleTypes.Phantom) ||
!await ValidateCmd(call, sender, target))
{
return false;
}
Rpc62CheckVanish.Deserialize(reader, out var maxDuration);
return await HandleCheckVanish(sender, maxDuration);
}
case RpcCalls.StartVanish:
{
if (!await ValidateHost(call, sender) ||
!await ValidateBroadcast(call, sender, target))
{
return false;
}
Rpc63StartVanish.Deserialize(reader);
return await HandleStartVanish(sender);
}
case RpcCalls.CheckAppear:
{
if (!await ValidateOwnership(call, sender) ||
!await ValidateRole(call, sender, PlayerInfo, RoleTypes.Phantom) ||
!await ValidateCmd(call, sender, target))
{
return false;
}
Rpc64CheckAppear.Deserialize(reader, out var shouldAnimate);
return await HandleCheckAppear(sender, shouldAnimate);
}
case RpcCalls.StartAppear:
{
if (!await ValidateHost(call, sender) ||
!await ValidateBroadcast(call, sender, target))
{
return false;
}
Rpc65StartAppear.Deserialize(reader, out var shouldAnimate);
return await HandleStartAppear(sender, shouldAnimate);
}
default: default:
return await base.HandleRpcAsync(sender, target, call, reader); return await base.HandleRpcAsync(sender, target, call, reader);
} }
@@ -686,7 +736,7 @@ namespace Impostor.Server.Net.Inner.Objects
} }
} }
PlayerInfo.PlayerName = name; PlayerInfo.CurrentOutfit.PlayerName = name;
return true; return true;
} }
@@ -834,6 +884,19 @@ namespace Impostor.Server.Net.Inner.Objects
return true; return true;
} }
private async ValueTask<bool> HandleSetLevel(ClientPlayer sender, uint level)
{
if (Game.GameState == GameStates.Started &&
await sender.Client.ReportCheatAsync(RpcCalls.SetLevel, CheatCategory.GameFlow, "Client tried to set level while not in game"))
{
return false;
}
PlayerInfo.PlayerLevel = level;
return true;
}
private async ValueTask<bool> HandleCheckMurder(ClientPlayer sender, InnerPlayerControl? target) private async ValueTask<bool> HandleCheckMurder(ClientPlayer sender, InnerPlayerControl? target)
{ {
if (!PlayerInfo.CanMurder(Game, _dateTimeProvider)) if (!PlayerInfo.CanMurder(Game, _dateTimeProvider))
@@ -1008,5 +1071,61 @@ namespace Impostor.Server.Net.Inner.Objects
return true; return true;
} }
private async ValueTask<bool> HandleCheckVanish(ClientPlayer sender, float maxDuration)
{
// TODO: Check if operation is taking place during lobby/meetings
// TODO: Check max duration
// If the game is host authoritive, the RPC is handled by the host, otherwise by the server
if (_game.IsHostAuthoritive)
{
return true;
}
else
{
await StartVanishAsync();
return false;
}
}
private async ValueTask<bool> HandleStartVanish(ClientPlayer sender)
{
if (!_game.IsHostAuthoritive)
{
if (await sender.Client.ReportCheatAsync(RpcCalls.StartVanish, CheatCategory.GameFlow, "Client tried to send StartVanish directly"))
{
return false;
}
}
return true;
}
private async ValueTask<bool> HandleCheckAppear(ClientPlayer sender, bool shouldAnimate)
{
// If the game is host authoritive, the RPC is handled by the host, otherwise by the server
if (_game.IsHostAuthoritive)
{
return true;
}
else
{
await StartAppearAsync(shouldAnimate);
return false;
}
}
private async ValueTask<bool> HandleStartAppear(ClientPlayer sender, bool shouldAnimate)
{
if (!_game.IsHostAuthoritive)
{
if (await sender.Client.ReportCheatAsync(RpcCalls.StartAppear, CheatCategory.GameFlow, "Client tried to send StartAppear directly"))
{
return false;
}
}
return true;
}
} }
} }
@@ -1,12 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Impostor.Api.Events.Managers;
using Impostor.Api.Games; using Impostor.Api.Games;
using Impostor.Api.Innersloth; using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.Customization; using Impostor.Api.Innersloth.Customization;
using Impostor.Api.Innersloth.GameOptions; using Impostor.Api.Innersloth.GameOptions;
using Impostor.Api.Net; using Impostor.Api.Net;
using Impostor.Api.Net.Custom; using Impostor.Api.Net.Custom;
using Impostor.Api.Net.Inner;
using Impostor.Api.Net.Messages.Rpcs;
using Impostor.Api.Utils; using Impostor.Api.Utils;
using Impostor.Server.Net.State; using Impostor.Server.Net.State;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -15,11 +18,13 @@ namespace Impostor.Server.Net.Inner.Objects
{ {
internal partial class InnerPlayerInfo internal partial class InnerPlayerInfo
{ {
private readonly IEventManager _eventManager;
private readonly ILogger<InnerPlayerInfo> _logger; private readonly ILogger<InnerPlayerInfo> _logger;
public InnerPlayerInfo(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerPlayerInfo> logger) : base(customMessageManager, game) public InnerPlayerInfo(ICustomMessageManager<ICustomRpc> customMessageManager, IEventManager eventManager, Game game, ILogger<InnerPlayerInfo> logger) : base(customMessageManager, game)
{ {
Components.Add(this); Components.Add(this);
_eventManager = eventManager;
_logger = logger; _logger = logger;
} }
@@ -29,7 +34,7 @@ namespace Impostor.Server.Net.Inner.Objects
public int ClientId { get; internal set; } public int ClientId { get; internal set; }
public string PlayerName { get; internal set; } = string.Empty; public string PlayerName => CurrentOutfit.PlayerName;
public Dictionary<PlayerOutfitType, PlayerOutfit> Outfits { get; } = new() public Dictionary<PlayerOutfitType, PlayerOutfit> Outfits { get; } = new()
{ {
@@ -46,7 +51,7 @@ namespace Impostor.Server.Net.Inner.Objects
public bool Disconnected { get; internal set; } public bool Disconnected { get; internal set; }
public bool IsImpostor => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.ImpostorGhost; public bool IsImpostor => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.ImpostorGhost or RoleTypes.Phantom;
public bool CanVent => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.Engineer; public bool CanVent => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.Engineer;
@@ -54,7 +59,7 @@ namespace Impostor.Server.Net.Inner.Objects
public DeathReason LastDeathReason { get; internal set; } public DeathReason LastDeathReason { get; internal set; }
public List<InnerGameData.TaskInfo> Tasks { get; internal set; } = new List<InnerGameData.TaskInfo>(0); public List<TaskInfo> Tasks { get; internal set; } = new List<TaskInfo>(0);
public DateTimeOffset LastMurder { get; set; } public DateTimeOffset LastMurder { get; set; }
@@ -176,5 +181,39 @@ namespace Impostor.Server.Net.Inner.Objects
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
public override async ValueTask<bool> HandleRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader)
{
switch (call)
{
case RpcCalls.SetTasks:
Rpc29SetTasks.Deserialize(reader, out var taskTypeIds);
SetTasks(taskTypeIds);
break;
default:
return await base.HandleRpcAsync(sender, target, call, reader);
}
return true;
}
private void SetTasks(ReadOnlyMemory<byte> taskTypeIds)
{
if (Disconnected)
{
return;
}
Tasks = new List<TaskInfo>(taskTypeIds.Length);
var taskId = 0u;
foreach (var taskTypeId in taskTypeIds.Span)
{
var mapTasks = Game.GameNet!.ShipStatus?.Data.Tasks;
var taskType = (mapTasks != null && mapTasks.ContainsKey(taskTypeId)) ? mapTasks[taskTypeId] : null;
Tasks.Add(new TaskInfo(this, _eventManager, taskId++, taskType));
}
}
} }
} }
@@ -0,0 +1,68 @@
using System.Threading.Tasks;
using Impostor.Api;
using Impostor.Api.Events.Managers;
using Impostor.Api.Innersloth.Maps;
using Impostor.Api.Net.Inner;
using Impostor.Api.Net.Inner.Objects;
using Impostor.Api.Net.Messages.Rpcs;
using Impostor.Server.Events.Player;
namespace Impostor.Server.Net.Inner.Objects
{
internal class TaskInfo : ITaskInfo
{
private readonly InnerPlayerInfo _playerInfo;
private readonly IEventManager _eventManager;
public TaskInfo(InnerPlayerInfo playerInfo, IEventManager eventManager, uint id, TaskData? task)
{
_playerInfo = playerInfo;
_eventManager = eventManager;
Id = id;
Task = task;
}
public uint Id { get; internal set; }
public TaskData? Task { get; internal set; }
public bool Complete { get; internal set; }
public void Serialize(IMessageWriter writer)
{
writer.WritePacked(Id);
writer.Write(Complete);
}
public void Deserialize(IMessageReader reader)
{
Id = reader.ReadPackedUInt32();
Complete = reader.ReadBoolean();
}
public async ValueTask CompleteAsync()
{
if (_playerInfo.Controller == null)
{
throw new ImpostorException("Can't complete a task that doesn't have a player assigned");
}
var player = _playerInfo.Controller;
if (Complete)
{
throw new ImpostorException("Can't complete a task that is already completed");
}
Complete = true;
// Send RPC.
using var writer = player.Game.StartRpc(player.NetId, RpcCalls.CompleteTask);
Rpc01CompleteTask.Serialize(writer, Id);
await player.Game.FinishRpcAsync(writer);
// Notify plugins.
await _eventManager.CallAsync(new PlayerCompletedTaskEvent(player.Game, player.Game.GetClientPlayer(player.OwnerId)!, player, this));
}
}
}
+27 -3
View File
@@ -99,6 +99,8 @@ namespace Impostor.Server.Net.State
{ {
using var reader = parent.ReadMessage(); using var reader = parent.ReadMessage();
_logger.LogTrace("Client {SenderId} sent GameData {Tag}", sender.Client.Id, reader.Tag);
switch (reader.Tag) switch (reader.Tag)
{ {
case GameDataTag.DataFlag: case GameDataTag.DataFlag:
@@ -106,6 +108,7 @@ namespace Impostor.Server.Net.State
var netId = reader.ReadPackedUInt32(); var netId = reader.ReadPackedUInt32();
if (_allObjectsFast.TryGetValue(netId, out var obj)) if (_allObjectsFast.TryGetValue(netId, out var obj))
{ {
_logger.LogTrace("Received Data for {NetId}, which is of type {Type}", netId, obj.GetType().Name);
await obj.DeserializeAsync(sender, target, reader, false); await obj.DeserializeAsync(sender, target, reader, false);
} }
else else
@@ -121,7 +124,15 @@ namespace Impostor.Server.Net.State
var netId = reader.ReadPackedUInt32(); var netId = reader.ReadPackedUInt32();
if (_allObjectsFast.TryGetValue(netId, out var obj)) if (_allObjectsFast.TryGetValue(netId, out var obj))
{ {
if (!await obj.HandleRpcAsync(sender, target, (RpcCalls)reader.ReadByte(), reader)) var call = (RpcCalls)reader.ReadByte();
_logger.LogTrace(
"Client {SenderId} called Rpc {Call} on NetId {CallerId} and sent it to {Target}",
sender.Client.Id,
call,
obj.NetId,
target?.Client.Id.ToString() ?? "everyone");
if (!await obj.HandleRpcAsync(sender, target, call, reader))
{ {
parent.RemoveMessage(reader); parent.RemoveMessage(reader);
continue; continue;
@@ -261,9 +272,10 @@ namespace Impostor.Server.Net.State
if (scene != "OnlineGame") if (scene != "OnlineGame")
{ {
_logger.LogWarning( _logger.LogWarning(
"Player {0} ({1}) tried to send SceneChangeFlag with disallowed scene.", "Player {PlayerName} ({ClientId}) tried to send SceneChangeFlag with disallowed scene \"{Scene}\".",
sender.Client.Name, sender.Client.Name,
sender.Client.Id); sender.Client.Id,
scene);
return false; return false;
} }
@@ -510,6 +522,18 @@ namespace Impostor.Server.Net.State
await SendObjectSpawnAsync(playerInfo); await SendObjectSpawnAsync(playerInfo);
} }
private async ValueTask DespawnPlayerInfoAsync(InnerPlayerInfo playerInfo)
{
if (playerInfo.OwnerId == ServerOwned)
{
_logger.LogDebug("Despawning PlayerInfo {nid}", playerInfo.NetId);
GameNet.GameData.RemovePlayer(playerInfo.PlayerId);
RemoveNetObject(playerInfo);
await SendObjectDespawnAsync(playerInfo);
}
}
private bool AddNetObject(InnerNetObject obj) private bool AddNetObject(InnerNetObject obj)
{ {
if (_allObjectsFast.ContainsKey(obj.NetId)) if (_allObjectsFast.ContainsKey(obj.NetId))
@@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Impostor.Api.Games; using Impostor.Api.Games;
@@ -43,6 +44,12 @@ namespace Impostor.Server.Net.State
player.Value.Limbo = LimboStates.PreSpawn; player.Value.Limbo = LimboStates.PreSpawn;
} }
// Delete all PlayerInfo objects
foreach (var playerInfo in GameNet.GameData.Players.Values.ToList())
{
await DespawnPlayerInfoAsync(playerInfo);
}
await _eventManager.CallAsync(new GameEndedEvent(this, gameOverReason)); await _eventManager.CallAsync(new GameEndedEvent(this, gameOverReason));
} }
@@ -134,5 +134,14 @@ namespace Impostor.Server.Net.State
writer.EndMessage(); writer.EndMessage();
await FinishGameDataAsync(writer, targetClientId); await FinishGameDataAsync(writer, targetClientId);
} }
private ValueTask SendObjectDespawnAsync(InnerNetObject obj, int? targetClientId = null)
{
using var writer = StartGameData(targetClientId);
writer.StartMessage(GameDataTag.DespawnFlag);
writer.WritePacked(obj.NetId);
writer.EndMessage();
return FinishGameDataAsync(writer, targetClientId);
}
} }
} }
@@ -85,6 +85,13 @@ namespace Impostor.Server.Net.State
} }
}); });
// Clean up the PlayerInfo if we own it and we're still in the lobby
if (GameState == GameStates.NotStarted)
{
var playerInfo = GameNet.GameData.PlayersByClientId[playerId];
await DespawnPlayerInfoAsync(playerInfo);
}
return true; return true;
} }