+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();
}
}