1 
2 /*
3  * d_net.c
4  * This version has the fixed ticdup code
5  */
6 
7 #ifdef HAVE_USLEEP
8 #include <unistd.h>
9 #endif
10 #include "doomdef.h"
11 
12 #define NCMD_EXIT               0x80000000
13 #define NCMD_RETRANSMIT         0x40000000
14 #define NCMD_SETUP              0x20000000
15 #define NCMD_KILL               0x10000000      /* kill game */
16 #define NCMD_CHECKSUM           0x0fffffff
17 
18 
19 doomcom_t               *doomcom;
20 doomdata_t              *netbuffer;             /* points inside doomcom */
21 
22 
23 /*
24   ==============================================================================
25 
26   NETWORKING
27 
28   gametic is the tic about to (or currently being) run
29   maketic is the tick that hasn't had control made for it yet
30   nettics[] has the maketics for all players
31 
32   a gametic cannot be run until nettics[] > gametic for all players
33 
34   ==============================================================================
35 */
36 
37 #define RESENDCOUNT     10
38 #define PL_DRONE        0x80                    /* bit flag in doomdata->player */
39 
40 ticcmd_t        localcmds[BACKUPTICS];
41 
42 ticcmd_t        netcmds[MAXPLAYERS][BACKUPTICS];
43 int             nettics[MAXNETNODES];
44 boolean         nodeingame[MAXNETNODES];        /* set false as nodes leave game */
45 boolean         remoteresend[MAXNETNODES];      /* set when local needs tics */
46 int             resendto[MAXNETNODES];          /* set when remote needs tics */
47 int             resendcount[MAXNETNODES];
48 
49 int             nodeforplayer[MAXPLAYERS];
50 
51 int             maketic;
52 int             lastnettic, skiptics;
53 int             ticdup;
54 int             maxsend;                        /* BACKUPTICS/(2*ticdup)-1 */
55 
56 void D_ProcessEvents (void);
57 void G_BuildTiccmd (ticcmd_t *cmd);
58 void D_DoAdvanceDemo (void);
59 
60 boolean         reboundpacket;
61 doomdata_t      reboundstore;
62 
63 
NetbufferSize(void)64 unsigned long NetbufferSize (void)
65 {
66   return (unsigned long)&(((doomdata_t *)0)->cmds[netbuffer->numtics]);
67 }
68 
NetbufferChecksum(void)69 unsigned NetbufferChecksum (void)
70 {
71   unsigned int  c;
72   int           i,l;
73 
74   c = 0x1234567;
75 
76 #if defined(NeXT)
77   return 0;                       /* byte order problems */
78 #endif
79 
80   l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
81   for (i=0 ; i<l ; i++)
82     c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);
83 
84   return c & NCMD_CHECKSUM;
85 }
86 
ExpandTics(int low)87 int ExpandTics (int low)
88 {
89   int     delta;
90 
91   delta = low - (maketic&0xff);
92 
93   if (delta >= -64 && delta <= 64)
94     return (maketic&~0xff) + low;
95   if (delta > 64)
96     return (maketic&~0xff) - 256 + low;
97   if (delta < -64)
98     return (maketic&~0xff) + 256 + low;
99 
100   I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic);
101   return 0;
102 }
103 
104 
105 /* ============================================================================ */
106 
107 
108 /*
109   ==============
110   =
111   = HSendPacket
112   =
113   ==============
114 */
115 
HSendPacket(int node,int flags)116 void HSendPacket (int node, int flags)
117 {
118   netbuffer->checksum = NetbufferChecksum () | flags;
119 
120   /* if (!node) */
121   if (node == doomcom->consoleplayer)
122     {
123       reboundstore = *netbuffer;
124       reboundpacket = true;
125       return;
126     }
127 
128   if (demoplayback)
129     return;
130 
131   if (!netgame)
132     I_Error ("Tried to transmit to another node");
133 
134   doomcom->command = CMD_SEND;
135   doomcom->remotenode = node;
136   doomcom->datalength = NetbufferSize ();
137 
138   if (debugfile)
139     {
140       int             i;
141       int             realretrans;
142       if (netbuffer->checksum & NCMD_RETRANSMIT)
143 	realretrans = ExpandTics (netbuffer->retransmitfrom);
144       else
145 	realretrans = -1;
146       fprintf (debugfile,"send (%i + %i, R %i) [%i] "
147 	       ,ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
148       for (i=0 ; i<doomcom->datalength ; i++)
149 	fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
150       fprintf (debugfile,"\n");
151     }
152 
153   I_NetCmd ();
154 }
155 
156 /*
157   ==============
158   =
159   = HGetPacket
160   =
161   = Returns false if no packet is waiting
162   =
163   ==============
164 */
165 
HGetPacket(void)166 boolean HGetPacket (void)
167 {
168   if (reboundpacket)
169     {
170       *netbuffer = reboundstore;
171       /* doomcom->remotenode = 0; */
172       doomcom->remotenode = doomcom->consoleplayer;
173       reboundpacket = false;
174       return true;
175     }
176 
177   if (!netgame)
178     return false;
179   if (demoplayback)
180     return false;
181 
182   doomcom->command = CMD_GET;
183   I_NetCmd ();
184   if (doomcom->remotenode == -1)
185     return false;
186 
187   if (doomcom->datalength != NetbufferSize ())
188     {
189       if (debugfile)
190 	fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
191       return false;
192     }
193 
194   if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
195     {
196       if (debugfile)
197 	fprintf (debugfile,"bad packet checksum\n");
198       return false;
199     }
200 
201   if (debugfile)
202     {
203       int     realretrans;
204       int     i;
205 
206       if (netbuffer->checksum & NCMD_SETUP)
207 	fprintf (debugfile,"setup packet\n");
208       else
209 	{
210 	  if (netbuffer->checksum & NCMD_RETRANSMIT)
211 	    realretrans = ExpandTics (netbuffer->retransmitfrom);
212 	  else
213 	    realretrans = -1;
214 	  fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",doomcom->remotenode,
215 		   ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
216 	  for (i=0 ; i<doomcom->datalength ; i++)
217 	    fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
218 	  fprintf (debugfile,"\n");
219 	}
220     }
221   return true;
222 }
223 
224 
225 /*
226   ===================
227   =
228   = GetPackets
229   =
230   ===================
231 */
232 
233 char    exitmsg[80];
234 
GetPackets(void)235 void GetPackets (void)
236 {
237   int             netconsole;
238   int             netnode;
239   ticcmd_t        *src, *dest;
240   int             realend;
241   int             realstart;
242 
243   while (HGetPacket ())
244     {
245       if (netbuffer->checksum & NCMD_SETUP)
246 	continue;               /* extra setup packet */
247 
248       netconsole = netbuffer->player & ~PL_DRONE;
249       netnode = doomcom->remotenode;
250       /*
251        * to save bytes, only the low byte of tic numbers are sent
252        * Figure out what the rest of the bytes are
253        */
254       realstart = ExpandTics (netbuffer->starttic);
255       realend = (realstart+netbuffer->numtics);
256 
257       /*
258        * check for exiting the game
259        */
260       if (netbuffer->checksum & NCMD_EXIT)
261 	{
262 	  if (!nodeingame[netnode])
263 	    continue;
264 	  nodeingame[netnode] = false;
265 	  playeringame[netconsole] = false;
266 	  strcpy (exitmsg, "PLAYER 1 LEFT THE GAME");
267 	  exitmsg[7] += netconsole;
268 	  players[consoleplayer].message = exitmsg;
269           players[consoleplayer].messageTics = MESSAGETICS;
270 
271 	  /*
272 	   *			if (demorecording)
273 	   *				G_CheckDemoStatus ();
274 	   */
275 	  continue;
276 	}
277 
278       /*
279        * check for a remote game kill
280        */
281       if (netbuffer->checksum & NCMD_KILL)
282 	I_Error ("Killed by network driver");
283 
284       nodeforplayer[netconsole] = netnode;
285 
286       /*
287        * check for retransmit request
288        */
289       if ( resendcount[netnode] <= 0
290 	   && (netbuffer->checksum & NCMD_RETRANSMIT) )
291 	{
292 	  resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
293 	  if (debugfile)
294 	    fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
295 	  resendcount[netnode] = RESENDCOUNT;
296 	}
297       else
298 	resendcount[netnode]--;
299 
300       /*
301        * check for out of order / duplicated packet
302        */
303       if (realend == nettics[netnode])
304 	continue;
305 
306       if (realend < nettics[netnode])
307 	{
308 	  if (debugfile)
309 	    fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics);
310 	  continue;
311 	}
312 
313       /*
314        * check for a missed packet
315        */
316       if (realstart > nettics[netnode])
317 	{
318 	  /* stop processing until the other system resends the missed tics */
319 	  if (debugfile)
320 	    fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, realstart, nettics[netnode]);
321 	  remoteresend[netnode] = true;
322 	  continue;
323 	}
324 
325       /*
326        * update command store from the packet
327        */
328       {
329 	int             start;
330 
331 	remoteresend[netnode] = false;
332 
333 	start = nettics[netnode] - realstart;
334 	src = &netbuffer->cmds[start];
335 
336 	while (nettics[netnode] < realend)
337 	  {
338 	    dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
339 	    nettics[netnode]++;
340 	    *dest = *src;
341 	    src++;
342 	  }
343       }
344     }
345 }
346 
347 /*
348   =============
349   =
350   = NetUpdate
351   =
352   = Builds ticcmds for console player
353   = sends out a packet
354   =============
355 */
356 
357 int      gametime;
358 
NetUpdate(void)359 void NetUpdate (void)
360 {
361   int             nowtime;
362   int             newtics;
363   int             i,j;
364   int             realstart;
365   int             gameticdiv;
366 
367   /*
368    * check time
369    */
370   nowtime = I_GetTime ()/ticdup;
371   newtics = nowtime - gametime;
372   gametime = nowtime;
373 
374   if (newtics <= 0)                       /* nothing new to update */
375     goto listen;
376 
377   if (skiptics <= newtics)
378     {
379       newtics -= skiptics;
380       skiptics = 0;
381     }
382   else
383     {
384       skiptics -= newtics;
385       newtics = 0;
386     }
387 
388 
389   netbuffer->player = consoleplayer;
390 
391   /*
392    * build new ticcmds for console player
393    */
394   gameticdiv = gametic/ticdup;
395   for (i=0 ; i<newtics ; i++)
396     {
397       I_StartTic ();
398       D_ProcessEvents ();
399       if (maketic - gameticdiv >= BACKUPTICS/2-1)
400 	break;          /* can't hold any more */
401       /*   printf ("mk:%i ",maketic);   */
402       G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
403       maketic++;
404     }
405 
406 
407   if (singletics)
408     return;         /* singletic update is syncronous */
409 
410   /*
411    * send the packet to the other nodes
412    */
413   for (i=0 ; i<doomcom->numnodes ; i++)
414     if (nodeingame[i])
415       {
416 	netbuffer->starttic = realstart = resendto[i];
417 	netbuffer->numtics = maketic - realstart;
418 	if (netbuffer->numtics > BACKUPTICS)
419 	  I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
420 
421 	resendto[i] = maketic - doomcom->extratics;
422 
423 	for (j=0 ; j< netbuffer->numtics ; j++)
424 	  netbuffer->cmds[j] =
425 	    localcmds[(realstart+j)%BACKUPTICS];
426 
427 	if (remoteresend[i])
428 	  {
429 	    netbuffer->retransmitfrom = nettics[i];
430 	    HSendPacket (i, NCMD_RETRANSMIT);
431 	  }
432 	else
433 	  {
434 	    netbuffer->retransmitfrom = 0;
435 	    HSendPacket (i, 0);
436 	  }
437       }
438 
439   /*
440    * listen for other packets
441    */
442  listen:
443 
444   GetPackets ();
445 }
446 
447 
448 /*
449   =====================
450   =
451   = CheckAbort
452   =
453   =====================
454 */
455 
CheckAbort(void)456 void CheckAbort (void)
457 {
458   event_t         *ev;
459   int             stoptic;
460 
461   stoptic = I_GetTime () + 2;
462   while (I_GetTime() < stoptic)
463     I_StartTic ();
464 
465   I_StartTic ();
466   for ( ; eventtail != eventhead
467 	  ; eventtail = (++eventtail)&(MAXEVENTS-1) )
468     {
469       ev = &events[eventtail];
470       if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
471 	I_Error ("Network game synchronization aborted.");
472     }
473 }
474 
475 /*
476   =====================
477   =
478   = D_ArbitrateNetStart
479   =
480   =====================
481 */
482 
D_ArbitrateNetStart(void)483 void D_ArbitrateNetStart (void)
484 {
485   int             i;
486   boolean         gotinfo[MAXNETNODES];
487 
488   autostart = true;
489   memset (gotinfo,0,sizeof(gotinfo));
490 
491   if (doomcom->consoleplayer)
492     {       /* listen for setup info from key player */
493       printf ("listening for network start info...\n");
494       while (1)
495 	{
496 	  CheckAbort ();
497 	  if (!HGetPacket ())
498 	    continue;
499 	  if (netbuffer->checksum & NCMD_SETUP)
500 	    {
501 	      if (netbuffer->player != VERSION)
502 		I_Error ("Different Heretic versions cannot play a net game!");
503 	      startskill = netbuffer->retransmitfrom & 15;
504 	      deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6;
505 	      nomonsters = (netbuffer->retransmitfrom & 0x20) > 0;
506 	      respawnparm = (netbuffer->retransmitfrom & 0x10) > 0;
507 				/*   startmap = netbuffer->starttic & 0x3f;   */
508 				/*   startepisode = netbuffer->starttic >> 6;   */
509 	      startmap = netbuffer->starttic&15;
510 	      startepisode = netbuffer->starttic>>4;
511 	      return;
512 	    }
513 	}
514     }
515   else
516     {       /* key player, send the setup info */
517       printf ("sending network start info...\n");
518       do
519 	{
520 	  CheckAbort ();
521 	  for (i=0 ; i<doomcom->numnodes ; i++)
522 	    {
523 	      netbuffer->retransmitfrom = startskill;
524 	      if (deathmatch)
525 		netbuffer->retransmitfrom |= (deathmatch<<6);
526 	      if (nomonsters)
527 		netbuffer->retransmitfrom |= 0x20;
528 	      if (respawnparm)
529 		netbuffer->retransmitfrom |= 0x10;
530 				/*   netbuffer->starttic = startepisode * 64 + startmap;   */
531 	      netbuffer->starttic = (startepisode<<4)+startmap;
532 	      netbuffer->player = VERSION;
533 	      netbuffer->numtics = 0;
534 	      HSendPacket (i, NCMD_SETUP);
535 	    }
536 
537 #if 1
538 	  for(i = 10 ; i  &&  HGetPacket(); --i)
539 	    {
540 	      if((netbuffer->player&0x7f) < MAXNETNODES)
541 		gotinfo[netbuffer->player&0x7f] = true;
542 	    }
543 #else
544 	  while (HGetPacket ())
545 	    {
546 	      gotinfo[netbuffer->player&0x7f] = true;
547 	    }
548 #endif
549 
550 	  for (i=1 ; i<doomcom->numnodes ; i++)
551 	    if (!gotinfo[i])
552 	      break;
553 	} while (i < doomcom->numnodes);
554     }
555 }
556 
557 /*
558   ===================
559   =
560   = D_CheckNetGame
561   =
562   = Works out player numbers among the net participants
563   ===================
564 */
565 
566 extern  int       viewangleoffset;
567 
D_CheckNetGame(void)568 void D_CheckNetGame (void)
569 {
570   int             i;
571 
572   for (i=0 ; i<MAXNETNODES ; i++)
573     {
574       nodeingame[i] = false;
575       nettics[i] = 0;
576       remoteresend[i] = false;        /* set when local needs tics */
577       resendto[i] = 0;                /* which tic to start sending */
578     }
579 
580   /* I_InitNetwork sets doomcom and netgame */
581   I_InitNetwork ();
582   if (doomcom->id != DOOMCOM_ID)
583     I_Error ("Doomcom buffer invalid!");
584   netbuffer = &doomcom->data;
585   consoleplayer = displayplayer = doomcom->consoleplayer;
586   if (netgame)
587     D_ArbitrateNetStart ();
588   printf ("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n", startskill, deathmatch, startmap, startepisode);
589 
590   /* read values out of doomcom */
591   ticdup = doomcom->ticdup;
592   maxsend = BACKUPTICS/(2*ticdup)-1;
593   if (maxsend<1)
594     maxsend = 1;
595 
596   for (i=0 ; i<doomcom->numplayers ; i++)
597     playeringame[i] = true;
598   for (i=0 ; i<doomcom->numnodes ; i++)
599     nodeingame[i] = true;
600 
601   printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
602 
603 }
604 
605 /*
606   ==================
607   =
608   = D_QuitNetGame
609   =
610   = Called before quitting to leave a net game without hanging the
611   = other players
612   =
613   ==================
614 */
615 
D_QuitNetGame(void)616 void D_QuitNetGame (void)
617 {
618   int             i, j;
619 
620   if (debugfile)
621     fclose (debugfile);
622 
623   if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
624     return;
625 
626   /* send a bunch of packets for security */
627   netbuffer->player = consoleplayer;
628   netbuffer->numtics = 0;
629   for (i=0 ; i<4 ; i++)
630     {
631       for (j=0 ; j<doomcom->numnodes ; j++)
632 	if (nodeingame[j])
633 	  HSendPacket (j, NCMD_EXIT);
634       I_WaitVBL (1);
635     }
636 }
637 
638 
639 
640 /*
641   ===============
642   =
643   = TryRunTics
644   =
645   ===============
646 */
647 
648 int     frametics[4], frameon;
649 int     frameskip[4];
650 int     oldnettics;
651 extern  boolean advancedemo;
652 
TryRunTics(void)653 void TryRunTics (void)
654 {
655   int             i;
656   int             lowtic;
657   int             entertic;
658   static int      oldentertics;
659   int             realtics, availabletics;
660   int             counts;
661   int             numplaying;
662 
663   /*
664    * get real tics
665    */
666   entertic = I_GetTime ()/ticdup;
667   realtics = entertic - oldentertics;
668   oldentertics = entertic;
669 
670   /*
671    * get available tics
672    */
673   NetUpdate ();
674 
675   lowtic = MAXINT;
676   numplaying = 0;
677   for (i=0 ; i<doomcom->numnodes ; i++)
678     if (nodeingame[i])
679       {
680 	numplaying++;
681 	if (nettics[i] < lowtic)
682 	  lowtic = nettics[i];
683       }
684   availabletics = lowtic - gametic/ticdup;
685 
686 
687   /*
688    * decide how many tics to run
689    */
690   if (realtics < availabletics-1)
691     counts = realtics+1;
692   else if (realtics < availabletics)
693     counts = realtics;
694   else
695     counts = availabletics;
696   if (counts < 1)
697     counts = 1;
698 
699   frameon++;
700 
701   if (debugfile)
702     fprintf (debugfile,"=======real: %i  avail: %i  game: %i\n",realtics, availabletics,counts);
703 
704   if (!demoplayback)
705     {
706       /* =============================================================================
707        *
708        *      ideally nettics[0] should be 1 - 3 tics above lowtic
709        *      if we are consistantly slower, speed up time
710        *
711        */
712 
713       for (i=0 ; i<MAXPLAYERS ; i++)
714 	if (playeringame[i])
715 	  break;
716       if (consoleplayer == i)
717 	{       /* the key player does not adapt */
718 	}
719       else
720 	{
721 	  if (nettics[0] <= nettics[nodeforplayer[i]])
722 	    {
723 	      gametime--;
724 	      /*                      printf ("-"); */
725 	    }
726 	  frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
727 	  oldnettics = nettics[0];
728 	  if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
729 	    {
730 	      skiptics = 1;
731 	      /*                      printf ("+"); */
732 	    }
733 	}
734     }
735 
736   /* =============================================================================
737    *    demoplayback
738    *
739    *    wait for new tics if needed
740    *
741    */
742   while (lowtic < gametic/ticdup + counts)
743     {
744 #ifdef HAVE_USLEEP
745       /* Busywaiting is evil! We sleep 1/8 tic for each iteration. */
746       usleep((1000000/TICRATE)/8);
747 #endif
748 
749       NetUpdate ();
750       lowtic = MAXINT;
751 
752       for (i=0 ; i<doomcom->numnodes ; i++)
753 	if (nodeingame[i] && nettics[i] < lowtic)
754 	  lowtic = nettics[i];
755 
756       if (lowtic < gametic/ticdup)
757 	I_Error ("TryRunTics: lowtic < gametic");
758 
759       /* don't stay in here forever -- give the menu a chance to work */
760       if (I_GetTime ()/ticdup - entertic >= 20)
761 	{
762 	  MN_Ticker ();
763 	  return;
764 	}
765     }
766 
767   /*
768    * run the count * ticdup dics
769    */
770   while (counts--)
771     {
772       for (i=0 ; i<ticdup ; i++)
773 	{
774 	  if (gametic/ticdup > lowtic)
775 	    I_Error ("gametic>lowtic");
776 	  if (advancedemo)
777 	    D_DoAdvanceDemo ();
778 	  MN_Ticker ();
779 	  G_Ticker ();
780 	  gametic++;
781 	  /*
782 	   * modify command for duplicated tics
783 	   */
784 	  if (i != ticdup-1)
785 	    {
786 	      ticcmd_t        *cmd;
787 	      int             buf;
788 	      int             j;
789 
790 	      buf = (gametic/ticdup)%BACKUPTICS;
791 	      for (j=0 ; j<MAXPLAYERS ; j++)
792 		{
793 		  cmd = &netcmds[j][buf];
794 		  cmd->chatchar = 0;
795 		  if (cmd->buttons & BT_SPECIAL)
796 		    cmd->buttons = 0;
797 		}
798 	    }
799 	}
800       NetUpdate ();                  /* check for new console commands */
801     }
802 }
803 
804 
805