d_net.c
上传用户:xuyinpeng
上传日期:2021-05-12
资源大小:455k
文件大小:19k
源码类别:

射击游戏

开发平台:

Visual C++

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