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