Messages.cpp 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664
  1. /* Messages.cpp
  2. Interface between WinTrek and FedSrv
  3. Owner:
  4. */
  5. #include "pch.h"
  6. #include "MessageCore.h"
  7. const MsgClsPrio FedMessaging::c_mcpDefault = 1000;
  8. static GUID g_guidApplication;
  9. static DPID g_dpidFrom = 0;
  10. static bool g_fHandlingMsg = false;
  11. static CFMConnection * g_pcnxnFrom = NULL;
  12. static LPBYTE g_pbReceiveBuff = NULL;
  13. static DWORD g_dwMsgPacketSize = 0;
  14. // Ok, this is kinda hacky, but it's too late to make wide-effect changes. The problem is that while in the OnAppMessage
  15. // callback, they may delete the connection from which the message came. Then there may be another FEDMEESSAGE packed in
  16. // the dplay message. In order to process any FEDMESSAGE, we need the pfsPlayer, which is no longer valid if we've deleted
  17. // the connection, so we need to set a flag when a connection is deleted, so we know not to process any more FEDMESSAGEs
  18. // that might be part of the same packet. Note that this does not apply to other messages in different packets (dplay
  19. // messages), because in that case the private data for the player will be NULL, and we won't get the pfsPlayer
  20. static bool g_fConnectionDeleted = false;
  21. void DumpMemory()
  22. {
  23. _CrtDumpMemoryLeaks();
  24. }
  25. void whodidit()
  26. {
  27. if (g_fHandlingMsg)
  28. {
  29. CB cb = ((FEDMESSAGE*)(g_pbReceiveBuff))->cbmsg;
  30. FEDMSGID id = ((FEDMESSAGE*)(g_pbReceiveBuff))->fmid;
  31. debugf("Currently handling message from %s(%u).\n"
  32. "cbmsg=%d, fmid=%d, total packet size=%d\n",
  33. g_pcnxnFrom ? g_pcnxnFrom->GetName() : "<unknown>", g_dpidFrom,
  34. cb, id, g_dwMsgPacketSize);
  35. }
  36. else
  37. debugf("Not currently handling a message.\n");
  38. }
  39. /*-------------------------------------------------------------------------
  40. * EnumAddressCallback
  41. *-------------------------------------------------------------------------
  42. * Purpose:
  43. * Silly dplay mechanism to get all the parts of an address
  44. *
  45. * Parameters:
  46. * DPlay defined
  47. *
  48. * Returns:
  49. * Whether to continue enumerating address parts
  50. */
  51. BOOL FAR PASCAL EnumAddressCallback(REFGUID guidDataType,
  52. DWORD dwDataSize,
  53. LPCVOID lpData,
  54. LPVOID lpContext)
  55. {
  56. // What the hell? Why do I get so many parts, and not even of the known guid types??? Oh well, jump through the hoops
  57. if (DPAID_INet == guidDataType)
  58. {
  59. assert(dwDataSize > (DWORD) lstrlen((char *) lpData));
  60. lstrcpy((char *) lpContext, (char *) lpData);
  61. return FALSE;
  62. }
  63. return TRUE;
  64. }
  65. /*-------------------------------------------------------------------------
  66. * VerifyMessages
  67. *-------------------------------------------------------------------------
  68. Purpose:
  69. Make sure that our output buffer adheres to the expected format of a
  70. string of valid FEDMESSAGEs.
  71. Side Effects:
  72. Important: The default recipient no longer remains the default if
  73. SendMessages is called with a non-NULL precip
  74. */
  75. void VerifyMessage(LPBYTE pb, CB cbTotal)
  76. {
  77. #ifndef NO_MSG_CRC
  78. // <sigh> we can't do this as long as we have the hack to support non-crc clients, since we munge the message length on ack from logon to lobby
  79. #else
  80. int cMsgs = 0;
  81. CB cbRunning = 0;
  82. while (cbRunning < cbTotal)
  83. {
  84. FEDMESSAGE * pfm = (FEDMESSAGE *) pb;
  85. assert(pfm->cbmsg > 0);
  86. assert(pfm->fmid > 0);
  87. cbRunning += pfm->cbmsg;
  88. pb += pfm->cbmsg;
  89. cMsgs++;
  90. }
  91. assert(cbRunning == cbTotal);
  92. #endif
  93. }
  94. /*-------------------------------------------------------------------------
  95. * EnumSessionsCallback
  96. *-------------------------------------------------------------------------
  97. * Purpose:
  98. * Called by IDirectPlay#::EnumSessions when doing a broadcast EnumSessions
  99. *
  100. * Parameters:
  101. * specified by dplay
  102. */
  103. BOOL FAR PASCAL EnumSessionsCallback (LPCDPSESSIONDESC2 lpThisSD,
  104. LPDWORD lpdwTimeOut,
  105. DWORD dwFlags,
  106. LPVOID lpContext)
  107. {
  108. if( dwFlags & DPESC_TIMEDOUT )
  109. return FALSE; // The enumeration has timed out, so stop the enumeration.
  110. assert(lpThisSD);
  111. // lpContext is a FedMessaging*
  112. #ifdef DEBUG
  113. char szBuff[128];
  114. wsprintf(szBuff, "Found a session: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x} for application {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
  115. lpThisSD->guidInstance.Data1, lpThisSD->guidInstance.Data2,
  116. lpThisSD->guidInstance.Data3, lpThisSD->guidInstance.Data4[0],
  117. lpThisSD->guidInstance.Data4[1], lpThisSD->guidInstance.Data4[2],
  118. lpThisSD->guidInstance.Data4[3], lpThisSD->guidInstance.Data4[4],
  119. lpThisSD->guidInstance.Data4[5], lpThisSD->guidInstance.Data4[6],
  120. lpThisSD->guidInstance.Data4[7],
  121. lpThisSD->guidApplication.Data1, lpThisSD->guidApplication.Data2,
  122. lpThisSD->guidApplication.Data3, lpThisSD->guidApplication.Data4[0],
  123. lpThisSD->guidApplication.Data4[1], lpThisSD->guidApplication.Data4[2],
  124. lpThisSD->guidApplication.Data4[3], lpThisSD->guidApplication.Data4[4],
  125. lpThisSD->guidApplication.Data4[5], lpThisSD->guidApplication.Data4[6],
  126. lpThisSD->guidApplication.Data4[7]);
  127. debugf(szBuff);
  128. #endif
  129. if (lpThisSD->guidApplication == g_guidApplication)
  130. {
  131. FedMessaging * pfm = (FedMessaging *)lpContext;
  132. FMSessionDesc fmSession(const_cast<LPDPSESSIONDESC2>(lpThisSD));
  133. pfm->m_guidInstance = lpThisSD->guidInstance;
  134. if (pfm->m_fSessionCallback)
  135. pfm->m_pfmSite->OnSessionFound(pfm, &fmSession);
  136. }
  137. return TRUE;
  138. }
  139. FedMessaging::FedMessaging(IFedMessagingSite * pfmSite) :
  140. m_pDirectPlay(NULL),
  141. m_pDirectPlayLobby(NULL),
  142. m_cMsgsOdometer(0),
  143. m_cBytesOdometer(0),
  144. m_pbFMNext(m_rgbbuffOutPacket),
  145. m_pfmSite(pfmSite),
  146. m_fConnected(false),
  147. m_precipDefault(NULL),
  148. m_fmGuaranteedDefault(FM_GUARANTEED),
  149. m_pcnxnServer(NULL),
  150. m_pgrpEveryone(NULL),
  151. m_pcnxnMe(NULL),
  152. m_fSecondaryOut(false),
  153. m_pbFMNextT(m_rgbbuffSecondaryOutPacket),
  154. m_guidInstance(GUID_NULL),
  155. m_guidApplication(GUID_NULL),
  156. m_timeMsgLast(Time::Now()),
  157. m_priority(c_mcpDefault)
  158. {
  159. assert (pfmSite);
  160. }
  161. FedMessaging::~FedMessaging()
  162. {
  163. Shutdown();
  164. }
  165. /*-------------------------------------------------------------------------
  166. * SetDefaultRecipient
  167. *-------------------------------------------------------------------------
  168. Purpose:
  169. Set default recipient, to be used if the out buffer overflows, or
  170. NULL recipient is specified in SendMessages
  171. Important: The default recipient no longer remains the default if
  172. SendMessages is called with a NULL precip
  173. */
  174. void FedMessaging::SetDefaultRecipient(CFMRecipient * precip, FMGuaranteed fmg)
  175. {
  176. assert(0 == CbUsedSpaceInOutbox()); // should call this BEFORE you queue any messages
  177. m_precipDefault = precip;
  178. m_fmGuaranteedDefault = fmg;
  179. }
  180. /*-------------------------------------------------------------------------
  181. * GetDefaultRecipient
  182. *-------------------------------------------------------------------------
  183. Purpose:
  184. Get default recipient, and optionally other details about default recipient.
  185. See SetDefaultRecipient
  186. Parameters:
  187. pfGuaranteedDefault: output parameter to get guaranteed flag, or NULL if don't care
  188. */
  189. CFMRecipient * FedMessaging::GetDefaultRecipient(FMGuaranteed * pfmg)
  190. {
  191. if (pfmg)
  192. *pfmg = m_fmGuaranteedDefault;
  193. return m_precipDefault;
  194. }
  195. /*-------------------------------------------------------------------------
  196. * PFedMsgCreate
  197. *-------------------------------------------------------------------------
  198. * Purpose:
  199. * Generate a FedMsg of specified type, and construct all variable-length parms
  200. *
  201. * Parameters:
  202. * pfm - Where to create the message (non-NULL). If NULL, memory is allocated for message
  203. * Ignored if fQueueMsg==true
  204. * Returns:
  205. * pointer to newly created message
  206. *
  207. * Side Effects:
  208. * Queues up the message if fQueueMsg is set, otherwise allocates new buffer for msg
  209. */
  210. void * FedMessaging::PFedMsgCreate(bool fQueueMsg, BYTE * pbFMBuff, FEDMSGID fmid, CB cbfm, ...)
  211. {
  212. va_list vl;
  213. BYTE * pBlobSrc, * pBlobDst;
  214. BYTE * pbFM;
  215. assert (IsConnected());
  216. StartOver: // if we overrun our buffer, need to start over after flushing it
  217. va_start(vl, cbfm); // make sure we have the last required parm
  218. if (fQueueMsg && (cbfm > CbFreeSpaceInOutbox()))
  219. SendToDefault(FM_FLUSH);
  220. //figure out where to compose this message
  221. pbFM = fQueueMsg ? m_pbFMNext :
  222. (pbFMBuff ? pbFMBuff : m_rgbbuffAlloc);
  223. ((FEDMESSAGE*)pbFM)->fmid = fmid;
  224. pBlobDst = pbFM + cbfm; // where we start putting the blobs
  225. IB * pib = (IB*)(pbFM + sizeof(FEDMESSAGE)); // relies on ib/cb pairs starting immediately after fm
  226. CB * pcb;
  227. CB cbBlob;
  228. while (FM_END_VAR_PARMS != (pBlobSrc = va_arg(vl, BYTE *)))
  229. {
  230. pcb = pib + 1;
  231. *pib = pBlobDst - pbFM;
  232. *pcb = 0;
  233. BYTE * pbLastNonSpace; // we trim the trailing whitespace too as a free added bonus
  234. cbBlob = va_arg(vl, CB);
  235. if (CB_ZTS == cbBlob)
  236. {
  237. if (pBlobSrc) // we have something for this parm (as opposed to an intentional NULL)
  238. {
  239. if (*pBlobSrc) // pointer to non-NULL string
  240. {
  241. int cbLen = lstrlen((char *)(pBlobSrc));
  242. pbLastNonSpace = pBlobSrc + cbLen - 1;
  243. while (cbLen > 0 && ' ' == *pbLastNonSpace)
  244. {
  245. pbLastNonSpace--;
  246. cbLen--;
  247. }
  248. if (fQueueMsg && (pBlobDst + cbLen > BuffOut() + GetBuffOutSize()))
  249. {
  250. // Need to send what was cued up, then move the current message (portion so far) to the head of the cue
  251. SendToDefault(FM_FLUSH);
  252. goto StartOver;
  253. }
  254. CopyMemory(pBlobDst, pBlobSrc, cbLen);
  255. *(pBlobDst + cbLen++) = 0;
  256. pBlobDst += cbLen;
  257. *pcb = cbLen;
  258. }
  259. }
  260. }
  261. else // length supplied
  262. {
  263. if (fQueueMsg && (pBlobDst + cbBlob > BuffOut() + GetBuffOutSize()))
  264. {
  265. // Need to send what was cued up, then move the current message (portion so far) to the head of the cue
  266. SendToDefault(FM_FLUSH);
  267. goto StartOver;
  268. }
  269. // if non-NULL pointer, then copy, otherwise, just reserve the space
  270. if (pBlobSrc)
  271. CopyMemory(pBlobDst, pBlobSrc, cbBlob);
  272. pBlobDst += cbBlob;
  273. *pcb = cbBlob;
  274. }
  275. pib += 2; // advance to next ib/cb pair
  276. } // while more blobs left
  277. va_end(vl);
  278. ((FEDMESSAGE *)pbFM)->cbmsg = pBlobDst - pbFM;
  279. if (fQueueMsg || !pbFMBuff) // if we're copying to pre-alloced pfm, then we're done
  280. {
  281. if (fQueueMsg)
  282. {
  283. m_pbFMNext = pBlobDst;
  284. // check for overrun buffer--relies on unsigned CB, otherwise check for free space >= 0
  285. assert(CbFreeSpaceInOutbox() <= GetBuffOutSize());
  286. }
  287. else // need to allocate space
  288. {
  289. // allocate a new blob to return the message in
  290. assert (((FEDMESSAGE *)pbFM)->cbmsg <= sizeof(m_rgbbuffAlloc));
  291. BYTE * pbMsg = (BYTE *) GlobalAllocPtr(GMEM_MOVEABLE, ((FEDMESSAGE *)pbFM)->cbmsg);
  292. CopyMemory(pbMsg, pbFM, ((FEDMESSAGE *)pbFM)->cbmsg);
  293. pbFM = pbMsg;
  294. }
  295. }
  296. assert(((FEDMESSAGE*)pbFM)->cbmsg > 0 && ((FEDMESSAGE*)pbFM)->fmid > 0);
  297. return pbFM;
  298. }
  299. /*-------------------------------------------------------------------------
  300. * PfmGetNext
  301. *-------------------------------------------------------------------------
  302. * Purpose:
  303. * Get message after some provided message
  304. *
  305. * Parameters:
  306. * a message
  307. */
  308. FEDMESSAGE * FedMessaging::PfmGetNext(FEDMESSAGE * pfm)
  309. {
  310. BYTE * pfmNext = (BYTE*)pfm + pfm->cbmsg;
  311. assert(pfmNext >= BuffIn() && (BYTE*) pfm < (BuffIn()) + sizeof(m_rgbbuffInPacket));
  312. if (pfmNext - BuffIn() >= (int) PacketSize()) // no more messages in packet
  313. {
  314. assert((BYTE*)pfmNext == BuffIn() + PacketSize()); // should be exactly equal
  315. return NULL;
  316. }
  317. // Assert(!IsBadReadPointer(pfmNext, pfmNext->fm.cbfm))
  318. return (FEDMESSAGE *)pfmNext;
  319. }
  320. /*-------------------------------------------------------------------------
  321. * QueueExistingMsg
  322. *-------------------------------------------------------------------------
  323. * Purpose:
  324. * Take a message that already exists, and queue it up for the outgoing packet
  325. *
  326. * Parameters:
  327. * pfm: the pre-existing, properly setup message
  328. */
  329. void FedMessaging::QueueExistingMsg(const FEDMESSAGE * pfm)
  330. {
  331. assert(m_fConnected);
  332. assert(pfm->cbmsg > 0);
  333. assert(pfm->fmid > 0);
  334. if (CbFreeSpaceInOutbox() < pfm->cbmsg) // overrun buffer--send it if we have a default recipient
  335. {
  336. assert(m_precipDefault);
  337. SendToDefault(FM_FLUSH);
  338. }
  339. CopyMemory(m_pbFMNext, pfm, pfm->cbmsg);
  340. m_pbFMNext += pfm->cbmsg;
  341. assert(CbFreeSpaceInOutbox() <= GetBuffOutSize());
  342. }
  343. /*-------------------------------------------------------------------------
  344. * GenericSend
  345. *-------------------------------------------------------------------------
  346. * Purpose:
  347. * All messages go through this. Not used by FedMessaging users directly. Use SendMessages or ForwardMessage.
  348. */
  349. HRESULT FedMessaging::GenericSend(CFMRecipient * precip, const void * pv, CB cb, FMGuaranteed fmg)
  350. {
  351. DWORD dwFlags, dwPriority, dwTimeout;
  352. HRESULT hr;
  353. //
  354. // Give guaranteed messages a higher priority, and longer ttl
  355. //
  356. bool fGuaranteed = FM_GUARANTEED == fmg;
  357. dwFlags = DPSEND_ASYNC | (fGuaranteed ?
  358. DPSEND_GUARANTEED : DPSEND_NOSENDCOMPLETEMSG);
  359. dwPriority = m_priority;
  360. if (fGuaranteed)
  361. dwPriority += GetGuaranteedBias();
  362. dwTimeout = (fGuaranteed ? 0 : 500); // guaranteed messages will NEVER time out
  363. #ifdef DUMPMSGS
  364. debugf("*** Sending message to %u, flags = 0x%x, Priority = %d, "
  365. "Timeout = %d, length = %d, thread = 0x%x\n", precip->GetDPID(), dwFlags,
  366. dwPriority, dwTimeout, cbBuffer, GetCurrentThreadId());
  367. #endif
  368. #ifndef NO_MSG_CRC
  369. // add the crc to the end to minimize possible conflict of assumptions
  370. // We have to use our own buffer, so as to not overrun any input buffers ** :-( **
  371. char crcbuff[sizeof(m_rgbbuffOutPacket)];
  372. CopyMemory(crcbuff, pv, cb);
  373. int crc = MemoryCRC(crcbuff, cb);
  374. *(int*)(crcbuff + cb) = crc;
  375. pv = crcbuff;
  376. cb += sizeof(crc);
  377. #endif
  378. static CTempTimer tt("in dplay SendEx", 0.05f);
  379. tt.Start();
  380. hr = m_pDirectPlay->SendEx(m_pcnxnMe->GetID(), precip->GetID(), dwFlags, (void*) pv, cb,
  381. dwPriority, dwTimeout, precip, NULL);
  382. tt.Stop("dpidTo=%u, guaranteed=%d, hr=%x, 1st message (fmid)=%u", precip->GetID(), fGuaranteed, hr,
  383. ((FEDMESSAGE*)pv)->fmid);
  384. m_pfmSite->OnMessageSent(this, precip, pv, cb, fmg);
  385. assert(hr != E_INVALIDARG);
  386. #ifdef DEBUG
  387. if (!(hr == E_PENDING || SUCCEEDED(hr)))
  388. {
  389. debugf("Return code of SendEx was 0x%x to player with dpid %u.\n",
  390. hr, precip->GetID());
  391. }
  392. #endif // DEBUG
  393. return(hr);
  394. }
  395. /*-------------------------------------------------------------------------
  396. * SendToDefault
  397. *-------------------------------------------------------------------------
  398. Purpose:
  399. Send messages to default recipient
  400. Returns:
  401. Same as SendMessages
  402. */
  403. int FedMessaging::SendToDefault(FMFlush fmf)
  404. {
  405. return SendMessages(NULL, m_fmGuaranteedDefault, fmf);
  406. }
  407. /*-------------------------------------------------------------------------
  408. * SendMessages
  409. *-------------------------------------------------------------------------
  410. Purpose:
  411. Send queued-up messages
  412. Parameters:
  413. pvContext: This is passed back to us when dp tell us it couldn't
  414. deliver a message that we were tracking. FedSrv uses this as a pFSCon,
  415. and the client doesn't use it (yet).
  416. fflush: Whether you want to flush the queued message buffer
  417. Returns:
  418. bytes sent
  419. Side Effects:
  420. Important: The default recipient no longer remains the default if
  421. SendMessages is called with a non-NULL precip
  422. */
  423. int FedMessaging::SendMessages(CFMRecipient * precip, FMGuaranteed fmg, FMFlush fmf)
  424. {
  425. assert(m_fConnected);
  426. // if they don't supply a recip, then they better have previously supplied one
  427. assert (precip || m_precipDefault);
  428. if (precip)
  429. {
  430. m_precipDefault = NULL;
  431. }
  432. else
  433. {
  434. precip = m_precipDefault;
  435. fmg = m_fmGuaranteedDefault;
  436. }
  437. HRESULT hr;
  438. hr = S_OK;
  439. int cbToSend = CbUsedSpaceInOutbox();
  440. assert(cbToSend <= GetBuffOutSize());
  441. if (cbToSend > 0)
  442. {
  443. VerifyMessage(BuffOut(), cbToSend);
  444. hr = GenericSend(precip, BuffOut(), cbToSend, fmg);
  445. m_cMsgsOdometer++;
  446. m_cBytesOdometer += cbToSend;
  447. if (FM_FLUSH == fmf)
  448. m_pbFMNext = BuffOut();
  449. }
  450. return cbToSend;
  451. }
  452. /*-------------------------------------------------------------------------
  453. * CheckOdometer
  454. *-------------------------------------------------------------------------
  455. * Purpose:
  456. * Tells you:
  457. * How long it's been,
  458. * How many messages have been sent, and
  459. * How many bytes have been sent
  460. * since you've last called CheckOdometer. So obviously the first time you
  461. * call it you can ignore the return values
  462. *
  463. * Parameters: The return values
  464. *
  465. * Side Effects: Resets the odometer
  466. *
  467. * Returns: Current time
  468. */
  469. Time FedMessaging::CheckOdometer(float& flDTime, int& cMsgsOdometer, int& cBytesOdometer)
  470. {
  471. Time timeNow = Time::Now ();
  472. flDTime = timeNow - m_timeOdometerStart;
  473. cMsgsOdometer = m_cMsgsOdometer;
  474. cBytesOdometer = m_cBytesOdometer;
  475. m_timeOdometerStart = timeNow;
  476. m_cMsgsOdometer = 0;
  477. m_cBytesOdometer = 0;
  478. return timeNow;
  479. }
  480. /*-------------------------------------------------------------------------
  481. * HandleSysMsg
  482. *-------------------------------------------------------------------------
  483. * Purpose:
  484. * Handle system message
  485. *
  486. * Parameters:
  487. * System messages are those that dplay send us. They're handled here
  488. *
  489. * Side Effects:
  490. * Various
  491. */
  492. HRESULT FedMessaging::OnSysMessage(FedMessaging * pthis, LPDPMSG_GENERIC pMsg)
  493. {
  494. // The body of each case is there so you can set a breakpoint and examine
  495. // the contents of the message received.
  496. static CTempTimer timerSysMsg("spent in OnSysMessage()", .01f);
  497. timerSysMsg.Start();
  498. Time timeNow = Time::Now();
  499. m_pfmSite->OnSysMessage(this); // just for tracking
  500. switch (pMsg->dwType)
  501. {
  502. case DPSYS_CREATEPLAYERORGROUP:
  503. {
  504. static CTempTimer tt("handling DPSYS_CREATEPLAYERORGROUP", .01f);
  505. tt.Start();
  506. LPDPMSG_CREATEPLAYERORGROUP lp = (LPDPMSG_CREATEPLAYERORGROUP) pMsg;
  507. bool fPlayer = lp->dwPlayerType == DPPLAYERTYPE_PLAYER;
  508. debugf("DPSYS_CREATEPLAYERORGROUP for %s %s(%u)\n",
  509. fPlayer ? "player" : "group", lp->dpnName.lpszShortNameA, lp->dpId);
  510. // **CFMConnections** are created IN RESPONSE to getting DPSYS_CREATEPLAYERORGROUP.
  511. // But **CFMGroups** are created prior to, and result in, getting DPSYS_CREATEPLAYERORGROUP.
  512. if (fPlayer)
  513. {
  514. CFMConnection * pcnxn = CreateConnection(lp->dpnName.lpszShortNameA, lp->dpId);
  515. char szRemoteAddress[16];
  516. GetIPAddress(*pcnxn, szRemoteAddress);
  517. debugf(" ip=%s, which gives us %u players\n", szRemoteAddress, lp->dwCurrentPlayers);
  518. }
  519. else
  520. {
  521. assert(0 == lp->dpIdParent); // nesting groups is bad, so we don't support it
  522. }
  523. tt.Stop();
  524. }
  525. break;
  526. case DPSYS_DESTROYPLAYERORGROUP:
  527. {
  528. static CTempTimer tt("handling DPSYS_DESTROYPLAYERORGROUP", .01f);
  529. tt.Start();
  530. LPDPMSG_DESTROYPLAYERORGROUP lp = (LPDPMSG_DESTROYPLAYERORGROUP) pMsg;
  531. bool fPlayer = lp->dwPlayerType == DPPLAYERTYPE_PLAYER;
  532. debugf("DPSYS_DESTROYPLAYERORGROUP for %s %s(%u)\n",
  533. fPlayer ? "player" : "group", lp->dpnName.lpszShortNameA, lp->dpId);
  534. if (fPlayer)
  535. {
  536. CFMConnection * pcnxn = GetConnectionFromDpid(lp->dpId);
  537. // if your player dies, or the server dies, you're SOL--not much point in
  538. // waiting until dplay gets around to telling the session is dead,
  539. // since sometimes they don't anyway.
  540. if (pcnxn && (m_pcnxnMe == pcnxn || m_pcnxnServer == pcnxn))
  541. {
  542. m_pfmSite->OnSessionLost(this);
  543. Shutdown();
  544. }
  545. else if (pcnxn)
  546. DeleteConnection(*pcnxn);
  547. // else we've lost the above player, but they don't have an associated CFMConnection, probably because we manually nuked it
  548. }
  549. else
  550. {
  551. // we should've already nuked our CFMGroup
  552. // But there's a very small nonzero chance that dplay got around to nuking their group before we did
  553. //assert(!GetGroupFromDpid(lp->dpId));
  554. }
  555. tt.Stop();
  556. }
  557. break;
  558. case DPSYS_SENDCOMPLETE:
  559. {
  560. static CTempTimer tt("handling DPSYS_SENDCOMPLETE", .01f);
  561. tt.Start();
  562. LPDPMSG_SENDCOMPLETE lp = (LPDPMSG_SENDCOMPLETE) pMsg;
  563. bool fGroup = true;
  564. CFMRecipient * prcp = NULL;
  565. prcp = GetGroupFromDpid(lp->idTo);
  566. if (!prcp)
  567. {
  568. fGroup = false;
  569. CFMConnection * pcnxn = GetConnectionFromDpid(lp->idTo);
  570. if (pcnxn)
  571. {
  572. prcp = pcnxn;
  573. if (DP_OK == lp->hr)
  574. pcnxn->SetLastComplete(timeNow.clock());
  575. }
  576. }
  577. if (DP_OK != lp->hr)
  578. {
  579. debugf("DPSYS_SENDCOMPLETE: Message was not delivered to %s(%u), priority %d, "
  580. "hr=0x%x, SendTime=%d.\n",
  581. prcp ? prcp->GetName() : "<not found>", lp->idTo, lp->dwPriority, lp->hr, lp->dwSendTime);
  582. if (prcp)
  583. m_pfmSite->OnMessageNAK(this, lp->dwSendTime, prcp);
  584. }
  585. tt.Stop();
  586. }
  587. break;
  588. case DPSYS_ADDPLAYERTOGROUP:
  589. {
  590. LPDPMSG_ADDPLAYERTOGROUP lp = (LPDPMSG_ADDPLAYERTOGROUP) pMsg;
  591. CFMGroup * pgrp = GetGroupFromDpid(lp->dpIdGroup );
  592. CFMConnection * pcnxn = GetConnectionFromDpid(lp->dpIdPlayer);
  593. if (pgrp)
  594. pgrp->PlayerAdded(pcnxn);
  595. debugf("DPSYS_ADDPLAYERTOGROUP: player %s(%u) to group %s(%u)\n",
  596. pcnxn ? pcnxn->GetName() : "<gone>", lp->dpIdPlayer,
  597. pgrp ? pgrp->GetName() : "<gone>", lp->dpIdGroup);
  598. }
  599. break;
  600. case DPSYS_DELETEPLAYERFROMGROUP:
  601. {
  602. LPDPMSG_DELETEPLAYERFROMGROUP lp = (LPDPMSG_DELETEPLAYERFROMGROUP) pMsg;
  603. CFMGroup * pgrp = GetGroupFromDpid(lp->dpIdGroup );
  604. CFMConnection * pcnxn = GetConnectionFromDpid(lp->dpIdPlayer);
  605. if (pgrp)
  606. pgrp->PlayerDeleted(pcnxn);
  607. debugf("DPSYS_DELETEPLAYERFROMGROUP: player %s(%u) from group %s(%u)\n",
  608. pcnxn ? pcnxn->GetName() : "<gone>", lp->dpIdPlayer,
  609. pgrp ? pgrp->GetName() : "<gone>", lp->dpIdGroup);
  610. }
  611. break;
  612. case DPSYS_SESSIONLOST:
  613. {
  614. m_pfmSite->OnSessionLost(this);
  615. }
  616. break;
  617. default:
  618. {
  619. debugf("***** Unknown system message, type %lu *****\n", pMsg->dwType);
  620. }
  621. }
  622. timerSysMsg.Stop();
  623. return(S_OK);
  624. }
  625. /*-------------------------------------------------------------------------
  626. * ReceiveMessages
  627. *-------------------------------------------------------------------------
  628. * Purpose:
  629. * Handles all inbound messages and dispatches them appropriately
  630. */
  631. HRESULT FedMessaging::ReceiveMessages()
  632. {
  633. HRESULT hr = S_OK;
  634. DWORD dwMsgBufferSize = 0;
  635. DPID dpidFrom, dpidTo;
  636. assert(!g_fHandlingMsg);
  637. g_fHandlingMsg = true;
  638. // loop to read all messages in queue
  639. while (SUCCEEDED(hr) && IsConnected())
  640. {
  641. // read messages from any player, including system player
  642. dpidFrom = 0;
  643. dpidTo = 0;
  644. m_dwcbPacket = sizeof(m_rgbbuffInPacket); // NOT PacketSize()!
  645. g_pbReceiveBuff = m_rgbbuffInPacket;
  646. static CTempTimer ttReceive("spent in m_pDirectPlay->Receive()", .01f);
  647. ttReceive.Start();
  648. hr = m_pDirectPlay->Receive(&dpidFrom, &dpidTo, DPRECEIVE_ALL,
  649. m_rgbbuffInPacket, &m_dwcbPacket);
  650. ttReceive.Stop();
  651. g_dpidFrom = dpidFrom;
  652. g_dwMsgPacketSize = m_dwcbPacket;
  653. assert(IMPLIES(FAILED(hr), DPERR_NOMESSAGES == hr)); // this is the only error return code we expect
  654. if ((SUCCEEDED(hr)) && // successfully read a message
  655. m_dwcbPacket >= sizeof(DPMSG_GENERIC)) // it is big enough
  656. {
  657. /*
  658. {
  659. static Time timeStart;
  660. static DWORD cbTotal;
  661. static DWORD cMessagesTotal;
  662. static bool bRunning = false;
  663. Time now = Time::Now();
  664. if (bRunning)
  665. {
  666. float dt = now - timeStart;
  667. if (dt >= 5.0f)
  668. {
  669. debugf("Messages @ %d = %f bytes/second; %f total bytes/second; %f packets/second\n",
  670. now.clock(), float(cbTotal) / dt, float(cbTotal + cMessagesTotal * 55) / dt, float(cMessagesTotal) / dt);
  671. timeStart = now;
  672. cbTotal = PacketSize();
  673. cMessagesTotal = 1;
  674. }
  675. else
  676. {
  677. cbTotal += PacketSize();
  678. cMessagesTotal++;
  679. }
  680. }
  681. else
  682. {
  683. timeStart = now;
  684. cbTotal = 0;
  685. cMessagesTotal = 0;
  686. bRunning = true;
  687. }
  688. }
  689. */
  690. // check for system message
  691. if (dpidFrom == DPID_SYSMSG)
  692. OnSysMessage(this, (LPDPMSG_GENERIC) m_rgbbuffInPacket);
  693. else
  694. {
  695. CFMConnection * pcnxnFrom = NULL;
  696. DWORD dwSize = sizeof(CFMConnection *);
  697. hr = m_pDirectPlay->GetPlayerData(dpidFrom, &pcnxnFrom, &dwSize, DPGET_LOCAL);
  698. // debugf("Called GetPlayerData: hr=0x%08x, pcnxnFrom=0x%08x, dwSize=%u\n", hr, pcnxnFrom, dwSize);
  699. g_pcnxnFrom = pcnxnFrom;
  700. if (pcnxnFrom && SUCCEEDED(hr)) // if we can't resolve the player, then it's into the bit bucket with this message
  701. {
  702. FEDMESSAGE * pfm = (FEDMESSAGE *) m_rgbbuffInPacket;
  703. int cMsgs = 0;
  704. #ifndef NO_MSG_CRC
  705. int crc = MemoryCRC(m_rgbbuffInPacket, PacketSize());
  706. if (crc == *(int*)(m_rgbbuffInPacket + PacketSize()))
  707. {
  708. #endif
  709. static CTempTimer tt("spent looping through message queue", .1f);
  710. tt.Start();
  711. g_fConnectionDeleted = false;
  712. while (pfm && !g_fConnectionDeleted)
  713. {
  714. if(pfm->fmid > 0 && pfm->cbmsg >= sizeof(FEDMESSAGE) && pfm->cbmsg <= PacketSize())
  715. {
  716. m_pfmSite->OnAppMessage(this, *pcnxnFrom, pfm);
  717. pfm = PfmGetNext(pfm);
  718. cMsgs++;
  719. }
  720. else
  721. {
  722. // ACK! Hacker?
  723. whodidit();
  724. #ifndef NO_MSG_CRC
  725. // we'll just call it a bad crc, even though it's not quite the same thing
  726. m_pfmSite->OnBadCRC(this, *pcnxnFrom, m_rgbbuffInPacket, m_dwcbPacket);
  727. #endif
  728. break;
  729. }
  730. }
  731. tt.Stop();
  732. #ifndef NO_MSG_CRC
  733. }
  734. else
  735. {
  736. // ACK! Failed CRC. WTF??? ...<sigh> but it happens
  737. m_pfmSite->OnBadCRC(this, *pcnxnFrom, m_rgbbuffInPacket, m_dwcbPacket);
  738. }
  739. #endif
  740. }
  741. else
  742. {
  743. debugf("Warning: Ignoring message from dpid %u who doesn't have an associated CFMConnection\n", dpidFrom);
  744. assert(DPERR_INVALIDPLAYER == hr);
  745. }
  746. m_timeMsgLast = Time::Now();
  747. }
  748. }
  749. else
  750. assert (DPERR_NOMESSAGES == hr || !IsConnected());
  751. }
  752. g_dpidFrom = 0;
  753. g_pcnxnFrom = NULL;
  754. g_fHandlingMsg = false;
  755. return (DP_OK);
  756. }
  757. /*-------------------------------------------------------------------------
  758. * ConnectToDPAddress
  759. *-------------------------------------------------------------------------
  760. * Purpose:
  761. * Connect to a machine, given a dplay address
  762. */
  763. HRESULT FedMessaging::ConnectToDPAddress(LPVOID pvAddress)
  764. {
  765. HRESULT hr = m_pDirectPlay->InitializeConnection(pvAddress, 0);
  766. if (FAILED(hr))
  767. {
  768. if (DPERR_ALREADYINITIALIZED == hr)
  769. hr = S_OK;
  770. else
  771. m_pfmSite->OnMessageBox(this, "Failed to connect to server. Please check network connection.",
  772. "Allegiance", MB_OK);
  773. }
  774. return hr;
  775. }
  776. /*-------------------------------------------------------------------------
  777. * Connect
  778. *-------------------------------------------------------------------------
  779. * Purpose:
  780. * Connect to a host machine. Could be self.
  781. *
  782. * Parameters:
  783. * string address of host
  784. */
  785. HRESULT FedMessaging::Connect(const char * szAddress)
  786. {
  787. assert(m_pDirectPlay);
  788. assert(!m_fConnected);
  789. void * pvAddress = NULL;
  790. DWORD dwAddressSize;
  791. HRESULT hr = E_FAIL;
  792. // Find out how big the address buffer needs to be--intentional fail 1st time
  793. hr = m_pDirectPlayLobby->CreateAddress(DPSPGUID_TCPIP, DPAID_INet, szAddress,
  794. lstrlen(szAddress) + 1, pvAddress, &dwAddressSize);
  795. assert(DPERR_BUFFERTOOSMALL == hr);
  796. pvAddress = new char[dwAddressSize];
  797. ZSucceeded(m_pDirectPlayLobby->CreateAddress(DPSPGUID_TCPIP, DPAID_INet, szAddress,
  798. lstrlen(szAddress) + 1, pvAddress, &dwAddressSize));
  799. hr = ConnectToDPAddress(pvAddress);
  800. delete [] pvAddress;
  801. return hr;
  802. }
  803. /*-------------------------------------------------------------------------
  804. * KillSvr
  805. *-------------------------------------------------------------------------
  806. Purpose:
  807. Kill dplaysvr
  808. Returns:
  809. Whether it was sucessfully killed (or if it wasn't running in the first place)
  810. Side Effects:
  811. Any running dplay server apps will be killed. This should never be
  812. true if any other dplay server app is running
  813. */
  814. bool FedMessaging::KillSvr()
  815. {
  816. PROCESS_INFORMATION pi;
  817. STARTUPINFO si;
  818. ZeroMemory(&si, sizeof(si));
  819. si.cb = sizeof(si);
  820. Shutdown(); // can't be doing this with an open session
  821. BOOL fCreated = CreateProcess(NULL, "KillSvr.exe", NULL, NULL, FALSE,
  822. CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
  823. if (fCreated)
  824. {
  825. // Now we need to make sure it finishes before we go start using dplaysvr again
  826. // WARNING: We could hang if killsvr never comes back. Maybe just abort if it doesn't come back in a few seconds?
  827. DWORD exitcode = STILL_ACTIVE;
  828. int i = 0;
  829. const int cRetries = 50; // let's give it 50 retries
  830. while (STILL_ACTIVE == exitcode && i > cRetries)
  831. {
  832. GetExitCodeProcess(pi.hProcess, &exitcode);
  833. Sleep(100);
  834. i++;
  835. }
  836. if (i > cRetries)
  837. fCreated = false;
  838. else
  839. debugf("Slept (100ms) %d times waiting for killsvr\n", i);
  840. Sleep(1000); // wait another second to make sure it's really gone
  841. }
  842. else
  843. {
  844. LPVOID lpMsgBuf;
  845. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  846. NULL,
  847. GetLastError(),
  848. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  849. (LPTSTR) &lpMsgBuf,
  850. 0,
  851. NULL
  852. );
  853. m_pfmSite->OnMessageBox(this, (LPCTSTR)lpMsgBuf, "Couldn't run killsvr.exe.", MB_OK | MB_ICONINFORMATION);
  854. LocalFree( lpMsgBuf );
  855. }
  856. return !!fCreated;
  857. }
  858. /*-------------------------------------------------------------------------
  859. * InitDPlay
  860. *-------------------------------------------------------------------------
  861. * Purpose:
  862. * Must be called before you can do anything with dplay
  863. *
  864. * Side Effects:
  865. * Creates a dplay object m_pDirectPlay, that must later be closed,
  866. * released, and set to NULL.
  867. */
  868. HRESULT FedMessaging::InitDPlay(
  869. #ifdef MONOLITHIC_DPLAY
  870. bool fMonolithic
  871. #endif
  872. )
  873. {
  874. HRESULT hr = E_FAIL;
  875. assert(!m_fConnected);
  876. assert(!m_pDirectPlay);
  877. // Create an IDirectPlay interface
  878. m_pfmSite->OnPreCreate(this);
  879. IDirectPlayX* pdp = NULL;
  880. hr = CoCreateInstance(
  881. #ifdef MONOLITHIC_DPLAY
  882. fMonolithic ? MSRG_CLSID_DPLAY :
  883. #endif
  884. CLSID_DirectPlay, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlayX, (void**)&pdp);
  885. m_pfmSite->OnPostCreate(this, pdp, &m_pDirectPlay);
  886. if (FAILED(hr))
  887. {
  888. assert(!pdp);
  889. m_pfmSite->OnMessageBox(this, "Failed to initialze DirectPlay in DirectX 6. Check installation of DirectX, and reinstall if necessary.",
  890. "Allegiance", MB_OK);
  891. return hr;
  892. }
  893. if (!CheckVersion())
  894. {
  895. Shutdown();
  896. return E_FAIL;
  897. }
  898. IDirectPlayLobby * pDirectPlayLobby;
  899. ZSucceeded(DirectPlayLobbyCreate(NULL, &pDirectPlayLobby, NULL, NULL, 0));
  900. ZSucceeded(pDirectPlayLobby->QueryInterface(IID_IDirectPlayLobbyX, (LPVOID*) &m_pDirectPlayLobby));
  901. pDirectPlayLobby->Release();
  902. pDirectPlayLobby = NULL;
  903. return S_OK;
  904. }
  905. /*-------------------------------------------------------------------------
  906. * HostSession
  907. *-------------------------------------------------------------------------
  908. * Purpose:
  909. * Start a session and be the host (server)
  910. *
  911. * Parameters:
  912. * guidApplication: Clients will only find this session if they ask for the same application
  913. * fKeepAlive: Should dplay detect timeouts and kill players on dead connections?
  914. * hEventServer: Event to be signaled when a message comes to the server
  915. * fKillSvr: Whether to kill the dplaysvr process.
  916. *
  917. * Returns:
  918. *
  919. *
  920. * Side Effects:
  921. *
  922. */
  923. HRESULT FedMessaging::HostSession(GUID guidApplication, bool fKeepAlive, HANDLE hEventServer, bool fProtocol
  924. #ifdef MONOLITHIC_DPLAY
  925. , bool fMonolithic
  926. #endif
  927. )
  928. {
  929. HRESULT hr = E_FAIL;
  930. void * pvAddress = NULL; // dplay address of self
  931. // DPSECURITYDESC securityDesc;
  932. DPSESSIONDESC2 sessionDesc;
  933. bool fSecure = false;
  934. DPSECURITYDESC * pSecurityDesc = NULL; // assume no security descriptor will be needed;
  935. char szHostName[20]; // length of machine name--also long enough for ip address if that's what we get
  936. WSADATA WSAData;
  937. assert(!m_fConnected);
  938. if (false) // TODO: How do we know when to make sure dplaysvr isn't hosed?
  939. {
  940. if (!KillSvr())
  941. {
  942. m_pfmSite->OnMessageBox(this, "Failed to successfully run killsvr.exe (or it failed to complete), which kills dplaysvr.exe. If we can't veryify that dplaysvr starts out clean, we're in for trouble, so I'm aborting.",
  943. "Allegiance", MB_OK);
  944. return E_FAIL;
  945. }
  946. }
  947. if(!m_pDirectPlay)
  948. {
  949. hr = InitDPlay(
  950. #ifdef MONOLITHIC_DPLAY
  951. fMonolithic
  952. #endif
  953. );
  954. if FAILED(hr)
  955. goto CLEANUP;
  956. }
  957. hr = E_FAIL;
  958. WSAStartup(MAKEWORD(1,1), &WSAData);
  959. gethostname(szHostName, sizeof(szHostName));
  960. WSACleanup();
  961. hr = Connect(szHostName);
  962. if (FAILED(hr))
  963. goto CLEANUP;
  964. // Start session
  965. ZeroMemory(&sessionDesc, sizeof(DPSESSIONDESC2));
  966. sessionDesc.dwSize = sizeof(DPSESSIONDESC2);
  967. sessionDesc.guidApplication = guidApplication;
  968. sessionDesc.dwMaxPlayers = 0; // no limit
  969. sessionDesc.lpszSessionNameA = "FedSrv";
  970. #if !defined(DPSESSION_NOSESSSIONDESCMESSAGES)
  971. #define DPSESSION_NOSESSSIONDESCMESSAGES 0x00020000 // only available in dx8 and private Allegiance dplay bits
  972. #endif
  973. sessionDesc.dwFlags = DPSESSION_CLIENTSERVER |
  974. DPSESSION_OPTIMIZELATENCY |
  975. (fProtocol ? DPSESSION_DIRECTPLAYPROTOCOL : 0) |
  976. DPSESSION_NODATAMESSAGES |
  977. #ifdef MONOLITHIC_DPLAY
  978. (fMonolithic ? DPSESSION_NOSESSSIONDESCMESSAGES : 0) |
  979. #else
  980. DPSESSION_NOSESSSIONDESCMESSAGES |
  981. #endif
  982. (fKeepAlive ? DPSESSION_KEEPALIVE : 0) |
  983. (fSecure ? DPSESSION_SECURESERVER : 0);
  984. m_guidApplication = guidApplication;
  985. /*
  986. if (fSecure)
  987. {
  988. ZeroMemory(&securityDesc, sizeof(DPSECURITYDESC));
  989. securityDesc.dwSize = sizeof(DPSECURITYDESC);
  990. securityDesc.dwFlags = 0;
  991. securityDesc.lpszSSPIProviderA = "something";
  992. pSecurityDesc = &securityDesc;
  993. }
  994. */
  995. // host the session
  996. hr = m_pDirectPlay->Open(&sessionDesc, DPOPEN_CREATE);
  997. // Now, since we need to use a flag that was added as a bug fix, the version of dplay we run on may or may not have this flag.
  998. // The only way to know is to try using it, and if it fails with DPERR_INVALIDFLAGS, then we need to try again without it.
  999. if (DPERR_INVALIDFLAGS == hr)
  1000. {
  1001. sessionDesc.dwFlags &= ~DPSESSION_NOSESSSIONDESCMESSAGES;
  1002. hr = m_pDirectPlay->Open(&sessionDesc, DPOPEN_CREATE);
  1003. }
  1004. ZSucceeded(hr);
  1005. m_fConnected = true;
  1006. // Now let's get the sessiondesc back to look at it
  1007. {
  1008. DPSESSIONDESC2 * psd = NULL;
  1009. DWORD dwSize = 0;
  1010. hr = m_pDirectPlay->GetSessionDesc(psd, &dwSize);
  1011. assert (DPERR_BUFFERTOOSMALL == hr);
  1012. psd = (DPSESSIONDESC2 *) new char[dwSize];
  1013. hr = m_pDirectPlay->GetSessionDesc(psd, &dwSize);
  1014. m_guidInstance = psd->guidInstance;
  1015. delete [] psd;
  1016. }
  1017. // create a server "player"
  1018. DPID dpid;
  1019. ZSucceeded(m_pDirectPlay->CreatePlayer(&dpid, NULL, hEventServer,
  1020. NULL, 0, DPPLAYER_SERVERPLAYER));
  1021. assert(dpid == DPID_SERVERPLAYER);
  1022. m_pcnxnMe = new CFMConnection(this, "<Server connection>", dpid);
  1023. // then create the Everyone group
  1024. ZVerify(m_pgrpEveryone = CreateGroup("Everyone"));
  1025. ResetOutBuffer();
  1026. CLEANUP:
  1027. if (FAILED(hr))
  1028. Shutdown();
  1029. return hr;
  1030. }
  1031. /*-------------------------------------------------------------------------
  1032. * Shutdown
  1033. *-------------------------------------------------------------------------
  1034. * Purpose:
  1035. * Shutdown an open session. It is assumed that logoff, etc. has already been done
  1036. */
  1037. void FedMessaging::Shutdown()
  1038. {
  1039. static CTempTimer tt("In FedMessaging::Shutdown", 0.1f);
  1040. tt.Start();
  1041. if (m_pDirectPlay)
  1042. {
  1043. // empty the msg queue, and wait to make sure sys msgs get flushed
  1044. m_pDirectPlay->CancelMessage(0, 0);
  1045. DWORD dwQueueLen = 0;
  1046. do
  1047. {
  1048. Sleep(100); // don't ask
  1049. GetSendQueue(&dwQueueLen, NULL);
  1050. } while (dwQueueLen > 0);
  1051. if (m_pcnxnMe)
  1052. {
  1053. Sleep(100); // don't ask
  1054. m_pDirectPlay->DestroyPlayer(m_pcnxnMe->GetDPID());
  1055. delete m_pcnxnMe;
  1056. m_pcnxnMe = NULL;
  1057. }
  1058. Sleep(1); // don't ask
  1059. m_pDirectPlay->Close();
  1060. m_pDirectPlay->Release();
  1061. m_pDirectPlay = NULL;
  1062. }
  1063. if (m_pcnxnServer)
  1064. {
  1065. delete m_pcnxnServer;
  1066. m_pcnxnServer = NULL;
  1067. }
  1068. ResetOutBuffer();
  1069. m_guidInstance = GUID_NULL;
  1070. m_fConnected = false;
  1071. tt.Stop();
  1072. }
  1073. /*-------------------------------------------------------------------------
  1074. * JoinSession
  1075. *-------------------------------------------------------------------------
  1076. * Purpose:
  1077. * Become a client in a session
  1078. *
  1079. * Parameters:
  1080. * what server, what application, and what instance to join (or NULL for default)
  1081. * OUT: connection to the sevrer that all messages should be sent to
  1082. */
  1083. HRESULT FedMessaging::JoinSession(GUID guidApplication, const char * szServer, const char * szCharName)
  1084. {
  1085. HRESULT hr = E_FAIL;
  1086. Time timeStart = Time::Now();
  1087. m_fSessionCallback = false; // we're going to join the session right here, so we don't want any site methods being called
  1088. hr = EnumSessionsInternal(guidApplication, szServer);
  1089. do
  1090. {
  1091. Sleep(500);
  1092. hr = EnumSessionsInternal(guidApplication, NULL);
  1093. if (DPERR_CONNECTING != hr && FAILED(hr))
  1094. goto Cleanup;
  1095. // Important implementation detail!!! dplay times out enum requests at 15 seconds, and bad things have been known to happen
  1096. // if you try and kill a request, so to avoid that possibility, let's make SURE we wait longer than that.
  1097. } while ((SUCCEEDED(hr) || DPERR_CONNECTING == hr) &&
  1098. IsEqualGUID(GUID_NULL, m_guidInstance) &&
  1099. (Time::Now().clock() - timeStart.clock() < 17000)); // don't try for more than 17 seconds
  1100. if (IsEqualGUID(GUID_NULL, m_guidInstance))
  1101. hr = E_FAIL; // something else?
  1102. else
  1103. hr = JoinSessionInstance(m_guidInstance, szCharName);
  1104. ResetOutBuffer();
  1105. Cleanup:
  1106. if (FAILED(hr))
  1107. Shutdown();
  1108. return hr;
  1109. }
  1110. /*-------------------------------------------------------------------------
  1111. * FedMessaging.JoinSessionInstance
  1112. *-------------------------------------------------------------------------
  1113. Purpose:
  1114. Join a session that has already been enum'd
  1115. Parameters:
  1116. session instance and name of player to create
  1117. Side Effects:
  1118. joins the session, creates the named player
  1119. */
  1120. HRESULT FedMessaging::JoinSessionInstance(GUID guidInstance, const char * szName)
  1121. {
  1122. DPSESSIONDESC2 sessionDesc;
  1123. static CTempTimer tt("in FedMessaging::JoinSessionInstance", 0.5f);
  1124. tt.Start();
  1125. ZeroMemory(&sessionDesc, sizeof(sessionDesc));
  1126. sessionDesc.dwSize = sizeof(sessionDesc);
  1127. sessionDesc.guidInstance = guidInstance;
  1128. assert(m_pDirectPlay);
  1129. // But just to be paranoid, since people will be using retail clients before this can get lots of testing...
  1130. if (!m_pDirectPlay)
  1131. return E_FAIL;
  1132. HRESULT hr = m_pDirectPlay->Open(&sessionDesc, DPOPEN_JOIN);
  1133. if (FAILED(hr))
  1134. goto CLEANUP;
  1135. m_guidInstance = sessionDesc.guidInstance;
  1136. m_fConnected = true;
  1137. DPNAME dpName;
  1138. dpName.dwSize = sizeof(DPNAME);
  1139. dpName.dwFlags = 0;
  1140. dpName.lpszLongNameA =
  1141. dpName.lpszShortNameA = const_cast<char*>(szName);
  1142. DPID dpid;
  1143. ZSucceeded(hr = m_pDirectPlay->CreatePlayer(&dpid, &dpName, NULL, NULL, 0, 0));
  1144. m_pcnxnMe = new CFMConnection(this, szName, dpid);
  1145. // Set the output connection that the client will send msgs to
  1146. m_pcnxnServer = new CFMConnection(this, "Server", DPID_SERVERPLAYER);
  1147. CLEANUP:
  1148. tt.Stop();
  1149. return hr;
  1150. }
  1151. /*-------------------------------------------------------------------------
  1152. * CheckVersion
  1153. *-------------------------------------------------------------------------
  1154. Purpose:
  1155. Verify that the version of dplay on the system is the one expected
  1156. by other machines in the session
  1157. There are a variety of acceptable versions of dplay. You pass the test if:
  1158. you're using our custom branded dplay (1.2.3.12), or
  1159. you're using the released dx6.1a on win9x, or
  1160. you're using some minimum build of dx7 on win9x, or
  1161. you're using some minimum build of w2k (save caveat as above)
  1162. Returns:
  1163. Whether version is correct
  1164. Side Effects:
  1165. Fire message event if version is wrong
  1166. */
  1167. bool FedMessaging::CheckVersion()
  1168. {
  1169. HANDLE hMem = NULL;
  1170. LPVOID lpvMem = NULL;
  1171. char szFullPath[256];
  1172. DWORD dwVerHnd;
  1173. DWORD dwVerInfoSize;
  1174. bool fRet = false;
  1175. UINT dwBytes = 0;
  1176. VS_FIXEDFILEINFO *lpvsFixedFileInfo = NULL;
  1177. // Get version information from the application
  1178. HMODULE hmodDPlay = GetModuleHandle("dplayx");
  1179. if (NULL == hmodDPlay) // why isn't the dll loaded???
  1180. goto Cleanup;
  1181. GetModuleFileName(hmodDPlay, szFullPath, sizeof(szFullPath));
  1182. if (0 == (dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd)))
  1183. goto Cleanup;
  1184. if (NULL == (hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize)) ||
  1185. NULL == (lpvMem = GlobalLock(hMem)))
  1186. goto Cleanup;
  1187. GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpvMem);
  1188. if (!VerQueryValue(lpvMem, "\\", (LPVOID*) &lpvsFixedFileInfo, &dwBytes))
  1189. goto Cleanup;
  1190. {
  1191. WORD ver1 = HIWORD(lpvsFixedFileInfo->dwFileVersionMS);
  1192. WORD ver2 = LOWORD(lpvsFixedFileInfo->dwFileVersionMS);
  1193. WORD ver3 = HIWORD(lpvsFixedFileInfo->dwFileVersionLS);
  1194. WORD ver4 = LOWORD(lpvsFixedFileInfo->dwFileVersionLS);
  1195. char msg[256];
  1196. sprintf(msg, "DPlay Version found: %d.%d.%d.%d. Version Required: 4.7.0.177 (DX7) or 5.0.2046.1 (Win2k) or 4.6.3.516 (DX6.1a)\r\n",
  1197. ver1, ver2, ver3, ver4);
  1198. debugf(msg);
  1199. if (ver1 > 5) // assume all major new versions work
  1200. fRet = true;
  1201. if (ver1 == 5) // NT -- WARNING -- if future versions of DX go outside 4.??? and spill into NT versions, we're in trouble
  1202. {
  1203. fRet = ver2 > 0 || ver3 >= 2113; // ver3 == NT build #
  1204. }
  1205. if (ver1 == 4) // Stand-alone DX runtimes
  1206. {
  1207. if (ver2 > 7) // new major dx versions (DX8+)
  1208. fRet = true;
  1209. if (ver2 == 7) // DX7.0a
  1210. fRet = ver3 > 0 || ver4 >= 700;
  1211. if (ver2 == 6) // DX6.1a
  1212. fRet = (ver3 == 3 && ver4 >= 516) || ver3 > 3;
  1213. // Anything before 6.1a is NOT sufficient.
  1214. }
  1215. if (!fRet)
  1216. {
  1217. m_pfmSite->OnMessageBox(this, msg, "Allegiance", MB_OK);
  1218. goto Cleanup;
  1219. }
  1220. }
  1221. Cleanup:
  1222. if (lpvMem)
  1223. GlobalUnlock(hMem);
  1224. if (hMem)
  1225. GlobalFree(hMem);
  1226. return fRet;
  1227. }
  1228. CFMConnection * FedMessaging::GetConnectionFromDpid(DPID dpid)
  1229. {
  1230. DWORD dwSize = sizeof(CFMConnection);
  1231. CFMConnection * pcnxn = NULL;
  1232. static CTempTimer tt("in GetConnectionFromDpid", .01f);
  1233. tt.Start();
  1234. HRESULT hr = GetDPlay()->GetPlayerData(dpid, &pcnxn, &dwSize, DPGET_LOCAL);
  1235. if (hr == DPERR_INVALIDPLAYER) // dplay killed our player already! :-( See if we can find the dpid in our list
  1236. {
  1237. assert(!pcnxn);
  1238. ListConnections::Iterator iter(m_listCnxns);
  1239. for (; !pcnxn && !iter.End(); iter.Next())
  1240. {
  1241. if (iter.Value()->GetDPID() == dpid)
  1242. {
  1243. pcnxn = iter.Value();
  1244. break;
  1245. }
  1246. }
  1247. if (m_pcnxnMe && (m_pcnxnMe->GetDPID() == dpid))
  1248. pcnxn = m_pcnxnMe;
  1249. else if (m_pcnxnServer && (m_pcnxnServer->GetDPID() == dpid))
  1250. pcnxn = m_pcnxnServer;
  1251. }
  1252. tt.Stop();
  1253. return pcnxn;
  1254. }
  1255. CFMGroup * FedMessaging::GetGroupFromDpid(DPID dpid)
  1256. {
  1257. DWORD dwSize = sizeof(CFMGroup);
  1258. CFMGroup * pgrp = NULL;
  1259. static CTempTimer tt("in GetGroupFromDpid", .01f);
  1260. tt.Start();
  1261. GetDPlay()->GetGroupData(dpid, &pgrp, &dwSize, DPGET_LOCAL);
  1262. tt.Stop();
  1263. return pgrp;
  1264. }
  1265. HRESULT FedMessaging::GetIPAddress(CFMConnection & cnxn, char szRemoteAddress[16])
  1266. {
  1267. char rgcDPAddress[300];
  1268. DWORD cbDPAddress = sizeof(rgcDPAddress);
  1269. static CTempTimer tt("in GetIPAddress", .01f);
  1270. tt.Start();
  1271. HRESULT hr = GetDPlay()->GetPlayerAddress(cnxn.GetDPID(), rgcDPAddress, &cbDPAddress);
  1272. if (SUCCEEDED(hr))
  1273. hr = GetDPlayLobby()->EnumAddress(EnumAddressCallback, rgcDPAddress, cbDPAddress, szRemoteAddress);
  1274. tt.Stop();
  1275. return hr;
  1276. }
  1277. HRESULT FedMessaging::EnumSessions(GUID guidApplication, const char * szServer)
  1278. {
  1279. m_fSessionCallback = true;
  1280. return EnumSessionsInternal(guidApplication, szServer);
  1281. }
  1282. HRESULT FedMessaging::EnumSessionsInternal(GUID guidApplication, const char * szServer)
  1283. {
  1284. HRESULT hr = E_FAIL;
  1285. DWORD dwSize = 0;
  1286. Time timeStart = Time::Now();
  1287. static CTempTimer tt("in FedMessaging::EnumSessionsInternal", 1.0f);
  1288. tt.Start();
  1289. // restore the hourglass cursor
  1290. SetCursor(LoadCursor(NULL, IDC_WAIT));
  1291. if (szServer)
  1292. Shutdown();
  1293. if(!m_pDirectPlay)
  1294. {
  1295. hr = InitDPlay(
  1296. #ifdef MONOLITHIC_DPLAY
  1297. false
  1298. #endif
  1299. );
  1300. if FAILED(hr)
  1301. goto CLEANUP;
  1302. }
  1303. if (szServer)
  1304. {
  1305. hr = Connect(szServer);
  1306. if FAILED(hr)
  1307. goto CLEANUP;
  1308. }
  1309. // Find the session.
  1310. DPSESSIONDESC2 sessionDesc;
  1311. ZeroMemory(&sessionDesc, sizeof(sessionDesc));
  1312. sessionDesc.dwSize = sizeof(sessionDesc);
  1313. g_guidApplication = guidApplication;
  1314. sessionDesc.guidApplication = guidApplication; //GUID_NULL;
  1315. debugf("Looking for session on \"%s\"\n", szServer);
  1316. if (szServer)
  1317. hr = m_pDirectPlay->EnumSessions(&sessionDesc, 20000, EnumSessionsCallback, // resend every 3 seconds if we don't hear back
  1318. this, DPENUMSESSIONS_STOPASYNC);
  1319. hr = m_pDirectPlay->EnumSessions(&sessionDesc, 20000, EnumSessionsCallback, // resend every 3 seconds if we don't hear back
  1320. this, DPENUMSESSIONS_ASYNC);
  1321. CLEANUP:
  1322. tt.Stop(); // the shutdown below has its own timer
  1323. if (DPERR_CONNECTING != hr && FAILED(hr))
  1324. Shutdown();
  1325. // else we need to keep the connection/sessions around for more enum'ing and joining later
  1326. return hr;
  1327. }
  1328. CFMConnection * FedMessaging::CreateConnection(const char * szName, DPID dpid) // after the physical connection has already been established
  1329. {
  1330. static CTempTimer tt("in CreateConnection", .01f);
  1331. tt.Start();
  1332. CFMConnection * pcnxn = new CFMConnection(this, szName, dpid);
  1333. m_listCnxns.PushFront(pcnxn);
  1334. m_pgrpEveryone->AddConnection(this, pcnxn); // we'll let dplay own removing them when they disconnect, since they retain lifetime membership
  1335. m_pfmSite->OnNewConnection(this, *pcnxn);
  1336. tt.Stop();
  1337. return pcnxn;
  1338. }
  1339. CFMConnection::CFMConnection(FedMessaging * pfm, const char * szName, DPID dpid) : // only FedMessaging::CreateConnection can create these things
  1340. CFMRecipient(szName, dpid),
  1341. m_cAbsentCount(0),
  1342. m_dwPrivate(0)
  1343. {
  1344. assert(GetDPID() == dpid);
  1345. debugf("New CFMConnection: dpid=%u, name=%s, playerdata(this)=%p, private=%u\n",
  1346. dpid, szName, this, m_dwPrivate);
  1347. LPVOID pv = this;
  1348. if (FAILED((pfm->GetDPlay()->SetPlayerData(GetDPID(), &pv, sizeof(pv), DPSET_LOCAL))))
  1349. debugf("Player %s(%u) gone immediately after connection. He should drop out rsn.\n", szName, dpid);
  1350. }
  1351. void CFMConnection::Delete(FedMessaging * pfm) // basically the destructor, but with a parameter
  1352. {
  1353. debugf("Deleting connection: dpid=%u, name=%s, playerdata=%p, private=%u\n",
  1354. GetDPID(), GetName(), this, m_dwPrivate);
  1355. // Don't want to hear anything more from this player
  1356. static CTempTimer tt("in DestroyPlayer/SetPlayerData", 0.02f);
  1357. tt.Start();
  1358. HRESULT hr = pfm->GetDPlay()->SetPlayerData(GetDPID(), NULL, 0, DPSET_LOCAL); // just in case dplay hasn't really killed the player yet. ignore return code; they may already be dead
  1359. debugf("SetPlayerData=0x%08x\n", hr);
  1360. hr = pfm->GetDPlay()->DestroyPlayer(GetDPID());
  1361. debugf("DestroyPlayer=0x%08x\n", hr);
  1362. tt.Stop();
  1363. delete this;
  1364. g_fConnectionDeleted = true;
  1365. }
  1366. // ************** CFMGroup **************
  1367. void CFMGroup::AddConnection(FedMessaging * pfm, CFMConnection * pcnxn)
  1368. {
  1369. pfm->GetDPlay()->AddPlayerToGroup(GetDPID(), pcnxn->GetDPID());
  1370. }
  1371. void CFMGroup::DeleteConnection(FedMessaging * pfm, CFMConnection * pcnxn)
  1372. {
  1373. // ignore return code, since dplay connection could already be dead
  1374. pfm->GetDPlay()->DeletePlayerFromGroup(GetDPID(), pcnxn->GetDPID());
  1375. }
  1376. CFMGroup::CFMGroup(FedMessaging * pfm, const char * szName) :
  1377. m_cPlayers(0),
  1378. CFMRecipient(szName, 0) // dplay group hasn't been created yet
  1379. {
  1380. DPNAME dpn;
  1381. dpn.dwSize = sizeof(dpn);
  1382. dpn.dwFlags = 0;
  1383. dpn.lpszShortNameA = const_cast<char*>(szName);
  1384. dpn.lpszLongNameA = "";
  1385. ZSucceeded(pfm->GetDPlay()->CreateGroup(&m_dpid, &dpn, NULL, 0, 0));
  1386. LPVOID pv = this;
  1387. ZSucceeded(pfm->GetDPlay()->SetGroupData(GetDPID(), &pv, sizeof(pv), DPSET_LOCAL));
  1388. }
  1389. void CFMGroup::Delete (FedMessaging * pfm)
  1390. {
  1391. pfm->GetDPlay()->DestroyGroup(GetDPID());
  1392. delete this;
  1393. }
  1394. void CFMGroup::PlayerAdded(CFMConnection * pcnxn)
  1395. {
  1396. m_cPlayers++;
  1397. }
  1398. void CFMGroup::PlayerDeleted(CFMConnection * pcnxn)
  1399. {
  1400. m_cPlayers--;
  1401. }
  1402. /*-------------------------------------------------------------------------
  1403. * FMSessionDesc.FMSessionDesc
  1404. *-------------------------------------------------------------------------
  1405. Purpose:
  1406. Crack the name/value pairs in the dplay session name and populate the members
  1407. Side Effects:
  1408. This is kinda of bad coupling, since the server packs this, and the messaging layer unpacks.
  1409. */
  1410. FMSessionDesc::FMSessionDesc(LPDPSESSIONDESC2 pdpSessionDesc)
  1411. {
  1412. char * szName = NULL;
  1413. char * szValue = NULL;
  1414. m_nNumPlayers = 0;
  1415. m_nMaxPlayers = 1;
  1416. szName = strtok((char *) pdpSessionDesc->lpszSessionNameA, "\t");
  1417. while(szName)
  1418. {
  1419. if (!lstrcmp(szName, "GAM"))
  1420. {
  1421. szValue = strtok(NULL, "\n");
  1422. m_strGameName = szValue;
  1423. }
  1424. else if (!lstrcmp(szName, "PLR"))
  1425. {
  1426. szValue = strtok(NULL, "\n");
  1427. m_nNumPlayers = (short)atoi(szValue);
  1428. }
  1429. else if (!lstrcmp(szName, "LIM"))
  1430. {
  1431. szValue = strtok(NULL, "\n");
  1432. m_nMaxPlayers = (short)atoi(szValue);
  1433. }
  1434. // there could be many more
  1435. else // throw away any remainder
  1436. {
  1437. szValue = strtok(NULL, "\n");
  1438. }
  1439. szName = strtok(NULL, "\t");
  1440. }
  1441. m_guidInstance = pdpSessionDesc->guidInstance;
  1442. }