Exemple #1
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" );
}
Exemple #2
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 );
	}
}
Exemple #3
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" );
}
Exemple #4
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" );
}
Exemple #5
0
/*
* CL_GetServers_f
*/
void CL_GetServers_f( void )
{
	netadr_t adr, *padr;
	char *requeststring;
	int i;
	char *modname, *master;
	trie_error_t err;

	filter_allow_full = qfalse;
	filter_allow_empty = qfalse;
	for( i = 2; i < Cmd_Argc(); i++ )
	{
		if( !Q_stricmp( "full", Cmd_Argv( i ) ) )
			filter_allow_full = qtrue;

		if( !Q_stricmp( "empty", Cmd_Argv( i ) ) )
			filter_allow_empty = qtrue;
	}

	if( !Q_stricmp( Cmd_Argv( 1 ), "local" ) )
	{
		if( localQueryTimeStamp + LAN_SERVER_PINGING_TIMEOUT > Sys_Milliseconds() ) {
			return;
		}

		padr = &adr;
		localQueryTimeStamp = Sys_Milliseconds();

		// send a broadcast packet
		Com_DPrintf( "pinging broadcast...\n" );

		// erm... modname isn't sent in local queries?

		requeststring = va( "info %i %s %s", SERVERBROWSER_PROTOCOL_VERSION,
			filter_allow_full ? "full" : "",
			filter_allow_empty ? "empty" : "" );

		for( i = 0; i < NUM_BROADCAST_PORTS; i++ )
		{
			NET_BroadcastAddress( padr, PORT_SERVER + i );
			Netchan_OutOfBandPrint( &cls.socket_udp, padr, requeststring );
		}
		return;
	}

	//get what master
	master = Cmd_Argv( 2 );
	if( !master || !( *master ) )
		return;

	modname = Cmd_Argv( 3 );
	// never allow anyone to use DEFAULT_BASEGAME as mod name
	if( !modname || !modname[0] || !Q_stricmp( modname, DEFAULT_BASEGAME ) )
		modname = APPLICATION;

	assert( modname[0] );

	// check memory cache
	QMutex_Lock( resolveLock );
	err = Trie_Find( serverlist_masters_trie, master, TRIE_EXACT_MATCH, (void **)&padr );
	QMutex_Unlock( resolveLock );

	if( err == TRIE_OK && ( padr->type == NA_IP || padr->type == NA_IP6 ) )
	{
		const char *cmdname;
		socket_t *socket;

		if ( padr->type == NA_IP )
		{
			cmdname = "getservers";
			socket = &cls.socket_udp;
		}
		else
		{
			cmdname = "getserversExt";
			socket = &cls.socket_udp6;
		}

		// create the message
		requeststring = va( "%s %c%s %i %s %s", cmdname, toupper( modname[0] ), modname+1, SERVERBROWSER_PROTOCOL_VERSION,
			filter_allow_full ? "full" : "",
			filter_allow_empty ? "empty" : "" );

		if( NET_GetAddressPort( padr ) == 0 )
			NET_SetAddressPort( padr, PORT_MASTER );

		Netchan_OutOfBandPrint( socket, padr, requeststring );

		Com_DPrintf( "quering %s...%s: %s\n", master, NET_AddressToString(padr), requeststring );
	}
	else
	{
		Com_Printf( "Bad address: %s\n", master );
	}
}
Exemple #6
0
/*
* NET_IP_OpenSocket
*/
static qboolean NET_IP_OpenSocket( socket_t *sock, const netadr_t *address, socket_type_t socktype, qboolean server )
{
    int newsocket;
    const char *proto, *stype;

    assert( sock && !sock->open );
    assert( address );

    if( address->type == NA_IP )
        proto = "IP";
    else if( address->type == NA_IP6 )
        proto = "IPv6";
    else
    {
        NET_SetErrorString( "Invalid address type" );
        return qfalse;
    }

    if( socktype == SOCKET_UDP )
        stype = "UDP";
#ifdef TCP_SUPPORT
    else if( socktype == SOCKET_TCP )
        stype = "TCP";
#endif
    else
    {
        NET_SetErrorString( "Invalid socket type" );
        return qfalse;
    }

    if( NET_IsAnyAddress( address ) )
    {
        Com_Printf( "Opening %s/%s socket: *:%hu\n", stype, proto, NET_GetAddressPort( address ) );
    }
    else
    {
        Com_Printf( "Opening %s/%s socket: %s\n", stype, proto, NET_AddressToString( address ) );
    }

    if( ( newsocket = OpenSocket( socktype, ( address->type == NA_IP6 ? qtrue : qfalse ) ) ) == INVALID_SOCKET )
        return qfalse;

    // make it non-blocking
    if( !NET_SocketMakeNonBlocking( newsocket ) )
    {
        Sys_NET_SocketClose( newsocket );
        return qfalse;
    }

    if( socktype == SOCKET_UDP )
    {
        // make it broadcast capable
        if( !NET_SocketMakeBroadcastCapable( newsocket ) )
        {
            Sys_NET_SocketClose( newsocket );
            return qfalse;
        }
    }

    // wsw : pb : make it reusable (fast release of port when quit)
    /*if( setsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i)) == -1 ) {
    SetErrorStringFromErrno( "setsockopt" );
    return 0;
    }*/

    if( !BindSocket( newsocket, address ) )
    {
        Sys_NET_SocketClose( newsocket );
        return qfalse;
    }

    sock->open = qtrue;
    sock->type = socktype;
    sock->address = *address;
    sock->server = server;
    sock->handle = newsocket;

    return qtrue;
}
Exemple #7
0
/*
* CL_ParseServerData
*/
static void CL_ParseServerData( msg_t *msg )
{
	const char *str, *gamedir;
	int i, sv_bitflags, numpure;
	int http_portnum;

	Com_DPrintf( "Serverdata packet received.\n" );

	// wipe the client_state_t struct

	CL_ClearState();
	CL_SetClientState( CA_CONNECTED );

	// parse protocol version number
	i = MSG_ReadLong( msg );

	if( i != APP_PROTOCOL_VERSION && !(cls.demo.playing && i == APP_DEMO_PROTOCOL_VERSION) )
		Com_Error( ERR_DROP, "Server returned version %i, not %i", i, APP_PROTOCOL_VERSION );

	cl.servercount = MSG_ReadLong( msg );
	cl.snapFrameTime = (unsigned int)MSG_ReadShort( msg );
	cl.gamestart = true;

	// set extrapolation time to half snapshot time
	Cvar_ForceSet( "cl_extrapolationTime", va( "%i", (unsigned int)( cl.snapFrameTime * 0.5 ) ) );
	cl_extrapolationTime->modified = false;

	// base game directory
	str = MSG_ReadString( msg );
	if( !str || !str[0] )
		Com_Error( ERR_DROP, "Server sent an empty base game directory" );
	if( !COM_ValidateRelativeFilename( str ) || strchr( str, '/' ) )
		Com_Error( ERR_DROP, "Server sent an invalid base game directory: %s", str );
	if( strcmp( FS_BaseGameDirectory(), str ) )
	{
		Com_Error( ERR_DROP, "Server has different base game directory (%s) than the client (%s)", str,
			FS_BaseGameDirectory() );
	}

	// game directory
	str = MSG_ReadString( msg );
	if( !str || !str[0] )
		Com_Error( ERR_DROP, "Server sent an empty game directory" );
	if( !COM_ValidateRelativeFilename( str ) || strchr( str, '/' ) )
		Com_Error( ERR_DROP, "Server sent an invalid game directory: %s", str );
	gamedir = FS_GameDirectory();
	if( strcmp( str, gamedir ) )
	{
		// shutdown the cgame module first in case it is running for whatever reason
		// (happens on wswtv in lobby), otherwise precaches that are going to follow
		// will probably f**k up (like models trying to load before the world model)
		CL_GameModule_Shutdown();

		if( !FS_SetGameDirectory( str, true ) )
			Com_Error( ERR_DROP, "Failed to load game directory set by server: %s", str );
		ML_Restart( true );
	}

	// parse player entity number
	cl.playernum = MSG_ReadShort( msg );

	// get the full level name
	Q_strncpyz( cl.servermessage, MSG_ReadString( msg ), sizeof( cl.servermessage ) );

	sv_bitflags = MSG_ReadByte( msg );

	if( cls.demo.playing )
	{
		cls.reliable = ( sv_bitflags & SV_BITFLAGS_RELIABLE );
	}
	else
	{
		if( cls.reliable != ( ( sv_bitflags & SV_BITFLAGS_RELIABLE ) != 0 ) )
			Com_Error( ERR_DROP, "Server and client disagree about connection reliability" );
	}

	// builting HTTP server port
	if( cls.httpbaseurl ) {
		Mem_Free( cls.httpbaseurl );
		cls.httpbaseurl = NULL;
	}

	if( ( sv_bitflags & SV_BITFLAGS_HTTP ) != 0 ) {
		if( ( sv_bitflags & SV_BITFLAGS_HTTP_BASEURL ) != 0 ) {
			// read base upstream url
			cls.httpbaseurl = ZoneCopyString( MSG_ReadString( msg ) );
		}
		else {
			http_portnum = MSG_ReadShort( msg ) & 0xffff;
			cls.httpaddress = cls.serveraddress;
			if( cls.httpaddress.type == NA_IP6 ) {
				cls.httpaddress.address.ipv6.port = BigShort( http_portnum );
			} else {
				cls.httpaddress.address.ipv4.port = BigShort( http_portnum );
			}
			if( http_portnum ) {
				if( cls.httpaddress.type == NA_LOOPBACK ) {
					cls.httpbaseurl = ZoneCopyString( va( "http://localhost:%hu/", http_portnum ) );
				}
				else {
					cls.httpbaseurl = ZoneCopyString( va( "http://%s/", NET_AddressToString( &cls.httpaddress ) ) );
				}
			}
		}
	}

	// pure list

	// clean old, if necessary
	Com_FreePureList( &cls.purelist );

	// add new
	numpure = MSG_ReadShort( msg );
	while( numpure > 0 )
	{
		const char *pakname = MSG_ReadString( msg );
		const unsigned checksum = MSG_ReadLong( msg );

		Com_AddPakToPureList( &cls.purelist, pakname, checksum, NULL );

		numpure--;
	}

	//assert( numpure == 0 );

	// get the configstrings request
	CL_AddReliableCommand( va( "configstrings %i 0", cl.servercount ) );

	cls.sv_pure = ( sv_bitflags & SV_BITFLAGS_PURE ) != 0;
	cls.sv_tv = ( sv_bitflags & SV_BITFLAGS_TVSERVER ) != 0;

#ifdef PURE_CHEAT
	cls.sv_pure = false;
#endif

	cls.wakelock = Sys_AcquireWakeLock();

	if( !cls.demo.playing && ( cls.serveraddress.type == NA_IP ) )
		Steam_AdvertiseGame( cls.serveraddress.address.ipv4.ip, NET_GetAddressPort( &cls.serveraddress ) );

	// separate the printfs so the server message can have a color
	Com_Printf( S_COLOR_WHITE "\n" "=====================================\n" );
	Com_Printf( S_COLOR_WHITE "%s\n\n", cl.servermessage );
}
Exemple #8
0
/*
* SV_ReadPackets
*/
static void SV_ReadPackets( void )
{
	int i, socketind, ret;
	client_t *cl;
#ifdef TCP_SUPPORT
	socket_t newsocket;
	netadr_t mmserver;
#endif
	int game_port;
	socket_t *socket;
	netadr_t address;

	static msg_t msg;
	static qbyte msgData[MAX_MSGLEN];

	socket_t* sockets [] =
	{
		&svs.socket_loopback,
		&svs.socket_udp,
		&svs.socket_udp6,
	};

#ifdef TCP_SUPPORT

	if( SV_MM_Initialized() ) 
		SV_MM_NetAddress( &mmserver );

	if( svs.socket_tcp.open )
	{
		while( qtrue )
		{
			// find a free slot
			for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ )
			{
				if( !svs.incoming[i].active )
					break;
			}
			if( i == MAX_INCOMING_CONNECTIONS )
				break;

			if( ( ret = NET_Accept( &svs.socket_tcp, &newsocket, &address ) ) == 0 )
				break;
			if( ret == -1 )
			{
				Com_Printf( "NET_Accept: Error: %s\n", NET_ErrorString() );
				continue;
			}

			Com_Printf( "New TCP connection from %s\n", NET_AddressToString( &address ) );

			svs.incoming[i].active = qtrue;
			svs.incoming[i].socket = newsocket;
			svs.incoming[i].address = address;
			svs.incoming[i].time = svs.realtime;
		}
	}

	for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ )
	{
		if( !svs.incoming[i].active )
			continue;

		ret = NET_GetPacket( &svs.incoming[i].socket, &address, &msg );
		if( ret == -1 )
		{
			Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() );
			NET_CloseSocket( &svs.incoming[i].socket );
			svs.incoming[i].active = qfalse;
		}
		else if( ret == 1 )
		{
			if( SV_MM_Initialized() && NET_CompareBaseAddress( &mmserver, &address ) )
			{
				Com_DPrintf( "TCP packet from matchmaker\n" );
				SV_MM_SetConnection( &svs.incoming[i] );
				SV_MM_Packet( &msg );
				SV_MM_SetConnection( NULL );
				continue;
			}
			if( *(int *)msg.data != -1 )
			{
				Com_Printf( "Sequence packet without connection\n" );
				NET_CloseSocket( &svs.incoming[i].socket );
				svs.incoming[i].active = qfalse;
				continue;
			}

			Com_Printf( "Connectionless TCP packet from: %s\n", NET_AddressToString( &address ) );

			SV_ConnectionlessPacket( &svs.incoming[i].socket, &address, &msg );
		}
	}
#endif

	MSG_Init( &msg, msgData, sizeof( msgData ) );

	for( socketind = 0; socketind < sizeof( sockets ) / sizeof( sockets[0] ); socketind++ )
	{
		socket = sockets[socketind];

		if( !socket->open )
			continue;

		while( ( ret = NET_GetPacket( socket, &address, &msg ) ) != 0 )
		{
			if( ret == -1 )
			{
				Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() );
				continue;
			}

			// check for connectionless packet (0xffffffff) first
			if( *(int *)msg.data == -1 )
			{
				SV_ConnectionlessPacket( socket, &address, &msg );
				continue;
			}

			// read the game port out of the message so we can fix up
			// stupid address translating routers
			MSG_BeginReading( &msg );
			MSG_ReadLong( &msg ); // sequence number
			MSG_ReadLong( &msg ); // sequence number
			game_port = MSG_ReadShort( &msg ) & 0xffff;
			// data follows

			// check for packets from connected clients
			for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
			{
				unsigned short addr_port;

				if( cl->state == CS_FREE || cl->state == CS_ZOMBIE )
					continue;
				if( cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) )
					continue;
				if( !NET_CompareBaseAddress( &address, &cl->netchan.remoteAddress ) )
					continue;
				if( cl->netchan.game_port != game_port )
					continue;

				addr_port = NET_GetAddressPort( &address );
				if( NET_GetAddressPort( &cl->netchan.remoteAddress ) != addr_port )
				{
					Com_Printf( "SV_ReadPackets: fixing up a translated port\n" );
					NET_SetAddressPort( &cl->netchan.remoteAddress, addr_port );
				}

				if( SV_ProcessPacket( &cl->netchan, &msg ) ) // this is a valid, sequenced packet, so process it
				{
					cl->lastPacketReceivedTime = svs.realtime;
					SV_ParseClientMessage( cl, &msg );
				}
				break;
			}
		}
	}

	// handle clients with individual sockets
	for( i = 0; i < sv_maxclients->integer; i++ )
	{
		cl = &svs.clients[i];

		if( cl->state == CS_ZOMBIE || cl->state == CS_FREE )
			continue;

		if( !cl->individual_socket )
			continue;

		// not while, we only handle one packet per client at a time here
		if( ( ret = NET_GetPacket( cl->netchan.socket, &address, &msg ) ) != 0 )
		{
			if( ret == -1 )
			{
				Com_Printf( "Error receiving packet from %s: %s\n", NET_AddressToString( &cl->netchan.remoteAddress ),
					NET_ErrorString() );
				if( cl->reliable )
					SV_DropClient( cl, DROP_TYPE_GENERAL, "Error receiving packet: %s", NET_ErrorString() );
			}
			else
			{
				if( SV_ProcessPacket( &cl->netchan, &msg ) )
				{
					// this is a valid, sequenced packet, so process it
					cl->lastPacketReceivedTime = svs.realtime;
					SV_ParseClientMessage( cl, &msg );
				}
			}
		}
	}
}
Exemple #9
0
/*
* TV_Downstream_ReadPackets
*/
void TV_Downstream_ReadPackets( void )
{
	int i, socketind, ret, game_port;
	client_t *cl;
#ifdef TCP_SUPPORT
	socket_t newsocket;
#endif
	socket_t *socket;
	netadr_t address;
	msg_t msg;
	qbyte msgData[MAX_MSGLEN];

	socket_t* sockets [] =
	{
		&tvs.socket_udp,
		&tvs.socket_udp6,
	};

#ifdef TCP_SUPPORT
	if( tvs.socket_tcp.open )
	{
		while( qtrue )
		{
			// find a free slot
			for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ )
			{
				if( !tvs.incoming[i].active )
					break;
			}
			if( i == MAX_INCOMING_CONNECTIONS )
				break;

			if( ( ret = NET_Accept( &tvs.socket_tcp, &newsocket, &address ) ) == 0 )
				break;
			if( ret == -1 )
			{
				Com_Printf( "NET_Accept: Error: %s\n", NET_ErrorString() );
				continue;
			}

			tvs.incoming[i].active = qtrue;
			tvs.incoming[i].socket = newsocket;
			tvs.incoming[i].address = address;
			tvs.incoming[i].time = tvs.realtime;
		}
	}

	for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ )
	{
		if( !tvs.incoming[i].active )
			continue;

		ret = NET_GetPacket( &tvs.incoming[i].socket, &address, &msg );
		if( ret == -1 )
		{
			NET_CloseSocket( &tvs.incoming[i].socket );
			tvs.incoming[i].active = qfalse;
		}
		else if( ret == 1 )
		{
			if( *(int *)msg.data != -1 )
			{                  // sequence packet without upstreams
				NET_CloseSocket( &tvs.incoming[i].socket );
				tvs.incoming[i].active = qfalse;
				continue;
			}

			TV_Downstream_UpstreamlessPacket( &tvs.incoming[i].socket, &address, &msg );
		}
	}
#endif

	MSG_Init( &msg, msgData, sizeof( msgData ) );

	for( socketind = 0; socketind < sizeof( sockets ) / sizeof( sockets[0] ); socketind++ )
	{
		socket = sockets[socketind];

		while( socket->open && ( ret = NET_GetPacket( socket, &address, &msg ) ) != 0 )
		{
			if( ret == -1 )
			{
				Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() );
				continue;
			}

			// check for upstreamless packet (0xffffffff) first
			if( *(int *)msg.data == -1 )
			{
				TV_Downstream_UpstreamlessPacket( socket, &address, &msg );
				continue;
			}

			// read the game port out of the message so we can fix up
			// stupid address translating routers
			MSG_BeginReading( &msg );
			MSG_ReadLong( &msg ); // sequence number
			MSG_ReadLong( &msg ); // sequence number
			game_port = MSG_ReadShort( &msg ) & 0xffff;
			// data follows

			// check for packets from connected clients
			for( i = 0, cl = tvs.clients; i < tv_maxclients->integer; i++, cl++ )
			{
				unsigned short remoteaddr_port, addr_port;

				if( cl->state == CS_FREE || cl->state == CS_ZOMBIE )
					continue;
				if( !NET_CompareBaseAddress( &address, &cl->netchan.remoteAddress ) )
					continue;
				if( cl->netchan.game_port != game_port )
					continue;

				remoteaddr_port = NET_GetAddressPort( &cl->netchan.remoteAddress );
				addr_port = NET_GetAddressPort( &address );
				if( remoteaddr_port != addr_port )
				{
					Com_DPrintf( "%s" S_COLOR_WHITE ": Fixing up a translated port from %i to %i\n", cl->name,
						remoteaddr_port, addr_port );
					NET_SetAddressPort( &cl->netchan.remoteAddress, addr_port );
				}

				if( TV_Downstream_ProcessPacket( &cl->netchan, &msg ) )
				{                                           // this is a valid, sequenced packet, so process it
					cl->lastPacketReceivedTime = tvs.realtime;
					TV_Downstream_ParseClientMessage( cl, &msg );
				}
				break;
			}
		}
	}

	// handle clients with individual sockets
	for( i = 0; i < tv_maxclients->integer; i++ )
	{
		cl = &tvs.clients[i];

		if( cl->state == CS_ZOMBIE || cl->state == CS_FREE )
			continue;

		if( !cl->individual_socket )
			continue;

		// not while, we only handle one packet per client at a time here
		if( ( ret = NET_GetPacket( cl->netchan.socket, &address, &msg ) ) != 0 )
		{
			if( ret == -1 )
			{
				Com_Printf( "%s" S_COLOR_WHITE ": Error receiving packet: %s\n", cl->name, NET_ErrorString() );
				if( cl->reliable )
					TV_Downstream_DropClient( cl, DROP_TYPE_GENERAL, "Error receiving packet: %s", NET_ErrorString() );
			}
			else
			{
				if( *(int *)msg.data == -1 )
				{
					TV_Downstream_UpstreamlessPacket( cl->netchan.socket, &address, &msg );
				}
				else
				{
					if( TV_Downstream_ProcessPacket( &cl->netchan, &msg ) )
					{
						cl->lastPacketReceivedTime = tvs.realtime;
						TV_Downstream_ParseClientMessage( cl, &msg );
					}
				}
			}
		}
	}
}