1
0

GameTicker.Lobby.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. using System.Linq;
  2. using Content.Shared.GameTicking;
  3. using Content.Server.Station.Components;
  4. using Robust.Shared.Network;
  5. using Robust.Shared.Player;
  6. using System.Text;
  7. using Content.Shared.NPC.Prototypes;
  8. using Robust.Shared.Prototypes;
  9. using Content.Shared.NPC.Components;
  10. namespace Content.Server.GameTicking
  11. {
  12. public sealed partial class GameTicker
  13. {
  14. [ViewVariables]
  15. private readonly Dictionary<NetUserId, PlayerGameStatus> _playerGameStatuses = new();
  16. [ViewVariables]
  17. private TimeSpan _roundStartTime;
  18. /// <summary>
  19. /// How long before RoundStartTime do we load maps.
  20. /// </summary>
  21. [ViewVariables]
  22. public TimeSpan RoundPreloadTime { get; } = TimeSpan.FromSeconds(15);
  23. [ViewVariables]
  24. private TimeSpan _pauseTime;
  25. [ViewVariables]
  26. public new bool Paused { get; set; }
  27. [ViewVariables]
  28. private bool _roundStartCountdownHasNotStartedYetDueToNoPlayers;
  29. /// <summary>
  30. /// The game status of a players user Id. May contain disconnected players
  31. /// </summary>
  32. public IReadOnlyDictionary<NetUserId, PlayerGameStatus> PlayerGameStatuses => _playerGameStatuses;
  33. public void UpdateInfoText()
  34. {
  35. RaiseNetworkEvent(GetInfoMsg(), Filter.Empty().AddPlayers(_playerManager.NetworkedSessions));
  36. RaiseNetworkEvent(GetPlayerCountsMsg(), Filter.Empty().AddPlayers(_playerManager.NetworkedSessions)); // Send faction counts too
  37. }
  38. private string GetInfoText()
  39. {
  40. var preset = CurrentPreset ?? Preset;
  41. if (preset == null)
  42. {
  43. return string.Empty;
  44. }
  45. var playerCount = $"{_playerManager.PlayerCount}";
  46. var readyCount = _playerGameStatuses.Values.Count(x => x == PlayerGameStatus.ReadyToPlay);
  47. var stationNames = new StringBuilder();
  48. var query =
  49. EntityQueryEnumerator<StationJobsComponent, StationSpawningComponent, MetaDataComponent>();
  50. var foundOne = false;
  51. while (query.MoveNext(out _, out _, out var meta))
  52. {
  53. foundOne = true;
  54. if (stationNames.Length > 0)
  55. stationNames.Append('\n');
  56. stationNames.Append(meta.EntityName);
  57. }
  58. if (!foundOne)
  59. {
  60. stationNames.Append(_gameMapManager.GetSelectedMap()?.MapName ??
  61. Loc.GetString("game-ticker-no-map-selected"));
  62. }
  63. var gmTitle = Loc.GetString(preset.ModeTitle);
  64. var desc = Loc.GetString(preset.Description);
  65. return Loc.GetString(
  66. RunLevel == GameRunLevel.PreRoundLobby
  67. ? "game-ticker-get-info-preround-text"
  68. : "game-ticker-get-info-text",
  69. ("roundId", RoundId),
  70. ("playerCount", playerCount),
  71. ("readyCount", readyCount),
  72. ("mapName", stationNames.ToString()),
  73. ("gmTitle", gmTitle),
  74. ("desc", desc));
  75. }
  76. private TickerConnectionStatusEvent GetConnectionStatusMsg()
  77. {
  78. return new TickerConnectionStatusEvent(RoundStartTimeSpan);
  79. }
  80. private TickerLobbyStatusEvent GetStatusMsg(ICommonSession session)
  81. {
  82. _playerGameStatuses.TryGetValue(session.UserId, out var status);
  83. return new TickerLobbyStatusEvent(RunLevel != GameRunLevel.PreRoundLobby, LobbyBackground, status == PlayerGameStatus.ReadyToPlay, _roundStartTime, RoundPreloadTime, RoundStartTimeSpan, Paused);
  84. }
  85. private void SendStatusToAll()
  86. {
  87. foreach (var player in _playerManager.Sessions)
  88. {
  89. RaiseNetworkEvent(GetStatusMsg(player), player.Channel);
  90. }
  91. }
  92. private TickerLobbyInfoEvent GetInfoMsg()
  93. {
  94. return new(GetInfoText());
  95. }
  96. private GetPlayerFactionCounts GetPlayerCountsMsg()
  97. {
  98. return new(GetPlayerFactionCounts());
  99. }
  100. private void UpdateLateJoinStatus()
  101. {
  102. RaiseNetworkEvent(new TickerLateJoinStatusEvent(DisallowLateJoin));
  103. }
  104. public bool PauseStart(bool pause = true)
  105. {
  106. if (Paused == pause)
  107. {
  108. return false;
  109. }
  110. Paused = pause;
  111. if (pause)
  112. {
  113. _pauseTime = _gameTiming.CurTime;
  114. }
  115. else if (_pauseTime != default)
  116. {
  117. _roundStartTime += _gameTiming.CurTime - _pauseTime;
  118. }
  119. RaiseNetworkEvent(new TickerLobbyCountdownEvent(_roundStartTime, Paused));
  120. _chatManager.DispatchServerAnnouncement(Loc.GetString(Paused
  121. ? "game-ticker-pause-start"
  122. : "game-ticker-pause-start-resumed"));
  123. return true;
  124. }
  125. public bool TogglePause()
  126. {
  127. PauseStart(!Paused);
  128. return Paused;
  129. }
  130. public void ToggleReadyAll(bool ready)
  131. {
  132. var status = ready ? PlayerGameStatus.ReadyToPlay : PlayerGameStatus.NotReadyToPlay;
  133. foreach (var playerUserId in _playerGameStatuses.Keys)
  134. {
  135. _playerGameStatuses[playerUserId] = status;
  136. if (!_playerManager.TryGetSessionById(playerUserId, out var playerSession))
  137. continue;
  138. RaiseNetworkEvent(GetStatusMsg(playerSession), playerSession.Channel);
  139. }
  140. }
  141. public void ToggleReady(ICommonSession player, bool ready)
  142. {
  143. if (!_playerGameStatuses.ContainsKey(player.UserId))
  144. return;
  145. if (!_userDb.IsLoadComplete(player))
  146. return;
  147. if (RunLevel != GameRunLevel.PreRoundLobby)
  148. {
  149. return;
  150. }
  151. var status = ready ? PlayerGameStatus.ReadyToPlay : PlayerGameStatus.NotReadyToPlay;
  152. _playerGameStatuses[player.UserId] = ready ? PlayerGameStatus.ReadyToPlay : PlayerGameStatus.NotReadyToPlay;
  153. RaiseNetworkEvent(GetStatusMsg(player), player.Channel);
  154. // update server info to reflect new ready count
  155. UpdateInfoText();
  156. }
  157. public Dictionary<ProtoId<NpcFactionPrototype>, int> GetPlayerFactionCounts()
  158. {
  159. var factionCounts = new Dictionary<ProtoId<NpcFactionPrototype>, int>();
  160. // Iterate through all currently connected player sessions on the server
  161. foreach (var session in _playerManager.Sessions)
  162. {
  163. // Get the entity currently controlled by the player
  164. if (session.AttachedEntity is not { Valid: true } playerEntity)
  165. continue;
  166. // Try to get the NpcFactionMemberComponent from the player's controlled entity
  167. if (TryComp<NpcFactionMemberComponent>(playerEntity, out var factionMemberComponent))
  168. {
  169. // A player can be part of multiple factions
  170. foreach (var factionId in factionMemberComponent.Factions)
  171. {
  172. factionCounts.TryGetValue(factionId, out var currentCount);
  173. factionCounts[factionId] = currentCount + 1;
  174. }
  175. }
  176. }
  177. return factionCounts;
  178. }
  179. public bool UserHasJoinedGame(ICommonSession session)
  180. => UserHasJoinedGame(session.UserId);
  181. public bool UserHasJoinedGame(NetUserId userId)
  182. => PlayerGameStatuses.TryGetValue(userId, out var status) && status == PlayerGameStatus.JoinedGame;
  183. }
  184. }