--- /dev/null
+using Impostor.Api.Games;
+using Impostor.Api.Net.Inner.Objects;
+
+namespace Impostor.Api.Net.Messages.Rpcs
+{
+ public static class Rpc47CheckMurder
+ {
+ public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl)
+ {
+ writer.Write(playerControl.NetId);
+ }
+
+ public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl)
+ {
+ playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
+ }
+ }
+}
internal Queue<ColorType> RequestedColorId { get; } = new Queue<ColorType>();
+ /// <summary> Gets or sets target that was set by the last CheckMurder RPC. </summary>
+ internal IInnerPlayerControl? IsMurdering { get; set; } = null;
+
public override ValueTask<bool> SerializeAsync(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
case RpcCalls.MurderPlayer:
{
- if (!await ValidateOwnership(call, sender) || !await ValidateImpostor(call, sender, PlayerInfo))
+ if (!await ValidateHost(call, sender))
{
return false;
}
break;
}
+ case RpcCalls.CheckMurder:
+ {
+ if (!await ValidateImpostor(call, sender, PlayerInfo))
+ {
+ return false;
+ }
+
+ Rpc47CheckMurder.Deserialize(reader, Game, out var murdered);
+ return await HandleCheckMurder(sender, murdered);
+ }
+
default:
return await base.HandleRpcAsync(sender, target, call, reader);
}
// return true;
// }
- private async ValueTask<bool> HandleMurderPlayer(ClientPlayer sender, IInnerPlayerControl? target)
+ private async ValueTask<bool> HandleCheckMurder(ClientPlayer sender, IInnerPlayerControl? target)
{
if (!PlayerInfo.CanMurder(Game, _dateTimeProvider))
{
- if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Client tried to murder too fast"))
+ if (await sender.Client.ReportCheatAsync(RpcCalls.CheckMurder, "Client tried to murder too fast"))
+ {
+ return false;
+ }
+ }
+
+ PlayerInfo.LastMurder = _dateTimeProvider.UtcNow - TimeSpan.FromMilliseconds(sender.Client.Connection.AveragePing);
+
+ if (target == null || target.PlayerInfo.IsImpostor)
+ {
+ if (await sender.Client.ReportCheatAsync(RpcCalls.CheckMurder, "Client tried to murder invalid target"))
{
return false;
}
}
+ IsMurdering = target;
+
+ return true;
+ }
+
+ private async ValueTask<bool> HandleMurderPlayer(ClientPlayer sender, IInnerPlayerControl? target)
+ {
if (target == null || target.PlayerInfo.IsImpostor)
{
if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Client tried to murder invalid target"))
}
}
- PlayerInfo.LastMurder = _dateTimeProvider.UtcNow - TimeSpan.FromMilliseconds(sender.Client.Connection.AveragePing);
+ // If the host is also the impostor that committed the murder, CheckMurder is actually sent *after* the MurderPlayer RPC
+ if (sender.Character != this && target != IsMurdering)
+ {
+ if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Host tried to murder incorrect target"))
+ {
+ return false;
+ }
+ }
if (target != null && !target.PlayerInfo.IsDead)
{
await _eventManager.CallAsync(new PlayerMurderEvent(Game, sender, this, target));
}
+ IsMurdering = null;
+
return true;
}