Vote type delay, code comments.
Added doc comments to server side voting API. There is now a 4 minute delay between creating votes of the same type. Shuffled some code around. Made a StandardVoteType enum instead of string IDs.
This commit is contained in:
@@ -12,11 +12,14 @@
|
||||
</BoxContainer>
|
||||
<hudUi:HighDivider />
|
||||
|
||||
<BoxContainer Margin="8 2 8 0" VerticalExpand="True" VerticalAlignment="Top" Orientation="Horizontal">
|
||||
<OptionButton Name="VoteTypeButton" HorizontalExpand="True" />
|
||||
<Control HorizontalExpand="True">
|
||||
<OptionButton Name="VoteSecondButton" Visible="False" />
|
||||
</Control>
|
||||
<BoxContainer Orientation="Vertical" Margin="8 2 8 0" VerticalExpand="True" VerticalAlignment="Top">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<OptionButton Name="VoteTypeButton" HorizontalExpand="True" />
|
||||
<Control HorizontalExpand="True">
|
||||
<OptionButton Name="VoteSecondButton" Visible="False" />
|
||||
</Control>
|
||||
</BoxContainer>
|
||||
<Label Name="VoteTypeTimeoutLabel" Visible="False" />
|
||||
</BoxContainer>
|
||||
|
||||
<Button Margin="8 2" Name="CreateButton" Text="{Loc 'ui-vote-create-button'}" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Voting;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
@@ -9,6 +10,7 @@ using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Voting.UI
|
||||
{
|
||||
@@ -16,12 +18,15 @@ namespace Content.Client.Voting.UI
|
||||
public partial class VoteCallMenu : BaseWindow
|
||||
{
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IVoteManager _voteManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
public static readonly (string name, string id, (string name, string id)[]? secondaries)[] AvailableVoteTypes =
|
||||
{
|
||||
("ui-vote-type-restart", "restart", null),
|
||||
("ui-vote-type-gamemode", "preset", null)
|
||||
};
|
||||
public static readonly (string name, StandardVoteType type, (string name, string id)[]? secondaries)[]
|
||||
AvailableVoteTypes =
|
||||
{
|
||||
("ui-vote-type-restart", StandardVoteType.Restart, null),
|
||||
("ui-vote-type-gamemode", StandardVoteType.Preset, null)
|
||||
};
|
||||
|
||||
public VoteCallMenu()
|
||||
{
|
||||
@@ -42,6 +47,33 @@ namespace Content.Client.Voting.UI
|
||||
CreateButton.OnPressed += CreatePressed;
|
||||
}
|
||||
|
||||
protected override void Opened()
|
||||
{
|
||||
base.Opened();
|
||||
|
||||
_voteManager.CanCallVoteChanged += CanCallVoteChanged;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
|
||||
_voteManager.CanCallVoteChanged -= CanCallVoteChanged;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
UpdateVoteTimeout();
|
||||
}
|
||||
|
||||
private void CanCallVoteChanged(bool obj)
|
||||
{
|
||||
if (!obj)
|
||||
Close();
|
||||
}
|
||||
|
||||
private void CreatePressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
var typeId = VoteTypeButton.SelectedId;
|
||||
@@ -62,6 +94,20 @@ namespace Content.Client.Voting.UI
|
||||
Close();
|
||||
}
|
||||
|
||||
private void UpdateVoteTimeout()
|
||||
{
|
||||
var (_, typeKey, _) = AvailableVoteTypes[VoteTypeButton.SelectedId];
|
||||
var isAvailable = _voteManager.CanCallStandardVote(typeKey, out var timeout);
|
||||
CreateButton.Disabled = !isAvailable;
|
||||
VoteTypeTimeoutLabel.Visible = !isAvailable;
|
||||
|
||||
if (!isAvailable)
|
||||
{
|
||||
var remaining = timeout - _gameTiming.RealTime;
|
||||
VoteTypeTimeoutLabel.Text = Loc.GetString("ui-vote-type-timeout", ("remaining", remaining.ToString("mm\\:ss")));
|
||||
}
|
||||
}
|
||||
|
||||
private static void VoteSecondSelected(OptionButton.ItemSelectedEventArgs obj)
|
||||
{
|
||||
obj.Button.SelectId(obj.Id);
|
||||
|
||||
@@ -19,7 +19,10 @@ namespace Content.Client.Voting
|
||||
void ClearPopupContainer();
|
||||
void SetPopupContainer(Control container);
|
||||
bool CanCallVote { get; }
|
||||
|
||||
bool CanCallStandardVote(StandardVoteType type, out TimeSpan whenCan);
|
||||
event Action<bool> CanCallVoteChanged;
|
||||
event Action CanCallStandardVotesChanged;
|
||||
}
|
||||
|
||||
public sealed class VoteManager : IVoteManager
|
||||
@@ -29,13 +32,17 @@ namespace Content.Client.Voting
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
|
||||
private readonly Dictionary<StandardVoteType, TimeSpan> _standardVoteTimeouts = new();
|
||||
private readonly Dictionary<int, ActiveVote> _votes = new();
|
||||
private readonly Dictionary<int, UI.VotePopup> _votePopups = new();
|
||||
private Control? _popupContainer;
|
||||
|
||||
public bool CanCallVote { get; private set; }
|
||||
|
||||
public event Action<bool>? CanCallVoteChanged;
|
||||
|
||||
public event Action? CanCallStandardVotesChanged;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgVoteData>(ReceiveVoteData);
|
||||
@@ -54,6 +61,11 @@ namespace Content.Client.Voting
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanCallStandardVote(StandardVoteType type, out TimeSpan whenCan)
|
||||
{
|
||||
return !_standardVoteTimeouts.TryGetValue(type, out whenCan);
|
||||
}
|
||||
|
||||
public void ClearPopupContainer()
|
||||
{
|
||||
if (_popupContainer == null)
|
||||
@@ -161,11 +173,20 @@ namespace Content.Client.Voting
|
||||
|
||||
private void ReceiveVoteCanCall(MsgVoteCanCall message)
|
||||
{
|
||||
if (CanCallVote == message.CanCall)
|
||||
return;
|
||||
if (CanCallVote != message.CanCall)
|
||||
{
|
||||
// TODO: actually use the "when can call vote" time for UI display or something.
|
||||
CanCallVote = message.CanCall;
|
||||
CanCallVoteChanged?.Invoke(CanCallVote);
|
||||
}
|
||||
|
||||
CanCallVote = message.CanCall;
|
||||
CanCallVoteChanged?.Invoke(CanCallVote);
|
||||
_standardVoteTimeouts.Clear();
|
||||
foreach (var (type, time) in message.VotesUnavailable)
|
||||
{
|
||||
_standardVoteTimeouts.Add(type, _gameTiming.RealServerToLocal(time));
|
||||
}
|
||||
|
||||
CanCallStandardVotesChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void SendCastVote(int voteId, int option)
|
||||
|
||||
@@ -1,21 +1,87 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Voting.Managers;
|
||||
using Robust.Server.Player;
|
||||
|
||||
namespace Content.Server.Voting
|
||||
{
|
||||
/// <summary>
|
||||
/// A handle to vote, active or past.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Vote options are referred to by UI/networking as integer IDs.
|
||||
/// These IDs are the index of the vote option in the <see cref="VoteOptions.Options"/> list
|
||||
/// used to create the vote.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public interface IVoteHandle
|
||||
{
|
||||
/// <summary>
|
||||
/// The numeric ID of the vote. Can be used in <see cref="IVoteManager.TryGetVote"/>.
|
||||
/// </summary>
|
||||
int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The title of the vote.
|
||||
/// </summary>
|
||||
string Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Text representing who/what initiated the vote.
|
||||
/// </summary>
|
||||
string InitiatorText { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the vote has finished and is no longer active.
|
||||
/// </summary>
|
||||
bool Finished { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the vote was cancelled by an administrator and did not finish naturally.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this is true, <see cref="Finished"/> is also true.
|
||||
/// </remarks>
|
||||
bool Cancelled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Current count of votes per option type.
|
||||
/// </summary>
|
||||
IReadOnlyDictionary<object, int> VotesPerOption { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this vote has successfully finished.
|
||||
/// </summary>
|
||||
event VoteFinishedEventHandler OnFinished;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked if this vote gets cancelled.
|
||||
/// </summary>
|
||||
event VoteCancelledEventHandler OnCancelled;
|
||||
|
||||
/// <summary>
|
||||
/// Check whether a certain integer option ID is valid.
|
||||
/// </summary>
|
||||
/// <param name="optionId">The integer ID of the option.</param>
|
||||
/// <returns>True if the option ID is valid, false otherwise.</returns>
|
||||
bool IsValidOption(int optionId);
|
||||
|
||||
/// <summary>
|
||||
/// Cast a vote for a specific player.
|
||||
/// </summary>
|
||||
/// <param name="session">The player session to vote for.</param>
|
||||
/// <param name="optionId">
|
||||
/// The integer option ID to vote for. If null, "no vote" is selected (abstaining).
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="optionId"/> is not a valid option ID.
|
||||
/// </exception>
|
||||
void CastVote(IPlayerSession session, int? optionId);
|
||||
|
||||
/// <summary>
|
||||
/// Cancel this vote.
|
||||
/// </summary>
|
||||
void Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Voting;
|
||||
using Robust.Server.Player;
|
||||
|
||||
|
||||
namespace Content.Server.Voting.Managers
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages in-game votes that players can vote on.
|
||||
/// </summary>
|
||||
public interface IVoteManager
|
||||
{
|
||||
/// <summary>
|
||||
/// All votes that are currently active and can be voted on by players.
|
||||
/// </summary>
|
||||
IEnumerable<IVoteHandle> ActiveVotes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Try to get a vote handle by integer ID.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only votes that are currently active can be retrieved.
|
||||
/// </remarks>
|
||||
/// <param name="voteId">The integer ID of the vote, corresponding to <see cref="IVoteHandle.Id"/>.</param>
|
||||
/// <param name="vote">The vote handle, if found.</param>
|
||||
/// <returns>True if the vote was found and it was returned, false otherwise.</returns>
|
||||
bool TryGetVote(int voteId, [NotNullWhen(true)] out IVoteHandle? vote);
|
||||
|
||||
bool CanCallVote(IPlayerSession initiator);
|
||||
void CreateRestartVote(IPlayerSession? initiator);
|
||||
void CreatePresetVote(IPlayerSession? initiator);
|
||||
/// <summary>
|
||||
/// Check if a player can initiate a vote right now. Optionally of a specified standard type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Players cannot start votes if they have made another vote recently,
|
||||
/// or if the specified vote type has been made recently.
|
||||
/// </remarks>
|
||||
/// <param name="initiator">The player to check.</param>
|
||||
/// <param name="voteType">
|
||||
/// The standard vote type to check cooldown for.
|
||||
/// Null to only check timeout for all vote types for the specified player.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if <paramref name="initiator"/> can start votes right now,
|
||||
/// and if provided if they can start votes of type <paramref name="voteType"/>.
|
||||
/// </returns>
|
||||
bool CanCallVote(IPlayerSession initiator, StandardVoteType? voteType = null);
|
||||
|
||||
/// <summary>
|
||||
/// Initiate a standard vote such as restart round, that can be initiated by players.
|
||||
/// </summary>
|
||||
/// <param name="initiator">
|
||||
/// The player that called the vote.
|
||||
/// If null it is assumed to be an automatic vote by the server.
|
||||
/// </param>
|
||||
/// <param name="voteType">The type of standard vote to make.</param>
|
||||
void CreateStandardVote(IPlayerSession? initiator, StandardVoteType voteType);
|
||||
|
||||
/// <summary>
|
||||
/// Create a non-standard vote with special parameters.
|
||||
/// </summary>
|
||||
/// <param name="options">The options specifying the vote's behavior.</param>
|
||||
/// <returns>A handle to the created vote.</returns>
|
||||
IVoteHandle CreateVote(VoteOptions options);
|
||||
|
||||
void Initialize();
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Voting;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
@@ -13,7 +12,24 @@ namespace Content.Server.Voting.Managers
|
||||
{
|
||||
public sealed partial class VoteManager
|
||||
{
|
||||
public void CreateRestartVote(IPlayerSession? initiator)
|
||||
public void CreateStandardVote(IPlayerSession? initiator, StandardVoteType voteType)
|
||||
{
|
||||
switch (voteType)
|
||||
{
|
||||
case StandardVoteType.Restart:
|
||||
CreateRestartVote(initiator);
|
||||
break;
|
||||
case StandardVoteType.Preset:
|
||||
CreatePresetVote(initiator);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(voteType), voteType, null);
|
||||
}
|
||||
|
||||
TimeoutStandardVote(voteType);
|
||||
}
|
||||
|
||||
private void CreateRestartVote(IPlayerSession? initiator)
|
||||
{
|
||||
var alone = _playerManager.PlayerCount == 1 && initiator != null;
|
||||
var options = new VoteOptions
|
||||
@@ -72,7 +88,7 @@ namespace Content.Server.Voting.Managers
|
||||
}
|
||||
}
|
||||
|
||||
public void CreatePresetVote(IPlayerSession? initiator)
|
||||
private void CreatePresetVote(IPlayerSession? initiator)
|
||||
{
|
||||
var presets = new Dictionary<string, string>
|
||||
{
|
||||
@@ -122,5 +138,12 @@ namespace Content.Server.Voting.Managers
|
||||
EntitySystem.Get<GameTicker>().SetStartPreset(picked);
|
||||
};
|
||||
}
|
||||
|
||||
private void TimeoutStandardVote(StandardVoteType type)
|
||||
{
|
||||
var timeout = TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.VoteSameTypeTimeout));
|
||||
_standardVoteTimeout[type] = _timing.RealTime + timeout;
|
||||
DirtyCanCallVoteAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Linq;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Collections;
|
||||
using Content.Shared.Voting;
|
||||
@@ -39,6 +38,7 @@ namespace Content.Server.Voting.Managers
|
||||
private readonly Dictionary<int, VoteReg> _votes = new();
|
||||
private readonly Dictionary<int, VoteHandle> _voteHandles = new();
|
||||
|
||||
private readonly Dictionary<StandardVoteType, TimeSpan> _standardVoteTimeout = new();
|
||||
private readonly Dictionary<NetUserId, TimeSpan> _voteTimeout = new();
|
||||
private readonly HashSet<IPlayerSession> _playerCanCallVoteDirty = new();
|
||||
|
||||
@@ -146,6 +146,21 @@ namespace Content.Server.Voting.Managers
|
||||
DirtyCanCallVote(session);
|
||||
}
|
||||
|
||||
// Handle standard vote timeouts.
|
||||
var stdTimeoutRemQueue = new RemQueue<StandardVoteType>();
|
||||
foreach (var (type, timeout) in _standardVoteTimeout)
|
||||
{
|
||||
if (timeout < _timing.RealTime)
|
||||
stdTimeoutRemQueue.Add(type);
|
||||
}
|
||||
|
||||
foreach (var type in stdTimeoutRemQueue)
|
||||
{
|
||||
_standardVoteTimeout.Remove(type);
|
||||
|
||||
DirtyCanCallVoteAll();
|
||||
}
|
||||
|
||||
// Handle dirty canCallVotes.
|
||||
foreach (var dirtyPlayer in _playerCanCallVoteDirty)
|
||||
{
|
||||
@@ -240,26 +255,47 @@ namespace Content.Server.Voting.Managers
|
||||
private void SendUpdateCanCallVote(IPlayerSession player)
|
||||
{
|
||||
var msg = _netManager.CreateNetMessage<MsgVoteCanCall>();
|
||||
msg.CanCall = CanCallVote(player);
|
||||
msg.CanCall = CanCallVote(player, null, out var isAdmin, out var timeSpan);
|
||||
msg.WhenCanCallVote = timeSpan;
|
||||
|
||||
msg.VotesUnavailable = isAdmin
|
||||
? Array.Empty<(StandardVoteType, TimeSpan)>()
|
||||
: _standardVoteTimeout.Select(kv => (kv.Key, kv.Value)).ToArray();
|
||||
|
||||
_netManager.ServerSendMessage(msg, player.ConnectedClient);
|
||||
}
|
||||
|
||||
public bool CanCallVote(IPlayerSession player)
|
||||
private bool CanCallVote(
|
||||
IPlayerSession initiator,
|
||||
StandardVoteType? voteType,
|
||||
out bool isAdmin,
|
||||
out TimeSpan timeSpan)
|
||||
{
|
||||
isAdmin = false;
|
||||
timeSpan = default;
|
||||
|
||||
// Admins can always call votes.
|
||||
if (_adminMgr.HasAdminFlag(player, AdminFlags.Admin))
|
||||
if (_adminMgr.HasAdminFlag(initiator, AdminFlags.Admin))
|
||||
{
|
||||
isAdmin = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Cannot start vote if vote is already active (as non-admin).
|
||||
if (_votes.Count != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !_voteTimeout.ContainsKey(player.UserId);
|
||||
// Standard vote on timeout, no calling.
|
||||
// Ghosts I understand you're dead but stop spamming the restart vote bloody hell.
|
||||
if (voteType != null && _standardVoteTimeout.ContainsKey(voteType.Value))
|
||||
return false;
|
||||
|
||||
return !_voteTimeout.TryGetValue(initiator.UserId, out timeSpan);
|
||||
}
|
||||
|
||||
public bool CanCallVote(IPlayerSession initiator, StandardVoteType? voteType = null)
|
||||
{
|
||||
return CanCallVote(initiator, voteType, out _, out _);
|
||||
}
|
||||
|
||||
private void EndVote(VoteReg v)
|
||||
@@ -484,7 +520,7 @@ namespace Content.Server.Voting.Managers
|
||||
}
|
||||
|
||||
public IEnumerable<object> Keys => _reg.Entries.Select(c => c.Data);
|
||||
public IEnumerable<int> Values => _reg.Entries.Select(c => c.Votes);
|
||||
public IEnumerable<int> Values => _reg.Entries.Select(c => c.Votes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.Voting.Managers;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Voting;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
|
||||
namespace Content.Server.Voting
|
||||
{
|
||||
[AnyCommand]
|
||||
@@ -28,28 +27,21 @@ namespace Content.Server.Voting
|
||||
return;
|
||||
}
|
||||
|
||||
var type = args[0];
|
||||
if (!Enum.TryParse<StandardVoteType>(args[0], ignoreCase: true, out var type))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("create-vote-command-invalid-vote-type"));
|
||||
return;
|
||||
}
|
||||
|
||||
var mgr = IoCManager.Resolve<IVoteManager>();
|
||||
|
||||
if (shell.Player != null && !mgr.CanCallVote((IPlayerSession) shell.Player))
|
||||
if (shell.Player != null && !mgr.CanCallVote((IPlayerSession) shell.Player, type))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("create-vote-command-cannot-call-vote-now"));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case "restart":
|
||||
mgr.CreateRestartVote((IPlayerSession?) shell.Player);
|
||||
break;
|
||||
case "preset":
|
||||
mgr.CreatePresetVote((IPlayerSession?) shell.Player);
|
||||
break;
|
||||
default:
|
||||
shell.WriteError(Loc.GetString("create-vote-command-invalid-vote-type"));
|
||||
break;
|
||||
}
|
||||
mgr.CreateStandardVote((IPlayerSession?) shell.Player, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -330,9 +330,18 @@ namespace Content.Shared.CCVar
|
||||
* VOTE
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// The required ratio of the server that must agree for a restart round vote to go through.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> VoteRestartRequiredRatio =
|
||||
CVarDef.Create("vote.restart_required_ratio", 0.8f, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// The delay which two votes of the same type are allowed to be made by separate people, in seconds.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<float> VoteSameTypeTimeout =
|
||||
CVarDef.Create("vote.same_type_timeout", 240f, CVar.SERVERONLY);
|
||||
|
||||
/*
|
||||
* BAN
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Lidgren.Network;
|
||||
using System;
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Shared.Voting
|
||||
@@ -10,16 +11,47 @@ namespace Content.Shared.Voting
|
||||
{
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
// If true, we can currently call votes.
|
||||
public bool CanCall;
|
||||
// When we can call votes again in server RealTime.
|
||||
// Can be null if the reason is something not timeout related.
|
||||
public TimeSpan WhenCanCallVote;
|
||||
|
||||
// Which standard votes are currently unavailable, and when will they become available.
|
||||
public (StandardVoteType type, TimeSpan whenAvailable)[] VotesUnavailable = default!;
|
||||
|
||||
// It's possible to be able to call votes but all standard votes to be timed out.
|
||||
// In this case you can open the interface and see the timeout listed there, I suppose.
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer)
|
||||
{
|
||||
CanCall = buffer.ReadBoolean();
|
||||
buffer.ReadPadBits();
|
||||
WhenCanCallVote = TimeSpan.FromTicks(buffer.ReadInt64());
|
||||
|
||||
var lenVotes = buffer.ReadByte();
|
||||
VotesUnavailable = new (StandardVoteType type, TimeSpan whenAvailable)[lenVotes];
|
||||
for (var i = 0; i < lenVotes; i++)
|
||||
{
|
||||
var type = (StandardVoteType) buffer.ReadByte();
|
||||
var timeOut = TimeSpan.FromTicks(buffer.ReadInt64());
|
||||
|
||||
VotesUnavailable[i] = (type, timeOut);
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteToBuffer(NetOutgoingMessage buffer)
|
||||
{
|
||||
buffer.Write(CanCall);
|
||||
buffer.WritePadBits();
|
||||
buffer.Write(WhenCanCallVote.Ticks);
|
||||
|
||||
buffer.Write((byte) VotesUnavailable.Length);
|
||||
foreach (var (type, timeout) in VotesUnavailable)
|
||||
{
|
||||
buffer.Write((byte) type);
|
||||
buffer.Write(timeout.Ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
18
Content.Shared/Voting/StandardVoteType.cs
Normal file
18
Content.Shared/Voting/StandardVoteType.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Content.Shared.Voting
|
||||
{
|
||||
/// <summary>
|
||||
/// Standard vote types that players can initiate themselves from the escape menu.
|
||||
/// </summary>
|
||||
public enum StandardVoteType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Vote to restart the round.
|
||||
/// </summary>
|
||||
Restart,
|
||||
|
||||
/// <summary>
|
||||
/// Vote to change the game preset for next round.
|
||||
/// </summary>
|
||||
Preset
|
||||
}
|
||||
}
|
||||
4
Resources/Changelog/Parts/standard_vote_delay.yml
Normal file
4
Resources/Changelog/Parts/standard_vote_delay.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: PJB
|
||||
changes:
|
||||
- type: Tweak
|
||||
message: You can now only make the same type of vote once every 4 minutes.
|
||||
@@ -7,10 +7,13 @@ ui-vote-create-title = Call Vote
|
||||
# Submit button in the vote create button
|
||||
ui-vote-create-button = Call Vote
|
||||
|
||||
# Timeout text if a standard vote type is currently on timeout.
|
||||
ui-vote-type-timeout = This vote was called too recently ({$remaining})
|
||||
|
||||
# Hue hue hue
|
||||
ui-vote-fluff = Powered by Robust™ Anti-Tamper Technology
|
||||
|
||||
## Vote menu command
|
||||
|
||||
ui-vote-menu-command-description = Opens the voting menu
|
||||
ui-vote-menu-command-help-text = Usage: votemenu
|
||||
ui-vote-menu-command-help-text = Usage: votemenu
|
||||
|
||||
Reference in New Issue
Block a user