GameTicker.GamePreset.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. using Content.Server.GameTicking.Presets;
  2. using Content.Server.Maps;
  3. using Content.Shared.CCVar;
  4. using JetBrains.Annotations;
  5. using Robust.Shared.Player;
  6. using System.Diagnostics.CodeAnalysis;
  7. using System.Linq;
  8. using System.Threading.Tasks;
  9. namespace Content.Server.GameTicking
  10. {
  11. public sealed partial class GameTicker
  12. {
  13. public const float PresetFailedCooldownIncrease = 30f;
  14. /// <summary>
  15. /// The selected preset that will be used at the start of the next round.
  16. /// </summary>
  17. public GamePresetPrototype? Preset { get; private set; }
  18. /// <summary>
  19. /// The preset that's currently active.
  20. /// </summary>
  21. public GamePresetPrototype? CurrentPreset { get; private set; }
  22. private bool StartPreset(ICommonSession[] origReadyPlayers, bool force)
  23. {
  24. var startAttempt = new RoundStartAttemptEvent(origReadyPlayers, force);
  25. RaiseLocalEvent(startAttempt);
  26. if (!startAttempt.Cancelled)
  27. return true;
  28. var presetTitle = CurrentPreset != null ? Loc.GetString(CurrentPreset.ModeTitle) : string.Empty;
  29. void FailedPresetRestart()
  30. {
  31. SendServerMessage(Loc.GetString("game-ticker-start-round-cannot-start-game-mode-restart",
  32. ("failedGameMode", presetTitle)));
  33. RestartRound();
  34. DelayStart(TimeSpan.FromSeconds(PresetFailedCooldownIncrease));
  35. }
  36. if (_cfg.GetCVar(CCVars.GameLobbyFallbackEnabled))
  37. {
  38. var fallbackPresets = _cfg.GetCVar(CCVars.GameLobbyFallbackPreset).Split(",");
  39. var startFailed = true;
  40. foreach (var preset in fallbackPresets)
  41. {
  42. ClearGameRules();
  43. SetGamePreset(preset);
  44. AddGamePresetRules();
  45. StartGamePresetRules();
  46. startAttempt.Uncancel();
  47. RaiseLocalEvent(startAttempt);
  48. if (!startAttempt.Cancelled)
  49. {
  50. _chatManager.SendAdminAnnouncement(
  51. Loc.GetString("game-ticker-start-round-cannot-start-game-mode-fallback",
  52. ("failedGameMode", presetTitle),
  53. ("fallbackMode", Loc.GetString(preset))));
  54. RefreshLateJoinAllowed();
  55. startFailed = false;
  56. break;
  57. }
  58. }
  59. if (startFailed)
  60. {
  61. FailedPresetRestart();
  62. return false;
  63. }
  64. }
  65. else
  66. {
  67. FailedPresetRestart();
  68. return false;
  69. }
  70. return true;
  71. }
  72. private void InitializeGamePreset()
  73. {
  74. SetGamePreset(LobbyEnabled ? _cfg.GetCVar(CCVars.GameLobbyDefaultPreset) : "nomads");
  75. }
  76. public void SetGamePreset(GamePresetPrototype? preset, bool force = false)
  77. {
  78. // Do nothing if this game ticker is a dummy!
  79. if (DummyTicker)
  80. return;
  81. Preset = preset;
  82. ValidateMap();
  83. UpdateInfoText();
  84. if (force)
  85. {
  86. StartRound(true);
  87. }
  88. }
  89. public void SetGamePreset(string preset, bool force = false)
  90. {
  91. var proto = FindGamePreset(preset);
  92. if (proto != null)
  93. SetGamePreset(proto, force);
  94. }
  95. public GamePresetPrototype? FindGamePreset(string preset)
  96. {
  97. if (_prototypeManager.TryIndex(preset, out GamePresetPrototype? presetProto))
  98. return presetProto;
  99. foreach (var proto in _prototypeManager.EnumeratePrototypes<GamePresetPrototype>())
  100. {
  101. foreach (var alias in proto.Alias)
  102. {
  103. if (preset.Equals(alias, StringComparison.InvariantCultureIgnoreCase))
  104. return proto;
  105. }
  106. }
  107. return null;
  108. }
  109. public bool TryFindGamePreset(string preset, [NotNullWhen(true)] out GamePresetPrototype? prototype)
  110. {
  111. prototype = FindGamePreset(preset);
  112. return prototype != null;
  113. }
  114. public bool IsMapEligible(GameMapPrototype map)
  115. {
  116. if (Preset == null)
  117. return true;
  118. if (Preset.MapPool == null || !_prototypeManager.TryIndex<GameMapPoolPrototype>(Preset.MapPool, out var pool))
  119. return true;
  120. return pool.Maps.Contains(map.ID);
  121. }
  122. private void ValidateMap()
  123. {
  124. if (Preset == null || _gameMapManager.GetSelectedMap() is not { } map)
  125. return;
  126. if (Preset.MapPool == null ||
  127. !_prototypeManager.TryIndex<GameMapPoolPrototype>(Preset.MapPool, out var pool))
  128. return;
  129. if (pool.Maps.Contains(map.ID))
  130. return;
  131. _gameMapManager.SelectMapRandom();
  132. }
  133. [PublicAPI]
  134. private bool AddGamePresetRules()
  135. {
  136. if (DummyTicker || Preset == null)
  137. return false;
  138. CurrentPreset = Preset;
  139. foreach (var rule in Preset.Rules)
  140. {
  141. AddGameRule(rule);
  142. }
  143. return true;
  144. }
  145. public void StartGamePresetRules()
  146. {
  147. // May be touched by the preset during init.
  148. var rules = new List<EntityUid>(GetAddedGameRules());
  149. foreach (var rule in rules)
  150. {
  151. StartGameRule(rule);
  152. }
  153. }
  154. private void IncrementRoundNumber()
  155. {
  156. var playerIds = _playerGameStatuses.Keys.Select(player => player.UserId).ToArray();
  157. var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName);
  158. // TODO FIXME AAAAAAAAAAAAAAAAAAAH THIS IS BROKEN
  159. // Task.Run as a terrible dirty workaround to avoid synchronization context deadlock from .Result here.
  160. // This whole setup logic should be made asynchronous so we can properly wait on the DB AAAAAAAAAAAAAH
  161. var task = Task.Run(async () =>
  162. {
  163. var server = await _dbEntryManager.ServerEntity;
  164. return await _db.AddNewRound(server, playerIds);
  165. });
  166. _taskManager.BlockWaitOnTask(task);
  167. RoundId = task.GetAwaiter().GetResult();
  168. }
  169. }
  170. }