Пример #1
0
/*
==================
SV_FinalMessage

Used by SV_Shutdown to send a final message to all
connected clients before the server goes down.  The messages are sent immediately,
not just stuck on the outgoing message list, because the server is going
to totally exit after returning from this function.
==================
*/
void SV_FinalMessage( char *message, qboolean reconnect )
{
	sv_client_t	*cl;
	byte		msg_buf[1024];
	sizebuf_t		msg;
	int		i;
	
	BF_Init( &msg, "FinalMessage", msg_buf, sizeof( msg_buf ));
	BF_WriteByte( &msg, svc_print );
	BF_WriteByte( &msg, PRINT_HIGH );
	BF_WriteString( &msg, va( "%s\n", message ));

	if( reconnect )
	{
		BF_WriteByte( &msg, svc_changing );

		if( sv.loadgame || sv_maxclients->integer > 1 || sv.changelevel )
			BF_WriteOneBit( &msg, 1 ); // changelevel
		else BF_WriteOneBit( &msg, 0 );
	}
	else
	{
		BF_WriteByte( &msg, svc_disconnect );
	}

	// send it twice
	// stagger the packets to crutch operating system limited buffers
	for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
		if( cl->state >= cs_connected && !cl->fakeclient )
			Netchan_Transmit( &cl->netchan, BF_GetNumBytesWritten( &msg ), BF_GetData( &msg ));

	for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
		if( cl->state >= cs_connected && !cl->fakeclient )
			Netchan_Transmit( &cl->netchan, BF_GetNumBytesWritten( &msg ), BF_GetData( &msg ));
}
Пример #2
0
/*
=================
SV_BroadcastPrintf

Sends text to all active clients
=================
*/
void SV_BroadcastPrintf( int level, char *fmt, ... )
{
	char		string[MAX_SYSPATH];
	va_list		argptr;
	sv_client_t	*cl;
	int		i;

	if( !sv.state ) return;

	va_start( argptr, fmt );
	Q_vsprintf( string, fmt, argptr );
	va_end( argptr );
	
	// echo to console
	if( host.type == HOST_DEDICATED ) Msg( "%s", string );

	for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
	{
		if( level < cl->messagelevel ) continue;
		if( cl->state != cs_spawned ) continue;
		if( cl->fakeclient ) continue;

		BF_WriteByte( &cl->netchan.message, svc_print );
		BF_WriteByte( &cl->netchan.message, level );
		BF_WriteString( &cl->netchan.message, string );
	}
}
Пример #3
0
/*
=======================
SV_SendClientDatagram
=======================
*/
void SV_SendClientDatagram( sv_client_t *cl )
{
	byte    	msg_buf[NET_MAX_PAYLOAD];
	sizebuf_t	msg;

	svs.currentPlayer = cl;
	svs.currentPlayerNum = (cl - svs.clients);

	BF_Init( &msg, "Datagram", msg_buf, sizeof( msg_buf ));

	// always send servertime at new frame
	BF_WriteByte( &msg, svc_time );
	BF_WriteFloat( &msg, sv.time );

	SV_WriteClientdataToMessage( cl, &msg );
	SV_WriteEntitiesToClient( cl, &msg );

	// copy the accumulated multicast datagram
	// for this client out to the message
	if( BF_CheckOverflow( &cl->datagram )) MsgDev( D_WARN, "datagram overflowed for %s\n", cl->name );
	else BF_WriteBits( &msg, BF_GetData( &cl->datagram ), BF_GetNumBitsWritten( &cl->datagram ));
	BF_Clear( &cl->datagram );

	if( BF_CheckOverflow( &msg ))
	{	
		// must have room left for the packet header
		MsgDev( D_WARN, "msg overflowed for %s\n", cl->name );
		BF_Clear( &msg );
	}

	// send the datagram
	Netchan_TransmitBits( &cl->netchan, BF_GetNumBitsWritten( &msg ), BF_GetData( &msg ));
}
Пример #4
0
/*
===================
Cmd_ForwardToServer

adds the current command line as a clc_stringcmd to the client message.
things like godmode, noclip, etc, are commands directed to the server,
so when they are typed in at the console, they will need to be forwarded.
===================
*/
void Cmd_ForwardToServer( void )
{
	char	str[MAX_CMD_BUFFER];
	
	if( cls.demoplayback )
	{
		if( !Q_stricmp( Cmd_Argv( 0 ), "pause" ))
			cl.refdef.paused ^= 1;
		return;
	}

	if( cls.state != ca_connected && cls.state != ca_active )
	{
		MsgDev( D_INFO, "Can't \"%s\", not connected\n", Cmd_Argv( 0 ));
		return; // not connected
	}

	BF_WriteByte( &cls.netchan.message, clc_stringcmd );

	str[0] = 0;
	if( Q_stricmp( Cmd_Argv( 0 ), "cmd" ))
	{
		Q_strcat( str, Cmd_Argv( 0 ));
		Q_strcat( str, " " );
	}
	
	if( Cmd_Argc() > 1 )
		Q_strcat( str, Cmd_Args( ));
	else Q_strcat( str, "\n" );

	BF_WriteString( &cls.netchan.message, str );
}
Пример #5
0
/*
==============
CL_ParseResourceList

==============
*/
void CL_ParseResourceList( sizebuf_t *msg )
{
	int	i = 0;

	Q_memset( &reslist, 0, sizeof( resourcelist_t ));

	reslist.rescount = BF_ReadWord( msg ) - 1;

	for( i = 0; i < reslist.rescount; i++ )
	{
		reslist.restype[i] = BF_ReadWord( msg );
		Q_strncpy( reslist.resnames[i], BF_ReadString( msg ), CS_SIZE );
	}

	cls.downloadcount = 0;

	for( i = 0; i < reslist.rescount; i++ )
	{
		if( reslist.restype[i] == t_sound )
			CL_CheckingSoundResFile( reslist.resnames[i] );
		else CL_CheckingResFile( reslist.resnames[i] );
	}

	if( !cls.downloadcount )
	{
		BF_WriteByte( &cls.netchan.message, clc_stringcmd );
		BF_WriteString( &cls.netchan.message, "continueloading" );
	}
}
Пример #6
0
/*
=============================================================================

movevars_t communication

=============================================================================
*/
qboolean MSG_WriteDeltaMovevars( sizebuf_t *msg, movevars_t *from, movevars_t *to )
{
	delta_t		*pField;
	delta_info_t	*dt;
	int		i, startBit;
	int		numChanges = 0;

	dt = Delta_FindStruct( "movevars_t" );
	ASSERT( dt && dt->bInitialized );

	pField = dt->pFields;
	ASSERT( pField );

	startBit = msg->iCurBit;

	// activate fields and call custom encode func
	Delta_CustomEncode( dt, from, to );

	BF_WriteByte( msg, svc_deltamovevars );

	// process fields
	for( i = 0; i < dt->numFields; i++, pField++ )
	{
		if( Delta_WriteField( msg, pField, from, to, 0.0f ))
			numChanges++;
	}

	// if we have no changes - kill the message
	if( !numChanges )
	{
		BF_SeekToBit( msg, startBit );
		return false;
	}
	return true;
}
Пример #7
0
/*
==============
CL_CheckingResFile

==============
*/
void CL_CheckingResFile( char *pResFileName )
{
	sizebuf_t	buf;
	byte	data[32];

	if( FS_FileExists( pResFileName, false ))
		return;	// already exists

	cls.downloadcount++;

	
	if( cl_allow_fragment->integer )
	{
		Msg( "Starting file download: %s\n", pResFileName );
		if( cls.state == ca_disconnected ) return;

		BF_Init( &buf, "ClientPacket", data, sizeof( data ));
		BF_WriteByte( &buf, clc_resourcelist );
		BF_WriteString( &buf, pResFileName );

		if( !cls.netchan.remote_address.type )	// download in singleplayer ???
			cls.netchan.remote_address.type = NA_LOOPBACK;

		// make sure message will be delivered
		Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
	}
	else
	HTTP_AddDownload( pResFileName, -1, true );

}
Пример #8
0
/*
=================
SV_ClientPrintf

Sends text across to be displayed if the level passes
=================
*/
void SV_ClientPrintf( sv_client_t *cl, int level, char *fmt, ... )
{
	va_list	argptr;
	char	string[MAX_SYSPATH];

	if( level < cl->messagelevel || cl->fakeclient )
		return;
	
	va_start( argptr, fmt );
	Q_vsprintf( string, fmt, argptr );
	va_end( argptr );
	
	BF_WriteByte( &cl->netchan.message, svc_print );
	BF_WriteByte( &cl->netchan.message, level );
	BF_WriteString( &cl->netchan.message, string );
}
Пример #9
0
/*
=============
SV_EmitPings

=============
*/
void SV_EmitPings( sizebuf_t *msg )
{
	sv_client_t	*cl;
	int		packet_loss;
	int		i, ping;

	BF_WriteByte( msg, svc_updatepings );

	for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
	{
		if( cl->state != cs_spawned )
			continue;

		SV_GetPlayerStats( cl, &ping, &packet_loss );

		// there are 25 bits for each client
		BF_WriteOneBit( msg, 1 );
		BF_WriteUBitLong( msg, i, MAX_CLIENT_BITS );
		BF_WriteUBitLong( msg, ping, 12 );
		BF_WriteUBitLong( msg, packet_loss, 7 );
	}

	// end marker
	BF_WriteOneBit( msg, 0 );
}
Пример #10
0
/*
==============
CL_ParseCvarValue

Find the client cvar value
and sent it back to the server
==============
*/
void CL_ParseCvarValue( sizebuf_t *msg )
{
	const char *cvarName = BF_ReadString( msg );
	convar_t *cvar = Cvar_FindVar( cvarName );

	// build the answer
	BF_WriteByte( &cls.netchan.message, clc_requestcvarvalue );
	BF_WriteString( &cls.netchan.message, cvar ? cvar->string : "Not Found" );
}
Пример #11
0
static void pfnParticle( float *origin, int color, float life, int zpos, int zvel )
{
    int	v;

    if( !origin )
    {
        MsgDev( D_ERROR, "SV_StartParticle: NULL origin. Ignored\n" );
        return;
    }

    BF_WriteByte( &sv.reliable_datagram, svc_particle );
    BF_WriteVec3Coord( &sv.reliable_datagram, origin );
    BF_WriteChar( &sv.reliable_datagram, 0 ); // no x-vel
    BF_WriteChar( &sv.reliable_datagram, 0 ); // no y-vel
    v = bound( -128, (zpos * zvel) * 16.0f, 127 );
    BF_WriteChar( &sv.reliable_datagram, v ); // write z-vel
    BF_WriteByte( &sv.reliable_datagram, 1 );
    BF_WriteByte( &sv.reliable_datagram, color );
    BF_WriteByte( &sv.reliable_datagram, bound( 0, life * 8, 255 ));
}
Пример #12
0
void pfnUpdateServerInfo( const char *szKey, const char *szValue, const char *unused, void *unused2 )
{
	convar_t	*cv = Cvar_FindVar( szKey );

	if( !cv || !cv->modified ) return; // this cvar not changed

	BF_WriteByte( &sv.reliable_datagram, svc_serverinfo );
	BF_WriteString( &sv.reliable_datagram, szKey );
	BF_WriteString( &sv.reliable_datagram, szValue );
	cv->modified = false; // reset state
}
Пример #13
0
/*
=================
CL_Precache_f

The server will send this command right
before allowing the client into the server
=================
*/
void CL_Precache_f( void )
{
	int	spawncount;

	spawncount = Q_atoi( Cmd_Argv( 1 ));

	CL_PrepSound();
	CL_PrepVideo();

	BF_WriteByte( &cls.netchan.message, clc_stringcmd );
	BF_WriteString( &cls.netchan.message, va( "begin %i\n", spawncount ));
}
Пример #14
0
/*
=================
SV_BroadcastCommand

Sends text to all active clients
=================
*/
void SV_BroadcastCommand( char *fmt, ... )
{
	va_list	argptr;
	char	string[MAX_SYSPATH];
	
	if( !sv.state ) return;
	va_start( argptr, fmt );
	Q_vsprintf( string, fmt, argptr );
	va_end( argptr );

	BF_WriteByte( &sv.reliable_datagram, svc_stufftext );
	BF_WriteString( &sv.reliable_datagram, string );
}
Пример #15
0
/*
====================
CL_ProcessFile

A file has been received via the fragmentation/reassembly layer, put it in the right spot and
 see if we have finished downloading files.
====================
*/
void CL_ProcessFile( BOOL successfully_received, const char *filename )
{
	MsgDev( D_INFO, "Received %s, but file processing is not hooked up!!!\n", filename );

	if( cls.downloadfileid == cls.downloadcount - 1 )
	{
		MsgDev( D_INFO, "All Files downloaded\n" );

		BF_WriteByte( &cls.netchan.message, clc_stringcmd );
		BF_WriteString( &cls.netchan.message, "continueloading" );
	}

	cls.downloadfileid++;
}
Пример #16
0
/*
=====================
CL_SendDisconnectMessage

Sends a disconnect message to the server
=====================
*/
void CL_SendDisconnectMessage( void )
{
	sizebuf_t	buf;
	byte	data[32];

	if( cls.state == ca_disconnected ) return;

	BF_Init( &buf, "LastMessage", data, sizeof( data ));
	BF_WriteByte( &buf, clc_stringcmd );
	BF_WriteString( &buf, "disconnect" );

	if( !cls.netchan.remote_address.type )
		cls.netchan.remote_address.type = NA_LOOPBACK;

	// make sure message will be delivered
	Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
	Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
	Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
}
Пример #17
0
/*
=================
CL_Reconnect_f

The server is changing levels
=================
*/
void CL_Reconnect_f( void )
{
	if( cls.state == ca_disconnected )
		return;

	S_StopAllSounds ();

	if( cls.state == ca_connected )
	{
		cls.demonum = cls.movienum = -1;	// not in the demo loop now
		cls.state = ca_connected;

		// clear channel and stuff
		Netchan_Clear( &cls.netchan );
		BF_Clear( &cls.netchan.message );

		BF_WriteByte( &cls.netchan.message, clc_stringcmd );
		BF_WriteString( &cls.netchan.message, "new" );

		cl.validsequence = 0;		// haven't gotten a valid frame update yet
		cl.delta_sequence = -1;		// we'll request a full delta from the baseline
		cls.lastoutgoingcommand = -1;		// we don't have a backed up cmd history yet
		cls.nextcmdtime = host.realtime;	// we can send a cmd right away

		CL_StartupDemoHeader ();
		return;
	}

	if( cls.servername[0] )
	{
		if( cls.state >= ca_connected )
		{
			CL_Disconnect();
			cls.connect_time = host.realtime - 1.5;
		}
		else cls.connect_time = MAX_HEARTBEAT; // fire immediately

		cls.demonum = cls.movienum = -1;	// not in the demo loop now
		cls.state = ca_connecting;
		Msg( "reconnecting...\n" );
	}
}
Пример #18
0
/*
==============
CL_ParseResourceList

==============
*/
void CL_ParseResourceList( sizebuf_t *msg )
{
	int	i = 0;

	Q_memset( &reslist, 0, sizeof( resourcelist_t ));

	reslist.rescount = BF_ReadWord( msg ) - 1;

	for( i = 0; i < reslist.rescount; i++ )
	{
		reslist.restype[i] = BF_ReadWord( msg );
		Q_strncpy( reslist.resnames[i], BF_ReadString( msg ), CS_SIZE );
	}

	cls.downloadcount = 0;

	HTTP_ResetProcessState();

	for( i = 0; i < reslist.rescount; i++ )
	{
		// skip some types
#if 0
		if( reslist.restype[i] == t_model && !Q_strchr( download_types->latched_string, 'm' ) )
			continue;
		if( reslist.restype[i] == t_sound && !Q_strchr( download_types->latched_string, 's' ) )
			continue;
		if( reslist.restype[i] == t_eventscript && !Q_strchr( download_types->latched_string, 'e' ) )
			continue;
		if( reslist.restype[i] == t_generic && !Q_strchr( download_types->latched_string, 'c' ) )
			continue;
#endif
		if( reslist.restype[i] == t_sound )
			CL_CheckingSoundResFile( reslist.resnames[i] );
		else CL_CheckingResFile( reslist.resnames[i] );
	}

	if( !cls.downloadcount )
	{
		BF_WriteByte( &cls.netchan.message, clc_stringcmd );
		BF_WriteString( &cls.netchan.message, "continueloading" );
	}
}
Пример #19
0
/*
====================
CL_ProcessFile

A file has been received via the fragmentation/reassembly layer, put it in the right spot and
 see if we have finished downloading files.
====================
*/
void CL_ProcessFile( qboolean successfully_received, const char *filename )
{
	if( successfully_received)
		MsgDev( D_INFO, "Received %s\n", filename );
	else
		MsgDev( D_WARN, "Failed to download %s\n", filename );

	if( cls.downloadfileid == cls.downloadcount - 1 )
	{
		MsgDev( D_INFO, "Download completed, resuming connection\n" );
		FS_Rescan();
		BF_WriteByte( &cls.netchan.message, clc_stringcmd );
		BF_WriteString( &cls.netchan.message, "continueloading" );
		cls.downloadfileid = 0;
		cls.downloadcount = 0;
		return;
	}

	cls.downloadfileid++;
}
Пример #20
0
void Delta_WriteTableField( sizebuf_t *msg, int tableIndex, const delta_t *pField )
{
	int		nameIndex;
	delta_info_t	*dt;
	
	ASSERT( pField );

	if( !pField->name || !*pField->name )
		return;	// not initialized ?

	dt = Delta_FindStructByIndex( tableIndex );
	ASSERT( dt && dt->bInitialized );

	nameIndex = Delta_IndexForFieldInfo( dt->pInfo, pField->name );
	ASSERT( nameIndex >= 0 && nameIndex < dt->maxFields );

	BF_WriteByte( msg, svc_deltatable );
	BF_WriteUBitLong( msg, tableIndex, 4 );		// assume we support 16 network tables
	BF_WriteUBitLong( msg, nameIndex, 8 );		// 255 fields by struct should be enough
	BF_WriteUBitLong( msg, pField->flags, 10 );	// flags are indicated various input types
	BF_WriteUBitLong( msg, pField->bits - 1, 5 );	// max received value is 32 (32 bit)

	// multipliers is null-compressed
	if( pField->multiplier != 1.0f )
	{
		BF_WriteOneBit( msg, 1 );
		BF_WriteFloat( msg, pField->multiplier );
	}
	else BF_WriteOneBit( msg, 0 );

	if( pField->post_multiplier != 1.0f )
	{
		BF_WriteOneBit( msg, 1 );
		BF_WriteFloat( msg, pField->post_multiplier );
	}
	else BF_WriteOneBit( msg, 0 );
}
Пример #21
0
int SV_ModelIndex( const char *filename )
{
	char	name[64];
	int	i;

	if( !filename || !filename[0] )
		return 0;

	if( *filename == '!' ) filename++;
	Q_strncpy( name, filename, sizeof( name ));
	COM_FixSlashes( name );

	for( i = 1; i < MAX_MODELS && sv.model_precache[i][0]; i++ )
	{
		if( !Q_stricmp( sv.model_precache[i], name ))
			return i;
	}

	if( i == MAX_MODELS )
	{
		Host_Error( "SV_ModelIndex: MAX_MODELS limit exceeded\n" );
		return 0;
	}

	// register new model
	Q_strncpy( sv.model_precache[i], name, sizeof( sv.model_precache[i] ));

	if( sv.state != ss_loading )
	{	
		// send the update to everyone
		BF_WriteByte( &sv.reliable_datagram, svc_modelindex );
		BF_WriteUBitLong( &sv.reliable_datagram, i, MAX_MODEL_BITS );
		BF_WriteString( &sv.reliable_datagram, name );
	}

	return i;
}
Пример #22
0
int SV_SoundIndex( const char *filename )
{
	char	name[64];
	int	i;

	// don't precache sentence names!
	if( !filename || !filename[0] || filename[0] == '!' )
		return 0;

	Q_strncpy( name, filename, sizeof( name ));
	COM_FixSlashes( name );

	for( i = 1; i < MAX_SOUNDS && sv.sound_precache[i][0]; i++ )
	{
		if( !Q_stricmp( sv.sound_precache[i], name ))
			return i;
	}

	if( i == MAX_SOUNDS )
	{
		Host_Error( "SV_SoundIndex: MAX_SOUNDS limit exceeded\n" );
		return 0;
	}

	// register new sound
	Q_strncpy( sv.sound_precache[i], name, sizeof( sv.sound_precache[i] ));

	if( sv.state != ss_loading )
	{	
		// send the update to everyone
		BF_WriteByte( &sv.reliable_datagram, svc_soundindex );
		BF_WriteUBitLong( &sv.reliable_datagram, i, MAX_SOUND_BITS );
		BF_WriteString( &sv.reliable_datagram, name );
	}

	return i;
}
Пример #23
0
/*
=================
CL_ConnectionlessPacket

Responses to broadcasts, etc
=================
*/
void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
{
	char	*args;
	char	*c, buf[MAX_SYSPATH];
	int	len = sizeof( buf );
	int	dataoffset = 0;
	netadr_t	servadr;
	
	BF_Clear( msg );
	BF_ReadLong( msg ); // skip the -1

	args = BF_ReadStringLine( msg );

	Cmd_TokenizeString( args );
	c = Cmd_Argv( 0 );

	MsgDev( D_NOTE, "CL_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), c );

	// server connection
	if( !Q_strcmp( c, "client_connect" ))
	{
		if( cls.state == ca_connected )
		{
			MsgDev( D_INFO, "dup connect received. ignored\n");
			return;
		}

		Netchan_Setup( NS_CLIENT, &cls.netchan, from, Cvar_VariableValue( "net_qport" ));
		BF_WriteByte( &cls.netchan.message, clc_stringcmd );
		BF_WriteString( &cls.netchan.message, "new" );
		cls.state = ca_connected;

		cl.validsequence = 0;		// haven't gotten a valid frame update yet
		cl.delta_sequence = -1;		// we'll request a full delta from the baseline
		cls.lastoutgoingcommand = -1;		// we don't have a backed up cmd history yet
		cls.nextcmdtime = host.realtime;	// we can send a cmd right away

		CL_StartupDemoHeader ();

		UI_SetActiveMenu( false );
	}
	else if( !Q_strcmp( c, "info" ))
	{
		// server responding to a status broadcast
		CL_ParseStatusMessage( from, msg );
	}
	else if( !Q_strcmp( c, "netinfo" ))
	{
		// server responding to a status broadcast
		CL_ParseNETInfoMessage( from, msg );
	}
	else if( !Q_strcmp( c, "cmd" ))
	{
		// remote command from gui front end
		if( !NET_IsLocalAddress( from ))
		{
			Msg( "Command packet from remote host. Ignored.\n" );
			return;
		}

		ShowWindow( host.hWnd, SW_RESTORE );
		SetForegroundWindow ( host.hWnd );
		args = BF_ReadString( msg );
		Cbuf_AddText( args );
		Cbuf_AddText( "\n" );
	}
	else if( !Q_strcmp( c, "print" ))
	{
		// print command from somewhere
		args = BF_ReadString( msg );
		Msg( args );
	}
	else if( !Q_strcmp( c, "ping" ))
	{
		// ping from somewhere
		Netchan_OutOfBandPrint( NS_CLIENT, from, "ack" );
	}
	else if( !Q_strcmp( c, "challenge" ))
	{
		// challenge from the server we are connecting to
		cls.challenge = Q_atoi( Cmd_Argv( 1 ));
		CL_SendConnectPacket();
		return;
	}
	else if( !Q_strcmp( c, "echo" ))
	{
		// echo request from server
		Netchan_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv( 1 ));
	}
	else if( !Q_strcmp( c, "disconnect" ))
	{
		// a disconnect message from the server, which will happen if the server
		// dropped the connection but it is still getting packets from us
		CL_Disconnect();
	}
	else if( clgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len ))
	{
		// user out of band message (must be handled in CL_ConnectionlessPacket)
		if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, buf );
	}
	else if( msg->pData[0] == 0xFF && msg->pData[1] == 0xFF && msg->pData[2] == 0xFF && msg->pData[3] == 0xFF && msg->pData[4] == 0x66 && msg->pData[5] == 0x0A )
	{
		dataoffset = 6;

		while( 1 )
		{
			servadr.type = NA_IP;
			servadr.ip[0] = msg->pData[dataoffset + 0];
			servadr.ip[1] = msg->pData[dataoffset + 1];
			servadr.ip[2] = msg->pData[dataoffset + 2];
			servadr.ip[3] = msg->pData[dataoffset + 3];

			servadr.port = *(word *)&msg->pData[dataoffset + 4];

			if( !servadr.port )
				break;

			MsgDev( D_INFO, "Found server: %s\n", NET_AdrToString( servadr ));

			NET_Config( true ); // allow remote

			Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION );

			dataoffset += 6;
		}
	}
	else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args );
}
Пример #24
0
/*
=============
SV_EmitPacketEntities

Writes a delta update of an entity_state_t list to the message->
=============
*/
void SV_EmitPacketEntities( sv_client_t *cl, client_frame_t *to, sizebuf_t *msg )
{
	entity_state_t	*oldent, *newent;
	int		oldindex, newindex;
	int		oldnum, newnum;
	int		from_num_entities;
	client_frame_t	*from;

	// this is the frame that we are going to delta update from
	if( cl->delta_sequence != -1 )
	{
		from = &cl->frames[cl->delta_sequence & SV_UPDATE_MASK];
		from_num_entities = from->num_entities;

		// the snapshot's entities may still have rolled off the buffer, though
		if( from->first_entity <= svs.next_client_entities - svs.num_client_entities )
		{
			MsgDev( D_WARN, "%s: delta request from out of date entities.\n", cl->name );

			from = NULL;
			from_num_entities = 0;

			BF_WriteByte( msg, svc_packetentities );
			BF_WriteWord( msg, to->num_entities );
		}
		else
		{
			BF_WriteByte( msg, svc_deltapacketentities );
			BF_WriteWord( msg, to->num_entities );
			BF_WriteByte( msg, cl->delta_sequence );
		}
	}
	else
	{
		from = NULL;
		from_num_entities = 0;

		BF_WriteByte( msg, svc_packetentities );
		BF_WriteWord( msg, to->num_entities );
	}

	newent = NULL;
	oldent = NULL;
	newindex = 0;
	oldindex = 0;

	while( newindex < to->num_entities || oldindex < from_num_entities )
	{
		if( newindex >= to->num_entities )
		{
			newnum = MAX_ENTNUMBER;
		}
		else
		{
			newent = &svs.packet_entities[(to->first_entity+newindex)%svs.num_client_entities];
			newnum = newent->number;
		}

		if( oldindex >= from_num_entities )
		{
			oldnum = MAX_ENTNUMBER;
		}
		else
		{
			oldent = &svs.packet_entities[(from->first_entity+oldindex)%svs.num_client_entities];
			oldnum = oldent->number;
		}

		if( newnum == oldnum )
		{	
			// delta update from old position
			// because the force parm is false, this will not result
			// in any bytes being emited if the entity has not changed at all
			MSG_WriteDeltaEntity( oldent, newent, msg, false, SV_IsPlayerIndex( newent->number ), sv.time );
			oldindex++;
			newindex++;
			continue;
		}

		if( newnum < oldnum )
		{	
			// this is a new entity, send it from the baseline
			MSG_WriteDeltaEntity( &svs.baselines[newnum], newent, msg, true, SV_IsPlayerIndex( newent->number ), sv.time );
			newindex++;
			continue;
		}

		if( newnum > oldnum )
		{	
			qboolean	force;

			if( EDICT_NUM( oldent->number )->free )
				force = true;	// entity completely removed from server
			else force = false;		// just removed from delta-message 

			// remove from message
			MSG_WriteDeltaEntity( oldent, NULL, msg, force, false, sv.time );
			oldindex++;
			continue;
		}
	}

	BF_WriteWord( msg, 0 ); // end of packetentities
}
Пример #25
0
/*
=============
SV_EmitEvents

=============
*/
static void SV_EmitEvents( sv_client_t *cl, client_frame_t *to, sizebuf_t *msg )
{
	event_state_t	*es;
	event_info_t	*info;
	entity_state_t	*state;
	event_args_t	nullargs;
	int		ev_count = 0;
	int		count, ent_index;
	int		i, j, ev;

	Q_memset( &nullargs, 0, sizeof( nullargs ));

	es = &cl->events;

	// count events
	for( ev = 0; ev < MAX_EVENT_QUEUE; ev++ )
	{
		if( es->ei[ev].index ) ev_count++;
	}

	// nothing to send
	if( !ev_count ) return; // nothing to send

	if( ev_count >= 31 ) ev_count = 31;

	for( i = 0; i < MAX_EVENT_QUEUE; i++ )
	{
		info = &es->ei[i];
		if( info->index == 0 )
			continue;

		ent_index = info->entity_index;

		for( j = 0; j < to->num_entities; j++ )
		{
			state = &svs.packet_entities[(to->first_entity+j)%svs.num_client_entities];
			if( state->number == ent_index )
				break;
		}

		if( j >= to->num_entities )
		{
			// couldn't find
			info->packet_index = to->num_entities;
			info->args.entindex = ent_index;
		}
		else
		{
			info->packet_index = j;
			info->args.ducking = 0;

			if(!( info->args.flags & FEVENT_ORIGIN ))
				VectorClear( info->args.origin );

			if(!( info->args.flags & FEVENT_ANGLES ))
				VectorClear( info->args.angles );

			VectorClear( info->args.velocity );
		}
	}

	BF_WriteByte( msg, svc_event );	// create message
	BF_WriteUBitLong( msg, ev_count, 5 );	// up to MAX_EVENT_QUEUE events

	for( count = i = 0; i < MAX_EVENT_QUEUE; i++ )
	{
		info = &es->ei[i];

		if( info->index == 0 )
		{
			info->packet_index = -1;
			info->entity_index = -1;
			continue;
		}

		// only send if there's room
		if( count < ev_count )
		{
			BF_WriteUBitLong( msg, info->index, MAX_EVENT_BITS ); // 1024 events

			if( info->packet_index == -1 )
			{
				BF_WriteOneBit( msg, 0 );
			}
			else
			{
				BF_WriteOneBit( msg, 1 );
				BF_WriteUBitLong( msg, info->packet_index, MAX_ENTITY_BITS );

				if( !Q_memcmp( &nullargs, &info->args, sizeof( event_args_t )))
				{
					BF_WriteOneBit( msg, 0 );
				}
				else
				{
					BF_WriteOneBit( msg, 1 );
					MSG_WriteDeltaEvent( msg, &nullargs, &info->args );
				}
			}

			if( info->fire_time )
			{
				BF_WriteOneBit( msg, 1 );
				BF_WriteWord( msg, Q_rint( info->fire_time * 100.0f ));
			}
			else BF_WriteOneBit( msg, 0 );
		}

		info->index = 0;
		info->packet_index = -1;
		info->entity_index = -1;
		count++;
	}
}
Пример #26
0
/*
==================
SV_WriteClientdataToMessage

==================
*/
void SV_WriteClientdataToMessage( sv_client_t *cl, sizebuf_t *msg )
{
	clientdata_t	nullcd;
	clientdata_t	*from_cd, *to_cd;
	weapon_data_t	nullwd;
	weapon_data_t	*from_wd, *to_wd;
	client_frame_t	*frame;
	edict_t		*clent;
	int		i;

	Q_memset( &nullcd, 0, sizeof( nullcd ));

	clent = cl->edict;
	frame = &cl->frames[cl->netchan.outgoing_sequence & SV_UPDATE_MASK];

	frame->senttime = host.realtime;
	frame->raw_ping = -1.0f;
	frame->latency = -1.0f;

	if( cl->chokecount != 0 )
	{
		BF_WriteByte( msg, svc_chokecount );
		BF_WriteByte( msg, cl->chokecount );
		cl->chokecount = 0;
	}

	// update client fixangle
	switch( clent->v.fixangle )
	{
	case 1:
		BF_WriteByte( msg, svc_setangle );
		BF_WriteBitAngle( msg, clent->v.angles[0], 16 );
		BF_WriteBitAngle( msg, clent->v.angles[1], 16 );
		BF_WriteBitAngle( msg, clent->v.angles[2], 16 );
		clent->v.effects |= EF_NOINTERP;
		break;
	case 2:
		BF_WriteByte( msg, svc_addangle );
		BF_WriteBitAngle( msg, clent->v.avelocity[1], 16 );
		clent->v.avelocity[1] = 0.0f;
		break;
	}

	clent->v.fixangle = 0; // reset fixangle
	clent->v.pushmsec = 0; // reset net framenum
	Q_memset( &frame->clientdata, 0, sizeof( frame->clientdata ));

	// update clientdata_t

	svgame.dllFuncs.pfnUpdateClientData( clent, cl->local_weapons, &frame->clientdata );

	BF_WriteByte( msg, svc_clientdata );
	if( cl->hltv_proxy ) return;	// don't send more nothing

	if( cl->delta_sequence == -1 ) from_cd = &nullcd;
	else from_cd = &cl->frames[cl->delta_sequence & SV_UPDATE_MASK].clientdata;
	to_cd = &frame->clientdata;

	if( cl->delta_sequence == -1 )
	{
		BF_WriteOneBit( msg, 0 );	// no delta-compression
	}
	else
	{
		BF_WriteOneBit( msg, 1 );	// we are delta-ing from
		BF_WriteByte( msg, cl->delta_sequence );
	}

	// write clientdata_t
	MSG_WriteClientData( msg, from_cd, to_cd, sv.time );

	if( cl->local_weapons && svgame.dllFuncs.pfnGetWeaponData( clent, frame->weapondata ))
	{
		Q_memset( &nullwd, 0, sizeof( nullwd ));

		for( i = 0; i < 64; i++ )
		{
			if( cl->delta_sequence == -1 ) from_wd = &nullwd;
			else from_wd = &cl->frames[cl->delta_sequence & SV_UPDATE_MASK].weapondata[i];
			to_wd = &frame->weapondata[i];

			MSG_WriteWeaponData( msg, from_wd, to_wd, sv.time, i );
		}
	}

	// end marker
	BF_WriteOneBit( msg, 0 );
}
Пример #27
0
/*
=======================
SV_UpdateToReliableMessages
=======================
*/
void SV_UpdateToReliableMessages( void )
{
	int		i;
	sv_client_t	*cl;

	// check for changes to be sent over the reliable streams to all clients
	for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
	{
		if( !cl->edict ) continue;	// not in game yet

		if( cl->state != cs_spawned )
			continue;

		if( cl->sendinfo )
		{
			cl->sendinfo = false;
			SV_FullClientUpdate( cl, &sv.reliable_datagram );
		}

		if( cl->sendmovevars )
		{
			cl->sendmovevars = false;
			SV_FullUpdateMovevars( cl, &cl->netchan.message );
                    }
	}

	// 1% chanse for simulate random network bugs
	if( sv.write_bad_message && Com_RandomLong( 0, 512 ) == 404 )
	{
		// just for network debugging (send only for local client)
		BF_WriteByte( &sv.datagram, svc_bad );
		BF_WriteLong( &sv.datagram, rand( ));		// send some random data
		BF_WriteString( &sv.datagram, host.finalmsg );	// send final message
		sv.write_bad_message = false;
	}

	// clear the server datagram if it overflowed.
	if( BF_CheckOverflow( &sv.datagram ))
	{
		MsgDev( D_ERROR, "sv.datagram overflowed!\n" );
		BF_Clear( &sv.datagram );
	}

	// clear the server datagram if it overflowed.
	if( BF_CheckOverflow( &sv.spectator_datagram ))
	{
		MsgDev( D_ERROR, "sv.spectator_datagram overflowed!\n" );
		BF_Clear( &sv.spectator_datagram );
	}

	// now send the reliable and server datagrams to all clients.
	for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
	{
		if( cl->state < cs_connected || cl->fakeclient )
			continue;	// reliables go to all connected or spawned

		BF_WriteBits( &cl->netchan.message, BF_GetData( &sv.reliable_datagram ), BF_GetNumBitsWritten( &sv.reliable_datagram ));
		BF_WriteBits( &cl->datagram, BF_GetData( &sv.datagram ), BF_GetNumBitsWritten( &sv.datagram ));

		if( cl->hltv_proxy )
		{
			BF_WriteBits( &cl->datagram, BF_GetData( &sv.spectator_datagram ), BF_GetNumBitsWritten( &sv.spectator_datagram ));
		}
	}

	// now clear the reliable and datagram buffers.
	BF_Clear( &sv.spectator_datagram );
	BF_Clear( &sv.reliable_datagram );
	BF_Clear( &sv.datagram );
}
Пример #28
0
/*
===================
CL_WritePacket

Create and send the command packet to the server
Including both the reliable commands and the usercmds
===================
*/
void CL_WritePacket( void )
{
	sizebuf_t		buf;
	qboolean		send_command = false;
	byte		data[MAX_CMD_BUFFER];
	int		i, from, to, key, size;
	int		numbackup = 2;
	int		numcmds;
	int		newcmds;
	int		cmdnumber;
	
	// don't send anything if playing back a demo
	if( cls.demoplayback || cls.state == ca_cinematic )
		return;

	if( cls.state == ca_disconnected || cls.state == ca_connecting )
		return;

	CL_ComputePacketLoss ();

#ifndef _DEBUG
	if( cl_cmdrate->value < MIN_CMD_RATE )
	{
		Cvar_SetFloat( "cl_cmdrate", MIN_CMD_RATE );
	}
#endif
	Q_memset( data, 0, MAX_CMD_BUFFER );
	BF_Init( &buf, "ClientData", data, sizeof( data ));

	// Determine number of backup commands to send along
	numbackup = bound( 0, cl_cmdbackup->integer, MAX_BACKUP_COMMANDS );
	if( cls.state == ca_connected ) numbackup = 0;

	// Check to see if we can actually send this command

	// In single player, send commands as fast as possible
	// Otherwise, only send when ready and when not choking bandwidth
	if(( cl.maxclients == 1 ) || ( NET_IsLocalAddress( cls.netchan.remote_address ) && !host_limitlocal->integer ))
		send_command = true;
	else if(( host.realtime >= cls.nextcmdtime ) && Netchan_CanPacket( &cls.netchan ))
		send_command = true;

	if( cl.force_send_usercmd )
	{
		send_command = true;
		cl.force_send_usercmd = false;
	}

	if(( cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged ) >= CL_UPDATE_MASK )
	{
		if(( host.realtime - cls.netchan.last_received ) > CONNECTION_PROBLEM_TIME )
		{
			Con_NPrintf( 1, "^3Warning:^1 Connection Problem^7\n" );
			cl.validsequence = 0;
		}
	}

	if( cl_nodelta->integer )
	{
		cl.validsequence = 0;
	}
	
	// send a userinfo update if needed
	if( userinfo->modified )
	{
		BF_WriteByte( &cls.netchan.message, clc_userinfo );
		BF_WriteString( &cls.netchan.message, Cvar_Userinfo( ));
	}
		
	if( send_command )
	{
		int	outgoing_sequence;
	
		if( cl_cmdrate->integer > 0 )
			cls.nextcmdtime = host.realtime + ( 1.0f / cl_cmdrate->value );
		else cls.nextcmdtime = host.realtime; // always able to send right away

		if( cls.lastoutgoingcommand == -1 )
		{
			outgoing_sequence = cls.netchan.outgoing_sequence;
			cls.lastoutgoingcommand = cls.netchan.outgoing_sequence;
		}
		else outgoing_sequence = cls.lastoutgoingcommand + 1;

		// begin a client move command
		BF_WriteByte( &buf, clc_move );

		// save the position for a checksum byte
		key = BF_GetRealBytesWritten( &buf );
		BF_WriteByte( &buf, 0 );

		// write packet lossage percentation
		BF_WriteByte( &buf, cls.packet_loss );

		// say how many backups we'll be sending
		BF_WriteByte( &buf, numbackup );

		// how many real commands have queued up
		newcmds = ( cls.netchan.outgoing_sequence - cls.lastoutgoingcommand );

		// put an upper/lower bound on this
		newcmds = bound( 0, newcmds, MAX_TOTAL_CMDS );
		if( cls.state == ca_connected ) newcmds = 0;
	
		BF_WriteByte( &buf, newcmds );

		numcmds = newcmds + numbackup;
		from = -1;

		for( i = numcmds - 1; i >= 0; i-- )
		{
			cmdnumber = ( cls.netchan.outgoing_sequence - i ) & CL_UPDATE_MASK;

			to = cmdnumber;
			CL_WriteUsercmd( &buf, from, to );
			from = to;

			if( BF_CheckOverflow( &buf ))
				Host_Error( "CL_Move, overflowed command buffer (%i bytes)\n", MAX_CMD_BUFFER );
		}

		// calculate a checksum over the move commands
		size = BF_GetRealBytesWritten( &buf ) - key - 1;
		buf.pData[key] = CRC32_BlockSequence( buf.pData + key + 1, size, cls.netchan.outgoing_sequence );

		// message we are constructing.
		i = cls.netchan.outgoing_sequence & CL_UPDATE_MASK;
	
		// determine if we need to ask for a new set of delta's.
		if( cl.validsequence && (cls.state == ca_active) && !( cls.demorecording && cls.demowaiting ))
		{
			cl.delta_sequence = cl.validsequence;

			BF_WriteByte( &buf, clc_delta );
			BF_WriteByte( &buf, cl.validsequence & 0xFF );
		}
		else
		{
			// request delta compression of entities
			cl.delta_sequence = -1;
		}

		if( BF_CheckOverflow( &buf ))
		{
			Host_Error( "CL_Move, overflowed command buffer (%i bytes)\n", MAX_CMD_BUFFER );
		}

		// remember outgoing command that we are sending
		cls.lastoutgoingcommand = cls.netchan.outgoing_sequence;

		// composite the rest of the datagram..
		if( BF_GetNumBitsWritten( &cls.datagram ) <= BF_GetNumBitsLeft( &buf ))
			BF_WriteBits( &buf, BF_GetData( &cls.datagram ), BF_GetNumBitsWritten( &cls.datagram ));
		BF_Clear( &cls.datagram );

		// deliver the message (or update reliable)
		Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
	}
	else
	{
		// increment sequence number so we can detect that we've held back packets.
		cls.netchan.outgoing_sequence++;
	}

	if( cls.demorecording )
	{
		// Back up one because we've incremented outgoing_sequence each frame by 1 unit
		cmdnumber = ( cls.netchan.outgoing_sequence - 1 ) & CL_UPDATE_MASK;
		CL_WriteDemoUserCmd( cmdnumber );
	}

	// update download/upload slider.
	Netchan_UpdateProgress( &cls.netchan );
}
Пример #29
0
/*
=================
CL_ConnectionlessPacket

Responses to broadcasts, etc
=================
*/
void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
{
	char	*args;
	char	*c, buf[MAX_SYSPATH];
	int	len = sizeof( buf ), i = 0;
	netadr_t servadr;
	
	BF_Clear( msg );
	BF_ReadLong( msg ); // skip the -1

	args = BF_ReadStringLine( msg );

	Cmd_TokenizeString( args );
	c = Cmd_Argv( 0 );

	MsgDev( D_NOTE, "CL_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), c );

	// server connection
	if( !Q_strcmp( c, "client_connect" ))
	{
		if( cls.state == ca_connected )
		{
			MsgDev( D_INFO, "Dup connect received. Ignored.\n");
			return;
		}

		Netchan_Setup( NS_CLIENT, &cls.netchan, from, net_qport->integer);
		BF_WriteByte( &cls.netchan.message, clc_stringcmd );
		BF_WriteString( &cls.netchan.message, "new" );
		cls.state = ca_connected;

		cl.validsequence = 0;		// haven't gotten a valid frame update yet
		cl.delta_sequence = -1;		// we'll request a full delta from the baseline
		cls.lastoutgoingcommand = -1;		// we don't have a backed up cmd history yet
		cls.nextcmdtime = host.realtime;	// we can send a cmd right away

		CL_StartupDemoHeader ();
	}
	else if( !Q_strcmp( c, "info" ))
	{
		// server responding to a status broadcast
		CL_ParseStatusMessage( from, msg );
	}
	else if( !Q_strcmp( c, "netinfo" ))
	{
		// server responding to a status broadcast
		CL_ParseNETInfoMessage( from, msg );
	}
	else if( !Q_strcmp( c, "cmd" ))
	{
		// remote command from gui front end
		if( !NET_IsLocalAddress( from ))
		{
			Msg( "Command packet from remote host. Ignored.\n" );
			return;
		}
#ifdef XASH_SDL
		SDL_RestoreWindow( host.hWnd );
#endif
		args = BF_ReadString( msg );
		Cbuf_AddText( args );
		Cbuf_AddText( "\n" );
	}
	else if( !Q_strcmp( c, "print" ))
	{
		// print command from somewhere
		Msg("remote: %s\n", BF_ReadString( msg ) );
	}
	else if( !Q_strcmp( c, "ping" ))
	{
		// ping from somewhere
		Netchan_OutOfBandPrint( NS_CLIENT, from, "ack" );
	}
	else if( !Q_strcmp( c, "challenge" ))
	{
		// challenge from the server we are connecting to
		cls.challenge = Q_atoi( Cmd_Argv( 1 ));
		CL_SendConnectPacket();
		return;
	}
	else if( !Q_strcmp( c, "echo" ))
	{
		// echo request from server
		Netchan_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv( 1 ));
	}
	else if( !Q_strcmp( c, "disconnect" ))
	{
		// a disconnect message from the server, which will happen if the server
		// dropped the connection but it is still getting packets from us
		CL_Disconnect();
		CL_ClearEdicts();
	}
	else if( !Q_strcmp( c, "f") )
	{
		// serverlist got from masterserver
		while( !msg->bOverflow )
		{
			servadr.type = NA_IP;
			// 4 bytes for IP
			BF_ReadBytes( msg, servadr.ip, sizeof( servadr.ip ));
			// 2 bytes for Port
			servadr.port = BF_ReadShort( msg );

			if( !servadr.port )
				break;

			MsgDev( D_INFO, "Found server: %s\n", NET_AdrToString( servadr ));

			NET_Config( true ); // allow remote

			Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION );
		}

		// execute at next frame preventing relation on fps
		Cbuf_AddText("menu_resetping\n");
	}
	else if( clgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len ))
	{
		// user out of band message (must be handled in CL_ConnectionlessPacket)
		if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, (byte *)buf );
	}
	else MsgDev( D_ERROR, "Bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args );
}