5 #define NCMD_EXIT 0x80000000
6 #define NCMD_RETRANSMIT 0x40000000
7 #define NCMD_SETUP 0x20000000
8 #define NCMD_CHECKSUM 0x0fffffff
11 if more space needs to be crunched out of the protocol...
18 #define NCMD_EXIT 0x80000000
19 #define NCMD_RETRANSMIT 0x40000000 // a retransmit will have 0 tics
20 #define NCMD_DRONE 0x20000000
21 #define NCMD_PLAYER 0x18000000
22 #define NCMD_PLAYERSHIFT 27
23 #define NCMD_TIC 0x00ff0000
24 #define NCMD_TICSHIFT 16
25 #define NCMD_NUMTICS 0x0000ff00
26 #define NCMD_NUMTICSSHIFT 8
27 #define NCMD_CHECKSUM 0x000000ff
36 doomdata_t *netbuffer; // points inside doomcom
40 ==============================================================================
44 gametic is the tic about to (or currently being) run
45 maketic is the tick that hasn't had control made for it yet
46 nettics[] has the maketics for all players
48 a gametic cannot be run until nettics[] > gametic for all players
50 ==============================================================================
53 #define RESENDCOUNT 10
54 #define PL_DRONE 0x80 // bit flag in doomdata->player
56 ticcmd_t localcmds[BACKUPTICS];
58 ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
59 int nettics[MAXNETNODES];
60 boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
61 boolean remoteresend[MAXNETNODES]; // set when local needs tics
62 int resendto[MAXNETNODES]; // set when remote needs tics
63 int resendcount[MAXNETNODES];
65 int nodeforplayer[MAXPLAYERS];
69 int lastnettic, skiptics;
72 void D_ProcessEvents (void);
73 void G_BuildTiccmd (ticcmd_t *cmd);
74 void D_DoAdvanceDemo (void);
76 boolean reboundpacket;
77 doomdata_t reboundstore;
80 int NetbufferSize (void)
82 return (int)&(((doomdata_t *)0)->cmds[netbuffer->numtics]);
85 unsigned NetbufferChecksum (void)
93 return 0; // byte order problems
96 l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
98 c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);
100 return c & NCMD_CHECKSUM;
103 int ExpandTics (int low)
107 delta = low - (maketic&0xff);
109 if (delta >= -64 && delta <= 64)
110 return (maketic&~0xff) + low;
112 return (maketic&~0xff) - 256 + low;
114 return (maketic&~0xff) + 256 + low;
116 I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic);
121 //============================================================================
132 void HSendPacket (int node, int flags)
134 netbuffer->checksum = NetbufferChecksum () | flags;
138 reboundstore = *netbuffer;
139 reboundpacket = true;
144 I_Error ("Tried to transmit to another node");
146 doomcom->command = CMD_SEND;
147 doomcom->remotenode = node;
148 doomcom->datalength = NetbufferSize ();
154 if (netbuffer->checksum & NCMD_RETRANSMIT)
155 realretrans = ExpandTics (netbuffer->retransmitfrom);
158 fprintf (debugfile,"send (%i + %i, R %i) [%i] "
159 ,ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
160 for (i=0 ; i<doomcom->datalength ; i++)
161 fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
162 fprintf (debugfile,"\n");
173 = Returns false if no packet is waiting
178 boolean HGetPacket (void)
182 *netbuffer = reboundstore;
183 doomcom->remotenode = 0;
184 reboundpacket = false;
191 doomcom->command = CMD_GET;
193 if (doomcom->remotenode == -1)
196 if (doomcom->datalength != NetbufferSize ())
199 fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
203 if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
206 fprintf (debugfile,"bad packet checksum\n");
215 if (netbuffer->checksum & NCMD_SETUP)
216 fprintf (debugfile,"setup packet\n");
219 if (netbuffer->checksum & NCMD_RETRANSMIT)
220 realretrans = ExpandTics (netbuffer->retransmitfrom);
223 fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",doomcom->remotenode,
224 ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
225 for (i=0 ; i<doomcom->datalength ; i++)
226 fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
227 fprintf (debugfile,"\n");
244 void GetPackets (void)
250 ticcmd_t *src, *dest;
251 int dupedstart, dupedend;
255 while (HGetPacket ())
257 if (netbuffer->checksum & NCMD_SETUP)
258 continue; // extra setup packet
260 netdrone = netbuffer->player & PL_DRONE;
261 netconsole = netbuffer->player & ~PL_DRONE;
262 netnode = doomcom->remotenode;
264 // to save bytes, only the low byte of tic numbers are sent
265 // Figure out what the rest of the bytes are
267 realstart = ExpandTics (netbuffer->starttic);
268 dupedstart = realstart*doomcom->ticdup;
269 dupedend = (realstart+netbuffer->numtics)*doomcom->ticdup;
272 // check for exiting the game
274 if (netbuffer->checksum & NCMD_EXIT)
276 if (!nodeingame[netnode])
278 nodeingame[netnode] = false;
281 playeringame[netconsole] = false;
282 strcpy (exitmsg, "Player 1 left the game");
283 exitmsg[7] += netconsole;
284 players[consoleplayer].message = exitmsg;
290 // drone packets are just notifications
294 nettics[netnode] = dupedend;
298 nodeforplayer[netconsole] = netnode;
301 // check for retransmit request
303 if ( resendcount[netnode] <= 0
304 && (netbuffer->checksum & NCMD_RETRANSMIT) )
306 resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
308 fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
309 resendcount[netnode] = RESENDCOUNT;
312 resendcount[netnode]--;
315 // check for out of order / duplicated packet
317 if (dupedend == nettics[netnode])
320 if (dupedend < nettics[netnode])
323 fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics);
328 // check for a missed packet
330 if (dupedstart > nettics[netnode])
332 // stop processing until the other system resends the missed tics
334 fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, dupedstart, nettics[netnode]);
335 remoteresend[netnode] = true;
340 // update command store from the packet
342 remoteresend[netnode] = false;
344 skiptics = nettics[netnode]/doomcom->ticdup - realstart;
345 src = &netbuffer->cmds[skiptics];
347 while (nettics[netnode] < dupedend)
349 for (j=0 ; j<doomcom->ticdup ; j++)
351 dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
355 if (src->buttons & BT_SPECIAL)
368 = Builds ticcmds for console player
373 void NetUpdate (void)
382 return; // singletic update is syncronous
387 nowtime = I_GetTime ()/doomcom->ticdup;
388 newtics = nowtime - gametime;
390 if (newtics <= 0) // nothing new to update
393 if (skiptics <= newtics)
405 netbuffer->player = consoleplayer;
407 netbuffer->player |= PL_DRONE;
420 // build new ticcmds for console player
422 gameticdiv = (gametic+doomcom->ticdup-1)/doomcom->ticdup;
423 for (i=0 ; i<newtics ; i++)
427 if (maketic - gameticdiv >= BACKUPTICS/2 /* /doomcom->ticdup */- 1)
430 break; // can't hold any more
432 //printf ("mk:%i ",maketic);
433 G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
438 // send the packet to the other nodes
441 for (i=0 ; i<doomcom->numnodes ; i++)
446 netbuffer->starttic = realstart = maketic + BACKUPTICS/2;
447 netbuffer->numtics = 0;
451 netbuffer->starttic = realstart = resendto[i];
452 netbuffer->numtics = maketic - realstart;
453 resendto[i] = maketic - doomcom->extratics;
456 if (netbuffer->numtics > BACKUPTICS)
457 I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
459 for (j=0 ; j< netbuffer->numtics ; j++)
461 localcmds[(realstart+j)%BACKUPTICS];
465 netbuffer->retransmitfrom = nettics[i]/doomcom->ticdup;
466 HSendPacket (i, NCMD_RETRANSMIT);
470 netbuffer->retransmitfrom = 0;
476 // listen for other packets
485 =====================
489 =====================
492 void CheckAbort (void)
499 for ( ; eventtail != eventhead
500 ; eventtail = (++eventtail)&(MAXEVENTS-1) )
502 ev = &events[eventtail];
503 if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
504 I_Error ("Network game synchronization aborted.");
509 =====================
511 = D_ArbitrateNetStart
513 =====================
516 void D_ArbitrateNetStart (void)
519 boolean gotinfo[MAXNETNODES];
522 memset (gotinfo,0,sizeof(gotinfo));
524 if (doomcom->consoleplayer)
525 { // listen for setup info from key player
526 printf ("listening for network start info...\n");
532 if (netbuffer->checksum & NCMD_SETUP)
534 if (netbuffer->player != VERSION)
535 I_Error ("Different DOOM versions cannot play a net game!");
536 startskill = netbuffer->retransmitfrom & 15;
537 deathmatch = (netbuffer->retransmitfrom & 0x80) > 0;
538 nomonsters = (netbuffer->retransmitfrom & 0x40) > 0;
539 respawnparm = (netbuffer->retransmitfrom & 0x20) > 0;
540 startmap = netbuffer->starttic & 15;
541 startepisode = netbuffer->starttic >> 4;
547 { // key player, send the setup info
548 printf ("sending network start info...\n");
552 for (i=0 ; i<doomcom->numnodes ; i++)
554 netbuffer->retransmitfrom = startskill;
556 netbuffer->retransmitfrom |= 0x80;
558 netbuffer->retransmitfrom |= 0x40;
560 netbuffer->retransmitfrom |= 0x20;
561 netbuffer->starttic = startepisode * 16 + startmap;
562 netbuffer->player = VERSION;
563 netbuffer->numtics = 0;
564 HSendPacket (i, NCMD_SETUP);
567 while (HGetPacket ())
569 gotinfo[netbuffer->player&0x7f] = true;
572 for (i=1 ; i<doomcom->numnodes ; i++)
575 } while (i < doomcom->numnodes);
584 = Works out player numbers among the net participants
588 extern int viewangleoffset;
590 void D_CheckNetGame (void)
594 for (i=0 ; i<MAXNETNODES ; i++)
596 nodeingame[i] = false;
598 remoteresend[i] = false; // set when local needs tics
599 resendto[i] = 0; // which tic to start sending
602 // I_InitNetwork sets doomcom and netgame
604 if (doomcom->id != DOOMCOM_ID)
605 I_Error ("Doomcom buffer invalid!");
606 netbuffer = &doomcom->data;
607 consoleplayer = displayplayer = doomcom->consoleplayer;
609 D_ArbitrateNetStart ();
610 printf ("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", startskill, deathmatch, startmap, startepisode);
612 // read values out of doomcom
613 ticdup = doomcom->ticdup;
615 for (i=0 ; i<doomcom->numplayers ; i++)
616 playeringame[i] = true;
617 for (i=0 ; i<doomcom->numnodes ; i++)
618 nodeingame[i] = true;
620 printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
629 = Called before quitting to leave a net game without hanging the
635 void D_QuitNetGame (void)
642 if (!netgame || !usergame || consoleplayer == -1)
645 // send a bunch of packets for security
646 netbuffer->player = consoleplayer;
648 netbuffer->player |= PL_DRONE;
649 netbuffer->numtics = 0;
650 for (i=0 ; i<4 ; i++)
652 for (j=1 ; j<doomcom->numnodes ; j++)
654 HSendPacket (j, NCMD_EXIT);
669 int frametics[4], frameon;
672 extern boolean advancedemo;
674 void TryRunTics (void)
677 int lowtic, nextlowest;
679 int static oldentertics;
680 int realtics, availabletics;
687 entertic = I_GetTime ();
688 realtics = entertic - oldentertics;
689 oldentertics = entertic;
692 // get available tics
696 lowtic = nextlowest = MAXINT;
698 for (i=0 ; i<doomcom->numnodes ; i++)
702 if (nettics[i] < lowtic)
707 else if (nettics[i] < nextlowest)
708 nextlowest = nettics[i];
710 availabletics = lowtic - gametic;
714 // decide how many tics to run
716 if (realtics < availabletics-1)
718 else if (realtics < availabletics)
721 counts = availabletics;
728 fprintf (debugfile,"=======real: %i avail: %i game: %i\n",realtics, availabletics,counts);
730 //=============================================================================
732 // ideally nettics[0] should be 1 - 3 tics above lowtic
733 // if we are consistantly slower, speed up time
734 // drones should never hold up the other players
736 for (i=0 ; i<MAXPLAYERS ; i++)
739 if (consoleplayer == i)
740 { // the key player does not adapt
744 if (nettics[0] <= nettics[nodeforplayer[i]])
749 frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
750 oldnettics = nettics[0];
751 if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
757 //=============================================================================
760 // wait for new tics if needed
762 while (lowtic < gametic + counts)
768 for (i=0 ; i<doomcom->numnodes ; i++)
769 if (nodeingame[i] && nettics[i] < lowtic)
772 if (lowtic < gametic)
773 I_Error ("TryRunTics: lowtic < gametic");
775 // don't stay in here forever -- give the menu a chance to work
776 if (I_GetTime () - entertic >= 20)
787 NetUpdate (); // check for new console commands