Skip to content

Commit

Permalink
update lobby server with fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Licho1 authored Jan 7, 2025
1 parent e447a0e commit ad67a68
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 8 deletions.
10 changes: 6 additions & 4 deletions Shared/LobbyClient/DedicatedServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class DedicatedServer : IDisposable
public static EventHandler<SpringBattleContext> AnyDedicatedExited;

private readonly SpringPaths paths;
private readonly Timer timer = new Timer(20000);
private readonly Timer timer = new Timer(1000);

private Dictionary<string, HashSet<byte> > gamePrivateMessages = new Dictionary<string, HashSet<byte> >();

Expand Down Expand Up @@ -514,8 +514,7 @@ private void timer_Elapsed(object sender, ElapsedEventArgs e)
try
{
var timeSinceStart = DateTime.UtcNow.Subtract(Context.StartTime).TotalSeconds;
const int timeToWait = 160; // force start after 180s
const int timeToWarn = 100; // warn people after 120s
const int timeToWait = 160; // force start after this many seconds

if (Context.IsHosting && IsRunning && (Context.IngameStartTime == null))
{
Expand All @@ -524,7 +523,10 @@ private void timer_Elapsed(object sender, ElapsedEventArgs e)
Context.IsTimeoutForceStarted = true;
ForceStart();
}
else if (timeSinceStart > timeToWarn) SayGame($"Game will be force started in {Math.Max(20, timeToWait - Math.Round(timeSinceStart))} seconds");
else
{
talker.SendText($"/luarules pregame_timer_seconds {Convert.ToInt32(timeToWait - timeSinceStart)}");
}
}
}
catch (Exception ex)
Expand Down
2 changes: 2 additions & 0 deletions Shared/LobbyClient/Spring.SpringBattleContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public class SpringBattleContext

public BattlePlayerResult GetOrAddPlayer(string name)
{
if (string.IsNullOrEmpty(name)) return null; // we don't want to add null players

var ret = ActualPlayers.FirstOrDefault(y => y.Name == name);
if (ret == null)
{
Expand Down
2 changes: 1 addition & 1 deletion Shared/PlasmaShared/GlobalConst.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ public static void OverrideContentServiceClient(IContentServiceClient client)
}


public static string UnitSyncEngine = "unitsync";
public static string UnitSyncEngine = "105.1.1-1485-g78f9a2c";

public static int SteamContributionJarID = 2;
public static Dictionary<ulong, int> DlcToKudos = new Dictionary<ulong, int>() { { 842950, 100 }, { 842951, 250 }, { 842952, 500 } };
Expand Down
5 changes: 4 additions & 1 deletion Zero-K.info/Controllers/TourneyController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using System.Web.Mvc;
Expand All @@ -17,7 +18,9 @@ public class TourneyModel
public List<int> Team1Ids { get; set; } = new List<int>();
public List<int> Team2Ids { get; set; } = new List<int>();
public string Title { get; set; }
public string ModoptString { get; set; }

[DisplayFormat(ConvertEmptyStringToNull = false)]
public string ModoptString { get; set; } = "";
}

// GET: Tourney
Expand Down
63 changes: 62 additions & 1 deletion 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 @@ -174,7 +181,7 @@ public List<string> GetAllUserNames()
{
var ret = Users.Select(x => x.Key).ToList();
if (spring.IsRunning) ret.AddRange(spring.Context.ActualPlayers.Select(x => x.Name));
return ret.Distinct().ToList();
return ret.Distinct().Where(x=>x!=null).ToList();
}

public BattleCommand GetCommandByName(string name)
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
2 changes: 1 addition & 1 deletion ZkLobbyServer/autohost/Commands/CmdKick.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ private bool NotifyAdminChannel(ServerBattle battle, Say e, bool isActualKick)
{
gtype = string.Format("game on map {0}", battle.MapName);
PlasmaShared.BattlePlayerResult res = battle.spring.Context.GetOrAddPlayer(target);
isspec = res.IsSpectator;
isspec = res?.IsSpectator == true;
}
else
{
Expand Down

0 comments on commit ad67a68

Please sign in to comment.