2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 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.
14 Doom 3 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.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
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.
26 ===========================================================================
29 #include "../../idlib/precompiled.h"
32 #include "AsyncNetwork.h"
34 idAsyncServer idAsyncNetwork::server;
35 idAsyncClient idAsyncNetwork::client;
37 idCVar idAsyncNetwork::verbose( "net_verbose", "0", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "1 = verbose output, 2 = even more verbose output", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
38 idCVar idAsyncNetwork::allowCheats( "net_allowCheats", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_NETWORKSYNC, "Allow cheats in network game" );
40 // dedicated executable can only have a value of 1 for net_serverDedicated
41 idCVar idAsyncNetwork::serverDedicated( "net_serverDedicated", "1", CVAR_SERVERINFO | CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT | CVAR_ROM, "" );
43 idCVar idAsyncNetwork::serverDedicated( "net_serverDedicated", "0", CVAR_SERVERINFO | CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "1 = text console dedicated server, 2 = graphical dedicated server", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
45 idCVar idAsyncNetwork::serverSnapshotDelay( "net_serverSnapshotDelay", "50", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "delay between snapshots in milliseconds" );
46 idCVar idAsyncNetwork::serverMaxClientRate( "net_serverMaxClientRate", "16000", CVAR_SYSTEM | CVAR_INTEGER | CVAR_ARCHIVE | CVAR_NOCHEAT, "maximum rate to a client in bytes/sec" );
47 idCVar idAsyncNetwork::clientMaxRate( "net_clientMaxRate", "16000", CVAR_SYSTEM | CVAR_INTEGER | CVAR_ARCHIVE | CVAR_NOCHEAT, "maximum rate requested by client from server in bytes/sec" );
48 idCVar idAsyncNetwork::serverMaxUsercmdRelay( "net_serverMaxUsercmdRelay", "5", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "maximum number of usercmds from other clients the server relays to a client", 1, MAX_USERCMD_RELAY, idCmdSystem::ArgCompletion_Integer<1,MAX_USERCMD_RELAY> );
49 idCVar idAsyncNetwork::serverZombieTimeout( "net_serverZombieTimeout", "5", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "disconnected client timeout in seconds" );
50 idCVar idAsyncNetwork::serverClientTimeout( "net_serverClientTimeout", "40", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "client time out in seconds" );
51 idCVar idAsyncNetwork::clientServerTimeout( "net_clientServerTimeout", "40", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "server time out in seconds" );
52 idCVar idAsyncNetwork::serverDrawClient( "net_serverDrawClient", "-1", CVAR_SYSTEM | CVAR_INTEGER, "number of client for which to draw view on server" );
53 idCVar idAsyncNetwork::serverRemoteConsolePassword( "net_serverRemoteConsolePassword", "", CVAR_SYSTEM | CVAR_NOCHEAT, "remote console password" );
54 idCVar idAsyncNetwork::clientPrediction( "net_clientPrediction", "16", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "additional client side prediction in milliseconds" );
55 idCVar idAsyncNetwork::clientMaxPrediction( "net_clientMaxPrediction", "1000", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "maximum number of milliseconds a client can predict ahead of server." );
56 idCVar idAsyncNetwork::clientUsercmdBackup( "net_clientUsercmdBackup", "5", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "number of usercmds to resend" );
57 idCVar idAsyncNetwork::clientRemoteConsoleAddress( "net_clientRemoteConsoleAddress", "localhost", CVAR_SYSTEM | CVAR_NOCHEAT, "remote console address" );
58 idCVar idAsyncNetwork::clientRemoteConsolePassword( "net_clientRemoteConsolePassword", "", CVAR_SYSTEM | CVAR_NOCHEAT, "remote console password" );
59 idCVar idAsyncNetwork::master0( "net_master0", IDNET_HOST ":" IDNET_MASTER_PORT, CVAR_SYSTEM | CVAR_ROM, "idnet master server address" );
60 idCVar idAsyncNetwork::master1( "net_master1", "", CVAR_SYSTEM | CVAR_ARCHIVE, "1st master server address" );
61 idCVar idAsyncNetwork::master2( "net_master2", "", CVAR_SYSTEM | CVAR_ARCHIVE, "2nd master server address" );
62 idCVar idAsyncNetwork::master3( "net_master3", "", CVAR_SYSTEM | CVAR_ARCHIVE, "3rd master server address" );
63 idCVar idAsyncNetwork::master4( "net_master4", "", CVAR_SYSTEM | CVAR_ARCHIVE, "4th master server address" );
64 idCVar idAsyncNetwork::LANServer( "net_LANServer", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_NOCHEAT, "config LAN games only - affects clients and servers" );
65 idCVar idAsyncNetwork::serverReloadEngine( "net_serverReloadEngine", "0", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "perform a full reload on next map restart (including flushing referenced pak files) - decreased if > 0" );
66 idCVar idAsyncNetwork::serverAllowServerMod( "net_serverAllowServerMod", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_NOCHEAT, "allow server-side mods" );
67 idCVar idAsyncNetwork::idleServer( "si_idleServer", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_INIT | CVAR_SERVERINFO, "game clients are idle" );
68 idCVar idAsyncNetwork::clientDownload( "net_clientDownload", "1", CVAR_SYSTEM | CVAR_INTEGER | CVAR_ARCHIVE, "client pk4 downloads policy: 0 - never, 1 - ask, 2 - always (will still prompt for binary code)" );
70 int idAsyncNetwork::realTime;
71 master_t idAsyncNetwork::masters[ MAX_MASTER_SERVERS ];
75 idAsyncNetwork::idAsyncNetwork
78 idAsyncNetwork::idAsyncNetwork( void ) {
86 void idAsyncNetwork::Init( void ) {
90 memset( masters, 0, sizeof( masters ) );
91 masters[0].var = &master0;
92 masters[1].var = &master1;
93 masters[2].var = &master2;
94 masters[3].var = &master3;
95 masters[4].var = &master4;
98 cmdSystem->AddCommand( "spawnServer", SpawnServer_f, CMD_FL_SYSTEM, "spawns a server", idCmdSystem::ArgCompletion_MapName );
99 cmdSystem->AddCommand( "nextMap", NextMap_f, CMD_FL_SYSTEM, "loads the next map on the server" );
100 cmdSystem->AddCommand( "connect", Connect_f, CMD_FL_SYSTEM, "connects to a server" );
101 cmdSystem->AddCommand( "reconnect", Reconnect_f, CMD_FL_SYSTEM, "reconnect to the last server we tried to connect to" );
102 cmdSystem->AddCommand( "serverInfo", GetServerInfo_f, CMD_FL_SYSTEM, "shows server info" );
103 cmdSystem->AddCommand( "LANScan", GetLANServers_f, CMD_FL_SYSTEM, "scans LAN for servers" );
104 cmdSystem->AddCommand( "listServers", ListServers_f, CMD_FL_SYSTEM, "lists scanned servers" );
105 cmdSystem->AddCommand( "rcon", RemoteConsole_f, CMD_FL_SYSTEM, "sends remote console command to server" );
106 cmdSystem->AddCommand( "heartbeat", Heartbeat_f, CMD_FL_SYSTEM, "send a heartbeat to the the master servers" );
107 cmdSystem->AddCommand( "kick", Kick_f, CMD_FL_SYSTEM, "kick a client by connection number" );
108 cmdSystem->AddCommand( "checkNewVersion", CheckNewVersion_f, CMD_FL_SYSTEM, "check if a new version of the game is available" );
109 cmdSystem->AddCommand( "updateUI", UpdateUI_f, CMD_FL_SYSTEM, "internal - cause a sync down of game-modified userinfo" );
115 idAsyncNetwork::GetMasterAddress
118 netadr_t idAsyncNetwork::GetMasterAddress( void ) {
120 GetMasterAddress( 0, ret );
121 return masters[ 0 ].address;
126 idAsyncNetwork::GetMasterAddress
129 bool idAsyncNetwork::GetMasterAddress( int index, netadr_t &adr ) {
130 if ( !masters[ index ].var ) {
133 if ( masters[ index ].var->GetString()[0] == '\0' ) {
136 if ( !masters[ index ].resolved || masters[ index ].var->IsModified() ) {
137 masters[ index ].var->ClearModified();
138 if ( !Sys_StringToNetAdr( masters[ index ].var->GetString(), &masters[ index ].address, true ) ) {
139 common->Printf( "Failed to resolve master%d: %s\n", index, masters[ index ].var->GetString() );
140 memset( &masters[ index ].address, 0, sizeof( netadr_t ) );
141 masters[ index ].resolved = true;
144 if ( masters[ index ].address.port == 0 ) {
145 masters[ index ].address.port = atoi( IDNET_MASTER_PORT );
147 masters[ index ].resolved = true;
149 adr = masters[ index ].address;
155 idAsyncNetwork::Shutdown
158 void idAsyncNetwork::Shutdown( void ) {
159 client.serverList.Shutdown();
160 client.DisconnectFromServer();
161 client.ClearServers();
169 idAsyncNetwork::RunFrame
172 void idAsyncNetwork::RunFrame( void ) {
173 if ( console->Active() ) {
174 Sys_GrabMouseCursor( false );
175 usercmdGen->InhibitUsercmd( INHIBIT_ASYNC, true );
177 Sys_GrabMouseCursor( true );
178 usercmdGen->InhibitUsercmd( INHIBIT_ASYNC, false );
186 idAsyncNetwork::WriteUserCmdDelta
189 void idAsyncNetwork::WriteUserCmdDelta( idBitMsg &msg, const usercmd_t &cmd, const usercmd_t *base ) {
191 msg.WriteDeltaLongCounter( base->gameTime, cmd.gameTime );
192 msg.WriteDeltaByte( base->buttons, cmd.buttons );
193 msg.WriteDeltaShort( base->mx, cmd.mx );
194 msg.WriteDeltaShort( base->my, cmd.my );
195 msg.WriteDeltaChar( base->forwardmove, cmd.forwardmove );
196 msg.WriteDeltaChar( base->rightmove, cmd.rightmove );
197 msg.WriteDeltaChar( base->upmove, cmd.upmove );
198 msg.WriteDeltaShort( base->angles[0], cmd.angles[0] );
199 msg.WriteDeltaShort( base->angles[1], cmd.angles[1] );
200 msg.WriteDeltaShort( base->angles[2], cmd.angles[2] );
204 msg.WriteLong( cmd.gameTime );
205 msg.WriteByte( cmd.buttons );
206 msg.WriteShort( cmd.mx );
207 msg.WriteShort( cmd.my );
208 msg.WriteChar( cmd.forwardmove );
209 msg.WriteChar( cmd.rightmove );
210 msg.WriteChar( cmd.upmove );
211 msg.WriteShort( cmd.angles[0] );
212 msg.WriteShort( cmd.angles[1] );
213 msg.WriteShort( cmd.angles[2] );
218 idAsyncNetwork::ReadUserCmdDelta
221 void idAsyncNetwork::ReadUserCmdDelta( const idBitMsg &msg, usercmd_t &cmd, const usercmd_t *base ) {
222 memset( &cmd, 0, sizeof( cmd ) );
225 cmd.gameTime = msg.ReadDeltaLongCounter( base->gameTime );
226 cmd.buttons = msg.ReadDeltaByte( base->buttons );
227 cmd.mx = msg.ReadDeltaShort( base->mx );
228 cmd.my = msg.ReadDeltaShort( base->my );
229 cmd.forwardmove = msg.ReadDeltaChar( base->forwardmove );
230 cmd.rightmove = msg.ReadDeltaChar( base->rightmove );
231 cmd.upmove = msg.ReadDeltaChar( base->upmove );
232 cmd.angles[0] = msg.ReadDeltaShort( base->angles[0] );
233 cmd.angles[1] = msg.ReadDeltaShort( base->angles[1] );
234 cmd.angles[2] = msg.ReadDeltaShort( base->angles[2] );
238 cmd.gameTime = msg.ReadLong();
239 cmd.buttons = msg.ReadByte();
240 cmd.mx = msg.ReadShort();
241 cmd.my = msg.ReadShort();
242 cmd.forwardmove = msg.ReadChar();
243 cmd.rightmove = msg.ReadChar();
244 cmd.upmove = msg.ReadChar();
245 cmd.angles[0] = msg.ReadShort();
246 cmd.angles[1] = msg.ReadShort();
247 cmd.angles[2] = msg.ReadShort();
252 idAsyncNetwork::DuplicateUsercmd
255 bool idAsyncNetwork::DuplicateUsercmd( const usercmd_t &previousUserCmd, usercmd_t ¤tUserCmd, int frame, int time ) {
257 if ( currentUserCmd.gameTime <= previousUserCmd.gameTime ) {
259 currentUserCmd = previousUserCmd;
260 currentUserCmd.gameFrame = frame;
261 currentUserCmd.gameTime = time;
262 currentUserCmd.duplicateCount++;
264 if ( currentUserCmd.duplicateCount > MAX_USERCMD_DUPLICATION ) {
265 currentUserCmd.buttons &= ~BUTTON_ATTACK;
266 if ( abs( currentUserCmd.forwardmove ) > 2 ) currentUserCmd.forwardmove >>= 1;
267 if ( abs( currentUserCmd.rightmove ) > 2 ) currentUserCmd.rightmove >>= 1;
268 if ( abs( currentUserCmd.upmove ) > 2 ) currentUserCmd.upmove >>= 1;
278 idAsyncNetwork::UsercmdInputChanged
281 bool idAsyncNetwork::UsercmdInputChanged( const usercmd_t &previousUserCmd, const usercmd_t ¤tUserCmd ) {
282 return previousUserCmd.buttons != currentUserCmd.buttons ||
283 previousUserCmd.forwardmove != currentUserCmd.forwardmove ||
284 previousUserCmd.rightmove != currentUserCmd.rightmove ||
285 previousUserCmd.upmove != currentUserCmd.upmove ||
286 previousUserCmd.angles[0] != currentUserCmd.angles[0] ||
287 previousUserCmd.angles[1] != currentUserCmd.angles[1] ||
288 previousUserCmd.angles[2] != currentUserCmd.angles[2];
293 idAsyncNetwork::SpawnServer_f
296 void idAsyncNetwork::SpawnServer_f( const idCmdArgs &args ) {
298 if(args.Argc() > 1) {
299 cvarSystem->SetCVarString("si_map", args.Argv(1));
302 // don't let a server spawn with singleplayer game type - it will crash
303 if ( idStr::Icmp( cvarSystem->GetCVarString( "si_gameType" ), "singleplayer" ) == 0 ) {
304 cvarSystem->SetCVarString( "si_gameType", "deathmatch" );
306 com_asyncInput.SetBool( false );
307 // make sure the current system state is compatible with net_serverDedicated
308 switch ( cvarSystem->GetCVarInteger( "net_serverDedicated" ) ) {
311 if ( !renderSystem->IsOpenGLRunning() ) {
312 common->Warning( "OpenGL is not running, net_serverDedicated == %d", cvarSystem->GetCVarInteger( "net_serverDedicated" ) );
316 if ( renderSystem->IsOpenGLRunning() ) {
317 Sys_ShowConsole( 1, false );
318 renderSystem->ShutdownOpenGL();
320 soundSystem->SetMute( true );
321 soundSystem->ShutdownHW();
324 // use serverMapRestart if we already have a running server
325 if ( server.IsActive() ) {
326 cmdSystem->BufferCommandText( CMD_EXEC_NOW, "serverMapRestart" );
334 idAsyncNetwork::NextMap_f
337 void idAsyncNetwork::NextMap_f( const idCmdArgs &args ) {
338 server.ExecuteMapChange();
343 idAsyncNetwork::Connect_f
346 void idAsyncNetwork::Connect_f( const idCmdArgs &args ) {
347 if ( server.IsActive() ) {
348 common->Printf( "already running a server\n" );
351 if ( args.Argc() != 2 ) {
352 common->Printf( "USAGE: connect <serverName>\n" );
355 com_asyncInput.SetBool( false );
356 client.ConnectToServer( args.Argv( 1 ) );
361 idAsyncNetwork::Reconnect_f
364 void idAsyncNetwork::Reconnect_f( const idCmdArgs &args ) {
370 idAsyncNetwork::GetServerInfo_f
373 void idAsyncNetwork::GetServerInfo_f( const idCmdArgs &args ) {
374 client.GetServerInfo( args.Argv( 1 ) );
379 idAsyncNetwork::GetLANServers_f
382 void idAsyncNetwork::GetLANServers_f( const idCmdArgs &args ) {
383 client.GetLANServers();
388 idAsyncNetwork::ListServers_f
391 void idAsyncNetwork::ListServers_f( const idCmdArgs &args ) {
392 client.ListServers();
397 idAsyncNetwork::RemoteConsole_f
400 void idAsyncNetwork::RemoteConsole_f( const idCmdArgs &args ) {
401 client.RemoteConsole( args.Args() );
406 idAsyncNetwork::Heartbeat_f
409 void idAsyncNetwork::Heartbeat_f( const idCmdArgs &args ) {
410 if ( !server.IsActive() ) {
411 common->Printf( "server is not running\n" );
414 server.MasterHeartbeat( true );
419 idAsyncNetwork::Kick_f
422 void idAsyncNetwork::Kick_f( const idCmdArgs &args ) {
426 if ( !server.IsActive() ) {
427 common->Printf( "server is not running\n" );
431 clientId = args.Argv( 1 );
432 if ( !clientId.IsNumeric() ) {
433 common->Printf( "usage: kick <client number>\n" );
436 iclient = atoi( clientId );
438 if ( server.GetLocalClientNum() == iclient ) {
439 common->Printf( "can't kick the host\n" );
443 server.DropClient( iclient, "#str_07134" );
448 idAsyncNetwork::GetNETServers
451 void idAsyncNetwork::GetNETServers( ) {
452 client.GetNETServers();
457 idAsyncNetwork::CheckNewVersion_f
460 void idAsyncNetwork::CheckNewVersion_f( const idCmdArgs &args ) {
461 client.SendVersionCheck();
466 idAsyncNetwork::ExecuteSessionCommand
469 void idAsyncNetwork::ExecuteSessionCommand( const char *sessCmd ) {
470 if ( sessCmd[ 0 ] ) {
471 if ( !idStr::Icmp( sessCmd, "game_startmenu" ) ) {
472 session->SetGUI( game->StartMenu(), NULL );
479 idAsyncNetwork::UpdateUI_f
482 void idAsyncNetwork::UpdateUI_f( const idCmdArgs &args ) {
483 if ( args.Argc() != 2 ) {
484 common->Warning( "idAsyncNetwork::UpdateUI_f: wrong arguments\n" );
487 if ( !server.IsActive() ) {
488 common->Warning( "idAsyncNetwork::UpdateUI_f: server is not active\n" );
491 int clientNum = atoi( args.Args( 1 ) );
492 server.UpdateUI( clientNum );
497 idAsyncNetwork::BuildInvalidKeyMsg
500 void idAsyncNetwork::BuildInvalidKeyMsg( idStr &msg, bool valid[ 2 ] ) {
502 msg += common->GetLanguageDict()->GetString( "#str_07194" );
504 if ( fileSystem->HasD3XP() && !valid[ 1 ] ) {
505 if ( msg.Length() ) {
508 msg += common->GetLanguageDict()->GetString( "#str_07195" );
511 msg += common->GetLanguageDict()->GetString( "#str_04304" );