Subversion Repositories Kolibri OS

Rev

Rev 298 | 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 HSendPacket (int node,int flags )
  143. {
  144.     netbuffer->checksum = NetbufferChecksum () | flags;
  145.  
  146.     if (!node)
  147.     {
  148.         reboundstore = *netbuffer;
  149.         reboundpacket = true;
  150.         return;
  151.     }
  152.  
  153.     if (demoplayback)
  154.         return;
  155.  
  156.     if (!netgame)
  157.         I_Error ("Tried to transmit to another node");
  158.                
  159.     doomcom->command = CMD_SEND;
  160.     doomcom->remotenode = node;
  161.     doomcom->datalength = NetbufferSize ();
  162.        
  163.     if (debugfile)
  164.     {
  165.         int             i;
  166.         int             realretrans;
  167.         if (netbuffer->checksum & NCMD_RETRANSMIT)
  168.             realretrans = ExpandTics (netbuffer->retransmitfrom);
  169.         else
  170.             realretrans = -1;
  171.  
  172.         printf ("send (%i + %i, R %i) [%i] ",
  173.                  ExpandTics(netbuffer->starttic),
  174.                  netbuffer->numtics, realretrans, doomcom->datalength);
  175.        
  176.         for (i=0 ; i<doomcom->datalength ; i++)
  177.             printf ("%i ",((byte *)netbuffer)[i]);
  178.  
  179.         printf ("\n");
  180.     }
  181.  
  182.     I_NetCmd ();
  183. }
  184.  
  185. //
  186. // HGetPacket
  187. // Returns false if no packet is waiting
  188. //
  189. boolean HGetPacket (void)
  190. {      
  191.     if (reboundpacket)
  192.     {
  193.         *netbuffer = reboundstore;
  194.         doomcom->remotenode = 0;
  195.         reboundpacket = false;
  196.         return true;
  197.     }
  198.  
  199.     if (!netgame)
  200.         return false;
  201.  
  202.     if (demoplayback)
  203.         return false;
  204.                
  205.     doomcom->command = CMD_GET;
  206.     I_NetCmd ();
  207.    
  208.     if (doomcom->remotenode == -1)
  209.         return false;
  210.  
  211.     if (doomcom->datalength != NetbufferSize ())
  212.     {
  213.         if (debugfile)
  214.             printf ("bad packet length %i\n",doomcom->datalength);
  215.         return false;
  216.     }
  217.        
  218.     if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
  219.     {
  220.         if (debugfile)
  221.             printf ("bad packet checksum\n");
  222.         return false;
  223.     }
  224.  
  225.     if (debugfile)
  226.     {
  227.         int             realretrans;
  228.         int     i;
  229.                        
  230.         if (netbuffer->checksum & NCMD_SETUP)
  231.             printf ("setup packet\n");
  232.         else
  233.         {
  234.             if (netbuffer->checksum & NCMD_RETRANSMIT)
  235.                 realretrans = ExpandTics (netbuffer->retransmitfrom);
  236.             else
  237.                 realretrans = -1;
  238.            
  239.             printf ("get %i = (%i + %i, R %i)[%i] ",
  240.                      doomcom->remotenode,
  241.                      ExpandTics(netbuffer->starttic),
  242.                      netbuffer->numtics, realretrans, doomcom->datalength);
  243.  
  244.             for (i=0 ; i<doomcom->datalength ; i++)
  245.                 printf ("%i ",((byte *)netbuffer)[i]);
  246.             printf ("\n");
  247.         }
  248.     }
  249.     return true;       
  250. }
  251.  
  252.  
  253. //
  254. // GetPackets
  255. //
  256. char    exitmsg[80];
  257.  
  258. void GetPackets (void)
  259. {
  260.     int         netconsole;
  261.     int         netnode;
  262.     ticcmd_t    *src, *dest;
  263.     int         realend;
  264.     int         realstart;
  265.                                  
  266.     while ( HGetPacket() )
  267.     {
  268.         if (netbuffer->checksum & NCMD_SETUP)
  269.             continue;           // extra setup packet
  270.                        
  271.         netconsole = netbuffer->player & ~PL_DRONE;
  272.         netnode = doomcom->remotenode;
  273.        
  274.         // to save bytes, only the low byte of tic numbers are sent
  275.         // Figure out what the rest of the bytes are
  276.         realstart = ExpandTics (netbuffer->starttic);          
  277.         realend = (realstart+netbuffer->numtics);
  278.        
  279.         // check for exiting the game
  280.         if (netbuffer->checksum & NCMD_EXIT)
  281.         {
  282.             if (!nodeingame[netnode])
  283.                 continue;
  284.             nodeingame[netnode] = false;
  285.             playeringame[netconsole] = false;
  286.             strcpy (exitmsg, "Player 1 left the game");
  287.             exitmsg[7] += netconsole;
  288.             players[consoleplayer].message = exitmsg;
  289.             if (demorecording)
  290.                 G_CheckDemoStatus ();
  291.             continue;
  292.         }
  293.        
  294.         // check for a remote game kill
  295.         if (netbuffer->checksum & NCMD_KILL)
  296.             I_Error ("Killed by network driver");
  297.  
  298.         nodeforplayer[netconsole] = netnode;
  299.        
  300.         // check for retransmit request
  301.         if ( resendcount[netnode] <= 0
  302.              && (netbuffer->checksum & NCMD_RETRANSMIT) )
  303.         {
  304.             resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
  305.             if (debugfile)
  306.                 printf ("retransmit from %i\n", resendto[netnode]);
  307.             resendcount[netnode] = RESENDCOUNT;
  308.         }
  309.         else
  310.             resendcount[netnode]--;
  311.        
  312.         // check for out of order / duplicated packet          
  313.         if (realend == nettics[netnode])
  314.             continue;
  315.                        
  316.         if (realend < nettics[netnode])
  317.         {
  318.             if (debugfile)
  319.                 printf ("out of order packet (%i + %i)\n" ,
  320.                          realstart,netbuffer->numtics);
  321.             continue;
  322.         }
  323.        
  324.         // check for a missed packet
  325.         if (realstart > nettics[netnode])
  326.         {
  327.             // stop processing until the other system resends the missed tics
  328.             if (debugfile)
  329.                 printf ("missed tics from %i (%i - %i)\n",
  330.                          netnode, realstart, nettics[netnode]);
  331.             remoteresend[netnode] = true;
  332.             continue;
  333.         }
  334.  
  335.         // update command store from the packet
  336.         {
  337.             int         start;
  338.  
  339.             remoteresend[netnode] = false;
  340.                
  341.             start = nettics[netnode] - realstart;              
  342.             src = &netbuffer->cmds[start];
  343.  
  344.             while (nettics[netnode] < realend)
  345.             {
  346.                 dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
  347.                 nettics[netnode]++;
  348.                 *dest = *src;
  349.                 src++;
  350.             }
  351.         }
  352.     }
  353. }
  354.  
  355.  
  356. //
  357. // NetUpdate
  358. // Builds ticcmds for console player,
  359. // sends out a packet
  360. //
  361. int      gametime;
  362.  
  363. void NetUpdate (void)
  364. {
  365.     int             nowtime;
  366.     int             newtics;
  367.     int                         i,j;
  368.     int                         realstart;
  369.     int                         gameticdiv;
  370.    
  371.     // check time
  372.     nowtime = I_GetTime ()/ticdup;
  373.     newtics = nowtime - gametime;
  374.     gametime = nowtime;
  375.        
  376.     if (newtics <= 0)   // nothing new to update
  377.         goto listen;
  378.  
  379.     if (skiptics <= newtics)
  380.     {
  381.         newtics -= skiptics;
  382.         skiptics = 0;
  383.     }
  384.     else
  385.     {
  386.         skiptics -= newtics;
  387.         newtics = 0;
  388.     }
  389.        
  390.                
  391.     netbuffer->player = consoleplayer;
  392.    
  393.     // build new ticcmds for console player
  394.     gameticdiv = gametic/ticdup;
  395.     for (i=0 ; i<newtics ; i++)
  396.     {
  397.         I_StartTic ();
  398.         D_ProcessEvents ();
  399.         if (maketic - gameticdiv >= BACKUPTICS/2-1)
  400.             break;          // can't hold any more
  401.        
  402.         //printf ("mk:%i ",maketic);
  403.         G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
  404.         maketic++;
  405.     }
  406.  
  407.  
  408.     if (singletics)
  409.         return;         // singletic update is syncronous
  410.    
  411.     // send the packet to the other nodes
  412.     for (i=0 ; i<doomcom->numnodes ; i++)
  413.         if (nodeingame[i])
  414.         {
  415.             netbuffer->starttic = realstart = resendto[i];
  416.             netbuffer->numtics = maketic - realstart;
  417.             if (netbuffer->numtics > BACKUPTICS)
  418.                 I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
  419.  
  420.             resendto[i] = maketic - doomcom->extratics;
  421.  
  422.             for (j=0 ; j< netbuffer->numtics ; j++)
  423.                 netbuffer->cmds[j] =
  424.                     localcmds[(realstart+j)%BACKUPTICS];
  425.                                        
  426.             if (remoteresend[i])
  427.             {
  428.                 netbuffer->retransmitfrom = nettics[i];
  429.                 HSendPacket (i, NCMD_RETRANSMIT);
  430.             }
  431.             else
  432.             {
  433.                 netbuffer->retransmitfrom = 0;
  434.                 HSendPacket (i, 0);
  435.             }
  436.         }
  437.    
  438.     // listen for other packets
  439.   listen:
  440.     GetPackets ();
  441. }
  442.  
  443.  
  444.  
  445. //
  446. // CheckAbort
  447. //
  448. void CheckAbort (void)
  449. {
  450.     event_t *ev;
  451.     int         stoptic;
  452.        
  453.     stoptic = I_GetTime () + 2;
  454.     while (I_GetTime() < stoptic)
  455.         I_StartTic ();
  456.        
  457.     I_StartTic ();
  458.     for ( ; eventtail != eventhead
  459.               ; eventtail = (++eventtail)&(MAXEVENTS-1) )
  460.     {
  461.         ev = &events[eventtail];
  462.         if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
  463.             I_Error ("Network game synchronization aborted.");
  464.     }
  465. }
  466.  
  467.  
  468. //
  469. // D_ArbitrateNetStart
  470. //
  471. void D_ArbitrateNetStart (void)
  472. {
  473.     int         i;
  474.     boolean     gotinfo[MAXNETNODES];
  475.        
  476.     autostart = true;
  477.     memset (gotinfo,0,sizeof(gotinfo));
  478.        
  479.     if (doomcom->consoleplayer)
  480.     {
  481.         // listen for setup info from key player
  482. //      __libclog_printf ("listening for network start info...\n");
  483.         while (1)
  484.         {
  485.             CheckAbort ();
  486.             if (!HGetPacket ())
  487.                 continue;
  488.             if (netbuffer->checksum & NCMD_SETUP)
  489.             {
  490.                 if (netbuffer->player != VERSION_NUM)
  491.                     I_Error ("Different DOOM versions cannot play a net game!");
  492.                 startskill = netbuffer->retransmitfrom & 15;
  493.                 deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6;
  494.                 nomonsters = (netbuffer->retransmitfrom & 0x20) > 0;
  495.                 respawnparm = (netbuffer->retransmitfrom & 0x10) > 0;
  496.                 startmap = netbuffer->starttic & 0x3f;
  497.                 startepisode = netbuffer->starttic >> 6;
  498.                 return;
  499.             }
  500.         }
  501.     }
  502.     else
  503.     {
  504.         // key player, send the setup info
  505. //      __libclog_printf ("sending network start info...\n");
  506.         do
  507.         {
  508.             CheckAbort ();
  509.             for (i=0 ; i<doomcom->numnodes ; i++)
  510.             {
  511.                 netbuffer->retransmitfrom = startskill;
  512.                 if (deathmatch)
  513.                     netbuffer->retransmitfrom |= (deathmatch<<6);
  514.                 if (nomonsters)
  515.                     netbuffer->retransmitfrom |= 0x20;
  516.                 if (respawnparm)
  517.                     netbuffer->retransmitfrom |= 0x10;
  518.                 netbuffer->starttic = startepisode * 64 + startmap;
  519.                 netbuffer->player = VERSION_NUM;
  520.                 netbuffer->numtics = 0;
  521.                 HSendPacket (i, NCMD_SETUP);
  522.             }
  523.  
  524. #if 1
  525.             for(i = 10 ; i  &&  HGetPacket(); --i)
  526.             {
  527.                 if((netbuffer->player&0x7f) < MAXNETNODES)
  528.                     gotinfo[netbuffer->player&0x7f] = true;
  529.             }
  530. #else
  531.             while (HGetPacket ())
  532.             {
  533.                 gotinfo[netbuffer->player&0x7f] = true;
  534.             }
  535. #endif
  536.  
  537.             for (i=1 ; i<doomcom->numnodes ; i++)
  538.                 if (!gotinfo[i])
  539.                     break;
  540.         } while (i < doomcom->numnodes);
  541.     }
  542. }
  543.  
  544. //
  545. // D_CheckNetGame
  546. // Works out player numbers among the net participants
  547. //
  548. extern  int                     viewangleoffset;
  549.  
  550. void D_CheckNetGame (void)
  551. {
  552.     int             i;
  553.        
  554.     for (i=0 ; i<MAXNETNODES ; i++)
  555.     {
  556.         nodeingame[i] = false;
  557.         nettics[i] = 0;
  558.         remoteresend[i] = false;        // set when local needs tics
  559.         resendto[i] = 0;                // which tic to start sending
  560.     }
  561.        
  562.     // I_InitNetwork sets doomcom and netgame
  563.     I_InitNetwork ();
  564.     if (doomcom->id != DOOMCOM_ID)
  565.         I_Error ("Doomcom buffer invalid!");
  566.    
  567.     netbuffer = &doomcom->data;
  568.     consoleplayer = displayplayer = doomcom->consoleplayer;
  569.     if (netgame)
  570.         D_ArbitrateNetStart ();
  571.  
  572.  //   __libclog_printf ("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n",
  573. //          startskill, deathmatch, startmap, startepisode);
  574.        
  575.     // read values out of doomcom
  576.     ticdup = doomcom->ticdup;
  577.     maxsend = BACKUPTICS/(2*ticdup)-1;
  578.     if (maxsend<1)
  579.         maxsend = 1;
  580.                        
  581.     for (i=0 ; i<doomcom->numplayers ; i++)
  582.         playeringame[i] = true;
  583.     for (i=0 ; i<doomcom->numnodes ; i++)
  584.         nodeingame[i] = true;
  585.        
  586. //    __libclog_printf ("player %i of %i (%i nodes)\n",
  587. //          consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
  588.  
  589. }
  590.  
  591.  
  592. //
  593. // D_QuitNetGame
  594. // Called before quitting to leave a net game
  595. // without hanging the other players
  596. //
  597. void D_QuitNetGame (void)
  598. {
  599.     int             i, j;
  600.        
  601.     if (debugfile)
  602.         fclose (debugfile);
  603.                
  604.     if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
  605.         return;
  606.        
  607.     // send a bunch of packets for security
  608.     netbuffer->player = consoleplayer;
  609.     netbuffer->numtics = 0;
  610.     for (i=0 ; i<4 ; i++)
  611.     {
  612.         for (j=1 ; j<doomcom->numnodes ; j++)
  613.             if (nodeingame[j])
  614.                 HSendPacket (j, NCMD_EXIT);
  615.         I_WaitVBL (1);
  616.     }
  617. }
  618.  
  619.  
  620.  
  621. //
  622. // TryRunTics
  623. //
  624. int     frametics[4];
  625. int     frameon;
  626. int     frameskip[4];
  627. int     oldnettics;
  628.  
  629. extern  boolean advancedemo;
  630.  
  631. void TryRunTics (void)
  632. {
  633.     int         i;
  634.     int         lowtic;
  635.     int         entertic;
  636.     static int  oldentertics;
  637.     int         realtics;
  638.     int         availabletics;
  639.     int         counts;
  640.     int         numplaying;
  641.    
  642.     // get real tics           
  643.     entertic = I_GetTime ()/ticdup;
  644.     realtics = entertic - oldentertics;
  645.     oldentertics = entertic;
  646.    
  647.     // get available tics
  648.     NetUpdate ();
  649.        
  650.     lowtic = MAXINT;
  651.     numplaying = 0;
  652.     for (i=0 ; i<doomcom->numnodes ; i++)
  653.     {
  654.         if (nodeingame[i])
  655.         {
  656.             numplaying++;
  657.             if (nettics[i] < lowtic)
  658.                 lowtic = nettics[i];
  659.         }
  660.     }
  661.     availabletics = lowtic - gametic/ticdup;
  662.    
  663.     // decide how many tics to run
  664.     if (realtics < availabletics-1)
  665.         counts = realtics+1;
  666.     else if (realtics < availabletics)
  667.         counts = realtics;
  668.     else
  669.         counts = availabletics;
  670.    
  671.     if (counts < 1)
  672.         counts = 1;
  673.                
  674.     frameon++;
  675.  
  676.     if (debugfile)
  677.         fprintf (debugfile,
  678.                  "=======real: %i  avail: %i  game: %i\n",
  679.                  realtics, availabletics,counts);
  680.  
  681.     if (!demoplayback)
  682.     {  
  683.         // ideally nettics[0] should be 1 - 3 tics above lowtic
  684.         // if we are consistantly slower, speed up time
  685.         for (i=0 ; i<MAXPLAYERS ; i++)
  686.             if (playeringame[i])
  687.                 break;
  688.         if (consoleplayer == i)
  689.         {
  690.             // the key player does not adapt
  691.         }
  692.         else
  693.         {
  694.             if (nettics[0] <= nettics[nodeforplayer[i]])
  695.             {
  696.                 gametime--;
  697.                 // printf ("-");
  698.             }
  699.             frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
  700.             oldnettics = nettics[0];
  701.             if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
  702.             {
  703.                 skiptics = 1;
  704.                 // printf ("+");
  705.             }
  706.         }
  707.     }// demoplayback
  708.        
  709.     // wait for new tics if needed
  710.     while (lowtic < gametic/ticdup + counts)   
  711.     {
  712.         NetUpdate ();  
  713.         lowtic = MAXINT;
  714.        
  715.         for (i=0 ; i<doomcom->numnodes ; i++)
  716.             if (nodeingame[i] && nettics[i] < lowtic)
  717.                 lowtic = nettics[i];
  718.        
  719.         if (lowtic < gametic/ticdup)
  720.             I_Error ("TryRunTics: lowtic < gametic");
  721.                                
  722.         // don't stay in here forever -- give the menu a chance to work
  723.         if (I_GetTime ()/ticdup - entertic >= 20)
  724.         {
  725.             M_Ticker ();
  726.             return;
  727.         }
  728.     }
  729.    
  730.     // run the count * ticdup dics
  731.     while (counts--)
  732.     {
  733.         for (i=0 ; i<ticdup ; i++)
  734.         {
  735.             if (gametic/ticdup > lowtic)
  736.                 I_Error ("gametic>lowtic");
  737.             if (advancedemo)
  738.                 D_DoAdvanceDemo ();
  739.             M_Ticker ();
  740.             G_Ticker ();
  741.             gametic++;
  742.            
  743.             // modify command for duplicated tics
  744.             if (i != ticdup-1)
  745.             {
  746.                 ticcmd_t        *cmd;
  747.                 int                     buf;
  748.                 int                     j;
  749.                                
  750.                 buf = (gametic/ticdup)%BACKUPTICS;
  751.                 for (j=0 ; j<MAXPLAYERS ; j++)
  752.                 {
  753.                     cmd = &netcmds[j][buf];
  754.                     cmd->chatchar = 0;
  755.                     if (cmd->buttons & BT_SPECIAL)
  756.                         cmd->buttons = 0;
  757.                 }
  758.             }
  759.         }
  760.         NetUpdate ();   // check for new console commands
  761.     }
  762. }
  763.