]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
setgamepreset command rework (take two) (#30812)
authorErrant <35878406+Errant-4@users.noreply.github.com>
Fri, 18 Apr 2025 12:24:39 +0000 (14:24 +0200)
committerGitHub <noreply@github.com>
Fri, 18 Apr 2025 12:24:39 +0000 (22:24 +1000)
* gameticker.gamepreset namespace

* setgamepreset now has a finite duration

* comments, cleanup

Content.Server/GameTicking/Commands/SetGamePresetCommand.cs
Content.Server/GameTicking/GameTicker.GamePreset.cs
Content.Server/GameTicking/GameTicker.RoundFlow.cs
Content.Server/GameTicking/GameTicker.Spawning.cs
Resources/Locale/en-US/game-ticking/set-game-preset-command.ftl

index 83736bd92b0ef311304fd774109da43e13ae5256..78e2b452b7bedbe9bd2549fefe7c12501ae323ec 100644 (file)
@@ -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)
index d1a8b062c4860f5207d78b140300cfd7cd6d8141..14985051e78ebf0ae72fd8e69b2ccd3882031e9a 100644 (file)
+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!;
 
-        /// <summary>
-        /// The selected preset that will be used at the start of the next round.
-        /// </summary>
-        public GamePresetPrototype? Preset { get; private set; }
+    public const float PresetFailedCooldownIncrease = 30f;
 
-        /// <summary>
-        /// The preset that's currently active.
-        /// </summary>
-        public GamePresetPrototype? CurrentPreset { get; private set; }
+    /// <summary>
+    /// The selected preset that will be used at the start of the next round.
+    /// </summary>
+    public GamePresetPrototype? Preset { get; private set; }
 
-        private bool StartPreset(ICommonSession[] origReadyPlayers, bool force)
-        {
-            var startAttempt = new RoundStartAttemptEvent(origReadyPlayers, force);
-            RaiseLocalEvent(startAttempt);
+    /// <summary>
+    /// The preset that's currently active.
+    /// </summary>
+    public GamePresetPrototype? CurrentPreset { get; private set; }
+
+    /// <summary>
+    /// Countdown to the preset being reset to the server default.
+    /// </summary>
+    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<GamePresetPrototype>())
+        foreach (var proto in _prototypeManager.EnumeratePrototypes<GamePresetPrototype>())
+        {
+            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<GameMapPoolPrototype>(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<GameMapPoolPrototype>(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<GameMapPoolPrototype>(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<GameMapPoolPrototype>(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<EntityUid>(GetAddedGameRules());
+        foreach (var rule in rules)
         {
-            // May be touched by the preset during init.
-            var rules = new List<EntityUid>(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();
     }
 }
index 0e8f8bda1ea10d00b8f4b04740dfd04ecff3be26..5d5e68441d074c8e00b6215c28cf0b7df8e02fc0 100644 (file)
@@ -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"));
index 26242925aa31828f2b8ba12854cf671e39b53731..624bf35afe4257c190bd522f51cbf58f11479a22 100644 (file)
@@ -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;
index 46049643cb577ccc98516b717482331af1aab9fc..323d83aebafa1d6c7298aec526ad33a5046e0bef 100644 (file)
@@ -1,5 +1,7 @@
-set-game-preset-command-description = Sets the game preset for the current round.
-set-game-preset-command-help-text = setgamepreset <id>
+set-game-preset-command-description = Sets the game preset for the specified number of upcoming rounds.
+set-game-preset-command-help-text = setgamepreset <id> [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.