Exemple #1
0
/*
* TV_Lobby_SendClientMessages
*/
static void TV_Lobby_SendClientMessages( void )
{
    int i;
    client_t *client;

    // send a message to each connected client
    for( i = 0, client = tvs.clients; i < tv_maxclients->integer; i++, client++ )
    {
        if( client->state != CS_SPAWNED )
            continue;

        if( client->relay )
            continue;

        if( !TV_Lobby_SendClientDatagram( client ) )
        {
            Com_Printf( "%s" S_COLOR_WHITE ": Error sending message: %s\n", client->name, NET_ErrorString() );
            if( client->reliable )
            {
                TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error sending message: %s\n",
                                          NET_ErrorString() );
            }
        }
    }
}
Exemple #2
0
/*
* TV_Downstream_SendClientsFragments
*/
qboolean TV_Downstream_SendClientsFragments( void )
{
	client_t *client;
	int i;
	qboolean remaining = qfalse;

	// send a message to each connected client
	for( i = 0, client = tvs.clients; i < tv_maxclients->integer; i++, client++ )
	{
		if( client->state == CS_FREE || client->state == CS_ZOMBIE )
			continue;

		if( !client->netchan.unsentFragments )
			continue;

		if( !Netchan_TransmitNextFragment( &client->netchan ) )
		{
			Com_Printf( "%s" S_COLOR_WHITE ": Error sending fragment: %s\n", client->name, NET_ErrorString() );
			if( client->reliable )
			{
				TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error sending fragment: %s\n",
					NET_ErrorString() );
			}
			continue;
		}

		if( client->netchan.unsentFragments )
			remaining = qtrue;
	}

	return remaining;
}
Exemple #3
0
/*
* TV_Downstream_AddServerCommand
* 
* The given command will be transmitted to the client, and is guaranteed to
* not have future snapshot_t executed before it is executed
*/
void TV_Downstream_AddServerCommand( client_t *client, const char *cmd )
{
	int index;
	unsigned int i;

	assert( client );
	assert( cmd && strlen( cmd ) );

	if( !cmd || !cmd[0] || !strlen( cmd ) )
		return;

	// ch : To avoid overflow of messages from excessive amount of configstrings
	// we batch them here. On incoming "cs" command, we'll trackback the queue
	// to find a pending "cs" command that has space in it. If we'll find one,
	// we'll batch this there, if not, we'll create a new one.
	if( !strncmp( cmd, "cs ", 3 ) )
	{
		// length of the index/value (leave room for one space and null char)
		size_t len = strlen( cmd ) - 1;
		for( i = client->reliableSequence; i > client->reliableSent; i-- )
		{
			size_t otherLen;
			char *otherCmd;

			otherCmd= client->reliableCommands[i & ( MAX_RELIABLE_COMMANDS - 1)];
			if( !strncmp( otherCmd, "cs ", 3 ) )
			{
				otherLen = strlen( otherCmd );
				// is there any room? (should check for sizeof client->reliableCommands[0]?)
				if( (otherLen + len) < MAX_STRING_CHARS )
				{
					// yahoo, put it in here
					Q_strncatz( otherCmd, cmd + 2, MAX_STRING_CHARS - 1 );
					return;
				}
			}
		}
	}

	client->reliableSequence++;
	// if we would be losing an old command that hasn't been acknowledged, we must drop the connection
	// we check == instead of >= so a broadcast print added by SV_DropClient() doesn't cause a recursive drop client
	if( client->reliableSequence - client->reliableAcknowledge == MAX_RELIABLE_COMMANDS + 1 )
	{
		//Com_Printf( "===== pending server commands =====\n" );
		for( i = client->reliableAcknowledge + 1; i <= client->reliableSequence; i++ )
		{
			Com_DPrintf( "cmd %5d: %s\n", i, client->reliableCommands[i & ( MAX_RELIABLE_COMMANDS-1 )] );
		}
		Com_DPrintf( "cmd %5d: %s\n", i, cmd );
		TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Server command overflow" );
		return;
	}

	index = client->reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 );
	Q_strncpyz( client->reliableCommands[index], cmd, sizeof( client->reliableCommands[index] ) );
}
Exemple #4
0
/*
* TV_Module_DropClient
*/
static void TV_Module_DropClient( relay_t *relay, int numClient, int type, const char *message )
{
	client_t *client;

	if( !relay )
	{
		Com_Printf( "Error: TV_Module_DropClient: Relay not set\n" );
		return;
	}

	if( numClient < 0 || numClient >= tv_maxclients->integer )
		TV_Relay_Error( relay, "TV_Module_DropClient: Invalid numClient" );

	client = &tvs.clients[numClient];

	if( client->state == CS_FREE || client->state == CS_ZOMBIE || client->relay != relay )
		TV_Relay_Error( relay, "TV_Module_DropClient: Invalid client" );

	if( message )
		TV_Downstream_DropClient( client, type, "%s", message );
	else
		TV_Downstream_DropClient( client, type, "" );
}
Exemple #5
0
/*
* TV_Downstream_UserinfoChanged
* 
* Pull specific info from a newly changed userinfo string
* into a more C friendly form.
*/
void TV_Downstream_UserinfoChanged( client_t *client )
{
	char *val;

	assert( client );
	assert( Info_Validate( client->userinfo ) );

	// force the IP key/value pair so the game can filter based on ip
	if( !Info_SetValueForKey( client->userinfo, "socket", NET_SocketTypeToString( client->netchan.socket->type ) ) )
	{
		TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (socket)\n" );
		return;
	}
	if( !Info_SetValueForKey( client->userinfo, "ip", NET_AddressToString( &client->netchan.remoteAddress ) ) )
	{
		TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (ip)\n" );
		return;
	}

	// we handle name ourselves here, since tv module doesn't know about all the players
	val = TV_Downstream_FixName( Info_ValueForKey( client->userinfo, "name" ), client );
	Q_strncpyz( client->name, val, sizeof( client->name ) );
	if( !Info_SetValueForKey( client->userinfo, "name", client->name ) )
	{
		TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (name)" );
		return;
	}

	if( client->relay )
		TV_Relay_ClientUserinfoChanged( client->relay, client );

	if( !Info_Validate( client->userinfo ) )
	{
		TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error: Invalid userinfo (after game)" );
		return;
	}
}
/*
* TV_Downstream_UserinfoCommand_f
*/
static void TV_Downstream_UserinfoCommand_f( client_t *client )
{
	char *info;

	info = Cmd_Argv( 1 );

	if( !Info_Validate( info ) )
	{
		TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error: Invalid userinfo" );
		return;
	}

	Q_strncpyz( client->userinfo, info, sizeof( client->userinfo ) );
	TV_Downstream_UserinfoChanged( client );
}
Exemple #7
0
/*
* TV_Downstream_SendClientMessages
*/
void TV_Downstream_SendClientMessages( void )
{
	int i;
	client_t *client;
	msg_t message;
	qbyte messageData[MAX_MSGLEN];

	// send a message to each connected client
	for( i = 0, client = tvs.clients; i < tv_maxclients->integer; i++, client++ )
	{
		if( client->state == CS_FREE || client->state == CS_ZOMBIE )
			continue;

		if( client->state < CS_SPAWNED )
		{
			// send pending reliable commands, or send heartbeats for not timing out
			/*			if( client->reliableSequence > client->reliableSent ||
			(client->reliableSequence > client->reliableAcknowledge &&
			tvs.realtime - client->lastPacketSentTime > 50) ||
			tvs.realtime - client->lastPacketSentTime > 500 ) */
			if( client->reliableSequence > client->reliableAcknowledge ||
				tvs.realtime - client->lastPacketSentTime > 1000 )
			{
				TV_Downstream_InitClientMessage( client, &message, messageData, sizeof( messageData ) );

				TV_Downstream_AddReliableCommandsToMessage( client, &message );
				if( !TV_Downstream_SendMessageToClient( client, &message ) )
				{
					Com_Printf( "%s" S_COLOR_WHITE ": Error sending message: %s\n", client->name, NET_ErrorString() );
					if( client->reliable )
					{
						TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error sending message: %s\n",
							NET_ErrorString() );
					}
				}
			}
		}
	}
}
Exemple #8
0
/*
* TV_Downstream_CheckTimeouts
*/
void TV_Downstream_CheckTimeouts( void )
{
	client_t *client;
	int i;

#ifdef TCP_SUPPORT
	// timeout incoming upstreams
	for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ )
	{
		if( tvs.incoming[i].active && tvs.incoming[i].time + 1000 * 15 < tvs.realtime )
		{
			Com_Printf( "Incoming TCP upstream from %s timed out\n", NET_AddressToString( &tvs.incoming[i].address ) );
			NET_CloseSocket( &tvs.incoming[i].socket );
			tvs.incoming[i].active = qfalse;
		}
	}
#endif

	// timeout clients
	for( i = 0, client = tvs.clients; i < tv_maxclients->integer; i++, client++ )
	{
		// message times may be wrong across a changelevel
		if( client->lastPacketReceivedTime > tvs.realtime )
			client->lastPacketReceivedTime = tvs.realtime;

		if( client->state == CS_ZOMBIE && client->lastPacketReceivedTime + 1000 * tv_zombietime->value < tvs.realtime )
		{
			client->state = CS_FREE; // can now be reused
			if( client->individual_socket )
				NET_CloseSocket( &client->socket );
			continue;
		}

		if( ( client->state != CS_FREE && client->state != CS_ZOMBIE ) &&
			( client->lastPacketReceivedTime + 1000 * tv_timeout->value < tvs.realtime ) )
		{
			TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Upstream timed out" );
			client->state = CS_FREE; // don't bother with zombie state
			if( client->socket.open )
				NET_CloseSocket( &client->socket );
		}

		// timeout downloads left open
		if( ( client->state != CS_FREE && client->state != CS_ZOMBIE ) &&
			( client->download.name && client->download.timeout < tvs.realtime ) )
		{
			Com_Printf( "Download of %s to %s" S_COLOR_WHITE " timed out\n", client->download.name, client->name );

			if( client->download.data )
			{
				FS_FreeBaseFile( client->download.data );
				client->download.data = NULL;
			}

			Mem_ZoneFree( client->download.name );
			client->download.name = NULL;

			client->download.size = 0;
			client->download.timeout = 0;
		}
	}
}
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 );
					}
				}
			}
		}
	}
}
/*
* TV_Downstream_Disconnect_f
* The client is going to disconnect, so remove the upstream immediately
*/
static void TV_Downstream_Disconnect_f( client_t *client )
{
	TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "User disconnected" );
}