networkgame.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. /****************************************************************************
  2. * Copyright (C) 1995-1996 Microsoft Corporation. All Rights Reserved.
  3. *
  4. * File: NetworkGame.cpp
  5. *
  6. * Author: Curt Carpenter
  7. *
  8. * Description: DPlay interface for networking stuff
  9. *
  10. ******************************************************************************/
  11. #include "pch.h"
  12. #include <dplobby.h>
  13. #define CURRENT_DPLAY_VER 318
  14. //
  15. // Variables global to this module.
  16. //
  17. DPlayWrapper mDPlayWrap;
  18. BOOL mfMultiplex = FALSE;
  19. //
  20. // NetworkGame
  21. //
  22. // Basic constructor for a network game.
  23. NetworkGame::NetworkGame()
  24. {
  25. mpDirectPlay = NULL;
  26. mhPlayerEvent = NULL;
  27. mdpId = 0;
  28. mcMessagesSent = 0;
  29. mcMessagesReceived = 0;
  30. mcPlayers = 0;
  31. #ifdef RECEIVETHREAD
  32. mhReceiveThread = NULL;
  33. mhKillReceiveEvent = NULL;
  34. #endif
  35. }
  36. // ~NetworkGame
  37. //
  38. // Destructor. Releases all DirectPlay state.
  39. //
  40. NetworkGame::~NetworkGame()
  41. {
  42. debugf("NWG:~NetworkGame()\n");
  43. this->ShutdownConnection();
  44. }
  45. /****************************************************************************\
  46. *
  47. * FUNCTION: EnumSessionsCallback2
  48. *
  49. * PURPOSE: Called by IDirectPlay#::EnumSessions
  50. *
  51. *\***************************************************************************/
  52. BOOL FAR PASCAL EnumSessionsCallback2(LPCDPSESSIONDESC2 lpThisSD,
  53. LPDWORD lpdwTimeOut,
  54. DWORD dwFlags,
  55. LPVOID lpContext)
  56. {
  57. // lpContext is a pointer for where to stick the instance guid, which is all we want
  58. if (lpThisSD) // will be null if we timed out
  59. *(GUID*)lpContext = lpThisSD->guidInstance;
  60. return FALSE;
  61. }
  62. /****************************************************************************\
  63. *
  64. * FUNCTION: SetupConnection
  65. *
  66. * PURPOSE: sets up all the dplay stuff and puts us in a listen state
  67. *
  68. *\***************************************************************************/
  69. HRESULT NetworkGame::SetupConnection(const char * szFedServer,
  70. const char * szCharName,
  71. const char * szCharPW,
  72. bool fCreateNew,
  73. FILETIME * pftLastArtUpdate)
  74. {
  75. HRESULT hr = S_OK;
  76. char szPlayerName[30] = "/"; // we're going to start copying past the slash
  77. DWORD dwcbPlayerName = sizeof(szPlayerName) - 1;
  78. DWORD dwAddressSize = 0;
  79. #ifdef RECEIVETHREAD
  80. DWORD idReceiveThread = 0; // id of receive thread
  81. #endif
  82. //
  83. // Create an IDirectPlay3 session to the server.
  84. //
  85. hr = mDPlayWrap.AllocDirectPlaySession(szFedServer, &mpDirectPlay);
  86. if (SUCCEEDED(hr))
  87. {
  88. // Use character name / user name for player name
  89. GetUserName(szPlayerName + 1, &dwcbPlayerName);
  90. //
  91. // Create a player
  92. //
  93. DPNAME dpName;
  94. dpName.dwSize = sizeof(dpName);
  95. dpName.dwFlags = 0;
  96. dpName.lpszShortNameA = *(char**)&szCharName; // make compiler happy about const-ness
  97. dpName.lpszLongNameA = szPlayerName;
  98. hr = mpDirectPlay->CreatePlayer(&mdpId,
  99. &dpName,
  100. mhPlayerEvent,
  101. NULL,
  102. 0,
  103. 0);
  104. if (SUCCEEDED(hr))
  105. {
  106. //
  107. // Player on this machine always in first slot
  108. //
  109. mPlayers[0].dpid = mdpId;
  110. mPlayers[0].FormalName[0] = 0;
  111. strcpy(mPlayers[0].FriendlyName, szPlayerName);
  112. mcPlayers++;
  113. DPCAPS dpcaps;
  114. dpcaps.dwSize = sizeof(dpcaps);
  115. hr = mpDirectPlay->GetCaps(&dpcaps, 0);
  116. FM.Init(mpDirectPlay, mdpId);
  117. {
  118. }
  119. } else
  120. debugf("NWG: Failed to create user\n");
  121. } else
  122. debugf("NWG: Failed to obtain dplay session\n");
  123. if (FAILED(hr))
  124. this->ShutdownConnection();
  125. return(hr);
  126. }
  127. LPSTR NetworkGame::GetPlayerName(int i)
  128. {
  129. if ((i < 0) || (i >= mcPlayers))
  130. return NULL;
  131. return(&mPlayers[i].FriendlyName[0]);
  132. }
  133. HRESULT NetworkGame::SendMessages(DPID dpidTo, bool fGuaranteed)
  134. {
  135. FM.SendMessages(dpidTo, fGuaranteed, NULL);
  136. return S_OK;
  137. }
  138. DPID NetworkGame::DpId()
  139. {
  140. return(mdpId);
  141. }
  142. #ifdef RECEIVETHREAD
  143. /****************************************************************************\
  144. *
  145. * FUNCTION: ReceiveThread
  146. *
  147. * PURPOSE: Waits for messages in its own thread
  148. *
  149. *\***************************************************************************/
  150. DWORD WINAPI ReceiveThread(LPVOID pNWG_This)
  151. {
  152. //
  153. // BUGBUG: This won't work any more. If we want to implement this, we
  154. // need access to the BasePlayerData so that we can call the callback.
  155. // If we decide to implement this, it should probably be in the
  156. // BasePlayerData class instead of here.
  157. //
  158. NetworkGame * pNetworkGame = (NetworkGame * ) pNWG_This;
  159. HANDLE eventHandles[2];
  160. BOOL fSysMsg;
  161. eventHandles[0] = pNetworkGame->mhPlayerEvent;
  162. eventHandles[1] = pNetworkGame->mhKillReceiveEvent;
  163. // loop waiting for player events. If the kill event is signaled
  164. // the thread will exit
  165. while (WaitForMultipleObjects(2, eventHandles, FALSE, INFINITE) == WAIT_OBJECT_0)
  166. {
  167. // receive any messages in the queue
  168. pNetworkGame->ReceiveMessages(&fSysMsg);
  169. }
  170. ExitThread(0);
  171. return (0);
  172. }
  173. #endif // RECEIVETHREAD
  174. /****************************************************************************\
  175. *
  176. * FUNCTION: ReceiveMessages
  177. *
  178. * PURPOSE: Reads messages queued by DPlay
  179. *
  180. * Returns: S_OK if message needs processing,
  181. * S_FALSE message doesn't need processing,
  182. * or failure code (including no more messages)
  183. *
  184. *\***************************************************************************/
  185. HRESULT NetworkGame::ReceiveMessages(BOOL * pfSystemMsg)
  186. {
  187. DPID idFrom;
  188. HRESULT hr;
  189. *pfSystemMsg = FALSE;
  190. hr = S_FALSE;
  191. if (0 != mdpId)
  192. {
  193. idFrom = 0;
  194. hr = FM.ReceiveMessages(mdpId, &idFrom);
  195. if (SUCCEEDED(hr))
  196. {
  197. if (FM.PacketSize() >= sizeof(DPMSG_GENERIC))
  198. {
  199. mcMessagesReceived++;
  200. if (idFrom == DPID_SYSMSG)
  201. *pfSystemMsg = TRUE;
  202. } else
  203. hr = S_FALSE;
  204. }
  205. }
  206. return (hr);
  207. }
  208. /****************************************************************************\
  209. *
  210. * FUNCTION: ShutdownConnection
  211. *
  212. * PURPOSE: Shut everything down and go home
  213. *
  214. *\***************************************************************************/
  215. void NetworkGame::ShutdownConnection()
  216. {
  217. #ifdef RECEIVETHREAD
  218. if (mhReceiveThread)
  219. {
  220. // wake up receive thread and wait for it to quit
  221. SetEvent(mhKillReceiveEvent);
  222. WaitForSingleObject(mhReceiveThread, INFINITE);
  223. CloseHandle(mhReceiveThread);
  224. mhReceiveThread = NULL;
  225. }
  226. if (mhKillReceiveEvent)
  227. {
  228. CloseHandle(mhKillReceiveEvent);
  229. mhKillReceiveEvent = NULL;
  230. }
  231. #endif // RECEIVETHREAD
  232. mDPlayWrap.FreeDirectPlaySession(mpDirectPlay);
  233. mpDirectPlay = NULL;
  234. #ifdef RECEIVETHREAD
  235. if (mhPlayerEvent)
  236. {
  237. CloseHandle(mhPlayerEvent);
  238. mhPlayerEvent = NULL;
  239. }
  240. #endif // RECEIVETHREAD
  241. }
  242. /****************************************************************************\
  243. *
  244. * FUNCTION: HandleSysMsg
  245. *
  246. * PURPOSE: Handle system message
  247. *
  248. *\***************************************************************************/
  249. void NetworkGame::HandleSysMsg(DPID idTo, DPID idFrom)
  250. {
  251. // The body of each case is there so you can set a breakpoint and examine
  252. // the contents of the message received.
  253. LPDPMSG_GENERIC pMsg = (LPDPMSG_GENERIC) FM.BuffIn();
  254. debugf("NWG: HandleSysMsg from %lu to %lu, dwType %lu\n", idFrom, idTo, pMsg->dwType);
  255. switch (pMsg->dwType)
  256. {
  257. case DPSYS_CREATEPLAYERORGROUP:
  258. {
  259. // should never get this
  260. }
  261. break;
  262. case DPSYS_DESTROYPLAYERORGROUP:
  263. {
  264. LPDPMSG_DESTROYPLAYERORGROUP lp = (LPDPMSG_DESTROYPLAYERORGROUP) pMsg;
  265. }
  266. break;
  267. case DPSYS_ADDPLAYERTOGROUP:
  268. {
  269. LPDPMSG_ADDPLAYERTOGROUP lp = (LPDPMSG_ADDPLAYERTOGROUP) pMsg;
  270. }
  271. break;
  272. case DPSYS_DELETEPLAYERFROMGROUP:
  273. {
  274. LPDPMSG_DELETEPLAYERFROMGROUP lp = (LPDPMSG_DELETEPLAYERFROMGROUP) pMsg;
  275. }
  276. break;
  277. case DPSYS_SESSIONLOST:
  278. {
  279. LPDPMSG_SESSIONLOST lp = (LPDPMSG_SESSIONLOST) pMsg;
  280. }
  281. break;
  282. case DPSYS_HOST:
  283. {
  284. LPDPMSG_HOST lp = (LPDPMSG_HOST) pMsg;
  285. }
  286. break;
  287. case DPSYS_SETPLAYERORGROUPDATA:
  288. {
  289. LPDPMSG_SETPLAYERORGROUPDATA lp = (LPDPMSG_SETPLAYERORGROUPDATA) pMsg;
  290. assert(false);
  291. }
  292. break;
  293. case DPSYS_SETPLAYERORGROUPNAME:
  294. {
  295. LPDPMSG_SETPLAYERORGROUPNAME lp = (LPDPMSG_SETPLAYERORGROUPNAME) pMsg;
  296. assert(false);
  297. }
  298. break;
  299. case DPSYS_SECUREMESSAGE:
  300. {
  301. LPDPMSG_SECUREMESSAGE lp = (LPDPMSG_SECUREMESSAGE) pMsg;
  302. assert(false);
  303. }
  304. break;
  305. }
  306. }
  307. //
  308. // We really want to use the same IDirectPlay pointer in all of
  309. // our network game code (DPlay creates 3 threads per IDirectPlay).
  310. // To do this, I've created these static functions to handle the
  311. // initialization and termination of the one DPlay pointer.
  312. //
  313. DPlayWrapper::DPlayWrapper()
  314. {
  315. mpDirectPlay = NULL;
  316. mpDirectPlayLobbyA = NULL;
  317. }
  318. DPlayWrapper::~DPlayWrapper()
  319. {
  320. ZAssert(NULL == mpDirectPlay);
  321. if (NULL != mpDirectPlayLobbyA)
  322. mpDirectPlayLobbyA->Release();
  323. }
  324. HRESULT DPlayWrapper::AllocDirectPlaySession(const CHAR * szServer,
  325. IDirectPlayX ** ppDirectPlay)
  326. {
  327. BOOL fCreateSession;
  328. VOID * pvAddress;
  329. DWORD cbAddress;
  330. HRESULT hr;
  331. hr = S_OK;
  332. fCreateSession = FALSE;
  333. if (!CheckDPlayVersion())
  334. hr = E_FAIL;
  335. //
  336. // Create a lobby if we don't already have one.
  337. //
  338. if (SUCCEEDED(hr) && NULL == mpDirectPlayLobbyA)
  339. {
  340. hr = DirectPlayLobbyCreate(NULL,
  341. &mpDirectPlayLobbyA,
  342. NULL,
  343. NULL,
  344. 0);
  345. }
  346. //
  347. // Create a directplay session if need be.
  348. //
  349. if (SUCCEEDED(hr) && (NULL == mpDirectPlay))
  350. {
  351. hr = CoCreateInstance(CLSID_DirectPlay,
  352. NULL,
  353. CLSCTX_INPROC_SERVER,
  354. IID_IDirectPlayX,
  355. (VOID **) &mpDirectPlay);
  356. if (SUCCEEDED(hr))
  357. fCreateSession = TRUE;
  358. }
  359. if (TRUE == fCreateSession)
  360. {
  361. //
  362. // At this point I have a new directplay pointer and lobby pointer.
  363. //
  364. //
  365. // Need to find out how big of an address buffer we need, so
  366. // we intentionally fail the first time.
  367. //
  368. pvAddress = NULL;
  369. cbAddress = 0;
  370. hr = mpDirectPlayLobbyA->CreateAddress(DPSPGUID_TCPIP,
  371. DPAID_INet,
  372. szServer,
  373. strlen(szServer) + 1,
  374. pvAddress,
  375. &cbAddress);
  376. if (DPERR_BUFFERTOOSMALL == hr)
  377. {
  378. pvAddress = new BYTE[cbAddress];
  379. if (NULL != pvAddress)
  380. {
  381. hr = mpDirectPlayLobbyA->CreateAddress(DPSPGUID_TCPIP,
  382. DPAID_INet,
  383. szServer,
  384. strlen(szServer) + 1,
  385. pvAddress,
  386. &cbAddress);
  387. } else
  388. hr = E_OUTOFMEMORY;
  389. }
  390. if (SUCCEEDED(hr))
  391. {
  392. hr = mpDirectPlay->InitializeConnection(pvAddress, 0);
  393. if (SUCCEEDED(hr))
  394. {
  395. //
  396. // Find the session.
  397. //
  398. DPSESSIONDESC2 sessionDesc;
  399. GUID guidDPInstance, guidZero;
  400. ZeroMemory(&sessionDesc, sizeof(sessionDesc));
  401. sessionDesc.dwSize = sizeof(sessionDesc);
  402. sessionDesc.guidApplication = FEDSRV_GUID;
  403. ZeroMemory(&guidDPInstance, sizeof(guidDPInstance));
  404. ZeroMemory(&guidZero, sizeof(guidZero));
  405. hr = mpDirectPlay->EnumSessions(&sessionDesc,
  406. 5000,
  407. EnumSessionsCallback2,
  408. &guidDPInstance,
  409. 0);
  410. if (FAILED(hr) || (guidZero == guidDPInstance))
  411. {
  412. //
  413. // Maybe we hit a burp. Try again.
  414. //
  415. hr = mpDirectPlay->EnumSessions(&sessionDesc,
  416. 5000,
  417. EnumSessionsCallback2,
  418. &guidDPInstance,
  419. 0);
  420. }
  421. if (guidZero != guidDPInstance)
  422. {
  423. //
  424. // Join the session.
  425. //
  426. sessionDesc.guidInstance = guidDPInstance;
  427. hr = mpDirectPlay->SecureOpen(&sessionDesc,
  428. DPOPEN_JOIN,
  429. NULL,
  430. NULL);
  431. } else
  432. hr = E_FAIL;
  433. }
  434. }
  435. if (NULL != pvAddress)
  436. delete[] pvAddress;
  437. if (FAILED(hr))
  438. {
  439. mpDirectPlay->Release();
  440. mpDirectPlay = NULL;
  441. }
  442. }
  443. if (SUCCEEDED(hr))
  444. {
  445. *ppDirectPlay = mpDirectPlay;
  446. if (FALSE == mfMultiplex)
  447. mpDirectPlay = NULL;
  448. else
  449. mpDirectPlay->AddRef();
  450. } else
  451. *ppDirectPlay = NULL;
  452. return(hr);
  453. }
  454. VOID DPlayWrapper::FreeDirectPlaySession(IDirectPlayX * pDirectPlay)
  455. {
  456. if (NULL != pDirectPlay)
  457. {
  458. if (FALSE == mfMultiplex)
  459. {
  460. //
  461. // Not multiplexing. Shut the whole thing down.
  462. //
  463. pDirectPlay->Close();
  464. pDirectPlay->Release();
  465. } else if (1 == pDirectPlay->Release())
  466. {
  467. //
  468. // We own the last reference. Shut down.
  469. //
  470. mpDirectPlay->Close();
  471. mpDirectPlay->Release();
  472. mpDirectPlay = NULL;
  473. }
  474. }
  475. }