Skip to content

Commit

Permalink
fix #2997 added 30s cooldown for failed polls on >10 people autohosts…
Browse files Browse the repository at this point in the history
… for non moderators (#2999)
  • Loading branch information
Licho1 authored Jan 7, 2025
1 parent 2b499f4 commit 9fd83d5
Showing 1 changed file with 61 additions and 0 deletions.
61 changes: 61 additions & 0 deletions ZkLobbyServer/ServerBattle.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
Expand Down Expand Up @@ -70,6 +71,12 @@ public class ServerBattle : Battle

public CommandPoll ActivePoll { get; private set; }

// Dictionary tracking cooldown for each user that fails a poll
private readonly ConcurrentDictionary<string, DateTime> pollFailCooldowns = new ConcurrentDictionary<string, DateTime>();

// Returns the count of non-spectators in the current battle
public int NonSpectatorPlayerCount => Users.Values.Count(x => x!= null && !x.IsSpectator);

public bool IsAutohost { get; private set; }
public bool IsDefaultGame { get; private set; } = true;
public bool IsCbalEnabled { get; private set; } = true;
Expand Down Expand Up @@ -652,13 +659,48 @@ public async Task<bool> StartVote(Func<string, string> eligibilitySelector, List
await Respond(creator, $"Please wait, another poll already in progress: {ActivePoll.Topic}");
return false;
}

// Check if the user is on cooldown due to a failed poll
if (creator != null && IsOnPollCooldown(creator?.User, out var remain))
{
await Respond(creator, $"You cannot start a vote for {remain} seconds.");
return false;
}


await poll.Setup(eligibilitySelector, options, creator, topic);
ActivePoll = poll;
pollTimer.Interval = timeout * 1000;
pollTimer.Enabled = true;
return true;
}


private bool IsUserModerator(string username)
{
if (Users.TryGetValue(username, out var ubs) && (ubs?.LobbyUser?.IsAdmin == true))
return true;
if (server.ConnectedUsers.TryGetValue(username, out var con) && (con?.User?.IsAdmin == true)) // command can be sent by someone not in the battle
return true;
return false;
}


private bool IsOnPollCooldown(string username, out int remainSeconds)
{
remainSeconds = 0;
if (pollFailCooldowns.TryGetValue(username, out var blockedUntil))
{
var diff = blockedUntil - DateTime.UtcNow;
if (diff.TotalSeconds > 0)
{
remainSeconds = (int)Math.Ceiling(diff.TotalSeconds);
return true;
}
}
return false;
}


public async void StopVote()
{
Expand All @@ -669,7 +711,26 @@ public async void StopVote()
if (ActivePoll != null) await ActivePoll.End(false);
if (pollTimer != null) pollTimer.Enabled = false;
ActivePoll = null;

// Let the poll announce results or do final DB logging
await oldPoll?.PublishResult();


// 1) Did the poll pass?
bool pollPassed = oldPoll?.Outcome?.ChosenOption != null;

// 2) Who started this poll?
string creatorName = oldPoll?.Creator?.User;

// 3) If poll failed and conditions are met => apply 30s cooldown
if (!string.IsNullOrEmpty(creatorName) && // user is known
!pollPassed // poll is a failure
&& IsAutohost // only relevant in autohost
&& NonSpectatorPlayerCount >= 10
&& !IsUserModerator(creatorName))
{
pollFailCooldowns[creatorName] = DateTime.UtcNow.AddSeconds(30);
}
}
catch (Exception ex)
{
Expand Down

0 comments on commit 9fd83d5

Please sign in to comment.