Example #1
0
/*
===========
ClientUserInfoChanged

Called from ClientConnect when the player first connects and
directly by the server system when the player updates a userinfo variable.

The game can override any of the settings and call trap_SetUserinfo
if desired.
============
*/
const char *ClientUserinfoChanged( int clientNum, bool forceName )
{
	gentity_t *ent;
	const char      *s;
	char      model[ MAX_QPATH ];
	char      buffer[ MAX_QPATH ];
	char      oldname[ MAX_NAME_LENGTH ];
	char      newname[ MAX_NAME_LENGTH ];
	char      err[ MAX_STRING_CHARS ];
	bool  revertName = false;
	gclient_t *client;
	char      userinfo[ MAX_INFO_STRING ];

	ent = g_entities + clientNum;
	client = ent->client;

	trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );

	// check for malformed or illegal info strings
	if ( !Info_Validate( userinfo ) )
	{
		trap_SendServerCommand( ent - g_entities,
		                        "disconnect \"illegal or malformed userinfo\"" );
		trap_DropClient( ent - g_entities,
		                 "dropped: illegal or malformed userinfo" );
		return "Illegal or malformed userinfo";
	}
	// If their userinfo overflowed, tremded is in the process of disconnecting them.
	// If we send our own disconnect, it won't work, so just return to prevent crashes later
	//  in this function. This check must come after the Info_Validate call.
	else if ( !userinfo[ 0 ] )
	{
		return "Empty (overflowed) userinfo";
	}

	// stickyspec toggle
	s = Info_ValueForKey( userinfo, "cg_stickySpec" );
	client->pers.stickySpec = atoi( s ) != 0;

	// set name
	Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) );
	s = Info_ValueForKey( userinfo, "name" );
	G_ClientCleanName( s, newname, sizeof( newname ), client );

	if ( strcmp( oldname, newname ) )
	{
		if ( !forceName && client->pers.namelog->nameChangeTime &&
		     level.time - client->pers.namelog->nameChangeTime <=
		     g_minNameChangePeriod.value * 1000 )
		{
			trap_SendServerCommand( ent - g_entities, va(
			                          "print_tr %s %d", QQ( N_("Name change spam protection (g_minNameChangePeriod = $1$)") ),
			                          g_minNameChangePeriod.integer ) );
			revertName = true;
		}
		else if ( !forceName && g_maxNameChanges.integer > 0 &&
		          client->pers.namelog->nameChanges >= g_maxNameChanges.integer )
		{
			trap_SendServerCommand( ent - g_entities, va(
			                          "print_tr %s %d", QQ( N_("Maximum name changes reached (g_maxNameChanges = $1$)") ),
			                          g_maxNameChanges.integer ) );
			revertName = true;
		}
		else if ( !forceName && client->pers.namelog->muted )
		{
			trap_SendServerCommand( ent - g_entities,
			                        va( "print_tr %s", QQ( N_("You cannot change your name while you are muted") ) ) );
			revertName = true;
		}
		else if ( !G_admin_name_check( ent, newname, err, sizeof( err ) ) )
		{
			trap_SendServerCommand( ent - g_entities, va( "print_tr %s %s %s", QQ( "$1t$ $2$" ), Quote( err ), Quote( newname ) ) );
			revertName = true;
		}
		else if ( Q_UTF8_Strlen( newname ) > MAX_NAME_CHARACTERS )
		{
			trap_SendServerCommand( ent - g_entities,
			                        va( "print_tr %s %d", QQ( N_("Name is too long! Must be less than $1$ characters.") ), MAX_NAME_CHARACTERS ) );
			revertName = true;

		}

		if ( revertName )
		{
			Q_strncpyz( client->pers.netname, *oldname ? oldname : G_UnnamedClientName( client ),
			            sizeof( client->pers.netname ) );
		}
		else
		{
			if( G_IsUnnamed( newname ) )
			{
				Q_strncpyz( client->pers.netname, G_UnnamedClientName( client ), sizeof( client->pers.netname ) );
			}
			else
			{
				Q_strncpyz( client->pers.netname, newname, sizeof( client->pers.netname ) );
			}

			if ( !forceName && client->pers.connected == CON_CONNECTED )
			{
				client->pers.namelog->nameChangeTime = level.time;
				client->pers.namelog->nameChanges++;
			}

			if ( *oldname )
			{
				G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\" \"%s^7\"",
				             clientNum, client->pers.ip.str, client->pers.guid,
				             oldname, client->pers.netname,
				             client->pers.netname );
			}
		}

		G_namelog_update_name( client );

		Info_SetValueForKey(userinfo, "name", client->pers.netname, false);
		trap_SetUserinfo(clientNum, userinfo);
	}

	if ( client->pers.classSelection == PCL_NONE )
	{
		//This looks hacky and frankly it is. The clientInfo string needs to hold different
		//model details to that of the spawning class or the info change will not be
		//registered and an axis appears instead of the player model. There is zero chance
		//the player can spawn with the battlesuit, hence this choice.
		Com_sprintf( buffer, MAX_QPATH, "%s/%s",  BG_ClassModelConfig( PCL_HUMAN_BSUIT )->modelName,
		             BG_ClassModelConfig( PCL_HUMAN_BSUIT )->skinName );
	}
	else
	{
		Com_sprintf( buffer, MAX_QPATH, "%s/%s",  BG_ClassModelConfig( client->pers.classSelection )->modelName,
		             BG_ClassModelConfig( client->pers.classSelection )->skinName );

		if ( BG_ClassModelConfig( client->pers.classSelection )->segmented )
		{
			client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL;
		}
		else
		{
			client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL;
		}
	}

	Q_strncpyz( model, buffer, sizeof( model ) );

	// wallwalk follow
	s = Info_ValueForKey( userinfo, "cg_wwFollow" );

	if ( atoi( s ) )
	{
		client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW;
	}
	else
	{
		client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW;
	}

	// wallwalk toggle
	s = Info_ValueForKey( userinfo, "cg_wwToggle" );

	if ( atoi( s ) )
	{
		client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE;
	}
	else
	{
		client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE;
	}

	// always sprint
	s = Info_ValueForKey( userinfo, "cg_sprintToggle" );

	if ( atoi( s ) )
	{
		client->ps.persistant[ PERS_STATE ] |= PS_SPRINTTOGGLE;
	}
	else
	{
		client->ps.persistant[ PERS_STATE ] &= ~PS_SPRINTTOGGLE;
	}

	// fly speed
	s = Info_ValueForKey( userinfo, "cg_flySpeed" );

	if ( *s )
	{
		client->pers.flySpeed = atoi( s );
	}
	else
	{
		client->pers.flySpeed = BG_Class( PCL_NONE )->speed;
	}

	// disable blueprint errors
	s = Info_ValueForKey( userinfo, "cg_disableBlueprintErrors" );

	if ( atoi( s ) )
	{
		client->pers.disableBlueprintErrors = true;
	}
	else
	{
		client->pers.disableBlueprintErrors = false;
	}

	// teamInfo
	s = Info_ValueForKey( userinfo, "teamoverlay" );

	if ( atoi( s ) != 0 )
	{
		// teamoverlay was enabled so we need an update
		if ( client->pers.teamInfo == 0 )
		{
			client->pers.teamInfo = 1;
		}
	}
	else
	{
		client->pers.teamInfo = 0;
	}

	s = Info_ValueForKey( userinfo, "cg_unlagged" );

	if ( !s[ 0 ] || atoi( s ) != 0 )
	{
		client->pers.useUnlagged = true;
	}
	else
	{
		client->pers.useUnlagged = false;
	}

	Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ),
	            sizeof( client->pers.voice ) );

	// send over a subset of the userinfo keys so other clients can
	// print scoreboards, display models, and play custom sounds

	Com_sprintf( userinfo, sizeof( userinfo ),
	             "n\\%s\\t\\%i\\model\\%s\\ig\\%16s\\v\\%s",
	             client->pers.netname, client->pers.team, model,
	             Com_ClientListString( &client->sess.ignoreList ),
	             client->pers.voice );

	trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo );

	/*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/

	return nullptr;
}
Example #2
0
/*
===========
G_ClientCleanName
============
*/
static void G_ClientCleanName( const char *in, char *out, int outSize, gclient_t *client )
{
	int      len, colorlessLen;
	char     *p;
	int      spaces;
	qboolean escaped;
	qboolean invalid = qfalse;
	qboolean hasletter = qfalse;

	//save room for trailing null byte
	outSize--;

	len = 0;
	colorlessLen = 0;
	p = out;
	*p = 0;
	spaces = 0;

	for ( ; *in; in++ )
	{
		int cp, w;

		// don't allow leading spaces
		if ( colorlessLen == 0 && *in == ' ' )
		{
			continue;
		}

		// don't allow nonprinting characters or (dead) console keys
		// but do allow UTF-8 (unvalidated)
		if ( *in >= 0 && *in < ' ' )
		{
			continue;
		}

		// check colors
		if ( Q_IsColorString( in ) )
		{
			in++;

			// make sure room in dest for both chars
			if ( len > outSize - 2 )
			{
				break;
			}

			*out++ = Q_COLOR_ESCAPE;

			*out++ = *in;

			len += 2;
			continue;
		}
		else if ( !g_emoticonsAllowedInNames.integer && G_IsEmoticon( in, &escaped ) )
		{
			// make sure room in dest for both chars
			if ( len > outSize - 2 )
			{
				break;
			}

			*out++ = '[';
			*out++ = '[';
			len += 2;

			if ( escaped )
			{
				in++;
			}

			continue;
		}

		cp = Q_UTF8_CodePoint( in );

		if ( Q_Unicode_IsAlphaOrIdeo( cp ) )
		{
			hasletter = qtrue;
		}

		// don't allow too many consecutive spaces
		if ( *in == ' ' )
		{
			spaces++;

			if ( spaces > 3 )
			{
				continue;
			}
		}
		else
		{
			spaces = 0;
		}

		w = Q_UTF8_WidthCP( cp );

		if ( len > outSize - w )
		{
			break;
		}

		memcpy( out, in, w );
		colorlessLen++;
		len += w;
		out += w;
		in += w - 1; // allow for loop increment
	}

	*out = 0;

	// don't allow names beginning with S_SKIPNOTIFY because it messes up /ignore-related code
	if ( !Q_strnicmp( p, S_SKIPNOTIFY, 12 ) )
	{
		invalid = qtrue;
	}

	// don't allow comment-beginning strings because it messes up various parsers
	if ( strstr( p, "//" ) || strstr( p, "/*" ) )
	{
		invalid = qtrue;
	}

	// don't allow empty names
	if ( *p == 0 || colorlessLen == 0 )
	{
		invalid = qtrue;
	}

	// limit no. of code points
	if ( Q_UTF8_PrintStrlen( p ) > MAX_NAME_LENGTH_CP )
	{
		invalid = qtrue;
	}

	// if something made the name bad, put them back to UnnamedPlayer
	if ( invalid || !hasletter )
	{
		Q_strncpyz( p, G_UnnamedClientName( client ), outSize );
	}
}
Example #3
0
/*
===========
G_ClientCleanName
============
*/
static void G_ClientCleanName( const char *in, char *out, int outSize, gclient_t *client )
{
	--outSize;

	bool        has_visible_characters = false;
	std::string out_string;
	bool        hasletter = false;
	int         spaces = 0;

	for ( const auto& token : Color::Parser( in ) )
	{
		if ( out_string.size() + token.Size() > outSize )
		{
			break;
		}

		if ( token.Type() == Color::Token::TokenType::CHARACTER )
		{
			int cp = Q_UTF8_CodePoint(token.Begin());

			// don't allow leading spaces
			// TODO: use a Unicode-aware isspace
			if ( !has_visible_characters && Str::cisspace( cp ) )
			{
				continue;
			}

			// don't allow nonprinting characters or (dead) console keys
			// but do allow UTF-8 (unvalidated)
			if ( cp >= 0 && cp < ' ' )
			{
				continue;
			}

			bool escaped_emote = false;
			// single trailing ^ will mess up some things
			if ( cp == Color::Constants::ESCAPE && !*token.End() )
			{
				if ( out_string.size() + 2 > outSize )
				{
					break;
				}
				out_string += Color::Constants::ESCAPE;
			}
			else if ( !g_emoticonsAllowedInNames.integer && G_IsEmoticon( in, &escaped_emote ) )
			{
				if ( out_string.size() + 2 + token.Size() > outSize )
				{
					break;
				}

				out_string += "[[";
				if ( escaped_emote )
				{
					continue;
				}
			}

			if ( Q_Unicode_IsAlphaOrIdeo( cp ) )
			{
				hasletter = true;
			}

		// don't allow too many consecutive spaces
			// TODO: use a Unicode-aware isspace
			if ( Str::cisspace( cp ) )
			{
				spaces++;
				if ( spaces > 3 )
				{
					continue;
				}
			}
			else
			{
				spaces = 0;
				has_visible_characters = true;
			}
		}
		else if ( token.Type() == Color::Token::TokenType::ESCAPE )
		{
			has_visible_characters = true;
		}

		out_string.append(token.Begin(), token.Size());
	}

	bool invalid = false;

	// don't allow names beginning with S_SKIPNOTIFY because it messes up /ignore-related code
	if ( !out_string.compare( 0, 12, S_SKIPNOTIFY ) )
	{
		invalid = true;
	}

	// don't allow comment-beginning strings because it messes up various parsers
	if ( out_string.find( "//" ) != std::string::npos ||
		out_string.find( "/*" ) != std::string::npos )
	{
		out_string.erase( std::remove( out_string.begin(), out_string.end(), '/' ) );
	}

	// don't allow empty names
	if ( out_string.empty() || !hasletter )
	{
		invalid = true;
	}
	// don't allow names beginning with digits
	else if ( Str::cisdigit( out_string[0] ) )
	{
		out_string.erase( out_string.begin(),
			std::find_if_not( out_string.begin(), out_string.end(), Str::cisdigit ) );
	}

	// if something made the name bad, put them back to UnnamedPlayer
	if ( invalid )
	{
		Q_strncpyz( out, G_UnnamedClientName( client ), outSize );
	}
	else
	{
		Q_strncpyz( out, out_string.c_str(), outSize );
	}
}
Example #4
0
/*
===========
G_ClientCleanName
============
*/
static void G_ClientCleanName( const char *in, char *out, int outSize, gclient_t *client )
{
	int      len, colorlessLen;
	char     *p;
	int      spaces;
	qboolean escaped;
	qboolean invalid = qfalse;

	//save room for trailing null byte
	outSize--;

	len = 0;
	colorlessLen = 0;
	p = out;
	*p = 0;
	spaces = 0;

	for ( ; *in; in++ )
	{
		// don't allow leading spaces
		if ( colorlessLen == 0 && *in == ' ' )
		{
			continue;
		}

		// don't allow nonprinting characters or (dead) console keys
		if ( *in < ' ' || *in > '}' || *in == '`' )
		{
			continue;
		}

		// check colors
		if ( Q_IsColorString( in ) )
		{
			in++;

			// make sure room in dest for both chars
			if ( len > outSize - 2 )
			{
				break;
			}

			*out++ = Q_COLOR_ESCAPE;

			*out++ = *in;

			len += 2;
			continue;
		}
		else if ( !g_emoticonsAllowedInNames.integer && G_IsEmoticon( in, &escaped ) )
		{
			// make sure room in dest for both chars
			if ( len > outSize - 2 )
			{
				break;
			}

			*out++ = '[';
			*out++ = '[';
			len += 2;

			if ( escaped )
			{
				in++;
			}

			continue;
		}

		// don't allow too many consecutive spaces
		if ( *in == ' ' )
		{
			spaces++;

			if ( spaces > 3 )
			{
				continue;
			}
		}
		else
		{
			spaces = 0;
		}

		if ( len > outSize - 1 )
		{
			break;
		}

		*out++ = *in;
		colorlessLen++;
		len++;
	}

	*out = 0;

	// don't allow names beginning with "[skipnotify]" because it messes up /ignore-related code
	if ( !Q_stricmpn( p, "[skipnotify]", 12 ) )
	{
		invalid = qtrue;
	}

	// don't allow comment-beginning strings because it messes up various parsers
	if ( strstr( p, "//" ) || strstr( p, "/*" ) )
	{
		invalid = qtrue;
	}

	// don't allow empty names
	if ( *p == 0 || colorlessLen == 0 )
	{
		invalid = qtrue;
	}

	// if something made the name bad, put them back to UnnamedPlayer
	if ( invalid )
	{
		Q_strncpyz( p, G_UnnamedClientName( client ), outSize );
	}
}