From 7ff351c580dbf022d41399bcab9a00f160ce8251 Mon Sep 17 00:00:00 2001 From: gubsy420 Date: Fri, 11 Apr 2025 17:58:53 -0400 Subject: [PATCH 1/4] Updated CsServer.cs to handle workshop map IDs passed in via a new user config file, "workshop_maps.json" --- PugSharp/CsServer.cs | 58 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/PugSharp/CsServer.cs b/PugSharp/CsServer.cs index e66999f..e4b2798 100644 --- a/PugSharp/CsServer.cs +++ b/PugSharp/CsServer.cs @@ -9,6 +9,9 @@ using PugSharp.Models; using PugSharp.Server.Contract; +using System.IO; +using System.Text.Json; + namespace PugSharp; public class CsServer : ICsServer @@ -16,10 +19,15 @@ public class CsServer : ICsServer private readonly ILogger _Logger; private readonly ICssDispatcher _Dispatcher; + private Dictionary? _workshopMapLookup; + private readonly string _workshopMapConfigPath; + public CsServer(ILogger logger, ICssDispatcher dispatcher) { _Logger = logger; _Dispatcher = dispatcher; + + _workshopMapConfigPath = Path.Combine(GameDirectory, "csgo", "PugSharp", "Config", "workshop_maps.json"); } public string GameDirectory => CounterStrikeSharp.API.Server.GameDirectory; @@ -210,14 +218,56 @@ public void SwitchMap(string selectedMap) { _Dispatcher.NextWorldUpdate(() => { - if (!IsMapValid(selectedMap)) + if (string.IsNullOrWhiteSpace(selectedMap)) { - _Logger.LogInformation("The selected map is not valid: \"{SelectedMap}\"!", selectedMap); + _Logger.LogInformation("The selected map is null or empty!"); return; } - _Logger.LogInformation("Switch map to: \"{SelectedMap}\"!", selectedMap); - ExecuteCommand($"changelevel {selectedMap}"); + // Lazy-load the workshop map config if not already loaded + if (_workshopMapLookup == null) + { + try + { + if (File.Exists(_workshopMapConfigPath)) + { + string json = File.ReadAllText(_workshopMapConfigPath); + _workshopMapLookup = JsonSerializer.Deserialize>(json); + _Logger.LogInformation("Loaded workshop map config from {Path}", _workshopMapConfigPath); + } + else + { + _Logger.LogWarning("Workshop map config not found at {Path}. Workshop maps will not be available.", _workshopMapConfigPath); + _workshopMapLookup = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + } + catch (Exception ex) + { + _Logger.LogError(ex, "Failed to load workshop map config from {Path}", _workshopMapConfigPath); + _workshopMapLookup = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + } + + string command; + + if (_workshopMapLookup != null && _workshopMapLookup.TryGetValue(selectedMap, out var workshopId)) + { + _Logger.LogInformation("Translating map \"{SelectedMap}\" to Workshop ID: {WorkshopId}", selectedMap, workshopId); + command = $"host_workshop_map {workshopId}"; + } + else + { + if (!IsMapValid(selectedMap)) + { + _Logger.LogInformation("The selected map is not valid: \"{SelectedMap}\"!", selectedMap); + return; + } + + _Logger.LogInformation("Switching to standard map: \"{SelectedMap}\"", selectedMap); + command = $"changelevel {selectedMap}"; + } + + ExecuteCommand(command); }); } From fd3985de633c96acccdaa64e845d31e43cc7cad3 Mon Sep 17 00:00:00 2001 From: gubsy420 Date: Sun, 13 Apr 2025 15:44:05 -0400 Subject: [PATCH 2/4] Updated formatting of the two new workshop related fields. Modifies workshop map lookup to occur via asynchronous function that runs on entry of MatchState.none --- PugSharp.Match/Match.cs | 5 ++-- PugSharp/CsServer.cs | 62 ++++++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/PugSharp.Match/Match.cs b/PugSharp.Match/Match.cs index 9d24e3e..2fcfe92 100644 --- a/PugSharp.Match/Match.cs +++ b/PugSharp.Match/Match.cs @@ -112,8 +112,9 @@ private void InitializeStateMachine() #pragma warning restore MA0051 // Method is too long { _MatchStateMachine.Configure(MatchState.None) - .PermitDynamicIf(MatchCommand.LoadMatch, () => HasRestoredMatch() ? MatchState.RestoreMatch : MatchState.WaitingForPlayersConnectedReady); - + .PermitDynamicIf(MatchCommand.LoadMatch, () => HasRestoredMatch() ? MatchState.RestoreMatch : MatchState.WaitingForPlayersConnectedReady) + .OnEntryAsync(() => _CsServer.InitializeWorkshopMapLookupAsync()); + _MatchStateMachine.Configure(MatchState.WaitingForPlayersConnectedReady) .PermitDynamicIf(MatchCommand.PlayerReady, () => HasRestoredMatch() ? MatchState.MatchRunning : MatchState.DefineTeams, AllPlayersAreReady) .OnEntry(StartWarmup) diff --git a/PugSharp/CsServer.cs b/PugSharp/CsServer.cs index e4b2798..e9cd974 100644 --- a/PugSharp/CsServer.cs +++ b/PugSharp/CsServer.cs @@ -19,15 +19,15 @@ public class CsServer : ICsServer private readonly ILogger _Logger; private readonly ICssDispatcher _Dispatcher; - private Dictionary? _workshopMapLookup; - private readonly string _workshopMapConfigPath; + private Dictionary? _WorkshopMapLookup; + private readonly string _WorkshopMapConfigPath; public CsServer(ILogger logger, ICssDispatcher dispatcher) { _Logger = logger; _Dispatcher = dispatcher; - _workshopMapConfigPath = Path.Combine(GameDirectory, "csgo", "PugSharp", "Config", "workshop_maps.json"); + _WorkshopMapConfigPath = Path.Combine(GameDirectory, "csgo", "PugSharp", "Config", "workshop_maps.json"); } public string GameDirectory => CounterStrikeSharp.API.Server.GameDirectory; @@ -214,6 +214,36 @@ public void UnpauseMatch() ExecuteCommand("mp_unpause_match"); } + public async Task InitializeWorkshopMapLookupAsync() + { + if (_WorkshopMapLookup != null) + return; // Already loaded + + try + { + if (File.Exists(_WorkshopMapConfigPath)) + { + string json = await File.ReadAllTextAsync(_WorkshopMapConfigPath); + _WorkshopMapLookup = JsonSerializer.Deserialize>(json, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }) ?? new Dictionary(StringComparer.OrdinalIgnoreCase); + + _Logger.LogInformation("Loaded workshop map config from {Path}", _WorkshopMapConfigPath); + } + else + { + _Logger.LogWarning("Workshop map config not found at {Path}. Workshop maps will not be available.", _WorkshopMapConfigPath); + _WorkshopMapLookup = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + } + catch (Exception ex) + { + _Logger.LogError(ex, "Failed to load workshop map config from {Path}", _WorkshopMapConfigPath); + _WorkshopMapLookup = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + } + public void SwitchMap(string selectedMap) { _Dispatcher.NextWorldUpdate(() => @@ -224,33 +254,9 @@ public void SwitchMap(string selectedMap) return; } - // Lazy-load the workshop map config if not already loaded - if (_workshopMapLookup == null) - { - try - { - if (File.Exists(_workshopMapConfigPath)) - { - string json = File.ReadAllText(_workshopMapConfigPath); - _workshopMapLookup = JsonSerializer.Deserialize>(json); - _Logger.LogInformation("Loaded workshop map config from {Path}", _workshopMapConfigPath); - } - else - { - _Logger.LogWarning("Workshop map config not found at {Path}. Workshop maps will not be available.", _workshopMapConfigPath); - _workshopMapLookup = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - } - catch (Exception ex) - { - _Logger.LogError(ex, "Failed to load workshop map config from {Path}", _workshopMapConfigPath); - _workshopMapLookup = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - } - string command; - if (_workshopMapLookup != null && _workshopMapLookup.TryGetValue(selectedMap, out var workshopId)) + if (_WorkshopMapLookup != null && _WorkshopMapLookup.TryGetValue(selectedMap, out var workshopId)) { _Logger.LogInformation("Translating map \"{SelectedMap}\" to Workshop ID: {WorkshopId}", selectedMap, workshopId); command = $"host_workshop_map {workshopId}"; From 324bcdc3f1053ac042b0d629825f8e64d17598a1 Mon Sep 17 00:00:00 2001 From: gubsy420 Date: Sun, 13 Apr 2025 15:51:46 -0400 Subject: [PATCH 3/4] Updated server contract (ICsServer.cs) to include Task InitializeWorkshopMapLookupAsync(); --- PugSharp.Server.Contract/ICsServer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/PugSharp.Server.Contract/ICsServer.cs b/PugSharp.Server.Contract/ICsServer.cs index a92f566..d5c6c80 100644 --- a/PugSharp.Server.Contract/ICsServer.cs +++ b/PugSharp.Server.Contract/ICsServer.cs @@ -26,4 +26,5 @@ public interface ICsServer void SwitchMap(string selectedMap); void UnpauseMatch(); void UpdateConvar(string name, T value); + Task InitializeWorkshopMapLookupAsync(); } From 5d79dac6fd3bf567e46fdf23ebeee45cc15391b3 Mon Sep 17 00:00:00 2001 From: gubsy420 Date: Wed, 16 Apr 2025 06:02:04 -0400 Subject: [PATCH 4/4] Move Lookup call to Initialize in Application.cs. Added brackets to InitializeWorkshopMapLookupAsync per request --- PugSharp.Match/Match.cs | 3 +-- PugSharp/Application.cs | 2 ++ PugSharp/CsServer.cs | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/PugSharp.Match/Match.cs b/PugSharp.Match/Match.cs index 2fcfe92..0d6c12a 100644 --- a/PugSharp.Match/Match.cs +++ b/PugSharp.Match/Match.cs @@ -112,8 +112,7 @@ private void InitializeStateMachine() #pragma warning restore MA0051 // Method is too long { _MatchStateMachine.Configure(MatchState.None) - .PermitDynamicIf(MatchCommand.LoadMatch, () => HasRestoredMatch() ? MatchState.RestoreMatch : MatchState.WaitingForPlayersConnectedReady) - .OnEntryAsync(() => _CsServer.InitializeWorkshopMapLookupAsync()); + .PermitDynamicIf(MatchCommand.LoadMatch, () => HasRestoredMatch() ? MatchState.RestoreMatch : MatchState.WaitingForPlayersConnectedReady); _MatchStateMachine.Configure(MatchState.WaitingForPlayersConnectedReady) .PermitDynamicIf(MatchCommand.PlayerReady, () => HasRestoredMatch() ? MatchState.MatchRunning : MatchState.DefineTeams, AllPlayersAreReady) diff --git a/PugSharp/Application.cs b/PugSharp/Application.cs index 35bcf69..79c9964 100644 --- a/PugSharp/Application.cs +++ b/PugSharp/Application.cs @@ -85,6 +85,8 @@ public void Initialize(bool hotReload) { var serverConfigResult = _ConfigProvider.LoadServerConfig(); + _CsServer.InitializeWorkshopMapLookupAsync() + serverConfigResult.Switch( error => { }, // Do nothing - Error already logged serverConfig => diff --git a/PugSharp/CsServer.cs b/PugSharp/CsServer.cs index e9cd974..1f6e652 100644 --- a/PugSharp/CsServer.cs +++ b/PugSharp/CsServer.cs @@ -217,8 +217,9 @@ public void UnpauseMatch() public async Task InitializeWorkshopMapLookupAsync() { if (_WorkshopMapLookup != null) + { return; // Already loaded - + } try { if (File.Exists(_WorkshopMapConfigPath))