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