From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:24:39 +0000 (+0200) Subject: setgamepreset command rework (take two) (#30812) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=80ec149955a3f1681ab4cceb70f8e1fc2688485d;p=space-station-14.git setgamepreset command rework (take two) (#30812) * gameticker.gamepreset namespace * setgamepreset now has a finite duration * comments, cleanup --- diff --git a/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs b/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs index 83736bd92b..78e2b452b7 100644 --- a/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs +++ b/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs @@ -2,6 +2,7 @@ using Content.Server.Administration; using Content.Server.GameTicking.Presets; using Content.Shared.Administration; +using Linguini.Shared.Util; using Robust.Shared.Console; using Robust.Shared.Prototypes; @@ -19,9 +20,9 @@ namespace Content.Server.GameTicking.Commands public void Execute(IConsoleShell shell, string argStr, string[] args) { - if (args.Length != 1) + if (!args.Length.InRange(1, 2)) { - shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", ("properAmount", 1), ("currentAmount", args.Length))); + shell.WriteError(Loc.GetString("shell-need-between-arguments", ("lower", 1), ("upper", 2), ("currentAmount", args.Length))); return; } @@ -33,8 +34,16 @@ namespace Content.Server.GameTicking.Commands return; } - ticker.SetGamePreset(preset); - shell.WriteLine(Loc.GetString("set-game-preset-preset-set", ("preset", preset.ID))); + var rounds = 1; + + 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()))); } public CompletionResult GetCompletion(IConsoleShell shell, string[] args) diff --git a/Content.Server/GameTicking/GameTicker.GamePreset.cs b/Content.Server/GameTicking/GameTicker.GamePreset.cs index d1a8b062c4..14985051e7 100644 --- a/Content.Server/GameTicking/GameTicker.GamePreset.cs +++ b/Content.Server/GameTicking/GameTicker.GamePreset.cs @@ -1,208 +1,233 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; using Content.Server.GameTicking.Presets; using Content.Server.Maps; using Content.Shared.CCVar; +using Content.Shared.Mobs.Systems; using JetBrains.Annotations; using Robust.Shared.Player; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading.Tasks; -namespace Content.Server.GameTicking +namespace Content.Server.GameTicking; + +public sealed partial class GameTicker { - public sealed partial class GameTicker - { - public const float PresetFailedCooldownIncrease = 30f; + [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!; - /// - /// The selected preset that will be used at the start of the next round. - /// - public GamePresetPrototype? Preset { get; private set; } + public const float PresetFailedCooldownIncrease = 30f; - /// - /// The preset that's currently active. - /// - public GamePresetPrototype? CurrentPreset { get; private set; } + /// + /// The selected preset that will be used at the start of the next round. + /// + public GamePresetPrototype? Preset { get; private set; } - private bool StartPreset(ICommonSession[] origReadyPlayers, bool force) - { - var startAttempt = new RoundStartAttemptEvent(origReadyPlayers, force); - RaiseLocalEvent(startAttempt); + /// + /// The preset that's currently active. + /// + public GamePresetPrototype? CurrentPreset { get; private set; } + + /// + /// Countdown to the preset being reset to the server default. + /// + public int? ResetCountdown; - if (!startAttempt.Cancelled) - return true; + private bool StartPreset(ICommonSession[] origReadyPlayers, bool force) + { + var startAttempt = new RoundStartAttemptEvent(origReadyPlayers, force); + RaiseLocalEvent(startAttempt); - var presetTitle = CurrentPreset != null ? Loc.GetString(CurrentPreset.ModeTitle) : string.Empty; + if (!startAttempt.Cancelled) + return true; - void FailedPresetRestart() - { - SendServerMessage(Loc.GetString("game-ticker-start-round-cannot-start-game-mode-restart", - ("failedGameMode", presetTitle))); - RestartRound(); - DelayStart(TimeSpan.FromSeconds(PresetFailedCooldownIncrease)); - } + var presetTitle = CurrentPreset != null ? Loc.GetString(CurrentPreset.ModeTitle) : string.Empty; + + void FailedPresetRestart() + { + SendServerMessage(Loc.GetString("game-ticker-start-round-cannot-start-game-mode-restart", + ("failedGameMode", presetTitle))); + RestartRound(); + DelayStart(TimeSpan.FromSeconds(PresetFailedCooldownIncrease)); + } if (_cfg.GetCVar(CCVars.GameLobbyFallbackEnabled)) { var fallbackPresets = _cfg.GetCVar(CCVars.GameLobbyFallbackPreset).Split(","); var startFailed = true; - foreach (var preset in fallbackPresets) - { - ClearGameRules(); - SetGamePreset(preset); - AddGamePresetRules(); - StartGamePresetRules(); - - startAttempt.Uncancel(); - RaiseLocalEvent(startAttempt); - - if (!startAttempt.Cancelled) - { - _chatManager.SendAdminAnnouncement( - Loc.GetString("game-ticker-start-round-cannot-start-game-mode-fallback", - ("failedGameMode", presetTitle), - ("fallbackMode", Loc.GetString(preset)))); - RefreshLateJoinAllowed(); - startFailed = false; - break; - } - } + foreach (var preset in fallbackPresets) + { + ClearGameRules(); + SetGamePreset(preset); + AddGamePresetRules(); + StartGamePresetRules(); - if (startFailed) + startAttempt.Uncancel(); + RaiseLocalEvent(startAttempt); + + if (!startAttempt.Cancelled) { - FailedPresetRestart(); - return false; + _chatManager.SendAdminAnnouncement( + Loc.GetString("game-ticker-start-round-cannot-start-game-mode-fallback", + ("failedGameMode", presetTitle), + ("fallbackMode", Loc.GetString(preset)))); + RefreshLateJoinAllowed(); + startFailed = false; + break; } } - else + if (startFailed) { FailedPresetRestart(); return false; } + } - return true; + else + { + FailedPresetRestart(); + return false; } + return true; + } + private void InitializeGamePreset() { SetGamePreset(LobbyEnabled ? _cfg.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox"); } - public void SetGamePreset(GamePresetPrototype? preset, bool force = false) - { - // Do nothing if this game ticker is a dummy! - if (DummyTicker) - return; - - Preset = preset; - ValidateMap(); - UpdateInfoText(); + public void SetGamePreset(GamePresetPrototype? preset, bool force = false, int? resetDelay = null) + { + // Do nothing if this game ticker is a dummy! + if (DummyTicker) + return; - if (force) - { - StartRound(true); - } + if (resetDelay is not null) + { + ResetCountdown = resetDelay.Value; + // Reset counter is checked and changed at the end of each round + // So if the game is in the lobby, the first requested round will happen before the check, and we need one less check + if (CurrentPreset is null) + ResetCountdown = resetDelay.Value -1; } - public void SetGamePreset(string preset, bool force = false) + Preset = preset; + ValidateMap(); + UpdateInfoText(); + + if (force) { - var proto = FindGamePreset(preset); - if(proto != null) - SetGamePreset(proto, force); + StartRound(true); } + } - public GamePresetPrototype? FindGamePreset(string preset) - { - if (_prototypeManager.TryIndex(preset, out GamePresetPrototype? presetProto)) - return presetProto; + public void SetGamePreset(string preset, bool force = false) + { + var proto = FindGamePreset(preset); + if(proto != null) + SetGamePreset(proto, force); + } + + public GamePresetPrototype? FindGamePreset(string preset) + { + if (_prototypeManager.TryIndex(preset, out GamePresetPrototype? presetProto)) + return presetProto; - foreach (var proto in _prototypeManager.EnumeratePrototypes()) + foreach (var proto in _prototypeManager.EnumeratePrototypes()) + { + foreach (var alias in proto.Alias) { - foreach (var alias in proto.Alias) - { - if (preset.Equals(alias, StringComparison.InvariantCultureIgnoreCase)) - return proto; - } + if (preset.Equals(alias, StringComparison.InvariantCultureIgnoreCase)) + return proto; } - - return null; } - public bool TryFindGamePreset(string preset, [NotNullWhen(true)] out GamePresetPrototype? prototype) - { - prototype = FindGamePreset(preset); + return null; + } - return prototype != null; - } + public bool TryFindGamePreset(string preset, [NotNullWhen(true)] out GamePresetPrototype? prototype) + { + prototype = FindGamePreset(preset); - public bool IsMapEligible(GameMapPrototype map) - { - if (Preset == null) - return true; + return prototype != null; + } - if (Preset.MapPool == null || !_prototypeManager.TryIndex(Preset.MapPool, out var pool)) - return true; + public bool IsMapEligible(GameMapPrototype map) + { + if (Preset == null) + return true; - return pool.Maps.Contains(map.ID); - } + if (Preset.MapPool == null || !_prototypeManager.TryIndex(Preset.MapPool, out var pool)) + return true; - private void ValidateMap() - { - if (Preset == null || _gameMapManager.GetSelectedMap() is not { } map) - return; + return pool.Maps.Contains(map.ID); + } - if (Preset.MapPool == null || - !_prototypeManager.TryIndex(Preset.MapPool, out var pool)) - return; + private void ValidateMap() + { + if (Preset == null || _gameMapManager.GetSelectedMap() is not { } map) + return; - if (pool.Maps.Contains(map.ID)) - return; + if (Preset.MapPool == null || + !_prototypeManager.TryIndex(Preset.MapPool, out var pool)) + return; - _gameMapManager.SelectMapRandom(); - } + if (pool.Maps.Contains(map.ID)) + return; - [PublicAPI] - private bool AddGamePresetRules() - { - if (DummyTicker || Preset == null) - return false; + _gameMapManager.SelectMapRandom(); + } - CurrentPreset = Preset; - foreach (var rule in Preset.Rules) - { - AddGameRule(rule); - } + [PublicAPI] + private bool AddGamePresetRules() + { + if (DummyTicker || Preset == null) + return false; - return true; + CurrentPreset = Preset; + foreach (var rule in Preset.Rules) + { + AddGameRule(rule); } - public void StartGamePresetRules() + return true; + } + + private void TryResetPreset() + { + if (ResetCountdown is null || ResetCountdown-- > 0) + return; + + InitializeGamePreset(); + ResetCountdown = null; + } + + public void StartGamePresetRules() + { + // May be touched by the preset during init. + var rules = new List(GetAddedGameRules()); + foreach (var rule in rules) { - // May be touched by the preset during init. - var rules = new List(GetAddedGameRules()); - foreach (var rule in rules) - { - StartGameRule(rule); - } + StartGameRule(rule); } + } 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 () => - { - var server = await _dbEntryManager.ServerEntity; - return await _db.AddNewRound(server, playerIds); - }); + // 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(); - } + _taskManager.BlockWaitOnTask(task); + RoundId = task.GetAwaiter().GetResult(); } } diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index 0e8f8bda1e..5d5e68441d 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -647,6 +647,9 @@ namespace Content.Server.GameTicking if (_serverUpdates.RoundEnded()) return; + // Check if the GamePreset needs to be reset + TryResetPreset(); + _sawmill.Info("Restarting round!"); SendServerMessage(Loc.GetString("game-ticker-restart-round")); diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 26242925aa..624bf35afe 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -4,6 +4,7 @@ using System.Numerics; using Content.Server.Administration.Managers; using Content.Server.Administration.Systems; using Content.Server.GameTicking.Events; +using Content.Server.Ghost; using Content.Server.Spawners.Components; using Content.Server.Speech.Components; using Content.Server.Station.Components; 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 46049643cb..323d83aeba 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,5 +1,7 @@ -set-game-preset-command-description = Sets the game preset for the current round. -set-game-preset-command-help-text = setgamepreset +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-preset-error = Unable to find game preset "{$preset}" -set-game-preset-preset-set = Set game preset to "{$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.