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