diff --git a/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs b/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs index 78e2b452b7..6114a4ca0d 100644 --- a/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs +++ b/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs @@ -20,9 +20,9 @@ namespace Content.Server.GameTicking.Commands public void Execute(IConsoleShell shell, string argStr, string[] args) { - if (!args.Length.InRange(1, 2)) + if (!args.Length.InRange(1, 3)) { - shell.WriteError(Loc.GetString("shell-need-between-arguments", ("lower", 1), ("upper", 2), ("currentAmount", args.Length))); + shell.WriteError(Loc.GetString("shell-need-between-arguments", ("lower", 1), ("upper", 3), ("currentAmount", args.Length))); return; } @@ -36,32 +36,39 @@ namespace Content.Server.GameTicking.Commands var rounds = 1; - if (args.Length == 2 && !int.TryParse(args[1], out rounds)) + if (args.Length >= 2 && !int.TryParse(args[1], out rounds)) { shell.WriteError(Loc.GetString("set-game-preset-optional-argument-not-integer")); return; } - ticker.SetGamePreset(preset, false, rounds); - shell.WriteLine(Loc.GetString("set-game-preset-preset-set-finite", ("preset", preset.ID), ("rounds", rounds.ToString()))); + GamePresetPrototype? decoy = null; + + if (args.Length == 3 && !ticker.TryFindGamePreset(args[2], out decoy)) + { + shell.WriteError(Loc.GetString("set-game-preset-decoy-error", ("preset", args[2]))); + return; + } + + ticker.SetGamePreset(preset, false, decoy, rounds); + if (decoy == null) + shell.WriteLine(Loc.GetString("set-game-preset-preset-set-finite", ("preset", preset.ID), ("rounds", rounds.ToString()))); + else + shell.WriteLine(Loc.GetString("set-game-preset-preset-set-finite-with-decoy", ("preset", preset.ID), ("rounds", rounds.ToString()), ("decoy", decoy.ID))); } public CompletionResult GetCompletion(IConsoleShell shell, string[] args) { - if (args.Length == 1) + return args.Length switch { - var gamePresets = _prototype.EnumeratePrototypes() - .OrderBy(p => p.ID); - var options = new List(); - foreach (var preset in gamePresets) - { - options.Add(preset.ID); - options.AddRange(preset.Alias); - } + 1 => CompletionResult.FromHintOptions(CompletionHelper.PrototypeIDs(), + Loc.GetString("set-game-preset-command-hint-1")), + 2 => CompletionResult.FromHint(Loc.GetString("set-game-preset-command-hint-2")), + 3 => CompletionResult.FromHintOptions(CompletionHelper.PrototypeIDs(), + Loc.GetString("set-game-preset-command-hint-3")), - return CompletionResult.FromHintOptions(options, ""); - } - return CompletionResult.Empty; + _ => CompletionResult.Empty + }; } } } diff --git a/Content.Server/GameTicking/GameTicker.GamePreset.cs b/Content.Server/GameTicking/GameTicker.GamePreset.cs index 84a93da955..40608e45cb 100644 --- a/Content.Server/GameTicking/GameTicker.GamePreset.cs +++ b/Content.Server/GameTicking/GameTicker.GamePreset.cs @@ -18,6 +18,11 @@ public sealed partial class GameTicker /// public GamePresetPrototype? Preset { get; private set; } + /// + /// The selected preset that will be shown at the lobby screen to fool players. + /// + public GamePresetPrototype? Decoy { get; private set; } + /// /// The preset that's currently active. /// @@ -46,10 +51,10 @@ public sealed partial class GameTicker DelayStart(TimeSpan.FromSeconds(PresetFailedCooldownIncrease)); } - if (_cfg.GetCVar(CCVars.GameLobbyFallbackEnabled)) - { - var fallbackPresets = _cfg.GetCVar(CCVars.GameLobbyFallbackPreset).Split(","); - var startFailed = true; + if (_cfg.GetCVar(CCVars.GameLobbyFallbackEnabled)) + { + var fallbackPresets = _cfg.GetCVar(CCVars.GameLobbyFallbackPreset).Split(","); + var startFailed = true; foreach (var preset in fallbackPresets) { @@ -89,12 +94,12 @@ public sealed partial class GameTicker return true; } - private void InitializeGamePreset() - { - SetGamePreset(LobbyEnabled ? _cfg.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox"); - } + private void InitializeGamePreset() + { + SetGamePreset(LobbyEnabled ? _cfg.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox"); + } - public void SetGamePreset(GamePresetPrototype? preset, bool force = false, int? resetDelay = null) + public void SetGamePreset(GamePresetPrototype? preset, bool force = false, GamePresetPrototype? decoy = null, int? resetDelay = null) { // Do nothing if this game ticker is a dummy! if (DummyTicker) @@ -114,6 +119,7 @@ public sealed partial class GameTicker } Preset = preset; + Decoy = decoy; ValidateMap(); UpdateInfoText(); @@ -126,7 +132,7 @@ public sealed partial class GameTicker public void SetGamePreset(string preset, bool force = false) { var proto = FindGamePreset(preset); - if(proto != null) + if (proto != null) SetGamePreset(proto, force); } @@ -214,19 +220,19 @@ public sealed partial class GameTicker } } - private void IncrementRoundNumber() - { - var playerIds = _playerGameStatuses.Keys.Select(player => player.UserId).ToArray(); - var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName); - - // TODO FIXME AAAAAAAAAAAAAAAAAAAH THIS IS BROKEN - // Task.Run as a terrible dirty workaround to avoid synchronization context deadlock from .Result here. - // This whole setup logic should be made asynchronous so we can properly wait on the DB AAAAAAAAAAAAAH - var task = Task.Run(async () => + private void IncrementRoundNumber() { - var server = await _dbEntryManager.ServerEntity; - return await _db.AddNewRound(server, playerIds); - }); + var playerIds = _playerGameStatuses.Keys.Select(player => player.UserId).ToArray(); + var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName); + + // TODO FIXME AAAAAAAAAAAAAAAAAAAH THIS IS BROKEN + // Task.Run as a terrible dirty workaround to avoid synchronization context deadlock from .Result here. + // This whole setup logic should be made asynchronous so we can properly wait on the DB AAAAAAAAAAAAAH + var task = Task.Run(async () => + { + var server = await _dbEntryManager.ServerEntity; + return await _db.AddNewRound(server, playerIds); + }); _taskManager.BlockWaitOnTask(task); RoundId = task.GetAwaiter().GetResult(); diff --git a/Content.Server/GameTicking/GameTicker.Lobby.cs b/Content.Server/GameTicking/GameTicker.Lobby.cs index 9a9eb61b67..66c39ab469 100644 --- a/Content.Server/GameTicking/GameTicker.Lobby.cs +++ b/Content.Server/GameTicking/GameTicker.Lobby.cs @@ -61,7 +61,7 @@ namespace Content.Server.GameTicking { foundOne = true; if (stationNames.Length > 0) - stationNames.Append('\n'); + stationNames.Append('\n'); stationNames.Append(meta.EntityName); } @@ -72,8 +72,8 @@ namespace Content.Server.GameTicking Loc.GetString("game-ticker-no-map-selected")); } - var gmTitle = Loc.GetString(preset.ModeTitle); - var desc = Loc.GetString(preset.Description); + var gmTitle = (Decoy == null) ? Loc.GetString(preset.ModeTitle) : Loc.GetString(Decoy.ModeTitle); + var desc = (Decoy == null) ? Loc.GetString(preset.Description) : Loc.GetString(Decoy.Description); return Loc.GetString( RunLevel == GameRunLevel.PreRoundLobby ? "game-ticker-get-info-preround-text" @@ -107,7 +107,7 @@ namespace Content.Server.GameTicking private TickerLobbyInfoEvent GetInfoMsg() { - return new (GetInfoText()); + return new(GetInfoText()); } private void UpdateLateJoinStatus() diff --git a/Resources/Locale/en-US/game-ticking/set-game-preset-command.ftl b/Resources/Locale/en-US/game-ticking/set-game-preset-command.ftl index 323d83aeba..9659db643f 100644 --- a/Resources/Locale/en-US/game-ticking/set-game-preset-command.ftl +++ b/Resources/Locale/en-US/game-ticking/set-game-preset-command.ftl @@ -1,7 +1,13 @@ -set-game-preset-command-description = Sets the game preset for the specified number of upcoming rounds. -set-game-preset-command-help-text = setgamepreset [number of rounds, defaulting to 1] -set-game-preset-optional-argument-not-integer = If argument 2 is provided it must be a number. +set-game-preset-command-description = Sets the game preset for the specified number of upcoming rounds. Can also display another preset's title and description in the lobby to fool players. +set-game-preset-command-help-text = setgamepreset [number of rounds, defaulting to 1] [decoy preset] +set-game-preset-command-hint-1 = +set-game-preset-command-hint-2 = [number of rounds] +set-game-preset-command-hint-3 = [decoy preset] +set-game-preset-optional-argument-not-integer = If argument 2 is provided it must be a number. set-game-preset-preset-error = Unable to find game preset "{$preset}" +set-game-preset-decoy-error = If argument 3 is provided it must be a valid preset. Unable to find game preset "{$preset}" + #set-game-preset-preset-set = Set game preset to "{$preset}" set-game-preset-preset-set-finite = Set game preset to "{$preset}" for the next {$rounds} rounds. +set-game-preset-preset-set-finite-with-decoy = Set game preset to "{$preset}" for the next {$rounds} rounds, showing {$decoy} in the lobby. diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index f5ee905a6a..ff6450d7cb 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -124,30 +124,6 @@ rules: - Secret -- type: gamePreset - id: SecretExtended #For Admin Use: Runs Extended but shows "Secret" in lobby. - alias: - - secretextended - name: secret-title - showInVote: false #Admin Use - description: secret-description - rules: - - BasicStationEventScheduler - - MeteorSwarmScheduler - - SpaceTrafficControlEventScheduler - - BasicRoundstartVariation - -- type: gamePreset - id: SecretGreenshift #For Admin Use: Runs Greenshift but shows "Secret" in lobby. - alias: - - secretgreenshift - name: secret-title - showInVote: false #Admin Use - description: secret-description - rules: - - SpaceTrafficControlFriendlyEventScheduler - - BasicRoundstartVariation - - type: gamePreset id: Sandbox alias: