1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
6 // Copyright (C) 1993-1996 by id Software, Inc.
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.
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
20 // DOOM Network game communication and protocol,
21 // all OS independend parts.
23 //-----------------------------------------------------------------------------
26 static const char rcsid[] = "$Id: d_net.c,v 1.3 1997/02/03 22:01:47 b1 Exp $";
37 #define NCMD_EXIT 0x80000000
38 #define NCMD_RETRANSMIT 0x40000000
39 #define NCMD_SETUP 0x20000000
40 #define NCMD_KILL 0x10000000 // kill game
41 #define NCMD_CHECKSUM 0x0fffffff
45 doomdata_t* netbuffer; // points inside doomcom
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
55 // a gametic cannot be run until nettics[] > gametic for all players
57 #define RESENDCOUNT 10
58 #define PL_DRONE 0x80 // bit flag in doomdata->player
60 ticcmd_t localcmds[BACKUPTICS];
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];
69 int nodeforplayer[MAXPLAYERS];
75 int maxsend; // BACKUPTICS/(2*ticdup)-1
78 void D_ProcessEvents (void);
79 void G_BuildTiccmd (ticcmd_t *cmd);
80 void D_DoAdvanceDemo (void);
82 boolean reboundpacket;
83 doomdata_t reboundstore;
90 int NetbufferSize (void)
92 return (int)&(((doomdata_t *)0)->cmds[netbuffer->numtics]);
98 unsigned NetbufferChecksum (void)
107 return 0; // byte order problems
110 l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
111 for (i=0 ; i<l ; i++)
112 c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);
114 return c & NCMD_CHECKSUM;
120 int ExpandTics (int low)
124 delta = low - (maketic&0xff);
126 if (delta >= -64 && delta <= 64)
127 return (maketic&~0xff) + low;
129 return (maketic&~0xff) - 256 + low;
131 return (maketic&~0xff) + 256 + low;
133 I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic);
147 netbuffer->checksum = NetbufferChecksum () | flags;
151 reboundstore = *netbuffer;
152 reboundpacket = true;
160 I_Error ("Tried to transmit to another node");
162 doomcom->command = CMD_SEND;
163 doomcom->remotenode = node;
164 doomcom->datalength = NetbufferSize ();
170 if (netbuffer->checksum & NCMD_RETRANSMIT)
171 realretrans = ExpandTics (netbuffer->retransmitfrom);
175 fprintf (debugfile,"send (%i + %i, R %i) [%i] ",
176 ExpandTics(netbuffer->starttic),
177 netbuffer->numtics, realretrans, doomcom->datalength);
179 for (i=0 ; i<doomcom->datalength ; i++)
180 fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
182 fprintf (debugfile,"\n");
190 // Returns false if no packet is waiting
192 boolean HGetPacket (void)
196 *netbuffer = reboundstore;
197 doomcom->remotenode = 0;
198 reboundpacket = false;
208 doomcom->command = CMD_GET;
211 if (doomcom->remotenode == -1)
214 if (doomcom->datalength != NetbufferSize ())
217 fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
221 if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
224 fprintf (debugfile,"bad packet checksum\n");
233 if (netbuffer->checksum & NCMD_SETUP)
234 fprintf (debugfile,"setup packet\n");
237 if (netbuffer->checksum & NCMD_RETRANSMIT)
238 realretrans = ExpandTics (netbuffer->retransmitfrom);
242 fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",
244 ExpandTics(netbuffer->starttic),
245 netbuffer->numtics, realretrans, doomcom->datalength);
247 for (i=0 ; i<doomcom->datalength ; i++)
248 fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
249 fprintf (debugfile,"\n");
261 void GetPackets (void)
265 ticcmd_t *src, *dest;
269 while ( HGetPacket() )
271 if (netbuffer->checksum & NCMD_SETUP)
272 continue; // extra setup packet
274 netconsole = netbuffer->player & ~PL_DRONE;
275 netnode = doomcom->remotenode;
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);
282 // check for exiting the game
283 if (netbuffer->checksum & NCMD_EXIT)
285 if (!nodeingame[netnode])
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;
293 G_CheckDemoStatus ();
297 // check for a remote game kill
298 if (netbuffer->checksum & NCMD_KILL)
299 I_Error ("Killed by network driver");
301 nodeforplayer[netconsole] = netnode;
303 // check for retransmit request
304 if ( resendcount[netnode] <= 0
305 && (netbuffer->checksum & NCMD_RETRANSMIT) )
307 resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
309 fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
310 resendcount[netnode] = RESENDCOUNT;
313 resendcount[netnode]--;
315 // check for out of order / duplicated packet
316 if (realend == nettics[netnode])
319 if (realend < nettics[netnode])
323 "out of order packet (%i + %i)\n" ,
324 realstart,netbuffer->numtics);
328 // check for a missed packet
329 if (realstart > nettics[netnode])
331 // stop processing until the other system resends the missed tics
334 "missed tics from %i (%i - %i)\n",
335 netnode, realstart, nettics[netnode]);
336 remoteresend[netnode] = true;
340 // update command store from the packet
344 remoteresend[netnode] = false;
346 start = nettics[netnode] - realstart;
347 src = &netbuffer->cmds[start];
349 while (nettics[netnode] < realend)
351 dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
363 // Builds ticcmds for console player,
364 // sends out a packet
368 void NetUpdate (void)
377 nowtime = I_GetTime ()/ticdup;
378 newtics = nowtime - gametime;
381 if (newtics <= 0) // nothing new to update
384 if (skiptics <= newtics)
396 netbuffer->player = consoleplayer;
398 // build new ticcmds for console player
399 gameticdiv = gametic/ticdup;
400 for (i=0 ; i<newtics ; i++)
404 if (maketic - gameticdiv >= BACKUPTICS/2-1)
405 break; // can't hold any more
407 //printf ("mk:%i ",maketic);
408 G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
414 return; // singletic update is syncronous
416 // send the packet to the other nodes
417 for (i=0 ; i<doomcom->numnodes ; i++)
420 netbuffer->starttic = realstart = resendto[i];
421 netbuffer->numtics = maketic - realstart;
422 if (netbuffer->numtics > BACKUPTICS)
423 I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
425 resendto[i] = maketic - doomcom->extratics;
427 for (j=0 ; j< netbuffer->numtics ; j++)
429 localcmds[(realstart+j)%BACKUPTICS];
433 netbuffer->retransmitfrom = nettics[i];
434 HSendPacket (i, NCMD_RETRANSMIT);
438 netbuffer->retransmitfrom = 0;
443 // listen for other packets
453 void CheckAbort (void)
458 stoptic = I_GetTime () + 2;
459 while (I_GetTime() < stoptic)
463 for ( ; eventtail != eventhead
464 ; eventtail = (++eventtail)&(MAXEVENTS-1) )
466 ev = &events[eventtail];
467 if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
468 I_Error ("Network game synchronization aborted.");
474 // D_ArbitrateNetStart
476 void D_ArbitrateNetStart (void)
479 boolean gotinfo[MAXNETNODES];
482 memset (gotinfo,0,sizeof(gotinfo));
484 if (doomcom->consoleplayer)
486 // listen for setup info from key player
487 printf ("listening for network start info...\n");
493 if (netbuffer->checksum & NCMD_SETUP)
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;
509 // key player, send the setup info
510 printf ("sending network start info...\n");
514 for (i=0 ; i<doomcom->numnodes ; i++)
516 netbuffer->retransmitfrom = startskill;
518 netbuffer->retransmitfrom |= (deathmatch<<6);
520 netbuffer->retransmitfrom |= 0x20;
522 netbuffer->retransmitfrom |= 0x10;
523 netbuffer->starttic = startepisode * 64 + startmap;
524 netbuffer->player = VERSION;
525 netbuffer->numtics = 0;
526 HSendPacket (i, NCMD_SETUP);
529 for(i = 10 ; i && HGetPacket(); --i)
531 if((netbuffer->player&0x7f) < MAXNETNODES)
532 gotinfo[netbuffer->player&0x7f] = true;
535 for (i=1 ; i<doomcom->numnodes ; i++)
538 } while (i < doomcom->numnodes);
544 // Works out player numbers among the net participants
546 extern int viewangleoffset;
548 void D_CheckNetGame (void)
552 for (i=0 ; i<MAXNETNODES ; i++)
554 nodeingame[i] = false;
556 remoteresend[i] = false; // set when local needs tics
557 resendto[i] = 0; // which tic to start sending
560 // I_InitNetwork sets doomcom and netgame
562 if (doomcom->id != DOOMCOM_ID)
563 I_Error ("Doomcom buffer invalid!");
565 netbuffer = &doomcom->data;
566 consoleplayer = displayplayer = doomcom->consoleplayer;
568 D_ArbitrateNetStart ();
570 printf ("startskill %i deathmatch: %i startmap: %i startepisode: %i\n",
571 startskill, deathmatch, startmap, startepisode);
573 // read values out of doomcom
574 ticdup = doomcom->ticdup;
575 maxsend = BACKUPTICS/(2*ticdup)-1;
579 for (i=0 ; i<doomcom->numplayers ; i++)
580 playeringame[i] = true;
581 for (i=0 ; i<doomcom->numnodes ; i++)
582 nodeingame[i] = true;
584 printf ("player %i of %i (%i nodes)\n",
585 consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
592 // Called before quitting to leave a net game
593 // without hanging the other players
595 void D_QuitNetGame (void)
602 if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
605 // send a bunch of packets for security
606 netbuffer->player = consoleplayer;
607 netbuffer->numtics = 0;
608 for (i=0 ; i<4 ; i++)
610 for (j=1 ; j<doomcom->numnodes ; j++)
612 HSendPacket (j, NCMD_EXIT);
627 extern boolean advancedemo;
629 void TryRunTics (void)
634 static int oldentertics;
641 entertic = I_GetTime ()/ticdup;
642 realtics = entertic - oldentertics;
643 oldentertics = entertic;
645 // get available tics
650 for (i=0 ; i<doomcom->numnodes ; i++)
655 if (nettics[i] < lowtic)
659 availabletics = lowtic - gametic/ticdup;
661 // decide how many tics to run
662 if (realtics < availabletics-1)
664 else if (realtics < availabletics)
667 counts = availabletics;
676 "=======real: %i avail: %i game: %i\n",
677 realtics, availabletics,counts);
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++)
686 if (consoleplayer == i)
688 // the key player does not adapt
692 if (nettics[0] <= nettics[nodeforplayer[i]])
697 frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
698 oldnettics = nettics[0];
699 if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
707 // wait for new tics if needed
708 while (lowtic < gametic/ticdup + counts)
713 for (i=0 ; i<doomcom->numnodes ; i++)
714 if (nodeingame[i] && nettics[i] < lowtic)
717 if (lowtic < gametic/ticdup)
718 I_Error ("TryRunTics: lowtic < gametic");
720 // don't stay in here forever -- give the menu a chance to work
721 if (I_GetTime ()/ticdup - entertic >= 20)
728 // run the count * ticdup dics
731 for (i=0 ; i<ticdup ; i++)
733 if (gametic/ticdup > lowtic)
734 I_Error ("gametic>lowtic");
741 // modify command for duplicated tics
748 buf = (gametic/ticdup)%BACKUPTICS;
749 for (j=0 ; j<MAXPLAYERS ; j++)
751 cmd = &netcmds[j][buf];
753 if (cmd->buttons & BT_SPECIAL)
758 NetUpdate (); // check for new console commands