mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-19 15:51:42 +00:00
Add Murder/Protect/Exile API without checks
Force(.*)Async will perform the same function as \1Async but without throwing exceptions. The old API is maintained for backwards compatibility reasons. Murder checks are now shared with the main function that handles CheckMurder, as ProtectPlayerAsync and ExileAsync aren't used in the code I didn't make a similar change there. Checks were also synced between both methods
This commit is contained in:
1 parent
c672e19e99
commit
be0a0b917c
3 files changed
+90
-25
No files matched your search
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.Customization;
|
||||
@@ -118,28 +119,52 @@ namespace Impostor.Api.Net.Inner.Objects
|
||||
ValueTask MurderPlayerAsync(IInnerPlayerControl target, MurderResultFlags result);
|
||||
|
||||
/// <summary>
|
||||
/// Murder <paramref name="target" /> player successfully.
|
||||
/// Murder <paramref name="target" /> player.
|
||||
/// </summary>
|
||||
/// <param name="target">Target player to murder.</param>
|
||||
/// <param name="result">The result of the murder operation.</param>
|
||||
/// <returns>Task that must be awaited.</returns>
|
||||
ValueTask ForceMurderPlayerAsync(IInnerPlayerControl target, MurderResultFlags result);
|
||||
|
||||
/// <summary>
|
||||
/// Murder <paramref name="target" /> player after validating if this is a valid kill.
|
||||
/// </summary>
|
||||
/// <param name="target">Target player to murder.</param>
|
||||
/// <exception cref="ImpostorProtocolException">Thrown when player is not the impostor.</exception>
|
||||
/// <exception cref="ImpostorProtocolException">Thrown when player is dead.</exception>
|
||||
/// <exception cref="ImpostorProtocolException">Thrown when target is dead.</exception>
|
||||
/// <returns>Task that must be awaited.</returns>
|
||||
[Obsolete("Please switch to version with the MurderResultFlags argument")]
|
||||
ValueTask MurderPlayerAsync(IInnerPlayerControl target);
|
||||
|
||||
/// <summary>
|
||||
/// Protect <paramref name="target" /> player.
|
||||
/// </summary>
|
||||
/// <param name="target">Target player to protect.</param>
|
||||
/// <exception cref="ImpostorProtocolException">Thrown when target is a guardian angel.</exception>
|
||||
/// <exception cref="ImpostorProtocolException">Thrown when target is dead.</exception>
|
||||
/// <returns>Task that must be awaited.</returns>
|
||||
ValueTask ProtectPlayerAsync(IInnerPlayerControl target);
|
||||
|
||||
/// <summary>
|
||||
/// Protect <paramref name="target" /> player.
|
||||
/// </summary>
|
||||
/// <param name="target">Target player to protect.</param>
|
||||
/// <returns>Task that must be awaited.</returns>
|
||||
ValueTask ForceProtectPlayerAsync(IInnerPlayerControl target);
|
||||
|
||||
/// <summary>
|
||||
/// Exile the current player. This doesn't produce a body to be reported.
|
||||
/// Visible to all players.
|
||||
/// </summary>
|
||||
/// <exception cref="ImpostorProtocolException">Thrown if player to be exiled is already dead.</exception>
|
||||
/// <returns>Task that must be awaited.</returns>
|
||||
ValueTask ExileAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Exile the current player. This doesn't produce a body to be reported.
|
||||
/// Visible to all players.
|
||||
/// </summary>
|
||||
/// <returns>Task that must be awaited.</returns>
|
||||
ValueTask ExileAsync();
|
||||
ValueTask ForceExileAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api;
|
||||
using Impostor.Api.Innersloth;
|
||||
@@ -100,23 +101,45 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
await Game.FinishRpcAsync(writer, player.OwnerId);
|
||||
}
|
||||
|
||||
public async ValueTask MurderPlayerAsync(IInnerPlayerControl target, MurderResultFlags result)
|
||||
private bool ValidateMurderPlayer(IInnerPlayerControl target, MurderResultFlags result, [NotNullWhen(false)] out string? invalidReason)
|
||||
{
|
||||
if (!PlayerInfo.IsImpostor)
|
||||
{
|
||||
throw new ImpostorProtocolException("Tried to murder a player, but murderer was not the impostor.");
|
||||
invalidReason = "Tried to murder a player, but murderer was not an impostor.";
|
||||
}
|
||||
|
||||
if (PlayerInfo.IsDead)
|
||||
else if (PlayerInfo.IsDead)
|
||||
{
|
||||
throw new ImpostorProtocolException("Tried to murder a player, but murderer was not alive.");
|
||||
invalidReason = "Tried to murder a player, but murderer was not alive.";
|
||||
}
|
||||
|
||||
if (target.PlayerInfo.IsDead)
|
||||
else if (target.PlayerInfo.IsImpostor)
|
||||
{
|
||||
throw new ImpostorProtocolException("Tried to murder a player, but target was not alive.");
|
||||
invalidReason = "Tried to murder a player, but target is an impostor";
|
||||
}
|
||||
else if (target.PlayerInfo.IsDead)
|
||||
{
|
||||
invalidReason = "Tried to murder a player, but target was not alive.";
|
||||
}
|
||||
else
|
||||
{
|
||||
invalidReason = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async ValueTask MurderPlayerAsync(IInnerPlayerControl target, MurderResultFlags result)
|
||||
{
|
||||
if (!ValidateMurderPlayer(target, result, out var reason))
|
||||
{
|
||||
throw new ImpostorProtocolException(reason);
|
||||
}
|
||||
|
||||
await ForceMurderPlayerAsync(target, result);
|
||||
}
|
||||
|
||||
public async ValueTask ForceMurderPlayerAsync(IInnerPlayerControl target, MurderResultFlags result)
|
||||
{
|
||||
if (!result.IsFailed())
|
||||
{
|
||||
((InnerPlayerControl)target).Die(DeathReason.Kill);
|
||||
@@ -136,11 +159,16 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public async ValueTask ProtectPlayerAsync(IInnerPlayerControl target)
|
||||
{
|
||||
if (target.PlayerInfo.RoleType == RoleTypes.GuardianAngel)
|
||||
if (target.PlayerInfo.IsDead)
|
||||
{
|
||||
throw new ImpostorProtocolException("Tried to protect another Guardian Angel");
|
||||
throw new ImpostorProtocolException("Tried to protect a player that is dead");
|
||||
}
|
||||
|
||||
await ForceProtectPlayerAsync(target);
|
||||
}
|
||||
|
||||
public async ValueTask ForceProtectPlayerAsync(IInnerPlayerControl target)
|
||||
{
|
||||
((InnerPlayerControl)target).Protect(this);
|
||||
|
||||
using var writer = Game.StartRpc(NetId, RpcCalls.ProtectPlayer);
|
||||
@@ -155,6 +183,11 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
throw new ImpostorProtocolException("Tried to exile a player, but target was not alive.");
|
||||
}
|
||||
|
||||
await ForceExileAsync();
|
||||
}
|
||||
|
||||
public async ValueTask ForceExileAsync()
|
||||
{
|
||||
// Update player.
|
||||
Die(DeathReason.Exile);
|
||||
|
||||
|
||||
@@ -946,16 +946,6 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
}
|
||||
|
||||
// Host-only mods intentionally desync players, so it may appear that one killing role (like a genuine impostor) is killing another
|
||||
// killing role (like an sheriff). So this needs to be allowed if the host requested authority.
|
||||
if (target == null || (target.PlayerInfo.IsImpostor && !_game.IsHostAuthoritive))
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.CheckMurder, CheatCategory.GameFlow, "Client tried to murder invalid target"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PlayerInfo.LastMurder = _dateTimeProvider.UtcNow - TimeSpan.FromMilliseconds(sender.Client.Connection.AveragePing);
|
||||
IsMurdering = target;
|
||||
|
||||
@@ -966,17 +956,34 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target != null)
|
||||
if (target == null)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.CheckMurder, CheatCategory.GameFlow, "Client tried to murder a nonexisting target"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = target.IsProtected ? MurderResultFlags.FailedProtected : MurderResultFlags.Succeeded;
|
||||
|
||||
if (!ValidateMurderPlayer(target, result, out var invalidReason))
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.CheckMurder, CheatCategory.GameFlow, invalidReason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var evt = new PlayerCheckMurderEvent(Game, sender, this, target, result);
|
||||
await _eventManager.CallAsync(evt);
|
||||
|
||||
if (!evt.IsCancelled)
|
||||
{
|
||||
target.ProtectedOn = null; // Clear GA protection in all cases
|
||||
await MurderPlayerAsync(target, evt.Result);
|
||||
|
||||
// Don't repeat checks as they were already done in ValidateMP
|
||||
await ForceMurderPlayerAsync(target, evt.Result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user