1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1998-2000 by DooM Legacy Team.
4 // Copyright (C) 1999-2020 by Sonic Team Junior.
5 //
6 // This program is free software distributed under the
7 // terms of the GNU General Public License, version 2.
8 // See the 'LICENSE' file for more details.
9 //-----------------------------------------------------------------------------
10 /// \file  d_clisrv.c
11 /// \brief SRB2 Network game communication and protocol, all OS independent parts.
12 
13 #include <time.h>
14 #ifdef __GNUC__
15 #include <unistd.h> //for unlink
16 #endif
17 
18 #include "i_net.h"
19 #include "i_system.h"
20 #include "i_video.h"
21 #include "d_net.h"
22 #include "d_main.h"
23 #include "g_game.h"
24 #include "st_stuff.h"
25 #include "hu_stuff.h"
26 #include "keys.h"
27 #include "g_input.h" // JOY1
28 #include "m_menu.h"
29 #include "console.h"
30 #include "d_netfil.h"
31 #include "byteptr.h"
32 #include "p_saveg.h"
33 #include "z_zone.h"
34 #include "p_local.h"
35 #include "m_misc.h"
36 #include "am_map.h"
37 #include "m_random.h"
38 #include "mserv.h"
39 #include "y_inter.h"
40 #include "r_local.h"
41 #include "m_argv.h"
42 #include "p_setup.h"
43 #include "lzf.h"
44 #include "lua_script.h"
45 #include "lua_hook.h"
46 #include "md5.h"
47 #include "m_perfstats.h"
48 
49 #ifndef NONET
50 // cl loading screen
51 #include "v_video.h"
52 #include "f_finale.h"
53 #endif
54 
55 //
56 // NETWORKING
57 //
58 // gametic is the tic about to (or currently being) run
59 // Server:
60 //   maketic is the tic that hasn't had control made for it yet
61 //   nettics is the tic for each node
62 //   firstticstosend is the lowest value of nettics
63 // Client:
64 //   neededtic is the tic needed by the client to run the game
65 //   firstticstosend is used to optimize a condition
66 // Normally maketic >= gametic > 0
67 
68 #define PREDICTIONQUEUE BACKUPTICS
69 #define PREDICTIONMASK (PREDICTIONQUEUE-1)
70 #define MAX_REASONLENGTH 30
71 
72 boolean server = true; // true or false but !server == client
73 #define client (!server)
74 boolean nodownload = false;
75 boolean serverrunning = false;
76 INT32 serverplayer = 0;
77 char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support)
78 
79 // Server specific vars
80 UINT8 playernode[MAXPLAYERS];
81 char playeraddress[MAXPLAYERS][64];
82 
83 // Minimum timeout for sending the savegame
84 // The actual timeout will be longer depending on the savegame length
85 tic_t jointimeout = (10*TICRATE);
86 static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame?
87 static boolean resendingsavegame[MAXNETNODES]; // Are we resending the savegame?
88 static tic_t savegameresendcooldown[MAXNETNODES]; // How long before we can resend again?
89 static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout?
90 
91 // Incremented by cv_joindelay when a client joins, decremented each tic.
92 // If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled.
93 static tic_t joindelay = 0;
94 
95 UINT16 pingmeasurecount = 1;
96 UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone.
97 UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values.
98 SINT8 nodetoplayer[MAXNETNODES];
99 SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
100 UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen
101 boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
102 tic_t servermaxping = 800; // server's max ping. Defaults to 800
103 static tic_t nettics[MAXNETNODES]; // what tic the client have received
104 static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet
105 static UINT8 nodewaiting[MAXNETNODES];
106 static tic_t firstticstosend; // min of the nettics
107 static tic_t tictoclear = 0; // optimize d_clearticcmd
108 static tic_t maketic;
109 
110 static INT16 consistancy[BACKUPTICS];
111 
112 static UINT8 player_joining = false;
113 UINT8 hu_redownloadinggamestate = 0;
114 
115 UINT8 adminpassmd5[16];
116 boolean adminpasswordset = false;
117 
118 // Client specific
119 static ticcmd_t localcmds;
120 static ticcmd_t localcmds2;
121 static boolean cl_packetmissed;
122 // here it is for the secondary local player (splitscreen)
123 static UINT8 mynode; // my address pointofview server
124 static boolean cl_redownloadinggamestate = false;
125 
126 static UINT8 localtextcmd[MAXTEXTCMD];
127 static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen
128 static tic_t neededtic;
129 SINT8 servernode = 0; // the number of the server node
130 /// \brief do we accept new players?
131 /// \todo WORK!
132 boolean acceptnewnode = true;
133 
134 // engine
135 
136 // Must be a power of two
137 #define TEXTCMD_HASH_SIZE 4
138 
139 typedef struct textcmdplayer_s
140 {
141 	INT32 playernum;
142 	UINT8 cmd[MAXTEXTCMD];
143 	struct textcmdplayer_s *next;
144 } textcmdplayer_t;
145 
146 typedef struct textcmdtic_s
147 {
148 	tic_t tic;
149 	textcmdplayer_t *playercmds[TEXTCMD_HASH_SIZE];
150 	struct textcmdtic_s *next;
151 } textcmdtic_t;
152 
153 ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS];
154 static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL};
155 
156 
157 consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
158 
159 static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}};
160 consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL);
161 
G_DcpyTiccmd(void * dest,const ticcmd_t * src,const size_t n)162 static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n)
163 {
164 	const size_t d = n / sizeof(ticcmd_t);
165 	const size_t r = n % sizeof(ticcmd_t);
166 	UINT8 *ret = dest;
167 
168 	if (r)
169 		M_Memcpy(dest, src, n);
170 	else if (d)
171 		G_MoveTiccmd(dest, src, d);
172 	return ret+n;
173 }
174 
G_ScpyTiccmd(ticcmd_t * dest,void * src,const size_t n)175 static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n)
176 {
177 	const size_t d = n / sizeof(ticcmd_t);
178 	const size_t r = n % sizeof(ticcmd_t);
179 	UINT8 *ret = src;
180 
181 	if (r)
182 		M_Memcpy(dest, src, n);
183 	else if (d)
184 		G_MoveTiccmd(dest, src, d);
185 	return ret+n;
186 }
187 
188 
189 
190 // Some software don't support largest packet
191 // (original sersetup, not exactely, but the probability of sending a packet
192 // of 512 bytes is like 0.1)
193 UINT16 software_MAXPACKETLENGTH;
194 
195 /** Guesses the full value of a tic from its lowest byte, for a specific node
196   *
197   * \param low The lowest byte of the tic value
198   * \param node The node to deduce the tic for
199   * \return The full tic value
200   *
201   */
ExpandTics(INT32 low,INT32 node)202 tic_t ExpandTics(INT32 low, INT32 node)
203 {
204 	INT32 delta;
205 
206 	delta = low - (nettics[node] & UINT8_MAX);
207 
208 	if (delta >= -64 && delta <= 64)
209 		return (nettics[node] & ~UINT8_MAX) + low;
210 	else if (delta > 64)
211 		return (nettics[node] & ~UINT8_MAX) - 256 + low;
212 	else //if (delta < -64)
213 		return (nettics[node] & ~UINT8_MAX) + 256 + low;
214 }
215 
216 // -----------------------------------------------------------------
217 // Some extra data function for handle textcmd buffer
218 // -----------------------------------------------------------------
219 
220 static void (*listnetxcmd[MAXNETXCMD])(UINT8 **p, INT32 playernum);
221 
RegisterNetXCmd(netxcmd_t id,void (* cmd_f)(UINT8 ** p,INT32 playernum))222 void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum))
223 {
224 #ifdef PARANOIA
225 	if (id >= MAXNETXCMD)
226 		I_Error("Command id %d too big", id);
227 	if (listnetxcmd[id] != 0)
228 		I_Error("Command id %d already used", id);
229 #endif
230 	listnetxcmd[id] = cmd_f;
231 }
232 
SendNetXCmd(netxcmd_t id,const void * param,size_t nparam)233 void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam)
234 {
235 	if (localtextcmd[0]+2+nparam > MAXTEXTCMD)
236 	{
237 		// for future reference: if (cv_debug) != debug disabled.
238 		CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[0], sizeu1(nparam));
239 		return;
240 	}
241 	localtextcmd[0]++;
242 	localtextcmd[localtextcmd[0]] = (UINT8)id;
243 	if (param && nparam)
244 	{
245 		M_Memcpy(&localtextcmd[localtextcmd[0]+1], param, nparam);
246 		localtextcmd[0] = (UINT8)(localtextcmd[0] + (UINT8)nparam);
247 	}
248 }
249 
250 // splitscreen player
SendNetXCmd2(netxcmd_t id,const void * param,size_t nparam)251 void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam)
252 {
253 	if (localtextcmd2[0]+2+nparam > MAXTEXTCMD)
254 	{
255 		I_Error("No more place in the buffer for netcmd %d\n",id);
256 		return;
257 	}
258 	localtextcmd2[0]++;
259 	localtextcmd2[localtextcmd2[0]] = (UINT8)id;
260 	if (param && nparam)
261 	{
262 		M_Memcpy(&localtextcmd2[localtextcmd2[0]+1], param, nparam);
263 		localtextcmd2[0] = (UINT8)(localtextcmd2[0] + (UINT8)nparam);
264 	}
265 }
266 
GetFreeXCmdSize(void)267 UINT8 GetFreeXCmdSize(void)
268 {
269 	// -1 for the size and another -1 for the ID.
270 	return (UINT8)(localtextcmd[0] - 2);
271 }
272 
273 // Frees all textcmd memory for the specified tic
D_FreeTextcmd(tic_t tic)274 static void D_FreeTextcmd(tic_t tic)
275 {
276 	textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
277 	textcmdtic_t *textcmdtic = *tctprev;
278 
279 	while (textcmdtic && textcmdtic->tic != tic)
280 	{
281 		tctprev = &textcmdtic->next;
282 		textcmdtic = textcmdtic->next;
283 	}
284 
285 	if (textcmdtic)
286 	{
287 		INT32 i;
288 
289 		// Remove this tic from the list.
290 		*tctprev = textcmdtic->next;
291 
292 		// Free all players.
293 		for (i = 0; i < TEXTCMD_HASH_SIZE; i++)
294 		{
295 			textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i];
296 
297 			while (textcmdplayer)
298 			{
299 				textcmdplayer_t *tcpnext = textcmdplayer->next;
300 				Z_Free(textcmdplayer);
301 				textcmdplayer = tcpnext;
302 			}
303 		}
304 
305 		// Free this tic's own memory.
306 		Z_Free(textcmdtic);
307 	}
308 }
309 
310 // Gets the buffer for the specified ticcmd, or NULL if there isn't one
D_GetExistingTextcmd(tic_t tic,INT32 playernum)311 static UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum)
312 {
313 	textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
314 	while (textcmdtic && textcmdtic->tic != tic) textcmdtic = textcmdtic->next;
315 
316 	// Do we have an entry for the tic? If so, look for player.
317 	if (textcmdtic)
318 	{
319 		textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)];
320 		while (textcmdplayer && textcmdplayer->playernum != playernum) textcmdplayer = textcmdplayer->next;
321 
322 		if (textcmdplayer) return textcmdplayer->cmd;
323 	}
324 
325 	return NULL;
326 }
327 
328 // Gets the buffer for the specified ticcmd, creating one if necessary
D_GetTextcmd(tic_t tic,INT32 playernum)329 static UINT8* D_GetTextcmd(tic_t tic, INT32 playernum)
330 {
331 	textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
332 	textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
333 	textcmdplayer_t *textcmdplayer, **tcpprev;
334 
335 	// Look for the tic.
336 	while (textcmdtic && textcmdtic->tic != tic)
337 	{
338 		tctprev = &textcmdtic->next;
339 		textcmdtic = textcmdtic->next;
340 	}
341 
342 	// If we don't have an entry for the tic, make it.
343 	if (!textcmdtic)
344 	{
345 		textcmdtic = *tctprev = Z_Calloc(sizeof (textcmdtic_t), PU_STATIC, NULL);
346 		textcmdtic->tic = tic;
347 	}
348 
349 	tcpprev = &textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)];
350 	textcmdplayer = *tcpprev;
351 
352 	// Look for the player.
353 	while (textcmdplayer && textcmdplayer->playernum != playernum)
354 	{
355 		tcpprev = &textcmdplayer->next;
356 		textcmdplayer = textcmdplayer->next;
357 	}
358 
359 	// If we don't have an entry for the player, make it.
360 	if (!textcmdplayer)
361 	{
362 		textcmdplayer = *tcpprev = Z_Calloc(sizeof (textcmdplayer_t), PU_STATIC, NULL);
363 		textcmdplayer->playernum = playernum;
364 	}
365 
366 	return textcmdplayer->cmd;
367 }
368 
ExtraDataTicker(void)369 static void ExtraDataTicker(void)
370 {
371 	INT32 i;
372 
373 	for (i = 0; i < MAXPLAYERS; i++)
374 		if (playeringame[i] || i == 0)
375 		{
376 			UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i);
377 
378 			if (bufferstart)
379 			{
380 				UINT8 *curpos = bufferstart;
381 				UINT8 *bufferend = &curpos[curpos[0]+1];
382 
383 				curpos++;
384 				while (curpos < bufferend)
385 				{
386 					if (*curpos < MAXNETXCMD && listnetxcmd[*curpos])
387 					{
388 						const UINT8 id = *curpos;
389 						curpos++;
390 						DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i));
391 						(listnetxcmd[id])(&curpos, i);
392 						DEBFILE("done\n");
393 					}
394 					else
395 					{
396 						if (server)
397 						{
398 							SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
399 							DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic));
400 						}
401 						CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]);
402 						break;
403 					}
404 				}
405 			}
406 		}
407 
408 	// If you are a client, you can safely forget the net commands for this tic
409 	// If you are the server, you need to remember them until every client has been acknowledged,
410 	// because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it
411 	if (client)
412 		D_FreeTextcmd(gametic);
413 }
414 
D_Clearticcmd(tic_t tic)415 static void D_Clearticcmd(tic_t tic)
416 {
417 	INT32 i;
418 
419 	D_FreeTextcmd(tic);
420 
421 	for (i = 0; i < MAXPLAYERS; i++)
422 		netcmds[tic%BACKUPTICS][i].angleturn = 0;
423 
424 	DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS));
425 }
426 
D_ResetTiccmds(void)427 void D_ResetTiccmds(void)
428 {
429 	INT32 i;
430 
431 	memset(&localcmds, 0, sizeof(ticcmd_t));
432 	memset(&localcmds2, 0, sizeof(ticcmd_t));
433 
434 	// Reset the net command list
435 	for (i = 0; i < TEXTCMD_HASH_SIZE; i++)
436 		while (textcmds[i])
437 			D_Clearticcmd(textcmds[i]->tic);
438 }
439 
SendKick(UINT8 playernum,UINT8 msg)440 void SendKick(UINT8 playernum, UINT8 msg)
441 {
442 	UINT8 buf[2];
443 
444 	if (!(server && cv_rejointimeout.value))
445 		msg &= ~KICK_MSG_KEEP_BODY;
446 
447 	buf[0] = playernum;
448 	buf[1] = msg;
449 	SendNetXCmd(XD_KICK, &buf, 2);
450 }
451 
452 // -----------------------------------------------------------------
453 // end of extra data function
454 // -----------------------------------------------------------------
455 
456 // -----------------------------------------------------------------
457 // extra data function for lmps
458 // -----------------------------------------------------------------
459 
460 // if extradatabit is set, after the ziped tic you find this:
461 //
462 //   type   |  description
463 // ---------+--------------
464 //   byte   | size of the extradata
465 //   byte   | the extradata (xd) bits: see XD_...
466 //            with this byte you know what parameter folow
467 // if (xd & XDNAMEANDCOLOR)
468 //   byte   | color
469 //   char[MAXPLAYERNAME] | name of the player
470 // endif
471 // if (xd & XD_WEAPON_PREF)
472 //   byte   | original weapon switch: boolean, true if use the old
473 //          | weapon switch methode
474 //   char[NUMWEAPONS] | the weapon switch priority
475 //   byte   | autoaim: true if use the old autoaim system
476 // endif
477 /*boolean AddLmpExtradata(UINT8 **demo_point, INT32 playernum)
478 {
479 	UINT8 *textcmd = D_GetExistingTextcmd(gametic, playernum);
480 
481 	if (!textcmd)
482 		return false;
483 
484 	M_Memcpy(*demo_point, textcmd, textcmd[0]+1);
485 	*demo_point += textcmd[0]+1;
486 	return true;
487 }
488 
489 void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum)
490 {
491 	UINT8 nextra;
492 	UINT8 *textcmd;
493 
494 	if (!demo_pointer)
495 		return;
496 
497 	textcmd = D_GetTextcmd(gametic, playernum);
498 	nextra = **demo_pointer;
499 	M_Memcpy(textcmd, *demo_pointer, nextra + 1);
500 	// increment demo pointer
501 	*demo_pointer += nextra + 1;
502 }*/
503 
504 // -----------------------------------------------------------------
505 // end extra data function for lmps
506 // -----------------------------------------------------------------
507 
508 static INT16 Consistancy(void);
509 
510 typedef enum
511 {
512 	CL_SEARCHING,
513 	CL_DOWNLOADFILES,
514 	CL_ASKJOIN,
515 	CL_WAITJOINRESPONSE,
516 	CL_DOWNLOADSAVEGAME,
517 	CL_CONNECTED,
518 	CL_ABORTED
519 } cl_mode_t;
520 
521 static void GetPackets(void);
522 
523 static cl_mode_t cl_mode = CL_SEARCHING;
524 
525 #ifndef NONET
526 #define SNAKE_SPEED 5
527 
528 #define SNAKE_NUM_BLOCKS_X 20
529 #define SNAKE_NUM_BLOCKS_Y 10
530 #define SNAKE_BLOCK_SIZE 12
531 #define SNAKE_BORDER_SIZE 12
532 
533 #define SNAKE_MAP_WIDTH  (SNAKE_NUM_BLOCKS_X * SNAKE_BLOCK_SIZE)
534 #define SNAKE_MAP_HEIGHT (SNAKE_NUM_BLOCKS_Y * SNAKE_BLOCK_SIZE)
535 
536 #define SNAKE_LEFT_X ((BASEVIDWIDTH - SNAKE_MAP_WIDTH) / 2 - SNAKE_BORDER_SIZE)
537 #define SNAKE_RIGHT_X (SNAKE_LEFT_X + SNAKE_MAP_WIDTH + SNAKE_BORDER_SIZE * 2 - 1)
538 #define SNAKE_BOTTOM_Y (BASEVIDHEIGHT - 48)
539 #define SNAKE_TOP_Y (SNAKE_BOTTOM_Y - SNAKE_MAP_HEIGHT - SNAKE_BORDER_SIZE * 2 + 1)
540 
541 enum snake_bonustype_s {
542 	SNAKE_BONUS_NONE = 0,
543 	SNAKE_BONUS_SLOW,
544 	SNAKE_BONUS_FAST,
545 	SNAKE_BONUS_GHOST,
546 	SNAKE_BONUS_NUKE,
547 	SNAKE_BONUS_SCISSORS,
548 	SNAKE_BONUS_REVERSE,
549 	SNAKE_BONUS_EGGMAN,
550 	SNAKE_NUM_BONUSES,
551 };
552 
553 static const char *snake_bonuspatches[] = {
554 	NULL,
555 	"DL_SLOW",
556 	"TVSSC0",
557 	"TVIVC0",
558 	"TVARC0",
559 	"DL_SCISSORS",
560 	"TVRCC0",
561 	"TVEGC0",
562 };
563 
564 static const char *snake_backgrounds[] = {
565 	"RVPUMICF",
566 	"FRSTRCKF",
567 	"TAR",
568 	"MMFLRB4",
569 	"RVDARKF1",
570 	"RVZWALF1",
571 	"RVZWALF4",
572 	"RVZWALF5",
573 	"RVZGRS02",
574 	"RVZGRS04",
575 };
576 
577 typedef struct snake_s
578 {
579 	boolean paused;
580 	boolean pausepressed;
581 	tic_t time;
582 	tic_t nextupdate;
583 	boolean gameover;
584 	UINT8 background;
585 
586 	UINT16 snakelength;
587 	enum snake_bonustype_s snakebonus;
588 	tic_t snakebonustime;
589 	UINT8 snakex[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
590 	UINT8 snakey[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
591 	UINT8 snakedir[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y];
592 
593 	UINT8 applex;
594 	UINT8 appley;
595 
596 	enum snake_bonustype_s bonustype;
597 	UINT8 bonusx;
598 	UINT8 bonusy;
599 } snake_t;
600 
601 static snake_t *snake = NULL;
602 
Snake_Initialise(void)603 static void Snake_Initialise(void)
604 {
605 	if (!snake)
606 		snake = malloc(sizeof(snake_t));
607 
608 	snake->paused = false;
609 	snake->pausepressed = false;
610 	snake->time = 0;
611 	snake->nextupdate = SNAKE_SPEED;
612 	snake->gameover = false;
613 	snake->background = M_RandomKey(sizeof(snake_backgrounds) / sizeof(*snake_backgrounds));
614 
615 	snake->snakelength = 1;
616 	snake->snakebonus = SNAKE_BONUS_NONE;
617 	snake->snakex[0] = M_RandomKey(SNAKE_NUM_BLOCKS_X);
618 	snake->snakey[0] = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
619 	snake->snakedir[0] = 0;
620 	snake->snakedir[1] = 0;
621 
622 	snake->applex = M_RandomKey(SNAKE_NUM_BLOCKS_X);
623 	snake->appley = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
624 
625 	snake->bonustype = SNAKE_BONUS_NONE;
626 }
627 
Snake_GetOppositeDir(UINT8 dir)628 static UINT8 Snake_GetOppositeDir(UINT8 dir)
629 {
630 	if (dir == 1 || dir == 3)
631 		return dir + 1;
632 	else if (dir == 2 || dir == 4)
633 		return dir - 1;
634 	else
635 		return 12 + 5 - dir;
636 }
637 
Snake_FindFreeSlot(UINT8 * freex,UINT8 * freey,UINT8 headx,UINT8 heady)638 static void Snake_FindFreeSlot(UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady)
639 {
640 	UINT8 x, y;
641 	UINT16 i;
642 
643 	do
644 	{
645 		x = M_RandomKey(SNAKE_NUM_BLOCKS_X);
646 		y = M_RandomKey(SNAKE_NUM_BLOCKS_Y);
647 
648 		for (i = 0; i < snake->snakelength; i++)
649 			if (x == snake->snakex[i] && y == snake->snakey[i])
650 				break;
651 	} while (i < snake->snakelength || (x == headx && y == heady)
652 		|| (x == snake->applex && y == snake->appley)
653 		|| (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy));
654 
655 	*freex = x;
656 	*freey = y;
657 }
658 
Snake_Handle(void)659 static void Snake_Handle(void)
660 {
661 	UINT8 x, y;
662 	UINT8 oldx, oldy;
663 	UINT16 i;
664 
665 	// Handle retry
666 	if (snake->gameover && (PLAYER1INPUTDOWN(gc_jump) || gamekeydown[KEY_ENTER]))
667 	{
668 		Snake_Initialise();
669 		snake->pausepressed = true; // Avoid accidental pause on respawn
670 	}
671 
672 	// Handle pause
673 	if (PLAYER1INPUTDOWN(gc_pause) || gamekeydown[KEY_ENTER])
674 	{
675 		if (!snake->pausepressed)
676 			snake->paused = !snake->paused;
677 		snake->pausepressed = true;
678 	}
679 	else
680 		snake->pausepressed = false;
681 
682 	if (snake->paused)
683 		return;
684 
685 	snake->time++;
686 
687 	x = snake->snakex[0];
688 	y = snake->snakey[0];
689 	oldx = snake->snakex[1];
690 	oldy = snake->snakey[1];
691 
692 	// Update direction
693 	if (gamekeydown[KEY_LEFTARROW])
694 	{
695 		if (snake->snakelength < 2 || x <= oldx)
696 			snake->snakedir[0] = 1;
697 	}
698 	else if (gamekeydown[KEY_RIGHTARROW])
699 	{
700 		if (snake->snakelength < 2 || x >= oldx)
701 			snake->snakedir[0] = 2;
702 	}
703 	else if (gamekeydown[KEY_UPARROW])
704 	{
705 		if (snake->snakelength < 2 || y <= oldy)
706 			snake->snakedir[0] = 3;
707 	}
708 	else if (gamekeydown[KEY_DOWNARROW])
709 	{
710 		if (snake->snakelength < 2 || y >= oldy)
711 			snake->snakedir[0] = 4;
712 	}
713 
714 	if (snake->snakebonustime)
715 	{
716 		snake->snakebonustime--;
717 		if (!snake->snakebonustime)
718 			snake->snakebonus = SNAKE_BONUS_NONE;
719 	}
720 
721 	snake->nextupdate--;
722 	if (snake->nextupdate)
723 		return;
724 	if (snake->snakebonus == SNAKE_BONUS_SLOW)
725 		snake->nextupdate = SNAKE_SPEED * 2;
726 	else if (snake->snakebonus == SNAKE_BONUS_FAST)
727 		snake->nextupdate = SNAKE_SPEED * 2 / 3;
728 	else
729 		snake->nextupdate = SNAKE_SPEED;
730 
731 	if (snake->gameover)
732 		return;
733 
734 	// Find new position
735 	switch (snake->snakedir[0])
736 	{
737 		case 1:
738 			if (x > 0)
739 				x--;
740 			else
741 				snake->gameover = true;
742 			break;
743 		case 2:
744 			if (x < SNAKE_NUM_BLOCKS_X - 1)
745 				x++;
746 			else
747 				snake->gameover = true;
748 			break;
749 		case 3:
750 			if (y > 0)
751 				y--;
752 			else
753 				snake->gameover = true;
754 			break;
755 		case 4:
756 			if (y < SNAKE_NUM_BLOCKS_Y - 1)
757 				y++;
758 			else
759 				snake->gameover = true;
760 			break;
761 	}
762 
763 	// Check collision with snake
764 	if (snake->snakebonus != SNAKE_BONUS_GHOST)
765 		for (i = 1; i < snake->snakelength - 1; i++)
766 			if (x == snake->snakex[i] && y == snake->snakey[i])
767 			{
768 				if (snake->snakebonus == SNAKE_BONUS_SCISSORS)
769 				{
770 					snake->snakebonus = SNAKE_BONUS_NONE;
771 					snake->snakelength = i;
772 					S_StartSound(NULL, sfx_adderr);
773 				}
774 				else
775 					snake->gameover = true;
776 			}
777 
778 	if (snake->gameover)
779 	{
780 		S_StartSound(NULL, sfx_lose);
781 		return;
782 	}
783 
784 	// Check collision with apple
785 	if (x == snake->applex && y == snake->appley)
786 	{
787 		if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y)
788 		{
789 			snake->snakelength++;
790 			snake->snakex  [snake->snakelength - 1] = snake->snakex  [snake->snakelength - 2];
791 			snake->snakey  [snake->snakelength - 1] = snake->snakey  [snake->snakelength - 2];
792 			snake->snakedir[snake->snakelength - 1] = snake->snakedir[snake->snakelength - 2];
793 		}
794 
795 		// Spawn new apple
796 		Snake_FindFreeSlot(&snake->applex, &snake->appley, x, y);
797 
798 		// Spawn new bonus
799 		if (!(snake->snakelength % 5))
800 		{
801 			do
802 			{
803 				snake->bonustype = M_RandomKey(SNAKE_NUM_BONUSES - 1) + 1;
804 			} while (snake->snakelength > SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y * 3 / 4
805 				&& (snake->bonustype == SNAKE_BONUS_EGGMAN || snake->bonustype == SNAKE_BONUS_FAST || snake->bonustype == SNAKE_BONUS_REVERSE));
806 
807 			Snake_FindFreeSlot(&snake->bonusx, &snake->bonusy, x, y);
808 		}
809 
810 		S_StartSound(NULL, sfx_s3k6b);
811 	}
812 
813 	if (snake->snakelength > 1 && snake->snakedir[0])
814 	{
815 		UINT8 dir = snake->snakedir[0];
816 
817 		oldx = snake->snakex[1];
818 		oldy = snake->snakey[1];
819 
820 		// Move
821 		for (i = snake->snakelength - 1; i > 0; i--)
822 		{
823 			snake->snakex[i] = snake->snakex[i - 1];
824 			snake->snakey[i] = snake->snakey[i - 1];
825 			snake->snakedir[i] = snake->snakedir[i - 1];
826 		}
827 
828 		// Handle corners
829 		if      (x < oldx && dir == 3)
830 			dir = 5;
831 		else if (x > oldx && dir == 3)
832 			dir = 6;
833 		else if (x < oldx && dir == 4)
834 			dir = 7;
835 		else if (x > oldx && dir == 4)
836 			dir = 8;
837 		else if (y < oldy && dir == 1)
838 			dir = 9;
839 		else if (y < oldy && dir == 2)
840 			dir = 10;
841 		else if (y > oldy && dir == 1)
842 			dir = 11;
843 		else if (y > oldy && dir == 2)
844 			dir = 12;
845 		snake->snakedir[1] = dir;
846 	}
847 
848 	snake->snakex[0] = x;
849 	snake->snakey[0] = y;
850 
851 	// Check collision with bonus
852 	if (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)
853 	{
854 		S_StartSound(NULL, sfx_ncchip);
855 
856 		switch (snake->bonustype)
857 		{
858 		case SNAKE_BONUS_SLOW:
859 			snake->snakebonus = SNAKE_BONUS_SLOW;
860 			snake->snakebonustime = 20 * TICRATE;
861 			break;
862 		case SNAKE_BONUS_FAST:
863 			snake->snakebonus = SNAKE_BONUS_FAST;
864 			snake->snakebonustime = 20 * TICRATE;
865 			break;
866 		case SNAKE_BONUS_GHOST:
867 			snake->snakebonus = SNAKE_BONUS_GHOST;
868 			snake->snakebonustime = 10 * TICRATE;
869 			break;
870 		case SNAKE_BONUS_NUKE:
871 			for (i = 0; i < snake->snakelength; i++)
872 			{
873 				snake->snakex  [i] = snake->snakex  [0];
874 				snake->snakey  [i] = snake->snakey  [0];
875 				snake->snakedir[i] = snake->snakedir[0];
876 			}
877 
878 			S_StartSound(NULL, sfx_bkpoof);
879 			break;
880 		case SNAKE_BONUS_SCISSORS:
881 			snake->snakebonus = SNAKE_BONUS_SCISSORS;
882 			snake->snakebonustime = 60 * TICRATE;
883 			break;
884 		case SNAKE_BONUS_REVERSE:
885 			for (i = 0; i < (snake->snakelength + 1) / 2; i++)
886 			{
887 				UINT16 i2 = snake->snakelength - 1 - i;
888 				UINT8 tmpx   = snake->snakex  [i];
889 				UINT8 tmpy   = snake->snakey  [i];
890 				UINT8 tmpdir = snake->snakedir[i];
891 
892 				// Swap first segment with last segment
893 				snake->snakex  [i] = snake->snakex  [i2];
894 				snake->snakey  [i] = snake->snakey  [i2];
895 				snake->snakedir[i] = Snake_GetOppositeDir(snake->snakedir[i2]);
896 				snake->snakex  [i2] = tmpx;
897 				snake->snakey  [i2] = tmpy;
898 				snake->snakedir[i2] = Snake_GetOppositeDir(tmpdir);
899 			}
900 
901 			snake->snakedir[0] = 0;
902 
903 			S_StartSound(NULL, sfx_gravch);
904 			break;
905 		default:
906 			if (snake->snakebonus != SNAKE_BONUS_GHOST)
907 			{
908 				snake->gameover = true;
909 				S_StartSound(NULL, sfx_lose);
910 			}
911 		}
912 
913 		snake->bonustype = SNAKE_BONUS_NONE;
914 	}
915 }
916 
Snake_Draw(void)917 static void Snake_Draw(void)
918 {
919 	INT16 i;
920 
921 	// Background
922 	V_DrawFlatFill(
923 		SNAKE_LEFT_X + SNAKE_BORDER_SIZE,
924 		SNAKE_TOP_Y  + SNAKE_BORDER_SIZE,
925 		SNAKE_MAP_WIDTH,
926 		SNAKE_MAP_HEIGHT,
927 		W_GetNumForName(snake_backgrounds[snake->background])
928 	);
929 
930 	// Borders
931 	V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Top
932 	V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_TOP_Y, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Right
933 	V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE, SNAKE_TOP_Y + SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Bottom
934 	V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y + SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Left
935 
936 	// Apple
937 	V_DrawFixedPatch(
938 		(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->applex * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
939 		(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->appley * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
940 		FRACUNIT / 4,
941 		0,
942 		W_CachePatchLongName("DL_APPLE", PU_HUDGFX),
943 		NULL
944 	);
945 
946 	// Bonus
947 	if (snake->bonustype != SNAKE_BONUS_NONE)
948 		V_DrawFixedPatch(
949 			(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->bonusx * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2    ) * FRACUNIT,
950 			(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->bonusy * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 + 4) * FRACUNIT,
951 			FRACUNIT / 2,
952 			0,
953 			W_CachePatchLongName(snake_bonuspatches[snake->bonustype], PU_HUDGFX),
954 			NULL
955 		);
956 
957 	// Snake
958 	if (!snake->gameover || snake->time % 8 < 8 / 2) // Blink if game over
959 	{
960 		for (i = snake->snakelength - 1; i >= 0; i--)
961 		{
962 			const char *patchname;
963 			UINT8 dir = snake->snakedir[i];
964 
965 			if (i == 0) // Head
966 			{
967 				switch (dir)
968 				{
969 					case  1: patchname = "DL_SNAKEHEAD_L"; break;
970 					case  2: patchname = "DL_SNAKEHEAD_R"; break;
971 					case  3: patchname = "DL_SNAKEHEAD_T"; break;
972 					case  4: patchname = "DL_SNAKEHEAD_B"; break;
973 					default: patchname = "DL_SNAKEHEAD_M";
974 				}
975 			}
976 			else // Body
977 			{
978 				switch (dir)
979 				{
980 					case  1: patchname = "DL_SNAKEBODY_L"; break;
981 					case  2: patchname = "DL_SNAKEBODY_R"; break;
982 					case  3: patchname = "DL_SNAKEBODY_T"; break;
983 					case  4: patchname = "DL_SNAKEBODY_B"; break;
984 					case  5: patchname = "DL_SNAKEBODY_LT"; break;
985 					case  6: patchname = "DL_SNAKEBODY_RT"; break;
986 					case  7: patchname = "DL_SNAKEBODY_LB"; break;
987 					case  8: patchname = "DL_SNAKEBODY_RB"; break;
988 					case  9: patchname = "DL_SNAKEBODY_TL"; break;
989 					case 10: patchname = "DL_SNAKEBODY_TR"; break;
990 					case 11: patchname = "DL_SNAKEBODY_BL"; break;
991 					case 12: patchname = "DL_SNAKEBODY_BR"; break;
992 					default: patchname = "DL_SNAKEBODY_B";
993 				}
994 			}
995 
996 			V_DrawFixedPatch(
997 				(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->snakex[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
998 				(SNAKE_TOP_Y  + SNAKE_BORDER_SIZE + snake->snakey[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT,
999 				i == 0 && dir == 0 ? FRACUNIT / 5 : FRACUNIT / 2,
1000 				snake->snakebonus == SNAKE_BONUS_GHOST ? V_TRANSLUCENT : 0,
1001 				W_CachePatchLongName(patchname, PU_HUDGFX),
1002 				NULL
1003 			);
1004 		}
1005 	}
1006 
1007 	// Length
1008 	V_DrawString(SNAKE_RIGHT_X + 4, SNAKE_TOP_Y, V_MONOSPACE, va("%u", snake->snakelength));
1009 
1010 	// Bonus
1011 	if (snake->snakebonus != SNAKE_BONUS_NONE
1012 	&& (snake->snakebonustime >= 3 * TICRATE || snake->time % 4 < 4 / 2))
1013 		V_DrawFixedPatch(
1014 			(SNAKE_RIGHT_X + 10) * FRACUNIT,
1015 			(SNAKE_TOP_Y + 24) * FRACUNIT,
1016 			FRACUNIT / 2,
1017 			0,
1018 			W_CachePatchLongName(snake_bonuspatches[snake->snakebonus], PU_HUDGFX),
1019 			NULL
1020 		);
1021 }
1022 
1023 //
1024 // CL_DrawConnectionStatus
1025 //
1026 // Keep the local client informed of our status.
1027 //
CL_DrawConnectionStatus(void)1028 static inline void CL_DrawConnectionStatus(void)
1029 {
1030 	INT32 ccstime = I_GetTime();
1031 
1032 	// Draw background fade
1033 	if (!menuactive) // menu already draws its own fade
1034 		V_DrawFadeScreen(0xFF00, 16); // force default
1035 
1036 	// Draw the bottom box.
1037 	M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1);
1038 	V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort");
1039 
1040 	if (cl_mode != CL_DOWNLOADFILES)
1041 	{
1042 		INT32 i, animtime = ((ccstime / 4) & 15) + 16;
1043 		UINT8 palstart = (cl_mode == CL_SEARCHING) ? 32 : 96;
1044 		// 15 pal entries total.
1045 		const char *cltext;
1046 
1047 		if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1))
1048 			for (i = 0; i < 16; ++i)
1049 				V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15));
1050 
1051 		switch (cl_mode)
1052 		{
1053 			case CL_DOWNLOADSAVEGAME:
1054 				if (lastfilenum != -1)
1055 				{
1056 					UINT32 currentsize = fileneeded[lastfilenum].currentsize;
1057 					UINT32 totalsize = fileneeded[lastfilenum].totalsize;
1058 					INT32 dldlength;
1059 
1060 					cltext = M_GetText("Downloading game state...");
1061 					Net_GetNetStat();
1062 
1063 					dldlength = (INT32)((currentsize/(double)totalsize) * 256);
1064 					if (dldlength > 256)
1065 						dldlength = 256;
1066 					V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111);
1067 					V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96);
1068 
1069 					V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
1070 						va(" %4uK/%4uK",currentsize>>10,totalsize>>10));
1071 
1072 					V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
1073 						va("%3.1fK/s ", ((double)getbps)/1024));
1074 				}
1075 				else
1076 					cltext = M_GetText("Waiting to download game state...");
1077 				break;
1078 			case CL_ASKJOIN:
1079 			case CL_WAITJOINRESPONSE:
1080 				cltext = M_GetText("Requesting to join...");
1081 				break;
1082 			default:
1083 				cltext = M_GetText("Connecting to server...");
1084 				break;
1085 		}
1086 		V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, cltext);
1087 	}
1088 	else
1089 	{
1090 		if (lastfilenum != -1)
1091 		{
1092 			INT32 dldlength;
1093 			static char tempname[28];
1094 			fileneeded_t *file = &fileneeded[lastfilenum];
1095 			char *filename = file->filename;
1096 
1097 			Snake_Draw();
1098 
1099 			Net_GetNetStat();
1100 			dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256);
1101 			if (dldlength > 256)
1102 				dldlength = 256;
1103 			V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111);
1104 			V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96);
1105 
1106 			memset(tempname, 0, sizeof(tempname));
1107 			// offset filename to just the name only part
1108 			filename += strlen(filename) - nameonlylength(filename);
1109 
1110 			if (strlen(filename) > sizeof(tempname)-1) // too long to display fully
1111 			{
1112 				size_t endhalfpos = strlen(filename)-10;
1113 				// display as first 14 chars + ... + last 10 chars
1114 				// which should add up to 27 if our math(s) is correct
1115 				snprintf(tempname, sizeof(tempname), "%.14s...%.10s", filename, filename+endhalfpos);
1116 			}
1117 			else // we can copy the whole thing in safely
1118 			{
1119 				strncpy(tempname, filename, sizeof(tempname)-1);
1120 			}
1121 
1122 			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP,
1123 				va(M_GetText("Downloading \"%s\""), tempname));
1124 			V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
1125 				va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10));
1126 			V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
1127 				va("%3.1fK/s ", ((double)getbps)/1024));
1128 		}
1129 		else
1130 			V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP,
1131 				M_GetText("Waiting to download files..."));
1132 	}
1133 }
1134 #endif
1135 
1136 /** Sends a special packet to declare how many players in local
1137   * Used only in arbitratrenetstart()
1138   * Sends a PT_CLIENTJOIN packet to the server
1139   *
1140   * \return True if the packet was successfully sent
1141   * \todo Improve the description...
1142   *       Because to be honest, I have no idea what arbitratrenetstart is...
1143   *       Is it even used...?
1144   *
1145   */
CL_SendJoin(void)1146 static boolean CL_SendJoin(void)
1147 {
1148 	UINT8 localplayers = 1;
1149 	if (netgame)
1150 		CONS_Printf(M_GetText("Sending join request...\n"));
1151 	netbuffer->packettype = PT_CLIENTJOIN;
1152 
1153 	if (splitscreen || botingame)
1154 		localplayers++;
1155 	netbuffer->u.clientcfg.localplayers = localplayers;
1156 	netbuffer->u.clientcfg._255 = 255;
1157 	netbuffer->u.clientcfg.packetversion = PACKETVERSION;
1158 	netbuffer->u.clientcfg.version = VERSION;
1159 	netbuffer->u.clientcfg.subversion = SUBVERSION;
1160 	strncpy(netbuffer->u.clientcfg.application, SRB2APPLICATION,
1161 			sizeof netbuffer->u.clientcfg.application);
1162 
1163 	CleanupPlayerName(consoleplayer, cv_playername.zstring);
1164 	if (splitscreen)
1165 		CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */
1166 
1167 	strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME);
1168 	strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME);
1169 
1170 	return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
1171 }
1172 
FindRejoinerNum(SINT8 node)1173 static INT32 FindRejoinerNum(SINT8 node)
1174 {
1175 	char strippednodeaddress[64];
1176 	const char *nodeaddress;
1177 	char *port;
1178 	INT32 i;
1179 
1180 	// Make sure there is no dead dress before proceeding to the stripping
1181 	if (!I_GetNodeAddress)
1182 		return -1;
1183 	nodeaddress = I_GetNodeAddress(node);
1184 	if (!nodeaddress)
1185 		return -1;
1186 
1187 	// Strip the address of its port
1188 	strcpy(strippednodeaddress, nodeaddress);
1189 	port = strchr(strippednodeaddress, ':');
1190 	if (port)
1191 		*port = '\0';
1192 
1193 	// Check if any player matches the stripped address
1194 	for (i = 0; i < MAXPLAYERS; i++)
1195 	{
1196 		if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX
1197 		&& !strcmp(playeraddress[i], strippednodeaddress))
1198 			return i;
1199 	}
1200 
1201 	return -1;
1202 }
1203 
SV_SendServerInfo(INT32 node,tic_t servertime)1204 static void SV_SendServerInfo(INT32 node, tic_t servertime)
1205 {
1206 	UINT8 *p;
1207 
1208 	netbuffer->packettype = PT_SERVERINFO;
1209 	netbuffer->u.serverinfo._255 = 255;
1210 	netbuffer->u.serverinfo.packetversion = PACKETVERSION;
1211 	netbuffer->u.serverinfo.version = VERSION;
1212 	netbuffer->u.serverinfo.subversion = SUBVERSION;
1213 	strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION,
1214 			sizeof netbuffer->u.serverinfo.application);
1215 	// return back the time value so client can compute their ping
1216 	netbuffer->u.serverinfo.time = (tic_t)LONG(servertime);
1217 	netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime);
1218 
1219 	netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
1220 	netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
1221 
1222 	if (!node || FindRejoinerNum(node) != -1)
1223 		netbuffer->u.serverinfo.refusereason = 0;
1224 	else if (!cv_allownewplayer.value)
1225 		netbuffer->u.serverinfo.refusereason = 1;
1226 	else if (D_NumPlayers() >= cv_maxplayers.value)
1227 		netbuffer->u.serverinfo.refusereason = 2;
1228 	else
1229 		netbuffer->u.serverinfo.refusereason = 0;
1230 
1231 	strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype],
1232 			sizeof netbuffer->u.serverinfo.gametypename);
1233 	netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
1234 	netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
1235 	netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated;
1236 	strncpy(netbuffer->u.serverinfo.servername, cv_servername.string,
1237 		MAXSERVERNAME);
1238 	strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7);
1239 
1240 	M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16);
1241 
1242 	memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle);
1243 
1244 	if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl)
1245 	{
1246 		char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle;
1247 		while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0')
1248 		{
1249 			if (!(*read & 0x80))
1250 			{
1251 				*writ = toupper(*read);
1252 				writ++;
1253 			}
1254 			read++;
1255 		}
1256 		*writ = '\0';
1257 		//strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33);
1258 	}
1259 	else
1260 		strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32);
1261 
1262 	if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
1263 		netbuffer->u.serverinfo.iszone = 1;
1264 	else
1265 		netbuffer->u.serverinfo.iszone = 0;
1266 
1267 	if (mapheaderinfo[gamemap-1])
1268 		netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum;
1269 
1270 	p = PutFileNeeded();
1271 
1272 	HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
1273 }
1274 
SV_SendPlayerInfo(INT32 node)1275 static void SV_SendPlayerInfo(INT32 node)
1276 {
1277 	UINT8 i;
1278 	netbuffer->packettype = PT_PLAYERINFO;
1279 
1280 	for (i = 0; i < MAXPLAYERS; i++)
1281 	{
1282 		if (!playeringame[i])
1283 		{
1284 			netbuffer->u.playerinfo[i].num = 255; // This slot is empty.
1285 			continue;
1286 		}
1287 
1288 		netbuffer->u.playerinfo[i].num = i;
1289 		strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1);
1290 		netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0';
1291 
1292 		//fetch IP address
1293 		//No, don't do that, you fuckface.
1294 		memset(netbuffer->u.playerinfo[i].address, 0, 4);
1295 
1296 		if (G_GametypeHasTeams())
1297 		{
1298 			if (!players[i].ctfteam)
1299 				netbuffer->u.playerinfo[i].team = 255;
1300 			else
1301 				netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam;
1302 		}
1303 		else
1304 		{
1305 			if (players[i].spectator)
1306 				netbuffer->u.playerinfo[i].team = 255;
1307 			else
1308 				netbuffer->u.playerinfo[i].team = 0;
1309 		}
1310 
1311 		netbuffer->u.playerinfo[i].score = LONG(players[i].score);
1312 		netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE));
1313 		netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin
1314 #ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself
1315 		% 3
1316 #endif
1317 		);
1318 
1319 		// Extra data
1320 		netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor;
1321 
1322 		if (players[i].pflags & PF_TAGIT)
1323 			netbuffer->u.playerinfo[i].data |= 0x20;
1324 
1325 		if (players[i].gotflag)
1326 			netbuffer->u.playerinfo[i].data |= 0x40;
1327 
1328 		if (players[i].powers[pw_super])
1329 			netbuffer->u.playerinfo[i].data |= 0x80;
1330 	}
1331 
1332 	HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS);
1333 }
1334 
1335 /** Sends a PT_SERVERCFG packet
1336   *
1337   * \param node The destination
1338   * \return True if the packet was successfully sent
1339   *
1340   */
SV_SendServerConfig(INT32 node)1341 static boolean SV_SendServerConfig(INT32 node)
1342 {
1343 	boolean waspacketsent;
1344 
1345 	netbuffer->packettype = PT_SERVERCFG;
1346 
1347 	netbuffer->u.servercfg.version = VERSION;
1348 	netbuffer->u.servercfg.subversion = SUBVERSION;
1349 
1350 	netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer;
1351 	netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots);
1352 	netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic);
1353 	netbuffer->u.servercfg.clientnode = (UINT8)node;
1354 	netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
1355 	netbuffer->u.servercfg.gametype = (UINT8)gametype;
1356 	netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
1357 
1358 	memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
1359 
1360 	{
1361 		const size_t len = sizeof (serverconfig_pak);
1362 
1363 #ifdef DEBUGFILE
1364 		if (debugfile)
1365 		{
1366 			fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n",
1367 				sizeu1(len), node);
1368 		}
1369 #endif
1370 
1371 		waspacketsent = HSendPacket(node, true, 0, len);
1372 	}
1373 
1374 #ifdef DEBUGFILE
1375 	if (debugfile)
1376 	{
1377 		if (waspacketsent)
1378 		{
1379 			fprintf(debugfile, "ServerConfig Packet was sent\n");
1380 		}
1381 		else
1382 		{
1383 			fprintf(debugfile, "ServerConfig Packet could not be sent right now\n");
1384 		}
1385 	}
1386 #endif
1387 
1388 	return waspacketsent;
1389 }
1390 
1391 #ifndef NONET
1392 #define SAVEGAMESIZE (768*1024)
1393 
SV_ResendingSavegameToAnyone(void)1394 static boolean SV_ResendingSavegameToAnyone(void)
1395 {
1396 	INT32 i;
1397 
1398 	for (i = 0; i < MAXNETNODES; i++)
1399 		if (resendingsavegame[i])
1400 			return true;
1401 	return false;
1402 }
1403 
SV_SendSaveGame(INT32 node,boolean resending)1404 static void SV_SendSaveGame(INT32 node, boolean resending)
1405 {
1406 	size_t length, compressedlen;
1407 	UINT8 *savebuffer;
1408 	UINT8 *compressedsave;
1409 	UINT8 *buffertosend;
1410 
1411 	// first save it in a malloced buffer
1412 	savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
1413 	if (!savebuffer)
1414 	{
1415 		CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
1416 		return;
1417 	}
1418 
1419 	// Leave room for the uncompressed length.
1420 	save_p = savebuffer + sizeof(UINT32);
1421 
1422 	P_SaveNetGame(resending);
1423 
1424 	length = save_p - savebuffer;
1425 	if (length > SAVEGAMESIZE)
1426 	{
1427 		free(savebuffer);
1428 		save_p = NULL;
1429 		I_Error("Savegame buffer overrun");
1430 	}
1431 
1432 	// Allocate space for compressed save: one byte fewer than for the
1433 	// uncompressed data to ensure that the compression is worthwhile.
1434 	compressedsave = malloc(length - 1);
1435 	if (!compressedsave)
1436 	{
1437 		CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
1438 		return;
1439 	}
1440 
1441 	// Attempt to compress it.
1442 	if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1)))
1443 	{
1444 		// Compressing succeeded; send compressed data
1445 
1446 		free(savebuffer);
1447 
1448 		// State that we're compressed.
1449 		buffertosend = compressedsave;
1450 		WRITEUINT32(compressedsave, length - sizeof(UINT32));
1451 		length = compressedlen + sizeof(UINT32);
1452 	}
1453 	else
1454 	{
1455 		// Compression failed to make it smaller; send original
1456 
1457 		free(compressedsave);
1458 
1459 		// State that we're not compressed
1460 		buffertosend = savebuffer;
1461 		WRITEUINT32(savebuffer, 0);
1462 	}
1463 
1464 	AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0);
1465 	save_p = NULL;
1466 
1467 	// Remember when we started sending the savegame so we can handle timeouts
1468 	sendingsavegame[node] = true;
1469 	freezetimeout[node] = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte
1470 }
1471 
1472 #ifdef DUMPCONSISTENCY
1473 #define TMPSAVENAME "badmath.sav"
1474 static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
1475 
SV_SavedGame(void)1476 static void SV_SavedGame(void)
1477 {
1478 	size_t length;
1479 	UINT8 *savebuffer;
1480 	char tmpsave[256];
1481 
1482 	if (!cv_dumpconsistency.value)
1483 		return;
1484 
1485 	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
1486 
1487 	// first save it in a malloced buffer
1488 	save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
1489 	if (!save_p)
1490 	{
1491 		CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
1492 		return;
1493 	}
1494 
1495 	P_SaveNetGame(false);
1496 
1497 	length = save_p - savebuffer;
1498 	if (length > SAVEGAMESIZE)
1499 	{
1500 		free(savebuffer);
1501 		save_p = NULL;
1502 		I_Error("Savegame buffer overrun");
1503 	}
1504 
1505 	// then save it!
1506 	if (!FIL_WriteFile(tmpsave, savebuffer, length))
1507 		CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave);
1508 
1509 	free(savebuffer);
1510 	save_p = NULL;
1511 }
1512 
1513 #undef  TMPSAVENAME
1514 #endif
1515 #define TMPSAVENAME "$$$.sav"
1516 
1517 
CL_LoadReceivedSavegame(boolean reloading)1518 static void CL_LoadReceivedSavegame(boolean reloading)
1519 {
1520 	UINT8 *savebuffer = NULL;
1521 	size_t length, decompressedlen;
1522 	char tmpsave[256];
1523 
1524 	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
1525 
1526 	length = FIL_ReadFile(tmpsave, &savebuffer);
1527 
1528 	CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length));
1529 	if (!length)
1530 	{
1531 		I_Error("Can't read savegame sent");
1532 		return;
1533 	}
1534 
1535 	save_p = savebuffer;
1536 
1537 	// Decompress saved game if necessary.
1538 	decompressedlen = READUINT32(save_p);
1539 	if(decompressedlen > 0)
1540 	{
1541 		UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL);
1542 		lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen);
1543 		Z_Free(savebuffer);
1544 		save_p = savebuffer = decompressedbuffer;
1545 	}
1546 
1547 	paused = false;
1548 	demoplayback = false;
1549 	titlemapinaction = TITLEMAP_OFF;
1550 	titledemo = false;
1551 	automapactive = false;
1552 
1553 	// load a base level
1554 	if (P_LoadNetGame(reloading))
1555 	{
1556 		const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum;
1557 		CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap));
1558 		if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, ""))
1559 		{
1560 			CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl);
1561 			if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
1562 				CONS_Printf(M_GetText(" Zone"));
1563 			if (actnum > 0)
1564 				CONS_Printf(" %2d", actnum);
1565 		}
1566 		CONS_Printf("\"\n");
1567 	}
1568 	else
1569 	{
1570 		CONS_Alert(CONS_ERROR, M_GetText("Can't load the level!\n"));
1571 	}
1572 
1573 	// done
1574 	Z_Free(savebuffer);
1575 	save_p = NULL;
1576 	if (unlink(tmpsave) == -1)
1577 		CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
1578 	consistancy[gametic%BACKUPTICS] = Consistancy();
1579 	CON_ToggleOff();
1580 
1581 	// Tell the server we have received and reloaded the gamestate
1582 	// so they know they can resume the game
1583 	netbuffer->packettype = PT_RECEIVEDGAMESTATE;
1584 	HSendPacket(servernode, true, 0, 0);
1585 }
1586 
CL_ReloadReceivedSavegame(void)1587 static void CL_ReloadReceivedSavegame(void)
1588 {
1589 	INT32 i;
1590 
1591 	for (i = 0; i < MAXPLAYERS; i++)
1592 	{
1593 		LUA_InvalidatePlayer(&players[i]);
1594 		sprintf(player_names[i], "Player %d", i + 1);
1595 	}
1596 
1597 	CL_LoadReceivedSavegame(true);
1598 
1599 	if (neededtic < gametic)
1600 		neededtic = gametic;
1601 	maketic = neededtic;
1602 
1603 	ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn;
1604 	P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16));
1605 	if (splitscreen)
1606 	{
1607 		ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn;
1608 		P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16));
1609 	}
1610 
1611 	camera.subsector = R_PointInSubsector(camera.x, camera.y);
1612 	camera2.subsector = R_PointInSubsector(camera2.x, camera2.y);
1613 
1614 	cl_redownloadinggamestate = false;
1615 
1616 	CONS_Printf(M_GetText("Game state reloaded\n"));
1617 }
1618 #endif
1619 
1620 #ifndef NONET
SendAskInfo(INT32 node)1621 static void SendAskInfo(INT32 node)
1622 {
1623 	const tic_t asktime = I_GetTime();
1624 	netbuffer->packettype = PT_ASKINFO;
1625 	netbuffer->u.askinfo.version = VERSION;
1626 	netbuffer->u.askinfo.time = (tic_t)LONG(asktime);
1627 
1628 	// Even if this never arrives due to the host being firewalled, we've
1629 	// now allowed traffic from the host to us in, so once the MS relays
1630 	// our address to the host, it'll be able to speak to us.
1631 	HSendPacket(node, false, 0, sizeof (askinfo_pak));
1632 }
1633 
1634 serverelem_t serverlist[MAXSERVERLIST];
1635 UINT32 serverlistcount = 0;
1636 
1637 #define FORCECLOSE 0x8000
1638 
SL_ClearServerList(INT32 connectedserver)1639 static void SL_ClearServerList(INT32 connectedserver)
1640 {
1641 	UINT32 i;
1642 
1643 	for (i = 0; i < serverlistcount; i++)
1644 		if (connectedserver != serverlist[i].node)
1645 		{
1646 			Net_CloseConnection(serverlist[i].node|FORCECLOSE);
1647 			serverlist[i].node = 0;
1648 		}
1649 	serverlistcount = 0;
1650 }
1651 
SL_SearchServer(INT32 node)1652 static UINT32 SL_SearchServer(INT32 node)
1653 {
1654 	UINT32 i;
1655 	for (i = 0; i < serverlistcount; i++)
1656 		if (serverlist[i].node == node)
1657 			return i;
1658 
1659 	return UINT32_MAX;
1660 }
1661 
SL_InsertServer(serverinfo_pak * info,SINT8 node)1662 static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
1663 {
1664 	UINT32 i;
1665 
1666 	// search if not already on it
1667 	i = SL_SearchServer(node);
1668 	if (i == UINT32_MAX)
1669 	{
1670 		// not found add it
1671 		if (serverlistcount >= MAXSERVERLIST)
1672 			return; // list full
1673 
1674 		if (info->_255 != 255)
1675 			return;/* old packet format */
1676 
1677 		if (info->packetversion != PACKETVERSION)
1678 			return;/* old new packet format */
1679 
1680 		if (info->version != VERSION)
1681 			return; // Not same version.
1682 
1683 		if (info->subversion != SUBVERSION)
1684 			return; // Close, but no cigar.
1685 
1686 		if (strcmp(info->application, SRB2APPLICATION))
1687 			return;/* that's a different mod */
1688 
1689 		i = serverlistcount++;
1690 	}
1691 
1692 	serverlist[i].info = *info;
1693 	serverlist[i].node = node;
1694 
1695 	// resort server list
1696 	M_SortServerList();
1697 }
1698 
1699 #if defined (MASTERSERVER) && defined (HAVE_THREADS)
1700 struct Fetch_servers_ctx
1701 {
1702 	int room;
1703 	int id;
1704 };
1705 
1706 static void
Fetch_servers_thread(struct Fetch_servers_ctx * ctx)1707 Fetch_servers_thread (struct Fetch_servers_ctx *ctx)
1708 {
1709 	msg_server_t *server_list;
1710 
1711 	server_list = GetShortServersList(ctx->room, ctx->id);
1712 
1713 	if (server_list)
1714 	{
1715 		I_lock_mutex(&ms_QueryId_mutex);
1716 		{
1717 			if (ctx->id != ms_QueryId)
1718 			{
1719 				free(server_list);
1720 				server_list = NULL;
1721 			}
1722 		}
1723 		I_unlock_mutex(ms_QueryId_mutex);
1724 
1725 		if (server_list)
1726 		{
1727 			I_lock_mutex(&m_menu_mutex);
1728 			{
1729 				if (m_waiting_mode == M_WAITING_SERVERS)
1730 					m_waiting_mode = M_NOT_WAITING;
1731 			}
1732 			I_unlock_mutex(m_menu_mutex);
1733 
1734 			I_lock_mutex(&ms_ServerList_mutex);
1735 			{
1736 				ms_ServerList = server_list;
1737 			}
1738 			I_unlock_mutex(ms_ServerList_mutex);
1739 		}
1740 	}
1741 
1742 	free(ctx);
1743 }
1744 #endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/
1745 
CL_QueryServerList(msg_server_t * server_list)1746 void CL_QueryServerList (msg_server_t *server_list)
1747 {
1748 	INT32 i;
1749 
1750 	for (i = 0; server_list[i].header.buffer[0]; i++)
1751 	{
1752 		// Make sure MS version matches our own, to
1753 		// thwart nefarious servers who lie to the MS.
1754 
1755 		/* lol bruh, that version COMES from the servers */
1756 		//if (strcmp(version, server_list[i].version) == 0)
1757 		{
1758 			INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port);
1759 			if (node == -1)
1760 				break; // no more node free
1761 			SendAskInfo(node);
1762 			// Force close the connection so that servers can't eat
1763 			// up nodes forever if we never get a reply back from them
1764 			// (usually when they've not forwarded their ports).
1765 			//
1766 			// Don't worry, we'll get in contact with the working
1767 			// servers again when they send SERVERINFO to us later!
1768 			//
1769 			// (Note: as a side effect this probably means every
1770 			// server in the list will probably be using the same node (e.g. node 1),
1771 			// not that it matters which nodes they use when
1772 			// the connections are closed afterwards anyway)
1773 			// -- Monster Iestyn 12/11/18
1774 			Net_CloseConnection(node|FORCECLOSE);
1775 		}
1776 	}
1777 }
1778 
CL_UpdateServerList(boolean internetsearch,INT32 room)1779 void CL_UpdateServerList(boolean internetsearch, INT32 room)
1780 {
1781 	(void)internetsearch;
1782 	(void)room;
1783 
1784 	SL_ClearServerList(0);
1785 
1786 	if (!netgame && I_NetOpenSocket)
1787 	{
1788 		if (I_NetOpenSocket())
1789 		{
1790 			netgame = true;
1791 			multiplayer = true;
1792 		}
1793 	}
1794 
1795 	// search for local servers
1796 	if (netgame)
1797 		SendAskInfo(BROADCASTADDR);
1798 
1799 #ifdef MASTERSERVER
1800 	if (internetsearch)
1801 	{
1802 #ifdef HAVE_THREADS
1803 		struct Fetch_servers_ctx *ctx;
1804 
1805 		ctx = malloc(sizeof *ctx);
1806 
1807 		/* This called from M_Refresh so I don't use a mutex */
1808 		m_waiting_mode = M_WAITING_SERVERS;
1809 
1810 		I_lock_mutex(&ms_QueryId_mutex);
1811 		{
1812 			ctx->id = ms_QueryId;
1813 		}
1814 		I_unlock_mutex(ms_QueryId_mutex);
1815 
1816 		ctx->room = room;
1817 
1818 		I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx);
1819 #else
1820 		msg_server_t *server_list;
1821 
1822 		server_list = GetShortServersList(room, 0);
1823 
1824 		if (server_list)
1825 		{
1826 			CL_QueryServerList(server_list);
1827 			free(server_list);
1828 		}
1829 #endif
1830 	}
1831 #endif/*MASTERSERVER*/
1832 }
1833 
1834 #endif // ifndef NONET
1835 
1836 /** Called by CL_ServerConnectionTicker
1837   *
1838   * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit.
1839   * \return False if the connection was aborted
1840   * \sa CL_ServerConnectionTicker
1841   * \sa CL_ConnectToServer
1842   *
1843   */
CL_ServerConnectionSearchTicker(tic_t * asksent)1844 static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
1845 {
1846 #ifndef NONET
1847 	INT32 i;
1848 
1849 	// serverlist is updated by GetPacket function
1850 	if (serverlistcount > 0)
1851 	{
1852 		// this can be a responce to our broadcast request
1853 		if (servernode == -1 || servernode >= MAXNETNODES)
1854 		{
1855 			i = 0;
1856 			servernode = serverlist[i].node;
1857 			CONS_Printf(M_GetText("Found, "));
1858 		}
1859 		else
1860 		{
1861 			i = SL_SearchServer(servernode);
1862 			if (i < 0)
1863 				return true;
1864 		}
1865 
1866 		// Quit here rather than downloading files and being refused later.
1867 		if (serverlist[i].info.refusereason)
1868 		{
1869 			D_QuitNetGame();
1870 			CL_Reset();
1871 			D_StartTitle();
1872 			if (serverlist[i].info.refusereason == 1)
1873 				M_StartMessage(M_GetText("The server is not accepting\njoins for the moment.\n\nPress ESC\n"), NULL, MM_NOTHING);
1874 			else if (serverlist[i].info.refusereason == 2)
1875 				M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING);
1876 			else
1877 				M_StartMessage(M_GetText("You can't join.\nI don't know why,\nbut you can't join.\n\nPress ESC\n"), NULL, MM_NOTHING);
1878 			return false;
1879 		}
1880 
1881 		if (client)
1882 		{
1883 			D_ParseFileneeded(serverlist[i].info.fileneedednum,
1884 				serverlist[i].info.fileneeded);
1885 			CONS_Printf(M_GetText("Checking files...\n"));
1886 			i = CL_CheckFiles();
1887 			if (i == 3) // too many files
1888 			{
1889 				D_QuitNetGame();
1890 				CL_Reset();
1891 				D_StartTitle();
1892 				M_StartMessage(M_GetText(
1893 					"You have too many WAD files loaded\n"
1894 					"to add ones the server is using.\n"
1895 					"Please restart SRB2 before connecting.\n\n"
1896 					"Press ESC\n"
1897 				), NULL, MM_NOTHING);
1898 				return false;
1899 			}
1900 			else if (i == 2) // cannot join for some reason
1901 			{
1902 				D_QuitNetGame();
1903 				CL_Reset();
1904 				D_StartTitle();
1905 				M_StartMessage(M_GetText(
1906 					"You have the wrong addons loaded.\n\n"
1907 					"To play on this server, restart\n"
1908 					"the game and don't load any addons.\n"
1909 					"SRB2 will automatically add\n"
1910 					"everything you need when you join.\n\n"
1911 					"Press ESC\n"
1912 				), NULL, MM_NOTHING);
1913 				return false;
1914 			}
1915 			else if (i == 1)
1916 				cl_mode = CL_ASKJOIN;
1917 			else
1918 			{
1919 				// must download something
1920 				// can we, though?
1921 				if (!CL_CheckDownloadable()) // nope!
1922 				{
1923 					D_QuitNetGame();
1924 					CL_Reset();
1925 					D_StartTitle();
1926 					M_StartMessage(M_GetText(
1927 						"You cannot connect to this server\n"
1928 						"because you cannot download the files\n"
1929 						"that you are missing from the server.\n\n"
1930 						"See the console or log file for\n"
1931 						"more details.\n\n"
1932 						"Press ESC\n"
1933 					), NULL, MM_NOTHING);
1934 					return false;
1935 				}
1936 				// no problem if can't send packet, we will retry later
1937 				if (CL_SendFileRequest())
1938 				{
1939 					cl_mode = CL_DOWNLOADFILES;
1940 #ifndef NONET
1941 					Snake_Initialise();
1942 #endif
1943 				}
1944 			}
1945 		}
1946 		else
1947 			cl_mode = CL_ASKJOIN; // files need not be checked for the server.
1948 
1949 		return true;
1950 	}
1951 
1952 	// Ask the info to the server (askinfo packet)
1953 	if (*asksent + NEWTICRATE < I_GetTime())
1954 	{
1955 		SendAskInfo(servernode);
1956 		*asksent = I_GetTime();
1957 	}
1958 #else
1959 	(void)asksent;
1960 	// No netgames, so we skip this state.
1961 	cl_mode = CL_ASKJOIN;
1962 #endif // ifndef NONET/else
1963 
1964 	return true;
1965 }
1966 
1967 /** Called by CL_ConnectToServer
1968   *
1969   * \param tmpsave The name of the gamestate file???
1970   * \param oldtic Used for knowing when to poll events and redraw
1971   * \param asksent ???
1972   * \return False if the connection was aborted
1973   * \sa CL_ServerConnectionSearchTicker
1974   * \sa CL_ConnectToServer
1975   *
1976   */
CL_ServerConnectionTicker(const char * tmpsave,tic_t * oldtic,tic_t * asksent)1977 static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent)
1978 {
1979 	boolean waitmore;
1980 	INT32 i;
1981 
1982 #ifdef NONET
1983 	(void)tmpsave;
1984 #endif
1985 
1986 	switch (cl_mode)
1987 	{
1988 		case CL_SEARCHING:
1989 			if (!CL_ServerConnectionSearchTicker(asksent))
1990 				return false;
1991 			break;
1992 
1993 		case CL_DOWNLOADFILES:
1994 			waitmore = false;
1995 			for (i = 0; i < fileneedednum; i++)
1996 				if (fileneeded[i].status == FS_DOWNLOADING
1997 					|| fileneeded[i].status == FS_REQUESTED)
1998 				{
1999 					waitmore = true;
2000 					break;
2001 				}
2002 			if (waitmore)
2003 				break; // exit the case
2004 
2005 #ifndef NONET
2006 			if (snake)
2007 			{
2008 				free(snake);
2009 				snake = NULL;
2010 			}
2011 #endif
2012 
2013 			cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now
2014 			/* FALLTHRU */
2015 
2016 		case CL_ASKJOIN:
2017 			CL_LoadServerFiles();
2018 #ifndef NONET
2019 			// prepare structures to save the file
2020 			// WARNING: this can be useless in case of server not in GS_LEVEL
2021 			// but since the network layer doesn't provide ordered packets...
2022 			CL_PrepareDownloadSaveGame(tmpsave);
2023 #endif
2024 			if (CL_SendJoin())
2025 				cl_mode = CL_WAITJOINRESPONSE;
2026 			break;
2027 
2028 #ifndef NONET
2029 		case CL_DOWNLOADSAVEGAME:
2030 			// At this state, the first (and only) needed file is the gamestate
2031 			if (fileneeded[0].status == FS_FOUND)
2032 			{
2033 				// Gamestate is now handled within CL_LoadReceivedSavegame()
2034 				CL_LoadReceivedSavegame(false);
2035 				cl_mode = CL_CONNECTED;
2036 			} // don't break case continue to CL_CONNECTED
2037 			else
2038 				break;
2039 #endif
2040 
2041 		case CL_WAITJOINRESPONSE:
2042 		case CL_CONNECTED:
2043 		default:
2044 			break;
2045 
2046 		// Connection closed by cancel, timeout or refusal.
2047 		case CL_ABORTED:
2048 			cl_mode = CL_SEARCHING;
2049 			return false;
2050 
2051 	}
2052 
2053 	GetPackets();
2054 	Net_AckTicker();
2055 
2056 	// Call it only once by tic
2057 	if (*oldtic != I_GetTime())
2058 	{
2059 		I_OsPolling();
2060 		for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
2061 			G_MapEventsToControls(&events[eventtail]);
2062 
2063 		if (gamekeydown[KEY_ESCAPE] || gamekeydown[KEY_JOY1+1])
2064 		{
2065 			CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
2066 //				M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
2067 
2068 #ifndef NONET
2069 			if (snake)
2070 			{
2071 				free(snake);
2072 				snake = NULL;
2073 			}
2074 #endif
2075 
2076 			D_QuitNetGame();
2077 			CL_Reset();
2078 			D_StartTitle();
2079 			memset(gamekeydown, 0, NUMKEYS);
2080 			return false;
2081 		}
2082 #ifndef NONET
2083 		else if (cl_mode == CL_DOWNLOADFILES && snake)
2084 			Snake_Handle();
2085 #endif
2086 
2087 		if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME))
2088 			FileReceiveTicker();
2089 
2090 		// why are these here? this is for servers, we're a client
2091 		//if (key == 's' && server)
2092 		//	doomcom->numnodes = (INT16)pnumnodes;
2093 		//FileSendTicker();
2094 		*oldtic = I_GetTime();
2095 
2096 #ifndef NONET
2097 		if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED)
2098 		{
2099 			if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_DOWNLOADSAVEGAME)
2100 			{
2101 				F_MenuPresTicker(true); // title sky
2102 				F_TitleScreenTicker(true);
2103 				F_TitleScreenDrawer();
2104 			}
2105 			CL_DrawConnectionStatus();
2106 			I_UpdateNoVsync(); // page flip or blit buffer
2107 			if (moviemode)
2108 				M_SaveFrame();
2109 			S_UpdateSounds();
2110 			S_UpdateClosedCaptions();
2111 		}
2112 #else
2113 		CON_Drawer();
2114 		I_UpdateNoVsync();
2115 #endif
2116 	}
2117 	else
2118 		I_Sleep();
2119 
2120 	return true;
2121 }
2122 
2123 /** Use adaptive send using net_bandwidth and stat.sendbytes
2124   *
2125   * \todo Better description...
2126   *
2127   */
CL_ConnectToServer(void)2128 static void CL_ConnectToServer(void)
2129 {
2130 	INT32 pnumnodes, nodewaited = doomcom->numnodes, i;
2131 	tic_t oldtic;
2132 #ifndef NONET
2133 	tic_t asksent;
2134 	char tmpsave[256];
2135 
2136 	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
2137 
2138 	lastfilenum = -1;
2139 #endif
2140 
2141 	cl_mode = CL_SEARCHING;
2142 
2143 #ifndef NONET
2144 	// Don't get a corrupt savegame error because tmpsave already exists
2145 	if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
2146 		I_Error("Can't delete %s\n", tmpsave);
2147 #endif
2148 
2149 	if (netgame)
2150 	{
2151 		if (servernode < 0 || servernode >= MAXNETNODES)
2152 			CONS_Printf(M_GetText("Searching for a server...\n"));
2153 		else
2154 			CONS_Printf(M_GetText("Contacting the server...\n"));
2155 	}
2156 
2157 	if (gamestate == GS_INTERMISSION)
2158 		Y_EndIntermission(); // clean up intermission graphics etc
2159 
2160 	DEBFILE(va("waiting %d nodes\n", doomcom->numnodes));
2161 	G_SetGamestate(GS_WAITINGPLAYERS);
2162 	wipegamestate = GS_WAITINGPLAYERS;
2163 
2164 	ClearAdminPlayers();
2165 	pnumnodes = 1;
2166 	oldtic = I_GetTime() - 1;
2167 #ifndef NONET
2168 	asksent = (tic_t) - TICRATE;
2169 
2170 	i = SL_SearchServer(servernode);
2171 
2172 	if (i != -1)
2173 	{
2174 		char *gametypestr = serverlist[i].info.gametypename;
2175 		CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername);
2176 		gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0';
2177 		CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr);
2178 		CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100,
2179 		 serverlist[i].info.version%100, serverlist[i].info.subversion);
2180 	}
2181 	SL_ClearServerList(servernode);
2182 #endif
2183 
2184 	do
2185 	{
2186 		// If the connection was aborted for some reason, leave
2187 #ifndef NONET
2188 		if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent))
2189 #else
2190 		if (!CL_ServerConnectionTicker((char*)NULL, &oldtic, (tic_t *)NULL))
2191 #endif
2192 			return;
2193 
2194 		if (server)
2195 		{
2196 			pnumnodes = 0;
2197 			for (i = 0; i < MAXNETNODES; i++)
2198 				if (nodeingame[i])
2199 					pnumnodes++;
2200 		}
2201 	}
2202 	while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes))));
2203 
2204 	DEBFILE(va("Synchronisation Finished\n"));
2205 
2206 	displayplayer = consoleplayer;
2207 }
2208 
2209 #ifndef NONET
2210 typedef struct banreason_s
2211 {
2212 	char *reason;
2213 	struct banreason_s *prev; //-1
2214 	struct banreason_s *next; //+1
2215 } banreason_t;
2216 
2217 static banreason_t *reasontail = NULL; //last entry, use prev
2218 static banreason_t *reasonhead = NULL; //1st entry, use next
2219 
Command_ShowBan(void)2220 static void Command_ShowBan(void) //Print out ban list
2221 {
2222 	size_t i;
2223 	const char *address, *mask;
2224 	banreason_t *reasonlist = reasonhead;
2225 
2226 	if (I_GetBanAddress)
2227 		CONS_Printf(M_GetText("Ban List:\n"));
2228 	else
2229 		return;
2230 
2231 	for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
2232 	{
2233 		if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
2234 			CONS_Printf("%s: %s ", sizeu1(i+1), address);
2235 		else
2236 			CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask);
2237 
2238 		if (reasonlist && reasonlist->reason)
2239 			CONS_Printf("(%s)\n", reasonlist->reason);
2240 		else
2241 			CONS_Printf("\n");
2242 
2243 		if (reasonlist) reasonlist = reasonlist->next;
2244 	}
2245 
2246 	if (i == 0 && !address)
2247 		CONS_Printf(M_GetText("(empty)\n"));
2248 }
2249 
D_SaveBan(void)2250 void D_SaveBan(void)
2251 {
2252 	FILE *f;
2253 	size_t i;
2254 	banreason_t *reasonlist = reasonhead;
2255 	const char *address, *mask;
2256 	const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt");
2257 
2258 	if (!reasonhead)
2259 	{
2260 		remove(path);
2261 		return;
2262 	}
2263 
2264 	f = fopen(path, "w");
2265 
2266 	if (!f)
2267 	{
2268 		CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n"));
2269 		return;
2270 	}
2271 
2272 	for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
2273 	{
2274 		if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
2275 			fprintf(f, "%s 0", address);
2276 		else
2277 			fprintf(f, "%s %s", address, mask);
2278 
2279 		if (reasonlist && reasonlist->reason)
2280 			fprintf(f, " %s\n", reasonlist->reason);
2281 		else
2282 			fprintf(f, " %s\n", "NA");
2283 
2284 		if (reasonlist) reasonlist = reasonlist->next;
2285 	}
2286 
2287 	fclose(f);
2288 }
2289 
Ban_Add(const char * reason)2290 static void Ban_Add(const char *reason)
2291 {
2292 	banreason_t *reasonlist = malloc(sizeof(*reasonlist));
2293 
2294 	if (!reasonlist)
2295 		return;
2296 	if (!reason)
2297 		reason = "NA";
2298 
2299 	reasonlist->next = NULL;
2300 	reasonlist->reason = Z_StrDup(reason);
2301 	if ((reasonlist->prev = reasontail) == NULL)
2302 		reasonhead = reasonlist;
2303 	else
2304 		reasontail->next = reasonlist;
2305 	reasontail = reasonlist;
2306 }
2307 
Ban_Clear(void)2308 static void Ban_Clear(void)
2309 {
2310 	banreason_t *temp;
2311 
2312 	I_ClearBans();
2313 
2314 	reasontail = NULL;
2315 
2316 	while (reasonhead)
2317 	{
2318 		temp = reasonhead->next;
2319 		Z_Free(reasonhead->reason);
2320 		free(reasonhead);
2321 		reasonhead = temp;
2322 	}
2323 }
2324 
Command_ClearBans(void)2325 static void Command_ClearBans(void)
2326 {
2327 	if (!I_ClearBans)
2328 		return;
2329 
2330 	Ban_Clear();
2331 	D_SaveBan();
2332 }
2333 
Ban_Load_File(boolean warning)2334 static void Ban_Load_File(boolean warning)
2335 {
2336 	FILE *f;
2337 	size_t i;
2338 	const char *address, *mask;
2339 	char buffer[MAX_WADPATH];
2340 
2341 	if (!I_ClearBans)
2342 		return;
2343 
2344 	f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r");
2345 
2346 	if (!f)
2347 	{
2348 		if (warning)
2349 			CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n"));
2350 		return;
2351 	}
2352 
2353 	Ban_Clear();
2354 
2355 	for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++)
2356 	{
2357 		address = strtok(buffer, " \t\r\n");
2358 		mask = strtok(NULL, " \t\r\n");
2359 
2360 		I_SetBanAddress(address, mask);
2361 
2362 		Ban_Add(strtok(NULL, "\r\n"));
2363 	}
2364 
2365 	fclose(f);
2366 }
2367 
Command_ReloadBan(void)2368 static void Command_ReloadBan(void)  //recheck ban.txt
2369 {
2370 	Ban_Load_File(true);
2371 }
2372 
Command_connect(void)2373 static void Command_connect(void)
2374 {
2375 	if (COM_Argc() < 2 || *COM_Argv(1) == 0)
2376 	{
2377 		CONS_Printf(M_GetText(
2378 			"Connect <serveraddress> (port): connect to a server\n"
2379 			"Connect ANY: connect to the first lan server found\n"
2380 			//"Connect SELF: connect to your own server.\n"
2381 			));
2382 		return;
2383 	}
2384 
2385 	if (Playing() || titledemo)
2386 	{
2387 		CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n"));
2388 		return;
2389 	}
2390 
2391 	// modified game check: no longer handled
2392 	// we don't request a restart unless the filelist differs
2393 
2394 	server = false;
2395 /*
2396 	if (!stricmp(COM_Argv(1), "self"))
2397 	{
2398 		servernode = 0;
2399 		server = true;
2400 		/// \bug should be but...
2401 		//SV_SpawnServer();
2402 	}
2403 	else
2404 */
2405 	{
2406 		// used in menu to connect to a server in the list
2407 		if (netgame && !stricmp(COM_Argv(1), "node"))
2408 		{
2409 			servernode = (SINT8)atoi(COM_Argv(2));
2410 		}
2411 		else if (netgame)
2412 		{
2413 			CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n"));
2414 			return;
2415 		}
2416 		else if (I_NetOpenSocket)
2417 		{
2418 			I_NetOpenSocket();
2419 			netgame = true;
2420 			multiplayer = true;
2421 
2422 			if (!stricmp(COM_Argv(1), "any"))
2423 				servernode = BROADCASTADDR;
2424 			else if (I_NetMakeNodewPort)
2425 			{
2426 				if (COM_Argc() >= 3) // address AND port
2427 					servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2));
2428 				else // address only, or address:port
2429 					servernode = I_NetMakeNode(COM_Argv(1));
2430 			}
2431 			else
2432 			{
2433 				CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n"));
2434 				D_CloseConnection();
2435 				return;
2436 			}
2437 		}
2438 		else
2439 			CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n"));
2440 	}
2441 
2442 	splitscreen = false;
2443 	SplitScreen_OnChange();
2444 	botingame = false;
2445 	botskin = 0;
2446 	CL_ConnectToServer();
2447 }
2448 #endif
2449 
2450 static void ResetNode(INT32 node);
2451 
2452 //
2453 // CL_ClearPlayer
2454 //
2455 // Clears the player data so that a future client can use this slot
2456 //
CL_ClearPlayer(INT32 playernum)2457 void CL_ClearPlayer(INT32 playernum)
2458 {
2459 	if (players[playernum].mo)
2460 		P_RemoveMobj(players[playernum].mo);
2461 	memset(&players[playernum], 0, sizeof (player_t));
2462 	memset(playeraddress[playernum], 0, sizeof(*playeraddress));
2463 }
2464 
2465 //
2466 // CL_RemovePlayer
2467 //
2468 // Removes a player from the current game
2469 //
CL_RemovePlayer(INT32 playernum,kickreason_t reason)2470 static void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
2471 {
2472 	// Sanity check: exceptional cases (i.e. c-fails) can cause multiple
2473 	// kick commands to be issued for the same player.
2474 	if (!playeringame[playernum])
2475 		return;
2476 
2477 	if (server && !demoplayback && playernode[playernum] != UINT8_MAX)
2478 	{
2479 		INT32 node = playernode[playernum];
2480 		playerpernode[node]--;
2481 		if (playerpernode[node] <= 0)
2482 		{
2483 			nodeingame[node] = false;
2484 			Net_CloseConnection(node);
2485 			ResetNode(node);
2486 		}
2487 	}
2488 
2489 	if (gametyperules & GTR_TEAMFLAGS)
2490 		P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you!
2491 
2492 	// If in a special stage, redistribute the player's spheres across
2493 	// the remaining players.
2494 	if (G_IsSpecialStage(gamemap))
2495 	{
2496 		INT32 i, count, sincrement, spheres, rincrement, rings;
2497 
2498 		for (i = 0, count = 0; i < MAXPLAYERS; i++)
2499 		{
2500 			if (playeringame[i])
2501 				count++;
2502 		}
2503 
2504 		count--;
2505 		sincrement = spheres = players[playernum].spheres;
2506 		rincrement = rings = players[playernum].rings;
2507 
2508 		if (count)
2509 		{
2510 			sincrement /= count;
2511 			rincrement /= count;
2512 		}
2513 
2514 		for (i = 0; i < MAXPLAYERS; i++)
2515 		{
2516 			if (playeringame[i] && i != playernum)
2517 			{
2518 				if (spheres < 2*sincrement)
2519 				{
2520 					P_GivePlayerSpheres(&players[i], spheres);
2521 					spheres = 0;
2522 				}
2523 				else
2524 				{
2525 					P_GivePlayerSpheres(&players[i], sincrement);
2526 					spheres -= sincrement;
2527 				}
2528 
2529 				if (rings < 2*rincrement)
2530 				{
2531 					P_GivePlayerRings(&players[i], rings);
2532 					rings = 0;
2533 				}
2534 				else
2535 				{
2536 					P_GivePlayerRings(&players[i], rincrement);
2537 					rings -= rincrement;
2538 				}
2539 			}
2540 		}
2541 	}
2542 
2543 	LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting
2544 
2545 	// don't look through someone's view who isn't there
2546 	if (playernum == displayplayer)
2547 	{
2548 		// Call ViewpointSwitch hooks here.
2549 		// The viewpoint was forcibly changed.
2550 		LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true);
2551 		displayplayer = consoleplayer;
2552 	}
2553 
2554 	// Reset player data
2555 	CL_ClearPlayer(playernum);
2556 
2557 	// remove avatar of player
2558 	playeringame[playernum] = false;
2559 	playernode[playernum] = UINT8_MAX;
2560 	while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1)
2561 		doomcom->numslots--;
2562 
2563 	// Reset the name
2564 	sprintf(player_names[playernum], "Player %d", playernum+1);
2565 
2566 	player_name_changes[playernum] = 0;
2567 
2568 	if (IsPlayerAdmin(playernum))
2569 	{
2570 		RemoveAdminPlayer(playernum); // don't stay admin after you're gone
2571 	}
2572 
2573 	LUA_InvalidatePlayer(&players[playernum]);
2574 
2575 	if (G_TagGametype()) //Check if you still have a game. Location flexible. =P
2576 		P_CheckSurvivors();
2577 	else if (gametyperules & GTR_RACE)
2578 		P_CheckRacers();
2579 }
2580 
CL_Reset(void)2581 void CL_Reset(void)
2582 {
2583 	if (metalrecording)
2584 		G_StopMetalRecording(false);
2585 	if (metalplayback)
2586 		G_StopMetalDemo();
2587 	if (demorecording)
2588 		G_CheckDemoStatus();
2589 
2590 	// reset client/server code
2591 	DEBFILE(va("\n-=-=-=-=-=-=-= Client reset =-=-=-=-=-=-=-\n\n"));
2592 
2593 	if (servernode > 0 && servernode < MAXNETNODES)
2594 	{
2595 		nodeingame[(UINT8)servernode] = false;
2596 		Net_CloseConnection(servernode);
2597 	}
2598 	D_CloseConnection(); // netgame = false
2599 	multiplayer = false;
2600 	servernode = 0;
2601 	server = true;
2602 	doomcom->numnodes = 1;
2603 	doomcom->numslots = 1;
2604 	SV_StopServer();
2605 	SV_ResetServer();
2606 	CV_RevertNetVars();
2607 
2608 	// make sure we don't leave any fileneeded gunk over from a failed join
2609 	fileneedednum = 0;
2610 	memset(fileneeded, 0, sizeof(fileneeded));
2611 
2612 	// D_StartTitle should get done now, but the calling function will handle it
2613 }
2614 
2615 #ifndef NONET
Command_GetPlayerNum(void)2616 static void Command_GetPlayerNum(void)
2617 {
2618 	INT32 i;
2619 
2620 	for (i = 0; i < MAXPLAYERS; i++)
2621 		if (playeringame[i])
2622 		{
2623 			if (serverplayer == i)
2624 				CONS_Printf(M_GetText("num:%2d  node:%2d  %s\n"), i, playernode[i], player_names[i]);
2625 			else
2626 				CONS_Printf(M_GetText("\x82num:%2d  node:%2d  %s\n"), i, playernode[i], player_names[i]);
2627 		}
2628 }
2629 
nametonum(const char * name)2630 SINT8 nametonum(const char *name)
2631 {
2632 	INT32 playernum, i;
2633 
2634 	if (!strcmp(name, "0"))
2635 		return 0;
2636 
2637 	playernum = (SINT8)atoi(name);
2638 
2639 	if (playernum < 0 || playernum >= MAXPLAYERS)
2640 		return -1;
2641 
2642 	if (playernum)
2643 	{
2644 		if (playeringame[playernum])
2645 			return (SINT8)playernum;
2646 		else
2647 			return -1;
2648 	}
2649 
2650 	for (i = 0; i < MAXPLAYERS; i++)
2651 		if (playeringame[i] && !stricmp(player_names[i], name))
2652 			return (SINT8)i;
2653 
2654 	CONS_Printf(M_GetText("There is no player named \"%s\"\n"), name);
2655 
2656 	return -1;
2657 }
2658 
2659 /** Lists all players and their player numbers.
2660   *
2661   * \sa Command_GetPlayerNum
2662   */
Command_Nodes(void)2663 static void Command_Nodes(void)
2664 {
2665 	INT32 i;
2666 	size_t maxlen = 0;
2667 	const char *address;
2668 
2669 	for (i = 0; i < MAXPLAYERS; i++)
2670 	{
2671 		const size_t plen = strlen(player_names[i]);
2672 		if (playeringame[i] && plen > maxlen)
2673 			maxlen = plen;
2674 	}
2675 
2676 	for (i = 0; i < MAXPLAYERS; i++)
2677 	{
2678 		if (playeringame[i])
2679 		{
2680 			CONS_Printf("%.2u: %*s", i, (int)maxlen, player_names[i]);
2681 
2682 			if (playernode[i] != UINT8_MAX)
2683 			{
2684 				CONS_Printf(" - node %.2d", playernode[i]);
2685 				if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL)
2686 					CONS_Printf(" - %s", address);
2687 			}
2688 
2689 			if (IsPlayerAdmin(i))
2690 				CONS_Printf(M_GetText(" (verified admin)"));
2691 
2692 			if (players[i].spectator)
2693 				CONS_Printf(M_GetText(" (spectator)"));
2694 
2695 			CONS_Printf("\n");
2696 		}
2697 	}
2698 }
2699 
Command_Ban(void)2700 static void Command_Ban(void)
2701 {
2702 	if (COM_Argc() < 2)
2703 	{
2704 		CONS_Printf(M_GetText("Ban <playername/playernum> <reason>: ban and kick a player\n"));
2705 		return;
2706 	}
2707 
2708 	if (!netgame) // Don't kick Tails in splitscreen!
2709 	{
2710 		CONS_Printf(M_GetText("This only works in a netgame.\n"));
2711 		return;
2712 	}
2713 
2714 	if (server || IsPlayerAdmin(consoleplayer))
2715 	{
2716 		UINT8 buf[3 + MAX_REASONLENGTH];
2717 		UINT8 *p = buf;
2718 		const SINT8 pn = nametonum(COM_Argv(1));
2719 		const INT32 node = playernode[(INT32)pn];
2720 
2721 		if (pn == -1 || pn == 0)
2722 			return;
2723 
2724 		WRITEUINT8(p, pn);
2725 
2726 		if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now
2727 		{
2728 			CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
2729 			WRITEUINT8(p, KICK_MSG_GO_AWAY);
2730 			SendNetXCmd(XD_KICK, &buf, 2);
2731 		}
2732 		else
2733 		{
2734 			if (server) // only the server is allowed to do this right now
2735 			{
2736 				Ban_Add(COM_Argv(2));
2737 				D_SaveBan(); // save the ban list
2738 			}
2739 
2740 			if (COM_Argc() == 2)
2741 			{
2742 				WRITEUINT8(p, KICK_MSG_BANNED);
2743 				SendNetXCmd(XD_KICK, &buf, 2);
2744 			}
2745 			else
2746 			{
2747 				size_t i, j = COM_Argc();
2748 				char message[MAX_REASONLENGTH];
2749 
2750 				//Steal from the motd code so you don't have to put the reason in quotes.
2751 				strlcpy(message, COM_Argv(2), sizeof message);
2752 				for (i = 3; i < j; i++)
2753 				{
2754 					strlcat(message, " ", sizeof message);
2755 					strlcat(message, COM_Argv(i), sizeof message);
2756 				}
2757 
2758 				WRITEUINT8(p, KICK_MSG_CUSTOM_BAN);
2759 				WRITESTRINGN(p, message, MAX_REASONLENGTH);
2760 				SendNetXCmd(XD_KICK, &buf, p - buf);
2761 			}
2762 		}
2763 	}
2764 	else
2765 		CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
2766 
2767 }
2768 
Command_BanIP(void)2769 static void Command_BanIP(void)
2770 {
2771 	if (COM_Argc() < 2)
2772 	{
2773 		CONS_Printf(M_GetText("banip <ip> <reason>: ban an ip address\n"));
2774 		return;
2775 	}
2776 
2777 	if (server) // Only the server can use this, otherwise does nothing.
2778 	{
2779 		const char *address = (COM_Argv(1));
2780 		const char *reason;
2781 
2782 		if (COM_Argc() == 2)
2783 			reason = NULL;
2784 		else
2785 			reason = COM_Argv(2);
2786 
2787 
2788 		if (I_SetBanAddress && I_SetBanAddress(address, NULL))
2789 		{
2790 			if (reason)
2791 				CONS_Printf("Banned IP address %s for: %s\n", address, reason);
2792 			else
2793 				CONS_Printf("Banned IP address %s\n", address);
2794 
2795 			Ban_Add(reason);
2796 			D_SaveBan();
2797 		}
2798 		else
2799 		{
2800 			return;
2801 		}
2802 	}
2803 }
2804 
Command_Kick(void)2805 static void Command_Kick(void)
2806 {
2807 	if (COM_Argc() < 2)
2808 	{
2809 		CONS_Printf(M_GetText("kick <playername/playernum> <reason>: kick a player\n"));
2810 		return;
2811 	}
2812 
2813 	if (!netgame) // Don't kick Tails in splitscreen!
2814 	{
2815 		CONS_Printf(M_GetText("This only works in a netgame.\n"));
2816 		return;
2817 	}
2818 
2819 	if (server || IsPlayerAdmin(consoleplayer))
2820 	{
2821 		UINT8 buf[3 + MAX_REASONLENGTH];
2822 		UINT8 *p = buf;
2823 		const SINT8 pn = nametonum(COM_Argv(1));
2824 
2825 		if (pn == -1 || pn == 0)
2826 			return;
2827 
2828 		// Special case if we are trying to kick a player who is downloading the game state:
2829 		// trigger a timeout instead of kicking them, because a kick would only
2830 		// take effect after they have finished downloading
2831 		if (server && playernode[pn] != UINT8_MAX && sendingsavegame[playernode[pn]])
2832 		{
2833 			Net_ConnectionTimeout(playernode[pn]);
2834 			return;
2835 		}
2836 
2837 		WRITESINT8(p, pn);
2838 
2839 		if (COM_Argc() == 2)
2840 		{
2841 			WRITEUINT8(p, KICK_MSG_GO_AWAY);
2842 			SendNetXCmd(XD_KICK, &buf, 2);
2843 		}
2844 		else
2845 		{
2846 			size_t i, j = COM_Argc();
2847 			char message[MAX_REASONLENGTH];
2848 
2849 			//Steal from the motd code so you don't have to put the reason in quotes.
2850 			strlcpy(message, COM_Argv(2), sizeof message);
2851 			for (i = 3; i < j; i++)
2852 			{
2853 				strlcat(message, " ", sizeof message);
2854 				strlcat(message, COM_Argv(i), sizeof message);
2855 			}
2856 
2857 			WRITEUINT8(p, KICK_MSG_CUSTOM_KICK);
2858 			WRITESTRINGN(p, message, MAX_REASONLENGTH);
2859 			SendNetXCmd(XD_KICK, &buf, p - buf);
2860 		}
2861 	}
2862 	else
2863 		CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
2864 }
2865 #endif
2866 
Got_KickCmd(UINT8 ** p,INT32 playernum)2867 static void Got_KickCmd(UINT8 **p, INT32 playernum)
2868 {
2869 	INT32 pnum, msg;
2870 	char buf[3 + MAX_REASONLENGTH];
2871 	char *reason = buf;
2872 	kickreason_t kickreason = KR_KICK;
2873 	boolean keepbody;
2874 
2875 	pnum = READUINT8(*p);
2876 	msg = READUINT8(*p);
2877 	keepbody = (msg & KICK_MSG_KEEP_BODY) != 0;
2878 	msg &= ~KICK_MSG_KEEP_BODY;
2879 
2880 	if (pnum == serverplayer && IsPlayerAdmin(playernum))
2881 	{
2882 		CONS_Printf(M_GetText("Server is being shut down remotely. Goodbye!\n"));
2883 
2884 		if (server)
2885 			COM_BufAddText("quit\n");
2886 
2887 		return;
2888 	}
2889 
2890 	// Is playernum authorized to make this kick?
2891 	if (playernum != serverplayer && !IsPlayerAdmin(playernum)
2892 		&& !(playernode[playernum] != UINT8_MAX && playerpernode[playernode[playernum]] == 2
2893 		&& nodetoplayer2[playernode[playernum]] == pnum))
2894 	{
2895 		// We received a kick command from someone who isn't the
2896 		// server or admin, and who isn't in splitscreen removing
2897 		// player 2. Thus, it must be someone with a modified
2898 		// binary, trying to kick someone but without having
2899 		// authorization.
2900 
2901 		// We deal with this by changing the kick reason to
2902 		// "consistency failure" and kicking the offending user
2903 		// instead.
2904 
2905 		// Note: Splitscreen in netgames is broken because of
2906 		// this. Only the server has any idea of which players
2907 		// are using splitscreen on the same computer, so
2908 		// clients cannot always determine if a kick is
2909 		// legitimate.
2910 
2911 		CONS_Alert(CONS_WARNING, M_GetText("Illegal kick command received from %s for player %d\n"), player_names[playernum], pnum);
2912 
2913 		// In debug, print a longer message with more details.
2914 		// TODO Callum: Should we translate this?
2915 /*
2916 		CONS_Debug(DBG_NETPLAY,
2917 			"So, you must be asking, why is this an illegal kick?\n"
2918 			"Well, let's take a look at the facts, shall we?\n"
2919 			"\n"
2920 			"playernum (this is the guy who did it), he's %d.\n"
2921 			"pnum (the guy he's trying to kick) is %d.\n"
2922 			"playernum's node is %d.\n"
2923 			"That node has %d players.\n"
2924 			"Player 2 on that node is %d.\n"
2925 			"pnum's node is %d.\n"
2926 			"That node has %d players.\n"
2927 			"Player 2 on that node is %d.\n"
2928 			"\n"
2929 			"If you think this is a bug, please report it, including all of the details above.\n",
2930 				playernum, pnum,
2931 				playernode[playernum], playerpernode[playernode[playernum]],
2932 				nodetoplayer2[playernode[playernum]],
2933 				playernode[pnum], playerpernode[playernode[pnum]],
2934 				nodetoplayer2[playernode[pnum]]);
2935 */
2936 		pnum = playernum;
2937 		msg = KICK_MSG_CON_FAIL;
2938 		keepbody = true;
2939 	}
2940 
2941 	//CONS_Printf("\x82%s ", player_names[pnum]);
2942 
2943 	// If a verified admin banned someone, the server needs to know about it.
2944 	// If the playernum isn't zero (the server) then the server needs to record the ban.
2945 	if (server && playernum && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN))
2946 	{
2947 		if (I_Ban && !I_Ban(playernode[(INT32)pnum]))
2948 			CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
2949 #ifndef NONET
2950 		else
2951 			Ban_Add(reason);
2952 #endif
2953 	}
2954 
2955 	switch (msg)
2956 	{
2957 		case KICK_MSG_GO_AWAY:
2958 			if (!players[pnum].quittime)
2959 				HU_AddChatText(va("\x82*%s has been kicked (Go away)", player_names[pnum]), false);
2960 			kickreason = KR_KICK;
2961 			break;
2962 		case KICK_MSG_PING_HIGH:
2963 			HU_AddChatText(va("\x82*%s left the game (Broke ping limit)", player_names[pnum]), false);
2964 			kickreason = KR_PINGLIMIT;
2965 			break;
2966 		case KICK_MSG_CON_FAIL:
2967 			HU_AddChatText(va("\x82*%s left the game (Synch Failure)", player_names[pnum]), false);
2968 			kickreason = KR_SYNCH;
2969 
2970 			if (M_CheckParm("-consisdump")) // Helps debugging some problems
2971 			{
2972 				INT32 i;
2973 
2974 				CONS_Printf(M_GetText("Player kicked is #%d, dumping consistency...\n"), pnum);
2975 
2976 				for (i = 0; i < MAXPLAYERS; i++)
2977 				{
2978 					if (!playeringame[i])
2979 						continue;
2980 					CONS_Printf("-------------------------------------\n");
2981 					CONS_Printf("Player %d: %s\n", i, player_names[i]);
2982 					CONS_Printf("Skin: %d\n", players[i].skin);
2983 					CONS_Printf("Color: %d\n", players[i].skincolor);
2984 					CONS_Printf("Speed: %d\n",players[i].speed>>FRACBITS);
2985 					if (players[i].mo)
2986 					{
2987 						if (!players[i].mo->skin)
2988 							CONS_Printf("Mobj skin: NULL!\n");
2989 						else
2990 							CONS_Printf("Mobj skin: %s\n", ((skin_t *)players[i].mo->skin)->name);
2991 						CONS_Printf("Position: %d, %d, %d\n", players[i].mo->x, players[i].mo->y, players[i].mo->z);
2992 						if (!players[i].mo->state)
2993 							CONS_Printf("State: S_NULL\n");
2994 						else
2995 							CONS_Printf("State: %d\n", (statenum_t)(players[i].mo->state-states));
2996 					}
2997 					else
2998 						CONS_Printf("Mobj: NULL\n");
2999 					CONS_Printf("-------------------------------------\n");
3000 				}
3001 			}
3002 			break;
3003 		case KICK_MSG_TIMEOUT:
3004 			HU_AddChatText(va("\x82*%s left the game (Connection timeout)", player_names[pnum]), false);
3005 			kickreason = KR_TIMEOUT;
3006 			break;
3007 		case KICK_MSG_PLAYER_QUIT:
3008 			if (netgame && !players[pnum].quittime) // not splitscreen/bots or soulless body
3009 				HU_AddChatText(va("\x82*%s left the game", player_names[pnum]), false);
3010 			kickreason = KR_LEAVE;
3011 			break;
3012 		case KICK_MSG_BANNED:
3013 			HU_AddChatText(va("\x82*%s has been banned (Don't come back)", player_names[pnum]), false);
3014 			kickreason = KR_BAN;
3015 			break;
3016 		case KICK_MSG_CUSTOM_KICK:
3017 			READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
3018 			HU_AddChatText(va("\x82*%s has been kicked (%s)", player_names[pnum], reason), false);
3019 			kickreason = KR_KICK;
3020 			break;
3021 		case KICK_MSG_CUSTOM_BAN:
3022 			READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
3023 			HU_AddChatText(va("\x82*%s has been banned (%s)", player_names[pnum], reason), false);
3024 			kickreason = KR_BAN;
3025 			break;
3026 	}
3027 
3028 	if (pnum == consoleplayer)
3029 	{
3030 		LUAh_GameQuit(false);
3031 #ifdef DUMPCONSISTENCY
3032 		if (msg == KICK_MSG_CON_FAIL) SV_SavedGame();
3033 #endif
3034 		D_QuitNetGame();
3035 		CL_Reset();
3036 		D_StartTitle();
3037 		if (msg == KICK_MSG_CON_FAIL)
3038 			M_StartMessage(M_GetText("Server closed connection\n(synch failure)\nPress ESC\n"), NULL, MM_NOTHING);
3039 		else if (msg == KICK_MSG_PING_HIGH)
3040 			M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING);
3041 		else if (msg == KICK_MSG_BANNED)
3042 			M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING);
3043 		else if (msg == KICK_MSG_CUSTOM_KICK)
3044 			M_StartMessage(va(M_GetText("You have been kicked\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING);
3045 		else if (msg == KICK_MSG_CUSTOM_BAN)
3046 			M_StartMessage(va(M_GetText("You have been banned\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING);
3047 		else
3048 			M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING);
3049 	}
3050 	else if (keepbody)
3051 	{
3052 		if (server && !demoplayback && playernode[pnum] != UINT8_MAX)
3053 		{
3054 			INT32 node = playernode[pnum];
3055 			playerpernode[node]--;
3056 			if (playerpernode[node] <= 0)
3057 			{
3058 				nodeingame[node] = false;
3059 				Net_CloseConnection(node);
3060 				ResetNode(node);
3061 			}
3062 		}
3063 
3064 		playernode[pnum] = UINT8_MAX;
3065 
3066 		players[pnum].quittime = 1;
3067 	}
3068 	else
3069 		CL_RemovePlayer(pnum, kickreason);
3070 }
3071 
Command_ResendGamestate(void)3072 static void Command_ResendGamestate(void)
3073 {
3074 	SINT8 playernum;
3075 
3076 	if (COM_Argc() == 1)
3077 	{
3078 		CONS_Printf(M_GetText("resendgamestate <playername/playernum>: resend the game state to a player\n"));
3079 		return;
3080 	}
3081 	else if (client)
3082 	{
3083 		CONS_Printf(M_GetText("Only the server can use this.\n"));
3084 		return;
3085 	}
3086 
3087 	playernum = nametonum(COM_Argv(1));
3088 	if (playernum == -1 || playernum == 0)
3089 		return;
3090 
3091 	// Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on
3092 	netbuffer->packettype = PT_WILLRESENDGAMESTATE;
3093 	if (!HSendPacket(playernode[playernum], true, 0, 0))
3094 	{
3095 		CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n"));
3096 		return;
3097 	}
3098 }
3099 
3100 static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}};
3101 consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL);
3102 
3103 consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
3104 consvar_t cv_joinnextround = CVAR_INIT ("joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); /// \todo not done
3105 static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}};
3106 consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL);
3107 static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}};
3108 consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL);
3109 static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}};
3110 consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "2", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL);
3111 
3112 static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}};
3113 consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL);
3114 consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
3115 
3116 // max file size to send to a player (in kilobytes)
3117 static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {51200, "MAX"}, {0, NULL}};
3118 consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL);
3119 consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
3120 
3121 // Speed of file downloading (in packets per tic)
3122 static CV_PossibleValue_t downloadspeed_cons_t[] = {{0, "MIN"}, {32, "MAX"}, {0, NULL}};
3123 consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL);
3124 
3125 static void Got_AddPlayer(UINT8 **p, INT32 playernum);
3126 
3127 // called one time at init
D_ClientServerInit(void)3128 void D_ClientServerInit(void)
3129 {
3130 	DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n",
3131 		VERSION/100, VERSION%100, SUBVERSION));
3132 
3133 #ifndef NONET
3134 	COM_AddCommand("getplayernum", Command_GetPlayerNum);
3135 	COM_AddCommand("kick", Command_Kick);
3136 	COM_AddCommand("ban", Command_Ban);
3137 	COM_AddCommand("banip", Command_BanIP);
3138 	COM_AddCommand("clearbans", Command_ClearBans);
3139 	COM_AddCommand("showbanlist", Command_ShowBan);
3140 	COM_AddCommand("reloadbans", Command_ReloadBan);
3141 	COM_AddCommand("connect", Command_connect);
3142 	COM_AddCommand("nodes", Command_Nodes);
3143 	COM_AddCommand("resendgamestate", Command_ResendGamestate);
3144 #ifdef PACKETDROP
3145 	COM_AddCommand("drop", Command_Drop);
3146 	COM_AddCommand("droprate", Command_Droprate);
3147 #endif
3148 #ifdef _DEBUG
3149 	COM_AddCommand("numnodes", Command_Numnodes);
3150 #endif
3151 #endif
3152 
3153 	RegisterNetXCmd(XD_KICK, Got_KickCmd);
3154 	RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer);
3155 #ifndef NONET
3156 #ifdef DUMPCONSISTENCY
3157 	CV_RegisterVar(&cv_dumpconsistency);
3158 #endif
3159 	Ban_Load_File(false);
3160 #endif
3161 
3162 	gametic = 0;
3163 	localgametic = 0;
3164 
3165 	// do not send anything before the real begin
3166 	SV_StopServer();
3167 	SV_ResetServer();
3168 	if (dedicated)
3169 		SV_SpawnServer();
3170 }
3171 
ResetNode(INT32 node)3172 static void ResetNode(INT32 node)
3173 {
3174 	nodeingame[node] = false;
3175 	nodewaiting[node] = 0;
3176 
3177 	nettics[node] = gametic;
3178 	supposedtics[node] = gametic;
3179 
3180 	nodetoplayer[node] = -1;
3181 	nodetoplayer2[node] = -1;
3182 	playerpernode[node] = 0;
3183 
3184 	sendingsavegame[node] = false;
3185 	resendingsavegame[node] = false;
3186 	savegameresendcooldown[node] = 0;
3187 }
3188 
SV_ResetServer(void)3189 void SV_ResetServer(void)
3190 {
3191 	INT32 i;
3192 
3193 	// +1 because this command will be executed in com_executebuffer in
3194 	// tryruntic so gametic will be incremented, anyway maketic > gametic
3195 	// is not an issue
3196 
3197 	maketic = gametic + 1;
3198 	neededtic = maketic;
3199 	tictoclear = maketic;
3200 
3201 	joindelay = 0;
3202 
3203 	for (i = 0; i < MAXNETNODES; i++)
3204 		ResetNode(i);
3205 
3206 	for (i = 0; i < MAXPLAYERS; i++)
3207 	{
3208 		LUA_InvalidatePlayer(&players[i]);
3209 		playeringame[i] = false;
3210 		playernode[i] = UINT8_MAX;
3211 		memset(playeraddress[i], 0, sizeof(*playeraddress));
3212 		sprintf(player_names[i], "Player %d", i + 1);
3213 		adminplayers[i] = -1; // Populate the entire adminplayers array with -1.
3214 	}
3215 
3216 	memset(player_name_changes, 0, sizeof player_name_changes);
3217 
3218 	mynode = 0;
3219 	cl_packetmissed = false;
3220 	cl_redownloadinggamestate = false;
3221 
3222 	if (dedicated)
3223 	{
3224 		nodeingame[0] = true;
3225 		serverplayer = 0;
3226 	}
3227 	else
3228 		serverplayer = consoleplayer;
3229 
3230 	if (server)
3231 		servernode = 0;
3232 
3233 	doomcom->numslots = 0;
3234 
3235 	// clear server_context
3236 	memset(server_context, '-', 8);
3237 
3238 	DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
3239 }
3240 
SV_GenContext(void)3241 static inline void SV_GenContext(void)
3242 {
3243 	UINT8 i;
3244 	// generate server_context, as exactly 8 bytes of randomly mixed A-Z and a-z
3245 	// (hopefully M_Random is initialized!! if not this will be awfully silly!)
3246 	for (i = 0; i < 8; i++)
3247 	{
3248 		const char a = M_RandomKey(26*2);
3249 		if (a < 26) // uppercase
3250 			server_context[i] = 'A'+a;
3251 		else // lowercase
3252 			server_context[i] = 'a'+(a-26);
3253 	}
3254 }
3255 
3256 //
3257 // D_QuitNetGame
3258 // Called before quitting to leave a net game
3259 // without hanging the other players
3260 //
D_QuitNetGame(void)3261 void D_QuitNetGame(void)
3262 {
3263 	if (!netgame || !netbuffer)
3264 		return;
3265 
3266 	DEBFILE("===========================================================================\n"
3267 	        "                  Quitting Game, closing connection\n"
3268 	        "===========================================================================\n");
3269 
3270 	// abort send/receive of files
3271 	CloseNetFile();
3272 	RemoveAllLuaFileTransfers();
3273 	waitingforluafiletransfer = false;
3274 	waitingforluafilecommand = false;
3275 
3276 	if (server)
3277 	{
3278 		INT32 i;
3279 
3280 		netbuffer->packettype = PT_SERVERSHUTDOWN;
3281 		for (i = 0; i < MAXNETNODES; i++)
3282 			if (nodeingame[i])
3283 				HSendPacket(i, true, 0, 0);
3284 #ifdef MASTERSERVER
3285 		if (serverrunning && ms_RoomId > 0)
3286 			UnregisterServer();
3287 #endif
3288 	}
3289 	else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode])
3290 	{
3291 		netbuffer->packettype = PT_CLIENTQUIT;
3292 		HSendPacket(servernode, true, 0, 0);
3293 	}
3294 
3295 	D_CloseConnection();
3296 	ClearAdminPlayers();
3297 
3298 	DEBFILE("===========================================================================\n"
3299 	        "                         Log finish\n"
3300 	        "===========================================================================\n");
3301 #ifdef DEBUGFILE
3302 	if (debugfile)
3303 	{
3304 		fclose(debugfile);
3305 		debugfile = NULL;
3306 	}
3307 #endif
3308 }
3309 
3310 // Adds a node to the game (player will follow at map change or at savegame....)
SV_AddNode(INT32 node)3311 static inline void SV_AddNode(INT32 node)
3312 {
3313 	nettics[node] = gametic;
3314 	supposedtics[node] = gametic;
3315 	// little hack because the server connects to itself and puts
3316 	// nodeingame when connected not here
3317 	if (node)
3318 		nodeingame[node] = true;
3319 }
3320 
3321 // Xcmd XD_ADDPLAYER
Got_AddPlayer(UINT8 ** p,INT32 playernum)3322 static void Got_AddPlayer(UINT8 **p, INT32 playernum)
3323 {
3324 	INT16 node, newplayernum;
3325 	boolean splitscreenplayer;
3326 	boolean rejoined;
3327 	player_t *newplayer;
3328 
3329 	if (playernum != serverplayer && !IsPlayerAdmin(playernum))
3330 	{
3331 		// protect against hacked/buggy client
3332 		CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]);
3333 		if (server)
3334 			SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
3335 		return;
3336 	}
3337 
3338 	node = READUINT8(*p);
3339 	newplayernum = READUINT8(*p);
3340 	splitscreenplayer = newplayernum & 0x80;
3341 	newplayernum &= ~0x80;
3342 
3343 	rejoined = playeringame[newplayernum];
3344 
3345 	if (!rejoined)
3346 	{
3347 		// Clear player before joining, lest some things get set incorrectly
3348 		// HACK: don't do this for splitscreen, it relies on preset values
3349 		if (!splitscreen && !botingame)
3350 			CL_ClearPlayer(newplayernum);
3351 		playeringame[newplayernum] = true;
3352 		G_AddPlayer(newplayernum);
3353 		if (newplayernum+1 > doomcom->numslots)
3354 			doomcom->numslots = (INT16)(newplayernum+1);
3355 
3356 		if (server && I_GetNodeAddress)
3357 		{
3358 			const char *address = I_GetNodeAddress(node);
3359 			char *port = NULL;
3360 			if (address) // MI: fix msvcrt.dll!_mbscat crash?
3361 			{
3362 				strcpy(playeraddress[newplayernum], address);
3363 				port = strchr(playeraddress[newplayernum], ':');
3364 				if (port)
3365 					*port = '\0';
3366 			}
3367 		}
3368 	}
3369 
3370 	newplayer = &players[newplayernum];
3371 
3372 	newplayer->jointime = 0;
3373 	newplayer->quittime = 0;
3374 
3375 	READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME);
3376 
3377 	// the server is creating my player
3378 	if (node == mynode)
3379 	{
3380 		playernode[newplayernum] = 0; // for information only
3381 		if (!splitscreenplayer)
3382 		{
3383 			consoleplayer = newplayernum;
3384 			displayplayer = newplayernum;
3385 			secondarydisplayplayer = newplayernum;
3386 			DEBFILE("spawning me\n");
3387 			ticcmd_oldangleturn[0] = newplayer->oldrelangleturn;
3388 		}
3389 		else
3390 		{
3391 			secondarydisplayplayer = newplayernum;
3392 			DEBFILE("spawning my brother\n");
3393 			if (botingame)
3394 				newplayer->bot = 1;
3395 			ticcmd_oldangleturn[1] = newplayer->oldrelangleturn;
3396 		}
3397 		P_ForceLocalAngle(newplayer, (angle_t)(newplayer->angleturn << 16));
3398 		D_SendPlayerConfig();
3399 		addedtogame = true;
3400 
3401 		if (rejoined)
3402 		{
3403 			if (newplayer->mo)
3404 			{
3405 				newplayer->viewheight = 41*newplayer->height/48;
3406 
3407 				if (newplayer->mo->eflags & MFE_VERTICALFLIP)
3408 					newplayer->viewz = newplayer->mo->z + newplayer->mo->height - newplayer->viewheight;
3409 				else
3410 					newplayer->viewz = newplayer->mo->z + newplayer->viewheight;
3411 			}
3412 
3413 			// wake up the status bar
3414 			ST_Start();
3415 			// wake up the heads up text
3416 			HU_Start();
3417 
3418 			if (camera.chase && !splitscreenplayer)
3419 				P_ResetCamera(newplayer, &camera);
3420 			if (camera2.chase && splitscreenplayer)
3421 				P_ResetCamera(newplayer, &camera2);
3422 		}
3423 	}
3424 
3425 	if (netgame)
3426 	{
3427 		char joinmsg[256];
3428 
3429 		if (rejoined)
3430 			strcpy(joinmsg, M_GetText("\x82*%s has rejoined the game (player %d)"));
3431 		else
3432 			strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)"));
3433 		strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum));
3434 
3435 		// Merge join notification + IP to avoid clogging console/chat
3436 		if (server && cv_showjoinaddress.value && I_GetNodeAddress)
3437 		{
3438 			const char *address = I_GetNodeAddress(node);
3439 			if (address)
3440 				strcat(joinmsg, va(" (%s)", address));
3441 		}
3442 
3443 		HU_AddChatText(joinmsg, false);
3444 	}
3445 
3446 	if (server && multiplayer && motd[0] != '\0')
3447 		COM_BufAddText(va("sayto %d %s\n", newplayernum, motd));
3448 
3449 	if (!rejoined)
3450 		LUAh_PlayerJoin(newplayernum);
3451 }
3452 
SV_AddWaitingPlayers(const char * name,const char * name2)3453 static boolean SV_AddWaitingPlayers(const char *name, const char *name2)
3454 {
3455 	INT32 node, n, newplayer = false;
3456 	UINT8 buf[2 + MAXPLAYERNAME];
3457 	UINT8 *p;
3458 	INT32 newplayernum;
3459 
3460 	for (node = 0; node < MAXNETNODES; node++)
3461 	{
3462 		// splitscreen can allow 2 player in one node
3463 		for (; nodewaiting[node] > 0; nodewaiting[node]--)
3464 		{
3465 			newplayer = true;
3466 
3467 			newplayernum = FindRejoinerNum(node);
3468 			if (newplayernum == -1)
3469 			{
3470 				// search for a free playernum
3471 				// we can't use playeringame since it is not updated here
3472 				for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++)
3473 				{
3474 					if (playeringame[newplayernum])
3475 						continue;
3476 					for (n = 0; n < MAXNETNODES; n++)
3477 						if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum)
3478 							break;
3479 					if (n == MAXNETNODES)
3480 						break;
3481 				}
3482 			}
3483 
3484 			// should never happen since we check the playernum
3485 			// before accepting the join
3486 			I_Assert(newplayernum < MAXPLAYERS);
3487 
3488 			playernode[newplayernum] = (UINT8)node;
3489 
3490 			p = buf + 2;
3491 			buf[0] = (UINT8)node;
3492 			buf[1] = newplayernum;
3493 			if (playerpernode[node] < 1)
3494 			{
3495 				nodetoplayer[node] = newplayernum;
3496 				WRITESTRINGN(p, name, MAXPLAYERNAME);
3497 			}
3498 			else
3499 			{
3500 				nodetoplayer2[node] = newplayernum;
3501 				buf[1] |= 0x80;
3502 				WRITESTRINGN(p, name2, MAXPLAYERNAME);
3503 			}
3504 			playerpernode[node]++;
3505 
3506 			SendNetXCmd(XD_ADDPLAYER, &buf, p - buf);
3507 
3508 			DEBFILE(va("Server added player %d node %d\n", newplayernum, node));
3509 		}
3510 	}
3511 
3512 	return newplayer;
3513 }
3514 
CL_AddSplitscreenPlayer(void)3515 void CL_AddSplitscreenPlayer(void)
3516 {
3517 	if (cl_mode == CL_CONNECTED)
3518 		CL_SendJoin();
3519 }
3520 
CL_RemoveSplitscreenPlayer(void)3521 void CL_RemoveSplitscreenPlayer(void)
3522 {
3523 	if (cl_mode != CL_CONNECTED)
3524 		return;
3525 
3526 	SendKick(secondarydisplayplayer, KICK_MSG_PLAYER_QUIT);
3527 }
3528 
3529 // is there a game running
Playing(void)3530 boolean Playing(void)
3531 {
3532 	return (server && serverrunning) || (client && cl_mode == CL_CONNECTED);
3533 }
3534 
SV_SpawnServer(void)3535 boolean SV_SpawnServer(void)
3536 {
3537 	if (demoplayback)
3538 		G_StopDemo(); // reset engine parameter
3539 	if (metalplayback)
3540 		G_StopMetalDemo();
3541 
3542 	if (!serverrunning)
3543 	{
3544 		CONS_Printf(M_GetText("Starting Server....\n"));
3545 		serverrunning = true;
3546 		SV_ResetServer();
3547 		SV_GenContext();
3548 		if (netgame && I_NetOpenSocket)
3549 		{
3550 			I_NetOpenSocket();
3551 #ifdef MASTERSERVER
3552 			if (ms_RoomId > 0)
3553 				RegisterServer();
3554 #endif
3555 		}
3556 
3557 		// non dedicated server just connect to itself
3558 		if (!dedicated)
3559 			CL_ConnectToServer();
3560 		else doomcom->numslots = 1;
3561 	}
3562 
3563 	return SV_AddWaitingPlayers(cv_playername.zstring, cv_playername2.zstring);
3564 }
3565 
SV_StopServer(void)3566 void SV_StopServer(void)
3567 {
3568 	tic_t i;
3569 
3570 	if (gamestate == GS_INTERMISSION)
3571 		Y_EndIntermission();
3572 	gamestate = wipegamestate = GS_NULL;
3573 
3574 	localtextcmd[0] = 0;
3575 	localtextcmd2[0] = 0;
3576 
3577 	for (i = firstticstosend; i < firstticstosend + BACKUPTICS; i++)
3578 		D_Clearticcmd(i);
3579 
3580 	consoleplayer = 0;
3581 	cl_mode = CL_SEARCHING;
3582 	maketic = gametic+1;
3583 	neededtic = maketic;
3584 	serverrunning = false;
3585 }
3586 
3587 // called at singleplayer start and stopdemo
SV_StartSinglePlayerServer(void)3588 void SV_StartSinglePlayerServer(void)
3589 {
3590 	server = true;
3591 	netgame = false;
3592 	multiplayer = false;
3593 	G_SetGametype(GT_COOP);
3594 
3595 	// no more tic the game with this settings!
3596 	SV_StopServer();
3597 
3598 	if (splitscreen)
3599 		multiplayer = true;
3600 }
3601 
SV_SendRefuse(INT32 node,const char * reason)3602 static void SV_SendRefuse(INT32 node, const char *reason)
3603 {
3604 	strcpy(netbuffer->u.serverrefuse.reason, reason);
3605 
3606 	netbuffer->packettype = PT_SERVERREFUSE;
3607 	HSendPacket(node, true, 0, strlen(netbuffer->u.serverrefuse.reason) + 1);
3608 	Net_CloseConnection(node);
3609 }
3610 
3611 // used at txtcmds received to check packetsize bound
TotalTextCmdPerTic(tic_t tic)3612 static size_t TotalTextCmdPerTic(tic_t tic)
3613 {
3614 	INT32 i;
3615 	size_t total = 1; // num of textcmds in the tic (ntextcmd byte)
3616 
3617 	for (i = 0; i < MAXPLAYERS; i++)
3618 	{
3619 		UINT8 *textcmd = D_GetExistingTextcmd(tic, i);
3620 		if ((!i || playeringame[i]) && textcmd)
3621 			total += 2 + textcmd[0]; // "+2" for size and playernum
3622 	}
3623 
3624 	return total;
3625 }
3626 
3627 /** Called when a PT_CLIENTJOIN packet is received
3628   *
3629   * \param node The packet sender
3630   *
3631   */
HandleConnect(SINT8 node)3632 static void HandleConnect(SINT8 node)
3633 {
3634 	char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1];
3635 	INT32 rejoinernum;
3636 	INT32 i;
3637 
3638 	rejoinernum = FindRejoinerNum(node);
3639 
3640 	if (bannednode && bannednode[node])
3641 		SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server."));
3642 	else if (netbuffer->u.clientcfg._255 != 255 ||
3643 			netbuffer->u.clientcfg.packetversion != PACKETVERSION)
3644 		SV_SendRefuse(node, "Incompatible packet formats.");
3645 	else if (strncmp(netbuffer->u.clientcfg.application, SRB2APPLICATION,
3646 				sizeof netbuffer->u.clientcfg.application))
3647 		SV_SendRefuse(node, "Different SRB2 modifications\nare not compatible.");
3648 	else if (netbuffer->u.clientcfg.version != VERSION
3649 		|| netbuffer->u.clientcfg.subversion != SUBVERSION)
3650 		SV_SendRefuse(node, va(M_GetText("Different SRB2 versions cannot\nplay a netgame!\n(server version %d.%d.%d)"), VERSION/100, VERSION%100, SUBVERSION));
3651 	else if (!cv_allownewplayer.value && node && rejoinernum == -1)
3652 		SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment."));
3653 	else if (D_NumPlayers() >= cv_maxplayers.value && rejoinernum == -1)
3654 		SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), cv_maxplayers.value));
3655 	else if (netgame && netbuffer->u.clientcfg.localplayers > 1) // Hacked client?
3656 		SV_SendRefuse(node, M_GetText("Too many players from\nthis node."));
3657 	else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join?
3658 		SV_SendRefuse(node, M_GetText("No players from\nthis node."));
3659 	else if (luafiletransfers)
3660 		SV_SendRefuse(node, M_GetText("The server is broadcasting a file\nrequested by a Lua script.\nPlease wait a bit and then\ntry rejoining."));
3661 	else if (netgame && joindelay > 2 * (tic_t)cv_joindelay.value * TICRATE)
3662 		SV_SendRefuse(node, va(M_GetText("Too many people are connecting.\nPlease wait %d seconds and then\ntry rejoining."),
3663 			(joindelay - 2 * cv_joindelay.value * TICRATE) / TICRATE));
3664 	else
3665 	{
3666 #ifndef NONET
3667 		boolean newnode = false;
3668 #endif
3669 
3670 		for (i = 0; i < netbuffer->u.clientcfg.localplayers - playerpernode[node]; i++)
3671 		{
3672 			strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1);
3673 			if (!EnsurePlayerNameIsGood(names[i], rejoinernum))
3674 			{
3675 				SV_SendRefuse(node, "Bad player name");
3676 				return;
3677 			}
3678 		}
3679 
3680 		// client authorised to join
3681 		nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]);
3682 		if (!nodeingame[node])
3683 		{
3684 			gamestate_t backupstate = gamestate;
3685 #ifndef NONET
3686 			newnode = true;
3687 #endif
3688 			SV_AddNode(node);
3689 
3690 			if (cv_joinnextround.value && gameaction == ga_nothing)
3691 				G_SetGamestate(GS_WAITINGPLAYERS);
3692 			if (!SV_SendServerConfig(node))
3693 			{
3694 				G_SetGamestate(backupstate);
3695 				/// \note Shouldn't SV_SendRefuse be called before ResetNode?
3696 				ResetNode(node);
3697 				SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
3698 				/// \todo fix this !!!
3699 				return; // restart the while
3700 			}
3701 			//if (gamestate != GS_LEVEL) // GS_INTERMISSION, etc?
3702 			//	SV_SendPlayerConfigs(node); // send bare minimum player info
3703 			G_SetGamestate(backupstate);
3704 			DEBFILE("new node joined\n");
3705 		}
3706 #ifndef NONET
3707 		if (nodewaiting[node])
3708 		{
3709 			if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode)
3710 			{
3711 				SV_SendSaveGame(node, false); // send a complete game state
3712 				DEBFILE("send savegame\n");
3713 			}
3714 			SV_AddWaitingPlayers(names[0], names[1]);
3715 			joindelay += cv_joindelay.value * TICRATE;
3716 			player_joining = true;
3717 		}
3718 #endif
3719 	}
3720 }
3721 
3722 /** Called when a PT_SERVERSHUTDOWN packet is received
3723   *
3724   * \param node The packet sender (should be the server)
3725   *
3726   */
HandleShutdown(SINT8 node)3727 static void HandleShutdown(SINT8 node)
3728 {
3729 	(void)node;
3730 	LUAh_GameQuit(false);
3731 	D_QuitNetGame();
3732 	CL_Reset();
3733 	D_StartTitle();
3734 	M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING);
3735 }
3736 
3737 /** Called when a PT_NODETIMEOUT packet is received
3738   *
3739   * \param node The packet sender (should be the server)
3740   *
3741   */
HandleTimeout(SINT8 node)3742 static void HandleTimeout(SINT8 node)
3743 {
3744 	(void)node;
3745 	LUAh_GameQuit(false);
3746 	D_QuitNetGame();
3747 	CL_Reset();
3748 	D_StartTitle();
3749 	M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
3750 }
3751 
3752 #ifndef NONET
3753 /** Called when a PT_SERVERINFO packet is received
3754   *
3755   * \param node The packet sender
3756   * \note What happens if the packet comes from a client or something like that?
3757   *
3758   */
HandleServerInfo(SINT8 node)3759 static void HandleServerInfo(SINT8 node)
3760 {
3761 	// compute ping in ms
3762 	const tic_t ticnow = I_GetTime();
3763 	const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time);
3764 	const tic_t ticdiff = (ticnow - ticthen)*1000/NEWTICRATE;
3765 	netbuffer->u.serverinfo.time = (tic_t)LONG(ticdiff);
3766 	netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0;
3767 	netbuffer->u.serverinfo.application
3768 		[sizeof netbuffer->u.serverinfo.application - 1] = '\0';
3769 	netbuffer->u.serverinfo.gametypename
3770 		[sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0';
3771 
3772 	SL_InsertServer(&netbuffer->u.serverinfo, node);
3773 }
3774 #endif
3775 
PT_WillResendGamestate(void)3776 static void PT_WillResendGamestate(void)
3777 {
3778 	char tmpsave[256];
3779 
3780 	if (server || cl_redownloadinggamestate)
3781 		return;
3782 
3783 	// Send back a PT_CANRECEIVEGAMESTATE packet to the server
3784 	// so they know they can start sending the game state
3785 	netbuffer->packettype = PT_CANRECEIVEGAMESTATE;
3786 	if (!HSendPacket(servernode, true, 0, 0))
3787 		return;
3788 
3789 	CONS_Printf(M_GetText("Reloading game state...\n"));
3790 
3791 	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
3792 
3793 	// Don't get a corrupt savegame error because tmpsave already exists
3794 	if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
3795 		I_Error("Can't delete %s\n", tmpsave);
3796 
3797 	CL_PrepareDownloadSaveGame(tmpsave);
3798 
3799 	cl_redownloadinggamestate = true;
3800 }
3801 
PT_CanReceiveGamestate(SINT8 node)3802 static void PT_CanReceiveGamestate(SINT8 node)
3803 {
3804 	if (client || sendingsavegame[node])
3805 		return;
3806 
3807 	CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]);
3808 
3809 	SV_SendSaveGame(node, true); // Resend a complete game state
3810 	resendingsavegame[node] = true;
3811 }
3812 
3813 /** Handles a packet received from a node that isn't in game
3814   *
3815   * \param node The packet sender
3816   * \todo Choose a better name, as the packet can also come from the server apparently?
3817   * \sa HandlePacketFromPlayer
3818   * \sa GetPackets
3819   *
3820   */
HandlePacketFromAwayNode(SINT8 node)3821 static void HandlePacketFromAwayNode(SINT8 node)
3822 {
3823 	if (node != servernode)
3824 		DEBFILE(va("Received packet from unknown host %d\n", node));
3825 
3826 // macro for packets that should only be sent by the server
3827 // if it is NOT from the server, bail out and close the connection!
3828 #define SERVERONLY \
3829 			if (node != servernode) \
3830 			{ \
3831 				Net_CloseConnection(node); \
3832 				break; \
3833 			}
3834 	switch (netbuffer->packettype)
3835 	{
3836 		case PT_ASKINFOVIAMS:
3837 #if 0
3838 			if (server && serverrunning)
3839 			{
3840 				INT32 clientnode;
3841 				if (ms_RoomId < 0) // ignore if we're not actually on the MS right now
3842 				{
3843 					Net_CloseConnection(node); // and yes, close connection
3844 					return;
3845 				}
3846 				clientnode = I_NetMakeNode(netbuffer->u.msaskinfo.clientaddr);
3847 				if (clientnode != -1)
3848 				{
3849 					SV_SendServerInfo(clientnode, (tic_t)LONG(netbuffer->u.msaskinfo.time));
3850 					SV_SendPlayerInfo(clientnode); // Send extra info
3851 					Net_CloseConnection(clientnode);
3852 					// Don't close connection to MS...
3853 				}
3854 				else
3855 					Net_CloseConnection(node); // ...unless the IP address is not valid
3856 			}
3857 			else
3858 				Net_CloseConnection(node); // you're not supposed to get it, so ignore it
3859 #else
3860 			Net_CloseConnection(node);
3861 #endif
3862 			break;
3863 
3864 		case PT_ASKINFO:
3865 			if (server && serverrunning)
3866 			{
3867 				SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time));
3868 				SV_SendPlayerInfo(node); // Send extra info
3869 			}
3870 			Net_CloseConnection(node);
3871 			break;
3872 
3873 		case PT_SERVERREFUSE: // Negative response of client join request
3874 			if (server && serverrunning)
3875 			{ // But wait I thought I'm the server?
3876 				Net_CloseConnection(node);
3877 				break;
3878 			}
3879 			SERVERONLY
3880 			if (cl_mode == CL_WAITJOINRESPONSE)
3881 			{
3882 				// Save the reason so it can be displayed after quitting the netgame
3883 				char *reason = strdup(netbuffer->u.serverrefuse.reason);
3884 				if (!reason)
3885 					I_Error("Out of memory!\n");
3886 
3887 				D_QuitNetGame();
3888 				CL_Reset();
3889 				D_StartTitle();
3890 
3891 				M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
3892 					reason), NULL, MM_NOTHING);
3893 
3894 				free(reason);
3895 
3896 				// Will be reset by caller. Signals refusal.
3897 				cl_mode = CL_ABORTED;
3898 			}
3899 			break;
3900 
3901 		case PT_SERVERCFG: // Positive response of client join request
3902 		{
3903 			if (server && serverrunning && node != servernode)
3904 			{ // but wait I thought I'm the server?
3905 				Net_CloseConnection(node);
3906 				break;
3907 			}
3908 			SERVERONLY
3909 			/// \note how would this happen? and is it doing the right thing if it does?
3910 			if (cl_mode != CL_WAITJOINRESPONSE)
3911 				break;
3912 
3913 			if (client)
3914 			{
3915 				maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
3916 				G_SetGametype(netbuffer->u.servercfg.gametype);
3917 				modifiedgame = netbuffer->u.servercfg.modifiedgame;
3918 				memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
3919 			}
3920 
3921 			nodeingame[(UINT8)servernode] = true;
3922 			serverplayer = netbuffer->u.servercfg.serverplayer;
3923 			doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
3924 			mynode = netbuffer->u.servercfg.clientnode;
3925 			if (serverplayer >= 0)
3926 				playernode[(UINT8)serverplayer] = servernode;
3927 
3928 			if (netgame)
3929 #ifndef NONET
3930 				CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n"));
3931 #else
3932 				CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n"));
3933 #endif
3934 			DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode));
3935 
3936 #ifndef NONET
3937 			/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
3938 			///       Shouldn't them be downloaded even at intermission time?
3939 			///       Also, according to HandleConnect, the server will send the savegame even during intermission...
3940 			if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* ||
3941 				netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
3942 				cl_mode = CL_DOWNLOADSAVEGAME;
3943 			else
3944 #endif
3945 				cl_mode = CL_CONNECTED;
3946 			break;
3947 		}
3948 
3949 		// Handled in d_netfil.c
3950 		case PT_FILEFRAGMENT:
3951 			if (server)
3952 			{ // But wait I thought I'm the server?
3953 				Net_CloseConnection(node);
3954 				break;
3955 			}
3956 			SERVERONLY
3957 			PT_FileFragment();
3958 			break;
3959 
3960 		case PT_FILEACK:
3961 			if (server)
3962 				PT_FileAck();
3963 			break;
3964 
3965 		case PT_FILERECEIVED:
3966 			if (server)
3967 				PT_FileReceived();
3968 			break;
3969 
3970 		case PT_REQUESTFILE:
3971 			if (server)
3972 			{
3973 				if (!cv_downloading.value || !PT_RequestFile(node))
3974 					Net_CloseConnection(node); // close connection if one of the requested files could not be sent, or you disabled downloading anyway
3975 			}
3976 			else
3977 				Net_CloseConnection(node); // nope
3978 			break;
3979 
3980 		case PT_NODETIMEOUT:
3981 		case PT_CLIENTQUIT:
3982 			if (server)
3983 				Net_CloseConnection(node);
3984 			break;
3985 
3986 		case PT_CLIENTCMD:
3987 			break; // This is not an "unknown packet"
3988 
3989 		case PT_SERVERTICS:
3990 			// Do not remove my own server (we have just get a out of order packet)
3991 			if (node == servernode)
3992 				break;
3993 			/* FALLTHRU */
3994 
3995 		default:
3996 			DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
3997 			Net_CloseConnection(node);
3998 			break; // Ignore it
3999 
4000 	}
4001 #undef SERVERONLY
4002 }
4003 
4004 /** Handles a packet received from a node that is in game
4005   *
4006   * \param node The packet sender
4007   * \todo Choose a better name
4008   * \sa HandlePacketFromAwayNode
4009   * \sa GetPackets
4010   *
4011   */
HandlePacketFromPlayer(SINT8 node)4012 static void HandlePacketFromPlayer(SINT8 node)
4013 {
4014 	INT32 netconsole;
4015 	tic_t realend, realstart;
4016 	UINT8 *pak, *txtpak, numtxtpak;
4017 #ifndef NOMD5
4018 	UINT8 finalmd5[16];/* Well, it's the cool thing to do? */
4019 #endif
4020 
4021 	txtpak = NULL;
4022 
4023 	if (dedicated && node == 0)
4024 		netconsole = 0;
4025 	else
4026 		netconsole = nodetoplayer[node];
4027 #ifdef PARANOIA
4028 	if (netconsole >= MAXPLAYERS)
4029 		I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
4030 #endif
4031 
4032 	switch (netbuffer->packettype)
4033 	{
4034 // -------------------------------------------- SERVER RECEIVE ----------
4035 		case PT_CLIENTCMD:
4036 		case PT_CLIENT2CMD:
4037 		case PT_CLIENTMIS:
4038 		case PT_CLIENT2MIS:
4039 		case PT_NODEKEEPALIVE:
4040 		case PT_NODEKEEPALIVEMIS:
4041 			if (client)
4042 				break;
4043 
4044 			// To save bytes, only the low byte of tic numbers are sent
4045 			// Use ExpandTics to figure out what the rest of the bytes are
4046 			realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node);
4047 			realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node);
4048 
4049 			if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
4050 				|| netbuffer->packettype == PT_NODEKEEPALIVEMIS
4051 				|| supposedtics[node] < realend)
4052 			{
4053 				supposedtics[node] = realend;
4054 			}
4055 			// Discard out of order packet
4056 			if (nettics[node] > realend)
4057 			{
4058 				DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node]));
4059 				break;
4060 			}
4061 
4062 			// Update the nettics
4063 			nettics[node] = realend;
4064 
4065 			// Don't do anything for packets of type NODEKEEPALIVE?
4066 			if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
4067 				|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
4068 				break;
4069 
4070 			// As long as clients send valid ticcmds, the server can keep running, so reset the timeout
4071 			/// \todo Use a separate cvar for that kind of timeout?
4072 			freezetimeout[node] = I_GetTime() + connectiontimeout;
4073 
4074 			// Copy ticcmd
4075 			G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
4076 
4077 			// Check ticcmd for "speed hacks"
4078 			if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
4079 				|| netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
4080 			{
4081 				CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole);
4082 				//D_Clearticcmd(k);
4083 
4084 				SendKick(netconsole, KICK_MSG_CON_FAIL);
4085 				break;
4086 			}
4087 
4088 			// Splitscreen cmd
4089 			if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
4090 				&& nodetoplayer2[node] >= 0)
4091 				G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
4092 					&netbuffer->u.client2pak.cmd2, 1);
4093 
4094 			// Check player consistancy during the level
4095 			if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL
4096 				&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy)
4097 				&& !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime()
4098 				&& !SV_ResendingSavegameToAnyone())
4099 			{
4100 				if (cv_resynchattempts.value)
4101 				{
4102 					// Tell the client we are about to resend them the gamestate
4103 					netbuffer->packettype = PT_WILLRESENDGAMESTATE;
4104 					HSendPacket(node, true, 0, 0);
4105 
4106 					resendingsavegame[node] = true;
4107 
4108 					if (cv_blamecfail.value)
4109 						CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
4110 							netconsole+1, player_names[netconsole],
4111 							consistancy[realstart%BACKUPTICS],
4112 							SHORT(netbuffer->u.clientpak.consistancy));
4113 					DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n",
4114 						netconsole, realstart, consistancy[realstart%BACKUPTICS],
4115 						SHORT(netbuffer->u.clientpak.consistancy)));
4116 					break;
4117 				}
4118 				else
4119 				{
4120 					SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
4121 					DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
4122 						netconsole, realstart, consistancy[realstart%BACKUPTICS],
4123 						SHORT(netbuffer->u.clientpak.consistancy)));
4124 					break;
4125 				}
4126 			}
4127 			break;
4128 		case PT_TEXTCMD2: // splitscreen special
4129 			netconsole = nodetoplayer2[node];
4130 			/* FALLTHRU */
4131 		case PT_TEXTCMD:
4132 			if (client)
4133 				break;
4134 
4135 			if (netconsole < 0 || netconsole >= MAXPLAYERS)
4136 				Net_UnAcknowledgePacket(node);
4137 			else
4138 			{
4139 				size_t j;
4140 				tic_t tic = maketic;
4141 				UINT8 *textcmd;
4142 
4143 				// ignore if the textcmd has a reported size of zero
4144 				// this shouldn't be sent at all
4145 				if (!netbuffer->u.textcmd[0])
4146 				{
4147 					DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n",
4148 						node, netconsole));
4149 					Net_UnAcknowledgePacket(node);
4150 					break;
4151 				}
4152 
4153 				// ignore if the textcmd size var is actually larger than it should be
4154 				// BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength
4155 				if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1)
4156 				{
4157 					DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n",
4158 					netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1),
4159 						node, netconsole));
4160 					Net_UnAcknowledgePacket(node);
4161 					break;
4162 				}
4163 
4164 				// check if tic that we are making isn't too large else we cannot send it :(
4165 				// doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time
4166 				j = software_MAXPACKETLENGTH
4167 					- (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE
4168 					+ (doomcom->numslots+1)*sizeof(ticcmd_t));
4169 
4170 				// search a tic that have enougth space in the ticcmd
4171 				while ((textcmd = D_GetExistingTextcmd(tic, netconsole)),
4172 					(TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD)
4173 					&& tic < firstticstosend + BACKUPTICS)
4174 					tic++;
4175 
4176 				if (tic >= firstticstosend + BACKUPTICS)
4177 				{
4178 					DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, "
4179 						"tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)),
4180 						maketic, firstticstosend, node, netconsole));
4181 					Net_UnAcknowledgePacket(node);
4182 					break;
4183 				}
4184 
4185 				// Make sure we have a buffer
4186 				if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole);
4187 
4188 				DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n",
4189 					tic, textcmd[0]+1, netconsole, firstticstosend, maketic));
4190 
4191 				M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
4192 				textcmd[0] += (UINT8)netbuffer->u.textcmd[0];
4193 			}
4194 			break;
4195 		case PT_LOGIN:
4196 			if (client)
4197 				break;
4198 
4199 #ifndef NOMD5
4200 			if (doomcom->datalength < 16)/* ignore partial sends */
4201 				break;
4202 
4203 			if (!adminpasswordset)
4204 			{
4205 				CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]);
4206 				break;
4207 			}
4208 
4209 			// Do the final pass to compare with the sent md5
4210 			D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5);
4211 
4212 			if (!memcmp(netbuffer->u.md5sum, finalmd5, 16))
4213 			{
4214 				CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]);
4215 				COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately
4216 			}
4217 			else
4218 				CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]);
4219 #endif
4220 			break;
4221 		case PT_NODETIMEOUT:
4222 		case PT_CLIENTQUIT:
4223 			if (client)
4224 				break;
4225 
4226 			// nodeingame will be put false in the execution of kick command
4227 			// this allow to send some packets to the quitting client to have their ack back
4228 			nodewaiting[node] = 0;
4229 			if (netconsole != -1 && playeringame[netconsole])
4230 			{
4231 				UINT8 kickmsg;
4232 
4233 				if (netbuffer->packettype == PT_NODETIMEOUT)
4234 					kickmsg = KICK_MSG_TIMEOUT;
4235 				else
4236 					kickmsg = KICK_MSG_PLAYER_QUIT;
4237 				kickmsg |= KICK_MSG_KEEP_BODY;
4238 
4239 				SendKick(netconsole, kickmsg);
4240 				nodetoplayer[node] = -1;
4241 
4242 				if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0
4243 					&& playeringame[(UINT8)nodetoplayer2[node]])
4244 				{
4245 					SendKick(nodetoplayer2[node], kickmsg);
4246 					nodetoplayer2[node] = -1;
4247 				}
4248 			}
4249 			Net_CloseConnection(node);
4250 			nodeingame[node] = false;
4251 			break;
4252 		case PT_CANRECEIVEGAMESTATE:
4253 			PT_CanReceiveGamestate(node);
4254 			break;
4255 		case PT_ASKLUAFILE:
4256 			if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED)
4257 				AddLuaFileToSendQueue(node, luafiletransfers->realfilename);
4258 			break;
4259 		case PT_HASLUAFILE:
4260 			if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING)
4261 				SV_HandleLuaFileSent(node);
4262 			break;
4263 		case PT_RECEIVEDGAMESTATE:
4264 			sendingsavegame[node] = false;
4265 			resendingsavegame[node] = false;
4266 			savegameresendcooldown[node] = I_GetTime() + 5 * TICRATE;
4267 			break;
4268 // -------------------------------------------- CLIENT RECEIVE ----------
4269 		case PT_SERVERTICS:
4270 			// Only accept PT_SERVERTICS from the server.
4271 			if (node != servernode)
4272 			{
4273 				CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node);
4274 				if (server)
4275 					SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
4276 				break;
4277 			}
4278 
4279 			realstart = netbuffer->u.serverpak.starttic;
4280 			realend = realstart + netbuffer->u.serverpak.numtics;
4281 
4282 			if (!txtpak)
4283 				txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots
4284 					* netbuffer->u.serverpak.numtics];
4285 
4286 			if (realend > gametic + CLIENTBACKUPTICS)
4287 				realend = gametic + CLIENTBACKUPTICS;
4288 			cl_packetmissed = realstart > neededtic;
4289 
4290 			if (realstart <= neededtic && realend > neededtic)
4291 			{
4292 				tic_t i, j;
4293 				pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
4294 
4295 				for (i = realstart; i < realend; i++)
4296 				{
4297 					// clear first
4298 					D_Clearticcmd(i);
4299 
4300 					// copy the tics
4301 					pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
4302 						netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
4303 
4304 					// copy the textcmds
4305 					numtxtpak = *txtpak++;
4306 					for (j = 0; j < numtxtpak; j++)
4307 					{
4308 						INT32 k = *txtpak++; // playernum
4309 						const size_t txtsize = txtpak[0]+1;
4310 
4311 						if (i >= gametic) // Don't copy old net commands
4312 							M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize);
4313 						txtpak += txtsize;
4314 					}
4315 				}
4316 
4317 				neededtic = realend;
4318 			}
4319 			else
4320 			{
4321 				DEBFILE(va("frame not in bound: %u\n", neededtic));
4322 				/*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart)
4323 					I_Error("Received an out of order PT_SERVERTICS packet!\n"
4324 							"Got tics %d-%d, needed tic %d\n\n"
4325 							"Please report this crash on the Master Board,\n"
4326 							"IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/
4327 			}
4328 			break;
4329 		case PT_PING:
4330 			// Only accept PT_PING from the server.
4331 			if (node != servernode)
4332 			{
4333 				CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node);
4334 				if (server)
4335 					SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
4336 				break;
4337 			}
4338 
4339 			//Update client ping table from the server.
4340 			if (client)
4341 			{
4342 				UINT8 i;
4343 				for (i = 0; i < MAXPLAYERS; i++)
4344 					if (playeringame[i])
4345 						playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i];
4346 
4347 				servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS];
4348 			}
4349 
4350 			break;
4351 		case PT_SERVERCFG:
4352 			break;
4353 		case PT_FILEFRAGMENT:
4354 			// Only accept PT_FILEFRAGMENT from the server.
4355 			if (node != servernode)
4356 			{
4357 				CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node);
4358 				if (server)
4359 					SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
4360 				break;
4361 			}
4362 			if (client)
4363 				PT_FileFragment();
4364 			break;
4365 		case PT_FILEACK:
4366 			if (server)
4367 				PT_FileAck();
4368 			break;
4369 		case PT_FILERECEIVED:
4370 			if (server)
4371 				PT_FileReceived();
4372 			break;
4373 		case PT_WILLRESENDGAMESTATE:
4374 			PT_WillResendGamestate();
4375 			break;
4376 		case PT_SENDINGLUAFILE:
4377 			if (client)
4378 				CL_PrepareDownloadLuaFile();
4379 			break;
4380 		default:
4381 			DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n",
4382 				netbuffer->packettype, node));
4383 	} // end switch
4384 }
4385 
4386 /**	Handles all received packets, if any
4387   *
4388   * \todo Add details to this description (lol)
4389   *
4390   */
GetPackets(void)4391 static void GetPackets(void)
4392 {
4393 	SINT8 node; // The packet sender
4394 
4395 	player_joining = false;
4396 
4397 	while (HGetPacket())
4398 	{
4399 		node = (SINT8)doomcom->remotenode;
4400 
4401 		if (netbuffer->packettype == PT_CLIENTJOIN && server)
4402 		{
4403 			HandleConnect(node);
4404 			continue;
4405 		}
4406 		if (node == servernode && client && cl_mode != CL_SEARCHING)
4407 		{
4408 			if (netbuffer->packettype == PT_SERVERSHUTDOWN)
4409 			{
4410 				HandleShutdown(node);
4411 				continue;
4412 			}
4413 			if (netbuffer->packettype == PT_NODETIMEOUT)
4414 			{
4415 				HandleTimeout(node);
4416 				continue;
4417 			}
4418 		}
4419 
4420 #ifndef NONET
4421 		if (netbuffer->packettype == PT_SERVERINFO)
4422 		{
4423 			HandleServerInfo(node);
4424 			continue;
4425 		}
4426 #endif
4427 
4428 		if (netbuffer->packettype == PT_PLAYERINFO)
4429 			continue; // We do nothing with PLAYERINFO, that's for the MS browser.
4430 
4431 		// Packet received from someone already playing
4432 		if (nodeingame[node])
4433 			HandlePacketFromPlayer(node);
4434 		// Packet received from someone not playing
4435 		else
4436 			HandlePacketFromAwayNode(node);
4437 	}
4438 }
4439 
4440 //
4441 // NetUpdate
4442 // Builds ticcmds for console player,
4443 // sends out a packet
4444 //
4445 // no more use random generator, because at very first tic isn't yet synchronized
4446 // Note: It is called consistAncy on purpose.
4447 //
Consistancy(void)4448 static INT16 Consistancy(void)
4449 {
4450 	INT32 i;
4451 	UINT32 ret = 0;
4452 #ifdef MOBJCONSISTANCY
4453 	thinker_t *th;
4454 	mobj_t *mo;
4455 #endif
4456 
4457 	DEBFILE(va("TIC %u ", gametic));
4458 
4459 	for (i = 0; i < MAXPLAYERS; i++)
4460 	{
4461 		if (!playeringame[i])
4462 			ret ^= 0xCCCC;
4463 		else if (!players[i].mo);
4464 		else
4465 		{
4466 			ret += players[i].mo->x;
4467 			ret -= players[i].mo->y;
4468 			ret += players[i].powers[pw_shield];
4469 			ret *= i+1;
4470 		}
4471 	}
4472 	// I give up
4473 	// Coop desynching enemies is painful
4474 	if (!G_PlatformGametype())
4475 		ret += P_GetRandSeed();
4476 
4477 #ifdef MOBJCONSISTANCY
4478 	if (gamestate == GS_LEVEL)
4479 	{
4480 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
4481 		{
4482 			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
4483 				continue;
4484 
4485 			mo = (mobj_t *)th;
4486 
4487 			if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY))
4488 			{
4489 				ret -= mo->type;
4490 				ret += mo->x;
4491 				ret -= mo->y;
4492 				ret += mo->z;
4493 				ret -= mo->momx;
4494 				ret += mo->momy;
4495 				ret -= mo->momz;
4496 				ret += mo->angle;
4497 				ret -= mo->flags;
4498 				ret += mo->flags2;
4499 				ret -= mo->eflags;
4500 				if (mo->target)
4501 				{
4502 					ret += mo->target->type;
4503 					ret -= mo->target->x;
4504 					ret += mo->target->y;
4505 					ret -= mo->target->z;
4506 					ret += mo->target->momx;
4507 					ret -= mo->target->momy;
4508 					ret += mo->target->momz;
4509 					ret -= mo->target->angle;
4510 					ret += mo->target->flags;
4511 					ret -= mo->target->flags2;
4512 					ret += mo->target->eflags;
4513 					ret -= mo->target->state - states;
4514 					ret += mo->target->tics;
4515 					ret -= mo->target->sprite;
4516 					ret += mo->target->frame;
4517 				}
4518 				else
4519 					ret ^= 0x3333;
4520 				if (mo->tracer && mo->tracer->type != MT_OVERLAY)
4521 				{
4522 					ret += mo->tracer->type;
4523 					ret -= mo->tracer->x;
4524 					ret += mo->tracer->y;
4525 					ret -= mo->tracer->z;
4526 					ret += mo->tracer->momx;
4527 					ret -= mo->tracer->momy;
4528 					ret += mo->tracer->momz;
4529 					ret -= mo->tracer->angle;
4530 					ret += mo->tracer->flags;
4531 					ret -= mo->tracer->flags2;
4532 					ret += mo->tracer->eflags;
4533 					ret -= mo->tracer->state - states;
4534 					ret += mo->tracer->tics;
4535 					ret -= mo->tracer->sprite;
4536 					ret += mo->tracer->frame;
4537 				}
4538 				else
4539 					ret ^= 0xAAAA;
4540 				ret -= mo->state - states;
4541 				ret += mo->tics;
4542 				ret -= mo->sprite;
4543 				ret += mo->frame;
4544 			}
4545 		}
4546 	}
4547 #endif
4548 
4549 	DEBFILE(va("Consistancy = %u\n", (ret & 0xFFFF)));
4550 
4551 	return (INT16)(ret & 0xFFFF);
4552 }
4553 
4554 // send the client packet to the server
CL_SendClientCmd(void)4555 static void CL_SendClientCmd(void)
4556 {
4557 	size_t packetsize = 0;
4558 
4559 	netbuffer->packettype = PT_CLIENTCMD;
4560 
4561 	if (cl_packetmissed)
4562 		netbuffer->packettype++;
4563 	netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX);
4564 	netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX);
4565 
4566 	if (gamestate == GS_WAITINGPLAYERS)
4567 	{
4568 		// Send PT_NODEKEEPALIVE packet
4569 		netbuffer->packettype += 4;
4570 		packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16);
4571 		HSendPacket(servernode, false, 0, packetsize);
4572 	}
4573 	else if (gamestate != GS_NULL && (addedtogame || dedicated))
4574 	{
4575 		G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
4576 		netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
4577 
4578 		// Send a special packet with 2 cmd for splitscreen
4579 		if (splitscreen || botingame)
4580 		{
4581 			netbuffer->packettype += 2;
4582 			G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1);
4583 			packetsize = sizeof (client2cmd_pak);
4584 		}
4585 		else
4586 			packetsize = sizeof (clientcmd_pak);
4587 
4588 		HSendPacket(servernode, false, 0, packetsize);
4589 	}
4590 
4591 	if (cl_mode == CL_CONNECTED || dedicated)
4592 	{
4593 		// Send extra data if needed
4594 		if (localtextcmd[0])
4595 		{
4596 			netbuffer->packettype = PT_TEXTCMD;
4597 			M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
4598 			// All extra data have been sent
4599 			if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail...
4600 				localtextcmd[0] = 0;
4601 		}
4602 
4603 		// Send extra data if needed for player 2 (splitscreen)
4604 		if (localtextcmd2[0])
4605 		{
4606 			netbuffer->packettype = PT_TEXTCMD2;
4607 			M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1);
4608 			// All extra data have been sent
4609 			if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail...
4610 				localtextcmd2[0] = 0;
4611 		}
4612 	}
4613 }
4614 
4615 // send the server packet
4616 // send tic from firstticstosend to maketic-1
SV_SendTics(void)4617 static void SV_SendTics(void)
4618 {
4619 	tic_t realfirsttic, lasttictosend, i;
4620 	UINT32 n;
4621 	INT32 j;
4622 	size_t packsize;
4623 	UINT8 *bufpos;
4624 	UINT8 *ntextcmd;
4625 
4626 	// send to all client but not to me
4627 	// for each node create a packet with x tics and send it
4628 	// x is computed using supposedtics[n], max packet size and maketic
4629 	for (n = 1; n < MAXNETNODES; n++)
4630 		if (nodeingame[n])
4631 		{
4632 			// assert supposedtics[n]>=nettics[n]
4633 			realfirsttic = supposedtics[n];
4634 			lasttictosend = min(maketic, nettics[n] + CLIENTBACKUPTICS);
4635 
4636 			if (realfirsttic >= lasttictosend)
4637 			{
4638 				// well we have sent all tics we will so use extrabandwidth
4639 				// to resent packet that are supposed lost (this is necessary since lost
4640 				// packet detection work when we have received packet with firsttic > neededtic
4641 				// (getpacket servertics case)
4642 				DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n",
4643 					n, maketic, supposedtics[n], nettics[n]));
4644 				realfirsttic = nettics[n];
4645 				if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3)
4646 					// all tic are ok
4647 					continue;
4648 				DEBFILE(va("Sent %d anyway\n", realfirsttic));
4649 			}
4650 			if (realfirsttic < firstticstosend)
4651 				realfirsttic = firstticstosend;
4652 
4653 			// compute the length of the packet and cut it if too large
4654 			packsize = BASESERVERTICSSIZE;
4655 			for (i = realfirsttic; i < lasttictosend; i++)
4656 			{
4657 				packsize += sizeof (ticcmd_t) * doomcom->numslots;
4658 				packsize += TotalTextCmdPerTic(i);
4659 
4660 				if (packsize > software_MAXPACKETLENGTH)
4661 				{
4662 					DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n",
4663 						sizeu1(packsize), i, realfirsttic, lasttictosend));
4664 					lasttictosend = i;
4665 
4666 					// too bad: too much player have send extradata and there is too
4667 					//          much data in one tic.
4668 					// To avoid it put the data on the next tic. (see getpacket
4669 					// textcmd case) but when numplayer changes the computation can be different
4670 					if (lasttictosend == realfirsttic)
4671 					{
4672 						if (packsize > MAXPACKETLENGTH)
4673 							I_Error("Too many players: can't send %s data for %d players to node %d\n"
4674 							        "Well sorry nobody is perfect....\n",
4675 							        sizeu1(packsize), doomcom->numslots, n);
4676 						else
4677 						{
4678 							lasttictosend++; // send it anyway!
4679 							DEBFILE("sending it anyway\n");
4680 						}
4681 					}
4682 					break;
4683 				}
4684 			}
4685 
4686 			// Send the tics
4687 			netbuffer->packettype = PT_SERVERTICS;
4688 			netbuffer->u.serverpak.starttic = realfirsttic;
4689 			netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic);
4690 			netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots);
4691 			bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds;
4692 
4693 			for (i = realfirsttic; i < lasttictosend; i++)
4694 			{
4695 				bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t));
4696 			}
4697 
4698 			// add textcmds
4699 			for (i = realfirsttic; i < lasttictosend; i++)
4700 			{
4701 				ntextcmd = bufpos++;
4702 				*ntextcmd = 0;
4703 				for (j = 0; j < MAXPLAYERS; j++)
4704 				{
4705 					UINT8 *textcmd = D_GetExistingTextcmd(i, j);
4706 					INT32 size = textcmd ? textcmd[0] : 0;
4707 
4708 					if ((!j || playeringame[j]) && size)
4709 					{
4710 						(*ntextcmd)++;
4711 						WRITEUINT8(bufpos, j);
4712 						M_Memcpy(bufpos, textcmd, size + 1);
4713 						bufpos += size + 1;
4714 					}
4715 				}
4716 			}
4717 			packsize = bufpos - (UINT8 *)&(netbuffer->u);
4718 
4719 			HSendPacket(n, false, 0, packsize);
4720 			// when tic are too large, only one tic is sent so don't go backward!
4721 			if (lasttictosend-doomcom->extratics > realfirsttic)
4722 				supposedtics[n] = lasttictosend-doomcom->extratics;
4723 			else
4724 				supposedtics[n] = lasttictosend;
4725 			if (supposedtics[n] < nettics[n]) supposedtics[n] = nettics[n];
4726 		}
4727 	// node 0 is me!
4728 	supposedtics[0] = maketic;
4729 }
4730 
4731 //
4732 // TryRunTics
4733 //
Local_Maketic(INT32 realtics)4734 static void Local_Maketic(INT32 realtics)
4735 {
4736 	I_OsPolling(); // I_Getevent
4737 	D_ProcessEvents(); // menu responder, cons responder,
4738 	                   // game responder calls HU_Responder, AM_Responder,
4739 	                   // and G_MapEventsToControls
4740 	if (!dedicated) rendergametic = gametic;
4741 	// translate inputs (keyboard/mouse/joystick) into game controls
4742 	G_BuildTiccmd(&localcmds, realtics, 1);
4743 	if (splitscreen || botingame)
4744 		G_BuildTiccmd(&localcmds2, realtics, 2);
4745 
4746 	localcmds.angleturn |= TICCMD_RECEIVED;
4747 	localcmds2.angleturn |= TICCMD_RECEIVED;
4748 }
4749 
4750 // create missed tic
SV_Maketic(void)4751 static void SV_Maketic(void)
4752 {
4753 	INT32 i;
4754 
4755 	for (i = 0; i < MAXPLAYERS; i++)
4756 	{
4757 		if (!playeringame[i])
4758 			continue;
4759 
4760 		// We didn't receive this tic
4761 		if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0)
4762 		{
4763 			ticcmd_t *    ticcmd = &netcmds[(maketic    ) % BACKUPTICS][i];
4764 			ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i];
4765 
4766 			if (players[i].quittime)
4767 			{
4768 				// Copy the angle/aiming from the previous tic
4769 				// and empty the other inputs
4770 				memset(ticcmd, 0, sizeof(netcmds[0][0]));
4771 				ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED;
4772 				ticcmd->aiming = prevticcmd->aiming;
4773 			}
4774 			else
4775 			{
4776 				DEBFILE(va("MISS tic%4d for player %d\n", maketic, i));
4777 				// Copy the input from the previous tic
4778 				*ticcmd = *prevticcmd;
4779 				ticcmd->angleturn &= ~TICCMD_RECEIVED;
4780 			}
4781 		}
4782 	}
4783 
4784 	// all tic are now proceed make the next
4785 	maketic++;
4786 }
4787 
TryRunTics(tic_t realtics)4788 void TryRunTics(tic_t realtics)
4789 {
4790 	// the machine has lagged but it is not so bad
4791 	if (realtics > TICRATE/7) // FIXME: consistency failure!!
4792 	{
4793 		if (server)
4794 			realtics = 1;
4795 		else
4796 			realtics = TICRATE/7;
4797 	}
4798 
4799 	if (singletics)
4800 		realtics = 1;
4801 
4802 	if (realtics >= 1)
4803 	{
4804 		COM_BufTicker();
4805 		if (mapchangepending)
4806 			D_MapChange(-1, 0, ultimatemode, false, 2, false, fromlevelselect); // finish the map change
4807 	}
4808 
4809 	NetUpdate();
4810 
4811 	if (demoplayback)
4812 	{
4813 		neededtic = gametic + (realtics * cv_playbackspeed.value);
4814 		// start a game after a demo
4815 		maketic += realtics;
4816 		firstticstosend = maketic;
4817 		tictoclear = firstticstosend;
4818 	}
4819 
4820 	GetPackets();
4821 
4822 #ifdef DEBUGFILE
4823 	if (debugfile && (realtics || neededtic > gametic))
4824 	{
4825 		//SoM: 3/30/2000: Need long INT32 in the format string for args 4 & 5.
4826 		//Shut up stupid warning!
4827 		fprintf(debugfile, "------------ Tryruntic: REAL:%d NEED:%d GAME:%d LOAD: %d\n",
4828 			realtics, neededtic, gametic, debugload);
4829 		debugload = 100000;
4830 	}
4831 #endif
4832 
4833 	if (player_joining)
4834 		return;
4835 
4836 	if (neededtic > gametic)
4837 	{
4838 		if (advancedemo)
4839 		{
4840 			if (timedemo_quit)
4841 				COM_ImmedExecute("quit");
4842 			else
4843 				D_StartTitle();
4844 		}
4845 		else
4846 			// run the count * tics
4847 			while (neededtic > gametic)
4848 			{
4849 				DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic));
4850 
4851 				ps_tictime = I_GetPreciseTime();
4852 
4853 				G_Ticker((gametic % NEWTICRATERATIO) == 0);
4854 				ExtraDataTicker();
4855 				gametic++;
4856 				consistancy[gametic%BACKUPTICS] = Consistancy();
4857 
4858 				ps_tictime = I_GetPreciseTime() - ps_tictime;
4859 
4860 				// Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame.
4861 				if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value)
4862 					break;
4863 			}
4864 	}
4865 }
4866 
4867 /*
4868 Ping Update except better:
4869 We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
4870 If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
4871 */
4872 
4873 static INT32 pingtimeout[MAXPLAYERS];
4874 
PingUpdate(void)4875 static inline void PingUpdate(void)
4876 {
4877 	INT32 i;
4878 	boolean laggers[MAXPLAYERS];
4879 	UINT8 numlaggers = 0;
4880 	memset(laggers, 0, sizeof(boolean) * MAXPLAYERS);
4881 
4882 	netbuffer->packettype = PT_PING;
4883 
4884 	//check for ping limit breakage.
4885 	if (cv_maxping.value)
4886 	{
4887 		for (i = 1; i < MAXPLAYERS; i++)
4888 		{
4889 			if (playeringame[i] && !players[i].quittime
4890 			&& (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value))
4891 			{
4892 				if (players[i].jointime > 30 * TICRATE)
4893 					laggers[i] = true;
4894 				numlaggers++;
4895 			}
4896 			else
4897 				pingtimeout[i] = 0;
4898 		}
4899 
4900 		//kick lagging players... unless everyone but the server's ping sucks.
4901 		//in that case, it is probably the server's fault.
4902 		if (numlaggers < D_NumPlayers() - 1)
4903 		{
4904 			for (i = 1; i < MAXPLAYERS; i++)
4905 			{
4906 				if (playeringame[i] && laggers[i])
4907 				{
4908 					pingtimeout[i]++;
4909 					// ok your net has been bad for too long, you deserve to die.
4910 					if (pingtimeout[i] > cv_pingtimeout.value)
4911 					{
4912 						pingtimeout[i] = 0;
4913 						SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY);
4914 					}
4915 				}
4916 				/*
4917 					you aren't lagging,
4918 					but you aren't free yet.
4919 					In case you'll keep spiking,
4920 					we just make the timer go back down. (Very unstable net must still get kicked).
4921 				*/
4922 				else
4923 					pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1);
4924 			}
4925 		}
4926 	}
4927 
4928 	//make the ping packet and clear server data for next one
4929 	for (i = 0; i < MAXPLAYERS; i++)
4930 	{
4931 		netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount;
4932 		//server takes a snapshot of the real ping for display.
4933 		//otherwise, pings fluctuate a lot and would be odd to look at.
4934 		playerpingtable[i] = realpingtable[i] / pingmeasurecount;
4935 		realpingtable[i] = 0; //Reset each as we go.
4936 	}
4937 
4938 	// send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked.
4939 	netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value;
4940 
4941 	//send out our ping packets
4942 	for (i = 0; i < MAXNETNODES; i++)
4943 		if (nodeingame[i])
4944 			HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1));
4945 
4946 	pingmeasurecount = 1; //Reset count
4947 }
4948 
NetUpdate(void)4949 void NetUpdate(void)
4950 {
4951 	static tic_t gametime = 0;
4952 	static tic_t resptime = 0;
4953 	tic_t nowtime;
4954 	INT32 i;
4955 	INT32 realtics;
4956 
4957 	nowtime = I_GetTime();
4958 	realtics = nowtime - gametime;
4959 
4960 	if (realtics <= 0) // nothing new to update
4961 		return;
4962 	if (realtics > 5)
4963 	{
4964 		if (server)
4965 			realtics = 1;
4966 		else
4967 			realtics = 5;
4968 	}
4969 
4970 	gametime = nowtime;
4971 
4972 	if (server)
4973 	{
4974 		if (netgame && !(gametime % 35)) // update once per second.
4975 			PingUpdate();
4976 		// update node latency values so we can take an average later.
4977 		for (i = 0; i < MAXPLAYERS; i++)
4978 			if (playeringame[i] && playernode[i] != UINT8_MAX)
4979 				realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
4980 		pingmeasurecount++;
4981 	}
4982 
4983 	if (client)
4984 		maketic = neededtic;
4985 
4986 	Local_Maketic(realtics); // make local tic, and call menu?
4987 
4988 	if (server)
4989 		CL_SendClientCmd(); // send it
4990 
4991 	GetPackets(); // get packet from client or from server
4992 
4993 	// client send the command after a receive of the server
4994 	// the server send before because in single player is beter
4995 
4996 #ifdef MASTERSERVER
4997 	MasterClient_Ticker(); // Acking the Master Server
4998 #endif
4999 
5000 	if (client)
5001 	{
5002 		// If the client just finished redownloading the game state, load it
5003 		if (cl_redownloadinggamestate && fileneeded[0].status == FS_FOUND)
5004 			CL_ReloadReceivedSavegame();
5005 
5006 		CL_SendClientCmd(); // Send tic cmd
5007 		hu_redownloadinggamestate = cl_redownloadinggamestate;
5008 	}
5009 	else
5010 	{
5011 		if (!demoplayback)
5012 		{
5013 			INT32 counts;
5014 
5015 			hu_redownloadinggamestate = false;
5016 
5017 			firstticstosend = gametic;
5018 			for (i = 0; i < MAXNETNODES; i++)
5019 				if (nodeingame[i] && nettics[i] < firstticstosend)
5020 				{
5021 					firstticstosend = nettics[i];
5022 
5023 					if (maketic + 1 >= nettics[i] + BACKUPTICS)
5024 						Net_ConnectionTimeout(i);
5025 				}
5026 
5027 			// Don't erase tics not acknowledged
5028 			counts = realtics;
5029 
5030 			if (maketic + counts >= firstticstosend + BACKUPTICS)
5031 				counts = firstticstosend+BACKUPTICS-maketic-1;
5032 
5033 			for (i = 0; i < counts; i++)
5034 				SV_Maketic(); // Create missed tics and increment maketic
5035 
5036 			for (; tictoclear < firstticstosend; tictoclear++) // Clear only when acknowledged
5037 				D_Clearticcmd(tictoclear);                    // Clear the maketic the new tic
5038 
5039 			SV_SendTics();
5040 
5041 			neededtic = maketic; // The server is a client too
5042 		}
5043 	}
5044 
5045 	Net_AckTicker();
5046 
5047 	// Handle timeouts to prevent definitive freezes from happenning
5048 	if (server)
5049 	{
5050 		for (i = 1; i < MAXNETNODES; i++)
5051 			if (nodeingame[i] && freezetimeout[i] < I_GetTime())
5052 				Net_ConnectionTimeout(i);
5053 
5054 		// In case the cvar value was lowered
5055 		if (joindelay)
5056 			joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE);
5057 	}
5058 
5059 	nowtime /= NEWTICRATERATIO;
5060 	if (nowtime > resptime)
5061 	{
5062 		resptime = nowtime;
5063 #ifdef HAVE_THREADS
5064 		I_lock_mutex(&m_menu_mutex);
5065 #endif
5066 		M_Ticker();
5067 #ifdef HAVE_THREADS
5068 		I_unlock_mutex(m_menu_mutex);
5069 #endif
5070 		CON_Ticker();
5071 	}
5072 
5073 	FileSendTicker();
5074 }
5075 
5076 /** Returns the number of players playing.
5077   * \return Number of players. Can be zero if we're running a ::dedicated
5078   *         server.
5079   * \author Graue <graue@oceanbase.org>
5080   */
D_NumPlayers(void)5081 INT32 D_NumPlayers(void)
5082 {
5083 	INT32 num = 0, ix;
5084 	for (ix = 0; ix < MAXPLAYERS; ix++)
5085 		if (playeringame[ix])
5086 			num++;
5087 	return num;
5088 }
5089 
GetLag(INT32 node)5090 tic_t GetLag(INT32 node)
5091 {
5092 	return gametic - nettics[node];
5093 }
5094 
D_MD5PasswordPass(const UINT8 * buffer,size_t len,const char * salt,void * dest)5095 void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest)
5096 {
5097 #ifdef NOMD5
5098 	(void)buffer;
5099 	(void)len;
5100 	(void)salt;
5101 	memset(dest, 0, 16);
5102 #else
5103 	char tmpbuf[256];
5104 	const size_t sl = strlen(salt);
5105 
5106 	if (len > 256-sl)
5107 		len = 256-sl;
5108 
5109 	memcpy(tmpbuf, buffer, len);
5110 	memmove(&tmpbuf[len], salt, sl);
5111 	//strcpy(&tmpbuf[len], salt);
5112 	len += strlen(salt);
5113 	if (len < 256)
5114 		memset(&tmpbuf[len],0,256-len);
5115 
5116 	// Yes, we intentionally md5 the ENTIRE buffer regardless of size...
5117 	md5_buffer(tmpbuf, 256, dest);
5118 #endif
5119 }
5120