Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix #2997 added 30s cooldown for failed polls on >10 people autohosts for non moderators #2999

Merged
merged 2 commits into from
Jan 7, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading