1 /**
2 * @file multi.cpp
3 *
4 * Implementation of functions for keeping multiplaye games in sync.
5 */
6 #include "all.h"
7 #include "options.h"
8 #include "../3rdParty/Storm/Source/storm.h"
9 #include "../DiabloUI/diabloui.h"
10 #include <config.h>
11
12 DEVILUTION_BEGIN_NAMESPACE
13
14 BOOLEAN gbSomebodyWonGameKludge;
15 TBuffer sgHiPriBuf;
16 char szPlayerDescript[128];
17 WORD sgwPackPlrOffsetTbl[MAX_PLRS];
18 PkPlayerStruct netplr[MAX_PLRS];
19 BOOLEAN sgbPlayerTurnBitTbl[MAX_PLRS];
20 BOOLEAN sgbPlayerLeftGameTbl[MAX_PLRS];
21 DWORD sgbSentThisCycle;
22 BOOL gbShouldValidatePackage;
23 BYTE gbActivePlayers;
24 BOOLEAN gbGameDestroyed;
25 BOOLEAN sgbSendDeltaTbl[MAX_PLRS];
26 GameData sgGameInitInfo;
27 BOOLEAN gbSelectProvider;
28 int sglTimeoutStart;
29 int sgdwPlayerLeftReasonTbl[MAX_PLRS];
30 TBuffer sgLoPriBuf;
31 DWORD sgdwGameLoops;
32 /**
33 * Specifies the maximum number of players in a game, where 1
34 * represents a single player game and 4 represents a multi player game.
35 */
36 bool gbIsMultiplayer;
37 BOOLEAN sgbTimeout;
38 char szPlayerName[128];
39 BYTE gbDeltaSender;
40 BOOL sgbNetInited;
41 int player_state[MAX_PLRS];
42
43 /**
44 * Contains the set of supported event types supported by the multiplayer
45 * event handler.
46 */
47 const event_type event_types[3] = {
48 EVENT_TYPE_PLAYER_LEAVE_GAME,
49 EVENT_TYPE_PLAYER_CREATE_GAME,
50 EVENT_TYPE_PLAYER_MESSAGE
51 };
52
buffer_init(TBuffer * pBuf)53 static void buffer_init(TBuffer *pBuf)
54 {
55 pBuf->dwNextWriteOffset = 0;
56 pBuf->bData[0] = 0;
57 }
58
59 // Microsoft VisualC 2-11/net runtime
multi_check_pkt_valid(TBuffer * pBuf)60 static int multi_check_pkt_valid(TBuffer *pBuf)
61 {
62 return pBuf->dwNextWriteOffset == 0;
63 }
64
multi_copy_packet(TBuffer * buf,void * packet,BYTE size)65 static void multi_copy_packet(TBuffer *buf, void *packet, BYTE size)
66 {
67 BYTE *p;
68
69 if (buf->dwNextWriteOffset + size + 2 > 0x1000) {
70 return;
71 }
72
73 p = &buf->bData[buf->dwNextWriteOffset];
74 buf->dwNextWriteOffset += size + 1;
75 *p = size;
76 p++;
77 memcpy(p, packet, size);
78 p[size] = 0;
79 }
80
multi_recv_packet(TBuffer * pBuf,BYTE * body,DWORD * size)81 static BYTE *multi_recv_packet(TBuffer *pBuf, BYTE *body, DWORD *size)
82 {
83 BYTE *src_ptr;
84 size_t chunk_size;
85
86 if (pBuf->dwNextWriteOffset != 0) {
87 src_ptr = pBuf->bData;
88 while (TRUE) {
89 if (*src_ptr == 0)
90 break;
91 chunk_size = *src_ptr;
92 if (chunk_size > *size)
93 break;
94 src_ptr++;
95 memcpy(body, src_ptr, chunk_size);
96 body += chunk_size;
97 src_ptr += chunk_size;
98 *size -= chunk_size;
99 }
100 memcpy(pBuf->bData, src_ptr, (pBuf->bData - src_ptr) + pBuf->dwNextWriteOffset + 1);
101 pBuf->dwNextWriteOffset += (pBuf->bData - src_ptr);
102 return body;
103 }
104 return body;
105 }
106
NetRecvPlrData(TPkt * pkt)107 static void NetRecvPlrData(TPkt *pkt)
108 {
109 pkt->hdr.wCheck = LOAD_BE32("\0\0ip");
110 pkt->hdr.px = plr[myplr]._px;
111 pkt->hdr.py = plr[myplr]._py;
112 pkt->hdr.targx = plr[myplr]._ptargx;
113 pkt->hdr.targy = plr[myplr]._ptargy;
114 pkt->hdr.php = plr[myplr]._pHitPoints;
115 pkt->hdr.pmhp = plr[myplr]._pMaxHP;
116 pkt->hdr.bstr = plr[myplr]._pBaseStr;
117 pkt->hdr.bmag = plr[myplr]._pBaseMag;
118 pkt->hdr.bdex = plr[myplr]._pBaseDex;
119 }
120
multi_msg_add(BYTE * pbMsg,BYTE bLen)121 void multi_msg_add(BYTE *pbMsg, BYTE bLen)
122 {
123 if (pbMsg && bLen) {
124 tmsg_add(pbMsg, bLen);
125 }
126 }
127
multi_send_packet(void * packet,BYTE dwSize)128 static void multi_send_packet(void *packet, BYTE dwSize)
129 {
130 TPkt pkt;
131
132 NetRecvPlrData(&pkt);
133 pkt.hdr.wLen = dwSize + sizeof(pkt.hdr);
134 memcpy(pkt.body, packet, dwSize);
135 if (!SNetSendMessage(myplr, &pkt.hdr, pkt.hdr.wLen))
136 nthread_terminate_game("SNetSendMessage0");
137 }
138
NetSendLoPri(BYTE * pbMsg,BYTE bLen)139 void NetSendLoPri(BYTE *pbMsg, BYTE bLen)
140 {
141 if (pbMsg && bLen) {
142 multi_copy_packet(&sgLoPriBuf, pbMsg, bLen);
143 multi_send_packet(pbMsg, bLen);
144 }
145 }
146
NetSendHiPri(BYTE * pbMsg,BYTE bLen)147 void NetSendHiPri(BYTE *pbMsg, BYTE bLen)
148 {
149 BYTE *hipri_body;
150 BYTE *lowpri_body;
151 DWORD size, len;
152 TPkt pkt;
153
154 if (pbMsg && bLen) {
155 multi_copy_packet(&sgHiPriBuf, pbMsg, bLen);
156 multi_send_packet(pbMsg, bLen);
157 }
158 if (!gbShouldValidatePackage) {
159 gbShouldValidatePackage = TRUE;
160 NetRecvPlrData(&pkt);
161 size = gdwNormalMsgSize - sizeof(TPktHdr);
162 hipri_body = multi_recv_packet(&sgHiPriBuf, pkt.body, &size);
163 lowpri_body = multi_recv_packet(&sgLoPriBuf, hipri_body, &size);
164 size = sync_all_monsters(lowpri_body, size);
165 len = gdwNormalMsgSize - size;
166 pkt.hdr.wLen = len;
167 if (!SNetSendMessage(-2, &pkt.hdr, len))
168 nthread_terminate_game("SNetSendMessage");
169 }
170 }
171
multi_send_msg_packet(int pmask,BYTE * src,BYTE len)172 void multi_send_msg_packet(int pmask, BYTE *src, BYTE len)
173 {
174 DWORD v, p, t;
175 TPkt pkt;
176
177 NetRecvPlrData(&pkt);
178 t = len + sizeof(pkt.hdr);
179 pkt.hdr.wLen = t;
180 memcpy(pkt.body, src, len);
181 for (v = 1, p = 0; p < MAX_PLRS; p++, v <<= 1) {
182 if (v & pmask) {
183 if (!SNetSendMessage(p, &pkt.hdr, t) && SErrGetLastError() != STORM_ERROR_INVALID_PLAYER) {
184 nthread_terminate_game("SNetSendMessage");
185 return;
186 }
187 }
188 }
189 }
190
multi_mon_seeds()191 static void multi_mon_seeds()
192 {
193 int i;
194 DWORD l;
195
196 sgdwGameLoops++;
197 l = (sgdwGameLoops >> 8) | (sgdwGameLoops << 24); // _rotr(sgdwGameLoops, 8)
198 for (i = 0; i < MAXMONSTERS; i++)
199 monster[i]._mAISeed = l + i;
200 }
201
multi_handle_turn_upper_bit(int pnum)202 static void multi_handle_turn_upper_bit(int pnum)
203 {
204 int i;
205
206 for (i = 0; i < MAX_PLRS; i++) {
207 if (player_state[i] & PS_CONNECTED && i != pnum)
208 break;
209 }
210
211 if (myplr == i) {
212 sgbSendDeltaTbl[pnum] = TRUE;
213 } else if (myplr == pnum) {
214 gbDeltaSender = i;
215 }
216 }
217
multi_parse_turn(int pnum,int turn)218 static void multi_parse_turn(int pnum, int turn)
219 {
220 DWORD absTurns;
221
222 if (turn >> 31)
223 multi_handle_turn_upper_bit(pnum);
224 absTurns = turn & 0x7FFFFFFF;
225 if (sgbSentThisCycle < gdwTurnsInTransit + absTurns) {
226 if (absTurns >= 0x7FFFFFFF)
227 absTurns &= 0xFFFF;
228 sgbSentThisCycle = absTurns + gdwTurnsInTransit;
229 sgdwGameLoops = 4 * absTurns * sgbNetUpdateRate;
230 }
231 }
232
multi_msg_countdown()233 void multi_msg_countdown()
234 {
235 int i;
236
237 for (i = 0; i < MAX_PLRS; i++) {
238 if (player_state[i] & PS_TURN_ARRIVED) {
239 if (gdwMsgLenTbl[i] == 4)
240 multi_parse_turn(i, *(DWORD *)glpMsgTbl[i]);
241 }
242 }
243 }
244
multi_player_left_msg(int pnum,int left)245 static void multi_player_left_msg(int pnum, int left)
246 {
247 const char *pszFmt;
248
249 if (plr[pnum].plractive) {
250 RemovePlrFromMap(pnum);
251 RemovePortalMissile(pnum);
252 DeactivatePortal(pnum);
253 delta_close_portal(pnum);
254 RemovePlrMissiles(pnum);
255 if (left) {
256 pszFmt = "Player '%s' just left the game";
257 switch (sgdwPlayerLeftReasonTbl[pnum]) {
258 case LEAVE_ENDING:
259 pszFmt = "Player '%s' killed Diablo and left the game!";
260 gbSomebodyWonGameKludge = TRUE;
261 break;
262 case LEAVE_DROP:
263 pszFmt = "Player '%s' dropped due to timeout";
264 break;
265 }
266 EventPlrMsg(pszFmt, plr[pnum]._pName);
267 }
268 plr[pnum].plractive = FALSE;
269 plr[pnum]._pName[0] = '\0';
270 gbActivePlayers--;
271 }
272 }
273
multi_clear_left_tbl()274 static void multi_clear_left_tbl()
275 {
276 int i;
277
278 for (i = 0; i < MAX_PLRS; i++) {
279 if (sgbPlayerLeftGameTbl[i]) {
280 if (gbBufferMsgs == 1)
281 msg_send_drop_pkt(i, sgdwPlayerLeftReasonTbl[i]);
282 else
283 multi_player_left_msg(i, 1);
284
285 sgbPlayerLeftGameTbl[i] = FALSE;
286 sgdwPlayerLeftReasonTbl[i] = 0;
287 }
288 }
289 }
290
multi_player_left(int pnum,int reason)291 void multi_player_left(int pnum, int reason)
292 {
293 sgbPlayerLeftGameTbl[pnum] = TRUE;
294 sgdwPlayerLeftReasonTbl[pnum] = reason;
295 multi_clear_left_tbl();
296 }
297
multi_net_ping()298 void multi_net_ping()
299 {
300 sgbTimeout = TRUE;
301 sglTimeoutStart = SDL_GetTicks();
302 }
303
multi_check_drop_player()304 static void multi_check_drop_player()
305 {
306 int i;
307
308 for (i = 0; i < MAX_PLRS; i++) {
309 if (!(player_state[i] & PS_ACTIVE) && player_state[i] & PS_CONNECTED) {
310 SNetDropPlayer(i, LEAVE_DROP);
311 }
312 }
313 }
314
multi_begin_timeout()315 static void multi_begin_timeout()
316 {
317 int i, nTicks, nState, nLowestActive, nLowestPlayer;
318 BYTE bGroupPlayers, bGroupCount;
319
320 if (!sgbTimeout) {
321 return;
322 }
323 #ifdef _DEBUG
324 if (debug_mode_key_i) {
325 return;
326 }
327 #endif
328
329 nTicks = SDL_GetTicks() - sglTimeoutStart;
330 if (nTicks > 20000) {
331 gbRunGame = FALSE;
332 return;
333 }
334 if (nTicks < 10000) {
335 return;
336 }
337
338 nLowestActive = -1;
339 nLowestPlayer = -1;
340 bGroupPlayers = 0;
341 bGroupCount = 0;
342 for (i = 0; i < MAX_PLRS; i++) {
343 nState = player_state[i];
344 if (nState & PS_CONNECTED) {
345 if (nLowestPlayer == -1) {
346 nLowestPlayer = i;
347 }
348 if (nState & PS_ACTIVE) {
349 bGroupPlayers++;
350 if (nLowestActive == -1) {
351 nLowestActive = i;
352 }
353 } else {
354 bGroupCount++;
355 }
356 }
357 }
358
359 /// ASSERT: assert(bGroupPlayers);
360 /// ASSERT: assert(nLowestActive != -1);
361 /// ASSERT: assert(nLowestPlayer != -1);
362
363 if (bGroupPlayers < bGroupCount) {
364 gbGameDestroyed = TRUE;
365 } else if (bGroupPlayers == bGroupCount) {
366 if (nLowestPlayer != nLowestActive) {
367 gbGameDestroyed = TRUE;
368 } else if (nLowestActive == myplr) {
369 multi_check_drop_player();
370 }
371 } else if (nLowestActive == myplr) {
372 multi_check_drop_player();
373 }
374 }
375
376 /**
377 * @return Always true for singleplayer
378 */
multi_handle_delta()379 int multi_handle_delta()
380 {
381 int i;
382 BOOL received;
383
384 if (gbGameDestroyed) {
385 gbRunGame = FALSE;
386 return FALSE;
387 }
388
389 for (i = 0; i < MAX_PLRS; i++) {
390 if (sgbSendDeltaTbl[i]) {
391 sgbSendDeltaTbl[i] = FALSE;
392 DeltaExportData(i);
393 }
394 }
395
396 sgbSentThisCycle = nthread_send_and_recv_turn(sgbSentThisCycle, 1);
397 if (!nthread_recv_turns(&received)) {
398 multi_begin_timeout();
399 return FALSE;
400 }
401
402 sgbTimeout = FALSE;
403 if (received) {
404 if (!gbShouldValidatePackage) {
405 NetSendHiPri(0, 0);
406 gbShouldValidatePackage = FALSE;
407 } else {
408 gbShouldValidatePackage = FALSE;
409 if (!multi_check_pkt_valid(&sgHiPriBuf))
410 NetSendHiPri(0, 0);
411 }
412 }
413 multi_mon_seeds();
414
415 return TRUE;
416 }
417
multi_handle_all_packets(int pnum,BYTE * pData,int nSize)418 static void multi_handle_all_packets(int pnum, BYTE *pData, int nSize)
419 {
420 int nLen;
421
422 while (nSize != 0) {
423 nLen = ParseCmd(pnum, (TCmd *)pData);
424 if (nLen == 0) {
425 break;
426 }
427 pData += nLen;
428 nSize -= nLen;
429 }
430 }
431
multi_process_tmsgs()432 static void multi_process_tmsgs()
433 {
434 int cnt;
435 TPkt pkt;
436
437 while ((cnt = tmsg_get((BYTE *)&pkt, 512)) != 0) {
438 multi_handle_all_packets(myplr, (BYTE *)&pkt, cnt);
439 }
440 }
441
multi_process_network_packets()442 void multi_process_network_packets()
443 {
444 int dx, dy;
445 TPktHdr *pkt;
446 DWORD dwMsgSize;
447 DWORD dwID;
448 BOOL cond;
449 char *data;
450
451 multi_clear_left_tbl();
452 multi_process_tmsgs();
453 while (SNetReceiveMessage((int *)&dwID, &data, (int *)&dwMsgSize)) {
454 dwRecCount++;
455 multi_clear_left_tbl();
456 pkt = (TPktHdr *)data;
457 if (dwMsgSize < sizeof(TPktHdr))
458 continue;
459 if (dwID >= MAX_PLRS)
460 continue;
461 if (pkt->wCheck != LOAD_BE32("\0\0ip"))
462 continue;
463 if (pkt->wLen != dwMsgSize)
464 continue;
465 plr[dwID]._pownerx = pkt->px;
466 plr[dwID]._pownery = pkt->py;
467 if (dwID != myplr) {
468 // ASSERT: gbBufferMsgs != BUFFER_PROCESS (2)
469 plr[dwID]._pHitPoints = pkt->php;
470 plr[dwID]._pMaxHP = pkt->pmhp;
471 cond = gbBufferMsgs == 1;
472 plr[dwID]._pBaseStr = pkt->bstr;
473 plr[dwID]._pBaseMag = pkt->bmag;
474 plr[dwID]._pBaseDex = pkt->bdex;
475 if (!cond && plr[dwID].plractive && plr[dwID]._pHitPoints != 0) {
476 if (currlevel == plr[dwID].plrlevel && !plr[dwID]._pLvlChanging) {
477 dx = abs(plr[dwID]._px - pkt->px);
478 dy = abs(plr[dwID]._py - pkt->py);
479 if ((dx > 3 || dy > 3) && dPlayer[pkt->px][pkt->py] == 0) {
480 FixPlrWalkTags(dwID);
481 plr[dwID]._poldx = plr[dwID]._px;
482 plr[dwID]._poldy = plr[dwID]._py;
483 FixPlrWalkTags(dwID);
484 plr[dwID]._px = pkt->px;
485 plr[dwID]._py = pkt->py;
486 plr[dwID]._pfutx = pkt->px;
487 plr[dwID]._pfuty = pkt->py;
488 dPlayer[plr[dwID]._px][plr[dwID]._py] = dwID + 1;
489 }
490 dx = abs(plr[dwID]._pfutx - plr[dwID]._px);
491 dy = abs(plr[dwID]._pfuty - plr[dwID]._py);
492 if (dx > 1 || dy > 1) {
493 plr[dwID]._pfutx = plr[dwID]._px;
494 plr[dwID]._pfuty = plr[dwID]._py;
495 }
496 MakePlrPath(dwID, pkt->targx, pkt->targy, TRUE);
497 } else {
498 plr[dwID]._px = pkt->px;
499 plr[dwID]._py = pkt->py;
500 plr[dwID]._pfutx = pkt->px;
501 plr[dwID]._pfuty = pkt->py;
502 plr[dwID]._ptargx = pkt->targx;
503 plr[dwID]._ptargy = pkt->targy;
504 }
505 }
506 }
507 multi_handle_all_packets(dwID, (BYTE *)(pkt + 1), dwMsgSize - sizeof(TPktHdr));
508 }
509 if (SErrGetLastError() != STORM_ERROR_NO_MESSAGES_WAITING)
510 nthread_terminate_game("SNetReceiveMsg");
511 }
512
multi_send_zero_packet(int pnum,BYTE bCmd,BYTE * pbSrc,DWORD dwLen)513 void multi_send_zero_packet(int pnum, BYTE bCmd, BYTE *pbSrc, DWORD dwLen)
514 {
515 DWORD dwOffset, dwBody, dwMsg;
516 TPkt pkt;
517 TCmdPlrInfoHdr *p;
518
519 /// ASSERT: assert(pnum != myplr);
520 /// ASSERT: assert(pbSrc);
521 /// ASSERT: assert(dwLen <= 0x0ffff);
522
523 dwOffset = 0;
524
525 while (dwLen != 0) {
526 pkt.hdr.wCheck = LOAD_BE32("\0\0ip");
527 pkt.hdr.px = 0;
528 pkt.hdr.py = 0;
529 pkt.hdr.targx = 0;
530 pkt.hdr.targy = 0;
531 pkt.hdr.php = 0;
532 pkt.hdr.pmhp = 0;
533 pkt.hdr.bstr = 0;
534 pkt.hdr.bmag = 0;
535 pkt.hdr.bdex = 0;
536 p = (TCmdPlrInfoHdr *)pkt.body;
537 p->bCmd = bCmd;
538 p->wOffset = dwOffset;
539 dwBody = gdwLargestMsgSize - sizeof(pkt.hdr) - sizeof(*p);
540 if (dwLen < dwBody) {
541 dwBody = dwLen;
542 }
543 /// ASSERT: assert(dwBody <= 0x0ffff);
544 p->wBytes = dwBody;
545 memcpy(&pkt.body[sizeof(*p)], pbSrc, p->wBytes);
546 dwMsg = sizeof(pkt.hdr);
547 dwMsg += sizeof(*p);
548 dwMsg += p->wBytes;
549 pkt.hdr.wLen = dwMsg;
550 if (!SNetSendMessage(pnum, &pkt, dwMsg)) {
551 nthread_terminate_game("SNetSendMessage2");
552 return;
553 }
554 #if 0
555 if((DWORD)pnum >= MAX_PLRS) {
556 if(myplr != 0) {
557 debug_plr_tbl[0]++;
558 }
559 if(myplr != 1) {
560 debug_plr_tbl[1]++;
561 }
562 if(myplr != 2) {
563 debug_plr_tbl[2]++;
564 }
565 if(myplr != 3) {
566 debug_plr_tbl[3]++;
567 }
568 } else {
569 debug_plr_tbl[pnum]++;
570 }
571 #endif
572 pbSrc += p->wBytes;
573 dwLen -= p->wBytes;
574 dwOffset += p->wBytes;
575 }
576 }
577
multi_send_pinfo(int pnum,char cmd)578 static void multi_send_pinfo(int pnum, char cmd)
579 {
580 PkPlayerStruct pkplr;
581
582 PackPlayer(&pkplr, myplr, TRUE);
583 dthread_send_delta(pnum, cmd, &pkplr, sizeof(pkplr));
584 }
585
InitLevelType(int l)586 static dungeon_type InitLevelType(int l)
587 {
588 if (l == 0)
589 return DTYPE_TOWN;
590 if (l >= 1 && l <= 4)
591 return DTYPE_CATHEDRAL;
592 if (l >= 5 && l <= 8)
593 return DTYPE_CATACOMBS;
594 if (l >= 9 && l <= 12)
595 return DTYPE_CAVES;
596 if (l >= 13 && l <= 16)
597 return DTYPE_HELL;
598 if (l >= 21 && l <= 24)
599 return DTYPE_CATHEDRAL; // Crypt
600 if (l >= 17 && l <= 20)
601 return DTYPE_CAVES; // Hive
602
603 return DTYPE_CATHEDRAL;
604 }
605
SetupLocalCoords()606 static void SetupLocalCoords()
607 {
608 int x, y;
609
610 if (!leveldebug || gbIsMultiplayer) {
611 currlevel = 0;
612 leveltype = DTYPE_TOWN;
613 setlevel = FALSE;
614 }
615 x = 75;
616 y = 68;
617 #ifdef _DEBUG
618 if (debug_mode_key_inverted_v || debug_mode_key_d) {
619 x = 49;
620 y = 23;
621 }
622 #endif
623 x += plrxoff[myplr];
624 y += plryoff[myplr];
625 plr[myplr]._px = x;
626 plr[myplr]._py = y;
627 plr[myplr]._pfutx = x;
628 plr[myplr]._pfuty = y;
629 plr[myplr]._ptargx = x;
630 plr[myplr]._ptargy = y;
631 plr[myplr].plrlevel = currlevel;
632 plr[myplr]._pLvlChanging = TRUE;
633 plr[myplr].pLvlLoad = 0;
634 plr[myplr]._pmode = PM_NEWLVL;
635 plr[myplr].destAction = ACTION_NONE;
636 }
637
multi_upgrade(BOOL * pfExitProgram)638 static BOOL multi_upgrade(BOOL *pfExitProgram)
639 {
640 BOOL result;
641 int status;
642
643 SNetPerformUpgrade((LPDWORD)&status);
644 result = TRUE;
645 if (status && status != 1) {
646 if (status != 2) {
647 if (status == -1) {
648 DrawDlg("Network upgrade failed");
649 }
650 } else {
651 *pfExitProgram = 1;
652 }
653
654 result = FALSE;
655 }
656
657 return result;
658 }
659
multi_handle_events(_SNETEVENT * pEvt)660 static void multi_handle_events(_SNETEVENT *pEvt)
661 {
662 DWORD LeftReason;
663 GameData *gameData;
664
665 switch (pEvt->eventid) {
666 case EVENT_TYPE_PLAYER_CREATE_GAME: {
667 GameData *gameData = (GameData *)pEvt->data;
668 if (gameData->size != sizeof(GameData))
669 app_fatal("Invalid size of game data: %d", gameData->size);
670 sgGameInitInfo = *gameData;
671 sgbPlayerTurnBitTbl[pEvt->playerid] = TRUE;
672 break;
673 }
674 case EVENT_TYPE_PLAYER_LEAVE_GAME:
675 sgbPlayerLeftGameTbl[pEvt->playerid] = TRUE;
676 sgbPlayerTurnBitTbl[pEvt->playerid] = FALSE;
677
678 LeftReason = 0;
679 if (pEvt->data && pEvt->databytes >= sizeof(DWORD))
680 LeftReason = *(DWORD *)pEvt->data;
681 sgdwPlayerLeftReasonTbl[pEvt->playerid] = LeftReason;
682 if (LeftReason == LEAVE_ENDING)
683 gbSomebodyWonGameKludge = TRUE;
684
685 sgbSendDeltaTbl[pEvt->playerid] = FALSE;
686 dthread_remove_player(pEvt->playerid);
687
688 if (gbDeltaSender == pEvt->playerid)
689 gbDeltaSender = MAX_PLRS;
690 break;
691 case EVENT_TYPE_PLAYER_MESSAGE:
692 ErrorPlrMsg((char *)pEvt->data);
693 break;
694 }
695 }
696
multi_event_handler(BOOL add)697 static void multi_event_handler(BOOL add)
698 {
699 DWORD i;
700 bool (*fn)(event_type, SEVTHANDLER);
701
702 if (add)
703 fn = SNetRegisterEventHandler;
704 else
705 fn = SNetUnregisterEventHandler;
706
707 for (i = 0; i < 3; i++) {
708 if (!fn(event_types[i], multi_handle_events) && add) {
709 app_fatal("SNetRegisterEventHandler:\n%s", TraceLastError());
710 }
711 }
712 }
713
NetClose()714 void NetClose()
715 {
716 if (!sgbNetInited) {
717 return;
718 }
719
720 sgbNetInited = FALSE;
721 nthread_cleanup();
722 dthread_cleanup();
723 tmsg_cleanup();
724 multi_event_handler(FALSE);
725 SNetLeaveGame(3);
726 if (gbIsMultiplayer)
727 SDL_Delay(2000);
728 }
729
NetInit(BOOL bSinglePlayer,BOOL * pfExitProgram)730 BOOL NetInit(BOOL bSinglePlayer, BOOL *pfExitProgram)
731 {
732 _SNETPROGRAMDATA ProgramData;
733 _SNETUIDATA UiData;
734 _SNETPLAYERDATA plrdata;
735
736 while (1) {
737 *pfExitProgram = FALSE;
738 SetRndSeed(0);
739 sgGameInitInfo.size = sizeof(sgGameInitInfo);
740 sgGameInitInfo.dwSeed = time(NULL);
741 sgGameInitInfo.programid = GAME_ID;
742 sgGameInitInfo.versionMajor = PROJECT_VERSION_MAJOR;
743 sgGameInitInfo.versionMinor = PROJECT_VERSION_MINOR;
744 sgGameInitInfo.versionPatch = PROJECT_VERSION_PATCH;
745 sgGameInitInfo.nDifficulty = gnDifficulty;
746 sgGameInitInfo.nTickRate = sgOptions.Gameplay.nTickRate;
747 sgGameInitInfo.bRunInTown = sgOptions.Gameplay.bRunInTown;
748 sgGameInitInfo.bTheoQuest = sgOptions.Gameplay.bTheoQuest;
749 sgGameInitInfo.bCowQuest = sgOptions.Gameplay.bCowQuest;
750 sgGameInitInfo.bFriendlyFire = sgOptions.Gameplay.bFriendlyFire;
751 memset(&ProgramData, 0, sizeof(ProgramData));
752 ProgramData.size = sizeof(ProgramData);
753 ProgramData.maxplayers = MAX_PLRS;
754 ProgramData.initdata = &sgGameInitInfo;
755 memset(&plrdata, 0, sizeof(plrdata));
756 plrdata.size = sizeof(plrdata);
757 memset(&UiData, 0, sizeof(UiData));
758 UiData.size = sizeof(UiData);
759 UiData.selectnamecallback = mainmenu_select_hero_dialog;
760 UiData.changenamecallback = (void (*)())mainmenu_change_name;
761 UiData.profilefields = UiProfileGetString();
762 memset(sgbPlayerTurnBitTbl, 0, sizeof(sgbPlayerTurnBitTbl));
763 gbGameDestroyed = FALSE;
764 memset(sgbPlayerLeftGameTbl, 0, sizeof(sgbPlayerLeftGameTbl));
765 memset(sgdwPlayerLeftReasonTbl, 0, sizeof(sgdwPlayerLeftReasonTbl));
766 memset(sgbSendDeltaTbl, 0, sizeof(sgbSendDeltaTbl));
767 memset(plr, 0, sizeof(plr));
768 memset(sgwPackPlrOffsetTbl, 0, sizeof(sgwPackPlrOffsetTbl));
769 SNetSetBasePlayer(0);
770 if (bSinglePlayer) {
771 if (!multi_init_single(&ProgramData, &plrdata, &UiData))
772 return FALSE;
773 } else {
774 if (!multi_init_multi(&ProgramData, &plrdata, &UiData, pfExitProgram))
775 return FALSE;
776 }
777 sgbNetInited = TRUE;
778 sgbTimeout = FALSE;
779 delta_init();
780 InitPlrMsg();
781 buffer_init(&sgHiPriBuf);
782 buffer_init(&sgLoPriBuf);
783 gbShouldValidatePackage = FALSE;
784 sync_init();
785 nthread_start(sgbPlayerTurnBitTbl[myplr]);
786 dthread_start();
787 tmsg_start();
788 sgdwGameLoops = 0;
789 sgbSentThisCycle = 0;
790 gbDeltaSender = myplr;
791 gbSomebodyWonGameKludge = FALSE;
792 nthread_send_and_recv_turn(0, 0);
793 SetupLocalCoords();
794 multi_send_pinfo(-2, CMD_SEND_PLRINFO);
795 plr[myplr].plractive = TRUE;
796 gbActivePlayers = 1;
797 if (sgbPlayerTurnBitTbl[myplr] == FALSE || msg_wait_resync())
798 break;
799 NetClose();
800 gbSelectProvider = FALSE;
801 }
802 SetRndSeed(sgGameInitInfo.dwSeed);
803 gnDifficulty = sgGameInitInfo.nDifficulty;
804 gnTickRate = sgGameInitInfo.nTickRate;
805 gnTickDelay = 1000 / gnTickRate;
806 gbRunInTown = sgGameInitInfo.bRunInTown;
807 gbTheoQuest = sgGameInitInfo.bTheoQuest;
808 gbCowQuest = sgGameInitInfo.bCowQuest;
809 gbFriendlyFire = sgGameInitInfo.bFriendlyFire;
810
811 for (int i = 0; i < NUMLEVELS; i++) {
812 glSeedTbl[i] = AdvanceRndSeed();
813 gnLevelTypeTbl[i] = InitLevelType(i);
814 }
815 if (!SNetGetGameInfo(GAMEINFO_NAME, szPlayerName, 128))
816 nthread_terminate_game("SNetGetGameInfo1");
817 if (!SNetGetGameInfo(GAMEINFO_PASSWORD, szPlayerDescript, 128))
818 nthread_terminate_game("SNetGetGameInfo2");
819
820 return TRUE;
821 }
822
multi_init_single(_SNETPROGRAMDATA * client_info,_SNETPLAYERDATA * user_info,_SNETUIDATA * ui_info)823 BOOL multi_init_single(_SNETPROGRAMDATA *client_info, _SNETPLAYERDATA *user_info, _SNETUIDATA *ui_info)
824 {
825 int unused;
826
827 if (!SNetInitializeProvider(SELCONN_LOOPBACK, client_info, user_info, ui_info, &fileinfo)) {
828 SErrGetLastError();
829 return FALSE;
830 }
831
832 unused = 0;
833 if (!SNetCreateGame("local", "local", "local", 0, (char *)&sgGameInitInfo, sizeof(sgGameInitInfo), 1, "local", "local", &unused)) {
834 app_fatal("SNetCreateGame1:\n%s", TraceLastError());
835 }
836
837 myplr = 0;
838 gbIsMultiplayer = false;
839
840 return TRUE;
841 }
842
multi_init_multi(_SNETPROGRAMDATA * client_info,_SNETPLAYERDATA * user_info,_SNETUIDATA * ui_info,BOOL * pfExitProgram)843 BOOL multi_init_multi(_SNETPROGRAMDATA *client_info, _SNETPLAYERDATA *user_info, _SNETUIDATA *ui_info, BOOL *pfExitProgram)
844 {
845 BOOL first;
846 int playerId;
847 int type;
848
849 for (first = TRUE;; first = FALSE) {
850 type = 0x00;
851 if (gbSelectProvider) {
852 if (!UiSelectProvider(0, client_info, user_info, ui_info, &fileinfo, &type)
853 && (!first || SErrGetLastError() != STORM_ERROR_REQUIRES_UPGRADE || !multi_upgrade(pfExitProgram))) {
854 return FALSE;
855 }
856 }
857
858 multi_event_handler(TRUE);
859 if (UiSelectGame(1, client_info, user_info, ui_info, &fileinfo, &playerId))
860 break;
861
862 gbSelectProvider = TRUE;
863 }
864
865 if ((DWORD)playerId >= MAX_PLRS) {
866 return FALSE;
867 } else {
868 myplr = playerId;
869 gbIsMultiplayer = true;
870
871 pfile_read_player_from_save();
872
873 return TRUE;
874 }
875 }
876
recv_plrinfo(int pnum,TCmdPlrInfoHdr * p,BOOL recv)877 void recv_plrinfo(int pnum, TCmdPlrInfoHdr *p, BOOL recv)
878 {
879 const char *szEvent;
880
881 if (myplr == pnum) {
882 return;
883 }
884 /// ASSERT: assert((DWORD)pnum < MAX_PLRS);
885
886 if (sgwPackPlrOffsetTbl[pnum] != p->wOffset) {
887 sgwPackPlrOffsetTbl[pnum] = 0;
888 if (p->wOffset != 0) {
889 return;
890 }
891 }
892 if (!recv && sgwPackPlrOffsetTbl[pnum] == 0) {
893 multi_send_pinfo(pnum, CMD_ACK_PLRINFO);
894 }
895
896 memcpy((char *)&netplr[pnum] + p->wOffset, &p[1], p->wBytes); /* todo: cast? */
897 sgwPackPlrOffsetTbl[pnum] += p->wBytes;
898 if (sgwPackPlrOffsetTbl[pnum] != sizeof(*netplr)) {
899 return;
900 }
901
902 sgwPackPlrOffsetTbl[pnum] = 0;
903 multi_player_left_msg(pnum, 0);
904 plr[pnum]._pGFXLoad = 0;
905 UnPackPlayer(&netplr[pnum], pnum, TRUE);
906
907 if (!recv) {
908 return;
909 }
910
911 plr[pnum].plractive = TRUE;
912 gbActivePlayers++;
913
914 if (sgbPlayerTurnBitTbl[pnum] != FALSE) {
915 szEvent = "Player '%s' (level %d) just joined the game";
916 } else {
917 szEvent = "Player '%s' (level %d) is already in the game";
918 }
919 EventPlrMsg(szEvent, plr[pnum]._pName, plr[pnum]._pLevel);
920
921 LoadPlrGFX(pnum, PFILE_STAND);
922 SyncInitPlr(pnum);
923
924 if (plr[pnum].plrlevel == currlevel) {
925 if (plr[pnum]._pHitPoints >> 6 > 0) {
926 StartStand(pnum, DIR_S);
927 } else {
928 plr[pnum]._pgfxnum = 0;
929 LoadPlrGFX(pnum, PFILE_DEATH);
930 plr[pnum]._pmode = PM_DEATH;
931 NewPlrAnim(pnum, plr[pnum]._pDAnim[DIR_S], plr[pnum]._pDFrames, 1, plr[pnum]._pDWidth);
932 plr[pnum]._pAnimFrame = plr[pnum]._pAnimLen - 1;
933 plr[pnum]._pVar8 = 2 * plr[pnum]._pAnimLen;
934 dFlags[plr[pnum]._px][plr[pnum]._py] |= BFLAG_DEAD_PLAYER;
935 }
936 }
937 }
938
939 DEVILUTION_END_NAMESPACE
940