Esempio n. 1
0
/*
* 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 );
}
Esempio n. 2
0
/*
* 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;
	}
}
Esempio n. 3
0
/*
* 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 );
}
Esempio n. 4
0
/*
* 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 );
	}
}
Esempio n. 5
0
/*
* 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 );
}
Esempio n. 6
0
/*
* 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" );
}
Esempio n. 7
0
/*
* 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 );
}
Esempio n. 8
0
/*
* 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 );
	}
}
Esempio n. 9
0
/*
* 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 );
}
Esempio n. 10
0
/*
* 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" );
}
Esempio n. 11
0
/*
* 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" );
}
Esempio n. 12
0
/*
* 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;
}
Esempio n. 13
0
/*
* 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();
}
Esempio n. 14
0
/*
* 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 );
	}
}
Esempio n. 15
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 );
}