/* ================ G_WriteClientSessionData Called on game shutdown ================ */ void G_WriteClientSessionData(gclient_t *client) { const char *s; const char *var; s = va("%i %i %i %i %i %i %i %i %s", client->sess.sessionTeam, client->sess.restartTeam, client->sess.spectatorTime, client->sess.spectatorState, client->sess.spectatorClient, client->sess.wins, client->sess.losses, client->sess.teamLeader, BG_ClientListString(&client->sess.ignoreList) ); var = va("session%i", client - level.clients); trap_Cvar_Set(var, s); }
/* =========== 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. ============ */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; int health; char *s; char model[ MAX_QPATH ]; char buffer[ MAX_QPATH ]; char filename[ MAX_QPATH ]; char oldname[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; qboolean revertName = qfalse; gclient_t *client; char c1[ MAX_INFO_STRING ]; char c2[ MAX_INFO_STRING ]; 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) ) strcpy( userinfo, "\\name\\badinfo" ); // set name Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey( userinfo, "name" ); G_ClientCleanName( s, newname, sizeof( newname ) ); if( strcmp( oldname, newname ) ) { // in case we need to revert and there's no oldname if( client->pers.connected != CON_CONNECTED ) Q_strncpyz( oldname, "UnnamedPlayer", sizeof( oldname ) ); if( client->pers.nameChangeTime && ( level.time - client->pers.nameChangeTime ) <= ( g_minNameChangePeriod.value * 1000 ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( g_maxNameChanges.integer > 0 && client->pers.nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", g_maxNameChanges.integer ) ); revertName = qtrue; } else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) ); revertName = qtrue; } if( revertName ) { Q_strncpyz( client->pers.netname, oldname, sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { Q_strncpyz( client->pers.netname, newname, sizeof( client->pers.netname ) ); if( client->pers.connected == CON_CONNECTED ) { client->pers.nameChangeTime = level.time; client->pers.nameChanges++; } } } if( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) Q_strncpyz( client->pers.netname, "scoreboard", sizeof( client->pers.netname ) ); if( client->pers.connected == CON_CONNECTED ) { if( strcmp( oldname, client->pers.netname ) ) { trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname ) ); G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s\" -> \"%s\"\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname ); G_admin_namelog_update( client, qfalse ); } } // set max health health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) client->pers.maxHealth = 100; 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_ClassConfig( PCL_HUMAN_BSUIT )->modelName, BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName ); } else { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( client->pers.classSelection )->modelName, BG_ClassConfig( client->pers.classSelection )->skinName ); //model segmentation Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", BG_ClassConfig( client->pers.classSelection )->modelName ); if( G_NonSegModel( filename ) ) 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; // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if( !*s || atoi( s ) != 0 ) client->pers.teamInfo = qtrue; else client->pers.teamInfo = qfalse; // colors strcpy( c1, Info_ValueForKey( userinfo, "color1" ) ); strcpy( c2, Info_ValueForKey( userinfo, "color2" ) ); 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\\c1\\%s\\c2\\%s\\" "hc\\%i\\ig\\%16s\\v\\%s", client->pers.netname, client->pers.teamSelection, model, c1, c2, client->pers.maxHealth, BG_ClientListString( &client->sess.ignoreList ), client->pers.voice ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ }
/* =========== 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. ============ */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; int teamTask, teamLeader, health; char *s; char model[ MAX_QPATH ]; char buffer[ MAX_QPATH ]; char filename[ MAX_QPATH ]; char oldname[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; qboolean revertName = qfalse; qboolean showRenameMsg = qtrue; gclient_t *client; char c1[ MAX_INFO_STRING ]; char c2[ MAX_INFO_STRING ]; char userinfo[ MAX_INFO_STRING ]; team_t team; ent = g_entities + clientNum; client = ent->client; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if( !Info_Validate(userinfo) ) strcpy( userinfo, "\\name\\badinfo" ); // check for local client s = Info_ValueForKey( userinfo, "ip" ); if( !strcmp( s, "localhost" ) ) client->pers.localClient = qtrue; // check the item prediction s = Info_ValueForKey( userinfo, "cg_predictItems" ); if( !atoi( s ) ) client->pers.predictItemPickup = qfalse; else client->pers.predictItemPickup = qtrue; // set name Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey( userinfo, "name" ); ClientCleanName( s, newname, sizeof( newname ) ); if( strcmp( oldname, newname ) ) { if( !strlen( oldname ) && client->pers.connected != CON_CONNECTED ) showRenameMsg = qfalse; // in case we need to revert and there's no oldname ClientCleanName( va( "%s", client->pers.netname ), oldname, sizeof( oldname ) ); if( g_newbieNumbering.integer ) { if( !strcmp( newname, "UnnamedPlayer" ) ) Q_strncpyz( newname, G_NextNewbieName( ent ), sizeof( newname ) ); if( !strcmp( oldname, "UnnamedPlayer" ) ) Q_strncpyz( oldname, G_NextNewbieName( ent ), sizeof( oldname ) ); } if( client->pers.muted ) { trap_SendServerCommand( ent - g_entities, "print \"You cannot change your name while you are muted\n\"" ); revertName = qtrue; } else if( client->pers.nameChangeTime && ( level.time - client->pers.nameChangeTime ) <= ( g_minNameChangePeriod.value * 1000 ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( g_maxNameChanges.integer > 0 && client->pers.nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", g_maxNameChanges.integer ) ); revertName = qtrue; } else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) ); revertName = qtrue; } else if( client->pers.nlocked ) { trap_SendServerCommand( ent - g_entities, "print \"Your name is locked, you can no longer rename.\n\"" ); revertName = qtrue; } if( revertName ) { Q_strncpyz( client->pers.netname, oldname, sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { Q_strncpyz( client->pers.netname, newname, sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", newname ); trap_SetUserinfo( clientNum, userinfo ); if( client->pers.connected == CON_CONNECTED ) { client->pers.nameChangeTime = level.time; client->pers.nameChanges++; } } } if( client->sess.sessionTeam == TEAM_SPECTATOR ) { if( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) Q_strncpyz( client->pers.netname, "scoreboard", sizeof( client->pers.netname ) ); } if( client->pers.connected >= CON_CONNECTING && showRenameMsg ) { if( strcmp( oldname, client->pers.netname ) ) { trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " renamed to %s^7\n\"", oldname, client->pers.netname ) ); if( g_decolourLogfiles.integer) { char decoloured[ MAX_STRING_CHARS ] = ""; if( g_decolourLogfiles.integer == 1 ) { Com_sprintf( decoloured, sizeof(decoloured), " (\"%s^7\" -> \"%s^7\")", oldname, client->pers.netname ); G_DecolorString( decoloured, decoloured ); G_LogPrintfColoured( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"%s\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname, decoloured ); } else { G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"%s\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname, decoloured ); } } else { G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname ); } G_admin_namelog_update( client, qfalse ); } } // set max health health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) client->pers.maxHealth = 100; //hack to force a client update if the config string does not change between spawning if( client->pers.classSelection == PCL_NONE ) client->pers.maxHealth = 0; // set model if( client->ps.stats[ STAT_PCLASS ] == PCL_HUMAN && BG_InventoryContainsUpgrade( UP_BATTLESUIT, client->ps.stats ) ) { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_FindModelNameForClass( PCL_HUMAN_BSUIT ), BG_FindSkinNameForClass( PCL_HUMAN_BSUIT ) ); } else 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_FindModelNameForClass( PCL_HUMAN_BSUIT ), BG_FindSkinNameForClass( PCL_HUMAN_BSUIT ) ); } else { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_FindModelNameForClass( client->pers.classSelection ), BG_FindSkinNameForClass( client->pers.classSelection ) ); } Q_strncpyz( model, buffer, sizeof( model ) ); //don't bother setting model type if spectating if( client->pers.classSelection != PCL_NONE ) { //model segmentation Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", BG_FindModelNameForClass( client->pers.classSelection ) ); if( G_NonSegModel( filename ) ) client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL; else client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL; } // 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; // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if( ! *s || atoi( s ) != 0 ) client->pers.teamInfo = qtrue; else client->pers.teamInfo = qfalse; s = Info_ValueForKey( userinfo, "cg_unlagged" ); if( !s[0] || atoi( s ) != 0 ) client->pers.useUnlagged = qtrue; else client->pers.useUnlagged = qfalse; // team task (0 = none, 1 = offence, 2 = defence) teamTask = atoi( Info_ValueForKey( userinfo, "teamtask" ) ); // team Leader (1 = leader, 0 is normal player) teamLeader = client->sess.teamLeader; // colors strcpy( c1, Info_ValueForKey( userinfo, "color1" ) ); strcpy( c2, Info_ValueForKey( userinfo, "color2" ) ); team = client->pers.teamSelection; // 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\\hmodel\\%s\\c1\\%s\\c2\\%s\\" "hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\" "tl\\%d\\ig\\%16s", client->pers.netname, team, model, model, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader, BG_ClientListString( &client->sess.ignoreList ) ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ }