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