Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  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.  
  29. #include "m_menu.h"
  30. #include "i_system.h"
  31. #include "i_video.h"
  32. #include "i_net.h"
  33. #include "g_game.h"
  34. #include "doomdef.h"
  35. #include "doomstat.h"
  36.  
  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
  42.  
  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)&(((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. //      __libclog_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_NUM)
  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. //      __libclog_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_NUM;
  525.                 netbuffer->numtics = 0;
  526.                 HSendPacket (i, NCMD_SETUP);
  527.             }
  528.  
  529. #if 1
  530.             for(i = 10 ; i  &&  HGetPacket(); --i)
  531.             {
  532.                 if((netbuffer->player&0x7f) < MAXNETNODES)
  533.                     gotinfo[netbuffer->player&0x7f] = true;
  534.             }
  535. #else
  536.             while (HGetPacket ())
  537.             {
  538.                 gotinfo[netbuffer->player&0x7f] = true;
  539.             }
  540. #endif
  541.  
  542.             for (i=1 ; i<doomcom->numnodes ; i++)
  543.                 if (!gotinfo[i])
  544.                     break;
  545.         } while (i < doomcom->numnodes);
  546.     }
  547. }
  548.  
  549. //
  550. // D_CheckNetGame
  551. // Works out player numbers among the net participants
  552. //
  553. extern  int                     viewangleoffset;
  554.  
  555. void D_CheckNetGame (void)
  556. {
  557.     int             i;
  558.        
  559.     for (i=0 ; i<MAXNETNODES ; i++)
  560.     {
  561.         nodeingame[i] = false;
  562.         nettics[i] = 0;
  563.         remoteresend[i] = false;        // set when local needs tics
  564.         resendto[i] = 0;                // which tic to start sending
  565.     }
  566.        
  567.     // I_InitNetwork sets doomcom and netgame
  568.     I_InitNetwork ();
  569.     if (doomcom->id != DOOMCOM_ID)
  570.         I_Error ("Doomcom buffer invalid!");
  571.    
  572.     netbuffer = &doomcom->data;
  573.     consoleplayer = displayplayer = doomcom->consoleplayer;
  574.     if (netgame)
  575.         D_ArbitrateNetStart ();
  576.  
  577.  //   __libclog_printf ("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n",
  578. //          startskill, deathmatch, startmap, startepisode);
  579.        
  580.     // read values out of doomcom
  581.     ticdup = doomcom->ticdup;
  582.     maxsend = BACKUPTICS/(2*ticdup)-1;
  583.     if (maxsend<1)
  584.         maxsend = 1;
  585.                        
  586.     for (i=0 ; i<doomcom->numplayers ; i++)
  587.         playeringame[i] = true;
  588.     for (i=0 ; i<doomcom->numnodes ; i++)
  589.         nodeingame[i] = true;
  590.        
  591. //    __libclog_printf ("player %i of %i (%i nodes)\n",
  592. //          consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
  593.  
  594. }
  595.  
  596.  
  597. //
  598. // D_QuitNetGame
  599. // Called before quitting to leave a net game
  600. // without hanging the other players
  601. //
  602. void D_QuitNetGame (void)
  603. {
  604.     int             i, j;
  605.        
  606.     if (debugfile)
  607.         fclose (debugfile);
  608.                
  609.     if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
  610.         return;
  611.        
  612.     // send a bunch of packets for security
  613.     netbuffer->player = consoleplayer;
  614.     netbuffer->numtics = 0;
  615.     for (i=0 ; i<4 ; i++)
  616.     {
  617.         for (j=1 ; j<doomcom->numnodes ; j++)
  618.             if (nodeingame[j])
  619.                 HSendPacket (j, NCMD_EXIT);
  620.         I_WaitVBL (1);
  621.     }
  622. }
  623.  
  624.  
  625.  
  626. //
  627. // TryRunTics
  628. //
  629. int     frametics[4];
  630. int     frameon;
  631. int     frameskip[4];
  632. int     oldnettics;
  633.  
  634. extern  boolean advancedemo;
  635.  
  636. void TryRunTics (void)
  637. {
  638.     int         i;
  639.     int         lowtic;
  640.     int         entertic;
  641.     static int  oldentertics;
  642.     int         realtics;
  643.     int         availabletics;
  644.     int         counts;
  645.     int         numplaying;
  646.    
  647.     // get real tics           
  648.     entertic = I_GetTime ()/ticdup;
  649.     realtics = entertic - oldentertics;
  650.     oldentertics = entertic;
  651.    
  652.     // get available tics
  653.     NetUpdate ();
  654.        
  655.     lowtic = MAXINT;
  656.     numplaying = 0;
  657.     for (i=0 ; i<doomcom->numnodes ; i++)
  658.     {
  659.         if (nodeingame[i])
  660.         {
  661.             numplaying++;
  662.             if (nettics[i] < lowtic)
  663.                 lowtic = nettics[i];
  664.         }
  665.     }
  666.     availabletics = lowtic - gametic/ticdup;
  667.    
  668.     // decide how many tics to run
  669.     if (realtics < availabletics-1)
  670.         counts = realtics+1;
  671.     else if (realtics < availabletics)
  672.         counts = realtics;
  673.     else
  674.         counts = availabletics;
  675.    
  676.     if (counts < 1)
  677.         counts = 1;
  678.                
  679.     frameon++;
  680.  
  681.     if (debugfile)
  682.         fprintf (debugfile,
  683.                  "=======real: %i  avail: %i  game: %i\n",
  684.                  realtics, availabletics,counts);
  685.  
  686.     if (!demoplayback)
  687.     {  
  688.         // ideally nettics[0] should be 1 - 3 tics above lowtic
  689.         // if we are consistantly slower, speed up time
  690.         for (i=0 ; i<MAXPLAYERS ; i++)
  691.             if (playeringame[i])
  692.                 break;
  693.         if (consoleplayer == i)
  694.         {
  695.             // the key player does not adapt
  696.         }
  697.         else
  698.         {
  699.             if (nettics[0] <= nettics[nodeforplayer[i]])
  700.             {
  701.                 gametime--;
  702.                 // printf ("-");
  703.             }
  704.             frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
  705.             oldnettics = nettics[0];
  706.             if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
  707.             {
  708.                 skiptics = 1;
  709.                 // printf ("+");
  710.             }
  711.         }
  712.     }// demoplayback
  713.        
  714.     // wait for new tics if needed
  715.     while (lowtic < gametic/ticdup + counts)   
  716.     {
  717.         NetUpdate ();  
  718.         lowtic = MAXINT;
  719.        
  720.         for (i=0 ; i<doomcom->numnodes ; i++)
  721.             if (nodeingame[i] && nettics[i] < lowtic)
  722.                 lowtic = nettics[i];
  723.        
  724.         if (lowtic < gametic/ticdup)
  725.             I_Error ("TryRunTics: lowtic < gametic");
  726.                                
  727.         // don't stay in here forever -- give the menu a chance to work
  728.         if (I_GetTime ()/ticdup - entertic >= 20)
  729.         {
  730.             M_Ticker ();
  731.             return;
  732.         }
  733.     }
  734.    
  735.     // run the count * ticdup dics
  736.     while (counts--)
  737.     {
  738.         for (i=0 ; i<ticdup ; i++)
  739.         {
  740.             if (gametic/ticdup > lowtic)
  741.                 I_Error ("gametic>lowtic");
  742.             if (advancedemo)
  743.                 D_DoAdvanceDemo ();
  744.             M_Ticker ();
  745.             G_Ticker ();
  746.             gametic++;
  747.            
  748.             // modify command for duplicated tics
  749.             if (i != ticdup-1)
  750.             {
  751.                 ticcmd_t        *cmd;
  752.                 int                     buf;
  753.                 int                     j;
  754.                                
  755.                 buf = (gametic/ticdup)%BACKUPTICS;
  756.                 for (j=0 ; j<MAXPLAYERS ; j++)
  757.                 {
  758.                     cmd = &netcmds[j][buf];
  759.                     cmd->chatchar = 0;
  760.                     if (cmd->buttons & BT_SPECIAL)
  761.                         cmd->buttons = 0;
  762.                 }
  763.             }
  764.         }
  765.         NetUpdate ();   // check for new console commands
  766.     }
  767. }
  768.