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