DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

d_net.cpp (19892B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #include "Precompiled.h"
     30 #include "globaldata.h"
     31 
     32 
     33 #include "m_menu.h"
     34 #include "i_system.h"
     35 #include "i_video.h"
     36 #include "i_net.h"
     37 #include "g_game.h"
     38 #include "doomdef.h"
     39 #include "doomstat.h"
     40 
     41 #include "doomlib.h"
     42 #include "Main.h"
     43 #include "d3xp/Game_local.h"
     44 
     45 
     46 void I_GetEvents( controller_t * );
     47 void D_ProcessEvents (void); 
     48 void G_BuildTiccmd (ticcmd_t *cmd, idUserCmdMgr *, int newTics ); 
     49 void D_DoAdvanceDemo (void);
     50 
     51 extern bool globalNetworking;
     52 
     53 //
     54 // NETWORKING
     55 //
     56 // ::g->gametic is the tic about to (or currently being) run
     57 // ::g->maketic is the tick that hasn't had control made for it yet
     58 // ::g->nettics[] has the maketics for all ::g->players 
     59 //
     60 // a ::g->gametic cannot be run until ::g->nettics[] > ::g->gametic for all ::g->players
     61 //
     62 
     63 
     64 
     65 
     66 #define NET_TIMEOUT	1 * TICRATE
     67 
     68 
     69 
     70 
     71 
     72 //
     73 //
     74 //
     75 int NetbufferSize (void)
     76 {
     77 	int size = (int)&(((doomdata_t *)0)->cmds[::g->netbuffer->numtics]);
     78 
     79 	return size;
     80 }
     81 
     82 //
     83 // Checksum 
     84 //
     85 unsigned NetbufferChecksum (void)
     86 {
     87 	unsigned		c;
     88 	int		i,l;
     89 
     90 	c = 0x1234567;
     91 
     92 	if ( globalNetworking ) {
     93 		l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
     94 		for (i=0 ; i<l ; i++)
     95 			c += ((unsigned *)&::g->netbuffer->retransmitfrom)[i] * (i+1);
     96 	}
     97 
     98 	return c & NCMD_CHECKSUM;
     99 }
    100 
    101 //
    102 //
    103 //
    104 int ExpandTics (int low)
    105 {
    106 	int	delta;
    107 
    108 	delta = low - (::g->maketic&0xff);
    109 
    110 	if (delta >= -64 && delta <= 64)
    111 		return (::g->maketic&~0xff) + low;
    112 	if (delta > 64)
    113 		return (::g->maketic&~0xff) - 256 + low;
    114 	if (delta < -64)
    115 		return (::g->maketic&~0xff) + 256 + low;
    116 
    117 	I_Error ("ExpandTics: strange value %i at ::g->maketic %i",low,::g->maketic);
    118 	return 0;
    119 }
    120 
    121 
    122 
    123 //
    124 // HSendPacket
    125 //
    126 void
    127 HSendPacket
    128 (int	node,
    129  int	flags )
    130 {
    131 	::g->netbuffer->checksum = NetbufferChecksum () | flags;
    132 
    133 	if (!node)
    134 	{
    135 		::g->reboundstore = *::g->netbuffer;
    136 		::g->reboundpacket = true;
    137 		return;
    138 	}
    139 
    140 	if (::g->demoplayback)
    141 		return;
    142 
    143 	if (!::g->netgame)
    144 		I_Error ("Tried to transmit to another node");
    145 
    146 	::g->doomcom.command = CMD_SEND;
    147 	::g->doomcom.remotenode = node;
    148 	::g->doomcom.datalength = NetbufferSize ();
    149 
    150 	if (::g->debugfile)
    151 	{
    152 		int		i;
    153 		int		realretrans;
    154 		if (::g->netbuffer->checksum & NCMD_RETRANSMIT)
    155 			realretrans = ExpandTics (::g->netbuffer->retransmitfrom);
    156 		else
    157 			realretrans = -1;
    158 
    159 		fprintf (::g->debugfile,"send (%i + %i, R %i) [%i] ",
    160 			ExpandTics(::g->netbuffer->starttic),
    161 			::g->netbuffer->numtics, realretrans, ::g->doomcom.datalength);
    162 
    163 		for (i=0 ; i < ::g->doomcom.datalength ; i++)
    164 			fprintf (::g->debugfile,"%i ",((byte *)::g->netbuffer)[i]);
    165 
    166 		fprintf (::g->debugfile,"\n");
    167 	}
    168 
    169 	I_NetCmd ();
    170 }
    171 
    172 //
    173 // HGetPacket
    174 // Returns false if no packet is waiting
    175 //
    176 qboolean HGetPacket (void)
    177 {	
    178 	if (::g->reboundpacket)
    179 	{
    180 		*::g->netbuffer = ::g->reboundstore;
    181 		::g->doomcom.remotenode = 0;
    182 		::g->reboundpacket = false;
    183 		return true;
    184 	}
    185 
    186 	if (!::g->netgame)
    187 		return false;
    188 
    189 	if (::g->demoplayback)
    190 		return false;
    191 
    192 	::g->doomcom.command = CMD_GET;
    193 	I_NetCmd ();
    194 
    195 	if (::g->doomcom.remotenode == -1)
    196 		return false;
    197 
    198 	if (::g->doomcom.datalength != NetbufferSize ())
    199 	{
    200 		if (::g->debugfile)
    201 			fprintf (::g->debugfile,"bad packet length %i\n",::g->doomcom.datalength);
    202 		return false;
    203 	}
    204 
    205 	// ALAN NETWORKING -- this fails a lot on 4 player split debug!!
    206 	// TODO: Networking
    207 #ifdef ID_ENABLE_DOOM_CLASSIC_NETWORKING
    208 	if ( !gameLocal->IsSplitscreen() && NetbufferChecksum() != (::g->netbuffer->checksum&NCMD_CHECKSUM) )
    209 	{
    210 		if (::g->debugfile) {
    211 			fprintf (::g->debugfile,"bad packet checksum\n");
    212 		}
    213 
    214 		return false;
    215 	}
    216 #endif
    217 
    218 	if (::g->debugfile)
    219 	{
    220 		int		realretrans;
    221 		int	i;
    222 
    223 		if (::g->netbuffer->checksum & NCMD_SETUP)
    224 			fprintf (::g->debugfile,"setup packet\n");
    225 		else
    226 		{
    227 			if (::g->netbuffer->checksum & NCMD_RETRANSMIT)
    228 				realretrans = ExpandTics (::g->netbuffer->retransmitfrom);
    229 			else
    230 				realretrans = -1;
    231 
    232 			fprintf (::g->debugfile,"get %i = (%i + %i, R %i)[%i] ",
    233 				::g->doomcom.remotenode,
    234 				ExpandTics(::g->netbuffer->starttic),
    235 				::g->netbuffer->numtics, realretrans, ::g->doomcom.datalength);
    236 
    237 			for (i=0 ; i < ::g->doomcom.datalength ; i++)
    238 				fprintf (::g->debugfile,"%i ",((byte *)::g->netbuffer)[i]);
    239 			fprintf (::g->debugfile,"\n");
    240 		}
    241 	}
    242 	return true;	
    243 }
    244 
    245 
    246 //
    247 // GetPackets
    248 //
    249 
    250 void GetPackets (void)
    251 {
    252 	int		netconsole;
    253 	int		netnode;
    254 	ticcmd_t	*src, *dest;
    255 	int		realend;
    256 	int		realstart;
    257 
    258 	while ( HGetPacket() )
    259 	{
    260 		if (::g->netbuffer->checksum & NCMD_SETUP)
    261 			continue;		// extra setup packet
    262 
    263 		netconsole = ::g->netbuffer->player & ~PL_DRONE;
    264 		netnode = ::g->doomcom.remotenode;
    265 
    266 		// to save bytes, only the low byte of tic numbers are sent
    267 		// Figure out what the rest of the bytes are
    268 		realstart = ExpandTics (::g->netbuffer->starttic);		
    269 		realend = (realstart+::g->netbuffer->numtics);
    270 
    271 		// check for exiting the game
    272 		if (::g->netbuffer->checksum & NCMD_EXIT)
    273 		{
    274 			if (!::g->nodeingame[netnode])
    275 				continue;
    276 			::g->nodeingame[netnode] = false;
    277 			::g->playeringame[netconsole] = false;
    278 			strcpy (::g->exitmsg, "Player 1 left the game");
    279 			::g->exitmsg[7] += netconsole;
    280 			::g->players[::g->consoleplayer].message = ::g->exitmsg;
    281 
    282 			if( ::g->demorecording ) {
    283 				G_CheckDemoStatus();
    284 			}
    285 			continue;
    286 		}
    287 
    288 		// check for a remote game kill
    289 /*
    290 		if (::g->netbuffer->checksum & NCMD_KILL)
    291 			I_Error ("Killed by network driver");
    292 */
    293 
    294 		::g->nodeforplayer[netconsole] = netnode;
    295 
    296 		// check for retransmit request
    297 		if ( ::g->resendcount[netnode] <= 0 
    298 			&& (::g->netbuffer->checksum & NCMD_RETRANSMIT) )
    299 		{
    300 			::g->resendto[netnode] = ExpandTics(::g->netbuffer->retransmitfrom);
    301 			if (::g->debugfile)
    302 				fprintf (::g->debugfile,"retransmit from %i\n", ::g->resendto[netnode]);
    303 			::g->resendcount[netnode] = RESENDCOUNT;
    304 		}
    305 		else
    306 			::g->resendcount[netnode]--;
    307 
    308 		// check for out of order / duplicated packet		
    309 		if (realend == ::g->nettics[netnode])
    310 			continue;
    311 
    312 		if (realend < ::g->nettics[netnode])
    313 		{
    314 			if (::g->debugfile)
    315 				fprintf (::g->debugfile,
    316 				"out of order packet (%i + %i)\n" ,
    317 				realstart,::g->netbuffer->numtics);
    318 			continue;
    319 		}
    320 
    321 		// check for a missed packet
    322 		if (realstart > ::g->nettics[netnode])
    323 		{
    324 			// stop processing until the other system resends the missed tics
    325 			if (::g->debugfile)
    326 				fprintf (::g->debugfile,
    327 				"missed tics from %i (%i - %i)\n",
    328 				netnode, realstart, ::g->nettics[netnode]);
    329 			::g->remoteresend[netnode] = true;
    330 			continue;
    331 		}
    332 
    333 		// update command store from the packet
    334 		{
    335 			int		start;
    336 
    337 			::g->remoteresend[netnode] = false;
    338 
    339 			start = ::g->nettics[netnode] - realstart;		
    340 			src = &::g->netbuffer->cmds[start];
    341 
    342 			while (::g->nettics[netnode] < realend)
    343 			{
    344 				dest = &::g->netcmds[netconsole][::g->nettics[netnode]%BACKUPTICS];
    345 				::g->nettics[netnode]++;
    346 				*dest = *src;
    347 				src++;
    348 			}
    349 		}
    350 	}
    351 }
    352 
    353 
    354 //
    355 // NetUpdate
    356 // Builds ticcmds for console player,
    357 // sends out a packet
    358 //
    359 
    360 void NetUpdate ( idUserCmdMgr * userCmdMgr )
    361 {
    362 	int             nowtime;
    363 	int             newtics;
    364 	int				i,j;
    365 	int				realstart;
    366 	int				gameticdiv;
    367 
    368 	// check time
    369 	nowtime = I_GetTime ()/::g->ticdup;
    370 	newtics = nowtime - ::g->gametime;
    371 	::g->gametime = nowtime;
    372 
    373 	if (newtics <= 0) 	// nothing new to update
    374 		goto listen; 
    375 
    376 	if (::g->skiptics <= newtics)
    377 	{
    378 		newtics -= ::g->skiptics;
    379 		::g->skiptics = 0;
    380 	}
    381 	else
    382 	{
    383 		::g->skiptics -= newtics;
    384 		newtics = 0;
    385 	}
    386 
    387 
    388 	::g->netbuffer->player = ::g->consoleplayer;
    389 
    390 	// build new ticcmds for console player
    391 	gameticdiv = ::g->gametic/::g->ticdup;
    392 	for (i=0 ; i<newtics ; i++)
    393 	{
    394 		//I_GetEvents( ::g->I_StartTicCallback () );
    395 		D_ProcessEvents ();
    396 		if (::g->maketic - gameticdiv >= BACKUPTICS/2-1) {
    397 			printf( "Out of room for ticcmds: maketic = %d, gameticdiv = %d\n", ::g->maketic, gameticdiv );
    398 			break;          // can't hold any more
    399 		}
    400 
    401 		//I_Printf ("mk:%i ",::g->maketic);
    402 
    403 		// Grab the latest tech5 command
    404 
    405 		G_BuildTiccmd (&::g->localcmds[::g->maketic%BACKUPTICS], userCmdMgr, newtics );
    406 		::g->maketic++;
    407 	}
    408 
    409 
    410 	if (::g->singletics)
    411 		return;         // singletic update is syncronous
    412 
    413 	// send the packet to the other ::g->nodes
    414 	for (i=0 ; i < ::g->doomcom.numnodes ; i++) {
    415 
    416 		if (::g->nodeingame[i])	{
    417 			::g->netbuffer->starttic = realstart = ::g->resendto[i];
    418 			::g->netbuffer->numtics = ::g->maketic - realstart;
    419 			if (::g->netbuffer->numtics > BACKUPTICS)
    420 				I_Error ("NetUpdate: ::g->netbuffer->numtics > BACKUPTICS");
    421 
    422 			::g->resendto[i] = ::g->maketic - ::g->doomcom.extratics;
    423 
    424 			for (j=0 ; j< ::g->netbuffer->numtics ; j++)
    425 				::g->netbuffer->cmds[j] = 
    426 				::g->localcmds[(realstart+j)%BACKUPTICS];
    427 
    428 			if (::g->remoteresend[i])
    429 			{
    430 				::g->netbuffer->retransmitfrom = ::g->nettics[i];
    431 				HSendPacket (i, NCMD_RETRANSMIT);
    432 			}
    433 			else
    434 			{
    435 				::g->netbuffer->retransmitfrom = 0;
    436 				HSendPacket (i, 0);
    437 			}
    438 		}
    439 	}
    440 
    441 	// listen for other packets
    442 listen:
    443 	GetPackets ();
    444 }
    445 
    446 
    447 
    448 //
    449 // CheckAbort
    450 //
    451 void CheckAbort (void)
    452 {
    453 	// DHM - Time starts at 0 tics when starting a multiplayer game, so we can
    454 	// check for timeouts easily.  If we're still waiting after N seconds, abort.
    455 	if ( I_GetTime() > NET_TIMEOUT ) {
    456 		// TOOD: Show error & leave net game.
    457 		printf( "NET GAME TIMED OUT!\n" );
    458 		//gameLocal->showFatalErrorMessage( XuiLookupStringTable(globalStrings,L"Timed out waiting for match start.") );
    459 
    460 
    461 		D_QuitNetGame();
    462 
    463 		session->QuitMatch();
    464 		common->Dialog().AddDialog( GDM_OPPONENT_CONNECTION_LOST, DIALOG_ACCEPT, NULL, NULL, false );
    465 	}
    466 }
    467 
    468 
    469 //
    470 // D_ArbitrateNetStart
    471 //
    472 bool D_ArbitrateNetStart (void)
    473 {
    474 	int		i;
    475 
    476 	::g->autostart = true;
    477 	if (::g->doomcom.consoleplayer)
    478 	{
    479 		// listen for setup info from key player
    480 		CheckAbort ();
    481 		if (!HGetPacket ())
    482 			return false;
    483 		if (::g->netbuffer->checksum & NCMD_SETUP)
    484 		{
    485 			printf( "Received setup info\n" );
    486 
    487 			if (::g->netbuffer->player != VERSION)
    488 				I_Error ("Different DOOM versions cannot play a net game!");
    489 			::g->startskill = (skill_t)(::g->netbuffer->retransmitfrom & 15);
    490 			::g->deathmatch = (::g->netbuffer->retransmitfrom & 0xc0) >> 6;
    491 			::g->nomonsters = (::g->netbuffer->retransmitfrom & 0x20) > 0;
    492 			::g->respawnparm = (::g->netbuffer->retransmitfrom & 0x10) > 0;
    493 			// VV original xbox doom :: don't do this.. it will be setup from the launcher
    494 			//::g->startmap = ::g->netbuffer->starttic & 0x3f;
    495 			//::g->startepisode = ::g->netbuffer->starttic >> 6;
    496 			return true;
    497 		}
    498 		return false;
    499 	}
    500 	else
    501 	{
    502 		// key player, send the setup info
    503 		CheckAbort ();
    504 		for (i=0 ; i < ::g->doomcom.numnodes ; i++)
    505 		{
    506 			printf( "Sending setup info to node %d\n", i );
    507 
    508 			::g->netbuffer->retransmitfrom = ::g->startskill;
    509 			if (::g->deathmatch)
    510 				::g->netbuffer->retransmitfrom |= (::g->deathmatch<<6);
    511 			if (::g->nomonsters)
    512 				::g->netbuffer->retransmitfrom |= 0x20;
    513 			if (::g->respawnparm)
    514 				::g->netbuffer->retransmitfrom |= 0x10;
    515 			::g->netbuffer->starttic = ::g->startepisode * 64 + ::g->startmap;
    516 			::g->netbuffer->player = VERSION;
    517 			::g->netbuffer->numtics = 0;
    518 			HSendPacket (i, NCMD_SETUP);
    519 		}
    520 
    521 		while (HGetPacket ())
    522 		{
    523 			::g->gotinfo[::g->netbuffer->player&0x7f] = true;
    524 		}
    525 
    526 		for (i=1 ; i < ::g->doomcom.numnodes ; i++) {
    527 			if (!::g->gotinfo[i])
    528 				break;
    529 		}
    530 
    531 		if (i >= ::g->doomcom.numnodes)
    532 			return true;
    533 
    534 		return false;
    535 	}
    536 }
    537 
    538 //
    539 // D_CheckNetGame
    540 // Works out player numbers among the net participants
    541 //
    542 
    543 void D_CheckNetGame (void)
    544 {
    545 	int             i;
    546 
    547 	for (i=0 ; i<MAXNETNODES ; i++)
    548 	{
    549 		::g->nodeingame[i] = false;
    550 		::g->nettics[i] = 0;
    551 		::g->remoteresend[i] = false;	// set when local needs tics
    552 		::g->resendto[i] = 0;		// which tic to start sending
    553 	}
    554 
    555 	// I_InitNetwork sets ::g->doomcom and ::g->netgame
    556 	I_InitNetwork ();
    557 #ifdef ID_ENABLE_DOOM_CLASSIC_NETWORKING
    558 	if (::g->doomcom.id != DOOMCOM_ID)
    559 		I_Error ("Doomcom buffer invalid!");
    560 #endif
    561 
    562 	::g->netbuffer = &::g->doomcom.data;
    563 	::g->consoleplayer = ::g->displayplayer = ::g->doomcom.consoleplayer;
    564 }
    565 
    566 bool D_PollNetworkStart()
    567 {
    568 	int             i;
    569 	if (::g->netgame)
    570 	{
    571 		if (D_ArbitrateNetStart () == false)
    572 			return false;
    573 	}
    574 
    575 	I_Printf ("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n",
    576 		::g->startskill, ::g->deathmatch, ::g->startmap, ::g->startepisode);
    577 
    578 	// read values out of ::g->doomcom
    579 	::g->ticdup = ::g->doomcom.ticdup;
    580 	::g->maxsend = BACKUPTICS/(2*::g->ticdup)-1;
    581 	if (::g->maxsend<1)
    582 		::g->maxsend = 1;
    583 
    584 	for (i=0 ; i < ::g->doomcom.numplayers ; i++)
    585 		::g->playeringame[i] = true;
    586 	for (i=0 ; i < ::g->doomcom.numnodes ; i++)
    587 		::g->nodeingame[i] = true;
    588 
    589 	I_Printf ("player %i of %i (%i ::g->nodes)\n",
    590 		::g->consoleplayer+1, ::g->doomcom.numplayers, ::g->doomcom.numnodes);
    591 
    592 	return true;
    593 }
    594 
    595 
    596 //
    597 // D_QuitNetGame
    598 // Called before quitting to leave a net game
    599 // without hanging the other ::g->players
    600 //
    601 void D_QuitNetGame (void)
    602 {
    603 	int i;
    604 
    605 	if ( (!::g->netgame && !::g->usergame) || ::g->consoleplayer == -1 || ::g->demoplayback || ::g->netbuffer == NULL )
    606 		return;
    607 
    608 	// send a quit packet to the other nodes
    609 	::g->netbuffer->player = ::g->consoleplayer;
    610 	::g->netbuffer->numtics = 0;
    611 
    612 	for ( i=1; i < ::g->doomcom.numnodes; i++ ) {
    613 		if ( ::g->nodeingame[i] ) {
    614 			HSendPacket( i, NCMD_EXIT );
    615 		}
    616 	}
    617 	DoomLib::SendNetwork();
    618 
    619 	for (i=1 ; i<MAXNETNODES ; i++)
    620 	{
    621 		::g->nodeingame[i] = false;
    622 		::g->nettics[i] = 0;
    623 		::g->remoteresend[i] = false;	// set when local needs tics
    624 		::g->resendto[i] = 0;		// which tic to start sending
    625 	}
    626 
    627 	//memset (&::g->doomcom, 0, sizeof(::g->doomcom) );
    628 
    629 	// Reset singleplayer state
    630 	::g->doomcom.id = DOOMCOM_ID;
    631 	::g->doomcom.ticdup = 1;
    632 	::g->doomcom.extratics = 0;
    633 	::g->doomcom.numplayers = ::g->doomcom.numnodes = 1;
    634 	::g->doomcom.deathmatch = false;
    635 	::g->doomcom.consoleplayer = 0;
    636 	::g->netgame = false;
    637 
    638 	::g->netbuffer = &::g->doomcom.data;
    639 	::g->consoleplayer = ::g->displayplayer = ::g->doomcom.consoleplayer;
    640 
    641 	::g->ticdup = ::g->doomcom.ticdup;
    642 	::g->maxsend = BACKUPTICS/(2*::g->ticdup)-1;
    643 	if (::g->maxsend<1)
    644 		::g->maxsend = 1;
    645 
    646 	for (i=0 ; i < ::g->doomcom.numplayers ; i++)
    647 		::g->playeringame[i] = true;
    648 	for (i=0 ; i < ::g->doomcom.numnodes ; i++)
    649 		::g->nodeingame[i] = true;
    650 }
    651 
    652 
    653 
    654 //
    655 // TryRunTics
    656 //
    657 bool TryRunTics ( idUserCmdMgr * userCmdMgr )
    658 {
    659 	int		i;
    660 	int		lowtic_node = -1;
    661 
    662 	// get real tics		
    663 	::g->trt_entertic = I_GetTime ()/::g->ticdup;
    664 	::g->trt_realtics = ::g->trt_entertic - ::g->oldtrt_entertics;
    665 	::g->oldtrt_entertics = ::g->trt_entertic;
    666 
    667 	// get available tics
    668 	NetUpdate ( userCmdMgr );
    669 
    670 	::g->trt_lowtic = MAXINT;
    671 	::g->trt_numplaying = 0;
    672 
    673 	for (i=0 ; i < ::g->doomcom.numnodes ; i++) {
    674 
    675 		if (::g->nodeingame[i]) {
    676 			::g->trt_numplaying++;
    677 
    678 			if (::g->nettics[i] < ::g->trt_lowtic) {
    679 				::g->trt_lowtic = ::g->nettics[i];
    680 				lowtic_node = i;
    681 			}
    682 		}
    683 	}
    684 
    685 	::g->trt_availabletics = ::g->trt_lowtic - ::g->gametic/::g->ticdup;
    686 
    687 	// decide how many tics to run
    688 	if (::g->trt_realtics < ::g->trt_availabletics-1) {
    689 		::g->trt_counts = ::g->trt_realtics+1;
    690 	} else if (::g->trt_realtics < ::g->trt_availabletics) {
    691 		::g->trt_counts = ::g->trt_realtics;
    692 	} else {
    693 		::g->trt_counts = ::g->trt_availabletics;
    694 	}
    695 
    696 	if (::g->trt_counts < 1) {
    697 		::g->trt_counts = 1;
    698 	}
    699 
    700 	::g->frameon++;
    701 
    702 	if (::g->debugfile) {
    703 		fprintf (::g->debugfile, "=======real: %i  avail: %i  game: %i\n", ::g->trt_realtics, ::g->trt_availabletics,::g->trt_counts);
    704 	}
    705 
    706 	if ( !::g->demoplayback )
    707 	{	
    708 		// ideally ::g->nettics[0] should be 1 - 3 tics above ::g->trt_lowtic
    709 		// if we are consistantly slower, speed up time
    710 		for (i=0 ; i<MAXPLAYERS ; i++) {
    711 			if (::g->playeringame[i]) {
    712 				break;
    713 			}
    714 		}
    715 
    716 		if (::g->consoleplayer == i) {
    717 			// the key player does not adapt
    718 		}
    719 		else {
    720 			if (::g->nettics[0] <= ::g->nettics[::g->nodeforplayer[i]])	{
    721 				::g->gametime--;
    722 				//OutputDebugString("-");
    723 			}
    724 
    725 			::g->frameskip[::g->frameon&3] = (::g->oldnettics > ::g->nettics[::g->nodeforplayer[i]]);
    726 			::g->oldnettics = ::g->nettics[0];
    727 
    728 			if (::g->frameskip[0] && ::g->frameskip[1] && ::g->frameskip[2] && ::g->frameskip[3]) {
    729 				::g->skiptics = 1;
    730 				//OutputDebugString("+");
    731 			}
    732 		}
    733 	}
    734 
    735 	// wait for new tics if needed
    736 	if (::g->trt_lowtic < ::g->gametic/::g->ticdup + ::g->trt_counts)	
    737 	{
    738 		int lagtime = 0;
    739 
    740 		if (::g->trt_lowtic < ::g->gametic/::g->ticdup) {
    741 			I_Error ("TryRunTics: ::g->trt_lowtic < gametic");
    742 		}
    743 
    744 		if ( ::g->lastnettic == 0 ) {
    745 			::g->lastnettic = ::g->trt_entertic;
    746 		}
    747 		lagtime = ::g->trt_entertic - ::g->lastnettic;
    748 
    749 		// Detect if a client has stopped sending updates, remove them from the game after 5 secs.
    750 		if ( common->IsMultiplayer() && (!::g->demoplayback && ::g->netgame) && lagtime >= TICRATE ) {
    751 
    752 			if ( lagtime > NET_TIMEOUT ) {
    753 
    754 				if ( lowtic_node == ::g->nodeforplayer[::g->consoleplayer] ) {
    755 
    756 #ifdef ID_ENABLE_DOOM_CLASSIC_NETWORKING
    757 #ifndef __PS3__
    758 					gameLocal->showFatalErrorMessage( XuiLookupStringTable(globalStrings,L"You have been disconnected from the match.") );
    759 					gameLocal->Interface.QuitCurrentGame();
    760 #endif
    761 #endif
    762 				} else {
    763 					if (::g->nodeingame[lowtic_node]) {
    764 						int i, consoleNum = lowtic_node;
    765 
    766 						for ( i=0; i < ::g->doomcom.numnodes; i++ ) {
    767 							if ( ::g->nodeforplayer[i] == lowtic_node ) {
    768 								consoleNum = i;
    769 								break;
    770 							}
    771 						}
    772 
    773 						::g->nodeingame[lowtic_node] = false;
    774 						::g->playeringame[consoleNum] = false;
    775 						strcpy (::g->exitmsg, "Player 1 left the game");
    776 						::g->exitmsg[7] += consoleNum;
    777 						::g->players[::g->consoleplayer].message = ::g->exitmsg;
    778 
    779 						// Stop a demo record now, as playback doesn't support losing players
    780 						G_CheckDemoStatus();
    781 					}
    782 				}
    783 			}
    784 		} 
    785 
    786 		return false;
    787 	}
    788 
    789 	::g->lastnettic = 0;
    790 
    791 	// run the count * ::g->ticdup dics
    792 	while (::g->trt_counts--)
    793 	{
    794 		for (i=0 ; i < ::g->ticdup ; i++)
    795 		{
    796 			if (::g->gametic/::g->ticdup > ::g->trt_lowtic) {
    797 				I_Error ("gametic(%d) greater than trt_lowtic(%d), trt_counts(%d)", ::g->gametic, ::g->trt_lowtic, ::g->trt_counts );
    798 				return false;
    799 			}
    800 
    801 			if (::g->advancedemo) {
    802 				D_DoAdvanceDemo ();
    803 			}
    804 
    805 			M_Ticker ();
    806 			G_Ticker ();
    807 			::g->gametic++;
    808 
    809 			// modify command for duplicated tics
    810 			if (i != ::g->ticdup-1)
    811 			{
    812 				ticcmd_t	*cmd;
    813 				int			buf;
    814 				int			j;
    815 
    816 				buf = (::g->gametic/::g->ticdup)%BACKUPTICS; 
    817 				for (j=0 ; j<MAXPLAYERS ; j++)
    818 				{
    819 					cmd = &::g->netcmds[j][buf];
    820 					if (cmd->buttons & BT_SPECIAL)
    821 						cmd->buttons = 0;
    822 				}
    823 			}
    824 		}
    825 
    826 		NetUpdate ( userCmdMgr );	// check for new console commands
    827 	}
    828 
    829 	return true;
    830 }
    831