PageRenderTime 2601ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/v1.14.88/menus/netgamebrowser.cpp

https://github.com/simX/d2x-xl
C++ | 429 lines | 363 code | 35 blank | 31 comment | 97 complexity | 97a68b39e0ee421f0e1bb533f9e6fba0 MD5 | raw file
Possible License(s): GPL-2.0
  1. #ifdef HAVE_CONFIG_H
  2. # include <conf.h>
  3. #endif
  4. #define PATCH12
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <stdlib.h>
  8. #ifdef _WIN32
  9. # include <winsock.h>
  10. #else
  11. # include <sys/socket.h>
  12. #endif
  13. #include "descent.h"
  14. #include "strutil.h"
  15. #include "args.h"
  16. #include "timer.h"
  17. #include "ipx.h"
  18. #include "ipx_udp.h"
  19. #include "menu.h"
  20. #include "key.h"
  21. #include "error.h"
  22. #include "network.h"
  23. #include "network_lib.h"
  24. #include "menu.h"
  25. #include "text.h"
  26. #include "byteswap.h"
  27. #include "netmisc.h"
  28. #include "kconfig.h"
  29. #include "autodl.h"
  30. #include "tracker.h"
  31. #include "gamefont.h"
  32. #include "menubackground.h"
  33. #include "netmenu.h"
  34. #define LHX(x) (gameStates.menus.bHires?2* (x):x)
  35. #define LHY(y) (gameStates.menus.bHires? (24* (y))/10:y)
  36. char szGameList [MAX_ACTIVE_NETGAMES + 5][100];
  37. //------------------------------------------------------------------------------
  38. void InitNetgameMenuOption (CMenu& menu, int j)
  39. {
  40. if (j >= int (menu.ToS ()))
  41. menu.AddMenu (szGameList [j]);
  42. CMenuItem* m = menu + j;
  43. sprintf (m->m_text, "%2d. ", j - 1 - tracker.m_bUse);
  44. m->m_value = 0;
  45. m->m_bRebuild = 1;
  46. }
  47. //------------------------------------------------------------------------------
  48. void InitNetgameMenu (CMenu& menu, int i)
  49. {
  50. int j;
  51. for (j = i + 2 + tracker.m_bUse; i < MAX_ACTIVE_NETGAMES; i++, j++)
  52. InitNetgameMenuOption (menu, j);
  53. }
  54. //------------------------------------------------------------------------------
  55. extern int nTabs [];
  56. char *PruneText (char *pszDest, char *pszSrc, int nSize, int nPos, int nVersion)
  57. {
  58. int lDots, lMax, l, tx, ty, ta;
  59. char* psz;
  60. CFont* curFont = CCanvas::Current ()->Font ();
  61. if (gameOpts->menus.bShowLevelVersion && (nVersion >= 0)) {
  62. if (nVersion)
  63. sprintf (pszDest, "[%d] ", nVersion);
  64. else
  65. strcpy (pszDest, "[?] ");
  66. strncat (pszDest + 4, pszSrc, nSize - 4);
  67. }
  68. else
  69. strncpy (pszDest, pszSrc, nSize);
  70. pszDest [nSize - 1] = '\0';
  71. if ((psz = strchr (pszDest, '\t')))
  72. *psz = '\0';
  73. fontManager.SetCurrent (SMALL_FONT);
  74. fontManager.Current ()->StringSize ("... ", lDots, ty, ta);
  75. fontManager.Current ()->StringSize (pszDest, tx, ty, ta);
  76. l = (int) strlen (pszDest);
  77. lMax = LHX (nTabs [nPos]) - LHX (nTabs [nPos - 1]);
  78. if (tx > lMax) {
  79. lMax -= lDots;
  80. do {
  81. pszDest [--l] = '\0';
  82. fontManager.Current ()->StringSize (pszDest, tx, ty, ta);
  83. } while (tx > lMax);
  84. strcat (pszDest, "...");
  85. }
  86. fontManager.SetCurrent (curFont);
  87. return pszDest;
  88. }
  89. //------------------------------------------------------------------------------
  90. const char *szModeLetters [] =
  91. {"ANRCHY",
  92. "TEAM",
  93. "ROBO",
  94. "COOP",
  95. "FLAG",
  96. "HOARD",
  97. "TMHOARD",
  98. "ENTROPY",
  99. "MONSTER"};
  100. int NetworkJoinPoll (CMenu& menu, int& key, int nCurItem, int nState)
  101. {
  102. if (nState)
  103. return nCurItem;
  104. // Polling loop for Join Game menu
  105. static fix t1 = 0;
  106. int t = SDL_GetTicks ();
  107. int i, h = 2 + tracker.m_bUse, osocket, nJoinStatus, bPlaySound = 0;
  108. const char *psz;
  109. char szOption [200];
  110. char szTrackers [100];
  111. if (tracker.m_bUse) {
  112. i = tracker.ActiveCount (0);
  113. menu [1].m_color = ORANGE_RGBA;
  114. sprintf (szTrackers, TXT_TRACKERS_FOUND, i, (i == 1) ? "" : "s");
  115. if (strcmp (menu [1].m_text, szTrackers)) {
  116. strcpy (menu [1].m_text, szTrackers);
  117. // menu [1].x = (short) 0x8000;
  118. menu [1].m_bRebuild = 1;
  119. }
  120. }
  121. if ((gameStates.multi.nGameType >= IPX_GAME) && networkData.bAllowSocketChanges) {
  122. osocket = networkData.nSocket;
  123. if (key == KEY_PAGEDOWN) {
  124. networkData.nSocket--;
  125. key = 0;
  126. }
  127. else if (key == KEY_PAGEUP) {
  128. networkData.nSocket++;
  129. key = 0;
  130. }
  131. if (networkData.nSocket > 99)
  132. networkData.nSocket = -99;
  133. else if (networkData.nSocket < -99)
  134. networkData.nSocket = 99;
  135. if (networkData.nSocket + IPX_DEFAULT_SOCKET > 0x8000)
  136. networkData.nSocket = 0x8000 - IPX_DEFAULT_SOCKET;
  137. if (networkData.nSocket + IPX_DEFAULT_SOCKET < 0)
  138. networkData.nSocket = IPX_DEFAULT_SOCKET;
  139. if (networkData.nSocket != osocket) {
  140. sprintf (menu [0].m_text, TXT_CURR_SOCK,
  141. (gameStates.multi.nGameType == IPX_GAME) ? "IPX" : "UDP",
  142. networkData.nSocket);
  143. menu [0].m_bRebuild = 1;
  144. #if 1
  145. console.printf (0, TXT_CHANGE_SOCK, networkData.nSocket);
  146. #endif
  147. NetworkListen ();
  148. IpxChangeDefaultSocket ((ushort) (IPX_DEFAULT_SOCKET + networkData.nSocket));
  149. RestartNetSearching (menu);
  150. NetworkSendGameListRequest ();
  151. return nCurItem;
  152. }
  153. }
  154. // send a request for game info every 3 seconds
  155. #if DBG
  156. if (!networkData.nActiveGames)
  157. #endif
  158. if (gameStates.multi.nGameType >= IPX_GAME) {
  159. if (t > t1 + 3000) {
  160. if (!NetworkSendGameListRequest ())
  161. return nCurItem;
  162. t1 = t;
  163. }
  164. }
  165. NetworkListen ();
  166. DeleteTimedOutNetGames ();
  167. if (networkData.bGamesChanged || (networkData.nActiveGames != networkData.nLastActiveGames)) {
  168. networkData.bGamesChanged = 0;
  169. networkData.nLastActiveGames = networkData.nActiveGames;
  170. #if 1
  171. console.printf (CON_DBG, "Found %d netgames.\n", networkData.nActiveGames);
  172. #endif
  173. // Copy the active games data into the menu options
  174. for (i = 0; i < networkData.nActiveGames; i++, h++) {
  175. int gameStatus = activeNetGames [i].gameStatus;
  176. int nplayers = 0;
  177. char szLevelName [20], szMissionName [50], szGameName [50];
  178. int nLevelVersion = gameOpts->menus.bShowLevelVersion ? FindMissionByName (activeNetGames [i].szMissionName, -1) : -1;
  179. // These next two loops protect against menu skewing
  180. // if missiontitle or gamename contain a tab
  181. PruneText (szMissionName, activeNetGames [i].szMissionTitle, sizeof (szMissionName), 4, nLevelVersion);
  182. PruneText (szGameName, activeNetGames [i].szGameName, sizeof (szGameName), 1, -1);
  183. nplayers = activeNetGames [i].nConnected;
  184. if (activeNetGames [i].nLevel < 0)
  185. sprintf (szLevelName, "S%d", -activeNetGames [i].nLevel);
  186. else
  187. sprintf (szLevelName, "%d", activeNetGames [i].nLevel);
  188. if (gameStatus == NETSTAT_STARTING)
  189. psz = "Forming";
  190. else if (gameStatus == NETSTAT_PLAYING) {
  191. nJoinStatus = CanJoinNetgame (activeNetGames + i, NULL);
  192. if (nJoinStatus == 1)
  193. psz = "Open";
  194. else if (nJoinStatus == 2)
  195. psz = "Full";
  196. else if (nJoinStatus == 3)
  197. psz = "Restrict";
  198. else
  199. psz = "Closed";
  200. }
  201. else
  202. psz = "Between";
  203. sprintf (szOption, "%2d.\t%s\t%s\t%d/%d\t%s\t%s\t%s",
  204. i + 1, szGameName, szModeLetters [activeNetGames [i].gameMode], nplayers,
  205. activeNetGames [i].nMaxPlayers, szMissionName, szLevelName, psz);
  206. Assert (strlen (szOption) < 100);
  207. if (strcmp (szOption, menu [h].m_text)) {
  208. memcpy (menu [h].m_text, szOption, 100);
  209. menu [h].m_bRebuild = 1;
  210. menu [h].m_bUnavailable = (nLevelVersion == 0);
  211. bPlaySound = 1;
  212. }
  213. }
  214. for (i = 3 + networkData.nActiveGames; i < MAX_ACTIVE_NETGAMES; i++, h++)
  215. InitNetgameMenuOption (menu, h);
  216. }
  217. #if 0
  218. else
  219. for (i = 3; i < networkData.nActiveGames; i++, h++)
  220. menu [h].m_value = SDL_GetTicks ();
  221. for (i = 3 + networkData.nActiveGames; i < MAX_ACTIVE_NETGAMES; i++, h++)
  222. if (menu [h].m_value && (t - menu [h].m_value > 10000))
  223. {
  224. InitNetgameMenuOption (menu, h);
  225. bPlaySound = 1;
  226. }
  227. #endif
  228. if (bPlaySound)
  229. audio.PlaySound (SOUND_HUD_MESSAGE);
  230. return nCurItem;
  231. }
  232. //------------------------------------------------------------------------------
  233. int NetworkBrowseGames (void)
  234. {
  235. CMenu menu (MAX_ACTIVE_NETGAMES + 5);
  236. int choice, bAutoRun = gameData.multiplayer.autoNG.bValid;
  237. char callsign [CALLSIGN_LEN+1];
  238. //PrintLog ("launching netgame browser\n");
  239. memcpy (callsign, LOCALPLAYER.callsign, sizeof (callsign));
  240. if (gameStates.multi.nGameType >= IPX_GAME) {
  241. if (!networkData.bActive) {
  242. MsgBox (NULL, NULL, 1, TXT_OK, TXT_IPX_NOT_FOUND);
  243. return 0;
  244. }
  245. }
  246. //PrintLog (" NetworkInit\n");
  247. NetworkInit ();
  248. gameData.multiplayer.nPlayers = 0;
  249. setjmp (gameExitPoint);
  250. networkData.nJoining = 0;
  251. networkData.nJoinState = 0;
  252. networkData.nStatus = NETSTAT_BROWSING; // We are looking at a game menu
  253. IpxChangeDefaultSocket ((ushort) (IPX_DEFAULT_SOCKET + networkData.nSocket));
  254. //PrintLog (" NetworkFlush\n");
  255. NetworkFlush ();
  256. //PrintLog (" NetworkListen\n");
  257. NetworkListen (); // Throw out old info
  258. //PrintLog (" NetworkSendGameListRequest\n");
  259. NetworkSendGameListRequest (); // broadcast a request for lists
  260. networkData.nActiveGames = 0;
  261. networkData.nLastActiveGames = 0;
  262. memset (activeNetGames, 0, sizeof (activeNetGames));
  263. memset (activeNetPlayers, 0, sizeof (activeNetPlayers));
  264. if (!bAutoRun) {
  265. fontManager.SetColorRGBi (RGB_PAL (15, 15, 23), 1, 0, 0);
  266. menu.AddText (szGameList [0]);
  267. menu.Top ()->m_bNoScroll = 1;
  268. menu.Top ()->m_x = (short) 0x8000; //centered
  269. if (gameStates.multi.nGameType >= IPX_GAME) {
  270. if (networkData.bAllowSocketChanges)
  271. sprintf (menu.Top ()->m_text, TXT_CURR_SOCK, (gameStates.multi.nGameType == IPX_GAME) ? "IPX" : "UDP", networkData.nSocket);
  272. else
  273. *menu.Top ()->m_text = '\0';
  274. }
  275. if (tracker.m_bUse) {
  276. menu.AddText (szGameList [1]);
  277. strcpy (menu.Top ()->m_text, TXT_0TRACKERS);
  278. menu.Top ()->m_x = (short) 0x8000;
  279. menu.Top ()->m_bNoScroll = 1;
  280. }
  281. menu.AddText (szGameList [1 + tracker.m_bUse]);
  282. strcpy (menu.Top ()->m_text, TXT_GAME_BROWSER);
  283. menu.Top ()->m_bNoScroll = 1;
  284. InitNetgameMenu (menu, 0);
  285. }
  286. networkData.bGamesChanged = 1;
  287. doMenu:
  288. gameStates.app.nExtGameStatus = GAMESTAT_JOIN_NETGAME;
  289. if (bAutoRun) {
  290. fix t, t0 = 0;
  291. do {
  292. if ((t = SDL_GetTicks ()) > t0 + I2X (1)) {
  293. t0 = t;
  294. NetworkSendGameListRequest ();
  295. }
  296. NetworkListen ();
  297. if (KeyInKey () == KEY_ESC)
  298. return 0;
  299. } while (!networkData.nActiveGames);
  300. choice = 0;
  301. }
  302. else {
  303. gameStates.multi.bSurfingNet = 1;
  304. // NMLoadBackground (BackgroundName (BG_MENU), &bg, 0); //load this here so if we abort after loading level, we restore the palette
  305. // paletteManager.LoadEffect ();
  306. choice = menu.Menu (TXT_NETGAMES, NULL, NetworkJoinPoll, NULL, NULL, LHX (340), -1, 1);
  307. // backgroundManager.Remove ();
  308. gameStates.multi.bSurfingNet = 0;
  309. }
  310. if (choice == -1) {
  311. ChangePlayerNumTo (0);
  312. memcpy (LOCALPLAYER.callsign, callsign, sizeof (callsign));
  313. networkData.nStatus = NETSTAT_MENU;
  314. return 0; // they cancelled
  315. }
  316. choice -= (2 + tracker.m_bUse);
  317. if ((choice < 0) || (choice >= networkData.nActiveGames)) {
  318. //MsgBox (TXT_SORRY, NULL, 1, TXT_OK, TXT_INVALID_CHOICE);
  319. goto doMenu;
  320. }
  321. // Choice has been made and looks legit
  322. if (AGI.gameStatus == NETSTAT_ENDLEVEL) {
  323. MsgBox (TXT_SORRY, NULL, 1, TXT_OK, TXT_NET_GAME_BETWEEN2);
  324. goto doMenu;
  325. }
  326. if (AGI.protocolVersion != MULTI_PROTO_VERSION) {
  327. if (AGI.protocolVersion == 3) {
  328. MsgBox (TXT_SORRY, NULL, 1, TXT_OK, TXT_INCOMPAT1);
  329. }
  330. else if (AGI.protocolVersion == 4) {
  331. }
  332. else {
  333. char szFmt [200], szError [200];
  334. sprintf (szFmt, "%s%s", TXT_VERSION_MISMATCH, TXT_NETGAME_VERSIONS);
  335. sprintf (szError, szFmt, MULTI_PROTO_VERSION, AGI.protocolVersion);
  336. MsgBox (TXT_SORRY, NULL, 1, TXT_OK, szError);
  337. }
  338. goto doMenu;
  339. }
  340. if (tracker.m_bUse) {
  341. //PrintLog (" getting server lists from trackers\n");
  342. tracker.GetServerFromList (choice);
  343. }
  344. // Check for valid mission name
  345. console.printf (CON_DBG, TXT_LOADING_MSN, AGI.szMissionName);
  346. if (!(LoadMissionByName (AGI.szMissionName, -1) || (downloadManager.DownloadMission (AGI.szMissionName) && LoadMissionByName (AGI.szMissionName, -1)))) {
  347. PrintLog ("Mission '%s' not found%s\n", AGI.szMissionName);
  348. MsgBox (NULL, NULL, 1, TXT_OK, TXT_MISSION_NOT_FOUND);
  349. goto doMenu;
  350. }
  351. if (IS_D2_OEM && (AGI.nLevel > 8)) {
  352. MsgBox (NULL, NULL, 1, TXT_OK, TXT_OEM_ONLY8);
  353. goto doMenu;
  354. }
  355. if (IS_MAC_SHARE && (AGI.nLevel > 4)) {
  356. MsgBox (NULL, NULL, 1, TXT_OK, TXT_SHARE_ONLY4);
  357. goto doMenu;
  358. }
  359. if (!NetworkWaitForAllInfo (choice)) {
  360. MsgBox (TXT_SORRY, NULL, 1, TXT_OK, TXT_JOIN_ERROR);
  361. networkData.nStatus = NETSTAT_BROWSING; // We are looking at a game menu
  362. goto doMenu;
  363. }
  364. networkData.nStatus = NETSTAT_BROWSING; // We are looking at a game menu
  365. if (!CanJoinNetgame (activeNetGames + choice, activeNetPlayers + choice)) {
  366. if (AGI.nNumPlayers == AGI.nMaxPlayers)
  367. MsgBox (TXT_SORRY, NULL, 1, TXT_OK, TXT_GAME_FULL);
  368. else
  369. MsgBox (TXT_SORRY, NULL, 1, TXT_OK, TXT_IN_PROGRESS);
  370. goto doMenu;
  371. }
  372. // Choice is valid, prepare to join in
  373. memcpy (&netGame, activeNetGames + choice, sizeof (tNetgameInfo));
  374. memcpy (&netPlayers, activeNetPlayers + choice, sizeof (tAllNetPlayersInfo));
  375. gameStates.app.nDifficultyLevel = netGame.difficulty;
  376. gameData.multiplayer.nMaxPlayers = netGame.nMaxPlayers;
  377. ChangePlayerNumTo (1);
  378. memcpy (LOCALPLAYER.callsign, callsign, sizeof (callsign));
  379. // Handle the extra data for the network driver
  380. // For the mcast4 driver, this is the game's multicast address, to
  381. // which the driver subscribes.
  382. if (IpxHandleNetGameAuxData (netGame.AuxData) < 0) {
  383. networkData.nStatus = NETSTAT_BROWSING;
  384. goto doMenu;
  385. }
  386. NetworkSetGameMode (netGame.gameMode);
  387. NetworkAdjustMaxDataSize ();
  388. //PrintLog ("loading level\n");
  389. StartNewLevel (netGame.nLevel, true);
  390. //PrintLog ("exiting netgame browser\n");
  391. //backgroundManager.Remove ();
  392. return 1; // look ma, we're in a game!!!
  393. }
  394. //------------------------------------------------------------------------------