1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file  d_net.c
12 /// \brief SRB2 network game communication and protocol, all OS independent parts.
13 //
14 ///        Implement a Sliding window protocol without receiver window
15 ///        (out of order reception)
16 ///        This protocol uses a mix of "goback n" and "selective repeat" implementation
17 ///        The NOTHING packet is sent when connection is idle to acknowledge packets
18 
19 #include "doomdef.h"
20 #include "g_game.h"
21 #include "i_net.h"
22 #include "i_system.h"
23 #include "m_argv.h"
24 #include "d_net.h"
25 #include "w_wad.h"
26 #include "d_netfil.h"
27 #include "d_clisrv.h"
28 #include "z_zone.h"
29 #include "i_tcp.h"
30 #include "d_main.h" // srb2home
31 
32 //
33 // NETWORKING
34 //
35 // gametic is the tic about to (or currently being) run
36 // Server:
37 //   maketic is the tic that hasn't had control made for it yet
38 //   nettics is the tic for each node
39 //   firstticstosend is the lowest value of nettics
40 // Client:
41 //   neededtic is the tic needed by the client to run the game
42 //   firstticstosend is used to optimize a condition
43 // Normally maketic >= gametic > 0
44 
45 #define FORCECLOSE 0x8000
46 tic_t connectiontimeout = (10*TICRATE);
47 
48 /// \brief network packet
49 doomcom_t *doomcom = NULL;
50 /// \brief network packet data, points inside doomcom
51 doomdata_t *netbuffer = NULL;
52 
53 #ifdef DEBUGFILE
54 FILE *debugfile = NULL; // put some net info in a file during the game
55 #endif
56 
57 #define MAXREBOUND 8
58 static doomdata_t reboundstore[MAXREBOUND];
59 static INT16 reboundsize[MAXREBOUND];
60 static INT32 rebound_head, rebound_tail;
61 
62 /// \brief bandwith of netgame
63 INT32 net_bandwidth;
64 
65 /// \brief max length per packet
66 INT16 hardware_MAXPACKETLENGTH;
67 
68 boolean (*I_NetGet)(void) = NULL;
69 void (*I_NetSend)(void) = NULL;
70 boolean (*I_NetCanSend)(void) = NULL;
71 boolean (*I_NetCanGet)(void) = NULL;
72 void (*I_NetCloseSocket)(void) = NULL;
73 void (*I_NetFreeNodenum)(INT32 nodenum) = NULL;
74 SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL;
75 boolean (*I_NetOpenSocket)(void) = NULL;
76 boolean (*I_Ban) (INT32 node) = NULL;
77 void (*I_ClearBans)(void) = NULL;
78 const char *(*I_GetNodeAddress) (INT32 node) = NULL;
79 const char *(*I_GetBanAddress) (size_t ban) = NULL;
80 const char *(*I_GetBanMask) (size_t ban) = NULL;
81 boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL;
82 boolean *bannednode = NULL;
83 
84 
85 // network stats
86 static tic_t statstarttic;
87 INT32 getbytes = 0;
88 INT64 sendbytes = 0;
89 static INT32 retransmit = 0, duppacket = 0;
90 static INT32 sendackpacket = 0, getackpacket = 0;
91 INT32 ticruned = 0, ticmiss = 0;
92 
93 // globals
94 INT32 getbps, sendbps;
95 float lostpercent, duppercent, gamelostpercent;
96 INT32 packetheaderlength;
97 
Net_GetNetStat(void)98 boolean Net_GetNetStat(void)
99 {
100 	const tic_t t = I_GetTime();
101 	static INT64 oldsendbyte = 0;
102 	if (statstarttic+STATLENGTH <= t)
103 	{
104 		const tic_t df = t-statstarttic;
105 		const INT64 newsendbyte = sendbytes - oldsendbyte;
106 		sendbps = (INT32)(newsendbyte*TICRATE)/df;
107 		getbps = (getbytes*TICRATE)/df;
108 		if (sendackpacket)
109 			lostpercent = 100.0f*(float)retransmit/(float)sendackpacket;
110 		else
111 			lostpercent = 0.0f;
112 		if (getackpacket)
113 			duppercent = 100.0f*(float)duppacket/(float)getackpacket;
114 		else
115 			duppercent = 0.0f;
116 		if (ticruned)
117 			gamelostpercent = 100.0f*(float)ticmiss/(float)ticruned;
118 		else
119 			gamelostpercent = 0.0f;
120 
121 		ticmiss = ticruned = 0;
122 		oldsendbyte = sendbytes;
123 		getbytes = 0;
124 		sendackpacket = getackpacket = duppacket = retransmit = 0;
125 		statstarttic = t;
126 
127 		return 1;
128 	}
129 	return 0;
130 }
131 
132 // -----------------------------------------------------------------
133 // Some structs and functions for acknowledgement of packets
134 // -----------------------------------------------------------------
135 #define MAXACKPACKETS 96 // Minimum number of nodes (wat)
136 #define MAXACKTOSEND 96
137 #define URGENTFREESLOTNUM 10
138 #define ACKTOSENDTIMEOUT (TICRATE/11)
139 
140 #ifndef NONET
141 typedef struct
142 {
143 	UINT8 acknum;
144 	UINT8 nextacknum;
145 	UINT8 destinationnode; // The node to send the ack to
146 	tic_t senttime; // The time when the ack was sent
147 	UINT16 length; // The packet size
148 	UINT16 resentnum; // The number of times the ack has been resent
149 	union {
150 		SINT8 raw[MAXPACKETLENGTH];
151 		doomdata_t data;
152 	} pak;
153 } ackpak_t;
154 #endif
155 
156 typedef enum
157 {
158 	NF_CLOSE = 1, // Flag is set when connection is closing
159 	NF_TIMEOUT = 2, // Flag is set when the node got a timeout
160 } node_flags_t;
161 
162 #ifndef NONET
163 // Table of packets that were not acknowleged can be resent (the sender window)
164 static ackpak_t ackpak[MAXACKPACKETS];
165 #endif
166 
167 typedef struct
168 {
169 	// ack return to send (like sliding window protocol)
170 	UINT8 firstacktosend;
171 
172 	// when no consecutive packets are received we keep in mind what packets
173 	// we already received in a queue
174 	UINT8 acktosend_head;
175 	UINT8 acktosend_tail;
176 	UINT8 acktosend[MAXACKTOSEND];
177 
178 	// automatically send keep alive packet when not enough trafic
179 	tic_t lasttimeacktosend_sent;
180 	// detect connection lost
181 	tic_t lasttimepacketreceived;
182 
183 	// flow control: do not send too many packets with ack
184 	UINT8 remotefirstack;
185 	UINT8 nextacknum;
186 
187 	UINT8 flags;
188 } node_t;
189 
190 static node_t nodes[MAXNETNODES];
191 #define NODETIMEOUT 14
192 
193 #ifndef NONET
194 // return <0 if a < b (mod 256)
195 //         0 if a = n (mod 256)
196 //        >0 if a > b (mod 256)
197 // mnemonic: to use it compare to 0: cmpack(a,b)<0 is "a < b" ...
cmpack(UINT8 a,UINT8 b)198 FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b)
199 {
200 	register INT32 d = a - b;
201 
202 	if (d >= 127 || d < -128)
203 		return -d;
204 	return d;
205 }
206 
207 /** Sets freeack to a free acknum and copies the netbuffer in the ackpak table
208   *
209   * \param freeack  The address to store the free acknum at
210   * \param lowtimer ???
211   * \return True if a free acknum was found
212   */
GetFreeAcknum(UINT8 * freeack,boolean lowtimer)213 static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
214 {
215 	node_t *node = &nodes[doomcom->remotenode];
216 	INT32 i, numfreeslot = 0;
217 
218 	if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0)
219 	{
220 		DEBFILE(va("too fast %d %d\n",node->remotefirstack,node->nextacknum));
221 		return false;
222 	}
223 
224 	for (i = 0; i < MAXACKPACKETS; i++)
225 		if (!ackpak[i].acknum)
226 		{
227 			// For low priority packets, make sure to let freeslots so urgent packets can be sent
228 			if (netbuffer->packettype >= PT_CANFAIL)
229 			{
230 				numfreeslot++;
231 				if (numfreeslot <= URGENTFREESLOTNUM)
232 					continue;
233 			}
234 
235 			ackpak[i].acknum = node->nextacknum;
236 			ackpak[i].nextacknum = node->nextacknum;
237 			node->nextacknum++;
238 			if (!node->nextacknum)
239 				node->nextacknum++;
240 			ackpak[i].destinationnode = (UINT8)(node - nodes);
241 			ackpak[i].length = doomcom->datalength;
242 			if (lowtimer)
243 			{
244 				// Lowtime means can't be sent now so try it as soon as possible
245 				ackpak[i].senttime = 0;
246 				ackpak[i].resentnum = 1;
247 			}
248 			else
249 			{
250 				ackpak[i].senttime = I_GetTime();
251 				ackpak[i].resentnum = 0;
252 			}
253 			M_Memcpy(ackpak[i].pak.raw, netbuffer, ackpak[i].length);
254 
255 			*freeack = ackpak[i].acknum;
256 
257 			sendackpacket++; // For stat
258 
259 			return true;
260 		}
261 #ifdef PARANOIA
262 	CONS_Debug(DBG_NETPLAY, "No more free ackpacket\n");
263 #endif
264 	if (netbuffer->packettype < PT_CANFAIL)
265 		I_Error("Connection lost\n");
266 	return false;
267 }
268 
269 /** Counts how many acks are free
270   *
271   * \param urgent True if the type of the packet meant to
272   *               use an ack is lower than PT_CANFAIL
273   *               If for some reason you don't want use it
274   *               for any packet type in particular,
275   *               just set to false
276   * \return The number of free acks
277   *
278   */
Net_GetFreeAcks(boolean urgent)279 INT32 Net_GetFreeAcks(boolean urgent)
280 {
281 	INT32 i, numfreeslot = 0;
282 	INT32 n = 0; // Number of free acks found
283 
284 	for (i = 0; i < MAXACKPACKETS; i++)
285 		if (!ackpak[i].acknum)
286 		{
287 			// For low priority packets, make sure to let freeslots so urgent packets can be sent
288 			if (!urgent)
289 			{
290 				numfreeslot++;
291 				if (numfreeslot <= URGENTFREESLOTNUM)
292 					continue;
293 			}
294 
295 			n++;
296 		}
297 
298 	return n;
299 }
300 
301 // Get a ack to send in the queue of this node
GetAcktosend(INT32 node)302 static UINT8 GetAcktosend(INT32 node)
303 {
304 	nodes[node].lasttimeacktosend_sent = I_GetTime();
305 	return nodes[node].firstacktosend;
306 }
307 
RemoveAck(INT32 i)308 static void RemoveAck(INT32 i)
309 {
310 	INT32 node = ackpak[i].destinationnode;
311 	DEBFILE(va("Remove ack %d\n",ackpak[i].acknum));
312 	ackpak[i].acknum = 0;
313 	if (nodes[node].flags & NF_CLOSE)
314 		Net_CloseConnection(node);
315 }
316 
317 // We have got a packet, proceed the ack request and ack return
Processackpak(void)318 static boolean Processackpak(void)
319 {
320 	INT32 i;
321 	boolean goodpacket = true;
322 	node_t *node = &nodes[doomcom->remotenode];
323 
324 	// Received an ack return, so remove the ack in the list
325 	if (netbuffer->ackreturn && cmpack(node->remotefirstack, netbuffer->ackreturn) < 0)
326 	{
327 		node->remotefirstack = netbuffer->ackreturn;
328 		// Search the ackbuffer and free it
329 		for (i = 0; i < MAXACKPACKETS; i++)
330 			if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes
331 				&& cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0)
332 			{
333 				RemoveAck(i);
334 			}
335 	}
336 
337 	// Received a packet with ack, queue it to send the ack back
338 	if (netbuffer->ack)
339 	{
340 		UINT8 ack = netbuffer->ack;
341 		getackpacket++;
342 		if (cmpack(ack, node->firstacktosend) <= 0)
343 		{
344 			DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack));
345 			duppacket++;
346 			goodpacket = false; // Discard packet (duplicate)
347 		}
348 		else
349 		{
350 			// Check if it is not already in the queue
351 			for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND)
352 				if (node->acktosend[i] == ack)
353 				{
354 					DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack));
355 					duppacket++;
356 					goodpacket = false; // Discard packet (duplicate)
357 					break;
358 				}
359 			if (goodpacket)
360 			{
361 				// Is a good packet so increment the acknowledge number,
362 				// Then search for a "hole" in the queue
363 				UINT8 nextfirstack = (UINT8)(node->firstacktosend + 1);
364 				if (!nextfirstack)
365 					nextfirstack = 1;
366 
367 				if (ack == nextfirstack)
368 				{
369 					UINT8 hm1; // head - 1
370 					boolean change = true;
371 
372 					node->firstacktosend = nextfirstack++;
373 					if (!nextfirstack)
374 						nextfirstack = 1;
375 					hm1 = (UINT8)((node->acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND);
376 					while (change)
377 					{
378 						change = false;
379 						for (i = node->acktosend_tail; i != node->acktosend_head;
380 							i = (i+1) % MAXACKTOSEND)
381 						{
382 							if (cmpack(node->acktosend[i], nextfirstack) <= 0)
383 							{
384 								if (node->acktosend[i] == nextfirstack)
385 								{
386 									node->firstacktosend = nextfirstack++;
387 									if (!nextfirstack)
388 										nextfirstack = 1;
389 									change = true;
390 								}
391 								if (i == node->acktosend_tail)
392 								{
393 									node->acktosend[node->acktosend_tail] = 0;
394 									node->acktosend_tail = (UINT8)((i+1) % MAXACKTOSEND);
395 								}
396 								else if (i == hm1)
397 								{
398 									node->acktosend[hm1] = 0;
399 									node->acktosend_head = hm1;
400 									hm1 = (UINT8)((hm1-1+MAXACKTOSEND) % MAXACKTOSEND);
401 								}
402 							}
403 						}
404 					}
405 				}
406 				else // Out of order packet
407 				{
408 					// Don't increment firsacktosend, put it in asktosend queue
409 					// Will be incremented when the nextfirstack comes (code above)
410 					UINT8 newhead = (UINT8)((node->acktosend_head+1) % MAXACKTOSEND);
411 					DEBFILE(va("out of order packet (%d expected)\n", nextfirstack));
412 					if (newhead != node->acktosend_tail)
413 					{
414 						node->acktosend[node->acktosend_head] = ack;
415 						node->acktosend_head = newhead;
416 					}
417 					else // Buffer full discard packet, sender will resend it
418 					{ // We can admit the packet but we will not detect the duplication after :(
419 						DEBFILE("no more freeackret\n");
420 						goodpacket = false;
421 					}
422 				}
423 			}
424 		}
425 	}
426 	return goodpacket;
427 }
428 #endif
429 
430 // send special packet with only ack on it
Net_SendAcks(INT32 node)431 void Net_SendAcks(INT32 node)
432 {
433 #ifdef NONET
434 	(void)node;
435 #else
436 	netbuffer->packettype = PT_NOTHING;
437 	M_Memcpy(netbuffer->u.textcmd, nodes[node].acktosend, MAXACKTOSEND);
438 	HSendPacket(node, false, 0, MAXACKTOSEND);
439 #endif
440 }
441 
442 #ifndef NONET
GotAcks(void)443 static void GotAcks(void)
444 {
445 	INT32 i, j;
446 
447 	for (j = 0; j < MAXACKTOSEND; j++)
448 		if (netbuffer->u.textcmd[j])
449 			for (i = 0; i < MAXACKPACKETS; i++)
450 				if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode)
451 				{
452 					if (ackpak[i].acknum == netbuffer->u.textcmd[j])
453 						RemoveAck(i);
454 					// nextacknum is first equal to acknum, then when receiving bigger ack
455 					// there is big chance the packet is lost
456 					// When resent, nextacknum = nodes[node].nextacknum
457 					// will redo the same but with different value
458 					else if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0
459 							&& ackpak[i].senttime > 0)
460 						{
461 							ackpak[i].senttime--; // hurry up
462 						}
463 				}
464 }
465 #endif
466 
Net_ConnectionTimeout(INT32 node)467 void Net_ConnectionTimeout(INT32 node)
468 {
469 	// Don't timeout several times
470 	if (nodes[node].flags & NF_TIMEOUT)
471 		return;
472 	nodes[node].flags |= NF_TIMEOUT;
473 
474 	// Send a very special packet to self (hack the reboundstore queue)
475 	// Main code will handle it
476 	reboundstore[rebound_head].packettype = PT_NODETIMEOUT;
477 	reboundstore[rebound_head].ack = 0;
478 	reboundstore[rebound_head].ackreturn = 0;
479 	reboundstore[rebound_head].u.textcmd[0] = (UINT8)node;
480 	reboundsize[rebound_head] = (INT16)(BASEPACKETSIZE + 1);
481 	rebound_head = (rebound_head+1) % MAXREBOUND;
482 
483 	// Do not redo it quickly (if we do not close connection it is
484 	// for a good reason!)
485 	nodes[node].lasttimepacketreceived = I_GetTime();
486 }
487 
488 // Resend the data if needed
Net_AckTicker(void)489 void Net_AckTicker(void)
490 {
491 #ifndef NONET
492 	INT32 i;
493 
494 	for (i = 0; i < MAXACKPACKETS; i++)
495 	{
496 		const INT32 nodei = ackpak[i].destinationnode;
497 		node_t *node = &nodes[nodei];
498 		if (ackpak[i].acknum && ackpak[i].senttime + NODETIMEOUT < I_GetTime())
499 		{
500 			if (ackpak[i].resentnum > 20 && (node->flags & NF_CLOSE))
501 			{
502 				DEBFILE(va("ack %d sent 20 times so connection is supposed lost: node %d\n",
503 					i, nodei));
504 				Net_CloseConnection(nodei | FORCECLOSE);
505 
506 				ackpak[i].acknum = 0;
507 				continue;
508 			}
509 			DEBFILE(va("Resend ack %d, %u<%d at %u\n", ackpak[i].acknum, ackpak[i].senttime,
510 				NODETIMEOUT, I_GetTime()));
511 			M_Memcpy(netbuffer, ackpak[i].pak.raw, ackpak[i].length);
512 			ackpak[i].senttime = I_GetTime();
513 			ackpak[i].resentnum++;
514 			ackpak[i].nextacknum = node->nextacknum;
515 			retransmit++; // For stat
516 			HSendPacket((INT32)(node - nodes), false, ackpak[i].acknum,
517 				(size_t)(ackpak[i].length - BASEPACKETSIZE));
518 		}
519 	}
520 
521 	for (i = 1; i < MAXNETNODES; i++)
522 	{
523 		// This is something like node open flag
524 		if (nodes[i].firstacktosend)
525 		{
526 			// We haven't sent a packet for a long time
527 			// Acknowledge packet if needed
528 			if (nodes[i].lasttimeacktosend_sent + ACKTOSENDTIMEOUT < I_GetTime())
529 				Net_SendAcks(i);
530 
531 			if (!(nodes[i].flags & NF_CLOSE)
532 				&& nodes[i].lasttimepacketreceived + connectiontimeout < I_GetTime())
533 			{
534 				Net_ConnectionTimeout(i);
535 			}
536 		}
537 	}
538 #endif
539 }
540 
541 // Remove last packet received ack before resending the ackreturn
542 // (the higher layer doesn't have room, or something else ....)
Net_UnAcknowledgePacket(INT32 node)543 void Net_UnAcknowledgePacket(INT32 node)
544 {
545 #ifdef NONET
546 	(void)node;
547 #else
548 	INT32 hm1 = (nodes[node].acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND;
549 	DEBFILE(va("UnAcknowledge node %d\n", node));
550 	if (!node)
551 		return;
552 	if (nodes[node].acktosend[hm1] == netbuffer->ack)
553 	{
554 		nodes[node].acktosend[hm1] = 0;
555 		nodes[node].acktosend_head = (UINT8)hm1;
556 	}
557 	else if (nodes[node].firstacktosend == netbuffer->ack)
558 	{
559 		nodes[node].firstacktosend--;
560 		if (!nodes[node].firstacktosend)
561 			nodes[node].firstacktosend = UINT8_MAX;
562 	}
563 	else
564 	{
565 		while (nodes[node].firstacktosend != netbuffer->ack)
566 		{
567 			nodes[node].acktosend_tail = (UINT8)
568 				((nodes[node].acktosend_tail-1+MAXACKTOSEND) % MAXACKTOSEND);
569 			nodes[node].acktosend[nodes[node].acktosend_tail] = nodes[node].firstacktosend;
570 
571 			nodes[node].firstacktosend--;
572 			if (!nodes[node].firstacktosend)
573 				nodes[node].firstacktosend = UINT8_MAX;
574 		}
575 		nodes[node].firstacktosend++;
576 		if (!nodes[node].firstacktosend)
577 			nodes[node].firstacktosend = 1;
578 	}
579 #endif
580 }
581 
582 #ifndef NONET
583 /** Checks if all acks have been received
584   *
585   * \return True if all acks have been received
586   *
587   */
Net_AllAcksReceived(void)588 static boolean Net_AllAcksReceived(void)
589 {
590 	INT32 i;
591 
592 	for (i = 0; i < MAXACKPACKETS; i++)
593 		if (ackpak[i].acknum)
594 			return false;
595 
596 	return true;
597 }
598 #endif
599 
600 /** Waits for all ackreturns
601   *
602   * \param timeout Timeout in seconds
603   *
604   */
Net_WaitAllAckReceived(UINT32 timeout)605 void Net_WaitAllAckReceived(UINT32 timeout)
606 {
607 #ifdef NONET
608 	(void)timeout;
609 #else
610 	tic_t tictac = I_GetTime();
611 	timeout = tictac + timeout*NEWTICRATE;
612 
613 	HGetPacket();
614 	while (timeout > I_GetTime() && !Net_AllAcksReceived())
615 	{
616 		while (tictac == I_GetTime())
617 			I_Sleep();
618 		tictac = I_GetTime();
619 		HGetPacket();
620 		Net_AckTicker();
621 	}
622 #endif
623 }
624 
InitNode(node_t * node)625 static void InitNode(node_t *node)
626 {
627 	node->acktosend_head = node->acktosend_tail = 0;
628 	node->firstacktosend = 0;
629 	node->nextacknum = 1;
630 	node->remotefirstack = 0;
631 	node->flags = 0;
632 }
633 
InitAck(void)634 static void InitAck(void)
635 {
636 	INT32 i;
637 
638 #ifndef NONET
639 	for (i = 0; i < MAXACKPACKETS; i++)
640 		ackpak[i].acknum = 0;
641 #endif
642 
643 	for (i = 0; i < MAXNETNODES; i++)
644 		InitNode(&nodes[i]);
645 }
646 
647 /** Removes all acks of a given packet type
648   *
649   * \param packettype The packet type to forget
650   *
651   */
Net_AbortPacketType(UINT8 packettype)652 void Net_AbortPacketType(UINT8 packettype)
653 {
654 #ifdef NONET
655 	(void)packettype;
656 #else
657 	INT32 i;
658 	for (i = 0; i < MAXACKPACKETS; i++)
659 		if (ackpak[i].acknum && (ackpak[i].pak.data.packettype == packettype
660 			|| packettype == UINT8_MAX))
661 		{
662 			ackpak[i].acknum = 0;
663 		}
664 #endif
665 }
666 
667 // -----------------------------------------------------------------
668 // end of acknowledge function
669 // -----------------------------------------------------------------
670 
671 // remove a node, clear all ack from this node and reset askret
Net_CloseConnection(INT32 node)672 void Net_CloseConnection(INT32 node)
673 {
674 #ifdef NONET
675 	(void)node;
676 #else
677 	INT32 i;
678 	boolean forceclose = (node & FORCECLOSE) != 0;
679 
680 	if (node == -1)
681 	{
682 		DEBFILE(M_GetText("Net_CloseConnection: node -1 detected!\n"));
683 		return; // nope, just ignore it
684 	}
685 
686 	node &= ~FORCECLOSE;
687 
688 	if (!node)
689 		return;
690 
691 	if (node < 0 || node >= MAXNETNODES) // prevent invalid nodes from crashing the game
692 	{
693 		DEBFILE(va(M_GetText("Net_CloseConnection: invalid node %d detected!\n"), node));
694 		return;
695 	}
696 
697 	nodes[node].flags |= NF_CLOSE;
698 
699 	// try to Send ack back (two army problem)
700 	if (GetAcktosend(node))
701 	{
702 		Net_SendAcks(node);
703 		Net_SendAcks(node);
704 	}
705 
706 	// check if we are waiting for an ack from this node
707 	for (i = 0; i < MAXACKPACKETS; i++)
708 		if (ackpak[i].acknum && ackpak[i].destinationnode == node)
709 		{
710 			if (!forceclose)
711 				return; // connection will be closed when ack is returned
712 			else
713 				ackpak[i].acknum = 0;
714 		}
715 
716 	InitNode(&nodes[node]);
717 	SV_AbortSendFiles(node);
718 	if (server)
719 		SV_AbortLuaFileTransfer(node);
720 	I_NetFreeNodenum(node);
721 #endif
722 }
723 
724 #ifndef NONET
725 //
726 // Checksum
727 //
NetbufferChecksum(void)728 static UINT32 NetbufferChecksum(void)
729 {
730 	UINT32 c = 0x1234567;
731 	const INT32 l = doomcom->datalength - 4;
732 	const UINT8 *buf = (UINT8 *)netbuffer + 4;
733 	INT32 i;
734 
735 	for (i = 0; i < l; i++, buf++)
736 		c += (*buf) * (i+1);
737 
738 	return LONG(c);
739 }
740 #endif
741 
742 #ifdef DEBUGFILE
743 
fprintfstring(char * s,size_t len)744 static void fprintfstring(char *s, size_t len)
745 {
746 	INT32 mode = 0;
747 	size_t i;
748 
749 	for (i = 0; i < len; i++)
750 		if (s[i] < 32)
751 		{
752 			if (!mode)
753 			{
754 				fprintf(debugfile, "[%d", (UINT8)s[i]);
755 				mode = 1;
756 			}
757 			else
758 				fprintf(debugfile, ",%d", (UINT8)s[i]);
759 		}
760 		else
761 		{
762 			if (mode)
763 			{
764 				fprintf(debugfile, "]");
765 				mode = 0;
766 			}
767 			fprintf(debugfile, "%c", s[i]);
768 		}
769 	if (mode)
770 		fprintf(debugfile, "]");
771 }
772 
fprintfstringnewline(char * s,size_t len)773 static void fprintfstringnewline(char *s, size_t len)
774 {
775 	fprintfstring(s, len);
776 	fprintf(debugfile, "\n");
777 }
778 
779 /// \warning Keep this up-to-date if you add/remove/rename packet types
780 static const char *packettypename[NUMPACKETTYPE] =
781 {
782 	"NOTHING",
783 	"SERVERCFG",
784 	"CLIENTCMD",
785 	"CLIENTMIS",
786 	"CLIENT2CMD",
787 	"CLIENT2MIS",
788 	"NODEKEEPALIVE",
789 	"NODEKEEPALIVEMIS",
790 	"SERVERTICS",
791 	"SERVERREFUSE",
792 	"SERVERSHUTDOWN",
793 	"CLIENTQUIT",
794 
795 	"ASKINFO",
796 	"SERVERINFO",
797 	"PLAYERINFO",
798 	"REQUESTFILE",
799 	"ASKINFOVIAMS",
800 
801 	"WILLRESENDGAMESTATE",
802 	"CANRECEIVEGAMESTATE",
803 	"RECEIVEDGAMESTATE",
804 
805 	"SENDINGLUAFILE",
806 	"ASKLUAFILE",
807 	"HASLUAFILE",
808 
809 	"FILEFRAGMENT",
810 	"FILEACK",
811 	"FILERECEIVED",
812 
813 	"TEXTCMD",
814 	"TEXTCMD2",
815 	"CLIENTJOIN",
816 	"NODETIMEOUT",
817 	"LOGIN",
818 	"PING"
819 };
820 
DebugPrintpacket(const char * header)821 static void DebugPrintpacket(const char *header)
822 {
823 	fprintf(debugfile, "%-12s (node %d,ack %d,ackret %d,size %d) type(%d) : %s\n",
824 		header, doomcom->remotenode, netbuffer->ack, netbuffer->ackreturn, doomcom->datalength,
825 		netbuffer->packettype, packettypename[netbuffer->packettype]);
826 
827 	switch (netbuffer->packettype)
828 	{
829 		case PT_ASKINFO:
830 		case PT_ASKINFOVIAMS:
831 			fprintf(debugfile, "    time %u\n", (tic_t)LONG(netbuffer->u.askinfo.time));
832 			break;
833 		case PT_CLIENTJOIN:
834 			fprintf(debugfile, "    number %d mode %d\n", netbuffer->u.clientcfg.localplayers,
835 				netbuffer->u.clientcfg.mode);
836 			break;
837 		case PT_SERVERTICS:
838 		{
839 			servertics_pak *serverpak = &netbuffer->u.serverpak;
840 			UINT8 *cmd = (UINT8 *)(&serverpak->cmds[serverpak->numslots * serverpak->numtics]);
841 			size_t ntxtcmd = &((UINT8 *)netbuffer)[doomcom->datalength] - cmd;
842 
843 			fprintf(debugfile, "    firsttic %u ply %d tics %d ntxtcmd %s\n    ",
844 				(UINT32)serverpak->starttic, serverpak->numslots, serverpak->numtics, sizeu1(ntxtcmd));
845 			/// \todo Display more readable information about net commands
846 			fprintfstringnewline((char *)cmd, ntxtcmd);
847 			/*fprintfstring((char *)cmd, 3);
848 			if (ntxtcmd > 4)
849 			{
850 				fprintf(debugfile, "[%s]", netxcmdnames[*((cmd) + 3) - 1]);
851 				fprintfstring(((char *)cmd) + 4, ntxtcmd - 4);
852 			}
853 			fprintf(debugfile, "\n");*/
854 			break;
855 		}
856 		case PT_CLIENTCMD:
857 		case PT_CLIENT2CMD:
858 		case PT_CLIENTMIS:
859 		case PT_CLIENT2MIS:
860 		case PT_NODEKEEPALIVE:
861 		case PT_NODEKEEPALIVEMIS:
862 			fprintf(debugfile, "    tic %4u resendfrom %u\n",
863 				(UINT32)ExpandTics(netbuffer->u.clientpak.client_tic, doomcom->remotenode),
864 				(UINT32)ExpandTics (netbuffer->u.clientpak.resendfrom, doomcom->remotenode));
865 			break;
866 		case PT_TEXTCMD:
867 		case PT_TEXTCMD2:
868 			fprintf(debugfile, "    length %d\n    ", netbuffer->u.textcmd[0]);
869 			fprintf(debugfile, "[%s]", netxcmdnames[netbuffer->u.textcmd[1] - 1]);
870 			fprintfstringnewline((char *)netbuffer->u.textcmd + 2, netbuffer->u.textcmd[0] - 1);
871 			break;
872 		case PT_SERVERCFG:
873 			fprintf(debugfile, "    playerslots %d clientnode %d serverplayer %d "
874 				"gametic %u gamestate %d gametype %d modifiedgame %d\n",
875 				netbuffer->u.servercfg.totalslotnum, netbuffer->u.servercfg.clientnode,
876 				netbuffer->u.servercfg.serverplayer, (UINT32)LONG(netbuffer->u.servercfg.gametic),
877 				netbuffer->u.servercfg.gamestate, netbuffer->u.servercfg.gametype,
878 				netbuffer->u.servercfg.modifiedgame);
879 			break;
880 		case PT_SERVERINFO:
881 			fprintf(debugfile, "    '%s' player %d/%d, map %s, filenum %d, time %u \n",
882 				netbuffer->u.serverinfo.servername, netbuffer->u.serverinfo.numberofplayer,
883 				netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.mapname,
884 				netbuffer->u.serverinfo.fileneedednum,
885 				(UINT32)LONG(netbuffer->u.serverinfo.time));
886 			fprintfstringnewline((char *)netbuffer->u.serverinfo.fileneeded,
887 				(UINT8)((UINT8 *)netbuffer + doomcom->datalength
888 				- (UINT8 *)netbuffer->u.serverinfo.fileneeded));
889 			break;
890 		case PT_SERVERREFUSE:
891 			fprintf(debugfile, "    reason %s\n", netbuffer->u.serverrefuse.reason);
892 			break;
893 		case PT_FILEFRAGMENT:
894 			fprintf(debugfile, "    fileid %d datasize %d position %u\n",
895 				netbuffer->u.filetxpak.fileid, (UINT16)SHORT(netbuffer->u.filetxpak.size),
896 				(UINT32)LONG(netbuffer->u.filetxpak.position));
897 			break;
898 		case PT_REQUESTFILE:
899 		default: // write as a raw packet
900 			fprintfstringnewline((char *)netbuffer->u.textcmd,
901 				(UINT8)((UINT8 *)netbuffer + doomcom->datalength - (UINT8 *)netbuffer->u.textcmd));
902 			break;
903 	}
904 }
905 #endif
906 
907 #ifdef PACKETDROP
908 static INT32 packetdropquantity[NUMPACKETTYPE] = {0};
909 static INT32 packetdroprate = 0;
910 
Command_Drop(void)911 void Command_Drop(void)
912 {
913 	INT32 packetquantity;
914 	const char *packetname;
915 	size_t i;
916 
917 	if (COM_Argc() < 2)
918 	{
919 		CONS_Printf("drop <packettype> [quantity]: drop packets\n"
920 					"drop reset: cancel all packet drops\n");
921 		return;
922 	}
923 
924 	if (!(stricmp(COM_Argv(1), "reset") && stricmp(COM_Argv(1), "cancel") && stricmp(COM_Argv(1), "stop")))
925 	{
926 		memset(packetdropquantity, 0, sizeof(packetdropquantity));
927 		return;
928 	}
929 
930 	if (COM_Argc() >= 3)
931 	{
932 		packetquantity = atoi(COM_Argv(2));
933 		if (packetquantity <= 0 && COM_Argv(2)[0] != '0')
934 		{
935 			CONS_Printf("Invalid quantity\n");
936 			return;
937 		}
938 	}
939 	else
940 		packetquantity = -1;
941 
942 	packetname = COM_Argv(1);
943 
944 	if (!(stricmp(packetname, "all") && stricmp(packetname, "any")))
945 		for (i = 0; i < NUMPACKETTYPE; i++)
946 			packetdropquantity[i] = packetquantity;
947 	else
948 	{
949 		for (i = 0; i < NUMPACKETTYPE; i++)
950 			if (!stricmp(packetname, packettypename[i]))
951 			{
952 				packetdropquantity[i] = packetquantity;
953 				return;
954 			}
955 
956 		CONS_Printf("Unknown packet name\n");
957 	}
958 }
959 
Command_Droprate(void)960 void Command_Droprate(void)
961 {
962 	INT32 droprate;
963 
964 	if (COM_Argc() < 2)
965 	{
966 		CONS_Printf("Packet drop rate: %d%%\n", packetdroprate);
967 		return;
968 	}
969 
970 	droprate = atoi(COM_Argv(1));
971 	if ((droprate <= 0 && COM_Argv(1)[0] != '0') || droprate > 100)
972 	{
973 		CONS_Printf("Packet drop rate must be between 0 and 100!\n");
974 		return;
975 	}
976 
977 	packetdroprate = droprate;
978 }
979 
980 #ifndef NONET
ShouldDropPacket(void)981 static boolean ShouldDropPacket(void)
982 {
983 	return (packetdropquantity[netbuffer->packettype])
984 		|| (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100;
985 }
986 #endif
987 #endif
988 
989 //
990 // HSendPacket
991 //
HSendPacket(INT32 node,boolean reliable,UINT8 acknum,size_t packetlength)992 boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength)
993 {
994 	doomcom->datalength = (INT16)(packetlength + BASEPACKETSIZE);
995 	if (node == 0) // Packet is to go back to us
996 	{
997 		if ((rebound_head+1) % MAXREBOUND == rebound_tail)
998 		{
999 #ifdef PARANOIA
1000 			CONS_Debug(DBG_NETPLAY, "No more rebound buf\n");
1001 #endif
1002 			return false;
1003 		}
1004 		netbuffer->ack = netbuffer->ackreturn = 0; // don't hold over values from last packet sent/received
1005 		M_Memcpy(&reboundstore[rebound_head], netbuffer,
1006 			doomcom->datalength);
1007 		reboundsize[rebound_head] = doomcom->datalength;
1008 		rebound_head = (rebound_head+1) % MAXREBOUND;
1009 #ifdef DEBUGFILE
1010 		if (debugfile)
1011 		{
1012 			doomcom->remotenode = (INT16)node;
1013 			DebugPrintpacket("SENDLOCAL");
1014 		}
1015 #endif
1016 		return true;
1017 	}
1018 
1019 	if (!netgame)
1020 		I_Error("Tried to transmit to another node");
1021 
1022 #ifdef NONET
1023 	(void)node;
1024 	(void)reliable;
1025 	(void)acknum;
1026 #else
1027 	// do this before GetFreeAcknum because this function backups
1028 	// the current packet
1029 	doomcom->remotenode = (INT16)node;
1030 	if (doomcom->datalength <= 0)
1031 	{
1032 		DEBFILE("HSendPacket: nothing to send\n");
1033 #ifdef DEBUGFILE
1034 		if (debugfile)
1035 			DebugPrintpacket("TRISEND");
1036 #endif
1037 		return false;
1038 	}
1039 
1040 	if (node < MAXNETNODES) // Can be a broadcast
1041 		netbuffer->ackreturn = GetAcktosend(node);
1042 	else
1043 		netbuffer->ackreturn = 0;
1044 	if (reliable)
1045 	{
1046 		if (I_NetCanSend && !I_NetCanSend())
1047 		{
1048 			if (netbuffer->packettype < PT_CANFAIL)
1049 				GetFreeAcknum(&netbuffer->ack, true);
1050 
1051 			DEBFILE("HSendPacket: Out of bandwidth\n");
1052 			return false;
1053 		}
1054 		else if (!GetFreeAcknum(&netbuffer->ack, false))
1055 			return false;
1056 	}
1057 	else
1058 		netbuffer->ack = acknum;
1059 
1060 	netbuffer->checksum = NetbufferChecksum();
1061 	sendbytes += packetheaderlength + doomcom->datalength; // For stat
1062 
1063 #ifdef PACKETDROP
1064 	// Simulate internet :)
1065 	//if (rand() >= (INT32)(RAND_MAX * (PACKETLOSSRATE / 100.f)))
1066 	if (!ShouldDropPacket())
1067 	{
1068 #endif
1069 #ifdef DEBUGFILE
1070 		if (debugfile)
1071 			DebugPrintpacket("SENT");
1072 #endif
1073 		I_NetSend();
1074 #ifdef PACKETDROP
1075 	}
1076 	else
1077 	{
1078 		if (packetdropquantity[netbuffer->packettype] > 0)
1079 			packetdropquantity[netbuffer->packettype]--;
1080 #ifdef DEBUGFILE
1081 		if (debugfile)
1082 			DebugPrintpacket("NOT SENT");
1083 #endif
1084 	}
1085 #endif
1086 
1087 #endif // ndef NONET
1088 
1089 	return true;
1090 }
1091 
1092 //
1093 // HGetPacket
1094 // Returns false if no packet is waiting
1095 // Check Datalength and checksum
1096 //
HGetPacket(void)1097 boolean HGetPacket(void)
1098 {
1099 	//boolean nodejustjoined;
1100 
1101 	// Get a packet from self
1102 	if (rebound_tail != rebound_head)
1103 	{
1104 		M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]);
1105 		doomcom->datalength = reboundsize[rebound_tail];
1106 		if (netbuffer->packettype == PT_NODETIMEOUT)
1107 			doomcom->remotenode = netbuffer->u.textcmd[0];
1108 		else
1109 			doomcom->remotenode = 0;
1110 
1111 		rebound_tail = (rebound_tail+1) % MAXREBOUND;
1112 #ifdef DEBUGFILE
1113 		if (debugfile)
1114 			DebugPrintpacket("GETLOCAL");
1115 #endif
1116 		return true;
1117 	}
1118 
1119 	if (!netgame)
1120 		return false;
1121 
1122 #ifndef NONET
1123 
1124 	while(true)
1125 	{
1126 		//nodejustjoined = I_NetGet();
1127 		I_NetGet();
1128 
1129 		if (doomcom->remotenode == -1) // No packet received
1130 			return false;
1131 
1132 		getbytes += packetheaderlength + doomcom->datalength; // For stat
1133 
1134 		if (doomcom->remotenode >= MAXNETNODES)
1135 		{
1136 			DEBFILE(va("Received packet from node %d!\n", doomcom->remotenode));
1137 			continue;
1138 		}
1139 
1140 		nodes[doomcom->remotenode].lasttimepacketreceived = I_GetTime();
1141 
1142 		if (netbuffer->checksum != NetbufferChecksum())
1143 		{
1144 			DEBFILE("Bad packet checksum\n");
1145 			//Net_CloseConnection(nodejustjoined ? (doomcom->remotenode | FORCECLOSE) : doomcom->remotenode);
1146 			Net_CloseConnection(doomcom->remotenode);
1147 			continue;
1148 		}
1149 
1150 #ifdef DEBUGFILE
1151 		if (debugfile)
1152 			DebugPrintpacket("GET");
1153 #endif
1154 
1155 		/*// If a new node sends an unexpected packet, just ignore it
1156 		if (nodejustjoined && server
1157 			&& !(netbuffer->packettype == PT_ASKINFO
1158 				|| netbuffer->packettype == PT_SERVERINFO
1159 				|| netbuffer->packettype == PT_PLAYERINFO
1160 				|| netbuffer->packettype == PT_REQUESTFILE
1161 				|| netbuffer->packettype == PT_ASKINFOVIAMS
1162 				|| netbuffer->packettype == PT_CLIENTJOIN))
1163 		{
1164 			DEBFILE(va("New node sent an unexpected %s packet\n", packettypename[netbuffer->packettype]));
1165 			//CONS_Alert(CONS_NOTICE, "New node sent an unexpected %s packet\n", packettypename[netbuffer->packettype]);
1166 			Net_CloseConnection(doomcom->remotenode | FORCECLOSE);
1167 			continue;
1168 		}*/
1169 
1170 		// Proceed the ack and ackreturn field
1171 		if (!Processackpak())
1172 			continue; // discarded (duplicated)
1173 
1174 		// A packet with just ackreturn
1175 		if (netbuffer->packettype == PT_NOTHING)
1176 		{
1177 			GotAcks();
1178 			continue;
1179 		}
1180 		break;
1181 	}
1182 #endif // ndef NONET
1183 
1184 	return true;
1185 }
1186 
Internal_Get(void)1187 static boolean Internal_Get(void)
1188 {
1189 	doomcom->remotenode = -1;
1190 	return false;
1191 }
1192 
Internal_Send(void)1193 FUNCNORETURN static ATTRNORETURN void Internal_Send(void)
1194 {
1195 	I_Error("Send without netgame\n");
1196 }
1197 
Internal_FreeNodenum(INT32 nodenum)1198 static void Internal_FreeNodenum(INT32 nodenum)
1199 {
1200 	(void)nodenum;
1201 }
1202 
I_NetMakeNode(const char * hostname)1203 SINT8 I_NetMakeNode(const char *hostname)
1204 {
1205 	SINT8 newnode = -1;
1206 	if (I_NetMakeNodewPort)
1207 	{
1208 		char *localhostname = strdup(hostname);
1209 		char  *t = localhostname;
1210 		const char *port;
1211 		if (!localhostname)
1212 			return newnode;
1213 		// retrieve portnum from address!
1214 		strtok(localhostname, ":");
1215 		port = strtok(NULL, ":");
1216 
1217 		// remove the port in the hostname as we've it already
1218 		while ((*t != ':') && (*t != '\0'))
1219 			t++;
1220 		*t = '\0';
1221 
1222 		newnode = I_NetMakeNodewPort(localhostname, port);
1223 		free(localhostname);
1224 	}
1225 	return newnode;
1226 }
1227 
D_SetDoomcom(void)1228 void D_SetDoomcom(void)
1229 {
1230 	if (doomcom) return;
1231 	doomcom = Z_Calloc(sizeof (doomcom_t), PU_STATIC, NULL);
1232 	doomcom->id = DOOMCOM_ID;
1233 	doomcom->numslots = doomcom->numnodes = 1;
1234 	doomcom->gametype = 0;
1235 	doomcom->consoleplayer = 0;
1236 	doomcom->extratics = 0;
1237 }
1238 
1239 //
1240 // D_CheckNetGame
1241 // Works out player numbers among the net participants
1242 //
D_CheckNetGame(void)1243 boolean D_CheckNetGame(void)
1244 {
1245 	boolean ret = false;
1246 
1247 	InitAck();
1248 	rebound_tail = rebound_head = 0;
1249 
1250 	statstarttic = I_GetTime();
1251 
1252 	I_NetGet = Internal_Get;
1253 	I_NetSend = Internal_Send;
1254 	I_NetCanSend = NULL;
1255 	I_NetCloseSocket = NULL;
1256 	I_NetFreeNodenum = Internal_FreeNodenum;
1257 	I_NetMakeNodewPort = NULL;
1258 
1259 	hardware_MAXPACKETLENGTH = MAXPACKETLENGTH;
1260 	net_bandwidth = 30000;
1261 	// I_InitNetwork sets doomcom and netgame
1262 	// check and initialize the network driver
1263 	multiplayer = false;
1264 
1265 	// only dos version with external driver will return true
1266 	netgame = I_InitNetwork();
1267 	if (!netgame && !I_NetOpenSocket)
1268 	{
1269 		D_SetDoomcom();
1270 		netgame = I_InitTcpNetwork();
1271 	}
1272 
1273 	if (netgame)
1274 		ret = true;
1275 	if (client && netgame)
1276 		netgame = false;
1277 	server = true; // WTF? server always true???
1278 		// no! The deault mode is server. Client is set elsewhere
1279 		// when the client executes connect command.
1280 	doomcom->ticdup = 1;
1281 
1282 	if (M_CheckParm("-extratic"))
1283 	{
1284 		if (M_IsNextParm())
1285 			doomcom->extratics = (INT16)atoi(M_GetNextParm());
1286 		else
1287 			doomcom->extratics = 1;
1288 		CONS_Printf(M_GetText("Set extratics to %d\n"), doomcom->extratics);
1289 	}
1290 
1291 	if (M_CheckParm("-bandwidth"))
1292 	{
1293 		if (M_IsNextParm())
1294 		{
1295 			net_bandwidth = atoi(M_GetNextParm());
1296 			if (net_bandwidth < 1000)
1297 				net_bandwidth = 1000;
1298 			if (net_bandwidth > 100000)
1299 				hardware_MAXPACKETLENGTH = MAXPACKETLENGTH;
1300 			CONS_Printf(M_GetText("Network bandwidth set to %d\n"), net_bandwidth);
1301 		}
1302 		else
1303 			I_Error("usage: -bandwidth <byte_per_sec>");
1304 	}
1305 
1306 	software_MAXPACKETLENGTH = hardware_MAXPACKETLENGTH;
1307 	if (M_CheckParm("-packetsize"))
1308 	{
1309 		if (M_IsNextParm())
1310 		{
1311 			INT32 p = atoi(M_GetNextParm());
1312 			if (p < 75)
1313 				p = 75;
1314 			if (p > hardware_MAXPACKETLENGTH)
1315 				p = hardware_MAXPACKETLENGTH;
1316 			software_MAXPACKETLENGTH = (UINT16)p;
1317 		}
1318 		else
1319 			I_Error("usage: -packetsize <bytes_per_packet>");
1320 	}
1321 
1322 	if (netgame)
1323 		multiplayer = true;
1324 
1325 	if (doomcom->id != DOOMCOM_ID)
1326 		I_Error("Doomcom buffer invalid!");
1327 	if (doomcom->numnodes > MAXNETNODES)
1328 		I_Error("Too many nodes (%d), max:%d", doomcom->numnodes, MAXNETNODES);
1329 
1330 	netbuffer = (doomdata_t *)(void *)&doomcom->data;
1331 
1332 #ifdef DEBUGFILE
1333 	if (M_CheckParm("-debugfile"))
1334 	{
1335 		char filename[21];
1336 		INT32 k = doomcom->consoleplayer - 1;
1337 		if (M_IsNextParm())
1338 			k = atoi(M_GetNextParm()) - 1;
1339 		while (!debugfile && k < MAXPLAYERS)
1340 		{
1341 			k++;
1342 			sprintf(filename, "debug%d.txt", k);
1343 			debugfile = fopen(va("%s" PATHSEP "%s", srb2home, filename), "w");
1344 		}
1345 		if (debugfile)
1346 			CONS_Printf(M_GetText("debug output to: %s\n"), va("%s" PATHSEP "%s", srb2home, filename));
1347 		else
1348 			CONS_Alert(CONS_WARNING, M_GetText("cannot debug output to file %s!\n"), va("%s" PATHSEP "%s", srb2home, filename));
1349 	}
1350 #endif
1351 
1352 	D_ClientServerInit();
1353 
1354 	return ret;
1355 }
1356 
1357 struct pingcell
1358 {
1359 	INT32 num;
1360 	INT32 ms;
1361 };
1362 
pingcellcmp(const void * va,const void * vb)1363 static int pingcellcmp(const void *va, const void *vb)
1364 {
1365 	const struct pingcell *a, *b;
1366 	a = va;
1367 	b = vb;
1368 	return ( a->ms - b->ms );
1369 }
1370 
1371 /*
1372 New ping command formatted nicely to present ping in
1373 ascending order. And with equally spaced columns.
1374 The caller's ping is presented at the bottom too, for
1375 convenience.
1376 */
1377 
Command_Ping_f(void)1378 void Command_Ping_f(void)
1379 {
1380 	struct pingcell pingv[MAXPLAYERS];
1381 	INT32           pingc;
1382 
1383 	int name_width = 0;
1384 	int   ms_width = 0;
1385 
1386 	int n;
1387 	INT32 i;
1388 
1389 	pingc = 0;
1390 	for (i = 1; i < MAXPLAYERS; ++i)
1391 		if (playeringame[i])
1392 	{
1393 		n = strlen(player_names[i]);
1394 		if (n > name_width)
1395 			name_width = n;
1396 
1397 		n = playerpingtable[i];
1398 		if (n > ms_width)
1399 			ms_width = n;
1400 
1401 		pingv[pingc].num = i;
1402 		pingv[pingc].ms  = playerpingtable[i];
1403 		pingc++;
1404 	}
1405 
1406 	     if (ms_width < 10)  ms_width = 1;
1407 	else if (ms_width < 100) ms_width = 2;
1408 	else                     ms_width = 3;
1409 
1410 	qsort(pingv, pingc, sizeof (struct pingcell), &pingcellcmp);
1411 
1412 	for (i = 0; i < pingc; ++i)
1413 	{
1414 		CONS_Printf("%02d : %-*s %*d ms\n",
1415 				pingv[i].num,
1416 				name_width, player_names[pingv[i].num],
1417 				ms_width,   pingv[i].ms);
1418 	}
1419 
1420 	if (!server && playeringame[consoleplayer])
1421 	{
1422 		CONS_Printf("\nYour ping is %d ms\n", playerpingtable[consoleplayer]);
1423 	}
1424 }
1425 
D_CloseConnection(void)1426 void D_CloseConnection(void)
1427 {
1428 	INT32 i;
1429 
1430 	if (netgame)
1431 	{
1432 		// wait the ackreturn with timout of 5 Sec
1433 		Net_WaitAllAckReceived(5);
1434 
1435 		// close all connection
1436 		for (i = 0; i < MAXNETNODES; i++)
1437 			Net_CloseConnection(i|FORCECLOSE);
1438 
1439 		InitAck();
1440 
1441 		if (I_NetCloseSocket)
1442 			I_NetCloseSocket();
1443 
1444 		I_NetGet = Internal_Get;
1445 		I_NetSend = Internal_Send;
1446 		I_NetCanSend = NULL;
1447 		I_NetCloseSocket = NULL;
1448 		I_NetFreeNodenum = Internal_FreeNodenum;
1449 		I_NetMakeNodewPort = NULL;
1450 		netgame = false;
1451 		addedtogame = false;
1452 	}
1453 
1454 	D_ResetTiccmds();
1455 }
1456