diff --git a/README.md b/README.md index 06164d6..8466ff3 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ When the plugin is first loaded it will create a `retakes_config.json` file in t | IsDebugMode | Whether to enable debug output to the server console or not. | false | false | true | | ShouldForceEvenTeamsWhenPlayerCountIsMultipleOf10 | Whether to force even teams when the active players is a multiple of 10 or not. (this means you will get 5v5 @ 10 players / 10v10 @ 20 players) | true | false | true | | EnableFallbackBombsiteAnnouncement | Whether to enable the fallback bombsite announcement. | true | false | true | -| ShouldRemoveSpectators | When a player is moved to spectators, remove them from all retake queues. Ensures that AFK plugins work as expected. | false | false | true | +| ShouldRemoveSpectators | When a player is moved to spectators, remove them from all retake queues. Ensures that AFK plugins work as expected. | false | false | true | ## Commands | Command | Arguments | Description | Permissions | @@ -76,6 +76,8 @@ When the plugin is first loaded it will create a `retakes_config.json` file in t | !removespawn | | Removes the nearest spawn point for the bombsite currently shown. | @css/root | | !nearestspawn | | Teleports the player to the nearest spawn. | @css/root | | !hidespawns | | Exits the spawn editing mode. | @css/root | +| !mapconfig | | Forces a specific map config file to load. | @css/root | +| !mapconfigs | | Displays a list of available map configs. | @css/root | | !scramble | | Scrambles the teams next round. | @css/admin | | !voices | | Toggles whether or not to hear the bombsite voice announcements. | | | css_debugqueues | | **SERVER ONLY** Shows the current queue state in the server console. | | diff --git a/RetakesPlugin/Modules/Configs/MapConfig.cs b/RetakesPlugin/Modules/Configs/MapConfig.cs index 540b0d5..49bcafd 100644 --- a/RetakesPlugin/Modules/Configs/MapConfig.cs +++ b/RetakesPlugin/Modules/Configs/MapConfig.cs @@ -17,7 +17,7 @@ public MapConfig(string moduleDirectory, string mapName) _mapConfigData = null; } - public void Load() + public void Load(bool isViaCommand = false) { Helpers.Debug($"Attempting to load map data from {_mapConfigPath}"); @@ -42,8 +42,12 @@ public void Load() catch (FileNotFoundException) { Helpers.Debug($"No config for map {_mapName}"); - _mapConfigData = new MapConfigData(); - Save(); + + if (!isViaCommand) + { + _mapConfigData = new MapConfigData(); + Save(); + } } catch (Exception ex) { diff --git a/RetakesPlugin/RetakesPlugin.cs b/RetakesPlugin/RetakesPlugin.cs index d42d469..a7ae6c7 100644 --- a/RetakesPlugin/RetakesPlugin.cs +++ b/RetakesPlugin/RetakesPlugin.cs @@ -82,8 +82,11 @@ public override void Load(bool hotReload) MessagePrefix = _translator["retakes.prefix"]; Helpers.Debug($"Plugin loaded!"); - - RegisterListener(OnMapStart); + + RegisterListener(mapName => + { + OnMapStart(mapName); + }); AddCommandListener("jointeam", OnCommandJoinTeam); @@ -98,19 +101,74 @@ public override void Load(bool hotReload) } #region Commands - [ConsoleCommand("css_forcebombsitestop", "Clear the forced bombsite and return back to normal.")] - [CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] + [ConsoleCommand("css_mapconfig", "Forces a specific map config file to load.")] + [ConsoleCommand("css_setmapconfig", "Forces a specific map config file to load.")] + [ConsoleCommand("css_loadmapconfig", "Forces a specific map config file to load.")] + [CommandHelper(minArgs: 1, usage: "[filename]", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] [RequiresPermissions("@css/root")] - public void OnCommandForceBombsiteStop(CCSPlayerController? player, CommandInfo commandInfo) + public void OnCommandMapConfig(CCSPlayerController? player, CommandInfo commandInfo) { if (!Helpers.IsValidPlayer(player)) { return; } - _forcedBombsite = null; + var mapConfigDirectory = Path.Combine(ModuleDirectory, "map_config"); - commandInfo.ReplyToCommand($"{MessagePrefix}The bombsite will no longer be forced."); + if (!Directory.Exists(mapConfigDirectory)) + { + commandInfo.ReplyToCommand($"{MessagePrefix}No map configs found."); + return; + } + + var mapConfigFileName = commandInfo.GetArg(1).Trim().Replace(".json", ""); + + var mapConfigFilePath = Path.Combine(mapConfigDirectory, $"{mapConfigFileName}.json"); + + if (!File.Exists(mapConfigFilePath)) + { + commandInfo.ReplyToCommand($"{MessagePrefix}Map config file not found."); + return; + } + + OnMapStart(Server.MapName, mapConfigFileName); + + commandInfo.ReplyToCommand($"{MessagePrefix}The new map config has been successfully loaded."); + } + + [ConsoleCommand("css_mapconfigs", "Displays a list of available map configs.")] + [ConsoleCommand("css_viewmapconfigs", "Displays a list of available map configs.")] + [ConsoleCommand("css_listmapconfigs", "Displays a list of available map configs.")] + [CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] + [RequiresPermissions("@css/root")] + public void OnCommandMapConfigs(CCSPlayerController? player, CommandInfo commandInfo) + { + if (player == null || !Helpers.IsValidPlayer(player)) + { + return; + } + + var mapConfigDirectory = Path.Combine(ModuleDirectory, "map_config"); + + var files = Directory.GetFiles(mapConfigDirectory); + + if (!Directory.Exists(mapConfigDirectory) || files.Length == 0) + { + commandInfo.ReplyToCommand($"{MessagePrefix}No map configs found."); + return; + } + + foreach (var file in files) + { + var transformedFile = file + .Replace($"{mapConfigDirectory}/", "") + .Replace(".json", ""); + + commandInfo.ReplyToCommand($"{MessagePrefix}{transformedFile}"); + player.PrintToConsole($"{MessagePrefix}{transformedFile}"); + } + + commandInfo.ReplyToCommand($"{MessagePrefix}A list of available map configs has been outputted above."); } [ConsoleCommand("css_forcebombsite", "Force the retakes to occur from a single bombsite.")] @@ -135,6 +193,21 @@ public void OnCommandForceBombsite(CCSPlayerController? player, CommandInfo comm commandInfo.ReplyToCommand($"{MessagePrefix}The bombsite will now be forced to {_forcedBombsite}."); } + [ConsoleCommand("css_forcebombsitestop", "Clear the forced bombsite and return back to normal.")] + [CommandHelper(whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] + [RequiresPermissions("@css/root")] + public void OnCommandForceBombsiteStop(CCSPlayerController? player, CommandInfo commandInfo) + { + if (!Helpers.IsValidPlayer(player)) + { + return; + } + + _forcedBombsite = null; + + commandInfo.ReplyToCommand($"{MessagePrefix}The bombsite will no longer be forced."); + } + [ConsoleCommand("css_showspawns", "Show the spawns for the specified bombsite.")] [ConsoleCommand("css_spawns", "Show the spawns for the specified bombsite.")] [ConsoleCommand("css_edit", "Show the spawns for the specified bombsite.")] @@ -483,9 +556,9 @@ public void OnCommandVoices(CCSPlayerController? player, CommandInfo commandInfo #endregion #region Listeners - private void OnMapStart(string mapName) + private void OnMapStart(string mapName, string? customMapConfig = null) { - Helpers.Debug($"OnMapStart listener triggered!"); + Helpers.Debug("OnMapStart listener triggered!"); ResetState(); @@ -496,9 +569,9 @@ private void OnMapStart(string mapName) }); // If we don't have a map config loaded, load it. - if (!MapConfig.IsLoaded(_mapConfig, Server.MapName)) + if (!MapConfig.IsLoaded(_mapConfig, customMapConfig ?? mapName)) { - _mapConfig = new MapConfig(ModuleDirectory, Server.MapName); + _mapConfig = new MapConfig(ModuleDirectory, customMapConfig ?? mapName); _mapConfig.Load(); } @@ -629,7 +702,7 @@ public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) if (_mapConfig != null) { - var numSpawns = Helpers.ShowSpawns(_mapConfig.GetSpawnsClone(), _showingSpawnsForBombsite); + Helpers.ShowSpawns(_mapConfig.GetSpawnsClone(), _showingSpawnsForBombsite); } return HookResult.Continue;