Skip to content

Commit 9fd83d5

Browse files
authored
fix #2997 added 30s cooldown for failed polls on >10 people autohosts for non moderators (#2999)
1 parent 2b499f4 commit 9fd83d5

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

ZkLobbyServer/ServerBattle.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Diagnostics;
45
using System.Linq;
@@ -70,6 +71,12 @@ public class ServerBattle : Battle
7071

7172
public CommandPoll ActivePoll { get; private set; }
7273

74+
// Dictionary tracking cooldown for each user that fails a poll
75+
private readonly ConcurrentDictionary<string, DateTime> pollFailCooldowns = new ConcurrentDictionary<string, DateTime>();
76+
77+
// Returns the count of non-spectators in the current battle
78+
public int NonSpectatorPlayerCount => Users.Values.Count(x => x!= null && !x.IsSpectator);
79+
7380
public bool IsAutohost { get; private set; }
7481
public bool IsDefaultGame { get; private set; } = true;
7582
public bool IsCbalEnabled { get; private set; } = true;
@@ -652,13 +659,48 @@ public async Task<bool> StartVote(Func<string, string> eligibilitySelector, List
652659
await Respond(creator, $"Please wait, another poll already in progress: {ActivePoll.Topic}");
653660
return false;
654661
}
662+
663+
// Check if the user is on cooldown due to a failed poll
664+
if (creator != null && IsOnPollCooldown(creator?.User, out var remain))
665+
{
666+
await Respond(creator, $"You cannot start a vote for {remain} seconds.");
667+
return false;
668+
}
669+
670+
655671
await poll.Setup(eligibilitySelector, options, creator, topic);
656672
ActivePoll = poll;
657673
pollTimer.Interval = timeout * 1000;
658674
pollTimer.Enabled = true;
659675
return true;
660676
}
661677

678+
679+
private bool IsUserModerator(string username)
680+
{
681+
if (Users.TryGetValue(username, out var ubs) && (ubs?.LobbyUser?.IsAdmin == true))
682+
return true;
683+
if (server.ConnectedUsers.TryGetValue(username, out var con) && (con?.User?.IsAdmin == true)) // command can be sent by someone not in the battle
684+
return true;
685+
return false;
686+
}
687+
688+
689+
private bool IsOnPollCooldown(string username, out int remainSeconds)
690+
{
691+
remainSeconds = 0;
692+
if (pollFailCooldowns.TryGetValue(username, out var blockedUntil))
693+
{
694+
var diff = blockedUntil - DateTime.UtcNow;
695+
if (diff.TotalSeconds > 0)
696+
{
697+
remainSeconds = (int)Math.Ceiling(diff.TotalSeconds);
698+
return true;
699+
}
700+
}
701+
return false;
702+
}
703+
662704

663705
public async void StopVote()
664706
{
@@ -669,7 +711,26 @@ public async void StopVote()
669711
if (ActivePoll != null) await ActivePoll.End(false);
670712
if (pollTimer != null) pollTimer.Enabled = false;
671713
ActivePoll = null;
714+
715+
// Let the poll announce results or do final DB logging
672716
await oldPoll?.PublishResult();
717+
718+
719+
// 1) Did the poll pass?
720+
bool pollPassed = oldPoll?.Outcome?.ChosenOption != null;
721+
722+
// 2) Who started this poll?
723+
string creatorName = oldPoll?.Creator?.User;
724+
725+
// 3) If poll failed and conditions are met => apply 30s cooldown
726+
if (!string.IsNullOrEmpty(creatorName) && // user is known
727+
!pollPassed // poll is a failure
728+
&& IsAutohost // only relevant in autohost
729+
&& NonSpectatorPlayerCount >= 10
730+
&& !IsUserModerator(creatorName))
731+
{
732+
pollFailCooldowns[creatorName] = DateTime.UtcNow.AddSeconds(30);
733+
}
673734
}
674735
catch (Exception ex)
675736
{

0 commit comments

Comments
 (0)