/* =========== 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; }
/* ================ Con_CheckResize If the line width has changed, reformat the buffer. ================ */ qboolean Con_CheckResize( void ) { int i, textWidthInChars, oldwidth, oldtotallines, numlines, numchars; conChar_t buf[ CON_TEXTSIZE ]; qboolean ret = qtrue; if ( cls.glconfig.vidWidth ) { const int consoleVidWidth = cls.glconfig.vidWidth - 2 * (consoleState.margin.sides + consoleState.padding.sides ); textWidthInChars = consoleVidWidth / SCR_ConsoleFontUnicharWidth( 'W' ); } else { textWidthInChars = 0; } if ( textWidthInChars == consoleState.textWidthInChars ) { // nothing } else if ( textWidthInChars < 1 ) // video hasn't been initialized yet { consoleState.textWidthInChars = DEFAULT_CONSOLE_WIDTH; consoleState.maxScrollbackLengthInLines = CON_TEXTSIZE / consoleState.textWidthInChars; Con_Clear(); consoleState.currentLine = consoleState.maxScrollbackLengthInLines - 1; consoleState.bottomDisplayedLine = consoleState.currentLine; consoleState.scrollLineIndex = consoleState.currentLine; ret = qfalse; } else { oldwidth = consoleState.textWidthInChars; consoleState.textWidthInChars = textWidthInChars; oldtotallines = consoleState.maxScrollbackLengthInLines; consoleState.maxScrollbackLengthInLines = CON_TEXTSIZE / consoleState.textWidthInChars; numlines = oldwidth < 0 ? 0 : oldtotallines; if ( consoleState.maxScrollbackLengthInLines < numlines ) { numlines = consoleState.maxScrollbackLengthInLines; } numchars = oldwidth; if ( consoleState.textWidthInChars < numchars ) { numchars = consoleState.textWidthInChars; } Com_Memcpy( buf, consoleState.text, sizeof( consoleState.text ) ); Con_Clear(); for ( i = 0; i < numlines; i++ ) { conChar_t* destination = consoleState.text + ( consoleState.maxScrollbackLengthInLines - 1 - i ) * consoleState.textWidthInChars; memcpy( destination, buf + ( ( consoleState.currentLine - i + oldtotallines ) % oldtotallines ) * oldwidth, numchars * sizeof( conChar_t ) ); if( destination[0].ch ) consoleState.usedScrollbackLengthInLines++; } consoleState.currentLine = consoleState.maxScrollbackLengthInLines - 1; consoleState.bottomDisplayedLine = consoleState.currentLine; consoleState.scrollLineIndex = consoleState.currentLine; } if ( con_prompt ) { char prompt[ MAX_STRING_CHARS ]; Q_strncpyz( prompt, con_prompt->string, sizeof( prompt ) ); Q_CleanStr( prompt ); g_console_field_width = consoleState.textWidthInChars - 8 - Q_UTF8_Strlen( prompt ); g_consoleField.SetWidth(g_console_field_width); } return ret; }