From fd2f559e23de2a6e78c36fbe80b5f3fe582461af Mon Sep 17 00:00:00 2001 From: Azim-Palmer Date: Sun, 1 Sep 2024 21:28:58 +0100 Subject: [PATCH 1/5] fix : Tourney Control modOptions ZeroK-RTS#2992 (#2993) --- Zero-K.info/Controllers/TourneyController.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Zero-K.info/Controllers/TourneyController.cs b/Zero-K.info/Controllers/TourneyController.cs index 2fbe8dd09..5660dbc59 100644 --- a/Zero-K.info/Controllers/TourneyController.cs +++ b/Zero-K.info/Controllers/TourneyController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web; using System.Web.Mvc; @@ -17,7 +18,9 @@ public class TourneyModel public List Team1Ids { get; set; } = new List(); public List Team2Ids { get; set; } = new List(); public string Title { get; set; } - public string ModoptString { get; set; } + + [DisplayFormat(ConvertEmptyStringToNull = false)] + public string ModoptString { get; set; } = ""; } // GET: Tourney From 6a6267a77aa82baf3c62896d4477465204d26d70 Mon Sep 17 00:00:00 2001 From: sprunk Date: Mon, 21 Oct 2024 03:03:55 +0200 Subject: [PATCH 2/5] Send pregame timing events to ingame --- Shared/LobbyClient/DedicatedServer.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Shared/LobbyClient/DedicatedServer.cs b/Shared/LobbyClient/DedicatedServer.cs index faa163e63..d4c38716f 100644 --- a/Shared/LobbyClient/DedicatedServer.cs +++ b/Shared/LobbyClient/DedicatedServer.cs @@ -22,7 +22,7 @@ public class DedicatedServer : IDisposable public static EventHandler AnyDedicatedExited; private readonly SpringPaths paths; - private readonly Timer timer = new Timer(20000); + private readonly Timer timer = new Timer(1000); private Dictionary > gamePrivateMessages = new Dictionary >(); @@ -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)) { @@ -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) From 2b499f44ec7061498d55c8d53a9834e8bf4d7625 Mon Sep 17 00:00:00 2001 From: Licho Date: Tue, 7 Jan 2025 20:19:03 +0100 Subject: [PATCH 3/5] fix unitsync issues with silly wars by updating unitsync to 105.1.1-1485 --- Shared/PlasmaShared/GlobalConst.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shared/PlasmaShared/GlobalConst.cs b/Shared/PlasmaShared/GlobalConst.cs index ec5429859..9f4001492 100644 --- a/Shared/PlasmaShared/GlobalConst.cs +++ b/Shared/PlasmaShared/GlobalConst.cs @@ -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 DlcToKudos = new Dictionary() { { 842950, 100 }, { 842951, 250 }, { 842952, 500 } }; From 9fd83d53c40276146501f55bf9caaf63eeda7132 Mon Sep 17 00:00:00 2001 From: Licho Date: Tue, 7 Jan 2025 23:02:48 +0100 Subject: [PATCH 4/5] fix #2997 added 30s cooldown for failed polls on >10 people autohosts for non moderators (#2999) --- ZkLobbyServer/ServerBattle.cs | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/ZkLobbyServer/ServerBattle.cs b/ZkLobbyServer/ServerBattle.cs index f1f760915..058983f24 100644 --- a/ZkLobbyServer/ServerBattle.cs +++ b/ZkLobbyServer/ServerBattle.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -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 pollFailCooldowns = new ConcurrentDictionary(); + + // 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; @@ -652,6 +659,15 @@ public async Task StartVote(Func 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; @@ -659,6 +675,32 @@ public async Task StartVote(Func eligibilitySelector, List 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() { @@ -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) { From 504ebc4b36a330d38049b88454b85781884d17f5 Mon Sep 17 00:00:00 2001 From: Licho Date: Tue, 7 Jan 2025 23:26:59 +0100 Subject: [PATCH 5/5] fixed issue where corrupted lobby server message can lead to null player be tracked by infra and then failing in !ring etc. fix #2998 --- Shared/LobbyClient/Spring.SpringBattleContext.cs | 2 ++ ZkLobbyServer/ServerBattle.cs | 2 +- ZkLobbyServer/autohost/Commands/CmdKick.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Shared/LobbyClient/Spring.SpringBattleContext.cs b/Shared/LobbyClient/Spring.SpringBattleContext.cs index 4d0ad9ef0..36114ecb0 100644 --- a/Shared/LobbyClient/Spring.SpringBattleContext.cs +++ b/Shared/LobbyClient/Spring.SpringBattleContext.cs @@ -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) { diff --git a/ZkLobbyServer/ServerBattle.cs b/ZkLobbyServer/ServerBattle.cs index 058983f24..69b6e4734 100644 --- a/ZkLobbyServer/ServerBattle.cs +++ b/ZkLobbyServer/ServerBattle.cs @@ -181,7 +181,7 @@ public List 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) diff --git a/ZkLobbyServer/autohost/Commands/CmdKick.cs b/ZkLobbyServer/autohost/Commands/CmdKick.cs index 68ea77d61..63d1cf466 100644 --- a/ZkLobbyServer/autohost/Commands/CmdKick.cs +++ b/ZkLobbyServer/autohost/Commands/CmdKick.cs @@ -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 {