1 // Emacs style mode select	 -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 //		DOOM Network game communication and protocol,
21 //		all OS independent parts.
22 //
23 //-----------------------------------------------------------------------------
24 
25 #include <stddef.h>
26 
27 #include "version.h"
28 #include "menu/menu.h"
29 #include "m_random.h"
30 #include "i_system.h"
31 #include "i_video.h"
32 #include "i_net.h"
33 #include "g_game.h"
34 #include "doomdef.h"
35 #include "doomstat.h"
36 #include "c_console.h"
37 #include "d_netinf.h"
38 #include "d_net.h"
39 #include "cmdlib.h"
40 #include "s_sound.h"
41 #include "m_cheat.h"
42 #include "p_local.h"
43 #include "c_dispatch.h"
44 #include "sbar.h"
45 #include "gi.h"
46 #include "m_misc.h"
47 #include "gameconfigfile.h"
48 #include "d_gui.h"
49 #include "templates.h"
50 #include "p_acs.h"
51 #include "p_trace.h"
52 #include "a_sharedglobal.h"
53 #include "st_start.h"
54 #include "teaminfo.h"
55 #include "p_conversation.h"
56 #include "g_level.h"
57 #include "d_event.h"
58 #include "m_argv.h"
59 #include "p_lnspec.h"
60 #include "v_video.h"
61 #include "p_spec.h"
62 #include "hardware.h"
63 #include "intermission/intermission.h"
64 
65 EXTERN_CVAR (Int, disableautosave)
66 EXTERN_CVAR (Int, autosavecount)
67 
68 //#define SIMULATEERRORS		(RAND_MAX/3)
69 #define SIMULATEERRORS			0
70 
71 extern BYTE		*demo_p;		// [RH] Special "ticcmds" get recorded in demos
72 extern char		savedescription[SAVESTRINGSIZE];
73 extern FString	savegamefile;
74 
75 extern short consistancy[MAXPLAYERS][BACKUPTICS];
76 
77 doomcom_t		doomcom;
78 #define netbuffer (doomcom.data)
79 
80 enum { NET_PeerToPeer, NET_PacketServer };
81 BYTE NetMode = NET_PeerToPeer;
82 
83 
84 
85 //
86 // NETWORKING
87 //
88 // gametic is the tic about to (or currently being) run
89 // maketic is the tick that hasn't had control made for it yet
90 // nettics[] has the maketics for all players
91 //
92 // a gametic cannot be run until nettics[] > gametic for all players
93 //
94 #define RESENDCOUNT 	10
95 #define PL_DRONE		0x80	// bit flag in doomdata->player
96 
97 ticcmd_t		localcmds[LOCALCMDTICS];
98 
99 FDynamicBuffer	NetSpecs[MAXPLAYERS][BACKUPTICS];
100 ticcmd_t		netcmds[MAXPLAYERS][BACKUPTICS];
101 int 			nettics[MAXNETNODES];
102 bool 			nodeingame[MAXNETNODES];				// set false as nodes leave game
103 bool			nodejustleft[MAXNETNODES];				// set when a node just left
104 bool	 		remoteresend[MAXNETNODES];				// set when local needs tics
105 int 			resendto[MAXNETNODES];					// set when remote needs tics
106 int 			resendcount[MAXNETNODES];
107 
108 unsigned int	lastrecvtime[MAXPLAYERS];				// [RH] Used for pings
109 unsigned int	currrecvtime[MAXPLAYERS];
110 unsigned int	lastglobalrecvtime;						// Identify the last time a packet was received.
111 bool			hadlate;
112 int				netdelay[MAXNETNODES][BACKUPTICS];		// Used for storing network delay times.
113 int				lastaverage;
114 
115 int 			nodeforplayer[MAXPLAYERS];
116 int				playerfornode[MAXNETNODES];
117 
118 int 			maketic;
119 int 			skiptics;
120 int 			ticdup;
121 
122 void D_ProcessEvents (void);
123 void G_BuildTiccmd (ticcmd_t *cmd);
124 void D_DoAdvanceDemo (void);
125 
126 static void SendSetup (DWORD playersdetected[MAXNETNODES], BYTE gotsetup[MAXNETNODES], int len);
127 static void RunScript(BYTE **stream, APlayerPawn *pawn, int snum, int argn, int always);
128 
129 int		reboundpacket;
130 BYTE	reboundstore[MAX_MSGLEN];
131 
132 int 	frameon;
133 int 	frameskip[4];
134 int 	oldnettics;
135 int		mastertics;
136 
137 static int 	entertic;
138 static int	oldentertics;
139 
140 extern	bool	 advancedemo;
141 
142 CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
143 {
144 	// Do not use the separate FPS limit timer if we are limiting FPS with this.
145 	if (self)
146 	{
147 		I_SetFPSLimit(0);
148 	}
149 	else
150 	{
151 		I_SetFPSLimit(-1);
152 	}
153 }
154 
155 CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO | CVAR_NOSAVE)
156 CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO | CVAR_NOSAVE)
157 {
158 	if (self < 0)
159 	{
160 		self = 0;
161 	}
162 	else if (self > 2)
163 	{
164 		self = 2;
165 	}
166 }
167 
168 #ifdef _DEBUG
169 CVAR(Int, net_fakelatency, 0, 0);
170 
171 struct PacketStore
172 {
173 	int timer;
174 	doomcom_t message;
175 };
176 
177 static TArray<PacketStore> InBuffer;
178 static TArray<PacketStore> OutBuffer;
179 #endif
180 
181 // [RH] Special "ticcmds" get stored in here
182 static struct TicSpecial
183 {
184 	BYTE *streams[BACKUPTICS];
185 	size_t used[BACKUPTICS];
186 	BYTE *streamptr;
187 	size_t streamoffs;
188 	size_t specialsize;
189 	int	  lastmaketic;
190 	bool  okay;
191 
TicSpecialTicSpecial192 	TicSpecial ()
193 	{
194 		int i;
195 
196 		lastmaketic = -1;
197 		specialsize = 256;
198 
199 		for (i = 0; i < BACKUPTICS; i++)
200 			streams[i] = NULL;
201 
202 		for (i = 0; i < BACKUPTICS; i++)
203 		{
204 			streams[i] = (BYTE *)M_Malloc (256);
205 			used[i] = 0;
206 		}
207 		okay = true;
208 	}
209 
~TicSpecialTicSpecial210 	~TicSpecial ()
211 	{
212 		int i;
213 
214 		for (i = 0; i < BACKUPTICS; i++)
215 		{
216 			if (streams[i])
217 			{
218 				M_Free (streams[i]);
219 				streams[i] = NULL;
220 				used[i] = 0;
221 			}
222 		}
223 		okay = false;
224 	}
225 
226 	// Make more room for special commands.
GetMoreSpaceTicSpecial227 	void GetMoreSpace (size_t needed)
228 	{
229 		int i;
230 
231 		specialsize = MAX(specialsize * 2, needed + 30);
232 
233 		DPrintf ("Expanding special size to %zu\n", specialsize);
234 
235 		for (i = 0; i < BACKUPTICS; i++)
236 			streams[i] = (BYTE *)M_Realloc (streams[i], specialsize);
237 
238 		streamptr = streams[(maketic/ticdup)%BACKUPTICS] + streamoffs;
239 	}
240 
CheckSpaceTicSpecial241 	void CheckSpace (size_t needed)
242 	{
243 		if (streamoffs + needed >= specialsize)
244 			GetMoreSpace (streamoffs + needed);
245 
246 		streamoffs += needed;
247 	}
248 
NewMakeTicTicSpecial249 	void NewMakeTic ()
250 	{
251 		int mt = maketic / ticdup;
252 		if (lastmaketic != -1)
253 		{
254 			if (lastmaketic == mt)
255 				return;
256 			used[lastmaketic%BACKUPTICS] = streamoffs;
257 		}
258 
259 		lastmaketic = mt;
260 		streamptr = streams[mt%BACKUPTICS];
261 		streamoffs = 0;
262 	}
263 
operator <<TicSpecial264 	TicSpecial &operator << (BYTE it)
265 	{
266 		if (streamptr)
267 		{
268 			CheckSpace (1);
269 			WriteByte (it, &streamptr);
270 		}
271 		return *this;
272 	}
273 
operator <<TicSpecial274 	TicSpecial &operator << (short it)
275 	{
276 		if (streamptr)
277 		{
278 			CheckSpace (2);
279 			WriteWord (it, &streamptr);
280 		}
281 		return *this;
282 	}
283 
operator <<TicSpecial284 	TicSpecial &operator << (int it)
285 	{
286 		if (streamptr)
287 		{
288 			CheckSpace (4);
289 			WriteLong (it, &streamptr);
290 		}
291 		return *this;
292 	}
293 
operator <<TicSpecial294 	TicSpecial &operator << (float it)
295 	{
296 		if (streamptr)
297 		{
298 			CheckSpace (4);
299 			WriteFloat (it, &streamptr);
300 		}
301 		return *this;
302 	}
303 
operator <<TicSpecial304 	TicSpecial &operator << (const char *it)
305 	{
306 		if (streamptr)
307 		{
308 			CheckSpace (strlen (it) + 1);
309 			WriteString (it, &streamptr);
310 		}
311 		return *this;
312 	}
313 
314 } specials;
315 
Net_ClearBuffers()316 void Net_ClearBuffers ()
317 {
318 	int i, j;
319 
320 	memset (localcmds, 0, sizeof(localcmds));
321 	memset (netcmds, 0, sizeof(netcmds));
322 	memset (nettics, 0, sizeof(nettics));
323 	memset (nodeingame, 0, sizeof(nodeingame));
324 	memset (remoteresend, 0, sizeof(remoteresend));
325 	memset (resendto, 0, sizeof(resendto));
326 	memset (resendcount, 0, sizeof(resendcount));
327 	memset (lastrecvtime, 0, sizeof(lastrecvtime));
328 	memset (currrecvtime, 0, sizeof(currrecvtime));
329 	memset (consistancy, 0, sizeof(consistancy));
330 	nodeingame[0] = true;
331 
332 	for (i = 0; i < MAXPLAYERS; i++)
333 	{
334 		for (j = 0; j < BACKUPTICS; j++)
335 		{
336 			NetSpecs[i][j].SetData (NULL, 0);
337 		}
338 	}
339 
340 	oldentertics = entertic;
341 	gametic = 0;
342 	maketic = 0;
343 
344 	lastglobalrecvtime = 0;
345 }
346 
347 //
348 // [RH] Rewritten to properly calculate the packet size
349 //		with our variable length commands.
350 //
NetbufferSize()351 int NetbufferSize ()
352 {
353 	if (netbuffer[0] & (NCMD_EXIT | NCMD_SETUP))
354 	{
355 		return doomcom.datalength;
356 	}
357 
358 	int k = 2, count, numtics;
359 
360 	if (netbuffer[0] & NCMD_RETRANSMIT)
361 		k++;
362 
363 	if (NetMode == NET_PacketServer && doomcom.remotenode == nodeforplayer[Net_Arbitrator])
364 		k++;
365 
366 	numtics = netbuffer[0] & NCMD_XTICS;
367 	if (numtics == 3)
368 	{
369 		numtics += netbuffer[k++];
370 	}
371 
372 	if (netbuffer[0] & NCMD_QUITTERS)
373 	{
374 		k += netbuffer[k] + 1;
375 	}
376 
377 	// Network delay byte
378 	k++;
379 
380 	if (netbuffer[0] & NCMD_MULTI)
381 	{
382 		count = netbuffer[k];
383 		k += count;
384 	}
385 	else
386 	{
387 		count = 1;
388 	}
389 
390 	// Need at least 3 bytes per tic per player
391 	if (doomcom.datalength < k + 3 * count * numtics)
392 	{
393 		return k + 3 * count * numtics;
394 	}
395 
396 	BYTE *skipper = &netbuffer[k];
397 	if ((netbuffer[0] & NCMD_EXIT) == 0)
398 	{
399 		while (count-- > 0)
400 		{
401 			SkipTicCmd (&skipper, numtics);
402 		}
403 	}
404 	return int(skipper - netbuffer);
405 }
406 
407 //
408 //
409 //
ExpandTics(int low)410 int ExpandTics (int low)
411 {
412 	int delta;
413 	int mt = maketic / ticdup;
414 
415 	delta = low - (mt&0xff);
416 
417 	if (delta >= -64 && delta <= 64)
418 		return (mt&~0xff) + low;
419 	if (delta > 64)
420 		return (mt&~0xff) - 256 + low;
421 	if (delta < -64)
422 		return (mt&~0xff) + 256 + low;
423 
424 	I_Error ("ExpandTics: strange value %i at maketic %i", low, maketic);
425 	return 0;
426 }
427 
428 
429 
430 //
431 // HSendPacket
432 //
HSendPacket(int node,int len)433 void HSendPacket (int node, int len)
434 {
435 	if (debugfile && node != 0)
436 	{
437 		int i, k, realretrans;
438 
439 		if (netbuffer[0] & NCMD_SETUP)
440 		{
441 			fprintf (debugfile,"%i/%i send %i = SETUP [%3i]", gametic, maketic, node, len);
442 			for (i = 0; i < len; i++)
443 				fprintf (debugfile," %2x", ((BYTE *)netbuffer)[i]);
444 		}
445 		else if (netbuffer[0] & NCMD_EXIT)
446 		{
447 			fprintf (debugfile,"%i/%i send %i = EXIT [%3i]", gametic, maketic, node, len);
448 			for (i = 0; i < len; i++)
449 				fprintf (debugfile," %2x", ((BYTE *)netbuffer)[i]);
450 		}
451 		else
452 		{
453 			k = 2;
454 
455 			if (NetMode == NET_PacketServer && consoleplayer == Net_Arbitrator &&
456 				node != 0)
457 			{
458 				k++;
459 			}
460 
461 			if (netbuffer[0] & NCMD_RETRANSMIT)
462 				realretrans = ExpandTics (netbuffer[k++]);
463 			else
464 				realretrans = -1;
465 
466 			int numtics = netbuffer[0] & 3;
467 			if (numtics == 3)
468 				numtics += netbuffer[k++];
469 
470 			fprintf (debugfile,"%i/%i send %i = (%i + %i, R %i) [%3i]",
471 					gametic, maketic,
472 					node,
473 					ExpandTics(netbuffer[1]),
474 					numtics, realretrans, len);
475 
476 			for (i = 0; i < len; i++)
477 				fprintf (debugfile, "%c%2x", i==k?'|':' ', ((BYTE *)netbuffer)[i]);
478 		}
479 		fprintf (debugfile, " [[ ");
480 		for (i = 0; i < doomcom.numnodes; ++i)
481 		{
482 			if (nodeingame[i])
483 			{
484 				fprintf (debugfile, "%d ", nettics[i]);
485 			}
486 			else
487 			{
488 				fprintf (debugfile, "--- ");
489 			}
490 		}
491 		fprintf (debugfile, "]]\n");
492 	}
493 
494 	if (node == 0)
495 	{
496 		memcpy (reboundstore, netbuffer, len);
497 		reboundpacket = len;
498 		return;
499 	}
500 
501 	if (demoplayback)
502 		return;
503 
504 	if (!netgame)
505 		I_Error ("Tried to transmit to another node");
506 
507 #if SIMULATEERRORS
508 	if (rand() < SIMULATEERRORS)
509 	{
510 		if (debugfile)
511 			fprintf (debugfile, "Drop!\n");
512 		return;
513 	}
514 #endif
515 
516 	doomcom.command = CMD_SEND;
517 	doomcom.remotenode = node;
518 	doomcom.datalength = len;
519 
520 #ifdef _DEBUG
521 	if (net_fakelatency / 2 > 0)
522 	{
523 		PacketStore store;
524 		store.message = doomcom;
525 		store.timer = I_GetTime(false) + ((net_fakelatency / 2) / (1000 / TICRATE));
526 		OutBuffer.Push(store);
527 	}
528 	else
529 		I_NetCmd();
530 
531 	for (unsigned int i = 0; i < OutBuffer.Size(); i++)
532 	{
533 		if (OutBuffer[i].timer <= I_GetTime(false))
534 		{
535 			doomcom = OutBuffer[i].message;
536 			I_NetCmd();
537 			OutBuffer.Delete(i);
538 			i = -1;
539 		}
540 	}
541 #else
542 	I_NetCmd();
543 #endif
544 }
545 
546 //
547 // HGetPacket
548 // Returns false if no packet is waiting
549 //
HGetPacket(void)550 bool HGetPacket (void)
551 {
552 	if (reboundpacket)
553 	{
554 		memcpy (netbuffer, reboundstore, reboundpacket);
555 		doomcom.remotenode = 0;
556 		reboundpacket = 0;
557 		return true;
558 	}
559 
560 	if (!netgame)
561 		return false;
562 
563 	if (demoplayback)
564 		return false;
565 
566 	doomcom.command = CMD_GET;
567 	I_NetCmd ();
568 
569 #ifdef _DEBUG
570 	if (net_fakelatency / 2 > 0 && doomcom.remotenode != -1)
571 	{
572 		PacketStore store;
573 		store.message = doomcom;
574 		store.timer = I_GetTime(false) + ((net_fakelatency / 2) / (1000 / TICRATE));
575 		InBuffer.Push(store);
576 		doomcom.remotenode = -1;
577 	}
578 
579 	if (doomcom.remotenode == -1)
580 	{
581 		bool gotmessage = false;
582 		for (unsigned int i = 0; i < InBuffer.Size(); i++)
583 		{
584 			if (InBuffer[i].timer <= I_GetTime(false))
585 			{
586 				doomcom = InBuffer[i].message;
587 				InBuffer.Delete(i);
588 				gotmessage = true;
589 				break;
590 			}
591 		}
592 		if (!gotmessage)
593 			return false;
594 	}
595 #else
596 	if (doomcom.remotenode == -1)
597 	{
598 		return false;
599 	}
600 #endif
601 
602 	if (debugfile)
603 	{
604 		int i, k, realretrans;
605 
606 		if (netbuffer[0] & NCMD_SETUP)
607 		{
608 			fprintf (debugfile,"%i/%i  get %i = SETUP [%3i]", gametic, maketic, doomcom.remotenode, doomcom.datalength);
609 			for (i = 0; i < doomcom.datalength; i++)
610 				fprintf (debugfile, " %2x", ((BYTE *)netbuffer)[i]);
611 			fprintf (debugfile, "\n");
612 		}
613 		else if (netbuffer[0] & NCMD_EXIT)
614 		{
615 			fprintf (debugfile,"%i/%i  get %i = EXIT [%3i]", gametic, maketic, doomcom.remotenode, doomcom.datalength);
616 			for (i = 0; i < doomcom.datalength; i++)
617 				fprintf (debugfile, " %2x", ((BYTE *)netbuffer)[i]);
618 			fprintf (debugfile, "\n");
619 		}
620 		else		{
621 			k = 2;
622 
623 			if (NetMode == NET_PacketServer &&
624 				doomcom.remotenode == nodeforplayer[Net_Arbitrator])
625 			{
626 				k++;
627 			}
628 
629 			if (netbuffer[0] & NCMD_RETRANSMIT)
630 				realretrans = ExpandTics (netbuffer[k++]);
631 			else
632 				realretrans = -1;
633 
634 			int numtics = netbuffer[0] & 3;
635 			if (numtics == 3)
636 				numtics += netbuffer[k++];
637 
638 			fprintf (debugfile,"%i/%i  get %i = (%i + %i, R %i) [%3i]",
639 					gametic, maketic,
640 					doomcom.remotenode,
641 					ExpandTics(netbuffer[1]),
642 					numtics, realretrans, doomcom.datalength);
643 
644 			for (i = 0; i < doomcom.datalength; i++)
645 				fprintf (debugfile, "%c%2x", i==k?'|':' ', ((BYTE *)netbuffer)[i]);
646 			if (numtics)
647 				fprintf (debugfile, " <<%4x>>\n",
648 					consistancy[playerfornode[doomcom.remotenode]][nettics[doomcom.remotenode]%BACKUPTICS] & 0xFFFF);
649 			else
650 				fprintf (debugfile, "\n");
651 		}
652 	}
653 
654 	if (doomcom.datalength != NetbufferSize ())
655 	{
656 		Printf("Bad packet length %i (calculated %i)\n",
657 			doomcom.datalength, NetbufferSize());
658 
659 		if (debugfile)
660 			fprintf (debugfile,"---bad packet length %i (calculated %i)\n",
661 				doomcom.datalength, NetbufferSize());
662 		return false;
663 	}
664 
665 	return true;
666 }
667 
PlayerIsGone(int netnode,int netconsole)668 void PlayerIsGone (int netnode, int netconsole)
669 {
670 	int i;
671 
672 	if (nodeingame[netnode])
673 	{
674 		for (i = netnode + 1; i < doomcom.numnodes; ++i)
675 		{
676 			if (nodeingame[i])
677 				break;
678 		}
679 		if (i == doomcom.numnodes)
680 		{
681 			doomcom.numnodes = netnode;
682 		}
683 
684 		if (playeringame[netconsole])
685 		{
686 			players[netconsole].playerstate = PST_GONE;
687 		}
688 		nodeingame[netnode] = false;
689 		nodejustleft[netnode] = false;
690 	}
691 	else if (nodejustleft[netnode]) // Packet Server
692 	{
693 		if (netnode + 1 == doomcom.numnodes)
694 		{
695 			doomcom.numnodes = netnode;
696 		}
697 		if (playeringame[netconsole])
698 		{
699 			players[netconsole].playerstate = PST_GONE;
700 		}
701 		nodejustleft[netnode] = false;
702 	}
703 	else return;
704 
705 	if (netconsole == Net_Arbitrator)
706 	{
707 		// Pick a new network arbitrator
708 		for (int i = 0; i < MAXPLAYERS; i++)
709 		{
710 			if (i != netconsole && playeringame[i] && players[i].Bot == NULL)
711 			{
712 				Net_Arbitrator = i;
713 				players[i].settings_controller = true;
714 				Printf("%s is the new arbitrator\n", players[i].userinfo.GetName());
715 				break;
716 			}
717 		}
718 	}
719 
720 	if (debugfile && NetMode == NET_PacketServer)
721 	{
722 		if (Net_Arbitrator == consoleplayer)
723 		{
724 			fprintf(debugfile, "I am the new master!\n");
725 		}
726 		else
727 		{
728 			fprintf(debugfile, "Node %d is the new master!\n", nodeforplayer[Net_Arbitrator]);
729 		}
730 	}
731 
732 	if (demorecording)
733 	{
734 		G_CheckDemoStatus ();
735 
736 		//WriteByte (DEM_DROPPLAYER, &demo_p);
737 		//WriteByte ((BYTE)netconsole, &demo_p);
738 	}
739 }
740 
741 //
742 // GetPackets
743 //
744 
GetPackets(void)745 void GetPackets (void)
746 {
747 	int netconsole;
748 	int netnode;
749 	int realend;
750 	int realstart;
751 	int numtics;
752 	int retransmitfrom;
753 	int k;
754 	BYTE playerbytes[MAXNETNODES];
755 	int numplayers;
756 
757 	while ( HGetPacket() )
758 	{
759 		if (netbuffer[0] & NCMD_SETUP)
760 		{
761 			if (consoleplayer == Net_Arbitrator)
762 			{
763 				// This player apparantly doesn't realise the game has started
764 				netbuffer[0] = NCMD_SETUP+3;
765 				HSendPacket (doomcom.remotenode, 1);
766 			}
767 			continue;			// extra setup packet
768 		}
769 
770 		netnode = doomcom.remotenode;
771 		netconsole = playerfornode[netnode] & ~PL_DRONE;
772 
773 		// [RH] Get "ping" times - totally useless, since it's bound to the frequency
774 		// packets go out at.
775 		lastrecvtime[netconsole] = currrecvtime[netconsole];
776 		currrecvtime[netconsole] = I_MSTime ();
777 
778 		// check for exiting the game
779 		if (netbuffer[0] & NCMD_EXIT)
780 		{
781 			if (!nodeingame[netnode])
782 				continue;
783 
784 			if (NetMode != NET_PacketServer || netconsole == Net_Arbitrator)
785 			{
786 				PlayerIsGone (netnode, netconsole);
787 				if (NetMode == NET_PacketServer)
788 				{
789 					BYTE *foo = &netbuffer[2];
790 					for (int i = 0; i < MAXPLAYERS; ++i)
791 					{
792 						if (playeringame[i])
793 						{
794 							int resend = ReadLong (&foo);
795 							if (i != consoleplayer)
796 							{
797 								resendto[nodeforplayer[i]] = resend;
798 							}
799 						}
800 					}
801 				}
802 			}
803 			else
804 			{
805 				nodeingame[netnode] = false;
806 				nodejustleft[netnode] = true;
807 			}
808 			continue;
809 		}
810 
811 		k = 2;
812 
813 		if (NetMode == NET_PacketServer &&
814 			netconsole == Net_Arbitrator &&
815 			netconsole != consoleplayer)
816 		{
817 			mastertics = ExpandTics (netbuffer[k++]);
818 		}
819 
820 		if (netbuffer[0] & NCMD_RETRANSMIT)
821 		{
822 			retransmitfrom = netbuffer[k++];
823 		}
824 		else
825 		{
826 			retransmitfrom = 0;
827 		}
828 
829 		numtics = (netbuffer[0] & NCMD_XTICS);
830 		if (numtics == 3)
831 		{
832 			numtics += netbuffer[k++];
833 		}
834 
835 		if (netbuffer[0] & NCMD_QUITTERS)
836 		{
837 			numplayers = netbuffer[k++];
838 			for (int i = 0; i < numplayers; ++i)
839 			{
840 				PlayerIsGone (nodeforplayer[netbuffer[k]], netbuffer[k]);
841 				k++;
842 			}
843 		}
844 
845 		// Pull current network delay from node
846 		netdelay[netnode][(nettics[netnode]+1) % BACKUPTICS] = netbuffer[k++];
847 
848 		playerbytes[0] = netconsole;
849 		if (netbuffer[0] & NCMD_MULTI)
850 		{
851 			numplayers = netbuffer[k++];
852 			memcpy (playerbytes+1, &netbuffer[k], numplayers - 1);
853 			k += numplayers - 1;
854 		}
855 		else
856 		{
857 			numplayers = 1;
858 		}
859 
860 		// to save bytes, only the low byte of tic numbers are sent
861 		// Figure out what the rest of the bytes are
862 		realstart = ExpandTics (netbuffer[1]);
863 		realend = (realstart + numtics);
864 
865 		nodeforplayer[netconsole] = netnode;
866 
867 		// check for retransmit request
868 		if (resendcount[netnode] <= 0 && (netbuffer[0] & NCMD_RETRANSMIT))
869 		{
870 			resendto[netnode] = ExpandTics (retransmitfrom);
871 			if (debugfile)
872 				fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
873 			resendcount[netnode] = RESENDCOUNT;
874 		}
875 		else
876 		{
877 			resendcount[netnode]--;
878 		}
879 
880 		// check for out of order / duplicated packet
881 		if (realend == nettics[netnode])
882 			continue;
883 
884 		if (realend < nettics[netnode])
885 		{
886 			if (debugfile)
887 				fprintf (debugfile, "out of order packet (%i + %i)\n" ,
888 						 realstart, numtics);
889 			continue;
890 		}
891 
892 		// check for a missed packet
893 		if (realstart > nettics[netnode])
894 		{
895 			// stop processing until the other system resends the missed tics
896 			if (debugfile)
897 				fprintf (debugfile, "missed tics from %i (%i to %i)\n",
898 						 netnode, nettics[netnode], realstart);
899 			remoteresend[netnode] = true;
900 			continue;
901 		}
902 
903 		// update command store from the packet
904 		{
905 			BYTE *start;
906 			int i, tics;
907 			remoteresend[netnode] = false;
908 
909 			start = &netbuffer[k];
910 
911 			for (i = 0; i < numplayers; ++i)
912 			{
913 				int node = nodeforplayer[playerbytes[i]];
914 
915 				SkipTicCmd (&start, nettics[node] - realstart);
916 				for (tics = nettics[node]; tics < realend; tics++)
917 					ReadTicCmd (&start, playerbytes[i], tics);
918 
919 				nettics[nodeforplayer[playerbytes[i]]] = realend;
920 			}
921 		}
922 	}
923 }
924 
925 //
926 // NetUpdate
927 // Builds ticcmds for console player,
928 // sends out a packet
929 //
930 int gametime;
931 
NetUpdate(void)932 void NetUpdate (void)
933 {
934 	int		lowtic;
935 	int 	nowtime;
936 	int 	newtics;
937 	int 	i,j;
938 	int 	realstart;
939 	BYTE	*cmddata;
940 	bool	resendOnly;
941 
942 	GC::CheckGC();
943 
944 	if (ticdup == 0)
945 	{
946 		return;
947 	}
948 
949 	// check time
950 	nowtime = I_GetTime (false);
951 	newtics = nowtime - gametime;
952 	gametime = nowtime;
953 
954 	if (newtics <= 0)	// nothing new to update
955 	{
956 		GetPackets ();
957 		return;
958 	}
959 
960 	if (skiptics <= newtics)
961 	{
962 		newtics -= skiptics;
963 		skiptics = 0;
964 	}
965 	else
966 	{
967 		skiptics -= newtics;
968 		newtics = 0;
969 	}
970 
971 	// build new ticcmds for console player
972 	for (i = 0; i < newtics; i++)
973 	{
974 		I_StartTic ();
975 		D_ProcessEvents ();
976 		if (pauseext || (maketic - gametic) / ticdup >= BACKUPTICS/2-1)
977 			break;			// can't hold any more
978 
979 		//Printf ("mk:%i ",maketic);
980 		G_BuildTiccmd (&localcmds[maketic % LOCALCMDTICS]);
981 		maketic++;
982 
983 		if (ticdup == 1 || maketic == 0)
984 		{
985 			Net_NewMakeTic ();
986 		}
987 		else
988 		{
989 			// Once ticdup tics have been collected, average their movements
990 			// and combine their buttons, since they will all be sent as a
991 			// single tic that gets duplicated ticdup times. Even with ticdup,
992 			// tics are still collected at the normal rate so that, with the
993 			// help of prediction, the game seems as responsive as normal.
994 			if (maketic % ticdup != 0)
995 			{
996 				int mod = maketic - maketic % ticdup;
997 				int j;
998 
999 				// Update the buttons for all tics in this ticdup set as soon as
1000 				// possible so that the prediction shows jumping as correctly as
1001 				// possible. (If you press +jump in the middle of a ticdup set,
1002 				// the jump will actually begin at the beginning of the set, not
1003 				// in the middle.)
1004 				for (j = maketic-2; j >= mod; --j)
1005 				{
1006 					localcmds[j % LOCALCMDTICS].ucmd.buttons |=
1007 						localcmds[(j + 1) % LOCALCMDTICS].ucmd.buttons;
1008 				}
1009 			}
1010 			else
1011 			{
1012 				// Average the ticcmds between these tics to get the
1013 				// movement that is actually sent across the network. We
1014 				// need to update them in all the localcmds slots that
1015 				// are dupped so that prediction works properly.
1016 				int mod = maketic - ticdup;
1017 				int modp, j;
1018 
1019 				int pitch = 0;
1020 				int yaw = 0;
1021 				int roll = 0;
1022 				int forwardmove = 0;
1023 				int sidemove = 0;
1024 				int upmove = 0;
1025 
1026 				for (j = 0; j < ticdup; ++j)
1027 				{
1028 					modp = (mod + j) % LOCALCMDTICS;
1029 					pitch += localcmds[modp].ucmd.pitch;
1030 					yaw += localcmds[modp].ucmd.yaw;
1031 					roll += localcmds[modp].ucmd.roll;
1032 					forwardmove += localcmds[modp].ucmd.forwardmove;
1033 					sidemove += localcmds[modp].ucmd.sidemove;
1034 					upmove += localcmds[modp].ucmd.upmove;
1035 				}
1036 
1037 				pitch /= ticdup;
1038 				yaw /= ticdup;
1039 				roll /= ticdup;
1040 				forwardmove /= ticdup;
1041 				sidemove /= ticdup;
1042 				upmove /= ticdup;
1043 
1044 				for (j = 0; j < ticdup; ++j)
1045 				{
1046 					modp = (mod + j) % LOCALCMDTICS;
1047 					localcmds[modp].ucmd.pitch = pitch;
1048 					localcmds[modp].ucmd.yaw = yaw;
1049 					localcmds[modp].ucmd.roll = roll;
1050 					localcmds[modp].ucmd.forwardmove = forwardmove;
1051 					localcmds[modp].ucmd.sidemove = sidemove;
1052 					localcmds[modp].ucmd.upmove = upmove;
1053 				}
1054 
1055 				Net_NewMakeTic ();
1056 			}
1057 		}
1058 	}
1059 
1060 	if (singletics)
1061 		return; 		// singletic update is synchronous
1062 
1063 	if (demoplayback)
1064 	{
1065 		resendto[0] = nettics[0] = (maketic / ticdup);
1066 		return;			// Don't touch netcmd data while playing a demo, as it'll already exist.
1067 	}
1068 
1069 	// If maketic didn't cross a ticdup boundary, only send packets
1070 	// to players waiting for resends.
1071 	resendOnly = (maketic / ticdup) == (maketic - i) / ticdup;
1072 
1073 	// send the packet to the other nodes
1074 	int count = 1;
1075 	int quitcount = 0;
1076 
1077 	if (consoleplayer == Net_Arbitrator)
1078 	{
1079 		if (NetMode == NET_PacketServer)
1080 		{
1081 			for (j = 0; j < MAXPLAYERS; j++)
1082 			{
1083 				if (playeringame[j] && players[j].Bot == NULL)
1084 				{
1085 					count++;
1086 				}
1087 			}
1088 
1089 			// The loop above added the local player to the count a second time,
1090 			// and it also added the player being sent the packet to the count.
1091 			count -= 2;
1092 
1093 			for (j = 0; j < doomcom.numnodes; ++j)
1094 			{
1095 				if (nodejustleft[j])
1096 				{
1097 					if (count == 0)
1098 					{
1099 						PlayerIsGone (j, playerfornode[j]);
1100 					}
1101 					else
1102 					{
1103 						quitcount++;
1104 					}
1105 				}
1106 			}
1107 
1108 			if (count == 0)
1109 			{
1110 				count = 1;
1111 			}
1112 		}
1113 	}
1114 
1115 	for (i = 0; i < doomcom.numnodes; i++)
1116 	{
1117 		BYTE playerbytes[MAXPLAYERS];
1118 
1119 		if (!nodeingame[i])
1120 		{
1121 			continue;
1122 		}
1123 		if (NetMode == NET_PacketServer &&
1124 			consoleplayer != Net_Arbitrator &&
1125 			i != nodeforplayer[Net_Arbitrator] &&
1126 			i != 0)
1127 		{
1128 			continue;
1129 		}
1130 		if (resendOnly && resendcount[i] <= 0 && !remoteresend[i] && nettics[i])
1131 		{
1132 			continue;
1133 		}
1134 
1135 		int numtics;
1136 		int k;
1137 
1138 		lowtic = maketic / ticdup;
1139 
1140 		netbuffer[0] = 0;
1141 		netbuffer[1] = realstart = resendto[i];
1142 		k = 2;
1143 
1144 		if (NetMode == NET_PacketServer &&
1145 			consoleplayer == Net_Arbitrator &&
1146 			i != 0)
1147 		{
1148 			for (j = 1; j < doomcom.numnodes; ++j)
1149 			{
1150 				if (nodeingame[j] && nettics[j] < lowtic && j != i)
1151 				{
1152 					lowtic = nettics[j];
1153 				}
1154 			}
1155 			netbuffer[k++] = lowtic;
1156 		}
1157 
1158 		numtics = MAX(0, lowtic - realstart);
1159 		if (numtics > BACKUPTICS)
1160 			I_Error ("NetUpdate: Node %d missed too many tics", i);
1161 
1162 		switch (net_extratic)
1163 		{
1164 		case 0:
1165 		default:
1166 			resendto[i] = lowtic; break;
1167 		case 1: resendto[i] = MAX(0, lowtic - 1); break;
1168 		case 2: resendto[i] = nettics[i]; break;
1169 		}
1170 
1171 		if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i])
1172 		{
1173 			continue;
1174 		}
1175 
1176 		if (remoteresend[i])
1177 		{
1178 			netbuffer[0] |= NCMD_RETRANSMIT;
1179 			netbuffer[k++] = nettics[i];
1180 		}
1181 
1182 		if (numtics < 3)
1183 		{
1184 			netbuffer[0] |= numtics;
1185 		}
1186 		else
1187 		{
1188 			netbuffer[0] |= NCMD_XTICS;
1189 			netbuffer[k++] = numtics - 3;
1190 		}
1191 
1192 		if (quitcount > 0)
1193 		{
1194 			netbuffer[0] |= NCMD_QUITTERS;
1195 			netbuffer[k++] = quitcount;
1196 			for (int l = 0; l < doomcom.numnodes; ++l)
1197 			{
1198 				if (nodejustleft[l])
1199 				{
1200 					netbuffer[k++] = playerfornode[l];
1201 				}
1202 			}
1203 		}
1204 
1205 		// Send current network delay
1206 		// The number of tics we just made should be removed from the count.
1207 		netbuffer[k++] = ((maketic - numtics - gametic) / ticdup);
1208 
1209 		if (numtics > 0)
1210 		{
1211 			int l;
1212 
1213 			if (count > 1 && i != 0 && consoleplayer == Net_Arbitrator)
1214 			{
1215 				netbuffer[0] |= NCMD_MULTI;
1216 				netbuffer[k++] = count;
1217 
1218 				if (NetMode == NET_PacketServer)
1219 				{
1220 					for (l = 1, j = 0; j < MAXPLAYERS; j++)
1221 					{
1222 						if (playeringame[j] && players[j].Bot == NULL && j != playerfornode[i] && j != consoleplayer)
1223 						{
1224 							playerbytes[l++] = j;
1225 							netbuffer[k++] = j;
1226 						}
1227 					}
1228 				}
1229 			}
1230 
1231 			cmddata = &netbuffer[k];
1232 
1233 			for (l = 0; l < count; ++l)
1234 			{
1235 				for (j = 0; j < numtics; j++)
1236 				{
1237 					int start = realstart + j, prev = start - 1;
1238 					int localstart, localprev;
1239 
1240 					localstart = (start * ticdup) % LOCALCMDTICS;
1241 					localprev = (prev * ticdup) % LOCALCMDTICS;
1242 					start %= BACKUPTICS;
1243 					prev %= BACKUPTICS;
1244 
1245 					// The local player has their tics sent first, followed by
1246 					// the other players.
1247 					if (l == 0)
1248 					{
1249 						WriteWord (localcmds[localstart].consistancy, &cmddata);
1250 						// [RH] Write out special "ticcmds" before real ticcmd
1251 						if (specials.used[start])
1252 						{
1253 							memcpy (cmddata, specials.streams[start], specials.used[start]);
1254 							cmddata += specials.used[start];
1255 						}
1256 						WriteUserCmdMessage (&localcmds[localstart].ucmd,
1257 							localprev >= 0 ? &localcmds[localprev].ucmd : NULL, &cmddata);
1258 					}
1259 					else if (i != 0)
1260 					{
1261 						int len;
1262 						BYTE *spec;
1263 
1264 						WriteWord (netcmds[playerbytes[l]][start].consistancy, &cmddata);
1265 						spec = NetSpecs[playerbytes[l]][start].GetData (&len);
1266 						if (spec != NULL)
1267 						{
1268 							memcpy (cmddata, spec, len);
1269 							cmddata += len;
1270 						}
1271 
1272 						WriteUserCmdMessage (&netcmds[playerbytes[l]][start].ucmd,
1273 							prev >= 0 ? &netcmds[playerbytes[l]][prev].ucmd : NULL, &cmddata);
1274 					}
1275 				}
1276 			}
1277 			HSendPacket (i, int(cmddata - netbuffer));
1278 		}
1279 		else
1280 		{
1281 			HSendPacket (i, k);
1282 		}
1283 	}
1284 
1285 	// listen for other packets
1286 	GetPackets ();
1287 
1288 	if (!resendOnly)
1289 	{
1290 		// ideally nettics[0] should be 1 - 3 tics above lowtic
1291 		// if we are consistantly slower, speed up time
1292 
1293 		// [RH] I had erroneously assumed frameskip[] had 4 entries
1294 		// because there were 4 players, but that's not the case at
1295 		// all. The game is comparing the lag behind the master for
1296 		// four runs of TryRunTics. If our tic count is ahead of the
1297 		// master all 4 times, the next run of NetUpdate will not
1298 		// process any new input. If we have less input than the
1299 		// master, the next run of NetUpdate will process extra tics
1300 		// (because gametime gets decremented here).
1301 
1302 		// the key player does not adapt
1303 		if (consoleplayer != Net_Arbitrator)
1304 		{
1305 			// I'm not sure about this when using a packet server, because
1306 			// if left unmodified from the P2P version, it can make the game
1307 			// very jerky. The way I have it written right now basically means
1308 			// that it won't adapt. Fortunately, player prediction helps
1309 			// alleviate the lag somewhat.
1310 
1311 			if (NetMode == NET_PeerToPeer)
1312 			{
1313 				int totalavg = 0;
1314 				if (net_ticbalance)
1315 				{
1316 					// Try to guess ahead the time it takes to send responses to the slowest node
1317 					int nodeavg = 0, arbavg = 0;
1318 
1319 					for (j = 0; j < BACKUPTICS; j++)
1320 					{
1321 						arbavg += netdelay[nodeforplayer[Net_Arbitrator]][j];
1322 						nodeavg += netdelay[0][j];
1323 					}
1324 					arbavg /= BACKUPTICS;
1325 					nodeavg /= BACKUPTICS;
1326 
1327 					// We shouldn't adapt if we are already the arbitrator isn't what we are waiting for, otherwise it just adds more latency
1328 					if (arbavg > nodeavg)
1329 					{
1330 						lastaverage = totalavg = ((arbavg + nodeavg) / 2);
1331 					}
1332 					else
1333 					{
1334 						// Allow room to guess two tics ahead
1335 						if (nodeavg > (arbavg + 2) && lastaverage > 0)
1336 							lastaverage--;
1337 						totalavg = lastaverage;
1338 					}
1339 				}
1340 
1341 				mastertics = nettics[nodeforplayer[Net_Arbitrator]] + totalavg;
1342 			}
1343 			if (nettics[0] <= mastertics)
1344 			{
1345 				gametime--;
1346 				if (debugfile) fprintf(debugfile, "-");
1347 			}
1348 			if (NetMode != NET_PacketServer)
1349 			{
1350 				frameskip[(maketic / ticdup) & 3] = (oldnettics > mastertics);
1351 			}
1352 			else
1353 			{
1354 				frameskip[(maketic / ticdup) & 3] = (oldnettics - mastertics) > 3;
1355 			}
1356 			if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
1357 			{
1358 				skiptics = 1;
1359 				if (debugfile) fprintf(debugfile, "+");
1360 			}
1361 			oldnettics = nettics[0];
1362 		}
1363 	}
1364 }
1365 
1366 
1367 //
1368 // D_ArbitrateNetStart
1369 //
1370 // User info packets look like this:
1371 //
1372 //  0 One byte set to NCMD_SETUP or NCMD_SETUP+1; if NCMD_SETUP+1, omit byte 9
1373 //  1 One byte for the player's number
1374 //2-4 Three bytes for the game version (255,high byte,low byte)
1375 //5-8 A bit mask for each player the sender knows about
1376 //  9 The high bit is set if the sender got the game info
1377 // 10 A stream of bytes with the user info
1378 //
1379 //    The guests always send NCMD_SETUP packets, and the host always
1380 //    sends NCMD_SETUP+1 packets.
1381 //
1382 // Game info packets look like this:
1383 //
1384 //  0 One byte set to NCMD_SETUP+2
1385 //  1 One byte for ticdup setting
1386 //  2 One byte for NetMode setting
1387 //  3 String with starting map's name
1388 //  . Four bytes for the RNG seed
1389 //  . Stream containing remaining game info
1390 //
1391 // Finished packet looks like this:
1392 //
1393 //  0 One byte set to NCMD_SETUP+3
1394 //
1395 // Each machine sends user info packets to the host. The host sends user
1396 // info packets back to the other machines as well as game info packets.
1397 // Negotiation is done when all the guests have reported to the host that
1398 // they know about the other nodes.
1399 
1400 struct ArbitrateData
1401 {
1402 	DWORD playersdetected[MAXNETNODES];
1403 	BYTE  gotsetup[MAXNETNODES];
1404 };
1405 
DoArbitrate(void * userdata)1406 bool DoArbitrate (void *userdata)
1407 {
1408 	ArbitrateData *data = (ArbitrateData *)userdata;
1409 	char *s;
1410 	BYTE *stream;
1411 	int version;
1412 	int node;
1413 	int i, j;
1414 
1415 	while (HGetPacket ())
1416 	{
1417 		if (netbuffer[0] == NCMD_EXIT)
1418 		{
1419 			I_FatalError ("The game was aborted.");
1420 		}
1421 
1422 		if (doomcom.remotenode == 0)
1423 		{
1424 			continue;
1425 		}
1426 
1427 		if (netbuffer[0] == NCMD_SETUP || netbuffer[0] == NCMD_SETUP+1)		// got user info
1428 		{
1429 			node = (netbuffer[0] == NCMD_SETUP) ? doomcom.remotenode : nodeforplayer[netbuffer[1]];
1430 
1431 			data->playersdetected[node] =
1432 				(netbuffer[5] << 24) | (netbuffer[6] << 16) | (netbuffer[7] << 8) | netbuffer[8];
1433 
1434 			if (netbuffer[0] == NCMD_SETUP)
1435 			{ // Sent to host
1436 				data->gotsetup[node] = netbuffer[9] & 0x80;
1437 				stream = &netbuffer[10];
1438 			}
1439 			else
1440 			{ // Sent from host
1441 				stream = &netbuffer[9];
1442 			}
1443 
1444 			D_ReadUserInfoStrings (netbuffer[1], &stream, false);
1445 			if (!nodeingame[node])
1446 			{
1447 				version = (netbuffer[2] << 16) | (netbuffer[3] << 8) | netbuffer[4];
1448 				if (version != (0xFF0000 | NETGAMEVERSION))
1449 				{
1450 					I_Error ("Different " GAMENAME " versions cannot play a net game");
1451 				}
1452 
1453 				playeringame[netbuffer[1]] = true;
1454 				nodeingame[node] = true;
1455 
1456 				data->playersdetected[0] |= 1 << netbuffer[1];
1457 
1458 				StartScreen->NetMessage ("Found %s (node %d, player %d)",
1459 						players[netbuffer[1]].userinfo.GetName(),
1460 						node, netbuffer[1]+1);
1461 			}
1462 		}
1463 		else if (netbuffer[0] == NCMD_SETUP+2)	// got game info
1464 		{
1465 			data->gotsetup[0] = 0x80;
1466 
1467 			ticdup = doomcom.ticdup = netbuffer[1];
1468 			NetMode = netbuffer[2];
1469 
1470 			stream = &netbuffer[3];
1471 			s = ReadString (&stream);
1472 			startmap = s;
1473 			delete[] s;
1474 			rngseed = ReadLong (&stream);
1475 			C_ReadCVars (&stream);
1476 		}
1477 		else if (netbuffer[0] == NCMD_SETUP+3)
1478 		{
1479 			return true;
1480 		}
1481 	}
1482 
1483 	// If everybody already knows everything, it's time to go
1484 	if (consoleplayer == Net_Arbitrator)
1485 	{
1486 		for (i = 0; i < doomcom.numnodes; ++i)
1487 			if (data->playersdetected[i] != DWORD(1 << doomcom.numnodes) - 1 || !data->gotsetup[i])
1488 				break;
1489 
1490 		if (i == doomcom.numnodes)
1491 			return true;
1492 	}
1493 
1494 	netbuffer[2] = 255;
1495 	netbuffer[3] = (NETGAMEVERSION >> 8) & 255;
1496 	netbuffer[4] = NETGAMEVERSION & 255;
1497 	netbuffer[5] = data->playersdetected[0] >> 24;
1498 	netbuffer[6] = data->playersdetected[0] >> 16;
1499 	netbuffer[7] = data->playersdetected[0] >> 8;
1500 	netbuffer[8] = data->playersdetected[0];
1501 
1502 	if (consoleplayer != Net_Arbitrator)
1503 	{ // Send user info for the local node
1504 		netbuffer[0] = NCMD_SETUP;
1505 		netbuffer[1] = consoleplayer;
1506 		netbuffer[9] = data->gotsetup[0];
1507 		stream = &netbuffer[10];
1508 		D_WriteUserInfoStrings (consoleplayer, &stream, true);
1509 		SendSetup (data->playersdetected, data->gotsetup, int(stream - netbuffer));
1510 	}
1511 	else
1512 	{ // Send user info for all nodes
1513 		netbuffer[0] = NCMD_SETUP+1;
1514 		for (i = 1; i < doomcom.numnodes; ++i)
1515 		{
1516 			for (j = 0; j < doomcom.numnodes; ++j)
1517 			{
1518 				// Send info about player j to player i?
1519 				if ((data->playersdetected[0] & (1<<j)) && !(data->playersdetected[i] & (1<<j)))
1520 				{
1521 					netbuffer[1] = j;
1522 					stream = &netbuffer[9];
1523 					D_WriteUserInfoStrings (j, &stream, true);
1524 					HSendPacket (i, int(stream - netbuffer));
1525 				}
1526 			}
1527 		}
1528 	}
1529 
1530 	// If we're the host, send the game info, too
1531 	if (consoleplayer == Net_Arbitrator)
1532 	{
1533 		netbuffer[0] = NCMD_SETUP+2;
1534 		netbuffer[1] = (BYTE)doomcom.ticdup;
1535 		netbuffer[2] = NetMode;
1536 		stream = &netbuffer[3];
1537 		WriteString (startmap, &stream);
1538 		WriteLong (rngseed, &stream);
1539 		C_WriteCVars (&stream, CVAR_SERVERINFO, true);
1540 
1541 		SendSetup (data->playersdetected, data->gotsetup, int(stream - netbuffer));
1542 	}
1543 	return false;
1544 }
1545 
D_ArbitrateNetStart(void)1546 void D_ArbitrateNetStart (void)
1547 {
1548 	ArbitrateData data;
1549 	int i;
1550 
1551 	// Return right away if we're just playing with ourselves.
1552 	if (doomcom.numnodes == 1)
1553 		return;
1554 
1555 	autostart = true;
1556 
1557 	memset (data.playersdetected, 0, sizeof(data.playersdetected));
1558 	memset (data.gotsetup, 0, sizeof(data.gotsetup));
1559 
1560 	// The arbitrator knows about himself, but the other players must
1561 	// be told about themselves, in case the host had to adjust their
1562 	// userinfo (e.g. assign them to a different team).
1563 	if (consoleplayer == Net_Arbitrator)
1564 	{
1565 		data.playersdetected[0] = 1 << consoleplayer;
1566 	}
1567 
1568 	// Assign nodes to players. The local player is always node 0.
1569 	// If the local player is not the host, then the host is node 1.
1570 	// Any remaining players are assigned node numbers in the order
1571 	// they were detected.
1572 	playerfornode[0] = consoleplayer;
1573 	nodeforplayer[consoleplayer] = 0;
1574 	if (consoleplayer == Net_Arbitrator)
1575 	{
1576 		for (i = 1; i < doomcom.numnodes; ++i)
1577 		{
1578 			playerfornode[i] = i;
1579 			nodeforplayer[i] = i;
1580 		}
1581 	}
1582 	else
1583 	{
1584 		playerfornode[1] = 0;
1585 		nodeforplayer[0] = 1;
1586 		for (i = 1; i < doomcom.numnodes; ++i)
1587 		{
1588 			if (i < consoleplayer)
1589 			{
1590 				playerfornode[i+1] = i;
1591 				nodeforplayer[i] = i+1;
1592 			}
1593 			else if (i > consoleplayer)
1594 			{
1595 				playerfornode[i] = i;
1596 				nodeforplayer[i] = i;
1597 			}
1598 		}
1599 	}
1600 
1601 	if (consoleplayer == Net_Arbitrator)
1602 	{
1603 		data.gotsetup[0] = 0x80;
1604 	}
1605 
1606 	StartScreen->NetInit ("Exchanging game information", 1);
1607 	if (!StartScreen->NetLoop (DoArbitrate, &data))
1608 	{
1609 		exit (0);
1610 	}
1611 
1612 	if (consoleplayer == Net_Arbitrator)
1613 	{
1614 		netbuffer[0] = NCMD_SETUP+3;
1615 		SendSetup (data.playersdetected, data.gotsetup, 1);
1616 	}
1617 
1618 	if (debugfile)
1619 	{
1620 		for (i = 0; i < doomcom.numnodes; ++i)
1621 		{
1622 			fprintf (debugfile, "player %d is on node %d\n", i, nodeforplayer[i]);
1623 		}
1624 	}
1625 	StartScreen->NetDone();
1626 }
1627 
SendSetup(DWORD playersdetected[MAXNETNODES],BYTE gotsetup[MAXNETNODES],int len)1628 static void SendSetup (DWORD playersdetected[MAXNETNODES], BYTE gotsetup[MAXNETNODES], int len)
1629 {
1630 	if (consoleplayer != Net_Arbitrator)
1631 	{
1632 		if (playersdetected[1] & (1 << consoleplayer))
1633 		{
1634 			HSendPacket (1, 10);
1635 		}
1636 		else
1637 		{
1638 			HSendPacket (1, len);
1639 		}
1640 	}
1641 	else
1642 	{
1643 		for (int i = 1; i < doomcom.numnodes; ++i)
1644 		{
1645 			if (!gotsetup[i] || netbuffer[0] == NCMD_SETUP+3)
1646 			{
1647 				HSendPacket (i, len);
1648 			}
1649 		}
1650 	}
1651 }
1652 
1653 //
1654 // D_CheckNetGame
1655 // Works out player numbers among the net participants
1656 //
1657 
D_CheckNetGame(void)1658 void D_CheckNetGame (void)
1659 {
1660 	const char *v;
1661 	int i;
1662 
1663 	for (i = 0; i < MAXNETNODES; i++)
1664 	{
1665 		nodeingame[i] = false;
1666 		nettics[i] = 0;
1667 		remoteresend[i] = false;		// set when local needs tics
1668 		resendto[i] = 0;				// which tic to start sending
1669 	}
1670 
1671 	// Packet server has proven to be rather slow over the internet. Print a warning about it.
1672 	v = Args->CheckValue("-netmode");
1673 	if (v != NULL && (atoi(v) != 0))
1674 	{
1675 		Printf(TEXTCOLOR_YELLOW "Notice: Using PacketServer (netmode 1) over the internet is prone to running too slow on some internet configurations."
1676 			"\nIf the game is running well below expected speeds, use netmode 0 (P2P) instead.\n");
1677 	}
1678 
1679 	// I_InitNetwork sets doomcom and netgame
1680 	if (I_InitNetwork ())
1681 	{
1682 		// For now, stop auto selecting PacketServer, as it's more likely to cause confusion.
1683 		//NetMode = NET_PacketServer;
1684 	}
1685 	if (doomcom.id != DOOMCOM_ID)
1686 	{
1687 		I_FatalError ("Doomcom buffer invalid!");
1688 	}
1689 	players[0].settings_controller = true;
1690 
1691 	consoleplayer = doomcom.consoleplayer;
1692 
1693 	if (consoleplayer == Net_Arbitrator)
1694 	{
1695 		v = Args->CheckValue("-netmode");
1696 		if (v != NULL)
1697 		{
1698 			NetMode = atoi(v) != 0 ? NET_PacketServer : NET_PeerToPeer;
1699 		}
1700 		if (doomcom.numnodes > 1)
1701 		{
1702 			Printf("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server",
1703 				v != NULL ? "forced" : "auto");
1704 		}
1705 
1706 		if (Args->CheckParm("-extratic"))
1707 		{
1708 			net_extratic = 1;
1709 		}
1710 	}
1711 
1712 	// [RH] Setup user info
1713 	D_SetupUserInfo ();
1714 
1715 	if (Args->CheckParm ("-debugfile"))
1716 	{
1717 		char filename[20];
1718 		mysnprintf (filename, countof(filename), "debug%i.txt", consoleplayer);
1719 		Printf ("debug output to: %s\n", filename);
1720 		debugfile = fopen (filename, "w");
1721 	}
1722 
1723 	if (netgame)
1724 	{
1725 		GameConfig->ReadNetVars ();	// [RH] Read network ServerInfo cvars
1726 		D_ArbitrateNetStart ();
1727 	}
1728 
1729 	// read values out of doomcom
1730 	ticdup = doomcom.ticdup;
1731 
1732 	for (i = 0; i < doomcom.numplayers; i++)
1733 		playeringame[i] = true;
1734 	for (i = 0; i < doomcom.numnodes; i++)
1735 		nodeingame[i] = true;
1736 
1737 	if (consoleplayer != Net_Arbitrator && doomcom.numnodes > 1)
1738 	{
1739 		Printf("Arbitrator selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode.\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server");
1740 	}
1741 
1742 	Printf ("player %i of %i (%i nodes)\n",
1743 			consoleplayer+1, doomcom.numplayers, doomcom.numnodes);
1744 }
1745 
1746 
1747 //
1748 // D_QuitNetGame
1749 // Called before quitting to leave a net game
1750 // without hanging the other players
1751 //
D_QuitNetGame(void)1752 void D_QuitNetGame (void)
1753 {
1754 	int i, j, k;
1755 
1756 	if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
1757 		return;
1758 
1759 	// send a bunch of packets for security
1760 	netbuffer[0] = NCMD_EXIT;
1761 	netbuffer[1] = 0;
1762 
1763 	k = 2;
1764 	if (NetMode == NET_PacketServer && consoleplayer == Net_Arbitrator)
1765 	{
1766 		BYTE *foo = &netbuffer[2];
1767 
1768 		// Let the new arbitrator know what resendto counts to use
1769 
1770 		for (i = 0; i < MAXPLAYERS; ++i)
1771 		{
1772 			if (playeringame[i] && i != consoleplayer)
1773 				WriteLong (resendto[nodeforplayer[i]], &foo);
1774 		}
1775 		k = int(foo - netbuffer);
1776 	}
1777 
1778 	for (i = 0; i < 4; i++)
1779 	{
1780 		if (NetMode == NET_PacketServer && consoleplayer != Net_Arbitrator)
1781 		{
1782 			HSendPacket (nodeforplayer[Net_Arbitrator], 2);
1783 		}
1784 		else
1785 		{
1786 			for (j = 1; j < doomcom.numnodes; j++)
1787 				if (nodeingame[j])
1788 					HSendPacket (j, k);
1789 		}
1790 		I_WaitVBL (1);
1791 	}
1792 
1793 	if (debugfile)
1794 		fclose (debugfile);
1795 }
1796 
1797 
1798 
1799 //
1800 // TryRunTics
1801 //
TryRunTics(void)1802 void TryRunTics (void)
1803 {
1804 	int 		i;
1805 	int 		lowtic;
1806 	int 		realtics;
1807 	int 		availabletics;
1808 	int 		counts;
1809 	int 		numplaying;
1810 
1811 	// If paused, do not eat more CPU time than we need, because it
1812 	// will all be wasted anyway.
1813 	if (pauseext)
1814 		r_NoInterpolate = true;
1815 	bool doWait = cl_capfps || r_NoInterpolate /*|| netgame*/;
1816 
1817 	// get real tics
1818 	if (doWait)
1819 	{
1820 		entertic = I_WaitForTic (oldentertics);
1821 	}
1822 	else
1823 	{
1824 		entertic = I_GetTime (false);
1825 	}
1826 	realtics = entertic - oldentertics;
1827 	oldentertics = entertic;
1828 
1829 	// get available tics
1830 	NetUpdate ();
1831 
1832 	if (pauseext)
1833 		return;
1834 
1835 	lowtic = INT_MAX;
1836 	numplaying = 0;
1837 	for (i = 0; i < doomcom.numnodes; i++)
1838 	{
1839 		if (nodeingame[i])
1840 		{
1841 			numplaying++;
1842 			if (nettics[i] < lowtic)
1843 				lowtic = nettics[i];
1844 		}
1845 	}
1846 
1847 	if (ticdup == 1)
1848 	{
1849 		availabletics = lowtic - gametic;
1850 	}
1851 	else
1852 	{
1853 		availabletics = lowtic - gametic / ticdup;
1854 	}
1855 
1856 	// decide how many tics to run
1857 	if (realtics < availabletics-1)
1858 		counts = realtics+1;
1859 	else if (realtics < availabletics)
1860 		counts = realtics;
1861 	else
1862 		counts = availabletics;
1863 
1864 	// Uncapped framerate needs seprate checks
1865 	if (counts == 0 && !doWait)
1866 	{
1867 		// Check possible stall conditions
1868 		Net_CheckLastReceived(counts);
1869 		if (realtics >= 1)
1870 		{
1871 			C_Ticker();
1872 			M_Ticker();
1873 			// Repredict the player for new buffered movement
1874 			P_UnPredictPlayer();
1875 			P_PredictPlayer(&players[consoleplayer]);
1876 		}
1877 		return;
1878 	}
1879 
1880 	if (counts < 1)
1881 		counts = 1;
1882 
1883 	if (debugfile)
1884 		fprintf (debugfile,
1885 				 "=======real: %i  avail: %i  game: %i\n",
1886 				 realtics, availabletics, counts);
1887 
1888 	// wait for new tics if needed
1889 	while (lowtic < gametic + counts)
1890 	{
1891 		NetUpdate ();
1892 		lowtic = INT_MAX;
1893 
1894 		for (i = 0; i < doomcom.numnodes; i++)
1895 			if (nodeingame[i] && nettics[i] < lowtic)
1896 				lowtic = nettics[i];
1897 
1898 		lowtic = lowtic * ticdup;
1899 
1900 		if (lowtic < gametic)
1901 			I_Error ("TryRunTics: lowtic < gametic");
1902 
1903 		// Check possible stall conditions
1904 		Net_CheckLastReceived (counts);
1905 
1906 		// don't stay in here forever -- give the menu a chance to work
1907 		if (I_GetTime (false) - entertic >= 1)
1908 		{
1909 			C_Ticker ();
1910 			M_Ticker ();
1911 			// Repredict the player for new buffered movement
1912 			P_UnPredictPlayer();
1913 			P_PredictPlayer(&players[consoleplayer]);
1914 			return;
1915 		}
1916 	}
1917 
1918 	//Tic lowtic is high enough to process this gametic. Clear all possible waiting info
1919 	hadlate = false;
1920 	for (i = 0; i < MAXPLAYERS; i++)
1921 		players[i].waiting = false;
1922 	lastglobalrecvtime = I_GetTime (false); //Update the last time the game tic'd over
1923 
1924 	// run the count tics
1925 	if (counts > 0)
1926 	{
1927 		P_UnPredictPlayer();
1928 		while (counts--)
1929 		{
1930 			if (gametic > lowtic)
1931 			{
1932 				I_Error ("gametic>lowtic");
1933 			}
1934 			if (advancedemo)
1935 			{
1936 				D_DoAdvanceDemo ();
1937 			}
1938 			if (debugfile) fprintf (debugfile, "run tic %d\n", gametic);
1939 			C_Ticker ();
1940 			M_Ticker ();
1941 			I_GetTime (true);
1942 			G_Ticker();
1943 			gametic++;
1944 
1945 			NetUpdate ();	// check for new console commands
1946 		}
1947 		P_PredictPlayer(&players[consoleplayer]);
1948 		S_UpdateSounds (players[consoleplayer].camera);	// move positional sounds
1949 	}
1950 }
1951 
Net_CheckLastReceived(int counts)1952 void Net_CheckLastReceived (int counts)
1953 {
1954 	// [Ed850] Check to see the last time a packet was received.
1955 	// If it's longer then 3 seconds, a node has likely stalled.
1956 	if (I_GetTime(false) - lastglobalrecvtime >= TICRATE * 3)
1957 	{
1958 		lastglobalrecvtime = I_GetTime(false); //Bump the count
1959 
1960 		if (NetMode == NET_PeerToPeer || consoleplayer == Net_Arbitrator)
1961 		{
1962 			//Keep the local node in the for loop so we can still log any cases where the local node is /somehow/ late.
1963 			//However, we don't send a resend request for sanity reasons.
1964 			for (int i = 0; i < doomcom.numnodes; i++)
1965 			{
1966 				if (nodeingame[i] && nettics[i] <= gametic + counts)
1967 				{
1968 					if (debugfile && !players[playerfornode[i]].waiting)
1969 						fprintf(debugfile, "%i is slow (%i to %i)\n",
1970 						i, nettics[i], gametic + counts);
1971 					//Send resend request to the late node. Also mark the node as waiting to display it in the hud.
1972 					if (i != 0)
1973 						remoteresend[i] = players[playerfornode[i]].waiting = hadlate = true;
1974 				}
1975 				else
1976 					players[playerfornode[i]].waiting = false;
1977 			}
1978 		}
1979 		else
1980 		{	//Send a resend request to the Arbitrator, as it's obvious we are stuck here.
1981 			if (debugfile && !players[Net_Arbitrator].waiting)
1982 				fprintf(debugfile, "Arbitrator is slow (%i to %i)\n",
1983 				nettics[nodeforplayer[Net_Arbitrator]], gametic + counts);
1984 			//Send resend request to the Arbitrator. Also mark the Arbitrator as waiting to display it in the hud.
1985 			remoteresend[nodeforplayer[Net_Arbitrator]] = players[Net_Arbitrator].waiting = hadlate = true;
1986 		}
1987 	}
1988 }
1989 
Net_NewMakeTic(void)1990 void Net_NewMakeTic (void)
1991 {
1992 	specials.NewMakeTic ();
1993 }
1994 
Net_WriteByte(BYTE it)1995 void Net_WriteByte (BYTE it)
1996 {
1997 	specials << it;
1998 }
1999 
Net_WriteWord(short it)2000 void Net_WriteWord (short it)
2001 {
2002 	specials << it;
2003 }
2004 
Net_WriteLong(int it)2005 void Net_WriteLong (int it)
2006 {
2007 	specials << it;
2008 }
2009 
Net_WriteFloat(float it)2010 void Net_WriteFloat (float it)
2011 {
2012 	specials << it;
2013 }
2014 
Net_WriteString(const char * it)2015 void Net_WriteString (const char *it)
2016 {
2017 	specials << it;
2018 }
2019 
Net_WriteBytes(const BYTE * block,int len)2020 void Net_WriteBytes (const BYTE *block, int len)
2021 {
2022 	while (len--)
2023 		specials << *block++;
2024 }
2025 
2026 //==========================================================================
2027 //
2028 // Dynamic buffer interface
2029 //
2030 //==========================================================================
2031 
FDynamicBuffer()2032 FDynamicBuffer::FDynamicBuffer ()
2033 {
2034 	m_Data = NULL;
2035 	m_Len = m_BufferLen = 0;
2036 }
2037 
~FDynamicBuffer()2038 FDynamicBuffer::~FDynamicBuffer ()
2039 {
2040 	if (m_Data)
2041 	{
2042 		free (m_Data);
2043 		m_Data = NULL;
2044 	}
2045 	m_Len = m_BufferLen = 0;
2046 }
2047 
SetData(const BYTE * data,int len)2048 void FDynamicBuffer::SetData (const BYTE *data, int len)
2049 {
2050 	if (len > m_BufferLen)
2051 	{
2052 		m_BufferLen = (len + 255) & ~255;
2053 		m_Data = (BYTE *)M_Realloc (m_Data, m_BufferLen);
2054 	}
2055 	if (data != NULL)
2056 	{
2057 		m_Len = len;
2058 		memcpy (m_Data, data, len);
2059 	}
2060 	else
2061 	{
2062 		m_Len = 0;
2063 	}
2064 }
2065 
GetData(int * len)2066 BYTE *FDynamicBuffer::GetData (int *len)
2067 {
2068 	if (len)
2069 		*len = m_Len;
2070 	return m_Len ? m_Data : NULL;
2071 }
2072 
2073 
KillAll(const PClass * cls)2074 static int KillAll(const PClass *cls)
2075 {
2076 	AActor *actor;
2077 	int killcount = 0;
2078 	TThinkerIterator<AActor> iterator(cls);
2079 	while ( (actor = iterator.Next ()) )
2080 	{
2081 		if (actor->IsA(cls))
2082 		{
2083 			if (!(actor->flags2 & MF2_DORMANT) && (actor->flags3 & MF3_ISMONSTER))
2084 					killcount += actor->Massacre ();
2085 		}
2086 	}
2087 	return killcount;
2088 
2089 }
2090 
RemoveClass(const PClass * cls)2091 static int RemoveClass(const PClass *cls)
2092 {
2093 	AActor *actor;
2094 	int removecount = 0;
2095 	bool player = false;
2096 	TThinkerIterator<AActor> iterator(cls);
2097 	while ((actor = iterator.Next()))
2098 	{
2099 		if (actor->IsA(cls))
2100 		{
2101 			// [MC]Do not remove LIVE players.
2102 			if (actor->player != NULL)
2103 			{
2104 				player = true;
2105 				continue;
2106 			}
2107 			removecount++;
2108 			actor->ClearCounters();
2109 			actor->Destroy();
2110 		}
2111 	}
2112 	if (player)
2113 		Printf("Cannot remove live players!\n");
2114 	return removecount;
2115 
2116 }
2117 // [RH] Execute a special "ticcmd". The type byte should
2118 //		have already been read, and the stream is positioned
2119 //		at the beginning of the command's actual data.
Net_DoCommand(int type,BYTE ** stream,int player)2120 void Net_DoCommand (int type, BYTE **stream, int player)
2121 {
2122 	BYTE pos = 0;
2123 	char *s = NULL;
2124 	int i;
2125 
2126 	switch (type)
2127 	{
2128 	case DEM_SAY:
2129 		{
2130 			const char *name = players[player].userinfo.GetName();
2131 			BYTE who = ReadByte (stream);
2132 
2133 			s = ReadString (stream);
2134 			CleanseString (s);
2135 			if (((who & 1) == 0) || players[player].userinfo.GetTeam() == TEAM_NONE)
2136 			{ // Said to everyone
2137 				if (who & 2)
2138 				{
2139 					Printf (PRINT_CHAT, TEXTCOLOR_BOLD "* %s" TEXTCOLOR_BOLD "%s" TEXTCOLOR_BOLD "\n", name, s);
2140 				}
2141 				else
2142 				{
2143 					Printf (PRINT_CHAT, "%s" TEXTCOLOR_CHAT ": %s" TEXTCOLOR_CHAT "\n", name, s);
2144 				}
2145 				S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.chatSound, 1, ATTN_NONE);
2146 			}
2147 			else if (players[player].userinfo.GetTeam() == players[consoleplayer].userinfo.GetTeam())
2148 			{ // Said only to members of the player's team
2149 				if (who & 2)
2150 				{
2151 					Printf (PRINT_TEAMCHAT, TEXTCOLOR_BOLD "* (%s" TEXTCOLOR_BOLD ")%s" TEXTCOLOR_BOLD "\n", name, s);
2152 				}
2153 				else
2154 				{
2155 					Printf (PRINT_TEAMCHAT, "(%s" TEXTCOLOR_TEAMCHAT "): %s" TEXTCOLOR_TEAMCHAT "\n", name, s);
2156 				}
2157 				S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.chatSound, 1, ATTN_NONE);
2158 			}
2159 		}
2160 		break;
2161 
2162 	case DEM_MUSICCHANGE:
2163 		s = ReadString (stream);
2164 		S_ChangeMusic (s);
2165 		break;
2166 
2167 	case DEM_PRINT:
2168 		s = ReadString (stream);
2169 		Printf ("%s", s);
2170 		break;
2171 
2172 	case DEM_CENTERPRINT:
2173 		s = ReadString (stream);
2174 		C_MidPrint (SmallFont, s);
2175 		break;
2176 
2177 	case DEM_UINFCHANGED:
2178 		D_ReadUserInfoStrings (player, stream, true);
2179 		break;
2180 
2181 	case DEM_SINFCHANGED:
2182 		D_DoServerInfoChange (stream, false);
2183 		break;
2184 
2185 	case DEM_SINFCHANGEDXOR:
2186 		D_DoServerInfoChange (stream, true);
2187 		break;
2188 
2189 	case DEM_GIVECHEAT:
2190 		s = ReadString (stream);
2191 		cht_Give (&players[player], s, ReadWord (stream));
2192 		break;
2193 
2194 	case DEM_TAKECHEAT:
2195 		s = ReadString (stream);
2196 		cht_Take (&players[player], s, ReadWord (stream));
2197 		break;
2198 
2199 	case DEM_WARPCHEAT:
2200 		{
2201 			int x, y;
2202 			x = ReadWord (stream);
2203 			y = ReadWord (stream);
2204 			P_TeleportMove (players[player].mo, x * 65536, y * 65536, ONFLOORZ, true);
2205 		}
2206 		break;
2207 
2208 	case DEM_GENERICCHEAT:
2209 		cht_DoCheat (&players[player], ReadByte (stream));
2210 		break;
2211 
2212 	case DEM_CHANGEMAP2:
2213 		pos = ReadByte (stream);
2214 		/* intentional fall-through */
2215 	case DEM_CHANGEMAP:
2216 		// Change to another map without disconnecting other players
2217 		s = ReadString (stream);
2218 		// Using LEVEL_NOINTERMISSION tends to throw the game out of sync.
2219 		// That was a long time ago. Maybe it works now?
2220 		level.flags |= LEVEL_CHANGEMAPCHEAT;
2221 		G_ChangeLevel(s, pos, 0);
2222 		break;
2223 
2224 	case DEM_SUICIDE:
2225 		cht_Suicide (&players[player]);
2226 		break;
2227 
2228 	case DEM_ADDBOT:
2229 		bglobal.TryAddBot (stream, player);
2230 		break;
2231 
2232 	case DEM_KILLBOTS:
2233 		bglobal.RemoveAllBots (true);
2234 		Printf ("Removed all bots\n");
2235 		break;
2236 
2237 	case DEM_CENTERVIEW:
2238 		players[player].centering = true;
2239 		break;
2240 
2241 	case DEM_INVUSEALL:
2242 		if (gamestate == GS_LEVEL && !paused)
2243 		{
2244 			AInventory *item = players[player].mo->Inventory;
2245 
2246 			while (item != NULL)
2247 			{
2248 				AInventory *next = item->Inventory;
2249 				if (item->ItemFlags & IF_INVBAR && !(item->IsKindOf(RUNTIME_CLASS(APuzzleItem))))
2250 				{
2251 					players[player].mo->UseInventory (item);
2252 				}
2253 				item = next;
2254 			}
2255 		}
2256 		break;
2257 
2258 	case DEM_INVUSE:
2259 	case DEM_INVDROP:
2260 		{
2261 			DWORD which = ReadLong (stream);
2262 
2263 			if (gamestate == GS_LEVEL && !paused
2264 				&& players[player].playerstate != PST_DEAD)
2265 			{
2266 				AInventory *item = players[player].mo->Inventory;
2267 				while (item != NULL && item->InventoryID != which)
2268 				{
2269 					item = item->Inventory;
2270 				}
2271 				if (item != NULL)
2272 				{
2273 					if (type == DEM_INVUSE)
2274 					{
2275 						players[player].mo->UseInventory (item);
2276 					}
2277 					else
2278 					{
2279 						players[player].mo->DropInventory (item);
2280 					}
2281 				}
2282 			}
2283 		}
2284 		break;
2285 
2286 	case DEM_SUMMON:
2287 	case DEM_SUMMONFRIEND:
2288 	case DEM_SUMMONFOE:
2289 	case DEM_SUMMONMBF:
2290 	case DEM_SUMMON2:
2291 	case DEM_SUMMONFRIEND2:
2292 	case DEM_SUMMONFOE2:
2293 		{
2294 			const PClass *typeinfo;
2295 			int angle = 0;
2296 			SWORD tid = 0;
2297 			BYTE special = 0;
2298 			int args[5];
2299 
2300 			s = ReadString (stream);
2301 			if (type >= DEM_SUMMON2 && type <= DEM_SUMMONFOE2)
2302 			{
2303 				angle = ReadWord(stream);
2304 				tid = ReadWord(stream);
2305 				special = ReadByte(stream);
2306 				for(i = 0; i < 5; i++) args[i] = ReadLong(stream);
2307 			}
2308 
2309 			typeinfo = PClass::FindClass (s);
2310 			if (typeinfo != NULL && typeinfo->ActorInfo != NULL)
2311 			{
2312 				AActor *source = players[player].mo;
2313 				if (source != NULL)
2314 				{
2315 					if (GetDefaultByType (typeinfo)->flags & MF_MISSILE)
2316 					{
2317 						P_SpawnPlayerMissile (source, typeinfo);
2318 					}
2319 					else
2320 					{
2321 						const AActor *def = GetDefaultByType (typeinfo);
2322 						fixedvec3 spawnpos = source->Vec3Angle(def->radius * 2 + source->radius, source->angle, 8 * FRACUNIT);
2323 
2324 						AActor *spawned = Spawn (typeinfo, spawnpos, ALLOW_REPLACE);
2325 						if (spawned != NULL)
2326 						{
2327 							if (type == DEM_SUMMONFRIEND || type == DEM_SUMMONFRIEND2 || type == DEM_SUMMONMBF)
2328 							{
2329 								if (spawned->CountsAsKill())
2330 								{
2331 									level.total_monsters--;
2332 								}
2333 								spawned->FriendPlayer = player + 1;
2334 								spawned->flags |= MF_FRIENDLY;
2335 								spawned->LastHeard = players[player].mo;
2336 								spawned->health = spawned->SpawnHealth();
2337 								if (type == DEM_SUMMONMBF)
2338 									spawned->flags3 |= MF3_NOBLOCKMONST;
2339 							}
2340 							else if (type == DEM_SUMMONFOE || type == DEM_SUMMONFOE2)
2341 							{
2342 								spawned->FriendPlayer = 0;
2343 								spawned->flags &= ~MF_FRIENDLY;
2344 								spawned->health = spawned->SpawnHealth();
2345 							}
2346 						}
2347 						if (type >= DEM_SUMMON2 && type <= DEM_SUMMONFOE2)
2348 						{
2349 							spawned->angle = source->angle - (ANGLE_1 * angle);
2350 							spawned->tid = tid;
2351 							spawned->special = special;
2352 							for(i = 0; i < 5; i++) {
2353 								spawned->args[i] = args[i];
2354 							}
2355 							if(tid) spawned->AddToHash();
2356 						}
2357 					}
2358 				}
2359 			}
2360 		}
2361 		break;
2362 
2363 	case DEM_SPRAY:
2364 		{
2365 			FTraceResults trace;
2366 
2367 			angle_t ang = players[player].mo->angle  >> ANGLETOFINESHIFT;
2368 			angle_t pitch = (angle_t)(players[player].mo->pitch) >> ANGLETOFINESHIFT;
2369 			fixed_t vx = FixedMul (finecosine[pitch], finecosine[ang]);
2370 			fixed_t vy = FixedMul (finecosine[pitch], finesine[ang]);
2371 			fixed_t vz = -finesine[pitch];
2372 
2373 			s = ReadString (stream);
2374 
2375 			if (Trace (players[player].mo->X(), players[player].mo->Y(),
2376 				players[player].mo->Top() - (players[player].mo->height>>2),
2377 				players[player].mo->Sector,
2378 				vx, vy, vz, 172*FRACUNIT, 0, ML_BLOCKEVERYTHING, players[player].mo,
2379 				trace, TRACE_NoSky))
2380 			{
2381 				if (trace.HitType == TRACE_HitWall)
2382 				{
2383 					DImpactDecal::StaticCreate (s,
2384 						trace.X, trace.Y, trace.Z,
2385 						trace.Line->sidedef[trace.Side], NULL);
2386 				}
2387 			}
2388 		}
2389 		break;
2390 
2391 	case DEM_PAUSE:
2392 		if (gamestate == GS_LEVEL)
2393 		{
2394 			if (paused)
2395 			{
2396 				paused = 0;
2397 				S_ResumeSound (false);
2398 			}
2399 			else
2400 			{
2401 				paused = player + 1;
2402 				S_PauseSound (false, false);
2403 			}
2404 			V_SetBorderNeedRefresh();
2405 		}
2406 		break;
2407 
2408 	case DEM_SAVEGAME:
2409 		if (gamestate == GS_LEVEL)
2410 		{
2411 			s = ReadString (stream);
2412 			savegamefile = s;
2413 			delete[] s;
2414 			s = ReadString (stream);
2415 			memset (savedescription, 0, sizeof(savedescription));
2416 			strncpy (savedescription, s, sizeof(savedescription));
2417 			if (player != consoleplayer)
2418 			{
2419 				// Paths sent over the network will be valid for the system that sent
2420 				// the save command. For other systems, the path needs to be changed.
2421 				const char *fileonly = savegamefile.GetChars();
2422 				const char *slash = strrchr (fileonly, '\\');
2423 				if (slash != NULL)
2424 				{
2425 					fileonly = slash + 1;
2426 				}
2427 				slash = strrchr (fileonly, '/');
2428 				if (slash != NULL)
2429 				{
2430 					fileonly = slash + 1;
2431 				}
2432 				if (fileonly != savegamefile.GetChars())
2433 				{
2434 					savegamefile = G_BuildSaveName (fileonly, -1);
2435 				}
2436 			}
2437 		}
2438 		gameaction = ga_savegame;
2439 		break;
2440 
2441 	case DEM_CHECKAUTOSAVE:
2442 		// Do not autosave in multiplayer games or when dead.
2443 		// For demo playback, DEM_DOAUTOSAVE already exists in the demo if the
2444 		// autosave happened. And if it doesn't, we must not generate it.
2445 		if (multiplayer ||
2446 			demoplayback ||
2447 			players[consoleplayer].playerstate != PST_LIVE ||
2448 			disableautosave >= 2 ||
2449 			autosavecount == 0)
2450 		{
2451 			break;
2452 		}
2453 		Net_WriteByte (DEM_DOAUTOSAVE);
2454 		break;
2455 
2456 	case DEM_DOAUTOSAVE:
2457 		gameaction = ga_autosave;
2458 		break;
2459 
2460 	case DEM_FOV:
2461 		{
2462 			float newfov = (float)ReadByte (stream);
2463 
2464 			if (newfov != players[consoleplayer].DesiredFOV)
2465 			{
2466 				Printf ("FOV%s set to %g\n",
2467 					consoleplayer == Net_Arbitrator ? " for everyone" : "",
2468 					newfov);
2469 			}
2470 
2471 			for (i = 0; i < MAXPLAYERS; ++i)
2472 			{
2473 				if (playeringame[i])
2474 				{
2475 					players[i].DesiredFOV = newfov;
2476 				}
2477 			}
2478 		}
2479 		break;
2480 
2481 	case DEM_MYFOV:
2482 		players[player].DesiredFOV = (float)ReadByte (stream);
2483 		break;
2484 
2485 	case DEM_RUNSCRIPT:
2486 	case DEM_RUNSCRIPT2:
2487 		{
2488 			int snum = ReadWord (stream);
2489 			int argn = ReadByte (stream);
2490 
2491 			RunScript(stream, players[player].mo, snum, argn, (type == DEM_RUNSCRIPT2) ? ACS_ALWAYS : 0);
2492 		}
2493 		break;
2494 
2495 	case DEM_RUNNAMEDSCRIPT:
2496 		{
2497 			char *sname = ReadString(stream);
2498 			int argn = ReadByte(stream);
2499 
2500 			RunScript(stream, players[player].mo, -FName(sname), argn & 127, (argn & 128) ? ACS_ALWAYS : 0);
2501 		}
2502 		break;
2503 
2504 	case DEM_RUNSPECIAL:
2505 		{
2506 			int snum = ReadByte(stream);
2507 			int argn = ReadByte(stream);
2508 			int arg[5] = { 0, 0, 0, 0, 0 };
2509 
2510 			for (i = 0; i < argn; ++i)
2511 			{
2512 				int argval = ReadLong(stream);
2513 				if ((unsigned)i < countof(arg))
2514 				{
2515 					arg[i] = argval;
2516 				}
2517 			}
2518 			if (!CheckCheatmode(player == consoleplayer))
2519 			{
2520 				P_ExecuteSpecial(snum, NULL, players[player].mo, false, arg[0], arg[1], arg[2], arg[3], arg[4]);
2521 			}
2522 		}
2523 		break;
2524 
2525 	case DEM_CROUCH:
2526 		if (gamestate == GS_LEVEL && players[player].mo != NULL &&
2527 			players[player].health > 0 && !(players[player].oldbuttons & BT_JUMP) &&
2528 			!P_IsPlayerTotallyFrozen(&players[player]))
2529 		{
2530 			players[player].crouching = players[player].crouchdir < 0 ? 1 : -1;
2531 		}
2532 		break;
2533 
2534 	case DEM_MORPHEX:
2535 		{
2536 			s = ReadString (stream);
2537 			const char *msg = cht_Morph (players + player, PClass::FindClass (s), false);
2538 			if (player == consoleplayer)
2539 			{
2540 				Printf ("%s\n", *msg != '\0' ? msg : "Morph failed.");
2541 			}
2542 		}
2543 		break;
2544 
2545 	case DEM_ADDCONTROLLER:
2546 		{
2547 			BYTE playernum = ReadByte (stream);
2548 			players[playernum].settings_controller = true;
2549 
2550 			if (consoleplayer == playernum || consoleplayer == Net_Arbitrator)
2551 				Printf ("%s has been added to the controller list.\n", players[playernum].userinfo.GetName());
2552 		}
2553 		break;
2554 
2555 	case DEM_DELCONTROLLER:
2556 		{
2557 			BYTE playernum = ReadByte (stream);
2558 			players[playernum].settings_controller = false;
2559 
2560 			if (consoleplayer == playernum || consoleplayer == Net_Arbitrator)
2561 				Printf ("%s has been removed from the controller list.\n", players[playernum].userinfo.GetName());
2562 		}
2563 		break;
2564 
2565 	case DEM_KILLCLASSCHEAT:
2566 		{
2567 			char *classname = ReadString (stream);
2568 			int killcount = 0;
2569 			const PClass *cls = PClass::FindClass(classname);
2570 
2571 			if (cls != NULL && cls->ActorInfo != NULL)
2572 			{
2573 				killcount = KillAll(cls);
2574 				const PClass *cls_rep = cls->GetReplacement();
2575 				if (cls != cls_rep)
2576 				{
2577 					killcount += KillAll(cls_rep);
2578 				}
2579 				Printf ("Killed %d monsters of type %s.\n",killcount, classname);
2580 			}
2581 			else
2582 			{
2583 				Printf ("%s is not an actor class.\n", classname);
2584 			}
2585 
2586 		}
2587 		break;
2588 	case DEM_REMOVE:
2589 	{
2590 		char *classname = ReadString(stream);
2591 		int removecount = 0;
2592 		const PClass *cls = PClass::FindClass(classname);
2593 		if (cls != NULL && cls->ActorInfo != NULL)
2594 		{
2595 			removecount = RemoveClass(cls);
2596 			const PClass *cls_rep = cls->GetReplacement();
2597 			if (cls != cls_rep)
2598 			{
2599 				removecount += RemoveClass(cls_rep);
2600 			}
2601 			Printf("Removed %d actors of type %s.\n", removecount, classname);
2602 		}
2603 		else
2604 		{
2605 			Printf("%s is not an actor class.\n", classname);
2606 		}
2607 	}
2608 		break;
2609 
2610 	case DEM_CONVREPLY:
2611 	case DEM_CONVCLOSE:
2612 	case DEM_CONVNULL:
2613 		P_ConversationCommand (type, player, stream);
2614 		break;
2615 
2616 	case DEM_SETSLOT:
2617 	case DEM_SETSLOTPNUM:
2618 		{
2619 			int pnum;
2620 			if (type == DEM_SETSLOTPNUM)
2621 			{
2622 				pnum = ReadByte(stream);
2623 			}
2624 			else
2625 			{
2626 				pnum = player;
2627 			}
2628 			unsigned int slot = ReadByte(stream);
2629 			int count = ReadByte(stream);
2630 			if (slot < NUM_WEAPON_SLOTS)
2631 			{
2632 				players[pnum].weapons.Slots[slot].Clear();
2633 			}
2634 			for(i = 0; i < count; ++i)
2635 			{
2636 				const PClass *wpn = Net_ReadWeapon(stream);
2637 				players[pnum].weapons.AddSlot(slot, wpn, pnum == consoleplayer);
2638 			}
2639 		}
2640 		break;
2641 
2642 	case DEM_ADDSLOT:
2643 		{
2644 			int slot = ReadByte(stream);
2645 			const PClass *wpn = Net_ReadWeapon(stream);
2646 			players[player].weapons.AddSlot(slot, wpn, player == consoleplayer);
2647 		}
2648 		break;
2649 
2650 	case DEM_ADDSLOTDEFAULT:
2651 		{
2652 			int slot = ReadByte(stream);
2653 			const PClass *wpn = Net_ReadWeapon(stream);
2654 			players[player].weapons.AddSlotDefault(slot, wpn, player == consoleplayer);
2655 		}
2656 		break;
2657 
2658 	case DEM_SETPITCHLIMIT:
2659 		players[player].MinPitch = ReadByte(stream) * -ANGLE_1;		// up
2660 		players[player].MaxPitch = ReadByte(stream) *  ANGLE_1;		// down
2661 		break;
2662 
2663 	case DEM_ADVANCEINTER:
2664 		F_AdvanceIntermission();
2665 		break;
2666 
2667 	case DEM_REVERTCAMERA:
2668 		players[player].camera = players[player].mo;
2669 		break;
2670 
2671 	case DEM_FINISHGAME:
2672 		// Simulate an end-of-game action
2673 		G_ChangeLevel(NULL, 0, 0);
2674 		break;
2675 
2676 	default:
2677 		I_Error ("Unknown net command: %d", type);
2678 		break;
2679 	}
2680 
2681 	if (s)
2682 		delete[] s;
2683 }
2684 
2685 // Used by DEM_RUNSCRIPT, DEM_RUNSCRIPT2, and DEM_RUNNAMEDSCRIPT
RunScript(BYTE ** stream,APlayerPawn * pawn,int snum,int argn,int always)2686 static void RunScript(BYTE **stream, APlayerPawn *pawn, int snum, int argn, int always)
2687 {
2688 	int arg[4] = { 0, 0, 0, 0 };
2689 	int i;
2690 
2691 	for (i = 0; i < argn; ++i)
2692 	{
2693 		int argval = ReadLong(stream);
2694 		if ((unsigned)i < countof(arg))
2695 		{
2696 			arg[i] = argval;
2697 		}
2698 	}
2699 	P_StartScript(pawn, NULL, snum, level.MapName, arg, MIN<int>(countof(arg), argn), ACS_NET | always);
2700 }
2701 
Net_SkipCommand(int type,BYTE ** stream)2702 void Net_SkipCommand (int type, BYTE **stream)
2703 {
2704 	BYTE t;
2705 	size_t skip;
2706 
2707 	switch (type)
2708 	{
2709 		case DEM_SAY:
2710 			skip = strlen ((char *)(*stream + 1)) + 2;
2711 			break;
2712 
2713 		case DEM_ADDBOT:
2714 			skip = strlen ((char *)(*stream + 1)) + 6;
2715 			break;
2716 
2717 		case DEM_GIVECHEAT:
2718 		case DEM_TAKECHEAT:
2719 			skip = strlen ((char *)(*stream)) + 3;
2720 			break;
2721 
2722 		case DEM_SUMMON2:
2723 		case DEM_SUMMONFRIEND2:
2724 		case DEM_SUMMONFOE2:
2725 			skip = strlen ((char *)(*stream)) + 26;
2726 			break;
2727 		case DEM_CHANGEMAP2:
2728 			skip = strlen((char *)(*stream + 1)) + 2;
2729 			break;
2730 		case DEM_MUSICCHANGE:
2731 		case DEM_PRINT:
2732 		case DEM_CENTERPRINT:
2733 		case DEM_UINFCHANGED:
2734 		case DEM_CHANGEMAP:
2735 		case DEM_SUMMON:
2736 		case DEM_SUMMONFRIEND:
2737 		case DEM_SUMMONFOE:
2738 		case DEM_SUMMONMBF:
2739 		case DEM_REMOVE:
2740 		case DEM_SPRAY:
2741 		case DEM_MORPHEX:
2742 		case DEM_KILLCLASSCHEAT:
2743 			skip = strlen ((char *)(*stream)) + 1;
2744 			break;
2745 
2746 		case DEM_INVUSE:
2747 		case DEM_INVDROP:
2748 		case DEM_WARPCHEAT:
2749 			skip = 4;
2750 			break;
2751 
2752 		case DEM_GENERICCHEAT:
2753 		case DEM_DROPPLAYER:
2754 		case DEM_FOV:
2755 		case DEM_MYFOV:
2756 		case DEM_ADDCONTROLLER:
2757 		case DEM_DELCONTROLLER:
2758 			skip = 1;
2759 			break;
2760 
2761 		case DEM_SAVEGAME:
2762 			skip = strlen ((char *)(*stream)) + 1;
2763 			skip += strlen ((char *)(*stream) + skip) + 1;
2764 			break;
2765 
2766 		case DEM_SINFCHANGEDXOR:
2767 		case DEM_SINFCHANGED:
2768 			t = **stream;
2769 			skip = 1 + (t & 63);
2770 			if (type == DEM_SINFCHANGED)
2771 			{
2772 				switch (t >> 6)
2773 				{
2774 				case CVAR_Bool:
2775 					skip += 1;
2776 					break;
2777 				case CVAR_Int: case CVAR_Float:
2778 					skip += 4;
2779 					break;
2780 				case CVAR_String:
2781 					skip += strlen ((char *)(*stream + skip)) + 1;
2782 					break;
2783 				}
2784 			}
2785 			else
2786 			{
2787 				skip += 1;
2788 			}
2789 			break;
2790 
2791 		case DEM_RUNSCRIPT:
2792 		case DEM_RUNSCRIPT2:
2793 			skip = 3 + *(*stream + 2) * 4;
2794 			break;
2795 
2796 		case DEM_RUNNAMEDSCRIPT:
2797 			skip = strlen((char *)(*stream)) + 2;
2798 			skip += ((*(*stream + skip - 1)) & 127) * 4;
2799 			break;
2800 
2801 		case DEM_RUNSPECIAL:
2802 			skip = 2 + *(*stream + 1) * 4;
2803 			break;
2804 
2805 		case DEM_CONVREPLY:
2806 			skip = 3;
2807 			break;
2808 
2809 		case DEM_SETSLOT:
2810 		case DEM_SETSLOTPNUM:
2811 			{
2812 				skip = 2 + (type == DEM_SETSLOTPNUM);
2813 				for(int numweapons = (*stream)[skip-1]; numweapons > 0; numweapons--)
2814 				{
2815 					skip += 1 + ((*stream)[skip] >> 7);
2816 				}
2817 			}
2818 			break;
2819 
2820 		case DEM_ADDSLOT:
2821 		case DEM_ADDSLOTDEFAULT:
2822 			skip = 2 + ((*stream)[1] >> 7);
2823 			break;
2824 
2825 		case DEM_SETPITCHLIMIT:
2826 			skip = 2;
2827 			break;
2828 
2829 		default:
2830 			return;
2831 	}
2832 
2833 	*stream += skip;
2834 }
2835 
2836 // [RH] List "ping" times
CCMD(pings)2837 CCMD (pings)
2838 {
2839 	int i;
2840 	for (i = 0; i < MAXPLAYERS; i++)
2841 		if (playeringame[i])
2842 			Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i],
2843 					players[i].userinfo.GetName());
2844 }
2845 
2846 //==========================================================================
2847 //
2848 // Network_Controller
2849 //
2850 // Implement players who have the ability to change settings in a network
2851 // game.
2852 //
2853 //==========================================================================
2854 
Network_Controller(int playernum,bool add)2855 static void Network_Controller (int playernum, bool add)
2856 {
2857 	if (consoleplayer != Net_Arbitrator)
2858 	{
2859 		Printf ("This command is only accessible to the net arbitrator.\n");
2860 		return;
2861 	}
2862 
2863 	if (players[playernum].settings_controller && add)
2864 	{
2865 		Printf ("%s is already on the setting controller list.\n", players[playernum].userinfo.GetName());
2866 		return;
2867 	}
2868 
2869 	if (!players[playernum].settings_controller && !add)
2870 	{
2871 		Printf ("%s is not on the setting controller list.\n", players[playernum].userinfo.GetName());
2872 		return;
2873 	}
2874 
2875 	if (!playeringame[playernum])
2876 	{
2877 		Printf ("Player (%d) not found!\n", playernum);
2878 		return;
2879 	}
2880 
2881 	if (players[playernum].Bot != NULL)
2882 	{
2883 		Printf ("Bots cannot be added to the controller list.\n");
2884 		return;
2885 	}
2886 
2887 	if (playernum == Net_Arbitrator)
2888 	{
2889 		Printf ("The net arbitrator cannot have their status changed on this list.\n");
2890 		return;
2891 	}
2892 
2893 	if (add)
2894 		Net_WriteByte (DEM_ADDCONTROLLER);
2895 	else
2896 		Net_WriteByte (DEM_DELCONTROLLER);
2897 
2898 	Net_WriteByte (playernum);
2899 }
2900 
2901 //==========================================================================
2902 //
2903 // CCMD net_addcontroller
2904 //
2905 //==========================================================================
2906 
CCMD(net_addcontroller)2907 CCMD (net_addcontroller)
2908 {
2909 	if (!netgame)
2910 	{
2911 		Printf ("This command can only be used when playing a net game.\n");
2912 		return;
2913 	}
2914 
2915 	if (argv.argc () < 2)
2916 	{
2917 		Printf ("Usage: net_addcontroller <player>\n");
2918 		return;
2919 	}
2920 
2921 	Network_Controller (atoi (argv[1]), true);
2922 }
2923 
2924 //==========================================================================
2925 //
2926 // CCMD net_removecontroller
2927 //
2928 //==========================================================================
2929 
CCMD(net_removecontroller)2930 CCMD (net_removecontroller)
2931 {
2932 	if (!netgame)
2933 	{
2934 		Printf ("This command can only be used when playing a net game.\n");
2935 		return;
2936 	}
2937 
2938 	if (argv.argc () < 2)
2939 	{
2940 		Printf ("Usage: net_removecontroller <player>\n");
2941 		return;
2942 	}
2943 
2944 	Network_Controller (atoi (argv[1]), false);
2945 }
2946 
2947 //==========================================================================
2948 //
2949 // CCMD net_listcontrollers
2950 //
2951 //==========================================================================
2952 
CCMD(net_listcontrollers)2953 CCMD (net_listcontrollers)
2954 {
2955 	if (!netgame)
2956 	{
2957 		Printf ("This command can only be used when playing a net game.\n");
2958 		return;
2959 	}
2960 
2961 	Printf ("The following players can change the game settings:\n");
2962 
2963 	for (int i = 0; i < MAXPLAYERS; i++)
2964 	{
2965 		if (!playeringame[i])
2966 			continue;
2967 
2968 		if (players[i].settings_controller)
2969 		{
2970 			Printf ("- %s\n", players[i].userinfo.GetName());
2971 		}
2972 	}
2973 }
2974