diff --git a/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs b/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs index 78e2b452b7be..83736bd92b0e 100644 --- a/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs +++ b/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs @@ -2,7 +2,6 @@ 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; @@ -20,9 +19,9 @@ public sealed class SetGamePresetCommand : IConsoleCommand public void Execute(IConsoleShell shell, string argStr, string[] args) { - if (!args.Length.InRange(1, 2)) + if (args.Length != 1) { - shell.WriteError(Loc.GetString("shell-need-between-arguments", ("lower", 1), ("upper", 2), ("currentAmount", args.Length))); + shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", ("properAmount", 1), ("currentAmount", args.Length))); return; } @@ -34,16 +33,8 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - 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()))); + ticker.SetGamePreset(preset); + shell.WriteLine(Loc.GetString("set-game-preset-preset-set", ("preset", preset.ID))); } public CompletionResult GetCompletion(IConsoleShell shell, string[] args) diff --git a/Content.Server/GameTicking/GameTicker.GamePreset.cs b/Content.Server/GameTicking/GameTicker.GamePreset.cs index 6c12a2ea27e9..5a2b375dd68c 100644 --- a/Content.Server/GameTicking/GameTicker.GamePreset.cs +++ b/Content.Server/GameTicking/GameTicker.GamePreset.cs @@ -16,327 +16,305 @@ using JetBrains.Annotations; using Robust.Shared.Player; -namespace Content.Server.GameTicking; - -public sealed partial class GameTicker +namespace Content.Server.GameTicking { - [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!; - - public const float PresetFailedCooldownIncrease = 30f; + public sealed partial class GameTicker + { + [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; } - /// - /// Countdown to the preset being reset to the server default. - /// - public int? ResetCountdown; + /// + /// The preset that's currently active. + /// + public GamePresetPrototype? CurrentPreset { get; private set; } - private bool StartPreset(ICommonSession[] origReadyPlayers, bool force) - { - var startAttempt = new RoundStartAttemptEvent(origReadyPlayers, force); - RaiseLocalEvent(startAttempt); - - 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; - if (_configurationManager.GetCVar(CCVars.GameLobbyFallbackEnabled)) - { - var fallbackPresets = _configurationManager.GetCVar(CCVars.GameLobbyFallbackPreset).Split(","); - var startFailed = true; + void FailedPresetRestart() + { + SendServerMessage(Loc.GetString("game-ticker-start-round-cannot-start-game-mode-restart", + ("failedGameMode", presetTitle))); + RestartRound(); + DelayStart(TimeSpan.FromSeconds(PresetFailedCooldownIncrease)); + } - foreach (var preset in fallbackPresets) + if (_configurationManager.GetCVar(CCVars.GameLobbyFallbackEnabled)) { - ClearGameRules(); - SetGamePreset(preset); - AddGamePresetRules(); - StartGamePresetRules(); + var fallbackPresets = _configurationManager.GetCVar(CCVars.GameLobbyFallbackPreset).Split(","); + var startFailed = true; - startAttempt.Uncancel(); - RaiseLocalEvent(startAttempt); + 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; + } + } - if (!startAttempt.Cancelled) + if (startFailed) { - _chatManager.SendAdminAnnouncement( - Loc.GetString("game-ticker-start-round-cannot-start-game-mode-fallback", - ("failedGameMode", presetTitle), - ("fallbackMode", Loc.GetString(preset)))); - RefreshLateJoinAllowed(); - startFailed = false; - break; + FailedPresetRestart(); + return false; } } - if (startFailed) + else { FailedPresetRestart(); return false; } + + return true; } - else + private void InitializeGamePreset() { - FailedPresetRestart(); - return false; + SetGamePreset(LobbyEnabled ? _configurationManager.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox"); } - return true; - } + public void SetGamePreset(GamePresetPrototype? preset, bool force = false) + { + // Do nothing if this game ticker is a dummy! + if (DummyTicker) + return; - private void InitializeGamePreset() - { - SetGamePreset(LobbyEnabled ? _configurationManager.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox"); - } + 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 (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; + if (force) + { + StartRound(true); + } } - Preset = preset; - ValidateMap(); - UpdateInfoText(); - - if (force) + public void SetGamePreset(string preset, bool force = false) { - StartRound(true); + var proto = FindGamePreset(preset); + if(proto != null) + SetGamePreset(proto, force); } - } - 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()) + public GamePresetPrototype? FindGamePreset(string preset) { - foreach (var alias in proto.Alias) + if (_prototypeManager.TryIndex(preset, out GamePresetPrototype? presetProto)) + return presetProto; + + foreach (var proto in _prototypeManager.EnumeratePrototypes()) { - if (preset.Equals(alias, StringComparison.InvariantCultureIgnoreCase)) - return proto; + foreach (var alias in proto.Alias) + { + 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; - - CurrentPreset = Preset; - foreach (var rule in Preset.Rules) - { - AddGameRule(rule); + _gameMapManager.SelectMapRandom(); } - return true; - } + [PublicAPI] + private bool AddGamePresetRules() + { + if (DummyTicker || Preset == null) + return false; - private void TryResetPreset() - { - if (ResetCountdown is null || ResetCountdown-- > 0) - return; + CurrentPreset = Preset; + foreach (var rule in Preset.Rules) + { + AddGameRule(rule); + } - InitializeGamePreset(); - ResetCountdown = null; - } + return true; + } - public void StartGamePresetRules() - { - // May be touched by the preset during init. - var rules = new List(GetAddedGameRules()); - foreach (var rule in rules) + public void StartGamePresetRules() { - StartGameRule(rule); + // May be touched by the preset during init. + var rules = new List(GetAddedGameRules()); + foreach (var rule in rules) + { + StartGameRule(rule); + } } - } - public bool OnGhostAttempt(EntityUid mindId, bool canReturnGlobal, bool viaCommand = false, MindComponent? mind = null) - { - if (!Resolve(mindId, ref mind)) - return false; + public bool OnGhostAttempt(EntityUid mindId, bool canReturnGlobal, bool viaCommand = false, MindComponent? mind = null) + { + if (!Resolve(mindId, ref mind)) + return false; - var playerEntity = mind.CurrentEntity; + var playerEntity = mind.CurrentEntity; - if (playerEntity != null && viaCommand) - _adminLogger.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} is attempting to ghost via command"); + if (playerEntity != null && viaCommand) + _adminLogger.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} is attempting to ghost via command"); - var handleEv = new GhostAttemptHandleEvent(mind, canReturnGlobal); - RaiseLocalEvent(handleEv); + var handleEv = new GhostAttemptHandleEvent(mind, canReturnGlobal); + RaiseLocalEvent(handleEv); - // Something else has handled the ghost attempt for us! We return its result. - if (handleEv.Handled) - return handleEv.Result; + // Something else has handled the ghost attempt for us! We return its result. + if (handleEv.Handled) + return handleEv.Result; - if (mind.PreventGhosting) - { - if (mind.Session != null) // Logging is suppressed to prevent spam from ghost attempts caused by movement attempts + if (mind.PreventGhosting) { - _chatManager.DispatchServerMessage(mind.Session, Loc.GetString("comp-mind-ghosting-prevented"), - true); + if (mind.Session != null) // Logging is suppressed to prevent spam from ghost attempts caused by movement attempts + { + _chatManager.DispatchServerMessage(mind.Session, Loc.GetString("comp-mind-ghosting-prevented"), + true); + } + + return false; } - return false; - } + if (TryComp(playerEntity, out var comp) && !comp.CanGhostInteract) + return false; + + if (mind.VisitingEntity != default) + { + _mind.UnVisit(mindId, mind: mind); + } - if (TryComp(playerEntity, out var comp) && !comp.CanGhostInteract) - return false; + var position = Exists(playerEntity) + ? Transform(playerEntity.Value).Coordinates + : GetObserverSpawnPoint(); - if (mind.VisitingEntity != default) - { - _mind.UnVisit(mindId, mind: mind); - } + if (position == default) + return false; - var position = Exists(playerEntity) - ? Transform(playerEntity.Value).Coordinates - : GetObserverSpawnPoint(); - - if (position == default) - return false; - - // Ok, so, this is the master place for the logic for if ghosting is "too cheaty" to allow returning. - // There's no reason at this time to move it to any other place, especially given that the 'side effects required' situations would also have to be moved. - // + If CharacterDeadPhysically applies, we're physically dead. Therefore, ghosting OK, and we can return (this is critical for gibbing) - // Note that we could theoretically be ICly dead and still physically alive and vice versa. - // (For example, a zombie could be dead ICly, but may retain memories and is definitely physically active) - // + If we're in a mob that is critical, and we're supposed to be able to return if possible, - // we're succumbing - the mob is killed. Therefore, character is dead. Ghosting OK. - // (If the mob survives, that's a bug. Ghosting is kept regardless.) - var canReturn = canReturnGlobal && _mind.IsCharacterDeadPhysically(mind); - - if (_configurationManager.GetCVar(CCVars.GhostKillCrit) && - canReturnGlobal && - TryComp(playerEntity, out MobStateComponent? mobState)) - { - if (_mobState.IsCritical(playerEntity.Value, mobState)) + // Ok, so, this is the master place for the logic for if ghosting is "too cheaty" to allow returning. + // There's no reason at this time to move it to any other place, especially given that the 'side effects required' situations would also have to be moved. + // + If CharacterDeadPhysically applies, we're physically dead. Therefore, ghosting OK, and we can return (this is critical for gibbing) + // Note that we could theoretically be ICly dead and still physically alive and vice versa. + // (For example, a zombie could be dead ICly, but may retain memories and is definitely physically active) + // + If we're in a mob that is critical, and we're supposed to be able to return if possible, + // we're succumbing - the mob is killed. Therefore, character is dead. Ghosting OK. + // (If the mob survives, that's a bug. Ghosting is kept regardless.) + var canReturn = canReturnGlobal && _mind.IsCharacterDeadPhysically(mind); + + if (_configurationManager.GetCVar(CCVars.GhostKillCrit) && + canReturnGlobal && + TryComp(playerEntity, out MobStateComponent? mobState)) { - canReturn = true; + if (_mobState.IsCritical(playerEntity.Value, mobState)) + { + canReturn = true; - //todo: what if they dont breathe lol - //cry deeply + //todo: what if they dont breathe lol + //cry deeply - FixedPoint2 dealtDamage = 200; - if (TryComp(playerEntity, out var damageable) - && TryComp(playerEntity, out var thresholds)) - { - var playerDeadThreshold = _mobThresholdSystem.GetThresholdForState(playerEntity.Value, MobState.Dead, thresholds); - dealtDamage = playerDeadThreshold - damageable.TotalDamage; - } + FixedPoint2 dealtDamage = 200; + if (TryComp(playerEntity, out var damageable) + && TryComp(playerEntity, out var thresholds)) + { + var playerDeadThreshold = _mobThresholdSystem.GetThresholdForState(playerEntity.Value, MobState.Dead, thresholds); + dealtDamage = playerDeadThreshold - damageable.TotalDamage; + } - DamageSpecifier damage = new(_prototypeManager.Index("Asphyxiation"), dealtDamage); + DamageSpecifier damage = new(_prototypeManager.Index("Asphyxiation"), dealtDamage); - _damageable.TryChangeDamage(playerEntity, damage, true); + _damageable.TryChangeDamage(playerEntity, damage, true); + } } - } - - var ghost = _ghost.SpawnGhost((mindId, mind), position, canReturn); - if (ghost == null) - return false; - if (playerEntity != null) - _adminLogger.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} ghosted{(!canReturn ? " (non-returnable)" : "")}"); + var ghost = _ghost.SpawnGhost((mindId, mind), position, canReturn); + if (ghost == null) + return false; - return true; - } + if (playerEntity != null) + _adminLogger.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} ghosted{(!canReturn ? " (non-returnable)" : "")}"); - private void IncrementRoundNumber() - { - var playerIds = _playerGameStatuses.Keys.Select(player => player.UserId).ToArray(); - var serverName = _configurationManager.GetCVar(CCVars.AdminLogsServerName); + return true; + } - // 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 = _configurationManager.GetCVar(CCVars.AdminLogsServerName); - _taskManager.BlockWaitOnTask(task); - RoundId = task.GetAwaiter().GetResult(); - } -} + // 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); + }); -public sealed class GhostAttemptHandleEvent : HandledEntityEventArgs -{ - public MindComponent Mind { get; } - public bool CanReturnGlobal { get; } - public bool Result { get; set; } + _taskManager.BlockWaitOnTask(task); + RoundId = task.GetAwaiter().GetResult(); + } + } - public GhostAttemptHandleEvent(MindComponent mind, bool canReturnGlobal) + public sealed class GhostAttemptHandleEvent : HandledEntityEventArgs { - Mind = mind; - CanReturnGlobal = canReturnGlobal; + public MindComponent Mind { get; } + public bool CanReturnGlobal { get; } + public bool Result { get; set; } + + public GhostAttemptHandleEvent(MindComponent mind, bool canReturnGlobal) + { + Mind = mind; + CanReturnGlobal = canReturnGlobal; + } } } diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index dc242fb6c082..ca087c46ed41 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -487,9 +487,6 @@ public void RestartRound() 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/Resources/Locale/en-US/game-ticking/set-game-preset-command.ftl b/Resources/Locale/en-US/game-ticking/set-game-preset-command.ftl index 323d83aebafa..46049643cb57 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,5 @@ -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 current round. +set-game-preset-command-help-text = setgamepreset 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-finite = Set game preset to "{$preset}" for the next {$rounds} rounds. +set-game-preset-preset-set = Set game preset to "{$preset}"