hello world

This commit is contained in:
Timothee 'TTimo' Besset
2011-11-22 15:28:15 -06:00
commit fb1609f554
2155 changed files with 1017022 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,218 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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.
===========================================================================
*/
#ifndef __ASYNCCLIENT_H__
#define __ASYNCCLIENT_H__
/*
===============================================================================
Network Client for asynchronous networking.
===============================================================================
*/
typedef enum {
CS_DISCONNECTED,
CS_PURERESTART,
CS_CHALLENGING,
CS_CONNECTING,
CS_CONNECTED,
CS_INGAME
} clientState_t;
typedef enum {
AUTHKEY_BADKEY,
AUTHKEY_GUID
} authKeyMsg_t;
typedef enum {
AUTHKEY_BAD_INVALID,
AUTHKEY_BAD_BANNED,
AUTHKEY_BAD_INUSE,
AUTHKEY_BAD_MSG
} authBadKeyStatus_t;
typedef enum {
UPDATE_NONE,
UPDATE_SENT,
UPDATE_READY,
UPDATE_DLING,
UPDATE_DONE
} clientUpdateState_t;
typedef struct {
idStr url;
idStr filename;
int size;
int checksum;
} pakDlEntry_t;
class idAsyncClient {
public:
idAsyncClient();
void Shutdown( void );
bool InitPort( void );
void ClosePort( void );
void ConnectToServer( const netadr_t adr );
void ConnectToServer( const char *address );
void Reconnect( void );
void DisconnectFromServer( void );
void GetServerInfo( const netadr_t adr );
void GetServerInfo( const char *address );
void GetLANServers( void );
void GetNETServers( void );
void ListServers( void );
void ClearServers( void );
void RemoteConsole( const char *command );
bool IsPortInitialized() { return clientPort.GetPort() != 0; }
bool IsActive( void ) const { return active; }
int GetLocalClientNum( void ) const { return clientNum; }
int GetPrediction( void ) const;
int GetTimeSinceLastPacket( void ) const;
int GetOutgoingRate( void ) const;
int GetIncomingRate( void ) const;
float GetOutgoingCompression( void ) const;
float GetIncomingCompression( void ) const;
float GetIncomingPacketLoss( void ) const;
int GetPredictedFrames( void ) const { return lastFrameDelta; }
void RunFrame( void );
void SendReliableGameMessage( const idBitMsg &msg );
void SendVersionCheck( bool fromMenu = false );
// pass NULL for the keys you don't care to auth for
// returns false if internet link doesn't appear to be available
bool SendAuthCheck( const char *cdkey, const char *xpkey );
void PacifierUpdate( void );
idServerScan serverList;
private:
bool active; // true if client is active
int realTime; // absolute time
int clientTime; // client local time
idPort clientPort; // UDP port
int clientId; // client identification
int clientDataChecksum; // checksum of the data used by the client
int clientNum; // client number on server
clientState_t clientState; // client state
int clientPrediction; // how far the client predicts ahead
int clientPredictTime; // prediction time used to send user commands
netadr_t serverAddress; // IP address of server
int serverId; // server identification
int serverChallenge; // challenge from server
int serverMessageSequence; // sequence number of last server message
netadr_t lastRconAddress; // last rcon address we emitted to
int lastRconTime; // when last rcon emitted
idMsgChannel channel; // message channel to server
int lastConnectTime; // last time a connect message was sent
int lastEmptyTime; // last time an empty message was sent
int lastPacketTime; // last time a packet was received from the server
int lastSnapshotTime; // last time a snapshot was received
int snapshotSequence; // sequence number of the last received snapshot
int snapshotGameFrame; // game frame number of the last received snapshot
int snapshotGameTime; // game time of the last received snapshot
int gameInitId; // game initialization identification
int gameFrame; // local game frame
int gameTime; // local game time
int gameTimeResidual; // left over time from previous frame
usercmd_t userCmds[MAX_USERCMD_BACKUP][MAX_ASYNC_CLIENTS];
idUserInterface * guiNetMenu;
clientUpdateState_t updateState;
int updateSentTime;
idStr updateMSG;
idStr updateURL;
bool updateDirectDownload;
idStr updateFile;
dlMime_t updateMime;
idStr updateFallback;
bool showUpdateMessage;
backgroundDownload_t backgroundDownload;
int dltotal;
int dlnow;
int lastFrameDelta;
int dlRequest; // randomized number to keep track of the requests
int dlChecksums[ MAX_PURE_PAKS ]; // 0-terminated, first element is the game pak checksum or 0
int dlCount; // total number of paks we request download for ( including the game pak )
idList<pakDlEntry_t>dlList; // list of paks to download, with url and name
int currentDlSize;
int totalDlSize; // for partial progress stuff
void Clear( void );
void ClearPendingPackets( void );
void DuplicateUsercmds( int frame, int time );
void SendUserInfoToServer( void );
void SendEmptyToServer( bool force = false, bool mapLoad = false );
void SendPingResponseToServer( int time );
void SendUsercmdsToServer( void );
void InitGame( int serverGameInitId, int serverGameFrame, int serverGameTime, const idDict &serverSI );
void ProcessUnreliableServerMessage( const idBitMsg &msg );
void ProcessReliableServerMessages( void );
void ProcessChallengeResponseMessage( const netadr_t from, const idBitMsg &msg );
void ProcessConnectResponseMessage( const netadr_t from, const idBitMsg &msg );
void ProcessDisconnectMessage( const netadr_t from, const idBitMsg &msg );
void ProcessInfoResponseMessage( const netadr_t from, const idBitMsg &msg );
void ProcessPrintMessage( const netadr_t from, const idBitMsg &msg );
void ProcessServersListMessage( const netadr_t from, const idBitMsg &msg );
void ProcessAuthKeyMessage( const netadr_t from, const idBitMsg &msg );
void ProcessVersionMessage( const netadr_t from, const idBitMsg &msg );
void ConnectionlessMessage( const netadr_t from, const idBitMsg &msg );
void ProcessMessage( const netadr_t from, idBitMsg &msg );
void SetupConnection( void );
void ProcessPureMessage( const netadr_t from, const idBitMsg &msg );
bool ValidatePureServerChecksums( const netadr_t from, const idBitMsg &msg );
void ProcessReliableMessagePure( const idBitMsg &msg );
static const char* HandleGuiCommand( const char *cmd );
const char* HandleGuiCommandInternal( const char *cmd );
void SendVersionDLUpdate( int state );
void HandleDownloads( void );
void Idle( void );
int UpdateTime( int clamp );
void ReadLocalizedServerString( const idBitMsg &msg, char* out, int maxLen );
bool CheckTimeout( void );
void ProcessDownloadInfoMessage( const netadr_t from, const idBitMsg &msg );
int GetDownloadRequest( const int checksums[ MAX_PURE_PAKS ], int count, int gamePakChecksum );
};
#endif /* !__ASYNCCLIENT_H__ */

View File

@@ -0,0 +1,513 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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.
===========================================================================
*/
#include "../../idlib/precompiled.h"
#pragma hdrstop
#include "AsyncNetwork.h"
idAsyncServer idAsyncNetwork::server;
idAsyncClient idAsyncNetwork::client;
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> );
idCVar idAsyncNetwork::allowCheats( "net_allowCheats", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_NETWORKSYNC, "Allow cheats in network game" );
#ifdef ID_DEDICATED
// dedicated executable can only have a value of 1 for net_serverDedicated
idCVar idAsyncNetwork::serverDedicated( "net_serverDedicated", "1", CVAR_SERVERINFO | CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT | CVAR_ROM, "" );
#else
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> );
#endif
idCVar idAsyncNetwork::serverSnapshotDelay( "net_serverSnapshotDelay", "50", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "delay between snapshots in milliseconds" );
idCVar idAsyncNetwork::serverMaxClientRate( "net_serverMaxClientRate", "16000", CVAR_SYSTEM | CVAR_INTEGER | CVAR_ARCHIVE | CVAR_NOCHEAT, "maximum rate to a client in bytes/sec" );
idCVar idAsyncNetwork::clientMaxRate( "net_clientMaxRate", "16000", CVAR_SYSTEM | CVAR_INTEGER | CVAR_ARCHIVE | CVAR_NOCHEAT, "maximum rate requested by client from server in bytes/sec" );
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> );
idCVar idAsyncNetwork::serverZombieTimeout( "net_serverZombieTimeout", "5", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "disconnected client timeout in seconds" );
idCVar idAsyncNetwork::serverClientTimeout( "net_serverClientTimeout", "40", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "client time out in seconds" );
idCVar idAsyncNetwork::clientServerTimeout( "net_clientServerTimeout", "40", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "server time out in seconds" );
idCVar idAsyncNetwork::serverDrawClient( "net_serverDrawClient", "-1", CVAR_SYSTEM | CVAR_INTEGER, "number of client for which to draw view on server" );
idCVar idAsyncNetwork::serverRemoteConsolePassword( "net_serverRemoteConsolePassword", "", CVAR_SYSTEM | CVAR_NOCHEAT, "remote console password" );
idCVar idAsyncNetwork::clientPrediction( "net_clientPrediction", "16", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "additional client side prediction in milliseconds" );
idCVar idAsyncNetwork::clientMaxPrediction( "net_clientMaxPrediction", "1000", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "maximum number of milliseconds a client can predict ahead of server." );
idCVar idAsyncNetwork::clientUsercmdBackup( "net_clientUsercmdBackup", "5", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "number of usercmds to resend" );
idCVar idAsyncNetwork::clientRemoteConsoleAddress( "net_clientRemoteConsoleAddress", "localhost", CVAR_SYSTEM | CVAR_NOCHEAT, "remote console address" );
idCVar idAsyncNetwork::clientRemoteConsolePassword( "net_clientRemoteConsolePassword", "", CVAR_SYSTEM | CVAR_NOCHEAT, "remote console password" );
idCVar idAsyncNetwork::master0( "net_master0", IDNET_HOST ":" IDNET_MASTER_PORT, CVAR_SYSTEM | CVAR_ROM, "idnet master server address" );
idCVar idAsyncNetwork::master1( "net_master1", "", CVAR_SYSTEM | CVAR_ARCHIVE, "1st master server address" );
idCVar idAsyncNetwork::master2( "net_master2", "", CVAR_SYSTEM | CVAR_ARCHIVE, "2nd master server address" );
idCVar idAsyncNetwork::master3( "net_master3", "", CVAR_SYSTEM | CVAR_ARCHIVE, "3rd master server address" );
idCVar idAsyncNetwork::master4( "net_master4", "", CVAR_SYSTEM | CVAR_ARCHIVE, "4th master server address" );
idCVar idAsyncNetwork::LANServer( "net_LANServer", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_NOCHEAT, "config LAN games only - affects clients and servers" );
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" );
idCVar idAsyncNetwork::serverAllowServerMod( "net_serverAllowServerMod", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_NOCHEAT, "allow server-side mods" );
idCVar idAsyncNetwork::idleServer( "si_idleServer", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_INIT | CVAR_SERVERINFO, "game clients are idle" );
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)" );
int idAsyncNetwork::realTime;
master_t idAsyncNetwork::masters[ MAX_MASTER_SERVERS ];
/*
==================
idAsyncNetwork::idAsyncNetwork
==================
*/
idAsyncNetwork::idAsyncNetwork( void ) {
}
/*
==================
idAsyncNetwork::Init
==================
*/
void idAsyncNetwork::Init( void ) {
realTime = 0;
memset( masters, 0, sizeof( masters ) );
masters[0].var = &master0;
masters[1].var = &master1;
masters[2].var = &master2;
masters[3].var = &master3;
masters[4].var = &master4;
#ifndef ID_DEMO_BUILD
cmdSystem->AddCommand( "spawnServer", SpawnServer_f, CMD_FL_SYSTEM, "spawns a server", idCmdSystem::ArgCompletion_MapName );
cmdSystem->AddCommand( "nextMap", NextMap_f, CMD_FL_SYSTEM, "loads the next map on the server" );
cmdSystem->AddCommand( "connect", Connect_f, CMD_FL_SYSTEM, "connects to a server" );
cmdSystem->AddCommand( "reconnect", Reconnect_f, CMD_FL_SYSTEM, "reconnect to the last server we tried to connect to" );
cmdSystem->AddCommand( "serverInfo", GetServerInfo_f, CMD_FL_SYSTEM, "shows server info" );
cmdSystem->AddCommand( "LANScan", GetLANServers_f, CMD_FL_SYSTEM, "scans LAN for servers" );
cmdSystem->AddCommand( "listServers", ListServers_f, CMD_FL_SYSTEM, "lists scanned servers" );
cmdSystem->AddCommand( "rcon", RemoteConsole_f, CMD_FL_SYSTEM, "sends remote console command to server" );
cmdSystem->AddCommand( "heartbeat", Heartbeat_f, CMD_FL_SYSTEM, "send a heartbeat to the the master servers" );
cmdSystem->AddCommand( "kick", Kick_f, CMD_FL_SYSTEM, "kick a client by connection number" );
cmdSystem->AddCommand( "checkNewVersion", CheckNewVersion_f, CMD_FL_SYSTEM, "check if a new version of the game is available" );
cmdSystem->AddCommand( "updateUI", UpdateUI_f, CMD_FL_SYSTEM, "internal - cause a sync down of game-modified userinfo" );
#endif
}
/*
==================
idAsyncNetwork::GetMasterAddress
==================
*/
netadr_t idAsyncNetwork::GetMasterAddress( void ) {
netadr_t ret;
GetMasterAddress( 0, ret );
return masters[ 0 ].address;
}
/*
==================
idAsyncNetwork::GetMasterAddress
==================
*/
bool idAsyncNetwork::GetMasterAddress( int index, netadr_t &adr ) {
if ( !masters[ index ].var ) {
return false;
}
if ( masters[ index ].var->GetString()[0] == '\0' ) {
return false;
}
if ( !masters[ index ].resolved || masters[ index ].var->IsModified() ) {
masters[ index ].var->ClearModified();
if ( !Sys_StringToNetAdr( masters[ index ].var->GetString(), &masters[ index ].address, true ) ) {
common->Printf( "Failed to resolve master%d: %s\n", index, masters[ index ].var->GetString() );
memset( &masters[ index ].address, 0, sizeof( netadr_t ) );
masters[ index ].resolved = true;
return false;
}
if ( masters[ index ].address.port == 0 ) {
masters[ index ].address.port = atoi( IDNET_MASTER_PORT );
}
masters[ index ].resolved = true;
}
adr = masters[ index ].address;
return true;
}
/*
==================
idAsyncNetwork::Shutdown
==================
*/
void idAsyncNetwork::Shutdown( void ) {
client.serverList.Shutdown();
client.DisconnectFromServer();
client.ClearServers();
client.ClosePort();
server.Kill();
server.ClosePort();
}
/*
==================
idAsyncNetwork::RunFrame
==================
*/
void idAsyncNetwork::RunFrame( void ) {
if ( console->Active() ) {
Sys_GrabMouseCursor( false );
usercmdGen->InhibitUsercmd( INHIBIT_ASYNC, true );
} else {
Sys_GrabMouseCursor( true );
usercmdGen->InhibitUsercmd( INHIBIT_ASYNC, false );
}
client.RunFrame();
server.RunFrame();
}
/*
==================
idAsyncNetwork::WriteUserCmdDelta
==================
*/
void idAsyncNetwork::WriteUserCmdDelta( idBitMsg &msg, const usercmd_t &cmd, const usercmd_t *base ) {
if ( base ) {
msg.WriteDeltaLongCounter( base->gameTime, cmd.gameTime );
msg.WriteDeltaByte( base->buttons, cmd.buttons );
msg.WriteDeltaShort( base->mx, cmd.mx );
msg.WriteDeltaShort( base->my, cmd.my );
msg.WriteDeltaChar( base->forwardmove, cmd.forwardmove );
msg.WriteDeltaChar( base->rightmove, cmd.rightmove );
msg.WriteDeltaChar( base->upmove, cmd.upmove );
msg.WriteDeltaShort( base->angles[0], cmd.angles[0] );
msg.WriteDeltaShort( base->angles[1], cmd.angles[1] );
msg.WriteDeltaShort( base->angles[2], cmd.angles[2] );
return;
}
msg.WriteLong( cmd.gameTime );
msg.WriteByte( cmd.buttons );
msg.WriteShort( cmd.mx );
msg.WriteShort( cmd.my );
msg.WriteChar( cmd.forwardmove );
msg.WriteChar( cmd.rightmove );
msg.WriteChar( cmd.upmove );
msg.WriteShort( cmd.angles[0] );
msg.WriteShort( cmd.angles[1] );
msg.WriteShort( cmd.angles[2] );
}
/*
==================
idAsyncNetwork::ReadUserCmdDelta
==================
*/
void idAsyncNetwork::ReadUserCmdDelta( const idBitMsg &msg, usercmd_t &cmd, const usercmd_t *base ) {
memset( &cmd, 0, sizeof( cmd ) );
if ( base ) {
cmd.gameTime = msg.ReadDeltaLongCounter( base->gameTime );
cmd.buttons = msg.ReadDeltaByte( base->buttons );
cmd.mx = msg.ReadDeltaShort( base->mx );
cmd.my = msg.ReadDeltaShort( base->my );
cmd.forwardmove = msg.ReadDeltaChar( base->forwardmove );
cmd.rightmove = msg.ReadDeltaChar( base->rightmove );
cmd.upmove = msg.ReadDeltaChar( base->upmove );
cmd.angles[0] = msg.ReadDeltaShort( base->angles[0] );
cmd.angles[1] = msg.ReadDeltaShort( base->angles[1] );
cmd.angles[2] = msg.ReadDeltaShort( base->angles[2] );
return;
}
cmd.gameTime = msg.ReadLong();
cmd.buttons = msg.ReadByte();
cmd.mx = msg.ReadShort();
cmd.my = msg.ReadShort();
cmd.forwardmove = msg.ReadChar();
cmd.rightmove = msg.ReadChar();
cmd.upmove = msg.ReadChar();
cmd.angles[0] = msg.ReadShort();
cmd.angles[1] = msg.ReadShort();
cmd.angles[2] = msg.ReadShort();
}
/*
==================
idAsyncNetwork::DuplicateUsercmd
==================
*/
bool idAsyncNetwork::DuplicateUsercmd( const usercmd_t &previousUserCmd, usercmd_t &currentUserCmd, int frame, int time ) {
if ( currentUserCmd.gameTime <= previousUserCmd.gameTime ) {
currentUserCmd = previousUserCmd;
currentUserCmd.gameFrame = frame;
currentUserCmd.gameTime = time;
currentUserCmd.duplicateCount++;
if ( currentUserCmd.duplicateCount > MAX_USERCMD_DUPLICATION ) {
currentUserCmd.buttons &= ~BUTTON_ATTACK;
if ( abs( currentUserCmd.forwardmove ) > 2 ) currentUserCmd.forwardmove >>= 1;
if ( abs( currentUserCmd.rightmove ) > 2 ) currentUserCmd.rightmove >>= 1;
if ( abs( currentUserCmd.upmove ) > 2 ) currentUserCmd.upmove >>= 1;
}
return true;
}
return false;
}
/*
==================
idAsyncNetwork::UsercmdInputChanged
==================
*/
bool idAsyncNetwork::UsercmdInputChanged( const usercmd_t &previousUserCmd, const usercmd_t &currentUserCmd ) {
return previousUserCmd.buttons != currentUserCmd.buttons ||
previousUserCmd.forwardmove != currentUserCmd.forwardmove ||
previousUserCmd.rightmove != currentUserCmd.rightmove ||
previousUserCmd.upmove != currentUserCmd.upmove ||
previousUserCmd.angles[0] != currentUserCmd.angles[0] ||
previousUserCmd.angles[1] != currentUserCmd.angles[1] ||
previousUserCmd.angles[2] != currentUserCmd.angles[2];
}
/*
==================
idAsyncNetwork::SpawnServer_f
==================
*/
void idAsyncNetwork::SpawnServer_f( const idCmdArgs &args ) {
if(args.Argc() > 1) {
cvarSystem->SetCVarString("si_map", args.Argv(1));
}
// don't let a server spawn with singleplayer game type - it will crash
if ( idStr::Icmp( cvarSystem->GetCVarString( "si_gameType" ), "singleplayer" ) == 0 ) {
cvarSystem->SetCVarString( "si_gameType", "deathmatch" );
}
com_asyncInput.SetBool( false );
// make sure the current system state is compatible with net_serverDedicated
switch ( cvarSystem->GetCVarInteger( "net_serverDedicated" ) ) {
case 0:
case 2:
if ( !renderSystem->IsOpenGLRunning() ) {
common->Warning( "OpenGL is not running, net_serverDedicated == %d", cvarSystem->GetCVarInteger( "net_serverDedicated" ) );
}
break;
case 1:
if ( renderSystem->IsOpenGLRunning() ) {
Sys_ShowConsole( 1, false );
renderSystem->ShutdownOpenGL();
}
soundSystem->SetMute( true );
soundSystem->ShutdownHW();
break;
}
// use serverMapRestart if we already have a running server
if ( server.IsActive() ) {
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "serverMapRestart" );
} else {
server.Spawn();
}
}
/*
==================
idAsyncNetwork::NextMap_f
==================
*/
void idAsyncNetwork::NextMap_f( const idCmdArgs &args ) {
server.ExecuteMapChange();
}
/*
==================
idAsyncNetwork::Connect_f
==================
*/
void idAsyncNetwork::Connect_f( const idCmdArgs &args ) {
if ( server.IsActive() ) {
common->Printf( "already running a server\n" );
return;
}
if ( args.Argc() != 2 ) {
common->Printf( "USAGE: connect <serverName>\n" );
return;
}
com_asyncInput.SetBool( false );
client.ConnectToServer( args.Argv( 1 ) );
}
/*
==================
idAsyncNetwork::Reconnect_f
==================
*/
void idAsyncNetwork::Reconnect_f( const idCmdArgs &args ) {
client.Reconnect();
}
/*
==================
idAsyncNetwork::GetServerInfo_f
==================
*/
void idAsyncNetwork::GetServerInfo_f( const idCmdArgs &args ) {
client.GetServerInfo( args.Argv( 1 ) );
}
/*
==================
idAsyncNetwork::GetLANServers_f
==================
*/
void idAsyncNetwork::GetLANServers_f( const idCmdArgs &args ) {
client.GetLANServers();
}
/*
==================
idAsyncNetwork::ListServers_f
==================
*/
void idAsyncNetwork::ListServers_f( const idCmdArgs &args ) {
client.ListServers();
}
/*
==================
idAsyncNetwork::RemoteConsole_f
==================
*/
void idAsyncNetwork::RemoteConsole_f( const idCmdArgs &args ) {
client.RemoteConsole( args.Args() );
}
/*
==================
idAsyncNetwork::Heartbeat_f
==================
*/
void idAsyncNetwork::Heartbeat_f( const idCmdArgs &args ) {
if ( !server.IsActive() ) {
common->Printf( "server is not running\n" );
return;
}
server.MasterHeartbeat( true );
}
/*
==================
idAsyncNetwork::Kick_f
==================
*/
void idAsyncNetwork::Kick_f( const idCmdArgs &args ) {
idStr clientId;
int iclient;
if ( !server.IsActive() ) {
common->Printf( "server is not running\n" );
return;
}
clientId = args.Argv( 1 );
if ( !clientId.IsNumeric() ) {
common->Printf( "usage: kick <client number>\n" );
return;
}
iclient = atoi( clientId );
if ( server.GetLocalClientNum() == iclient ) {
common->Printf( "can't kick the host\n" );
return;
}
server.DropClient( iclient, "#str_07134" );
}
/*
==================
idAsyncNetwork::GetNETServers
==================
*/
void idAsyncNetwork::GetNETServers( ) {
client.GetNETServers();
}
/*
==================
idAsyncNetwork::CheckNewVersion_f
==================
*/
void idAsyncNetwork::CheckNewVersion_f( const idCmdArgs &args ) {
client.SendVersionCheck();
}
/*
==================
idAsyncNetwork::ExecuteSessionCommand
==================
*/
void idAsyncNetwork::ExecuteSessionCommand( const char *sessCmd ) {
if ( sessCmd[ 0 ] ) {
if ( !idStr::Icmp( sessCmd, "game_startmenu" ) ) {
session->SetGUI( game->StartMenu(), NULL );
}
}
}
/*
=================
idAsyncNetwork::UpdateUI_f
=================
*/
void idAsyncNetwork::UpdateUI_f( const idCmdArgs &args ) {
if ( args.Argc() != 2 ) {
common->Warning( "idAsyncNetwork::UpdateUI_f: wrong arguments\n" );
return;
}
if ( !server.IsActive() ) {
common->Warning( "idAsyncNetwork::UpdateUI_f: server is not active\n" );
return;
}
int clientNum = atoi( args.Args( 1 ) );
server.UpdateUI( clientNum );
}
/*
===============
idAsyncNetwork::BuildInvalidKeyMsg
===============
*/
void idAsyncNetwork::BuildInvalidKeyMsg( idStr &msg, bool valid[ 2 ] ) {
if ( !valid[ 0 ] ) {
msg += common->GetLanguageDict()->GetString( "#str_07194" );
}
if ( fileSystem->HasD3XP() && !valid[ 1 ] ) {
if ( msg.Length() ) {
msg += "\n";
}
msg += common->GetLanguageDict()->GetString( "#str_07195" );
}
msg += "\n";
msg += common->GetLanguageDict()->GetString( "#str_04304" );
}

View File

@@ -0,0 +1,219 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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.
===========================================================================
*/
#ifndef __ASYNCNETWORK_H__
#define __ASYNCNETWORK_H__
/*
DOOM III gold: 33
1.1 beta patch: 34
1.1 patch: 35
1.2 XP: 36-39
1.3 patch: 40
1.3.1: 41
*/
const int ASYNC_PROTOCOL_MINOR = 41;
const int ASYNC_PROTOCOL_VERSION = ( ASYNC_PROTOCOL_MAJOR << 16 ) + ASYNC_PROTOCOL_MINOR;
#define MAJOR_VERSION(v) ( v >> 16 )
const int MAX_ASYNC_CLIENTS = 32;
const int MAX_USERCMD_BACKUP = 256;
const int MAX_USERCMD_DUPLICATION = 25;
const int MAX_USERCMD_RELAY = 10;
// index 0 is hardcoded to be the idnet master
// which leaves 4 to user customization
const int MAX_MASTER_SERVERS = 5;
const int MAX_NICKLEN = 32;
// max number of servers that will be scanned for at a single IP address
const int MAX_SERVER_PORTS = 8;
// special game init ids
const int GAME_INIT_ID_INVALID = -1;
const int GAME_INIT_ID_MAP_LOAD = -2;
#include "MsgChannel.h"
#include "AsyncServer.h"
#include "ServerScan.h"
#include "AsyncClient.h"
/*
===============================================================================
Asynchronous Networking.
===============================================================================
*/
// unreliable server -> client messages
enum {
SERVER_UNRELIABLE_MESSAGE_EMPTY = 0,
SERVER_UNRELIABLE_MESSAGE_PING,
SERVER_UNRELIABLE_MESSAGE_GAMEINIT,
SERVER_UNRELIABLE_MESSAGE_SNAPSHOT
};
// reliable server -> client messages
enum {
SERVER_RELIABLE_MESSAGE_PURE = 0,
SERVER_RELIABLE_MESSAGE_RELOAD,
SERVER_RELIABLE_MESSAGE_CLIENTINFO,
SERVER_RELIABLE_MESSAGE_SYNCEDCVARS,
SERVER_RELIABLE_MESSAGE_PRINT,
SERVER_RELIABLE_MESSAGE_DISCONNECT,
SERVER_RELIABLE_MESSAGE_APPLYSNAPSHOT,
SERVER_RELIABLE_MESSAGE_GAME,
SERVER_RELIABLE_MESSAGE_ENTERGAME
};
// unreliable client -> server messages
enum {
CLIENT_UNRELIABLE_MESSAGE_EMPTY = 0,
CLIENT_UNRELIABLE_MESSAGE_PINGRESPONSE,
CLIENT_UNRELIABLE_MESSAGE_USERCMD
};
// reliable client -> server messages
enum {
CLIENT_RELIABLE_MESSAGE_PURE = 0,
CLIENT_RELIABLE_MESSAGE_CLIENTINFO,
CLIENT_RELIABLE_MESSAGE_PRINT,
CLIENT_RELIABLE_MESSAGE_DISCONNECT,
CLIENT_RELIABLE_MESSAGE_GAME
};
// server print messages
enum {
SERVER_PRINT_MISC = 0,
SERVER_PRINT_BADPROTOCOL,
SERVER_PRINT_RCON,
SERVER_PRINT_GAMEDENY,
SERVER_PRINT_BADCHALLENGE
};
enum {
SERVER_DL_REDIRECT = 1,
SERVER_DL_LIST,
SERVER_DL_NONE
};
enum {
SERVER_PAK_NO = 0,
SERVER_PAK_YES,
SERVER_PAK_END
};
typedef struct master_s {
idCVar * var;
netadr_t address;
bool resolved;
} master_t;
class idAsyncNetwork {
public:
idAsyncNetwork();
static void Init( void );
static void Shutdown( void );
static bool IsActive( void ) { return ( server.IsActive() || client.IsActive() ); }
static void RunFrame( void );
static void WriteUserCmdDelta( idBitMsg &msg, const usercmd_t &cmd, const usercmd_t *base );
static void ReadUserCmdDelta( const idBitMsg &msg, usercmd_t &cmd, const usercmd_t *base );
static bool DuplicateUsercmd( const usercmd_t &previousUserCmd, usercmd_t &currentUserCmd, int frame, int time );
static bool UsercmdInputChanged( const usercmd_t &previousUserCmd, const usercmd_t &currentUserCmd );
// returns true if the corresponding master is set to something (and could be resolved)
static bool GetMasterAddress( int index, netadr_t &adr );
// get the hardcoded idnet master, equivalent to GetMasterAddress( 0, .. )
static netadr_t GetMasterAddress( void );
static void GetNETServers( );
static void ExecuteSessionCommand( const char *sessCmd );
static idAsyncServer server;
static idAsyncClient client;
static idCVar verbose; // verbose output
static idCVar allowCheats; // allow cheats
static idCVar serverDedicated; // if set run a dedicated server
static idCVar serverSnapshotDelay; // number of milliseconds between snapshots
static idCVar serverMaxClientRate; // maximum outgoing rate to clients
static idCVar clientMaxRate; // maximum rate from server requested by client
static idCVar serverMaxUsercmdRelay; // maximum number of usercmds relayed to other clients
static idCVar serverZombieTimeout; // time out in seconds for zombie clients
static idCVar serverClientTimeout; // time out in seconds for connected clients
static idCVar clientServerTimeout; // time out in seconds for server
static idCVar serverDrawClient; // the server draws the view of this client
static idCVar serverRemoteConsolePassword; // remote console password
static idCVar clientPrediction; // how many additional milliseconds the clients runs ahead
static idCVar clientMaxPrediction; // max milliseconds into the future a client can run prediction
static idCVar clientUsercmdBackup; // how many usercmds the client sends from previous frames
static idCVar clientRemoteConsoleAddress; // remote console address
static idCVar clientRemoteConsolePassword; // remote console password
static idCVar master0; // idnet master server
static idCVar master1; // 1st master server
static idCVar master2; // 2nd master server
static idCVar master3; // 3rd master server
static idCVar master4; // 4th master server
static idCVar LANServer; // LAN mode
static idCVar serverReloadEngine; // reload engine on map change instead of growing the referenced paks
static idCVar serverAllowServerMod; // let a pure server start with a different game code than what is referenced in game code
static idCVar idleServer; // serverinfo reply, indicates all clients are idle
static idCVar clientDownload; // preferred download policy
// same message used for offline check and network reply
static void BuildInvalidKeyMsg( idStr &msg, bool valid[ 2 ] );
private:
static int realTime;
static master_t masters[ MAX_MASTER_SERVERS]; // master1 etc.
static void SpawnServer_f( const idCmdArgs &args );
static void NextMap_f( const idCmdArgs &args );
static void Connect_f( const idCmdArgs &args );
static void Reconnect_f( const idCmdArgs &args );
static void GetServerInfo_f( const idCmdArgs &args );
static void GetLANServers_f( const idCmdArgs &args );
static void ListServers_f( const idCmdArgs &args );
static void RemoteConsole_f( const idCmdArgs &args );
static void Heartbeat_f( const idCmdArgs &args );
static void Kick_f( const idCmdArgs &args );
static void CheckNewVersion_f( const idCmdArgs &args );
static void UpdateUI_f( const idCmdArgs &args );
};
#endif /* !__ASYNCNETWORK_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,259 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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.
===========================================================================
*/
#ifndef __ASYNCSERVER_H__
#define __ASYNCSERVER_H__
/*
===============================================================================
Network Server for asynchronous networking.
===============================================================================
*/
// MAX_CHALLENGES is made large to prevent a denial of service attack that could cycle
// all of them out before legitimate users connected
const int MAX_CHALLENGES = 1024;
// if we don't hear from authorize server, assume it is down
const int AUTHORIZE_TIMEOUT = 5000;
// states for the server's authorization process
typedef enum {
CDK_WAIT = 0, // we are waiting for a confirm/deny from auth
// this is subject to timeout if we don't hear from auth
// or a permanent wait if auth said so
CDK_OK,
CDK_ONLYLAN,
CDK_PUREWAIT,
CDK_PUREOK,
CDK_MAXSTATES
} authState_t;
// states from the auth server, while the client is in CDK_WAIT
typedef enum {
AUTH_NONE = 0, // no reply yet
AUTH_OK, // this client is good
AUTH_WAIT, // wait - keep sending me srvAuth though
AUTH_DENY, // denied - don't send me anything about this client anymore
AUTH_MAXSTATES
} authReply_t;
// message from auth to be forwarded back to the client
// some are locally hardcoded to save space, auth has the possibility to send a custom reply
typedef enum {
AUTH_REPLY_WAITING = 0, // waiting on an initial reply from auth
AUTH_REPLY_UNKNOWN, // client unknown to auth
AUTH_REPLY_DENIED, // access denied
AUTH_REPLY_PRINT, // custom message
AUTH_REPLY_SRVWAIT, // auth server replied and tells us he's working on it
AUTH_REPLY_MAXSTATES
} authReplyMsg_t;
typedef struct challenge_s {
netadr_t address; // client address
int clientId; // client identification
int challenge; // challenge code
int time; // time the challenge was created
int pingTime; // time the challenge response was sent to client
bool connected; // true if the client is connected
authState_t authState; // local state regarding the client
authReply_t authReply; // cd key check replies
authReplyMsg_t authReplyMsg; // default auth messages
idStr authReplyPrint; // custom msg
char guid[12]; // guid
int OS;
} challenge_t;
typedef enum {
SCS_FREE, // can be reused for a new connection
SCS_ZOMBIE, // client has been disconnected, but don't reuse connection for a couple seconds
SCS_PUREWAIT, // client needs to update it's pure checksums before we can go further
SCS_CONNECTED, // client is connected
SCS_INGAME // client is in the game
} serverClientState_t;
typedef struct serverClient_s {
int OS;
int clientId;
serverClientState_t clientState;
int clientPrediction;
int clientAheadTime;
int clientRate;
int clientPing;
int gameInitSequence;
int gameFrame;
int gameTime;
idMsgChannel channel;
int lastConnectTime;
int lastEmptyTime;
int lastPingTime;
int lastSnapshotTime;
int lastPacketTime;
int lastInputTime;
int snapshotSequence;
int acknowledgeSnapshotSequence;
int numDuplicatedUsercmds;
char guid[12]; // Even Balance - M. Quinn
} serverClient_t;
class idAsyncServer {
public:
idAsyncServer();
bool InitPort( void );
void ClosePort( void );
void Spawn( void );
void Kill( void );
void ExecuteMapChange( void );
int GetPort( void ) const;
netadr_t GetBoundAdr( void ) const;
bool IsActive( void ) const { return active; }
int GetDelay( void ) const { return gameTimeResidual; }
int GetOutgoingRate( void ) const;
int GetIncomingRate( void ) const;
bool IsClientInGame( int clientNum ) const;
int GetClientPing( int clientNum ) const;
int GetClientPrediction( int clientNum ) const;
int GetClientTimeSinceLastPacket( int clientNum ) const;
int GetClientTimeSinceLastInput( int clientNum ) const;
int GetClientOutgoingRate( int clientNum ) const;
int GetClientIncomingRate( int clientNum ) const;
float GetClientOutgoingCompression( int clientNum ) const;
float GetClientIncomingCompression( int clientNum ) const;
float GetClientIncomingPacketLoss( int clientNum ) const;
int GetNumClients( void ) const;
int GetNumIdleClients( void ) const;
int GetLocalClientNum( void ) const { return localClientNum; }
void RunFrame( void );
void ProcessConnectionLessMessages( void );
void RemoteConsoleOutput( const char *string );
void SendReliableGameMessage( int clientNum, const idBitMsg &msg );
void SendReliableGameMessageExcluding( int clientNum, const idBitMsg &msg );
void LocalClientSendReliableMessage( const idBitMsg &msg );
void MasterHeartbeat( bool force = false );
void DropClient( int clientNum, const char *reason );
void PacifierUpdate( void );
void UpdateUI( int clientNum );
void UpdateAsyncStatsAvg( void );
void GetAsyncStatsAvgMsg( idStr &msg );
void PrintLocalServerInfo( void );
private:
bool active; // true if server is active
int realTime; // absolute time
int serverTime; // local server time
idPort serverPort; // UDP port
int serverId; // server identification
int serverDataChecksum; // checksum of the data used by the server
int localClientNum; // local client on listen server
challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting
serverClient_t clients[MAX_ASYNC_CLIENTS]; // clients
usercmd_t userCmds[MAX_USERCMD_BACKUP][MAX_ASYNC_CLIENTS];
int gameInitId; // game initialization identification
int gameFrame; // local game frame
int gameTime; // local game time
int gameTimeResidual; // left over time from previous frame
netadr_t rconAddress;
int nextHeartbeatTime;
int nextAsyncStatsTime;
bool serverReloadingEngine; // flip-flop to not loop over when net_serverReloadEngine is on
bool noRconOutput; // for default rcon response when command is silent
int lastAuthTime; // global for auth server timeout
// track the max outgoing rate over the last few secs to watch for spikes
// dependent on net_serverSnapshotDelay. 50ms, for a 3 seconds backlog -> 60 samples
static const int stats_numsamples = 60;
int stats_outrate[ stats_numsamples ];
int stats_current;
int stats_average_sum;
int stats_max;
int stats_max_index;
void PrintOOB( const netadr_t to, int opcode, const char *string );
void DuplicateUsercmds( int frame, int time );
void ClearClient( int clientNum );
void InitClient( int clientNum, int clientId, int clientRate );
void InitLocalClient( int clientNum );
void BeginLocalClient( void );
void LocalClientInput( void );
void CheckClientTimeouts( void );
void SendPrintBroadcast( const char *string );
void SendPrintToClient( int clientNum, const char *string );
void SendUserInfoBroadcast( int userInfoNum, const idDict &info, bool sendToAll = false );
void SendUserInfoToClient( int clientNum, int userInfoNum, const idDict &info );
void SendSyncedCvarsBroadcast( const idDict &cvars );
void SendSyncedCvarsToClient( int clientNum, const idDict &cvars );
void SendApplySnapshotToClient( int clientNum, int sequence );
bool SendEmptyToClient( int clientNum, bool force = false );
bool SendPingToClient( int clientNum );
void SendGameInitToClient( int clientNum );
bool SendSnapshotToClient( int clientNum );
void ProcessUnreliableClientMessage( int clientNum, const idBitMsg &msg );
void ProcessReliableClientMessages( int clientNum );
void ProcessChallengeMessage( const netadr_t from, const idBitMsg &msg );
void ProcessConnectMessage( const netadr_t from, const idBitMsg &msg );
void ProcessRemoteConsoleMessage( const netadr_t from, const idBitMsg &msg );
void ProcessGetInfoMessage( const netadr_t from, const idBitMsg &msg );
bool ConnectionlessMessage( const netadr_t from, const idBitMsg &msg );
bool ProcessMessage( const netadr_t from, idBitMsg &msg );
void ProcessAuthMessage( const idBitMsg &msg );
bool SendPureServerMessage( const netadr_t to, int OS ); // returns false if no pure paks on the list
void ProcessPureMessage( const netadr_t from, const idBitMsg &msg );
int ValidateChallenge( const netadr_t from, int challenge, int clientId ); // returns -1 if validate failed
bool SendReliablePureToClient( int clientNum );
void ProcessReliablePure( int clientNum, const idBitMsg &msg );
bool VerifyChecksumMessage( int clientNum, const netadr_t *from, const idBitMsg &msg, idStr &reply, int OS ); // if from is NULL, clientNum is used for error messages
void SendReliableMessage( int clientNum, const idBitMsg &msg ); // checks for overflow and disconnects the faulty client
int UpdateTime( int clamp );
void SendEnterGameToClient( int clientNum );
void ProcessDownloadRequestMessage( const netadr_t from, const idBitMsg &msg );
};
#endif /* !__ASYNCSERVER_H__ */

View File

@@ -0,0 +1,790 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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.
===========================================================================
*/
#include "../../idlib/precompiled.h"
#pragma hdrstop
#include "MsgChannel.h"
/*
packet header
-------------
2 bytes id
4 bytes outgoing sequence. high bit will be set if this is a fragmented message.
2 bytes optional fragment start byte if fragment bit is set.
2 bytes optional fragment length if fragment bit is set. if < FRAGMENT_SIZE, this is the last fragment.
If the id is -1, the packet should be handled as an out-of-band
message instead of as part of the message channel.
All fragments will have the same sequence numbers.
*/
#define MAX_PACKETLEN 1400 // max size of a network packet
#define FRAGMENT_SIZE (MAX_PACKETLEN - 100)
#define FRAGMENT_BIT (1<<31)
idCVar net_channelShowPackets( "net_channelShowPackets", "0", CVAR_SYSTEM | CVAR_BOOL, "show all packets" );
idCVar net_channelShowDrop( "net_channelShowDrop", "0", CVAR_SYSTEM | CVAR_BOOL, "show dropped packets" );
/*
===============
idMsgQueue::idMsgQueue
===============
*/
idMsgQueue::idMsgQueue( void ) {
Init( 0 );
}
/*
===============
idMsgQueue::Init
===============
*/
void idMsgQueue::Init( int sequence ) {
first = last = sequence;
startIndex = endIndex = 0;
}
/*
===============
idMsgQueue::Add
===============
*/
bool idMsgQueue::Add( const byte *data, const int size ) {
if ( GetSpaceLeft() < size + 8 ) {
return false;
}
int sequence = last;
WriteShort( size );
WriteLong( sequence );
WriteData( data, size );
last++;
return true;
}
/*
===============
idMsgQueue::Get
===============
*/
bool idMsgQueue::Get( byte *data, int &size ) {
if ( first == last ) {
size = 0;
return false;
}
int sequence;
size = ReadShort();
sequence = ReadLong();
ReadData( data, size );
assert( sequence == first );
first++;
return true;
}
/*
===============
idMsgQueue::GetTotalSize
===============
*/
int idMsgQueue::GetTotalSize( void ) const {
if ( startIndex <= endIndex ) {
return ( endIndex - startIndex );
} else {
return ( sizeof( buffer ) - startIndex + endIndex );
}
}
/*
===============
idMsgQueue::GetSpaceLeft
===============
*/
int idMsgQueue::GetSpaceLeft( void ) const {
if ( startIndex <= endIndex ) {
return sizeof( buffer ) - ( endIndex - startIndex ) - 1;
} else {
return ( startIndex - endIndex ) - 1;
}
}
/*
===============
idMsgQueue::CopyToBuffer
===============
*/
void idMsgQueue::CopyToBuffer( byte *buf ) const {
if ( startIndex <= endIndex ) {
memcpy( buf, buffer + startIndex, endIndex - startIndex );
} else {
memcpy( buf, buffer + startIndex, sizeof( buffer ) - startIndex );
memcpy( buf + sizeof( buffer ) - startIndex, buffer, endIndex );
}
}
/*
===============
idMsgQueue::WriteByte
===============
*/
void idMsgQueue::WriteByte( byte b ) {
buffer[endIndex] = b;
endIndex = ( endIndex + 1 ) & ( MAX_MSG_QUEUE_SIZE - 1 );
}
/*
===============
idMsgQueue::ReadByte
===============
*/
byte idMsgQueue::ReadByte( void ) {
byte b = buffer[startIndex];
startIndex = ( startIndex + 1 ) & ( MAX_MSG_QUEUE_SIZE - 1 );
return b;
}
/*
===============
idMsgQueue::WriteShort
===============
*/
void idMsgQueue::WriteShort( int s ) {
WriteByte( ( s >> 0 ) & 255 );
WriteByte( ( s >> 8 ) & 255 );
}
/*
===============
idMsgQueue::ReadShort
===============
*/
int idMsgQueue::ReadShort( void ) {
return ReadByte() | ( ReadByte() << 8 );
}
/*
===============
idMsgQueue::WriteLong
===============
*/
void idMsgQueue::WriteLong( int l ) {
WriteByte( ( l >> 0 ) & 255 );
WriteByte( ( l >> 8 ) & 255 );
WriteByte( ( l >> 16 ) & 255 );
WriteByte( ( l >> 24 ) & 255 );
}
/*
===============
idMsgQueue::ReadLong
===============
*/
int idMsgQueue::ReadLong( void ) {
return ReadByte() | ( ReadByte() << 8 ) | ( ReadByte() << 16 ) | ( ReadByte() << 24 );
}
/*
===============
idMsgQueue::WriteData
===============
*/
void idMsgQueue::WriteData( const byte *data, const int size ) {
for ( int i = 0; i < size; i++ ) {
WriteByte( data[i] );
}
}
/*
===============
idMsgQueue::ReadData
===============
*/
void idMsgQueue::ReadData( byte *data, const int size ) {
if ( data ) {
for ( int i = 0; i < size; i++ ) {
data[i] = ReadByte();
}
} else {
for ( int i = 0; i < size; i++ ) {
ReadByte();
}
}
}
/*
===============
idMsgChannel::idMsgChannel
===============
*/
idMsgChannel::idMsgChannel() {
id = -1;
}
/*
==============
idMsgChannel::Init
Opens a channel to a remote system.
==============
*/
void idMsgChannel::Init( const netadr_t adr, const int id ) {
this->remoteAddress = adr;
this->id = id;
this->maxRate = 50000;
this->compressor = idCompressor::AllocRunLength_ZeroBased();
lastSendTime = 0;
lastDataBytes = 0;
outgoingRateTime = 0;
outgoingRateBytes = 0;
incomingRateTime = 0;
incomingRateBytes = 0;
incomingReceivedPackets = 0.0f;
incomingDroppedPackets = 0.0f;
incomingPacketLossTime = 0;
outgoingCompression = 0.0f;
incomingCompression = 0.0f;
outgoingSequence = 1;
incomingSequence = 0;
unsentFragments = false;
unsentFragmentStart = 0;
fragmentSequence = 0;
fragmentLength = 0;
reliableSend.Init( 1 );
reliableReceive.Init( 0 );
}
/*
===============
idMsgChannel::Shutdown
================
*/
void idMsgChannel::Shutdown( void ) {
delete compressor;
compressor = NULL;
}
/*
=================
idMsgChannel::ResetRate
=================
*/
void idMsgChannel::ResetRate( void ) {
lastSendTime = 0;
lastDataBytes = 0;
outgoingRateTime = 0;
outgoingRateBytes = 0;
incomingRateTime = 0;
incomingRateBytes = 0;
}
/*
=================
idMsgChannel::ReadyToSend
=================
*/
bool idMsgChannel::ReadyToSend( const int time ) const {
int deltaTime;
if ( !maxRate ) {
return true;
}
deltaTime = time - lastSendTime;
if ( deltaTime > 1000 ) {
return true;
}
return ( ( lastDataBytes - ( deltaTime * maxRate ) / 1000 ) <= 0 );
}
/*
===============
idMsgChannel::WriteMessageData
================
*/
void idMsgChannel::WriteMessageData( idBitMsg &out, const idBitMsg &msg ) {
idBitMsg tmp;
byte tmpBuf[MAX_MESSAGE_SIZE];
tmp.Init( tmpBuf, sizeof( tmpBuf ) );
// write acknowledgement of last received reliable message
tmp.WriteLong( reliableReceive.GetLast() );
// write reliable messages
reliableSend.CopyToBuffer( tmp.GetData() + tmp.GetSize() );
tmp.SetSize( tmp.GetSize() + reliableSend.GetTotalSize() );
tmp.WriteShort( 0 );
// write data
tmp.WriteData( msg.GetData(), msg.GetSize() );
// write message size
out.WriteShort( tmp.GetSize() );
// compress message
idFile_BitMsg file( out );
compressor->Init( &file, true, 3 );
compressor->Write( tmp.GetData(), tmp.GetSize() );
compressor->FinishCompress();
outgoingCompression = compressor->GetCompressionRatio();
}
/*
===============
idMsgChannel::ReadMessageData
================
*/
bool idMsgChannel::ReadMessageData( idBitMsg &out, const idBitMsg &msg ) {
int reliableAcknowledge, reliableMessageSize, reliableSequence;
// read message size
out.SetSize( msg.ReadShort() );
// decompress message
idFile_BitMsg file( msg );
compressor->Init( &file, false, 3 );
compressor->Read( out.GetData(), out.GetSize() );
incomingCompression = compressor->GetCompressionRatio();
out.BeginReading();
// read acknowledgement of sent reliable messages
reliableAcknowledge = out.ReadLong();
// remove acknowledged reliable messages
while( reliableSend.GetFirst() <= reliableAcknowledge ) {
if ( !reliableSend.Get( NULL, reliableMessageSize ) ) {
break;
}
}
// read reliable messages
reliableMessageSize = out.ReadShort();
while( reliableMessageSize != 0 ) {
if ( reliableMessageSize <= 0 || reliableMessageSize > out.GetSize() - out.GetReadCount() ) {
common->Printf( "%s: bad reliable message\n", Sys_NetAdrToString( remoteAddress ) );
return false;
}
reliableSequence = out.ReadLong();
if ( reliableSequence == reliableReceive.GetLast() + 1 ) {
reliableReceive.Add( out.GetData() + out.GetReadCount(), reliableMessageSize );
}
out.ReadData( NULL, reliableMessageSize );
reliableMessageSize = out.ReadShort();
}
return true;
}
/*
=================
idMsgChannel::SendNextFragment
Sends one fragment of the current message.
=================
*/
void idMsgChannel::SendNextFragment( idPort &port, const int time ) {
idBitMsg msg;
byte msgBuf[MAX_PACKETLEN];
int fragLength;
if ( remoteAddress.type == NA_BAD ) {
return;
}
if ( !unsentFragments ) {
return;
}
// write the packet
msg.Init( msgBuf, sizeof( msgBuf ) );
msg.WriteShort( id );
msg.WriteLong( outgoingSequence | FRAGMENT_BIT );
fragLength = FRAGMENT_SIZE;
if ( unsentFragmentStart + fragLength > unsentMsg.GetSize() ) {
fragLength = unsentMsg.GetSize() - unsentFragmentStart;
}
msg.WriteShort( unsentFragmentStart );
msg.WriteShort( fragLength );
msg.WriteData( unsentMsg.GetData() + unsentFragmentStart, fragLength );
// send the packet
port.SendPacket( remoteAddress, msg.GetData(), msg.GetSize() );
// update rate control variables
UpdateOutgoingRate( time, msg.GetSize() );
if ( net_channelShowPackets.GetBool() ) {
common->Printf( "%d send %4i : s = %i fragment = %i,%i\n", id, msg.GetSize(), outgoingSequence - 1, unsentFragmentStart, fragLength );
}
unsentFragmentStart += fragLength;
// this exit condition is a little tricky, because a packet
// that is exactly the fragment length still needs to send
// a second packet of zero length so that the other side
// can tell there aren't more to follow
if ( unsentFragmentStart == unsentMsg.GetSize() && fragLength != FRAGMENT_SIZE ) {
outgoingSequence++;
unsentFragments = false;
}
}
/*
===============
idMsgChannel::SendMessage
Sends a message to a connection, fragmenting if necessary
A 0 length will still generate a packet.
================
*/
int idMsgChannel::SendMessage( idPort &port, const int time, const idBitMsg &msg ) {
int totalLength;
if ( remoteAddress.type == NA_BAD ) {
return -1;
}
if ( unsentFragments ) {
common->Error( "idMsgChannel::SendMessage: called with unsent fragments left" );
return -1;
}
totalLength = 4 + reliableSend.GetTotalSize() + 4 + msg.GetSize();
if ( totalLength > MAX_MESSAGE_SIZE ) {
common->Printf( "idMsgChannel::SendMessage: message too large, length = %i\n", totalLength );
return -1;
}
unsentMsg.Init( unsentBuffer, sizeof( unsentBuffer ) );
unsentMsg.BeginWriting();
// fragment large messages
if ( totalLength >= FRAGMENT_SIZE ) {
unsentFragments = true;
unsentFragmentStart = 0;
// write out the message data
WriteMessageData( unsentMsg, msg );
// send the first fragment now
SendNextFragment( port, time );
return outgoingSequence;
}
// write the header
unsentMsg.WriteShort( id );
unsentMsg.WriteLong( outgoingSequence );
// write out the message data
WriteMessageData( unsentMsg, msg );
// send the packet
port.SendPacket( remoteAddress, unsentMsg.GetData(), unsentMsg.GetSize() );
// update rate control variables
UpdateOutgoingRate( time, unsentMsg.GetSize() );
if ( net_channelShowPackets.GetBool() ) {
common->Printf( "%d send %4i : s = %i ack = %i\n", id, unsentMsg.GetSize(), outgoingSequence - 1, incomingSequence );
}
outgoingSequence++;
return ( outgoingSequence - 1 );
}
/*
=================
idMsgChannel::Process
Returns false if the message should not be processed due to being out of order or a fragment.
msg must be large enough to hold MAX_MESSAGE_SIZE, because if this is the final
fragment of a multi-part message, the entire thing will be copied out.
=================
*/
bool idMsgChannel::Process( const netadr_t from, int time, idBitMsg &msg, int &sequence ) {
int fragStart, fragLength, dropped;
bool fragmented;
idBitMsg fragMsg;
// the IP port can't be used to differentiate them, because
// some address translating routers periodically change UDP
// port assignments
if ( remoteAddress.port != from.port ) {
common->Printf( "idMsgChannel::Process: fixing up a translated port\n" );
remoteAddress.port = from.port;
}
// update incoming rate
UpdateIncomingRate( time, msg.GetSize() );
// get sequence numbers
sequence = msg.ReadLong();
// check for fragment information
if ( sequence & FRAGMENT_BIT ) {
sequence &= ~FRAGMENT_BIT;
fragmented = true;
} else {
fragmented = false;
}
// read the fragment information
if ( fragmented ) {
fragStart = msg.ReadShort();
fragLength = msg.ReadShort();
} else {
fragStart = 0; // stop warning message
fragLength = 0;
}
if ( net_channelShowPackets.GetBool() ) {
if ( fragmented ) {
common->Printf( "%d recv %4i : s = %i fragment = %i,%i\n", id, msg.GetSize(), sequence, fragStart, fragLength );
} else {
common->Printf( "%d recv %4i : s = %i\n", id, msg.GetSize(), sequence );
}
}
//
// discard out of order or duplicated packets
//
if ( sequence <= incomingSequence ) {
if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
common->Printf( "%s: out of order packet %i at %i\n", Sys_NetAdrToString( remoteAddress ), sequence, incomingSequence );
}
return false;
}
//
// dropped packets don't keep this message from being used
//
dropped = sequence - (incomingSequence+1);
if ( dropped > 0 ) {
if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
common->Printf( "%s: dropped %i packets at %i\n", Sys_NetAdrToString( remoteAddress ), dropped, sequence );
}
UpdatePacketLoss( time, 0, dropped );
}
//
// if the message is fragmented
//
if ( fragmented ) {
// make sure we have the correct sequence number
if ( sequence != fragmentSequence ) {
fragmentSequence = sequence;
fragmentLength = 0;
}
// if we missed a fragment, dump the message
if ( fragStart != fragmentLength ) {
if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
common->Printf( "%s: dropped a message fragment at seq %d\n", Sys_NetAdrToString( remoteAddress ), sequence );
}
// we can still keep the part that we have so far,
// so we don't need to clear fragmentLength
UpdatePacketLoss( time, 0, 1 );
return false;
}
// copy the fragment to the fragment buffer
if ( fragLength < 0 || fragLength > msg.GetRemaingData() || fragmentLength + fragLength > sizeof( fragmentBuffer ) ) {
if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
common->Printf( "%s: illegal fragment length\n", Sys_NetAdrToString( remoteAddress ) );
}
UpdatePacketLoss( time, 0, 1 );
return false;
}
memcpy( fragmentBuffer + fragmentLength, msg.GetData() + msg.GetReadCount(), fragLength );
fragmentLength += fragLength;
UpdatePacketLoss( time, 1, 0 );
// if this wasn't the last fragment, don't process anything
if ( fragLength == FRAGMENT_SIZE ) {
return false;
}
} else {
memcpy( fragmentBuffer, msg.GetData() + msg.GetReadCount(), msg.GetRemaingData() );
fragmentLength = msg.GetRemaingData();
UpdatePacketLoss( time, 1, 0 );
}
fragMsg.Init( fragmentBuffer, fragmentLength );
fragMsg.SetSize( fragmentLength );
fragMsg.BeginReading();
incomingSequence = sequence;
// read the message data
if ( !ReadMessageData( msg, fragMsg ) ) {
return false;
}
return true;
}
/*
=================
idMsgChannel::SendReliableMessage
=================
*/
bool idMsgChannel::SendReliableMessage( const idBitMsg &msg ) {
bool result;
assert( remoteAddress.type != NA_BAD );
if ( remoteAddress.type == NA_BAD ) {
return false;
}
result = reliableSend.Add( msg.GetData(), msg.GetSize() );
if ( !result ) {
common->Warning( "idMsgChannel::SendReliableMessage: overflowed" );
return false;
}
return result;
}
/*
=================
idMsgChannel::GetReliableMessage
=================
*/
bool idMsgChannel::GetReliableMessage( idBitMsg &msg ) {
int size;
bool result;
result = reliableReceive.Get( msg.GetData(), size );
msg.SetSize( size );
msg.BeginReading();
return result;
}
/*
===============
idMsgChannel::ClearReliableMessages
================
*/
void idMsgChannel::ClearReliableMessages( void ) {
reliableSend.Init( 1 );
reliableReceive.Init( 0 );
}
/*
=================
idMsgChannel::UpdateOutgoingRate
=================
*/
void idMsgChannel::UpdateOutgoingRate( const int time, const int size ) {
// update the outgoing rate control variables
int deltaTime = time - lastSendTime;
if ( deltaTime > 1000 ) {
lastDataBytes = 0;
} else {
lastDataBytes -= ( deltaTime * maxRate ) / 1000;
if ( lastDataBytes < 0 ) {
lastDataBytes = 0;
}
}
lastDataBytes += size;
lastSendTime = time;
// update outgoing rate variables
if ( time - outgoingRateTime > 1000 ) {
outgoingRateBytes -= outgoingRateBytes * ( time - outgoingRateTime - 1000 ) / 1000;
if ( outgoingRateBytes < 0 ) {
outgoingRateBytes = 0;
}
}
outgoingRateTime = time - 1000;
outgoingRateBytes += size;
}
/*
=================
idMsgChannel::UpdateIncomingRate
=================
*/
void idMsgChannel::UpdateIncomingRate( const int time, const int size ) {
// update incoming rate variables
if ( time - incomingRateTime > 1000 ) {
incomingRateBytes -= incomingRateBytes * ( time - incomingRateTime - 1000 ) / 1000;
if ( incomingRateBytes < 0 ) {
incomingRateBytes = 0;
}
}
incomingRateTime = time - 1000;
incomingRateBytes += size;
}
/*
=================
idMsgChannel::UpdatePacketLoss
=================
*/
void idMsgChannel::UpdatePacketLoss( const int time, const int numReceived, const int numDropped ) {
// update incoming packet loss variables
if ( time - incomingPacketLossTime > 5000 ) {
float scale = ( time - incomingPacketLossTime - 5000 ) * ( 1.0f / 5000.0f );
incomingReceivedPackets -= incomingReceivedPackets * scale;
if ( incomingReceivedPackets < 0.0f ) {
incomingReceivedPackets = 0.0f;
}
incomingDroppedPackets -= incomingDroppedPackets * scale;
if ( incomingDroppedPackets < 0.0f ) {
incomingDroppedPackets = 0.0f;
}
}
incomingPacketLossTime = time - 5000;
incomingReceivedPackets += numReceived;
incomingDroppedPackets += numDropped;
}
/*
=================
idMsgChannel::GetIncomingPacketLoss
=================
*/
float idMsgChannel::GetIncomingPacketLoss( void ) const {
if ( incomingReceivedPackets == 0.0f && incomingDroppedPackets == 0.0f ) {
return 0.0f;
}
return incomingDroppedPackets * 100.0f / ( incomingReceivedPackets + incomingDroppedPackets );
}

View File

@@ -0,0 +1,201 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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.
===========================================================================
*/
#ifndef __MSGCHANNEL_H__
#define __MSGCHANNEL_H__
/*
===============================================================================
Network channel.
Handles message fragmentation and out of order / duplicate suppression.
Unreliable messages are not garrenteed to arrive but when they do, they
arrive in order and without duplicates. Reliable messages always arrive,
and they also arrive in order without duplicates. Reliable messages piggy
back on unreliable messages. As such an unreliable message stream is
required for the reliable messages to be delivered.
===============================================================================
*/
#define MAX_MESSAGE_SIZE 16384 // max length of a message, which may
// be fragmented into multiple packets
#define CONNECTIONLESS_MESSAGE_ID -1 // id for connectionless messages
#define CONNECTIONLESS_MESSAGE_ID_MASK 0x7FFF // value to mask away connectionless message id
#define MAX_MSG_QUEUE_SIZE 16384 // must be a power of 2
class idMsgQueue {
public:
idMsgQueue();
void Init( int sequence );
bool Add( const byte *data, const int size );
bool Get( byte *data, int &size );
int GetTotalSize( void ) const;
int GetSpaceLeft( void ) const;
int GetFirst( void ) const { return first; }
int GetLast( void ) const { return last; }
void CopyToBuffer( byte *buf ) const;
private:
byte buffer[MAX_MSG_QUEUE_SIZE];
int first; // sequence number of first message in queue
int last; // sequence number of last message in queue
int startIndex; // index pointing to the first byte of the first message
int endIndex; // index pointing to the first byte after the last message
void WriteByte( byte b );
byte ReadByte( void );
void WriteShort( int s );
int ReadShort( void );
void WriteLong( int l );
int ReadLong( void );
void WriteData( const byte *data, const int size );
void ReadData( byte *data, const int size );
};
class idMsgChannel {
public:
idMsgChannel();
void Init( const netadr_t adr, const int id );
void Shutdown( void );
void ResetRate( void );
// Sets the maximum outgoing rate.
void SetMaxOutgoingRate( int rate ) { maxRate = rate; }
// Gets the maximum outgoing rate.
int GetMaxOutgoingRate( void ) { return maxRate; }
// Returns the address of the entity at the other side of the channel.
netadr_t GetRemoteAddress( void ) const { return remoteAddress; }
// Returns the average outgoing rate over the last second.
int GetOutgoingRate( void ) const { return outgoingRateBytes; }
// Returns the average incoming rate over the last second.
int GetIncomingRate( void ) const { return incomingRateBytes; }
// Returns the average outgoing compression ratio over the last second.
float GetOutgoingCompression( void ) const { return outgoingCompression; }
// Returns the average incoming compression ratio over the last second.
float GetIncomingCompression( void ) const { return incomingCompression; }
// Returns the average incoming packet loss over the last 5 seconds.
float GetIncomingPacketLoss( void ) const;
// Returns true if the channel is ready to send new data based on the maximum rate.
bool ReadyToSend( const int time ) const;
// Sends an unreliable message, in order and without duplicates.
int SendMessage( idPort &port, const int time, const idBitMsg &msg );
// Sends the next fragment if the last message was too large to send at once.
void SendNextFragment( idPort &port, const int time );
// Returns true if there are unsent fragments left.
bool UnsentFragmentsLeft( void ) const { return unsentFragments; }
// Processes the incoming message. Returns true when a complete message
// is ready for further processing. In that case the read pointer of msg
// points to the first byte ready for reading, and sequence is set to
// the sequence number of the message.
bool Process( const netadr_t from, int time, idBitMsg &msg, int &sequence );
// Sends a reliable message, in order and without duplicates.
bool SendReliableMessage( const idBitMsg &msg );
// Returns true if a new reliable message is available and stores the message.
bool GetReliableMessage( idBitMsg &msg );
// Removes any pending outgoing or incoming reliable messages.
void ClearReliableMessages( void );
private:
netadr_t remoteAddress; // address of remote host
int id; // our identification used instead of port number
int maxRate; // maximum number of bytes that may go out per second
idCompressor * compressor; // compressor used for data compression
// variables to control the outgoing rate
int lastSendTime; // last time data was sent out
int lastDataBytes; // bytes left to send at last send time
// variables to keep track of the rate
int outgoingRateTime;
int outgoingRateBytes;
int incomingRateTime;
int incomingRateBytes;
// variables to keep track of the compression ratio
float outgoingCompression;
float incomingCompression;
// variables to keep track of the incoming packet loss
float incomingReceivedPackets;
float incomingDroppedPackets;
int incomingPacketLossTime;
// sequencing variables
int outgoingSequence;
int incomingSequence;
// outgoing fragment buffer
bool unsentFragments;
int unsentFragmentStart;
byte unsentBuffer[MAX_MESSAGE_SIZE];
idBitMsg unsentMsg;
// incoming fragment assembly buffer
int fragmentSequence;
int fragmentLength;
byte fragmentBuffer[MAX_MESSAGE_SIZE];
// reliable messages
idMsgQueue reliableSend;
idMsgQueue reliableReceive;
private:
void WriteMessageData( idBitMsg &out, const idBitMsg &msg );
bool ReadMessageData( idBitMsg &out, const idBitMsg &msg );
void UpdateOutgoingRate( const int time, const int size );
void UpdateIncomingRate( const int time, const int size );
void UpdatePacketLoss( const int time, const int numReceived, const int numDropped );
};
#endif /* !__MSGCHANNEL_H__ */

View File

@@ -0,0 +1,215 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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.
===========================================================================
*/
#include "../../idlib/precompiled.h"
#pragma hdrstop
#include "NetworkSystem.h"
idNetworkSystem networkSystemLocal;
idNetworkSystem * networkSystem = &networkSystemLocal;
/*
==================
idNetworkSystem::ServerSendReliableMessage
==================
*/
void idNetworkSystem::ServerSendReliableMessage( int clientNum, const idBitMsg &msg ) {
if ( idAsyncNetwork::server.IsActive() ) {
idAsyncNetwork::server.SendReliableGameMessage( clientNum, msg );
}
}
/*
==================
idNetworkSystem::ServerSendReliableMessageExcluding
==================
*/
void idNetworkSystem::ServerSendReliableMessageExcluding( int clientNum, const idBitMsg &msg ) {
if ( idAsyncNetwork::server.IsActive() ) {
idAsyncNetwork::server.SendReliableGameMessageExcluding( clientNum, msg );
}
}
/*
==================
idNetworkSystem::ServerGetClientPing
==================
*/
int idNetworkSystem::ServerGetClientPing( int clientNum ) {
if ( idAsyncNetwork::server.IsActive() ) {
return idAsyncNetwork::server.GetClientPing( clientNum );
}
return 0;
}
/*
==================
idNetworkSystem::ServerGetClientPrediction
==================
*/
int idNetworkSystem::ServerGetClientPrediction( int clientNum ) {
if ( idAsyncNetwork::server.IsActive() ) {
return idAsyncNetwork::server.GetClientPrediction( clientNum );
}
return 0;
}
/*
==================
idNetworkSystem::ServerGetClientTimeSinceLastPacket
==================
*/
int idNetworkSystem::ServerGetClientTimeSinceLastPacket( int clientNum ) {
if ( idAsyncNetwork::server.IsActive() ) {
return idAsyncNetwork::server.GetClientTimeSinceLastPacket( clientNum );
}
return 0;
}
/*
==================
idNetworkSystem::ServerGetClientTimeSinceLastInput
==================
*/
int idNetworkSystem::ServerGetClientTimeSinceLastInput( int clientNum ) {
if ( idAsyncNetwork::server.IsActive() ) {
return idAsyncNetwork::server.GetClientTimeSinceLastInput( clientNum );
}
return 0;
}
/*
==================
idNetworkSystem::ServerGetClientOutgoingRate
==================
*/
int idNetworkSystem::ServerGetClientOutgoingRate( int clientNum ) {
if ( idAsyncNetwork::server.IsActive() ) {
return idAsyncNetwork::server.GetClientOutgoingRate( clientNum );
}
return 0;
}
/*
==================
idNetworkSystem::ServerGetClientIncomingRate
==================
*/
int idNetworkSystem::ServerGetClientIncomingRate( int clientNum ) {
if ( idAsyncNetwork::server.IsActive() ) {
return idAsyncNetwork::server.GetClientIncomingRate( clientNum );
}
return 0;
}
/*
==================
idNetworkSystem::ServerGetClientIncomingPacketLoss
==================
*/
float idNetworkSystem::ServerGetClientIncomingPacketLoss( int clientNum ) {
if ( idAsyncNetwork::server.IsActive() ) {
return idAsyncNetwork::server.GetClientIncomingPacketLoss( clientNum );
}
return 0.0f;
}
/*
==================
idNetworkSystem::ClientSendReliableMessage
==================
*/
void idNetworkSystem::ClientSendReliableMessage( const idBitMsg &msg ) {
if ( idAsyncNetwork::client.IsActive() ) {
idAsyncNetwork::client.SendReliableGameMessage( msg );
} else if ( idAsyncNetwork::server.IsActive() ) {
idAsyncNetwork::server.LocalClientSendReliableMessage( msg );
}
}
/*
==================
idNetworkSystem::ClientGetPrediction
==================
*/
int idNetworkSystem::ClientGetPrediction( void ) {
if ( idAsyncNetwork::client.IsActive() ) {
return idAsyncNetwork::client.GetPrediction();
}
return 0;
}
/*
==================
idNetworkSystem::ClientGetTimeSinceLastPacket
==================
*/
int idNetworkSystem::ClientGetTimeSinceLastPacket( void ) {
if ( idAsyncNetwork::client.IsActive() ) {
return idAsyncNetwork::client.GetTimeSinceLastPacket();
}
return 0;
}
/*
==================
idNetworkSystem::ClientGetOutgoingRate
==================
*/
int idNetworkSystem::ClientGetOutgoingRate( void ) {
if ( idAsyncNetwork::client.IsActive() ) {
return idAsyncNetwork::client.GetOutgoingRate();
}
return 0;
}
/*
==================
idNetworkSystem::ClientGetIncomingRate
==================
*/
int idNetworkSystem::ClientGetIncomingRate( void ) {
if ( idAsyncNetwork::client.IsActive() ) {
return idAsyncNetwork::client.GetIncomingRate();
}
return 0;
}
/*
==================
idNetworkSystem::ClientGetIncomingPacketLoss
==================
*/
float idNetworkSystem::ClientGetIncomingPacketLoss( void ) {
if ( idAsyncNetwork::client.IsActive() ) {
return idAsyncNetwork::client.GetIncomingPacketLoss();
}
return 0.0f;
}

View File

@@ -0,0 +1,65 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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.
===========================================================================
*/
#ifndef __NETWORKSYSTEM_H__
#define __NETWORKSYSTEM_H__
/*
===============================================================================
Network System.
===============================================================================
*/
class idNetworkSystem {
public:
virtual ~idNetworkSystem( void ) {}
virtual void ServerSendReliableMessage( int clientNum, const idBitMsg &msg );
virtual void ServerSendReliableMessageExcluding( int clientNum, const idBitMsg &msg );
virtual int ServerGetClientPing( int clientNum );
virtual int ServerGetClientPrediction( int clientNum );
virtual int ServerGetClientTimeSinceLastPacket( int clientNum );
virtual int ServerGetClientTimeSinceLastInput( int clientNum );
virtual int ServerGetClientOutgoingRate( int clientNum );
virtual int ServerGetClientIncomingRate( int clientNum );
virtual float ServerGetClientIncomingPacketLoss( int clientNum );
virtual void ClientSendReliableMessage( const idBitMsg &msg );
virtual int ClientGetPrediction( void );
virtual int ClientGetTimeSinceLastPacket( void );
virtual int ClientGetOutgoingRate( void );
virtual int ClientGetIncomingRate( void );
virtual float ClientGetIncomingPacketLoss( void );
};
extern idNetworkSystem * networkSystem;
#endif /* !__NETWORKSYSTEM_H__ */

View File

@@ -0,0 +1,639 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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.
===========================================================================
*/
#include "../../idlib/precompiled.h"
#pragma hdrstop
idCVar gui_filter_password( "gui_filter_password", "0", CVAR_GUI | CVAR_INTEGER | CVAR_ARCHIVE, "Password filter" );
idCVar gui_filter_players( "gui_filter_players", "0", CVAR_GUI | CVAR_INTEGER | CVAR_ARCHIVE, "Players filter" );
idCVar gui_filter_gameType( "gui_filter_gameType", "0", CVAR_GUI | CVAR_INTEGER | CVAR_ARCHIVE, "Gametype filter" );
idCVar gui_filter_idle( "gui_filter_idle", "0", CVAR_GUI | CVAR_INTEGER | CVAR_ARCHIVE, "Idle servers filter" );
idCVar gui_filter_game( "gui_filter_game", "0", CVAR_GUI | CVAR_INTEGER | CVAR_ARCHIVE, "Game filter" );
const char* l_gameTypes[] = {
"Deathmatch",
"Tourney",
"Team DM",
"Last Man",
"CTF",
NULL
};
static idServerScan *l_serverScan = NULL;
/*
================
idServerScan::idServerScan
================
*/
idServerScan::idServerScan( ) {
m_pGUI = NULL;
m_sort = SORT_PING;
m_sortAscending = true;
challenge = 0;
LocalClear();
}
/*
================
idServerScan::LocalClear
================
*/
void idServerScan::LocalClear( ) {
scan_state = IDLE;
incoming_net = false;
lan_pingtime = -1;
net_info.Clear();
net_servers.Clear();
cur_info = 0;
if ( listGUI ) {
listGUI->Clear();
}
incoming_useTimeout = false;
m_sortedServers.Clear();
}
/*
================
idServerScan::Clear
================
*/
void idServerScan::Clear( ) {
LocalClear();
idList<networkServer_t>::Clear();
}
/*
================
idServerScan::Shutdown
================
*/
void idServerScan::Shutdown( ) {
m_pGUI = NULL;
if ( listGUI ) {
listGUI->Config( NULL, NULL );
uiManager->FreeListGUI( listGUI );
listGUI = NULL;
}
screenshot.Clear();
}
/*
================
idServerScan::SetupLANScan
================
*/
void idServerScan::SetupLANScan( ) {
Clear();
GUIUpdateSelected();
scan_state = LAN_SCAN;
challenge++;
lan_pingtime = Sys_Milliseconds();
common->DPrintf( "SetupLANScan with challenge %d\n", challenge );
}
/*
================
idServerScan::InfoResponse
================
*/
int idServerScan::InfoResponse( networkServer_t &server ) {
if ( scan_state == IDLE ) {
return false;
}
idStr serv = Sys_NetAdrToString( server.adr );
if ( server.challenge != challenge ) {
common->DPrintf( "idServerScan::InfoResponse - ignoring response from %s, wrong challenge %d.", serv.c_str(), server.challenge );
return false;
}
if ( scan_state == NET_SCAN ) {
const idKeyValue *info = net_info.FindKey( serv.c_str() );
if ( !info ) {
common->DPrintf( "idServerScan::InfoResponse NET_SCAN: reply from unknown %s\n", serv.c_str() );
return false;
}
int id = atoi( info->GetValue() );
net_info.Delete( serv.c_str() );
inServer_t iserv = net_servers[ id ];
server.ping = Sys_Milliseconds() - iserv.time;
server.id = iserv.id;
} else {
server.ping = Sys_Milliseconds() - lan_pingtime;
server.id = 0;
// check for duplicate servers
for ( int i = 0; i < Num() ; i++ ) {
if ( memcmp( &(*this)[ i ].adr, &server.adr, sizeof(netadr_t) ) == 0 ) {
common->DPrintf( "idServerScan::InfoResponse LAN_SCAN: duplicate server %s\n", serv.c_str() );
return true;
}
}
}
const char *si_map = server.serverInfo.GetString( "si_map" );
const idDecl *mapDecl = declManager->FindType( DECL_MAPDEF, si_map, false );
const idDeclEntityDef *mapDef = static_cast< const idDeclEntityDef * >( mapDecl );
if ( mapDef ) {
const char *mapName = common->GetLanguageDict()->GetString( mapDef->dict.GetString( "name", si_map ) );
server.serverInfo.Set( "si_mapName", mapName );
} else {
server.serverInfo.Set( "si_mapName", si_map );
}
int index = Append( server );
// for now, don't maintain sorting when adding new info response servers
m_sortedServers.Append( Num()-1 );
if ( listGUI->IsConfigured( ) && !IsFiltered( server ) ) {
GUIAdd( Num()-1, server );
}
if ( listGUI->GetSelection( NULL, 0 ) == ( Num()-1 ) ) {
GUIUpdateSelected();
}
return index;
}
/*
================
idServerScan::AddServer
================
*/
void idServerScan::AddServer( int id, const char *srv ) {
inServer_t s;
incoming_net = true;
incoming_lastTime = Sys_Milliseconds() + INCOMING_TIMEOUT;
s.id = id;
// using IPs, not hosts
if ( !Sys_StringToNetAdr( srv, &s.adr, false ) ) {
common->DPrintf( "idServerScan::AddServer: failed to parse server %s\n", srv );
return;
}
if ( !s.adr.port ) {
s.adr.port = PORT_SERVER;
}
net_servers.Append( s );
}
/*
================
idServerScan::EndServers
================
*/
void idServerScan::EndServers( ) {
incoming_net = false;
l_serverScan = this;
m_sortedServers.Sort( idServerScan::Cmp );
ApplyFilter();
}
/*
================
idServerScan::StartServers
================
*/
void idServerScan::StartServers( bool timeout ) {
incoming_net = true;
incoming_useTimeout = timeout;
incoming_lastTime = Sys_Milliseconds() + REFRESH_START;
}
/*
================
idServerScan::EmitGetInfo
================
*/
void idServerScan::EmitGetInfo( netadr_t &serv ) {
idAsyncNetwork::client.GetServerInfo( serv );
}
/*
===============
idServerScan::GetChallenge
===============
*/
int idServerScan::GetChallenge( ) {
return challenge;
}
/*
================
idServerScan::NetScan
================
*/
void idServerScan::NetScan( ) {
if ( !idAsyncNetwork::client.IsPortInitialized() ) {
// if the port isn't open, initialize it, but wait for a short
// time to let the OS do whatever magic things it needs to do...
idAsyncNetwork::client.InitPort();
// start the scan one second from now...
scan_state = WAIT_ON_INIT;
endWaitTime = Sys_Milliseconds() + 1000;
return;
}
// make sure the client port is open
idAsyncNetwork::client.InitPort();
scan_state = NET_SCAN;
challenge++;
idList<networkServer_t>::Clear();
m_sortedServers.Clear();
cur_info = 0;
net_info.Clear();
listGUI->Clear();
GUIUpdateSelected();
common->DPrintf( "NetScan with challenge %d\n", challenge );
while ( cur_info < Min( net_servers.Num(), MAX_PINGREQUESTS ) ) {
netadr_t serv = net_servers[ cur_info ].adr;
EmitGetInfo( serv );
net_servers[ cur_info ].time = Sys_Milliseconds();
net_info.SetInt( Sys_NetAdrToString( serv ), cur_info );
cur_info++;
}
}
/*
===============
idServerScan::ServerScanFrame
===============
*/
void idServerScan::RunFrame( ) {
if ( scan_state == IDLE ) {
return;
}
if ( scan_state == WAIT_ON_INIT ) {
if ( Sys_Milliseconds() >= endWaitTime ) {
scan_state = IDLE;
NetScan();
}
return;
}
int timeout_limit = Sys_Milliseconds() - REPLY_TIMEOUT;
if ( scan_state == LAN_SCAN ) {
if ( timeout_limit > lan_pingtime ) {
common->Printf( "Scanned for servers on the LAN\n" );
scan_state = IDLE;
}
return;
}
// if scan_state == NET_SCAN
// check for timeouts
int i = 0;
while ( i < net_info.GetNumKeyVals() ) {
if ( timeout_limit > net_servers[ atoi( net_info.GetKeyVal( i )->GetValue().c_str() ) ].time ) {
common->DPrintf( "timeout %s\n", net_info.GetKeyVal( i )->GetKey().c_str() );
net_info.Delete( net_info.GetKeyVal( i )->GetKey().c_str() );
} else {
i++;
}
}
// possibly send more queries
while ( cur_info < net_servers.Num() && net_info.GetNumKeyVals() < MAX_PINGREQUESTS ) {
netadr_t serv = net_servers[ cur_info ].adr;
EmitGetInfo( serv );
net_servers[ cur_info ].time = Sys_Milliseconds();
net_info.SetInt( Sys_NetAdrToString( serv ), cur_info );
cur_info++;
}
// update state
if ( ( !incoming_net || ( incoming_useTimeout && Sys_Milliseconds() > incoming_lastTime ) ) && net_info.GetNumKeyVals() == 0 ) {
EndServers();
// the list is complete, we are no longer waiting for any getInfo replies
common->Printf( "Scanned %d servers.\n", cur_info );
scan_state = IDLE;
}
}
/*
===============
idServerScan::GetBestPing
===============
*/
bool idServerScan::GetBestPing( networkServer_t &serv ) {
int i, ic;
ic = Num();
if ( !ic ) {
return false;
}
serv = (*this)[ 0 ];
for ( i = 0 ; i < ic ; i++ ) {
if ( (*this)[ i ].ping < serv.ping ) {
serv = (*this)[ i ];
}
}
return true;
}
/*
================
idServerScan::GUIConfig
================
*/
void idServerScan::GUIConfig( idUserInterface *pGUI, const char *name ) {
m_pGUI = pGUI;
if ( listGUI == NULL ) {
listGUI = uiManager->AllocListGUI();
}
listGUI->Config( pGUI, name );
}
/*
================
idServerScan::GUIUpdateSelected
================
*/
void idServerScan::GUIUpdateSelected( void ) {
char screenshot[ MAX_STRING_CHARS ];
if ( !m_pGUI ) {
return;
}
int i = listGUI->GetSelection( NULL, 0 );
if ( i == -1 || i >= Num() ) {
m_pGUI->SetStateString( "server_name", "" );
m_pGUI->SetStateString( "player1", "" );
m_pGUI->SetStateString( "player2", "" );
m_pGUI->SetStateString( "player3", "" );
m_pGUI->SetStateString( "player4", "" );
m_pGUI->SetStateString( "player5", "" );
m_pGUI->SetStateString( "player6", "" );
m_pGUI->SetStateString( "player7", "" );
m_pGUI->SetStateString( "player8", "" );
m_pGUI->SetStateString( "server_map", "" );
m_pGUI->SetStateString( "browser_levelshot", "" );
m_pGUI->SetStateString( "server_gameType", "" );
m_pGUI->SetStateString( "server_IP", "" );
m_pGUI->SetStateString( "server_passworded", "" );
} else {
m_pGUI->SetStateString( "server_name", (*this)[ i ].serverInfo.GetString( "si_name" ) );
for ( int j = 0; j < 8; j++ ) {
if ( (*this)[i].clients > j ) {
m_pGUI->SetStateString( va( "player%i", j + 1 ) , (*this)[ i ].nickname[ j ] );
} else {
m_pGUI->SetStateString( va( "player%i", j + 1 ) , "" );
}
}
m_pGUI->SetStateString( "server_map", (*this)[ i ].serverInfo.GetString( "si_mapName" ) );
fileSystem->FindMapScreenshot( (*this)[ i ].serverInfo.GetString( "si_map" ), screenshot, MAX_STRING_CHARS );
m_pGUI->SetStateString( "browser_levelshot", screenshot );
m_pGUI->SetStateString( "server_gameType", (*this)[ i ].serverInfo.GetString( "si_gameType" ) );
m_pGUI->SetStateString( "server_IP", Sys_NetAdrToString( (*this)[ i ].adr ) );
if ( (*this)[ i ].serverInfo.GetBool( "si_usePass" ) ) {
m_pGUI->SetStateString( "server_passworded", "PASSWORD REQUIRED" );
} else {
m_pGUI->SetStateString( "server_passworded", "" );
}
}
}
/*
================
idServerScan::GUIAdd
================
*/
void idServerScan::GUIAdd( int id, const networkServer_t server ) {
idStr name = server.serverInfo.GetString( "si_name", GAME_NAME " Server" );
bool d3xp = false;
bool mod = false;
if ( !idStr::Icmp( server.serverInfo.GetString( "fs_game" ), "d3xp" ) ||
!idStr::Icmp( server.serverInfo.GetString( "fs_game_base" ), "d3xp" ) ) {
d3xp = true;
}
if ( server.serverInfo.GetString( "fs_game" )[ 0 ] != '\0' ) {
mod = true;
}
name += "\t";
if ( server.serverInfo.GetString( "sv_punkbuster" )[ 0 ] == '1' ) {
name += "mtr_PB";
}
name += "\t";
if ( d3xp ) {
// FIXME: even for a 'D3XP mod'
// could have a specific icon for this case
name += "mtr_doom3XPIcon";
} else if ( mod ) {
name += "mtr_doom3Mod";
} else {
name += "mtr_doom3Icon";
}
name += "\t";
name += va( "%i/%i\t", server.clients, server.serverInfo.GetInt( "si_maxPlayers" ) );
name += ( server.ping > -1 ) ? va( "%i\t", server.ping ) : "na\t";
name += server.serverInfo.GetString( "si_gametype" );
name += "\t";
name += server.serverInfo.GetString( "si_mapName" );
name += "\t";
listGUI->Add( id, name );
}
/*
================
idServerScan::ApplyFilter
================
*/
void idServerScan::ApplyFilter( ) {
int i;
networkServer_t serv;
idStr s;
listGUI->SetStateChanges( false );
listGUI->Clear();
for ( i = m_sortAscending ? 0 : m_sortedServers.Num() - 1;
m_sortAscending ? i < m_sortedServers.Num() : i >= 0;
m_sortAscending ? i++ : i-- ) {
serv = (*this)[ m_sortedServers[ i ] ];
if ( !IsFiltered( serv ) ) {
GUIAdd( m_sortedServers[ i ], serv );
}
}
GUIUpdateSelected();
listGUI->SetStateChanges( true );
}
/*
================
idServerScan::IsFiltered
================
*/
bool idServerScan::IsFiltered( const networkServer_t server ) {
int i;
const idKeyValue *keyval;
// OS support filter
#if 0
// filter out pure servers that won't provide checksumed game code for client OS
keyval = server.serverInfo.FindKey( "si_pure" );
if ( keyval && !idStr::Cmp( keyval->GetValue(), "1" ) ) {
if ( ( server.OSMask & ( 1 << BUILD_OS_ID ) ) == 0 ) {
return true;
}
}
#else
if ( ( server.OSMask & ( 1 << BUILD_OS_ID ) ) == 0 ) {
return true;
}
#endif
// password filter
keyval = server.serverInfo.FindKey( "si_usePass" );
if ( keyval && gui_filter_password.GetInteger() == 1 ) {
// show passworded only
if ( keyval->GetValue()[ 0 ] == '0' ) {
return true;
}
} else if ( keyval && gui_filter_password.GetInteger() == 2 ) {
// show no password only
if ( keyval->GetValue()[ 0 ] != '0' ) {
return true;
}
}
// players filter
keyval = server.serverInfo.FindKey( "si_maxPlayers" );
if ( keyval ) {
if ( gui_filter_players.GetInteger() == 1 && server.clients == atoi( keyval->GetValue() ) ) {
return true;
} else if ( gui_filter_players.GetInteger() == 2 && ( !server.clients || server.clients == atoi( keyval->GetValue() ) ) ) {
return true;
}
}
// gametype filter
keyval = server.serverInfo.FindKey( "si_gameType" );
if ( keyval && gui_filter_gameType.GetInteger() ) {
i = 0;
while ( l_gameTypes[ i ] ) {
if ( !keyval->GetValue().Icmp( l_gameTypes[ i ] ) ) {
break;
}
i++;
}
if ( l_gameTypes[ i ] && i != gui_filter_gameType.GetInteger() -1 ) {
return true;
}
}
// idle server filter
keyval = server.serverInfo.FindKey( "si_idleServer" );
if ( keyval && !gui_filter_idle.GetInteger() ) {
if ( !keyval->GetValue().Icmp( "1" ) ) {
return true;
}
}
// autofilter D3XP games if the user does not has the XP installed
if(!fileSystem->HasD3XP() && !idStr::Icmp(server.serverInfo.GetString( "fs_game" ), "d3xp")) {
return true;
}
// filter based on the game doom or XP
if(gui_filter_game.GetInteger() == 1) { //Only Doom
if(idStr::Icmp(server.serverInfo.GetString("fs_game"), "")) {
return true;
}
} else if(gui_filter_game.GetInteger() == 2) { //Only D3XP
if(idStr::Icmp(server.serverInfo.GetString("fs_game"), "d3xp")) {
return true;
}
}
return false;
}
/*
================
idServerScan::Cmp
================
*/
int idServerScan::Cmp( const int *a, const int *b ) {
networkServer_t serv1, serv2;
idStr s1, s2;
int ret;
serv1 = (*l_serverScan)[ *a ];
serv2 = (*l_serverScan)[ *b ];
switch ( l_serverScan->m_sort ) {
case SORT_PING:
ret = serv1.ping < serv2.ping ? -1 : ( serv1.ping > serv2.ping ? 1 : 0 );
return ret;
case SORT_SERVERNAME:
serv1.serverInfo.GetString( "si_name", "", s1 );
serv2.serverInfo.GetString( "si_name", "", s2 );
return s1.IcmpNoColor( s2 );
case SORT_PLAYERS:
ret = serv1.clients < serv2.clients ? -1 : ( serv1.clients > serv2.clients ? 1 : 0 );
return ret;
case SORT_GAMETYPE:
serv1.serverInfo.GetString( "si_gameType", "", s1 );
serv2.serverInfo.GetString( "si_gameType", "", s2 );
return s1.Icmp( s2 );
case SORT_MAP:
serv1.serverInfo.GetString( "si_mapName", "", s1 );
serv2.serverInfo.GetString( "si_mapName", "", s2 );
return s1.Icmp( s2 );
case SORT_GAME:
serv1.serverInfo.GetString( "fs_game", "", s1 );
serv2.serverInfo.GetString( "fs_game", "", s2 );
return s1.Icmp( s2 );
}
return 0;
}
/*
================
idServerScan::SetSorting
================
*/
void idServerScan::SetSorting( serverSort_t sort ) {
l_serverScan = this;
if ( sort == m_sort ) {
m_sortAscending = !m_sortAscending;
} else {
m_sort = sort;
m_sortAscending = true; // is the default for any new sort
m_sortedServers.Sort( idServerScan::Cmp );
}
// trigger a redraw
ApplyFilter();
}

View File

@@ -0,0 +1,170 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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.
===========================================================================
*/
#ifndef __SERVERSCAN_H__
#define __SERVERSCAN_H__
/*
===============================================================================
Scan for servers, on the LAN or from a list
Update a listDef GUI through usage of idListGUI class
When updating large lists of servers, sends out getInfo in small batches to avoid congestion
===============================================================================
*/
// storage for incoming servers / server scan
typedef struct {
netadr_t adr;
int id;
int time;
} inServer_t;
// the menu gui uses a hard-coded control type to display a list of network games
typedef struct {
netadr_t adr;
idDict serverInfo;
int ping;
int id; // idnet mode sends an id for each server in list
int clients;
char nickname[ MAX_NICKLEN ][ MAX_ASYNC_CLIENTS ];
short pings[ MAX_ASYNC_CLIENTS ];
int rate[ MAX_ASYNC_CLIENTS ];
int OSMask;
int challenge;
} networkServer_t;
typedef enum {
SORT_PING,
SORT_SERVERNAME,
SORT_PLAYERS,
SORT_GAMETYPE,
SORT_MAP,
SORT_GAME
} serverSort_t;
class idServerScan : public idList<networkServer_t> {
public:
idServerScan( );
int InfoResponse( networkServer_t &server );
// add an internet server - ( store a numeric id along with it )
void AddServer( int id, const char *srv );
// we are going to feed server entries to be pinged
// if timeout is true, use a timeout once we start AddServer to trigger EndServers and decide the scan is done
void StartServers( bool timeout );
// we are done filling up the list of server entries
void EndServers( );
// scan the current list of servers - used for refreshes and while receiving a fresh list
void NetScan( );
// clear
void Clear( );
// called each game frame. Updates the scanner state, takes care of ongoing scans
void RunFrame( );
typedef enum {
IDLE = 0,
WAIT_ON_INIT,
LAN_SCAN,
NET_SCAN
} scan_state_t;
scan_state_t GetState() { return scan_state; }
void SetState( scan_state_t );
bool GetBestPing( networkServer_t &serv );
// prepare for a LAN scan. idAsyncClient does the network job (UDP broadcast), we do the storage
void SetupLANScan( );
void GUIConfig( idUserInterface *pGUI, const char *name );
// update the GUI fields with information about the currently selected server
void GUIUpdateSelected( void );
void Shutdown( );
void ApplyFilter( );
// there is an internal toggle, call twice with same sort to switch
void SetSorting( serverSort_t sort );
int GetChallenge( );
private:
static const int MAX_PINGREQUESTS = 32; // how many servers to query at once
static const int REPLY_TIMEOUT = 999; // how long should we wait for a reply from a game server
static const int INCOMING_TIMEOUT = 1500; // when we got an incoming server list, how long till we decide the list is done
static const int REFRESH_START = 10000; // how long to wait when sending the initial refresh request
scan_state_t scan_state;
bool incoming_net; // set to true while new servers are fed through AddServer
bool incoming_useTimeout;
int incoming_lastTime;
int lan_pingtime; // holds the time of LAN scan
// servers we're waiting for a reply from
// won't exceed MAX_PINGREQUESTS elements
// holds index of net_servers elements, indexed by 'from' string
idDict net_info;
idList<inServer_t> net_servers;
// where we are in net_servers list for getInfo emissions ( NET_SCAN only )
// we may either be waiting on MAX_PINGREQUESTS, or for net_servers to grow some more ( through AddServer )
int cur_info;
idUserInterface *m_pGUI;
idListGUI * listGUI;
serverSort_t m_sort;
bool m_sortAscending;
idList<int> m_sortedServers; // use ascending for the walking order
idStr screenshot;
int challenge; // challenge for current scan
int endWaitTime; // when to stop waiting on a port init
private:
void LocalClear( ); // we need to clear some internal data as well
void EmitGetInfo( netadr_t &serv );
void GUIAdd( int id, const networkServer_t server );
bool IsFiltered( const networkServer_t server );
static int Cmp( const int *a, const int *b );
};
#endif /* !__SERVERSCAN_H__ */