2 //**************************************************************************
4 //** d_net.c : Heretic 2 : Raven Software, Corp.
11 //** This version has the fixed ticdup code.
13 //**************************************************************************
17 #include <stdlib.h> // for atoi()
19 #define NCMD_EXIT 0x80000000
20 #define NCMD_RETRANSMIT 0x40000000
21 #define NCMD_SETUP 0x20000000
22 #define NCMD_KILL 0x10000000 // kill game
23 #define NCMD_CHECKSUM 0x0fffffff
27 doomdata_t *netbuffer; // points inside doomcom
31 ==============================================================================
35 gametic is the tic about to (or currently being) run
36 maketic is the tick that hasn't had control made for it yet
37 nettics[] has the maketics for all players
39 a gametic cannot be run until nettics[] > gametic for all players
41 ==============================================================================
44 #define RESENDCOUNT 10
45 #define PL_DRONE 0x80 // bit flag in doomdata->player
47 ticcmd_t localcmds[BACKUPTICS];
49 ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
50 int nettics[MAXNETNODES];
51 boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
52 boolean remoteresend[MAXNETNODES]; // set when local needs tics
53 int resendto[MAXNETNODES]; // set when remote needs tics
54 int resendcount[MAXNETNODES];
56 int nodeforplayer[MAXPLAYERS];
59 int lastnettic, skiptics;
61 int maxsend; // BACKUPTICS/(2*ticdup)-1
63 void H2_ProcessEvents (void);
64 void G_BuildTiccmd (ticcmd_t *cmd);
65 void H2_DoAdvanceDemo (void);
66 extern void ST_NetProgress(void);
67 extern void ST_NetDone(void);
69 boolean reboundpacket;
70 doomdata_t reboundstore;
73 int NetbufferSize (void)
75 return (int)&(((doomdata_t *)0)->cmds[netbuffer->numtics]);
78 unsigned NetbufferChecksum (void)
85 #if defined(NeXT) || defined(NORMALUNIX)
86 return 0; // byte order problems
89 l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
91 c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);
93 return c & NCMD_CHECKSUM;
96 int ExpandTics (int low)
100 delta = low - (maketic&0xff);
102 if (delta >= -64 && delta <= 64)
103 return (maketic&~0xff) + low;
105 return (maketic&~0xff) - 256 + low;
107 return (maketic&~0xff) + 256 + low;
109 I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic);
114 //============================================================================
125 void HSendPacket (int node, int flags)
127 netbuffer->checksum = NetbufferChecksum () | flags;
131 reboundstore = *netbuffer;
132 reboundpacket = true;
140 I_Error ("Tried to transmit to another node");
142 doomcom->command = CMD_SEND;
143 doomcom->remotenode = node;
144 doomcom->datalength = NetbufferSize ();
150 if (netbuffer->checksum & NCMD_RETRANSMIT)
151 realretrans = ExpandTics (netbuffer->retransmitfrom);
154 fprintf (debugfile,"send (%i + %i, R %i) [%i] "
155 ,ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
156 for (i=0 ; i<doomcom->datalength ; i++)
157 fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
158 fprintf (debugfile,"\n");
164 //==========================================================================
168 //==========================================================================
170 void NET_SendFrags(player_t *player)
175 netbuffer->checksum = NetbufferChecksum();
183 I_Error ("Tried to transmit to another node");
187 for(i = 0; i < MAXPLAYERS; i++)
189 frags += player->frags[i];
191 doomcom->command = CMD_FRAG;
192 doomcom->remotenode = frags;
193 doomcom->datalength = NetbufferSize ();
203 = Returns false if no packet is waiting
208 boolean HGetPacket (void)
212 *netbuffer = reboundstore;
213 doomcom->remotenode = 0;
214 reboundpacket = false;
223 doomcom->command = CMD_GET;
225 if (doomcom->remotenode == -1)
228 if (doomcom->datalength != NetbufferSize ())
231 fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
235 if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
238 fprintf (debugfile,"bad packet checksum\n");
247 if (netbuffer->checksum & NCMD_SETUP)
248 fprintf (debugfile,"setup packet\n");
251 if (netbuffer->checksum & NCMD_RETRANSMIT)
252 realretrans = ExpandTics (netbuffer->retransmitfrom);
255 fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",doomcom->remotenode,
256 ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
257 for (i=0 ; i<doomcom->datalength ; i++)
258 fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
259 fprintf (debugfile,"\n");
276 void GetPackets (void)
280 ticcmd_t *src, *dest;
284 while (HGetPacket ())
286 if (netbuffer->checksum & NCMD_SETUP)
287 continue; // extra setup packet
289 netconsole = netbuffer->player & ~PL_DRONE;
290 netnode = doomcom->remotenode;
292 // to save bytes, only the low byte of tic numbers are sent
293 // Figure out what the rest of the bytes are
295 realstart = ExpandTics (netbuffer->starttic);
296 realend = (realstart+netbuffer->numtics);
299 // check for exiting the game
301 if (netbuffer->checksum & NCMD_EXIT)
303 if (!nodeingame[netnode])
305 nodeingame[netnode] = false;
306 playeringame[netconsole] = false;
307 strcpy (exitmsg, "PLAYER 1 LEFT THE GAME");
308 exitmsg[7] += netconsole;
309 P_SetMessage(&players[consoleplayer], exitmsg, true);
310 S_StartSound(NULL, SFX_CHAT);
311 // players[consoleplayer].message = exitmsg;
312 // if (demorecording)
313 // G_CheckDemoStatus ();
318 // check for a remote game kill
320 if (netbuffer->checksum & NCMD_KILL)
321 I_Error ("Killed by network driver");
323 nodeforplayer[netconsole] = netnode;
326 // check for retransmit request
328 if ( resendcount[netnode] <= 0
329 && (netbuffer->checksum & NCMD_RETRANSMIT) )
331 resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
333 fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
334 resendcount[netnode] = RESENDCOUNT;
337 resendcount[netnode]--;
340 // check for out of order / duplicated packet
342 if (realend == nettics[netnode])
345 if (realend < nettics[netnode])
348 fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics);
353 // check for a missed packet
355 if (realstart > nettics[netnode])
357 // stop processing until the other system resends the missed tics
359 fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, realstart, nettics[netnode]);
360 remoteresend[netnode] = true;
365 // update command store from the packet
370 remoteresend[netnode] = false;
372 start = nettics[netnode] - realstart;
373 src = &netbuffer->cmds[start];
375 while (nettics[netnode] < realend)
377 dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
392 = Builds ticcmds for console player
399 void NetUpdate (void)
410 nowtime = I_GetTime ()/ticdup;
411 newtics = nowtime - gametime;
414 if (newtics <= 0) // nothing new to update
417 if (skiptics <= newtics)
429 netbuffer->player = consoleplayer;
432 // build new ticcmds for console player
434 gameticdiv = gametic/ticdup;
435 for (i=0 ; i<newtics ; i++)
439 if (maketic - gameticdiv >= BACKUPTICS/2-1)
440 break; // can't hold any more
441 //printf ("mk:%i ",maketic);
442 G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
448 return; // singletic update is syncronous
451 // send the packet to the other nodes
453 for (i=0 ; i<doomcom->numnodes ; i++)
456 netbuffer->starttic = realstart = resendto[i];
457 netbuffer->numtics = maketic - realstart;
458 if (netbuffer->numtics > BACKUPTICS)
459 I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
461 resendto[i] = maketic - doomcom->extratics;
463 for (j=0 ; j< netbuffer->numtics ; j++)
465 localcmds[(realstart+j)%BACKUPTICS];
469 netbuffer->retransmitfrom = nettics[i];
470 HSendPacket (i, NCMD_RETRANSMIT);
474 netbuffer->retransmitfrom = 0;
480 // listen for other packets
489 =====================
493 =====================
496 void CheckAbort (void)
501 stoptic = I_GetTime () + 2;
502 while (I_GetTime() < stoptic)
506 for ( ; eventtail != eventhead
507 ; eventtail = (++eventtail)&(MAXEVENTS-1) )
509 ev = &events[eventtail];
510 if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
511 I_Error ("Network game synchronization aborted.");
516 =====================
518 = D_ArbitrateNetStart
520 =====================
523 void D_ArbitrateNetStart (void)
526 boolean gotinfo[MAXNETNODES];
527 boolean gotClass[MAXNETNODES];
530 fprintf(stderr, "Pre memset\n");
531 memset (gotClass,0,sizeof(gotClass));
532 memset (gotinfo,0,sizeof(gotinfo));
533 fprintf(stderr, "post memset\n");
534 gotClass[doomcom->consoleplayer] = true;
541 { // Check for any incoming packets
542 if(netbuffer->checksum&NCMD_SETUP && netbuffer->starttic >= 64)
545 PlayerClass[netbuffer->player] = netbuffer->starttic&0x3f;
546 if(!gotClass[netbuffer->player])
548 gotClass[netbuffer->player] = true;
552 if(netbuffer->retransmitfrom)
553 { // that node has received info from all other nodes
554 gotinfo[netbuffer->player] = true;
558 // Keep sending out packets containing the console class
559 for(i = 0; i < doomcom->numnodes; i++)
561 netbuffer->player = doomcom->consoleplayer;
562 netbuffer->starttic = PlayerClass[doomcom->consoleplayer]+64;
563 netbuffer->retransmitfrom = gotinfo[doomcom->consoleplayer];
564 netbuffer->numtics = 0;
565 HSendPacket(i, NCMD_SETUP);
567 for(i = 0; i < doomcom->numnodes; i++)
568 { // Make sure that all nodes have sent class info
575 if(i < doomcom->numnodes)
580 { // consoleplayer has received all player classes
581 if(gotinfo[doomcom->consoleplayer])
587 gotinfo[doomcom->consoleplayer] = true;
588 ST_Message("All player classes received, ready to proceed\n");
592 for (i = 0; i < doomcom->numnodes; i++)
593 { // Make sure that all nodes are ready to proceed
599 } while(i < doomcom->numnodes);
601 memset (gotinfo,0,sizeof(gotinfo));
603 if (doomcom->consoleplayer)
604 { // listen for setup info from key player
605 // ST_Message ("listening for network start info...\n");
611 if(netbuffer->checksum & NCMD_SETUP && netbuffer->starttic < 64)
613 if (netbuffer->player != VERSION)
614 I_Error ("Different HEXEN versions cannot play a net game!");
615 startskill = netbuffer->retransmitfrom & 15;
616 deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6;
617 nomonsters = (netbuffer->retransmitfrom & 0x20) > 0;
618 respawnparm = (netbuffer->retransmitfrom & 0x10) > 0;
619 startmap = netbuffer->starttic & 0x3f;
626 { // key player, send the setup info
627 // ST_Message ("sending network start info...\n");
631 for (i=0 ; i<doomcom->numnodes ; i++)
633 netbuffer->retransmitfrom = startskill;
635 netbuffer->retransmitfrom |= (deathmatch<<6);
637 netbuffer->retransmitfrom |= 0x20;
639 netbuffer->retransmitfrom |= 0x10;
640 netbuffer->starttic = startmap&0x3f;
641 netbuffer->player = VERSION;
642 netbuffer->numtics = 0;
643 HSendPacket (i, NCMD_SETUP);
647 for(i = 10 ; i && HGetPacket(); --i)
649 if((netbuffer->player&0x7f) < MAXNETNODES)
650 gotinfo[netbuffer->player&0x7f] = true;
653 while (HGetPacket ())
655 gotinfo[netbuffer->player&0x7f] = true;
659 for (i=1 ; i<doomcom->numnodes ; i++)
662 } while (i < doomcom->numnodes);
671 = Works out player numbers among the net participants
675 extern int viewangleoffset;
677 void D_CheckNetGame (void)
682 for (i=0 ; i<MAXNETNODES ; i++)
684 nodeingame[i] = false;
686 remoteresend[i] = false; // set when local needs tics
687 resendto[i] = 0; // which tic to start sending
690 // I_InitNetwork sets doomcom and netgame
692 if (doomcom->id != DOOMCOM_ID)
693 I_Error ("Doomcom buffer invalid!");
694 netbuffer = &doomcom->data;
695 consoleplayer = displayplayer = doomcom->consoleplayer;
696 pClass = PCLASS_FIGHTER;
697 if((i = M_CheckParm("-class"))) /* jim parens added to placate gcc */
699 pClass = atoi(myargv[i+1]);
700 if(pClass > PCLASS_MAGE || pClass < PCLASS_FIGHTER)
702 I_Error("Invalid player class: %d\n", pClass);
704 ST_Message("\nPlayer Class: %d\n", pClass);
706 PlayerClass[consoleplayer] = pClass;
708 D_ArbitrateNetStart ();
709 //ST_Message ("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", startskill, deathmatch, startmap, startepisode);
710 // read values out of doomcom
711 ticdup = doomcom->ticdup;
712 maxsend = BACKUPTICS/(2*ticdup)-1;
716 for (i=0 ; i<doomcom->numplayers ; i++)
717 playeringame[i] = true;
718 for (i=0 ; i<doomcom->numnodes ; i++)
719 nodeingame[i] = true;
721 //ST_Message ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
730 = Called before quitting to leave a net game without hanging the
736 void D_QuitNetGame (void)
743 if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
746 // send a bunch of packets for security
747 netbuffer->player = consoleplayer;
748 netbuffer->numtics = 0;
749 for (i=0 ; i<4 ; i++)
751 for (j=1 ; j<doomcom->numnodes ; j++)
753 HSendPacket (j, NCMD_EXIT);
768 int frametics[4], frameon;
771 extern boolean advancedemo;
773 void TryRunTics (void)
778 static int oldentertics;
779 int realtics, availabletics;
786 entertic = I_GetTime ()/ticdup;
787 realtics = entertic - oldentertics;
788 oldentertics = entertic;
791 // get available tics
797 for (i=0 ; i<doomcom->numnodes ; i++)
801 if (nettics[i] < lowtic)
804 availabletics = lowtic - gametic/ticdup;
808 // decide how many tics to run
810 if (realtics < availabletics-1)
812 else if (realtics < availabletics)
815 counts = availabletics;
822 fprintf (debugfile,"=======real: %i avail: %i game: %i\n",realtics, availabletics,counts);
826 //=============================================================================
828 // ideally nettics[0] should be 1 - 3 tics above lowtic
829 // if we are consistantly slower, speed up time
831 for (i=0 ; i<MAXPLAYERS ; i++)
834 if (consoleplayer == i)
835 { // the key player does not adapt
839 if (nettics[0] <= nettics[nodeforplayer[i]])
844 frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
845 oldnettics = nettics[0];
846 if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
852 //=============================================================================
856 // wait for new tics if needed
858 while (lowtic < gametic/ticdup + counts)
864 for (i=0 ; i<doomcom->numnodes ; i++)
865 if (nodeingame[i] && nettics[i] < lowtic)
868 if (lowtic < gametic/ticdup)
869 I_Error ("TryRunTics: lowtic < gametic");
871 // don't stay in here forever -- give the menu a chance to work
872 if (I_GetTime ()/ticdup - entertic >= 20)
880 // run the count * ticdup dics
884 for (i=0 ; i<ticdup ; i++)
886 if (gametic/ticdup > lowtic)
887 I_Error ("gametic>lowtic");
894 // modify command for duplicated tics
902 buf = (gametic/ticdup)%BACKUPTICS;
903 for (j=0 ; j<MAXPLAYERS ; j++)
905 cmd = &netcmds[j][buf];
907 if (cmd->buttons & BT_SPECIAL)
912 NetUpdate (); // check for new console commands