Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

620 lines
17 KiB

  1. /***************************************************************************/
  2. /** Microsoft Windows **/
  3. /** Copyright(c) Microsoft Corp., 1991, 1992 **/
  4. /***************************************************************************/
  5. /****************************************************************************
  6. ddecb.cpp
  7. Aug 92, JimH
  8. May 93, JimH chico port
  9. DDE callback functions are here, as well as some helper functions. The
  10. callbacks quickly call CMainWindow member function equivalents.
  11. ****************************************************************************/
  12. #include "hearts.h"
  13. #include "main.h"
  14. #include "debug.h"
  15. #include "resource.h"
  16. /****************************************************************************
  17. DdeServerCallback()
  18. ****************************************************************************/
  19. HDDEDATA EXPENTRY EXPORT DdeServerCallBack(WORD wType, WORD wFmt, HCONV hConv,
  20. HSZ hsz1, HSZ hsz2, HDDEDATA hData,
  21. DWORD dwData1, DWORD dwData2)
  22. {
  23. return ::pMainWnd->DdeSrvCallBack(wType, wFmt, hConv, hsz1, hsz2, hData,
  24. dwData1, dwData2);
  25. }
  26. HDDEDATA CMainWindow::DdeSrvCallBack(
  27. WORD wType, // transaction type
  28. WORD wFmt, // clipboard format
  29. HCONV hConv, // handle to conversation
  30. HSZ hsz1, // string handles
  31. HSZ hsz2,
  32. HDDEDATA hData, // handle to a global memory object
  33. DWORD dwData1, // transaction-specific data
  34. DWORD dwData2)
  35. {
  36. switch(wType)
  37. {
  38. case XTYP_ADVREQ: // server called PostAdvise
  39. if (hsz2 == hszStatus) // if status update
  40. {
  41. return GetGameStatus(hConv);
  42. }
  43. else if (hsz2 == hszGameNumber)
  44. {
  45. int num = GetGameNumber();
  46. return dde->CreateDataHandle(&num, sizeof(num), hszGameNumber);
  47. }
  48. else if (hsz2 == hszPass) // update pass selections all players
  49. {
  50. PASS12 pass12;
  51. GetPass12(pass12);
  52. return dde->CreateDataHandle(&pass12, sizeof(pass12), hszPass);
  53. }
  54. else if (hsz2 == hszMove)
  55. {
  56. return dde->CreateDataHandle(&::move, sizeof(::move), hszMove);
  57. }
  58. else
  59. return (HDDEDATA) NULL;
  60. case XTYP_ADVSTART: // client requested advise loop
  61. if (hsz2 == hszGameNumber)
  62. {
  63. if (CountClients() == 3)
  64. PostMessage(WM_COMMAND, IDM_NEWGAME);
  65. }
  66. return (HDDEDATA) TRUE;
  67. case XTYP_ADVSTOP:
  68. return (HDDEDATA) NULL;
  69. case XTYP_CONNECT: // return TRUE to accept connection
  70. return (HDDEDATA) TRUE;
  71. case XTYP_CONNECT_CONFIRM:
  72. return (HDDEDATA) NULL;
  73. case XTYP_DISCONNECT:
  74. {
  75. int id;
  76. if (IsCurrentPlayer(hConv, &id))
  77. PlayerQuit(id);
  78. }
  79. return (HDDEDATA) NULL;
  80. case XTYP_ERROR:
  81. return (HDDEDATA) NULL;
  82. case XTYP_EXECUTE:
  83. return (HDDEDATA) NULL;
  84. case XTYP_POKE:
  85. if (hsz2 == hszJoin)
  86. {
  87. // add new player and inform others of new arrival
  88. if (CountClients() < 3)
  89. {
  90. AddNewPlayer(hConv, hData);
  91. ddeServer->PostAdvise(hszStatus);
  92. return (HDDEDATA) DDE_FACK;
  93. }
  94. else
  95. return (HDDEDATA) DDE_FBUSY;
  96. }
  97. else if (hsz2 == hszPass)
  98. {
  99. // client notifies server of 3 cards to pass
  100. ReceivePassFromClient(hData);
  101. HandlePassing();
  102. return (HDDEDATA) DDE_FACK;
  103. }
  104. else if (hsz2 == hszMove)
  105. {
  106. // client informs server of card played
  107. ddeServer->GetData(hData, (PBYTE)&::move, sizeof(::move));
  108. ddeServer->PostAdvise(hszMove);
  109. ReceiveMove(::move);
  110. return (HDDEDATA) DDE_FACK;
  111. }
  112. else if (hsz2 == hszPassUpdate)
  113. {
  114. // Client just shuffled and wants to know who has passed so far.
  115. // Be sure we're not still looking at score.
  116. int mode = GetPlayerMode(0);
  117. if (mode == SELECTING || mode == DONE_SELECTING)
  118. ddeServer->PostAdvise(hszPass);
  119. }
  120. else
  121. return (HDDEDATA) DDE_FNOTPROCESSED;
  122. case XTYP_REGISTER:
  123. return (HDDEDATA) NULL;
  124. case XTYP_REQUEST:
  125. // when client first joins game, he asks for update on names
  126. // of other players already in the game
  127. if (hsz2 == hszStatus)
  128. return GetGameStatus(hConv);
  129. else
  130. return (HDDEDATA) NULL;
  131. case XTYP_UNREGISTER:
  132. return (HDDEDATA) NULL;
  133. case XTYP_WILDCONNECT:
  134. return (HDDEDATA) NULL;
  135. default:
  136. return (HDDEDATA) NULL;
  137. }
  138. }
  139. /****************************************************************************
  140. DdeClientCallback()
  141. ****************************************************************************/
  142. HDDEDATA EXPENTRY EXPORT DdeClientCallBack(WORD wType, WORD wFmt, HCONV hConv,
  143. HSZ hsz1, HSZ hsz2, HDDEDATA hData,
  144. DWORD dwData1, DWORD dwData2)
  145. {
  146. return ::pMainWnd->DdeCliCallBack(wType, wFmt, hConv, hsz1, hsz2, hData,
  147. dwData1, dwData2);
  148. }
  149. HDDEDATA CMainWindow::DdeCliCallBack(
  150. WORD wType, // transaction type
  151. WORD wFmt, // clipboard format
  152. HCONV hConv, // handle to conversation
  153. HSZ hsz1, // string handles
  154. HSZ hsz2,
  155. HDDEDATA hData, // handle to a global memory object
  156. DWORD dwData1, // transaction-specific data
  157. DWORD dwData2)
  158. {
  159. switch(wType)
  160. {
  161. case XTYP_ADVDATA:
  162. // advdata is sent whenever the server posts an advise
  163. // on topics client has set up an advise loop on
  164. if (hsz2 == hszStatus)
  165. {
  166. // someone new joined, and here's the names
  167. GAMESTATUS gs;
  168. ddeClient->GetData(hData, (PBYTE)&gs, sizeof(gs));
  169. UpdateStatusNames(gs);
  170. return (HDDEDATA) DDE_FACK;
  171. }
  172. else if (hsz2 == hszGameNumber)
  173. {
  174. // OnNewGame called, here's the game number
  175. int num;
  176. ddeClient->GetData(hData, (PBYTE)&num, sizeof(num));
  177. // Future versions of Hearts can use a negative number
  178. // in the gamenumber field to indicate a version.
  179. if (num < 0)
  180. FatalError(IDS_VERSION);
  181. SetGameNumber(num);
  182. OnNewGame();
  183. return (HDDEDATA) DDE_FACK;
  184. }
  185. else if (hsz2 == hszPass)
  186. {
  187. // someone has passed, here's the update on everyone
  188. PASS12 pass12;
  189. ddeClient->GetData(hData, (PBYTE)&pass12, sizeof(pass12));
  190. UpdatePassStatus(pass12);
  191. HandlePassing();
  192. return (HDDEDATA) DDE_FACK;
  193. }
  194. else if (hsz2 == hszMove)
  195. {
  196. // someone has moved
  197. MOVE moveLocal;
  198. ddeClient->GetData(hData, (PBYTE)&moveLocal, sizeof(moveLocal));
  199. ReceiveMove(moveLocal);
  200. return (HDDEDATA) DDE_FACK;
  201. }
  202. else
  203. return (HDDEDATA) NULL;
  204. case XTYP_DISCONNECT:
  205. FatalError(IDS_DISCONNECT);
  206. return (HDDEDATA) NULL;
  207. case XTYP_ERROR:
  208. return (HDDEDATA) NULL;
  209. case XTYP_UNREGISTER:
  210. return (HDDEDATA) NULL;
  211. case XTYP_XACT_COMPLETE:
  212. return (HDDEDATA) NULL;
  213. default:
  214. return (HDDEDATA) NULL;
  215. }
  216. }
  217. /****************************************************************************
  218. ****************************************************************************/
  219. // Server helper functions
  220. /****************************************************************************
  221. CMainWindow::AddNewPlayer()
  222. After the server gets a valid request from a new player to join the game,
  223. this function is called to create the new player object.
  224. ****************************************************************************/
  225. int CMainWindow::AddNewPlayer(HCONV hConv, HDDEDATA hData)
  226. {
  227. int id; // only gamemeister calls this, so pos == id
  228. if (!p[2]) // new players get added in order 2, 1, 3
  229. id = 2;
  230. else if (!p[1])
  231. id = 1;
  232. else
  233. id = 3;
  234. p[id] = new remote_human(id, id, hConv);
  235. if (p[id] == NULL)
  236. {
  237. FatalError(IDS_MEMORY);
  238. return id;
  239. }
  240. CClientDC dc(this);
  241. #ifdef USE_MIRRORING
  242. SetLayout(dc.m_hDC, 0);
  243. SetLayout(dc.m_hAttribDC, 0);
  244. #endif
  245. CString name = ddeServer->GetDataString(hData);
  246. p[id]->SetName(name, dc);
  247. p[id]->DisplayName(dc);
  248. return id;
  249. }
  250. /****************************************************************************
  251. CMainWindow::GetPass12()
  252. This function gets called in response to the server calling PostAdvise(hszPass).
  253. That happens when the gamemeister passes cards, when the gamemeister learns
  254. that another player has passed cards, or when a client request an update
  255. via Poke(hszPassUpdate).
  256. ****************************************************************************/
  257. void CMainWindow::GetPass12(PASS12& pass12)
  258. {
  259. pass12.passdir = passdir;
  260. for (int pos = 0; pos < MAXPLAYER; pos++)
  261. p[Pos2Id(pos)]->ReturnSelectedCards(pass12.cardid[pos]);
  262. }
  263. /****************************************************************************
  264. CMainWindow::ReceivePassFromClient()
  265. Called in reponse to a client poking hszPass info.
  266. ****************************************************************************/
  267. void CMainWindow::ReceivePassFromClient(HDDEDATA hData)
  268. {
  269. PASS3 pass3;
  270. ddeServer->GetData(hData, (PBYTE)&pass3, sizeof(pass3));
  271. // queue up pass info if gamemeister is still looking at score
  272. if (p[0]->GetMode() == SCORING)
  273. {
  274. ::passq[::cQdPasses++] = pass3;
  275. return;
  276. }
  277. else if (::cQdMoves > 0)
  278. {
  279. for (int i = 0; i < ::cQdPasses; i++)
  280. HandlePass(::passq[i]);
  281. ::cQdPasses = 0;
  282. }
  283. HandlePass(pass3);
  284. }
  285. /****************************************************************************
  286. CMainWindow::HandlePass()
  287. Called from ReceivePassFromClient() and also DispatchCards() in case any
  288. passes were queued up when the gamemeister was looking at the score.
  289. ****************************************************************************/
  290. void CMainWindow::HandlePass(PASS3& pass3)
  291. {
  292. CClientDC dc(this);
  293. #ifdef USE_MIRRORING
  294. SetLayout(dc.m_hDC, 0);
  295. SetLayout(dc.m_hAttribDC, 0);
  296. #endif
  297. int pos = pass3.id; // only gamemeister queues moves
  298. for (int i = 0; i < 3; i++)
  299. {
  300. SLOT s = p[pos]->GetSlot(pass3.cardid[i]);
  301. p[pos]->Select(s, TRUE);
  302. }
  303. p[pos]->SetMode(DONE_SELECTING);
  304. p[pos]->MarkSelectedCards(dc);
  305. ddeServer->PostAdvise(hszPass);
  306. }
  307. /****************************************************************************
  308. CMainWindow::IsCurrentPlayer()
  309. The gamemeister uses this function to determine if an XTYP_DISCONNECT
  310. message comes from a currently active player, or whether it can just
  311. be ignored.
  312. ****************************************************************************/
  313. BOOL CMainWindow::IsCurrentPlayer(HCONV hConv, int *id)
  314. {
  315. BOOL bResult = FALSE;
  316. for (int i = 1; i < MAXPLAYER; i++)
  317. if (p[i])
  318. if (hConv == p[i]->GetConv())
  319. {
  320. bResult = TRUE;
  321. *id = i;
  322. }
  323. return bResult;
  324. }
  325. /****************************************************************************
  326. CMainWindow::GetGameStatus()
  327. Returns a dde handle with current active players' names and ids.
  328. ****************************************************************************/
  329. HDDEDATA CMainWindow::GetGameStatus(HCONV hConv)
  330. {
  331. GAMESTATUS gs;
  332. gs.id = -1; // error value if not reset
  333. for (int i = 0; i < MAXPLAYER; i++)
  334. {
  335. if (p[i])
  336. {
  337. lstrcpy(gs.name[i], p[i]->GetName());
  338. if (p[i]->GetConv() == hConv)
  339. {
  340. gs.id = i;
  341. }
  342. }
  343. else
  344. gs.name[i][0] = '\0';
  345. }
  346. return dde->CreateDataHandle(&gs, sizeof(gs), hszStatus);
  347. }
  348. /****************************************************************************
  349. CMainWindow::UpdatePassStatus()
  350. Fills a PASS12 structure with current pass information.
  351. ****************************************************************************/
  352. void CMainWindow::UpdatePassStatus(PASS12& pass12)
  353. {
  354. CClientDC dc(this);
  355. #ifdef USE_MIRRORING
  356. SetLayout(dc.m_hDC, 0);
  357. SetLayout(dc.m_hAttribDC, 0);
  358. #endif
  359. for (int id = 0; id < MAXPLAYER; id++)
  360. {
  361. if (pass12.cardid[id][0] != EMPTY)
  362. {
  363. int pos = Id2Pos(id);
  364. if (pos != 0) // if it's not me
  365. {
  366. if (p[pos]->GetMode() == SELECTING)
  367. {
  368. for (int i = 0; i < 3; i++)
  369. {
  370. SLOT slot = p[pos]->GetSlot(pass12.cardid[id][i]);
  371. p[pos]->Select(slot, TRUE);
  372. }
  373. p[pos]->MarkSelectedCards(dc);
  374. p[pos]->SetMode(DONE_SELECTING);
  375. }
  376. }
  377. }
  378. }
  379. }
  380. /****************************************************************************
  381. CMainWindow::PlayerQuit()
  382. The gamemeister has been notified that a currently active player has
  383. disconnected.
  384. ****************************************************************************/
  385. void CMainWindow::PlayerQuit(int id)
  386. {
  387. // If hearts is running only because it has been autostarted, and
  388. // the autostarted leaves, you might as well shut down. Note that
  389. // bAutostarted is set to FALSE as soon as the dealer actually
  390. // starts a game.
  391. if (bAutostarted && (id == 2))
  392. PostMessage(WM_CLOSE);
  393. p[id]->Quit(); // mark this player as quitting
  394. modetype mode = p[id]->GetMode();
  395. // change name to [name]
  396. CString name = p[id]->GetName();
  397. CString newname = "[" + name + "]";
  398. CClientDC dc(this);
  399. #ifdef USE_MIRRORING
  400. SetLayout(dc.m_hDC, 0);
  401. SetLayout(dc.m_hAttribDC, 0);
  402. #endif
  403. p[id]->SetName(newname, dc);
  404. p[id]->DisplayName(dc);
  405. if (mode == SELECTING)
  406. {
  407. p[id]->SelectCardsToPass();
  408. ddeServer->PostAdvise(hszPass); // let others put up white dots
  409. BOOL bReady = TRUE; // time to ungray pass button?
  410. for (int i = 1; i < MAXPLAYER; i++)
  411. if (p[i]->GetMode() != DONE_SELECTING)
  412. bReady = FALSE;
  413. if (bReady)
  414. HandlePassing();
  415. }
  416. else if (mode == PLAYING)
  417. {
  418. p[id]->SelectCardToPlay(handinfo, bCheating);
  419. }
  420. ddeServer->PostAdvise(hszStatus); // let others know quitter is [quitter]
  421. }
  422. /****************************************************************************
  423. ****************************************************************************/
  424. // Helper functions used by both server and client
  425. /****************************************************************************
  426. CMainWindow::ReceiveMove()
  427. Move data has been received. Determine if the move can be handled now
  428. or if it must be queued.
  429. ****************************************************************************/
  430. void CMainWindow::ReceiveMove(MOVE& move)
  431. {
  432. if (move.playerid == m_myid) // don't have to handle my own moves
  433. return;
  434. int mode = p[0]->GetMode();
  435. if (mode == ACCEPTING || mode == SCORING || bTimerOn)
  436. {
  437. ::moveq[::cQdMoves++] = move;
  438. return;
  439. }
  440. else if (::cQdMoves > 0)
  441. {
  442. for (int i = 0; i < ::cQdMoves; i++)
  443. HandleMove(::moveq[i]);
  444. ::cQdMoves = 0;
  445. }
  446. HandleMove(move);
  447. }
  448. /****************************************************************************
  449. CMainWindow::Handle Move()
  450. This is called either from ReceiveMove() or from some other function that
  451. has temporarily suspended move processing and is now clearing out its
  452. move queue.
  453. ****************************************************************************/
  454. void CMainWindow::HandleMove(MOVE& move)
  455. {
  456. int pos = Id2Pos(move.playerid);
  457. SLOT s = p[pos]->GetSlot(move.cardid);
  458. ASSERT(s >= 0);
  459. ASSERT(s < MAXSLOT);
  460. ASSERT(p[pos]->Card(s)->IsValid());
  461. p[pos]->SetMode(WAITING);
  462. p[pos]->MarkCardPlayed(s);
  463. handinfo.cardplayed[move.playerid] = p[pos]->Card(s);
  464. OnRef();
  465. }