// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // 02111-1307, USA. // // DESCRIPTION: // DOOM Network game communication and protocol, // all OS independend parts. // //----------------------------------------------------------------------------- #include "doomfeatures.h" #include "d_main.h" #include "m_argv.h" #include "m_menu.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "g_game.h" #include "doomdef.h" #include "doomstat.h" #include "deh_main.h" #include "net_client.h" #include "net_gui.h" #include "net_io.h" #include "net_query.h" #include "net_server.h" #include "net_sdl.h" #include "net_loop.h" // // NETWORKING // // gametic is the tic about to (or currently being) run // maketic is the tick that hasn't had control made for it yet // nettics[] has the maketics for all players // // a gametic cannot be run until nettics[] > gametic for all players // ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; int nettics[MAXPLAYERS]; int maketic; int recvtic; // Used for original sync code. int lastnettic; int skiptics = 0; // Reduce the bandwidth needed by sampling game input less and transmitting // less. If ticdup is 2, sample half normal, 3 = one third normal, etc. int ticdup; // Send this many extra (backup) tics in each packet. int extratics; // Amount to offset the timer for game sync. fixed_t offsetms; // Use new client syncronisation code boolean net_cl_new_sync = true; // Connected but not participating in the game (observer) boolean drone = false; // 35 fps clock adjusted by offsetms milliseconds static int GetAdjustedTime(void) { int time_ms; time_ms = I_GetTimeMS(); if (net_cl_new_sync) { // Use the adjustments from net_client.c only if we are // using the new sync mode. time_ms += (offsetms / FRACUNIT); } return (time_ms * TICRATE) / 1000; } // // NetUpdate // Builds ticcmds for console player, // sends out a packet // int lasttime; void NetUpdate (void) { int nowtime; int newtics; int i; int gameticdiv; // If we are running with singletics (timing a demo), this // is all done separately. if (singletics) return; #ifdef FEATURE_MULTIPLAYER // Run network subsystems NET_CL_Run(); NET_SV_Run(); #endif // check time nowtime = GetAdjustedTime() / ticdup; newtics = nowtime - lasttime; lasttime = nowtime; if (skiptics <= newtics) { newtics -= skiptics; skiptics = 0; } else { skiptics -= newtics; newtics = 0; } // build new ticcmds for console player gameticdiv = gametic/ticdup; for (i=0 ; i 2) break; // Never go more than ~200ms ahead if (maketic - gameticdiv > 8) break; } else { if (maketic - gameticdiv >= 5) break; } //printf ("mk:%i ",maketic); G_BuildTiccmd(&cmd); #ifdef FEATURE_MULTIPLAYER if (netgame && !demoplayback) { NET_CL_SendTiccmd(&cmd, maketic); } #endif netcmds[consoleplayer][maketic % BACKUPTICS] = cmd; ++maketic; nettics[consoleplayer] = maketic; } } // Called when a player leaves the game static void D_PlayerQuitGame(player_t *player) { static char exitmsg[80]; unsigned int player_num; player_num = player - players; // Do this the same way as Vanilla Doom does, to allow dehacked // replacements of this message strncpy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg)); exitmsg[sizeof(exitmsg) - 1] = '\0'; exitmsg[7] += player_num; playeringame[player_num] = false; players[consoleplayer].message = exitmsg; // TODO: check if it is sensible to do this: if (demorecording) { G_CheckDemoStatus (); } } static void D_Disconnected(void) { int i; // In drone mode, the game cannot continue once disconnected. if (drone) { I_Error("Disconnected from server in drone mode."); } // disconnected from server printf("Disconnected from server.\n"); for (i=0; i 0) { NET_SV_Init(); NET_SV_AddModule(&net_loop_server_module); NET_SV_AddModule(&net_sdl_module); net_loop_client_module.InitClient(); addr = net_loop_client_module.ResolveAddress(NULL); } else { //! // @category net // // Automatically search the local LAN for a multiplayer // server and join it. // i = M_CheckParm("-autojoin"); if (i > 0) { addr = NET_FindLANServer(); if (addr == NULL) { I_Error("No server found on local LAN"); } } //! // @arg
// @category net // // Connect to a multiplayer server running on the given // address. // i = M_CheckParm("-connect"); if (i > 0) { net_sdl_module.InitClient(); addr = net_sdl_module.ResolveAddress(myargv[i+1]); if (addr == NULL) { I_Error("Unable to resolve '%s'\n", myargv[i+1]); } } } if (addr != NULL) { if (M_CheckParm("-drone") > 0) { drone = true; } //! // @category net // // Run as the left screen in three screen mode. // if (M_CheckParm("-left") > 0) { viewangleoffset = ANG90; drone = true; } //! // @category net // // Run as the right screen in three screen mode. // if (M_CheckParm("-right") > 0) { viewangleoffset = ANG270; drone = true; } if (!NET_CL_Connect(addr)) { I_Error("D_CheckNetGame: Failed to connect to %s\n", NET_AddrToString(addr)); } printf("D_CheckNetGame: Connected to %s\n", NET_AddrToString(addr)); NET_WaitForStart(); } } #endif num_players = 0; for (i=0; i 0) { DEH_printf("Levels will end after %d minute", timelimit); if (timelimit > 1) printf("s"); printf(".\n"); } } // // D_QuitNetGame // Called before quitting to leave a net game // without hanging the other players // void D_QuitNetGame (void) { if (debugfile) fclose (debugfile); #ifdef FEATURE_MULTIPLAYER NET_SV_Shutdown(); NET_CL_Disconnect(); #endif } // Returns true if there are currently any players in the game. static boolean PlayersInGame(void) { int i; for (i=0; i nettics[keyplayer]); oldnettics = maketic; if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) { skiptics = 1; // printf ("+"); } } } } if (counts < 1) counts = 1; // wait for new tics if needed while (!PlayersInGame() || lowtic < gametic/ticdup + counts) { NetUpdate (); lowtic = GetLowTic(); if (lowtic < gametic/ticdup) I_Error ("TryRunTics: lowtic < gametic"); // Don't stay in this loop forever. The menu is still running, // so return to update the screen if (I_GetTime() / ticdup - entertic > 0) { return; } I_Sleep(1); } // run the count * ticdup dics while (counts--) { for (i=0 ; i lowtic) I_Error ("gametic>lowtic"); if (advancedemo) D_DoAdvanceDemo (); G_Ticker (); gametic++; // modify command for duplicated tics if (i != ticdup-1) { ticcmd_t *cmd; int buf; int j; buf = (gametic/ticdup)%BACKUPTICS; for (j=0 ; jchatchar = 0; if (cmd->buttons & BT_SPECIAL) cmd->buttons = 0; } } } NetUpdate (); // check for new console commands } }