home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 28 / amigaformatcd28.iso / -screenplay- / otherstuff / adoomppc_src / d_net.c < prev    next >
C/C++ Source or Header  |  1998-04-23  |  18KB  |  812 lines

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