mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-19 15:51:42 +00:00
Support Judge and new vote
This commit is contained in:
1 parent
857e04dbc3
commit
f23dd8aa25
8 files changed
+228
-26
No files matched your search
@@ -1,4 +1,4 @@
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
namespace Impostor.Api.Events.Meeting
|
||||
{
|
||||
@@ -13,5 +13,15 @@ namespace Impostor.Api.Events.Meeting
|
||||
/// Gets a value indicating whether meeting result is a tie.
|
||||
/// </summary>
|
||||
bool IsTie { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a Judge overruled the vote result.
|
||||
/// </summary>
|
||||
bool WasOverruled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nonce of the winning Judge overrule, or 0 if the meeting wasn't overruled.
|
||||
/// </summary>
|
||||
ushort OverrideId { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Impostor.Api.Events.Player;
|
||||
|
||||
@@ -6,6 +6,24 @@ namespace Impostor.Api.Net.Inner.Objects
|
||||
{
|
||||
public interface IInnerMeetingHud : IInnerNetObject
|
||||
{
|
||||
public interface IJudgeOverrule
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the player id of the Judge that queued this overrule.
|
||||
/// </summary>
|
||||
byte JudgePlayerId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player id of the player the Judge wants to overrule.
|
||||
/// </summary>
|
||||
byte OverruledPlayerId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the overrule nonce used to match this overrule to the voting result.
|
||||
/// </summary>
|
||||
ushort OverruleNonce { get; }
|
||||
}
|
||||
|
||||
public interface IPlayerVoteArea
|
||||
{
|
||||
/// <summary>
|
||||
@@ -55,5 +73,10 @@ namespace Impostor.Api.Net.Inner.Objects
|
||||
/// Gets the player that started the meeting.
|
||||
/// </summary>
|
||||
IInnerPlayerInfo? Reporter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Judge overrules queued in this meeting.
|
||||
/// </summary>
|
||||
IReadOnlyCollection<IJudgeOverrule> JudgeOverrules { get; }
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,16 @@ namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc23VotingComplete
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, byte[] states, byte playerId, bool tie)
|
||||
public static void Serialize(IMessageWriter writer, byte[] states, byte playerId, bool tie, bool wasOverruled, ushort overrideId)
|
||||
{
|
||||
writer.WriteBytesAndSize(states);
|
||||
writer.Write(playerId);
|
||||
writer.Write(tie);
|
||||
writer.Write(wasOverruled);
|
||||
writer.Write(overrideId);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out IMessageReader[] states, out byte playerId, out bool tie)
|
||||
public static void Deserialize(IMessageReader reader, bool hasOverruleFields, out IMessageReader[] states, out byte playerId, out bool tie, out bool wasOverruled, out ushort overrideId)
|
||||
{
|
||||
var length = reader.ReadPackedInt32();
|
||||
states = new IMessageReader[length];
|
||||
@@ -20,6 +22,17 @@ namespace Impostor.Api.Net.Messages.Rpcs
|
||||
|
||||
playerId = reader.ReadByte();
|
||||
tie = reader.ReadBoolean();
|
||||
|
||||
if (hasOverruleFields)
|
||||
{
|
||||
wasOverruled = reader.ReadBoolean();
|
||||
overrideId = reader.ReadUInt16();
|
||||
}
|
||||
else
|
||||
{
|
||||
wasOverruled = false;
|
||||
overrideId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc66QueueOverruleVotes
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, byte judgePlayerId, byte targetPlayerId, ushort overruleNonce)
|
||||
{
|
||||
writer.Write(judgePlayerId);
|
||||
writer.Write(targetPlayerId);
|
||||
writer.Write(overruleNonce);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out byte judgePlayerId, out byte targetPlayerId, out ushort overruleNonce)
|
||||
{
|
||||
judgePlayerId = reader.ReadByte();
|
||||
targetPlayerId = reader.ReadByte();
|
||||
overruleNonce = reader.ReadUInt16();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Impostor.Api.Events.Meeting;
|
||||
using Impostor.Api.Events.Meeting;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
@@ -6,12 +6,14 @@ namespace Impostor.Server.Events.Meeting
|
||||
{
|
||||
public class MeetingEndedEvent : IMeetingEndedEvent
|
||||
{
|
||||
public MeetingEndedEvent(IGame game, IInnerMeetingHud meetingHud, IInnerPlayerControl? exiled, bool isTie)
|
||||
public MeetingEndedEvent(IGame game, IInnerMeetingHud meetingHud, IInnerPlayerControl? exiled, bool isTie, bool wasOverruled = false, ushort overrideId = 0)
|
||||
{
|
||||
Game = game;
|
||||
MeetingHud = meetingHud;
|
||||
Exiled = exiled;
|
||||
IsTie = isTie;
|
||||
WasOverruled = wasOverruled;
|
||||
OverrideId = overrideId;
|
||||
}
|
||||
|
||||
public IGame Game { get; }
|
||||
@@ -21,5 +23,9 @@ namespace Impostor.Server.Events.Meeting
|
||||
public IInnerPlayerControl? Exiled { get; }
|
||||
|
||||
public bool IsTie { get; }
|
||||
|
||||
public bool WasOverruled { get; }
|
||||
|
||||
public ushort OverrideId { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
@@ -9,5 +9,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
IReadOnlyCollection<IInnerMeetingHud.IPlayerVoteArea> IInnerMeetingHud.PlayerStates => Array.AsReadOnly(_playerStates);
|
||||
|
||||
IInnerPlayerInfo? IInnerMeetingHud.Reporter => Reporter;
|
||||
|
||||
IReadOnlyCollection<IInnerMeetingHud.IJudgeOverrule> IInnerMeetingHud.JudgeOverrules => JudgeOverrules;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
internal partial class InnerMeetingHud
|
||||
{
|
||||
public sealed class JudgeOverrule : IInnerMeetingHud.IJudgeOverrule
|
||||
{
|
||||
public JudgeOverrule(byte judgePlayerId, byte overruledPlayerId, ushort overruleNonce)
|
||||
{
|
||||
JudgePlayerId = judgePlayerId;
|
||||
OverruledPlayerId = overruledPlayerId;
|
||||
OverruleNonce = overruleNonce;
|
||||
}
|
||||
|
||||
public byte JudgePlayerId { get; set; }
|
||||
|
||||
public byte OverruledPlayerId { get; set; }
|
||||
|
||||
public ushort OverruleNonce { get; set; }
|
||||
|
||||
public static JudgeOverrule Deserialize(IMessageReader reader)
|
||||
{
|
||||
var inner = reader.ReadMessage();
|
||||
return new JudgeOverrule(inner.Tag, inner.ReadByte(), inner.ReadUInt16());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using Impostor.Api.Innersloth.GameOptions;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Inner;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
using Impostor.Api.Net.Messages.Rpcs;
|
||||
using Impostor.Server.Events.Meeting;
|
||||
using Impostor.Server.Events.Player;
|
||||
@@ -26,8 +27,8 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
private readonly ILogger<InnerMeetingHud> _logger;
|
||||
private readonly IEventManager _eventManager;
|
||||
|
||||
private readonly CancellationTokenSource _timerToken;
|
||||
private readonly List<JudgeOverrule> _judgeOverrules = new();
|
||||
|
||||
[AllowNull]
|
||||
private PlayerVoteArea[] _playerStates;
|
||||
@@ -67,6 +68,8 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public InnerPlayerInfo? Reporter { get; private set; }
|
||||
|
||||
public IReadOnlyCollection<IInnerMeetingHud.IJudgeOverrule> JudgeOverrules => _judgeOverrules;
|
||||
|
||||
public override ValueTask<bool> SerializeAsync(IMessageWriter writer, bool initialState)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@@ -114,11 +117,13 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
if (sender.Client.GameVersion >= JudgeMinVersion)
|
||||
{
|
||||
var overruleQueueListLength = reader.ReadPackedInt32();
|
||||
for (var i = 0; i < overruleQueueListLength; i++)
|
||||
if (overruleQueueListLength > 0)
|
||||
{
|
||||
var msg = reader.ReadMessage();
|
||||
|
||||
// TODO parse and handle JudgeOverrule
|
||||
_judgeOverrules.Clear();
|
||||
for (var i = 0; i < overruleQueueListLength; i++)
|
||||
{
|
||||
_judgeOverrules.Add(JudgeOverrule.Deserialize(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,6 +140,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
|
||||
Rpc22Close.Deserialize(reader);
|
||||
_judgeOverrules.Clear();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -145,7 +151,8 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc23VotingComplete.Deserialize(reader, out var states, out var playerId, out var tie);
|
||||
var hasOverruleFields = sender.Client.GameVersion >= JudgeMinVersion;
|
||||
Rpc23VotingComplete.Deserialize(reader, hasOverruleFields, out var states, out var playerId, out var tie, out var wasOverruled, out var overrideId);
|
||||
foreach (var messageReader in states)
|
||||
{
|
||||
messageReader.Dispose();
|
||||
@@ -176,18 +183,8 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
case RpcCalls.QueueOverruleVotes:
|
||||
{
|
||||
if (!await ValidateRole(call, sender, sender.Character!.PlayerInfo, RoleTypes.Judge) ||
|
||||
!await ValidateTarget(call, sender, target) ||
|
||||
!await ValidateHost(call, target!))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var judgePlayerId = reader.ReadByte();
|
||||
var targetPlayerId = reader.ReadByte();
|
||||
var overruleNonce = reader.ReadUInt16();
|
||||
|
||||
break;
|
||||
Rpc66QueueOverruleVotes.Deserialize(reader, out var judgePlayerId, out var targetPlayerId, out var overruleNonce);
|
||||
return await HandleQueueOverruleVotesAsync(sender, target, judgePlayerId, targetPlayerId, overruleNonce);
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -250,6 +247,87 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return true;
|
||||
}
|
||||
|
||||
private async ValueTask<bool> HandleQueueOverruleVotesAsync(ClientPlayer sender, ClientPlayer? target, byte judgePlayerId, byte targetPlayerId, ushort overruleNonce)
|
||||
{
|
||||
if (!await ValidateRole(RpcCalls.QueueOverruleVotes, sender, sender.Character?.PlayerInfo, RoleTypes.Judge) ||
|
||||
!await ValidateTarget(RpcCalls.QueueOverruleVotes, sender, target) ||
|
||||
!await ValidateHost(RpcCalls.QueueOverruleVotes, target!))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// A Judge can only queue an overrule for themself.
|
||||
if (judgePlayerId != sender.Character!.PlayerId)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.QueueOverruleVotes, CheatCategory.Ownership, "Client sent a Judge overrule for another player"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The client only generates nonces in the 1..65535 range.
|
||||
if (overruleNonce == 0)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.QueueOverruleVotes, CheatCategory.GameFlow, "Client sent a Judge overrule with an uninitialized nonce"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// A Judge can only overrule once per meeting.
|
||||
if (_judgeOverrules.Any(overrule => overrule.JudgePlayerId == judgePlayerId))
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.QueueOverruleVotes, CheatCategory.GameFlow, "Client sent more than one Judge overrule in the same meeting"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The overruled player must exist.
|
||||
if (Game.GameNet.GameData!.GetPlayerById(targetPlayerId) == null)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.QueueOverruleVotes, CheatCategory.InvalidObject, "Client sent a Judge overrule for an unknown player"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AddOrUpdateJudgeOverrule(judgePlayerId, targetPlayerId, overruleNonce);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AddOrUpdateJudgeOverrule(byte judgePlayerId, byte targetPlayerId, ushort overruleNonce)
|
||||
{
|
||||
var existing = _judgeOverrules.Find(overrule => overrule.JudgePlayerId == judgePlayerId);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.OverruledPlayerId = targetPlayerId;
|
||||
existing.OverruleNonce = overruleNonce;
|
||||
}
|
||||
else
|
||||
{
|
||||
_judgeOverrules.Add(new JudgeOverrule(judgePlayerId, targetPlayerId, overruleNonce));
|
||||
}
|
||||
}
|
||||
|
||||
private JudgeOverrule? GetWinningOverrule()
|
||||
{
|
||||
var gameData = Game.GameNet.GameData!;
|
||||
foreach (var overrule in _judgeOverrules)
|
||||
{
|
||||
var judge = gameData.GetPlayerById(overrule.JudgePlayerId);
|
||||
var target = gameData.GetPlayerById(overrule.OverruledPlayerId);
|
||||
|
||||
if (judge != null && target != null && !judge.Disconnected && !target.Disconnected)
|
||||
{
|
||||
return overrule;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async ValueTask CheckForEndVotingAsync()
|
||||
{
|
||||
if (_playerStates.All(ps => ps.IsDead || ps.DidVote))
|
||||
@@ -315,6 +393,28 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
var self = this.CalculateVotes();
|
||||
var max = MaxPair(self, out var tie);
|
||||
var exiled = tie ? null : Game.GameNet.GameData!.GetPlayerById(max.Key)?.Controller;
|
||||
|
||||
// exile or others?
|
||||
var wasOverruled = false;
|
||||
ushort overrideId = 0;
|
||||
|
||||
var winningOverrule = GetWinningOverrule();
|
||||
if (winningOverrule != null)
|
||||
{
|
||||
wasOverruled = true;
|
||||
overrideId = winningOverrule.OverruleNonce;
|
||||
|
||||
var overruled = Game.GameNet.GameData!.GetPlayerById(winningOverrule.OverruledPlayerId);
|
||||
if (overruled != null && overruled.IsImpostor)
|
||||
{
|
||||
exiled = overruled.Controller;
|
||||
}
|
||||
else
|
||||
{
|
||||
var judge = Game.GameNet.GameData!.GetPlayerById(winningOverrule.JudgePlayerId);
|
||||
exiled = judge?.Controller;
|
||||
}
|
||||
}
|
||||
|
||||
if (exiled != null && exiled.PlayerInfo != null)
|
||||
{
|
||||
@@ -322,7 +422,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
await _eventManager.CallAsync(new PlayerExileEvent(Game, Game.GetClientPlayer(exiled!.OwnerId)!, exiled));
|
||||
}
|
||||
|
||||
await _eventManager.CallAsync(new MeetingEndedEvent(Game, this, exiled, tie));
|
||||
await _eventManager.CallAsync(new MeetingEndedEvent(Game, this, exiled, tie, wasOverruled, overrideId));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user