home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #2 / amigaacscoverdisc1998-021998.iso / games / doom / source / linuxdoom-1.10 / d_net.c < prev    next >
C/C++ Source or Header  |  1997-12-22  |  16KB  |  768 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.  
  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.     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)
  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.     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;
  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.     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.     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.