/* * CL_WriteServerCache */ void CL_WriteServerCache( void ) { serverlist_t *server; int filehandle; char str[256]; netadr_t adr; if( FS_FOpenFile( SERVERSFILE, &filehandle, FS_WRITE ) == -1 ) { Com_Printf( "CL_WriteServerList: Couldn't create the cache file\n" ); return; } Q_snprintfz( str, sizeof( str ), "// servers cache file generated by %s. Do not modify\n", APPLICATION ); FS_Print( filehandle, str ); FS_Print( filehandle, "master\n" ); server = masterList; while( server ) { if( server->lastValidPing + 7 > Com_DaysSince1900() ) { if( NET_StringToAddress( server->address, &adr ) ) { Q_snprintfz( str, sizeof( str ), "%s %i\n", server->address, (int)server->lastValidPing ); FS_Print( filehandle, str ); } } server = server->pnext; } FS_Print( filehandle, "favorites\n" ); server = favoritesList; while( server ) { if( server->lastValidPing + 7 > Com_DaysSince1900() ) { if( NET_StringToAddress( server->address, &adr ) ) { Q_snprintfz( str, sizeof( str ), "%s %i\n", server->address, (int)server->lastValidPing ); FS_Print( filehandle, str ); } } server = server->pnext; } FS_FCloseFile( filehandle ); }
/* * CL_ParseGetServersResponse * Handle a reply from getservers message to master server */ void CL_ParseGetServersResponse( const socket_t *socket, const netadr_t *address, msg_t *msg, qboolean extended ) { serverlist_t *server; netadr_t adr; // CL_ReadServerCache(); // add the new server addresses to the local addresses list masterServerUpdateSeq++; if( !masterServerUpdateSeq ) { // wrapped masterServerUpdateSeq = 1; } CL_ParseGetServersResponseMessage( msg, extended ); // CL_WriteServerCache(); // dump servers we just received an update on from the master server server = masterList; while( server ) { if( server->masterServerUpdateSeq == masterServerUpdateSeq && NET_StringToAddress( server->address, &adr ) ) CL_UIModule_AddToServerList( server->address, "\\\\EOT" ); server = server->pnext; } }
/* * CL_PingServer_f */ void CL_PingServer_f( void ) { char *address_string; char requestString[64]; netadr_t adr; serverlist_t *pingserver; socket_t *socket; if( Cmd_Argc() < 2 ) Com_Printf( "Usage: pingserver [ip:port]\n" ); address_string = Cmd_Argv( 1 ); if( !NET_StringToAddress( address_string, &adr ) ) return; pingserver = CL_ServerFindInList( masterList, address_string ); if( !pingserver ) pingserver = CL_ServerFindInList( favoritesList, address_string ); if( !pingserver ) return; // never request a second ping while awaiting for a ping reply if( pingserver->pingTimeStamp + SERVER_PINGING_TIMEOUT > Sys_Milliseconds() ) return; pingserver->pingTimeStamp = Sys_Milliseconds(); Q_snprintfz( requestString, sizeof( requestString ), "info %i %s %s", SERVERBROWSER_PROTOCOL_VERSION, filter_allow_full ? "full" : "", filter_allow_empty ? "empty" : "" ); socket = ( adr.type == NA_IP6 ? &cls.socket_udp6 : &cls.socket_udp ); Netchan_OutOfBandPrint( socket, &adr, requestString ); }
/* * CL_QueryGetInfoMessage */ static void CL_QueryGetInfoMessage( const char *cmdname ) { netadr_t adr; char *requeststring; char *server; //get what master server = Cmd_Argv( 1 ); if( !server || !( *server ) ) { Com_Printf( "%s: no address provided %s...\n", Cmd_Argv( 0 ), server ? server : "" ); return; } requeststring = va( cmdname ); // send a broadcast packet Com_DPrintf( "quering %s...\n", server ); if( NET_StringToAddress( server, &adr ) ) { socket_t *socket; if( NET_GetAddressPort( &adr ) == 0 ) NET_SetAddressPort( &adr, PORT_SERVER ); socket = ( adr.type == NA_IP6 ? &cls.socket_udp6 : &cls.socket_udp ); Netchan_OutOfBandPrint( socket, &adr, requeststring ); } else { Com_Printf( "Bad address: %s\n", server ); } }
/* * CL_ResolveMasterAddress */ static void CL_ResolveMasterAddress( const char *master, netadr_t *adr ) { trie_error_t err; masteradrcache_t *cache; // check memory cache err = Trie_Find( serverlist_masters_trie, master, TRIE_EXACT_MATCH, (void **)&cache ); if( err == TRIE_OK ) { if( adr ) *adr = cache->adr; return; } // send a unicast packet cache = Mem_ZoneMalloc( sizeof( *cache ) ); NET_StringToAddress( master, &cache->adr ); if( adr ) *adr = cache->adr; cache->next = serverlist_masters_head; serverlist_masters_head = cache; Trie_Insert( serverlist_masters_trie, master, (void *)cache ); }
/* * TV_Downstream_AddMaster_f * Add a master server to the list */ static void TV_Downstream_AddMaster_f( const char *master ) { int i; if( !master || !master[0] ) return; if( !tv_public->integer ) { Com_Printf( "'TV_Downstream_AddMaster_f' Only public servers use masters.\n" ); return; } for( i = 0; i < MAX_MASTERS; i++ ) { if( tv_master_adr[i].type != NA_NOTRANSMIT ) continue; if( !NET_StringToAddress( master, &tv_master_adr[i] ) ) { Com_Printf( "'SV_AddMaster_f' Bad Master server address: %s\n", master ); return; } if( NET_GetAddressPort( &tv_master_adr[i] ) == 0 ) NET_SetAddressPort( &tv_master_adr[i], PORT_MASTER ); Com_Printf( "Added new master server #%i at %s\n", i, NET_AddressToString( &tv_master_adr[i] ) ); return; } Com_Printf( "'TV_Downstream_AddMaster_f' List of master servers is already full\n" ); }
/* * CL_ReadServerCache */ void CL_ReadServerCache( void ) { int filelen, filehandle; qbyte *buf = NULL; char *ptr, *token; netadr_t adr; char adrString[64]; qboolean favorite = qfalse; filelen = FS_FOpenFile( SERVERSFILE, &filehandle, FS_READ ); if( !filehandle || filelen < 1 ) { FS_FCloseFile( filehandle ); } else { buf = Mem_TempMalloc( filelen + 1 ); filelen = FS_Read( buf, filelen, filehandle ); FS_FCloseFile( filehandle ); } if( !buf ) return; ptr = ( char * )buf; while( ptr ) { token = COM_ParseExt( &ptr, qtrue ); if( !token[0] ) break; if( !Q_stricmp( token, "master" ) ) { favorite = qfalse; continue; } if( !Q_stricmp( token, "favorites" ) ) { favorite = qtrue; continue; } if( NET_StringToAddress( token, &adr ) ) { Q_strncpyz( adrString, token, sizeof( adrString ) ); token = COM_ParseExt( &ptr, qfalse ); if( !token[0] ) continue; if( favorite ) CL_AddServerToList( &favoritesList, adrString, (unsigned int)atoi( token ) ); else CL_AddServerToList( &masterList, adrString, (unsigned int)atoi( token ) ); } } Mem_TempFree( buf ); }
/* * CL_ResolveMasterAddress */ static void CL_ResolveMasterAddress( const char *master ) { netadr_t adr, *padr; if( master && *master ) { NET_StringToAddress( master, &adr ); QMutex_Lock( resolveLock ); padr = ( netadr_t * )malloc( sizeof( adr ) ); *padr = adr; Trie_Insert( serverlist_masters_trie, master, padr ); QMutex_Unlock( resolveLock ); } }
/* * TV_GenericConnect_f */ static void TV_GenericConnect_f( socket_type_t socket ) { netadr_t serveraddress; char *servername, *password, *name; upstream_t *upstream; unsigned int delay; if( Cmd_Argc() < 2 ) { Com_Printf( "Usage: %s <server> [password] [name] [delay]\n", Cmd_Argv( 0 ) ); return; } if( !NET_StringToAddress( Cmd_Argv( 1 ), &serveraddress ) ) { Com_Printf( "Bad server address: %s\n", Cmd_Argv( 1 ) ); return; } servername = TempCopyString( Cmd_Argv( 1 ) ); password = ( Cmd_Argc() >= 3 ? TempCopyString( Cmd_Argv( 2 ) ) : NULL ); name = ( Cmd_Argc() >= 4 ? Cmd_Argv( 3 ) : "" ); delay = ( Cmd_Argc() >= 5 ? (unsigned)atoi( Cmd_Argv( 4 ) )*1000 : RELAY_GLOBAL_DELAY ); if( TV_UpstreamForText( servername, &upstream ) ) { if( upstream->state >= CA_CONNECTED && upstream->relay.delay == delay && upstream->socket && upstream->socket->type == socket ) { Com_Printf( "Already connected to %s\n", servername ); goto exit; } TV_Upstream_Shutdown( upstream, "Disconnected by adminstrator" ); upstream = NULL; } upstream = TV_Upstream_New( servername, name, delay ); assert( upstream ); TV_Upstream_Connect( upstream, servername, password, socket, &serveraddress ); exit: Mem_TempFree( servername ); if( password ) Mem_TempFree( password ); }
/* * SV_AddMaster_f * Add a master server to the list */ static void SV_AddMaster_f( char *address, bool steam ) { int i; if( !address || !address[0] ) return; if( !sv_public->integer ) { Com_Printf( "'SV_AddMaster_f' Only public servers use masters.\n" ); return; } //never go public when not acting as a game server if( sv.state > ss_game ) return; for( i = 0; i < MAX_MASTERS; i++ ) { sv_master_t *master = &sv_masters[i]; if( master->address.type != NA_NOTRANSMIT ) continue; if( !NET_StringToAddress( address, &master->address ) ) { Com_Printf( "'SV_AddMaster_f' Bad Master server address: %s\n", address ); return; } if( NET_GetAddressPort( &master->address ) == 0 ) NET_SetAddressPort( &master->address, steam ? PORT_MASTER_STEAM : PORT_MASTER ); master->steam = steam; Com_Printf( "Added new master server #%i at %s\n", i, NET_AddressToString( &master->address ) ); return; } Com_Printf( "'SV_AddMaster_f' List of master servers is already full\n" ); }
/* * SV_AddMaster_f * Add a master server to the list */ static void SV_AddMaster_f( char *master ) { int i; if( !master || !master[0] ) return; if( !sv_public->integer ) { Com_Printf( "'SV_AddMaster_f' Only public servers use masters.\n" ); return; } //never go public when not acting as a game server if( sv.state > ss_game ) return; for( i = 0; i < MAX_MASTERS; i++ ) { if( master_adr[i].type != NA_NOTRANSMIT ) continue; if( !NET_StringToAddress( master, &master_adr[i] ) ) { Com_Printf( "'SV_AddMaster_f' Bad Master server address: %s\n", master ); return; } if( NET_GetAddressPort( &master_adr[i] ) == 0 ) NET_SetAddressPort( &master_adr[i], PORT_MASTER ); Com_Printf( "Added new master server #%i at %s\n", i, NET_AddressToString( &master_adr[i] ) ); return; } Com_Printf( "'SV_AddMaster_f' List of master servers is already full\n" ); }
/* * CL_AddServerToList */ static qboolean CL_AddServerToList( serverlist_t **serversList, char *adr, unsigned int days ) { serverlist_t *newserv; netadr_t nadr; if( !adr || !strlen( adr ) ) return qfalse; if( !NET_StringToAddress( adr, &nadr ) ) return qfalse; newserv = CL_ServerFindInList( *serversList, adr ); if( newserv ) { // ignore excessive updates for about a second or so, which may happen // when we're querying multiple master servers at once if( !newserv->masterServerUpdateSeq || newserv->lastUpdatedByMasterServer + 1000 < Sys_Milliseconds() ) { newserv->lastUpdatedByMasterServer = Sys_Milliseconds(); newserv->masterServerUpdateSeq = masterServerUpdateSeq; } return qfalse; } newserv = (serverlist_t *)Mem_ZoneMalloc( sizeof( serverlist_t ) ); Q_strncpyz( newserv->address, adr, sizeof( newserv->address ) ); newserv->pingTimeStamp = 0; if( days == 0 ) newserv->lastValidPing = Com_DaysSince1900(); else newserv->lastValidPing = days; newserv->lastUpdatedByMasterServer = Sys_Milliseconds(); newserv->masterServerUpdateSeq = masterServerUpdateSeq; newserv->pnext = *serversList; *serversList = newserv; return qtrue; }
/* * TV_Init * * Only called at plat.exe startup, not for each game */ void TV_Init( void ) { Com_Printf( "Initializing " APPLICATION " TV server\n" ); tv_mempool = Mem_AllocPool( NULL, "TV" ); TV_AddCommands(); Cvar_Get( "protocol", va( "%i", APP_PROTOCOL_VERSION ), CVAR_SERVERINFO | CVAR_NOSET ); Cvar_Get( "gamename", Cvar_String( "gamename" ), CVAR_SERVERINFO | CVAR_NOSET ); tv_password = Cvar_Get( "tv_password", "", 0 ); tv_ip = Cvar_Get( "tv_ip", "", CVAR_ARCHIVE | CVAR_NOSET ); tv_port = Cvar_Get( "tv_port", va( "%i", PORT_TV_SERVER ), CVAR_ARCHIVE | CVAR_NOSET ); tv_ip6 = Cvar_Get( "tv_ip6", "::", CVAR_ARCHIVE | CVAR_NOSET ); tv_port6 = Cvar_Get( "tv_port6", va( "%i", PORT_TV_SERVER ), CVAR_ARCHIVE | CVAR_NOSET ); #ifdef TCP_ALLOW_TVCONNECT tv_udp = Cvar_Get( "tv_udp", "1", CVAR_SERVERINFO | CVAR_NOSET ); tv_tcp = Cvar_Get( "tv_tcp", "1", CVAR_SERVERINFO | CVAR_NOSET ); #else tv_udp = Cvar_Get( "tv_udp", "1", CVAR_NOSET ); #endif #ifndef TCP_ALLOW_TVCONNECT Cvar_FullSet( "tv_tcp", "0", CVAR_READONLY, true ); #endif tv_reconnectlimit = Cvar_Get( "tv_reconnectlimit", "3", CVAR_ARCHIVE ); tv_timeout = Cvar_Get( "tv_timeout", "125", 0 ); tv_zombietime = Cvar_Get( "tv_zombietime", "2", 0 ); tv_name = Cvar_Get( "tv_name", APPLICATION "[TV]", CVAR_SERVERINFO | CVAR_ARCHIVE ); tv_compresspackets = Cvar_Get( "tv_compresspackets", "1", 0 ); tv_maxclients = Cvar_Get( "tv_maxclients", "32", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_NOSET ); tv_maxmvclients = Cvar_Get( "tv_maxmvclients", "4", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_NOSET ); tv_public = Cvar_Get( "tv_public", "1", CVAR_ARCHIVE | CVAR_SERVERINFO ); tv_rcon_password = Cvar_Get( "tv_rcon_password", "", 0 ); tv_autorecord = Cvar_Get( "tv_autorecord", "", CVAR_ARCHIVE ); tv_lobbymusic = Cvar_Get( "tv_lobbymusic", "", CVAR_ARCHIVE ); tv_masterservers = Cvar_Get( "tv_masterservers", DEFAULT_MASTER_SERVERS_IPS, CVAR_LATCH ); tv_masterservers_steam = Cvar_Get( "tv_masterservers_steam", DEFAULT_MASTER_SERVERS_STEAM_IPS, CVAR_LATCH ); // flood control tv_floodprotection_messages = Cvar_Get( "tv_floodprotection_messages", "10", 0 ); tv_floodprotection_messages->modified = true; tv_floodprotection_seconds = Cvar_Get( "tv_floodprotection_seconds", "4", 0 ); tv_floodprotection_seconds->modified = true; tv_floodprotection_penalty = Cvar_Get( "tv_floodprotection_delay", "20", 0 ); tv_floodprotection_penalty->modified = true; if( tv_maxclients->integer < 0 ) Cvar_ForceSet( "tv_maxclients", "0" ); if( tv_maxclients->integer ) { tvs.clients = ( client_t * )Mem_Alloc( tv_mempool, sizeof( client_t ) * tv_maxclients->integer ); } else { tvs.clients = NULL; } tvs.lobby.spawncount = rand(); tvs.lobby.snapFrameTime = 100; // IPv4 if( !NET_StringToAddress( tv_ip->string, &tvs.address ) ) Com_Error( ERR_FATAL, "Couldn't understand address of tv_ip cvar: %s\n", NET_ErrorString() ); NET_SetAddressPort( &tvs.address, tv_port->integer ); if( tv_udp->integer ) { if( !NET_OpenSocket( &tvs.socket_udp, SOCKET_UDP, &tvs.address, true ) ) { Com_Printf( "Error: Couldn't open UDP socket: %s\n", NET_ErrorString() ); Cvar_ForceSet( tv_udp->name, "0" ); } } // IPv6 if( !NET_StringToAddress( tv_ip6->string, &tvs.addressIPv6 ) ) Com_Error( ERR_FATAL, "Couldn't understand address of tv_ip6 cvar: %s\n", NET_ErrorString() ); NET_SetAddressPort( &tvs.addressIPv6, tv_port6->integer ); if( tvs.addressIPv6.type != NA_NOTRANSMIT ) { if( !NET_OpenSocket( &tvs.socket_udp6, SOCKET_UDP, &tvs.addressIPv6, true ) ) { Com_Printf( "Error: Couldn't open UDP6 socket: %s\n", NET_ErrorString() ); } } #ifdef TCP_ALLOW_TVCONNECT if( tv_tcp->integer ) { bool err = true; if( !NET_OpenSocket( &tvs.socket_tcp, SOCKET_TCP, &tvs.address, true ) ) { Com_Printf( "Error: Couldn't open TCP socket: %s\n", NET_ErrorString() ); } else { NET_SetSocketNoDelay( &tvs.socket_tcp, 1 ); if( !NET_Listen( &tvs.socket_tcp ) ) { Com_Printf( "Error: Couldn't listen to TCP socket: %s\n", NET_ErrorString() ); } else { err = false; } } if( tvs.addressIPv6.type != NA_NOTRANSMIT ) { if( !NET_OpenSocket( &tvs.socket_tcp6, SOCKET_TCP, &tvs.addressIPv6, true ) ) { Com_Printf( "Error: Couldn't open TCP6 socket: %s\n", NET_ErrorString() ); } else { NET_SetSocketNoDelay( &tvs.socket_tcp6, 1 ); if( !NET_Listen( &tvs.socket_tcp6 ) ) { Com_Printf( "Error: Couldn't listen to TCP6 socket: %s\n", NET_ErrorString() ); } else { err = false; } } } if( err ) { Cvar_ForceSet( tv_tcp->name, "0" ); } } #endif TV_Downstream_InitMaster(); }
/* * CL_ParseGetServersResponseMessage * Handle a reply from getservers message to master server */ static void CL_ParseGetServersResponseMessage( msg_t *msg, qboolean extended ) { const char *header; char adrString[64]; qbyte addr[16]; unsigned short port; netadr_t adr; MSG_BeginReading( msg ); MSG_ReadLong( msg ); // skip the -1 //jump over the command name header = ( extended ? "getserversExtResponse" : "getserversResponse" ); if( !MSG_SkipData( msg, strlen( header ) ) ) { Com_Printf( "Invalid master packet ( missing %s )\n", header ); return; } while( msg->readcount + 7 <= msg->cursize ) { char prefix = MSG_ReadChar( msg ); switch( prefix ) { case '\\': MSG_ReadData( msg, addr, 4 ); port = ShortSwap( MSG_ReadShort( msg ) ); // both endians need this swapped. Q_snprintfz( adrString, sizeof( adrString ), "%u.%u.%u.%u:%u", addr[0], addr[1], addr[2], addr[3], port ); break; case '/': if( extended ) { MSG_ReadData( msg, addr, 16 ); port = ShortSwap( MSG_ReadShort( msg ) ); // both endians need this swapped. Q_snprintfz( adrString, sizeof( adrString ), "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%hu", addr[ 0], addr[ 1], addr[ 2], addr[ 3], addr[ 4], addr[ 5], addr[ 6], addr[ 7], addr[ 8], addr[ 9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15], port ); } else { Com_Printf( "Invalid master packet ( IPv6 prefix in a non-extended response )\n" ); return; } break; default: Com_Printf( "Invalid master packet ( missing separator )\n" ); return; } if( port == 0 ) // last server seen return; Com_DPrintf( "%s\n", adrString ); if( !NET_StringToAddress( adrString, &adr ) ) { Com_Printf( "Bad address: %s\n", adrString ); continue; } CL_AddServerToList( &masterList, adrString, 0 ); } }
/* * SV_InitGame * A brand new game has been started */ void SV_InitGame( void ) { int i; edict_t *ent; netadr_t address, ipv6_address; // make sure the client is down CL_Disconnect( NULL ); SCR_BeginLoadingPlaque(); if( svs.initialized ) { // cause any connected clients to reconnect SV_ShutdownGame( "Server restarted", qtrue ); // SV_ShutdownGame will also call Cvar_GetLatchedVars } else { // get any latched variable changes (sv_maxclients, etc) Cvar_GetLatchedVars( CVAR_LATCH ); } svs.initialized = qtrue; if( sv_skilllevel->integer > 2 ) Cvar_ForceSet( "sv_skilllevel", "2" ); if( sv_skilllevel->integer < 0 ) Cvar_ForceSet( "sv_skilllevel", "0" ); // init clients if( sv_maxclients->integer < 1 ) Cvar_FullSet( "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH, qtrue ); else if( sv_maxclients->integer > MAX_CLIENTS ) Cvar_FullSet( "sv_maxclients", va( "%i", MAX_CLIENTS ), CVAR_SERVERINFO | CVAR_LATCH, qtrue ); svs.spawncount = rand(); svs.clients = Mem_Alloc( sv_mempool, sizeof( client_t )*sv_maxclients->integer ); svs.client_entities.num_entities = sv_maxclients->integer * UPDATE_BACKUP * MAX_SNAP_ENTITIES; svs.client_entities.entities = Mem_Alloc( sv_mempool, sizeof( entity_state_t ) * svs.client_entities.num_entities ); // init network stuff address.type = NA_NOTRANSMIT; ipv6_address.type = NA_NOTRANSMIT; if( !dedicated->integer ) { NET_InitAddress( &address, NA_LOOPBACK ); if( !NET_OpenSocket( &svs.socket_loopback, SOCKET_LOOPBACK, &address, qtrue ) ) Com_Error( ERR_FATAL, "Couldn't open loopback socket: %s\n", NET_ErrorString() ); } if( dedicated->integer || sv_maxclients->integer > 1 ) { qboolean socket_opened = qfalse; // IPv4 NET_StringToAddress( sv_ip->string, &address ); NET_SetAddressPort( &address, sv_port->integer ); if( !NET_OpenSocket( &svs.socket_udp, SOCKET_UDP, &address, qtrue ) ) Com_Printf( "Error: Couldn't open UDP socket: %s\n", NET_ErrorString() ); else socket_opened = qtrue; // IPv6 NET_StringToAddress( sv_ip6->string, &ipv6_address ); if( ipv6_address.type == NA_IP6 ) { NET_SetAddressPort( &ipv6_address, sv_port6->integer ); if( !NET_OpenSocket( &svs.socket_udp6, SOCKET_UDP, &ipv6_address, qtrue ) ) Com_Printf( "Error: Couldn't open UDP6 socket: %s\n", NET_ErrorString() ); else socket_opened = qtrue; } else Com_Printf( "Error: invalid IPv6 address: %s\n", sv_ip6->string ); if( dedicated->integer && !socket_opened ) Com_Error( ERR_FATAL, "Couldn't open any socket\n" ); } #ifdef TCP_ALLOW_CONNECT if( sv_tcp->integer && ( dedicated->integer || sv_maxclients->integer > 1 ) ) { if( !NET_OpenSocket( &svs.socket_tcp, SOCKET_TCP, &address, qtrue ) ) { Com_Printf( "Error: Couldn't open TCP socket: %s", NET_ErrorString() ); Cvar_ForceSet( "sv_tcp", "0" ); } else { if( !NET_Listen( &svs.socket_tcp ) ) { Com_Printf( "Error: Couldn't listen to TCP socket: %s", NET_ErrorString() ); NET_CloseSocket( &svs.socket_tcp ); Cvar_ForceSet( "sv_tcp", "0" ); } } } #endif // init mm // SV_MM_Init(); // init game SV_InitGameProgs(); for( i = 0; i < sv_maxclients->integer; i++ ) { ent = EDICT_NUM( i+1 ); ent->s.number = i+1; svs.clients[i].edict = ent; } // load the map assert( !svs.cms ); svs.cms = CM_New( NULL ); }