SessionBrowserScreen.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. //-----------------------------------------------------------------------------
  2. // SessionBrowserScreen.cs
  3. //
  4. // Shows available sessions and allows hosting a new game.
  5. //-----------------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Generic;
  8. using Microsoft.Xna.Framework;
  9. using Microsoft.Xna.Framework.Net;
  10. using Microsoft.Xna.Framework.Graphics;
  11. using Microsoft.Xna.Framework.Input;
  12. using GameStateManagement;
  13. using CardsFramework;
  14. namespace Blackjack
  15. {
  16. class SessionBrowserScreen : MenuScreen
  17. {
  18. MenuEntry hostGameMenuEntry;
  19. MenuEntry refreshMenuEntry;
  20. MenuEntry backMenuEntry;
  21. List<MenuEntry> sessionEntries = new List<MenuEntry>();
  22. List<AvailableNetworkSession> availableSessions = new List<AvailableNetworkSession>();
  23. TimeSpan timeSinceLastSearch = TimeSpan.Zero;
  24. const float AutoRefreshInterval = 5.0f; // Auto-refresh every 5 seconds
  25. bool isSearching = false;
  26. public SessionBrowserScreen()
  27. : base(Resources.JoinOrHostGame)
  28. {
  29. }
  30. public override void LoadContent()
  31. {
  32. hostGameMenuEntry = new MenuEntry(Resources.HostNewGame);
  33. refreshMenuEntry = new MenuEntry(Resources.Refresh);
  34. backMenuEntry = new MenuEntry(Resources.Back);
  35. hostGameMenuEntry.Selected += HostGameMenuEntrySelected;
  36. refreshMenuEntry.Selected += RefreshMenuEntrySelected;
  37. backMenuEntry.Selected += OnCancel;
  38. MenuEntries.Add(hostGameMenuEntry);
  39. MenuEntries.Add(refreshMenuEntry);
  40. MenuEntries.Add(backMenuEntry);
  41. // Start async session discovery
  42. BeginFindSessions();
  43. base.LoadContent();
  44. }
  45. void RefreshMenuEntrySelected(object sender, EventArgs e)
  46. {
  47. // Manually trigger a refresh
  48. if (!isSearching)
  49. {
  50. BeginFindSessions();
  51. timeSinceLastSearch = TimeSpan.Zero;
  52. }
  53. }
  54. void HostGameMenuEntrySelected(object sender, EventArgs e)
  55. {
  56. // Host a new session and go to lobby
  57. // Use SystemLink for local network testing (PlayerMatch requires online services)
  58. var asyncResult = NetworkSession.CreateAsync(
  59. NetworkSessionType.SystemLink,
  60. BlackjackConstants.MinPlayers, // local gamers
  61. BlackjackConstants.MaxPlayers, // max gamers
  62. 0, // private slots
  63. null);
  64. var busyScreen = new NetworkBusyScreen<NetworkSession>(asyncResult);
  65. busyScreen.OperationCompleted += (s, evt) =>
  66. {
  67. var networkSession = evt.Result as NetworkSession;
  68. if (networkSession != null)
  69. {
  70. NetworkSessionComponent.Create(ScreenManager, networkSession);
  71. ScreenManager.AddScreen(new BlackjackLobbyScreen(networkSession), null);
  72. }
  73. else
  74. {
  75. ScreenManager.AddScreen(new MessageBoxScreen(Resources.FailedToCreateSession), null);
  76. }
  77. };
  78. ScreenManager.AddScreen(busyScreen, null);
  79. }
  80. void JoinSessionMenuEntrySelected(object sender, EventArgs e)
  81. {
  82. var entry = sender as AvailableSessionMenuEntry;
  83. var availableSession = entry?.AvailableSession;
  84. if (availableSession != null)
  85. {
  86. var asyncResult = NetworkSession.JoinAsync(availableSession);
  87. var busyScreen = new NetworkBusyScreen<NetworkSession>(asyncResult);
  88. busyScreen.OperationCompleted += (s, evt) =>
  89. {
  90. var networkSession = evt.Result as NetworkSession;
  91. if (networkSession != null)
  92. {
  93. NetworkSessionComponent.Create(ScreenManager, networkSession);
  94. ScreenManager.AddScreen(new BlackjackLobbyScreen(networkSession), null);
  95. }
  96. else
  97. {
  98. ScreenManager.AddScreen(new MessageBoxScreen(Resources.FailedToJoinSession), null);
  99. }
  100. };
  101. ScreenManager.AddScreen(busyScreen, null);
  102. }
  103. }
  104. void RefreshSessionList()
  105. {
  106. // Remove all existing session entries from MenuEntries
  107. foreach (var entry in sessionEntries)
  108. {
  109. MenuEntries.Remove(entry);
  110. }
  111. sessionEntries.Clear();
  112. // Don't add session entries to MenuEntries - we'll draw them separately
  113. // Just create the entries for the available sessions
  114. foreach (var session in availableSessions)
  115. {
  116. var entry = new AvailableSessionMenuEntry(session);
  117. entry.Selected += JoinSessionMenuEntrySelected;
  118. sessionEntries.Add(entry);
  119. }
  120. }
  121. public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
  122. {
  123. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  124. // Auto-refresh session list periodically
  125. if (!isSearching && !coveredByOtherScreen)
  126. {
  127. timeSinceLastSearch += gameTime.ElapsedGameTime;
  128. if (timeSinceLastSearch.TotalSeconds >= AutoRefreshInterval)
  129. {
  130. BeginFindSessions();
  131. timeSinceLastSearch = TimeSpan.Zero;
  132. }
  133. }
  134. // Update refresh button text to show status
  135. if (refreshMenuEntry != null)
  136. {
  137. refreshMenuEntry.Text = isSearching ? Resources.NetworkBusy : Resources.Refresh;
  138. }
  139. }
  140. public override void HandleInput(InputState input)
  141. {
  142. // Handle input for session entries (manual click detection)
  143. // Check if mouse button was clicked (pressed then released)
  144. if (input.CurrentMouseState.LeftButton == ButtonState.Released &&
  145. input.LastMouseState.LeftButton == ButtonState.Pressed)
  146. {
  147. Vector2 mousePosition = new Vector2(input.CurrentMouseState.X, input.CurrentMouseState.Y);
  148. SpriteFont font = ScreenManager.Font;
  149. // Check if clicked on a session entry
  150. if (availableSessions.Count > 0)
  151. {
  152. Vector2 headerPosition = new Vector2(ScreenManager.SafeArea.Left + 100, ScreenManager.SafeArea.Top + 150);
  153. Vector2 sessionPosition = new Vector2(ScreenManager.SafeArea.Left + 120, headerPosition.Y + font.LineSpacing * 1.5f);
  154. float scale = 0.85f;
  155. for (int i = 0; i < sessionEntries.Count; i++)
  156. {
  157. var sessionEntry = sessionEntries[i];
  158. Vector2 textSize = font.MeasureString(sessionEntry.Text) * scale;
  159. Rectangle hitBox = new Rectangle(
  160. (int)sessionPosition.X,
  161. (int)sessionPosition.Y,
  162. (int)textSize.X,
  163. (int)(font.LineSpacing * scale)
  164. );
  165. if (hitBox.Contains(mousePosition))
  166. {
  167. // Clicked on this session - join it
  168. JoinSessionMenuEntrySelected(sessionEntry, EventArgs.Empty);
  169. return;
  170. }
  171. sessionPosition.Y += font.LineSpacing * scale + 10;
  172. }
  173. }
  174. }
  175. // Let base class handle button input
  176. base.HandleInput(input);
  177. }
  178. protected override void OnCancel(PlayerIndex playerIndex)
  179. {
  180. // Exit all screens and return to main menu (without BackgroundScreen to avoid logo)
  181. foreach (GameScreen screen in ScreenManager.GetScreens())
  182. screen.ExitScreen();
  183. ScreenManager.AddScreen(new BackgroundScreen(), null);
  184. ScreenManager.AddScreen(new MainMenuScreen(), null);
  185. }
  186. public override void Draw(GameTime gameTime)
  187. {
  188. // Draw solid background to cover any BackgroundScreen logo
  189. ScreenManager.GraphicsDevice.Clear(new Color(50, 20, 20)); // Dark red background
  190. // Draw menu entries (bottom buttons) and title from base class
  191. base.Draw(gameTime);
  192. SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
  193. SpriteFont font = ScreenManager.Font;
  194. spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, ScreenManager.GlobalTransformation);
  195. // Draw "Available Games:" section header and session list
  196. if (availableSessions.Count > 0)
  197. {
  198. // Draw header
  199. Vector2 headerPosition = new Vector2(ScreenManager.SafeArea.Left + 100, ScreenManager.SafeArea.Top + 150);
  200. spriteBatch.DrawString(font, Resources.AvailableGames, headerPosition, Color.Yellow, 0f, Vector2.Zero, 0.9f, SpriteEffects.None, 0f);
  201. // Draw session entries in a vertical list below the header
  202. Vector2 sessionPosition = new Vector2(ScreenManager.SafeArea.Left + 120, headerPosition.Y + font.LineSpacing * 1.5f);
  203. for (int i = 0; i < sessionEntries.Count; i++)
  204. {
  205. var sessionEntry = sessionEntries[i];
  206. Color color = Color.White;
  207. float scale = 0.85f;
  208. // Draw the session entry text
  209. string sessionText = sessionEntry.Text;
  210. spriteBatch.DrawString(font, sessionText, sessionPosition, color, 0f, Vector2.Zero, scale, SpriteEffects.None, 0f);
  211. // Move down for next entry
  212. sessionPosition.Y += font.LineSpacing * scale + 10; // Add some padding
  213. }
  214. }
  215. // Draw status at bottom left
  216. Vector2 statusPosition = new Vector2(ScreenManager.SafeArea.Left + 50, ScreenManager.SafeArea.Bottom - 80);
  217. var pluralGames = availableSessions.Count == 0 || availableSessions.Count > 1 ? "s" : "";
  218. string statusText = isSearching
  219. ? Resources.SearchingForGames
  220. : string.Format(Resources.FoundGames, availableSessions.Count, pluralGames);
  221. spriteBatch.DrawString(font, statusText, statusPosition, Color.LightGreen, 0f, Vector2.Zero, 0.8f, SpriteEffects.None, 0f);
  222. // Show auto-refresh timer
  223. if (!isSearching)
  224. {
  225. int secondsUntilRefresh = (int)(AutoRefreshInterval - timeSinceLastSearch.TotalSeconds);
  226. string timerText = string.Format(Resources.AutoRefreshIn, secondsUntilRefresh);
  227. statusPosition.Y += font.LineSpacing;
  228. spriteBatch.DrawString(font, timerText, statusPosition, Color.Gray, 0f, Vector2.Zero, 0.7f, SpriteEffects.None, 0f);
  229. }
  230. spriteBatch.End();
  231. }
  232. // Helper class for session info
  233. // Deprecated: AvailableSession replaced by AvailableNetworkSession
  234. void BeginFindSessions()
  235. {
  236. if (isSearching)
  237. return; // Already searching
  238. isSearching = true;
  239. var asyncResult = NetworkSession.FindAsync(
  240. NetworkSessionType.SystemLink,
  241. 1, // local gamers
  242. null);
  243. var busyScreen = new NetworkBusyScreen<AvailableNetworkSessionCollection>(asyncResult);
  244. busyScreen.OperationCompleted += (s, evt) =>
  245. {
  246. isSearching = false;
  247. var foundSessions = evt.Result as AvailableNetworkSessionCollection;
  248. availableSessions.Clear();
  249. if (foundSessions != null)
  250. {
  251. foreach (var session in foundSessions)
  252. availableSessions.Add(session);
  253. }
  254. RefreshSessionList();
  255. };
  256. ScreenManager.AddScreen(busyScreen, null);
  257. }
  258. }
  259. }