/** * @brief called whenever the player updates a userinfo variable. * @note The game can override any of the settings in place (forcing skins or names, etc) before copying it off. */ void G_ClientUserinfoChanged (player_t * player, const char *userinfo) { const bool alreadyReady = player->isReady; const int oldTeamnum = Info_IntegerForKey(player->pers.userinfo, "cl_teamnum"); /* check for malformed or illegal info strings */ if (!Info_Validate(userinfo)) userinfo = "\\cl_name\\badinfo"; /* set name */ Q_strncpyz(player->pers.netname, Info_ValueForKey(userinfo, "cl_name"), sizeof(player->pers.netname)); Q_strncpyz(player->pers.userinfo, userinfo, sizeof(player->pers.userinfo)); player->autostand = Info_IntegerForKey(userinfo, "cl_autostand"); player->reactionLeftover = Info_IntegerForKey(userinfo, "cl_reactionleftover"); player->isReady = Info_IntegerForKey(userinfo, "cl_ready"); /* send the updated config string */ gi.ConfigString(CS_PLAYERNAMES + player->num, "%s", player->pers.netname); /* try to update to the preferred team */ if (!G_MatchIsRunning() && oldTeamnum != Info_IntegerForKey(userinfo, "cl_teamnum")) { /* if the player is marked as ready he can't change his team */ if (!alreadyReady || !player->isReady) { player->pers.team = TEAM_NO_ACTIVE; G_GetTeam(player); } else { Com_DPrintf(DEBUG_GAME, "G_ClientUserinfoChanged: player %s is already marked as being ready\n", player->pers.netname); } } }
/* * SV_UserinfoCommand_f */ static void SV_UserinfoCommand_f( client_t *client ) { char *info; unsigned int time; info = Cmd_Argv( 1 ); if( !Info_Validate( info ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "%s", "Error: Invalid userinfo" ); return; } time = Sys_Milliseconds(); if( client->userinfoLatchTimeout > time ) { Q_strncpyz( client->userinfoLatched, info, sizeof( client->userinfo ) ); } else { Q_strncpyz( client->userinfo, info, sizeof( client->userinfo ) ); client->userinfoLatched[0] = '\0'; client->userinfoLatchTimeout = time + USERINFO_UPDATE_COOLDOWN_MSEC; SV_UserinfoChanged( client ); } }
/* =========== ClientUserInfoChanged called whenever the player updates a userinfo variable. The game can override any of the settings in place (forcing skins or names, etc) before copying it off. ============ */ void ClientUserinfoChanged (edict_t *ent, char *userinfo) { char *s; int playernum; // check for malformed or illegal info strings if (!Info_Validate(userinfo)) { strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt"); } // set name s = Info_ValueForKey (userinfo, "name"); strncpy (ent->client->pers.netname, s, sizeof(ent->client->pers.netname)-1); // set skin s = Info_ValueForKey (userinfo, "skin"); zCam_SetLocalCopy(ent, s); playernum = ent-g_edicts-1; // combine name and skin into a configstring gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s", ent->client->pers.netname, s) ); // fov if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) { ent->client->ps.fov = 90; } else { ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov")); if (ent->client->ps.fov < 1) ent->client->ps.fov = 90; else if (ent->client->ps.fov > 160) ent->client->ps.fov = 160; } // handedness s = Info_ValueForKey (userinfo, "hand"); if (strlen(s)) { ent->client->pers.hand = atoi(s); } // is the player cheating? s = Info_ValueForKey (userinfo, "gl_polyblend"); if (strlen(s)) { ent->client->pers.gl_polyblend = atoi(s); } // save off the userinfo in case we want to check something later strncpy (ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)-1); }
/* * TVM_ClientUserInfoChanged * * called whenever the player updates a userinfo variable. * * The game can override any of the settings in place * (forcing skins or names, etc) before copying it off. */ void TVM_ClientUserinfoChanged( tvm_relay_t *relay, edict_t *ent, char *userinfo ) { gclient_t *cl; char *s; assert( ent && ent->local && ent->r.client ); cl = ent->r.client; // check for malformed or illegal info strings if( !Info_Validate( userinfo ) ) { trap_DropClient( relay, PLAYERNUM( ent ), DROP_TYPE_GENERAL, "Error: Invalid userinfo" ); return; } if( !Info_ValueForKey( userinfo, "name" ) ) { trap_DropClient( relay, PLAYERNUM( ent ), DROP_TYPE_GENERAL, "Error: No name set" ); return; } // set name, it's validated and possibly changed first Q_strncpyz( cl->pers.netname, Info_ValueForKey( userinfo, "name" ), sizeof( cl->pers.netname ) ); // fov s = Info_ValueForKey( userinfo, "fov" ); if( !s ) { cl->pers.fov = 100; } else { cl->pers.fov = atoi( s ); clamp( cl->pers.fov, 1, 140 ); } s = Info_ValueForKey( userinfo, "zoomfov" ); if( !s ) { cl->pers.zoomfov = 30; } else { cl->pers.zoomfov = atoi( s ); clamp( cl->pers.zoomfov, 1, 60 ); } // save off the userinfo in case we want to check something later Q_strncpyz( cl->pers.userinfo, userinfo, sizeof( cl->pers.userinfo ) ); }
/* * 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; } }
/* * SV_UserinfoCommand_f */ static void SV_UserinfoCommand_f( client_t *client ) { char *info; info = Cmd_Argv( 1 ); if( !Info_Validate( info ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Invalid userinfo" ); return; } Q_strncpyz( client->userinfo, info, sizeof( client->userinfo ) ); SV_UserinfoChanged( client ); }
/* * SV_UserinfoCommand_f */ static void SV_UserinfoCommand_f( client_t *client ) { char *info; // prevent userinfo cmd flood if( client->userinfoUpTimeout > Sys_Milliseconds() ) return; client->userinfoUpTimeout = Sys_Milliseconds() + USERINFO_UPDATE_COOLDOWN_MSEC; info = Cmd_Argv( 1 ); if( !Info_Validate( info ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "%s", "Error: Invalid userinfo" ); return; } Q_strncpyz( client->userinfo, info, sizeof( client->userinfo ) ); SV_UserinfoChanged( client ); }
/* =========== 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. ============ */ char *ClientUserinfoChanged( int clientNum, qboolean forceName ) { gentity_t *ent; 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 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\n\"" ); 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 ) ); 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 \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( !forceName && g_maxNameChanges.integer > 0 && client->pers.namelog->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( !forceName && client->pers.namelog->muted ) { trap_SendServerCommand( ent - g_entities, "print \"You cannot change your name while you are muted\n\"" ); 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 ? oldname : "UnnamedPlayer", sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { G_CensorString( client->pers.netname, newname, sizeof( client->pers.netname ), ent ); 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\" \"%c%s%c^7\"\n", clientNum, client->pers.ip.str, client->pers.guid, oldname, client->pers.netname, DECOLOR_OFF, client->pers.netname, DECOLOR_ON ); } } G_namelog_update_name( client ); } 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; // 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 = qtrue; else client->pers.disableBlueprintErrors = qfalse; // 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 = qtrue; else client->pers.useUnlagged = qfalse; 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.teamSelection, model, Com_ClientListString( &client->sess.ignoreList ), client->pers.voice ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ return NULL; }
/* =========== 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, team, health; char *s; char model[MAX_QPATH]; char headModel[MAX_QPATH]; char oldname[MAX_STRING_CHARS]; gclient_t *client; char c1[MAX_INFO_STRING]; char c2[MAX_INFO_STRING]; char redTeam[MAX_INFO_STRING]; char blueTeam[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"); } // 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, client->pers.netname, sizeof(client->pers.netname) ); 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_CONNECTED ) { if ( strcmp( oldname, client->pers.netname ) ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname) ); } } // set max health #ifdef MISSIONPACK if (client->ps.powerups[PW_GUARD]) { client->pers.maxHealth = 200; } else { health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } } #else health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } #endif client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; // set model if( g_gametype.integer >= GT_TEAM ) { Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "team_headmodel"), sizeof( headModel ) ); } else { Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headmodel"), sizeof( headModel ) ); } // bots set their team a few frames later if (g_gametype.integer >= GT_TEAM && g_entities[clientNum].r.svFlags & SVF_BOT) { s = Info_ValueForKey( userinfo, "team" ); if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) { team = TEAM_RED; } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) { team = TEAM_BLUE; } else { // pick the team with the least number of players team = PickTeam( clientNum ); } } else { team = client->sess.sessionTeam; } /* NOTE: all client side now // team switch( team ) { case TEAM_RED: ForceClientSkin(client, model, "red"); // ForceClientSkin(client, headModel, "red"); break; case TEAM_BLUE: ForceClientSkin(client, model, "blue"); // ForceClientSkin(client, headModel, "blue"); break; } // don't ever use a default skin in teamplay, it would just waste memory // however bots will always join a team but they spawn in as spectator if ( g_gametype.integer >= GT_TEAM && team == TEAM_SPECTATOR) { ForceClientSkin(client, model, "red"); // ForceClientSkin(client, headModel, "red"); } */ #ifdef MISSIONPACK if (g_gametype.integer >= GT_TEAM) { client->pers.teamInfo = qtrue; } else { s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( ! *s || atoi( s ) != 0 ) { client->pers.teamInfo = qtrue; } else { client->pers.teamInfo = qfalse; } } #else // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( ! *s || atoi( s ) != 0 ) { client->pers.teamInfo = qtrue; } else { client->pers.teamInfo = qfalse; } #endif /* s = Info_ValueForKey( userinfo, "cg_pmove_fixed" ); if ( !*s || atoi( s ) == 0 ) { client->pers.pmoveFixed = qfalse; } else { client->pers.pmoveFixed = qtrue; } */ // 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" )); strcpy(redTeam, Info_ValueForKey( userinfo, "g_redteam" )); strcpy(blueTeam, Info_ValueForKey( userinfo, "g_blueteam" )); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds if ( ent->r.svFlags & SVF_BOT ) { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d", client->pers.netname, team, model, headModel, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader ); } else { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\g_redteam\\%s\\g_blueteam\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d", client->pers.netname, client->sess.sessionTeam, model, headModel, redTeam, blueTeam, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader); } trap_SetConfigstring( CS_PLAYERS+clientNum, s ); // this is not the userinfo, more like the configstring actually G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s ); }
/* * ClientConnect * Called when a player begins connecting to the server. * The game can refuse entrance to a client by returning false. * If the client is allowed, the connection process will continue * and eventually get to ClientBegin() * Changing levels will NOT cause this to be called again, but * loadgames will. */ bool ClientConnect( edict_t *ent, char *userinfo, bool fakeClient, bool tvClient ) { char *value; assert( ent ); assert( userinfo && Info_Validate( userinfo ) ); assert( Info_ValueForKey( userinfo, "ip" ) && Info_ValueForKey( userinfo, "socket" ) ); // verify that server gave us valid data if( !Info_Validate( userinfo ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Invalid userinfo" ); return false; } if( !Info_ValueForKey( userinfo, "ip" ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Error: Server didn't provide client IP" ); return false; } if( !Info_ValueForKey( userinfo, "ip" ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Error: Server didn't provide client socket" ); return false; } // check to see if they are on the banned IP list value = Info_ValueForKey( userinfo, "ip" ); if( SV_FilterPacket( value ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "You're banned from this server" ); return false; } // check for a password value = Info_ValueForKey( userinfo, "password" ); if( !fakeClient && ( *password->string && ( !value || strcmp( password->string, value ) ) ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_PASSWORD ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); if( value && value[0] ) { Info_SetValueForKey( userinfo, "rejmsg", "Incorrect password" ); } else { Info_SetValueForKey( userinfo, "rejmsg", "Password required" ); } return false; } // they can connect G_InitEdict( ent ); ent->s.modelindex = 0; ent->r.solid = SOLID_NOT; ent->r.client = game.clients + PLAYERNUM( ent ); ent->r.svflags = ( SVF_NOCLIENT | ( fakeClient ? SVF_FAKECLIENT : 0 ) ); memset( ent->r.client, 0, sizeof( gclient_t ) ); ent->r.client->ps.playerNum = PLAYERNUM( ent ); ent->r.client->connecting = true; ent->r.client->isTV = tvClient == true; ent->r.client->team = TEAM_SPECTATOR; G_Client_UpdateActivity( ent->r.client ); // activity detected ClientUserinfoChanged( ent, userinfo ); if( !fakeClient ) { char message[MAX_STRING_CHARS]; Q_snprintfz( message, sizeof( message ), "%s%s connected", ent->r.client->netname, S_COLOR_WHITE ); G_PrintMsg( NULL, "%s\n", message ); G_Printf( "%s%s connected from %s\n", ent->r.client->netname, S_COLOR_WHITE, ent->r.client->ip ); } // let the gametype scripts know this client just connected G_Gametype_ScoreEvent( ent->r.client, "connect", NULL ); G_CallVotes_ResetClient( PLAYERNUM( ent ) ); return true; }
/** 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, team, health; char *s; char oldname[MAX_STRING_CHARS]; gclient_t *client; char userinfo[MAX_INFO_STRING]; char *nameError; 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"); // don't keep those clients and userinfo trap_DropClient(clientNum, "Invalid userinfo"); } // 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"); nameError = ClientCleanName(s, client->pers.netname, sizeof client->pers.netname); if (client->pers.connected == CON_CONNECTED && strcmp(oldname, s)) { if (client->pers.muted) { ClientPrint(ent, "You cannot change your name while you are muted."); } else if (nameError) { ClientPrint(ent, "%s", nameError); } else { G_LogPrintf("%s ^7renamed to %s\n", oldname, client->pers.netname); } if (nameError || client->pers.muted) { Q_strncpyz(client->pers.netname, oldname, sizeof client->pers.netname); } } if (client->sess.sessionTeam == TEAM_SPECTATOR) { if (client->sess.spectatorState == SPECTATOR_SCOREBOARD) { Q_strncpyz(client->pers.netname, "scoreboard", sizeof(client->pers.netname)); } } // 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; } client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; // bots set their team a few frames later if (g_gametype.integer >= GT_TEAM && g_entities[clientNum].r.svFlags & SVF_BOT) { s = Info_ValueForKey(userinfo, "team"); if (!Q_stricmp(s, "red") || !Q_stricmp(s, "r")) { team = TEAM_RED; } else if (!Q_stricmp(s, "blue") || !Q_stricmp(s, "b")) { team = TEAM_BLUE; } else { // pick the team with the least number of players team = PickTeam(clientNum); } } else { team = client->sess.sessionTeam; } // 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; // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds if (ent->r.svFlags & SVF_BOT) { s = va("n\\%s\\t\\%i\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d", client->pers.netname, team, client->pers.maxHealth, client->sess.wins, client->sess.losses, Info_ValueForKey(userinfo, "skill"), teamTask, teamLeader); } else { s = va("n\\%s\\t\\%i\\s\\%d\\r\\%i\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d", client->pers.netname, team, client->sess.specOnly, client->pers.ready, client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader); } trap_SetConfigstring(CS_PLAYERS+clientNum, s); // this is not the userinfo, more like the configstring actually G_LogPrintf("ClientUserinfoChanged: %i %s\n", clientNum, s); }
/* * Called whenever the player updates a userinfo variable. * The game can override any of the settings in place * (forcing skins or names, etc) before copying it off. */ void ClientUserinfoChanged(edict_t *ent, char *userinfo) { char *s; int playernum; if (!ent || !userinfo) { return; } /* check for malformed or illegal info strings */ if (!Info_Validate(userinfo)) { strcpy(userinfo, "\\name\\badinfo\\skin\\male/grunt"); } /* set name */ s = Info_ValueForKey(userinfo, "name"); strncpy(ent->client->pers.netname, s, sizeof(ent->client->pers.netname) - 1); /* set spectator */ s = Info_ValueForKey(userinfo, "spectator"); /* spectators are only supported in deathmatch */ if (deathmatch->value && *s && strcmp(s, "0")) { ent->client->pers.spectator = true; } else { ent->client->pers.spectator = false; } /* set skin */ s = Info_ValueForKey(userinfo, "skin"); playernum = ent - g_edicts - 1; /* combine name and skin into a configstring */ gi.configstring(CS_PLAYERSKINS + playernum, va("%s\\%s", ent->client->pers.netname, s)); /* fov */ if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) { ent->client->ps.fov = 90; } else { ent->client->ps.fov = (int)strtol(Info_ValueForKey(userinfo, "fov"), (char **)NULL, 10); if (ent->client->ps.fov < 1) { ent->client->ps.fov = 90; } else if (ent->client->ps.fov > 160) { ent->client->ps.fov = 160; } } /* handedness */ s = Info_ValueForKey(userinfo, "hand"); if (strlen(s)) { ent->client->pers.hand = (int)strtol(s, (char **)NULL, 10); } /* save off the userinfo in case we want to check something later */ strncpy(ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo) - 1); }
/* =========== PlayerUserinfoChanged Called from PlayerConnect 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 PlayerUserinfoChanged( int playerNum ) { gentity_t *ent; int teamTask, teamLeader; char *s; char model[MAX_QPATH]; char headModel[MAX_QPATH]; char oldname[MAX_STRING_CHARS]; gplayer_t *player; char c1[MAX_INFO_STRING]; char c2[MAX_INFO_STRING]; char userinfo[MAX_INFO_STRING]; ent = g_entities + playerNum; player = ent->player; trap_GetUserinfo( playerNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if ( !Info_Validate(userinfo) ) { strcpy (userinfo, "\\name\\badinfo"); // don't keep those players and userinfo trap_DropPlayer(playerNum, "Invalid userinfo"); } // check the item prediction s = Info_ValueForKey( userinfo, "cg_predictItems" ); if ( !atoi( s ) ) { player->pers.predictItemPickup = qfalse; } else { player->pers.predictItemPickup = qtrue; } // check the anti lag s = Info_ValueForKey( userinfo, "cg_antiLag" ); player->pers.antiLag = atoi( s ); // set name Q_strncpyz ( oldname, player->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey (userinfo, "name"); PlayerCleanName( s, player->pers.netname, sizeof(player->pers.netname) ); if ( player->sess.sessionTeam == TEAM_SPECTATOR ) { if ( player->sess.spectatorState == SPECTATOR_SCOREBOARD ) { Q_strncpyz( player->pers.netname, "scoreboard", sizeof(player->pers.netname) ); } } if ( player->pers.connected == CON_CONNECTED ) { if ( strcmp( oldname, player->pers.netname ) ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, player->pers.netname) ); } } // set max health #ifdef MISSIONPACK if (player->ps.powerups[PW_GUARD]) { player->pers.maxHealth = 200; } else { player->pers.maxHealth = PlayerHandicap( player ); } #else player->pers.maxHealth = PlayerHandicap( player ); #endif player->ps.stats[STAT_MAX_HEALTH] = player->pers.maxHealth; // set model if( g_gametype.integer >= GT_TEAM ) { Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "team_headmodel"), sizeof( headModel ) ); } else { Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headmodel"), sizeof( headModel ) ); } #ifdef MISSIONPACK if (g_gametype.integer >= GT_TEAM) { player->pers.teamInfo = qtrue; } else { s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( ! *s || atoi( s ) != 0 ) { player->pers.teamInfo = qtrue; } else { player->pers.teamInfo = qfalse; } } #else // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( ! *s || atoi( s ) != 0 ) { player->pers.teamInfo = qtrue; } else { player->pers.teamInfo = qfalse; } #endif /* s = Info_ValueForKey( userinfo, "cg_pmove_fixed" ); if ( !*s || atoi( s ) == 0 ) { player->pers.pmoveFixed = qfalse; } else { player->pers.pmoveFixed = qtrue; } */ // team task (0 = none, 1 = offence, 2 = defence) teamTask = atoi(Info_ValueForKey(userinfo, "teamtask")); // team Leader (1 = leader, 0 is normal player) teamLeader = player->sess.teamLeader; // colors strcpy(c1, Info_ValueForKey( userinfo, "color1" )); strcpy(c2, Info_ValueForKey( userinfo, "color2" )); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds if (ent->r.svFlags & SVF_BOT) { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d", player->pers.netname, player->sess.sessionTeam, model, headModel, c1, c2, player->pers.maxHealth, player->sess.wins, player->sess.losses, Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader ); } else { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d", player->pers.netname, player->sess.sessionTeam, model, headModel, c1, c2, player->pers.maxHealth, player->sess.wins, player->sess.losses, teamTask, teamLeader); } trap_SetConfigstring( CS_PLAYERS+playerNum, s ); // this is not the userinfo, more like the configstring actually G_LogPrintf( "PlayerUserinfoChanged: %i %s\n", playerNum, s ); }
/* * SV_UserinfoChanged * * Pull specific info from a newly changed userinfo string * into a more C friendly form. */ void SV_UserinfoChanged( client_t *client ) { char *val; int ival; assert( client ); assert( Info_Validate( client->userinfo ) ); if( !client->edict || !( client->edict->r.svflags & SVF_FAKECLIENT ) ) { // 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 ) ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (socket)\n" ); return; } if( !Info_SetValueForKey( client->userinfo, "ip", NET_AddressToString( &client->netchan.remoteAddress ) ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (ip)\n" ); return; } } // mm session ival = 0; val = Info_ValueForKey( client->userinfo, "cl_mm_session" ); if( val ) ival = atoi( val ); if( !val || ival != client->mm_session ) Info_SetValueForKey( client->userinfo, "cl_mm_session", va("%d", client->mm_session ) ); // mm login if( client->mm_login[0] != '\0' ) { Info_SetValueForKey( client->userinfo, "cl_mm_login", client->mm_login ); } else { Info_RemoveKey( client->userinfo, "cl_mm_login" ); } // call prog code to allow overrides ge->ClientUserinfoChanged( client->edict, client->userinfo ); if( !Info_Validate( client->userinfo ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Invalid userinfo (after game)" ); return; } // we assume that game module deals with setting a correct name val = Info_ValueForKey( client->userinfo, "name" ); if( !val || !val[0] ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: No name set" ); return; } Q_strncpyz( client->name, val, sizeof( client->name ) ); #ifndef RATEKILLED // rate command if( NET_IsLANAddress( &client->netchan.remoteAddress ) ) { client->rate = 99999; // lans should not rate limit } else { val = Info_ValueForKey( client->userinfo, "rate" ); if( val && val[0] ) { int newrate; newrate = atoi( val ); if( sv_maxrate->integer && newrate > sv_maxrate->integer ) newrate = sv_maxrate->integer; else if( newrate > 90000 ) newrate = 90000; if( newrate < 1000 ) newrate = 1000; if( client->rate != newrate ) { client->rate = newrate; Com_Printf( "%s%s has rate %i\n", client->name, S_COLOR_WHITE, client->rate ); } } else client->rate = 5000; } #endif }
/* =========== 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 );*/ }
/* * TV_Downstream_DirectConnect * A upstream request that did not come from the master */ static void TV_Downstream_DirectConnect( const socket_t *socket, const netadr_t *address ) { #ifdef TCP_ALLOW_TVCONNECT int incoming = 0; #endif char userinfo[MAX_INFO_STRING], *name; client_t *cl, *newcl; int i, version, game_port, challenge; bool tv_client; version = atoi( Cmd_Argv( 1 ) ); if( version != APP_PROTOCOL_VERSION ) { if( version <= 6 ) { // before reject packet was added Netchan_OutOfBandPrint( socket, address, "print\nServer is version %4.2f. Protocol %3i\n", APP_VERSION, APP_PROTOCOL_VERSION ); } else { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer and client don't have the same version\n", DROP_TYPE_GENERAL, 0 ); } return; } game_port = atoi( Cmd_Argv( 2 ) ); challenge = atoi( Cmd_Argv( 3 ) ); tv_client = ( atoi( Cmd_Argv( 5 ) ) & 1 ? true : false ); if( !Info_Validate( Cmd_Argv( 4 ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nInvalid userinfo string\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Upstream from %s refused: invalid userinfo string\n", NET_AddressToString( address ) ); return; } Q_strncpyz( userinfo, Cmd_Argv( 4 ), sizeof( userinfo ) ); // force the IP key/value pair so the game can filter based on ip if( !Info_SetValueForKey( userinfo, "socket", NET_SocketTypeToString( socket->type ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (socket)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Upstream from %s refused: couldn't set userinfo (socket)\n", NET_AddressToString( address ) ); return; } if( !Info_SetValueForKey( userinfo, "ip", NET_AddressToString( address ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (ip)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Upstream from %s refused: couldn't set userinfo (ip)\n", NET_AddressToString( address ) ); return; } // we handle name ourselves here, since tv module doesn't know about all the players name = TV_Downstream_FixName( Info_ValueForKey( userinfo, "name" ), NULL ); if( !Info_SetValueForKey( userinfo, "name", name ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (name)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Upstream from %s refused: couldn't set userinfo (name)\n", NET_AddressToString( address ) ); return; } #ifdef TCP_ALLOW_TVCONNECT if( socket->type == SOCKET_TCP ) { // find the upstream for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !tvs.incoming[i].active ) continue; if( NET_CompareAddress( &tvs.incoming[i].address, address ) && socket == &tvs.incoming[i].socket ) break; } if( i == MAX_INCOMING_CONNECTIONS ) { Com_Error( ERR_FATAL, "Incoming upstream not found.\n" ); return; } incoming = i; } #endif // see if the challenge is valid for( i = 0; i < MAX_CHALLENGES; i++ ) { if( NET_CompareBaseAddress( address, &tvs.challenges[i].adr ) ) { if( challenge == tvs.challenges[i].challenge ) { tvs.challenges[i].challenge = 0; // wsw : r1q2 : reset challenge tvs.challenges[i].time = 0; NET_InitAddress( &tvs.challenges[i].adr, NA_NOTRANSMIT ); break; // good } Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nBad challenge\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } } if( i == MAX_CHALLENGES ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nNo challenge for address\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } newcl = NULL; // if there is already a slot for this ip, reuse it for( i = 0, cl = tvs.clients; i < tv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) continue; if( NET_CompareAddress( address, &cl->netchan.remoteAddress ) || ( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) && cl->netchan.game_port == game_port ) ) { if( !NET_IsLocalAddress( address ) && ( tvs.realtime - cl->lastconnect ) < (unsigned)( tv_reconnectlimit->integer * 1000 ) ) { return; } newcl = cl; break; } } // find a client slot if( !newcl ) { for( i = 0, cl = tvs.clients; i < tv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) { newcl = cl; break; } } if( !newcl ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer is full\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } } // get the game a chance to reject this upstream or modify the userinfo if( !TV_Downstream_ClientConnect( socket, address, newcl, userinfo, game_port, challenge, tv_client ) ) { char *rejtypeflag, *rejmsg; // hax because Info_ValueForKey can only be called twice in a row rejtypeflag = va( "%s\n%s", Info_ValueForKey( userinfo, "rejtype" ), Info_ValueForKey( userinfo, "rejflag" ) ); rejmsg = Info_ValueForKey( userinfo, "rejmsg" ); Netchan_OutOfBandPrint( socket, address, "reject\n%s\n%s\n", rejtypeflag, rejmsg ); return; } // send the connect packet to the client Netchan_OutOfBandPrint( socket, address, "client_connect" ); // free the incoming entry #ifdef TCP_ALLOW_TVCONNECT if( socket->type == SOCKET_TCP ) { tvs.incoming[incoming].active = false; tvs.incoming[incoming].socket.open = false; } #endif }
/* * SVC_DirectConnect * A connection request that did not come from the master */ static void SVC_DirectConnect( const socket_t *socket, const netadr_t *address ) { #ifdef TCP_ALLOW_CONNECT int incoming = 0; #endif char userinfo[MAX_INFO_STRING]; client_t *cl, *newcl; int i, version, game_port, challenge; int previousclients; int session_id; char *session_id_str; unsigned int ticket_id; qboolean tv_client; Com_DPrintf( "SVC_DirectConnect (%s)\n", Cmd_Args() ); version = atoi( Cmd_Argv( 1 ) ); if( version != APP_PROTOCOL_VERSION ) { if( version <= 6 ) { // before reject packet was added Netchan_OutOfBandPrint( socket, address, "print\nServer is version %4.2f. Protocol %3i\n", APP_VERSION, APP_PROTOCOL_VERSION ); } else { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer and client don't have the same version\n", DROP_TYPE_GENERAL, 0 ); } Com_DPrintf( " rejected connect from protocol %i\n", version ); return; } game_port = atoi( Cmd_Argv( 2 ) ); challenge = atoi( Cmd_Argv( 3 ) ); tv_client = ( atoi( Cmd_Argv( 5 ) ) & 1 ? qtrue : qfalse ); if( !Info_Validate( Cmd_Argv( 4 ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nInvalid userinfo string\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Connection from %s refused: invalid userinfo string\n", NET_AddressToString( address ) ); return; } Q_strncpyz( userinfo, Cmd_Argv( 4 ), sizeof( userinfo ) ); // force the IP key/value pair so the game can filter based on ip if( !Info_SetValueForKey( userinfo, "socket", NET_SocketTypeToString( socket->type ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (socket)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Connection from %s refused: couldn't set userinfo (socket)\n", NET_AddressToString( address ) ); return; } if( !Info_SetValueForKey( userinfo, "ip", NET_AddressToString( address ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (ip)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Connection from %s refused: couldn't set userinfo (ip)\n", NET_AddressToString( address ) ); return; } if( Cmd_Argc() >= 7 ) { // we have extended information, ticket-id and session-id Com_Printf("Extended information %s\n", Cmd_Argv(6) ); ticket_id = (unsigned int)atoi( Cmd_Argv(6) ); session_id_str = Info_ValueForKey( userinfo, "cl_mm_session" ); if( session_id_str != NULL ) session_id = atoi( session_id_str ); else session_id = 0; } else { ticket_id = 0; session_id = 0; } #ifdef TCP_ALLOW_CONNECT if( socket->type == SOCKET_TCP ) { // find the connection for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !svs.incoming[i].active ) continue; if( NET_CompareAddress( &svs.incoming[i].address, address ) && socket == &svs.incoming[i].socket ) break; } if( i == MAX_INCOMING_CONNECTIONS ) { Com_Error( ERR_FATAL, "Incoming connection not found.\n" ); return; } incoming = i; } #endif // see if the challenge is valid for( i = 0; i < MAX_CHALLENGES; i++ ) { if( NET_CompareBaseAddress( address, &svs.challenges[i].adr ) ) { if( challenge == svs.challenges[i].challenge ) { svs.challenges[i].challenge = 0; // wsw : r1q2 : reset challenge svs.challenges[i].time = 0; NET_InitAddress( &svs.challenges[i].adr, NA_NOTRANSMIT ); break; // good } Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nBad challenge\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } } if( i == MAX_CHALLENGES ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nNo challenge for address\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } //r1: limit connections from a single IP if( sv_iplimit->integer ) { previousclients = 0; for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) continue; if( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) ) { //r1: zombies are less dangerous if( cl->state == CS_ZOMBIE ) previousclients++; else previousclients += 2; } } if( previousclients >= sv_iplimit->integer * 2 ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nToo many connections from your host\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); Com_DPrintf( "%s:connect rejected : too many connections\n", NET_AddressToString( address ) ); return; } } newcl = NULL; // if there is already a slot for this ip, reuse it for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) continue; if( NET_CompareAddress( address, &cl->netchan.remoteAddress ) || ( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) && cl->netchan.game_port == game_port ) ) { if( !NET_IsLocalAddress( address ) && ( svs.realtime - cl->lastconnect ) < (unsigned)( sv_reconnectlimit->integer * 1000 ) ) { Com_DPrintf( "%s:reconnect rejected : too soon\n", NET_AddressToString( address ) ); return; } Com_Printf( "%s:reconnect\n", NET_AddressToString( address ) ); newcl = cl; break; } } // find a client slot if( !newcl ) { for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) { newcl = cl; break; } // overwrite fakeclient if no free spots found if( cl->state && cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) ) newcl = cl; } if( !newcl ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer is full\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); Com_DPrintf( "Server is full. Rejected a connection.\n" ); return; } if( newcl->state && newcl->edict && ( newcl->edict->r.svflags & SVF_FAKECLIENT ) ) SV_DropClient( newcl, DROP_TYPE_GENERAL, "Need room for a real player" ); } // get the game a chance to reject this connection or modify the userinfo if( !SV_ClientConnect( socket, address, newcl, userinfo, game_port, challenge, qfalse, tv_client, ticket_id, session_id ) ) { char *rejtype, *rejflag, *rejtypeflag, *rejmsg; rejtype = Info_ValueForKey( userinfo, "rejtype" ); if( !rejtype ) rejtype = "0"; rejflag = Info_ValueForKey( userinfo, "rejflag" ); if( !rejflag ) rejflag = "0"; // hax because Info_ValueForKey can only be called twice in a row rejtypeflag = va( "%s\n%s", rejtype, rejflag ); rejmsg = Info_ValueForKey( userinfo, "rejmsg" ); if( !rejmsg ) rejmsg = "Game module rejected connection"; Netchan_OutOfBandPrint( socket, address, "reject\n%s\n%s\n", rejtypeflag, rejmsg ); Com_DPrintf( "Game rejected a connection.\n" ); return; } // send the connect packet to the client Netchan_OutOfBandPrint( socket, address, "client_connect\n%s", newcl->session ); // free the incoming entry #ifdef TCP_ALLOW_CONNECT if( socket->type == SOCKET_TCP ) { svs.incoming[incoming].active = qfalse; svs.incoming[incoming].socket.open = qfalse; } #endif }
/* * ClientUserinfoChanged * called whenever the player updates a userinfo variable. * * The game can override any of the settings in place * (forcing skins or names, etc) before copying it off. */ void ClientUserinfoChanged( edict_t *ent, char *userinfo ) { char *s; char oldname[MAX_INFO_VALUE]; gclient_t *cl; int rgbcolor, i; assert( ent && ent->r.client ); assert( userinfo && Info_Validate( userinfo ) ); // check for malformed or illegal info strings if( !Info_Validate( userinfo ) ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Invalid userinfo" ); return; } cl = ent->r.client; // ip s = Info_ValueForKey( userinfo, "ip" ); if( !s ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Server didn't provide client IP" ); return; } Q_strncpyz( cl->ip, s, sizeof( cl->ip ) ); // socket s = Info_ValueForKey( userinfo, "socket" ); if( !s ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Server didn't provide client socket" ); return; } Q_strncpyz( cl->socket, s, sizeof( cl->socket ) ); // color s = Info_ValueForKey( userinfo, "color" ); if( s ) rgbcolor = COM_ReadColorRGBString( s ); else rgbcolor = -1; if( rgbcolor != -1 ) { rgbcolor = COM_ValidatePlayerColor( rgbcolor ); Vector4Set( cl->color, COLOR_R( rgbcolor ), COLOR_G( rgbcolor ), COLOR_B( rgbcolor ), 255 ); } else { Vector4Set( cl->color, 255, 255, 255, 255 ); } // set name, it's validated and possibly changed first Q_strncpyz( oldname, cl->netname, sizeof( oldname ) ); G_SetName( ent, Info_ValueForKey( userinfo, "name" ) ); if( oldname[0] && Q_stricmp( oldname, cl->netname ) && !cl->isTV && !CheckFlood( ent, false ) ) G_PrintMsg( NULL, "%s%s is now known as %s%s\n", oldname, S_COLOR_WHITE, cl->netname, S_COLOR_WHITE ); if( !Info_SetValueForKey( userinfo, "name", cl->netname ) ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (name)" ); return; } // clan tag G_SetClan( ent, Info_ValueForKey( userinfo, "clan" ) ); // handedness s = Info_ValueForKey( userinfo, "hand" ); if( !s ) cl->hand = 2; else cl->hand = bound( atoi( s ), 0, 2 ); // handicap s = Info_ValueForKey( userinfo, "handicap" ); if( s ) { i = atoi( s ); if( i > 90 || i < 0 ) { G_PrintMsg( ent, "Handicap must be defined in the [0-90] range.\n" ); cl->handicap = 0; } else { cl->handicap = i; } } s = Info_ValueForKey( userinfo, "cg_movementStyle" ); if( s ) { i = bound( atoi( s ), 0, GS_MAXBUNNIES - 1 ); if( trap_GetClientState( PLAYERNUM(ent) ) < CS_SPAWNED ) { if( i != cl->movestyle ) cl->movestyle = cl->movestyle_latched = i; } else if( cl->movestyle_latched != cl->movestyle ) { G_PrintMsg( ent, "A movement style change is already in progress. Please wait.\n" ); } else if( i != cl->movestyle_latched ) { cl->movestyle_latched = i; if( cl->movestyle_latched != cl->movestyle ) { edict_t *switcher; switcher = G_Spawn(); switcher->think = think_MoveTypeSwitcher; switcher->nextThink = level.time + 10000; switcher->s.ownerNum = ENTNUM( ent ); G_PrintMsg( ent, "Movement style will change in 10 seconds.\n" ); } } } // update the movement features depending on the movestyle if( !G_ISGHOSTING( ent ) && g_allow_bunny->integer ) { if( cl->movestyle == GS_CLASSICBUNNY ) cl->ps.pmove.stats[PM_STAT_FEATURES] &= ~PMFEAT_FWDBUNNY; else cl->ps.pmove.stats[PM_STAT_FEATURES] |= PMFEAT_FWDBUNNY; } s = Info_ValueForKey( userinfo, "cg_noAutohop" ); if( s && s[0] ) { if( atoi( s ) != 0 ) cl->ps.pmove.stats[PM_STAT_FEATURES] &= ~PMFEAT_CONTINOUSJUMP; else cl->ps.pmove.stats[PM_STAT_FEATURES] |= PMFEAT_CONTINOUSJUMP; } #ifdef UCMDTIMENUDGE s = Info_ValueForKey( userinfo, "cl_ucmdTimeNudge" ); if( !s ) { cl->ucmdTimeNudge = 0; } else { cl->ucmdTimeNudge = atoi( s ); clamp( cl->ucmdTimeNudge, -MAX_UCMD_TIMENUDGE, MAX_UCMD_TIMENUDGE ); } #endif // mm session // TODO: remove the key after storing it to gclient_t ! s = Info_ValueForKey( userinfo, "cl_mm_session" ); cl->mm_session = ( s == NULL ) ? 0 : atoi( s ); s = Info_ValueForKey( userinfo, "mmflags" ); cl->mmflags = ( s == NULL ) ? 0 : strtoul( s, NULL, 10 ); // tv if( cl->isTV ) { s = Info_ValueForKey( userinfo, "tv_port" ); cl->tv.port = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "tv_port6" ); cl->tv.port6 = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "max_cl" ); cl->tv.maxclients = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "num_cl" ); cl->tv.numclients = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "chan" ); cl->tv.channel = s ? atoi( s ) : 0; } if( !G_ISGHOSTING( ent ) && trap_GetClientState( PLAYERNUM( ent ) ) >= CS_SPAWNED ) G_Client_AssignTeamSkin( ent, userinfo ); // save off the userinfo in case we want to check something later Q_strncpyz( cl->userinfo, userinfo, sizeof( cl->userinfo ) ); G_UpdatePlayerInfoString( PLAYERNUM( ent ) ); G_UpdateMMPlayerInfoString( PLAYERNUM( ent ) ); G_Gametype_ScoreEvent( cl, "userinfochanged", oldname ); }
/* =========== 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 );*/ }
/** * Validate userinfo string * @autor: Nico */ static qboolean checkUserinfoString(int clientNum, char *userinfo) { size_t len = 0; int count = 0; int i = 0; char *ip = NULL; char parsedIp[MAX_QPATH] = { 0 }; char *name = NULL; // check for malformed or illegal info strings if (!Info_Validate(userinfo)) { // Nico, drop the client G_LogPrintf("Dropping client %d: forbidden character in userinfo\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of forbidden character in userinfo", 0); return qfalse; } // Nico, check userinfo length (from combinedfixes) len = strlen(userinfo); if (len > MAX_INFO_STRING - 44) { G_LogPrintf("Dropping client %d: oversized userinfo\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of oversized userinfo", 0); return qfalse; } // Nico, check userinfo leading backslash (from combinedfixes) if (userinfo[0] != '\\') { G_LogPrintf("Dropping client %d: malformed userinfo (missing leading backslash)\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of malformed userinfo", 0); return qfalse; } // Nico, check userinfo trailing backslash (from combinedfixes) if (len > 0 && userinfo[len - 1] == '\\') { G_LogPrintf("Dropping client %d: malformed userinfo (trailing backslash)\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of malformed userinfo", 0); return qfalse; } // Nico, make sure backslah number is even (from combinedfixes) for (i = 0; i < (int)len; ++i) { if (userinfo[i] == '\\') { count++; } } if (count % 2 != 0) { G_LogPrintf("Dropping client %d: malformed userinfo (odd number of backslash)\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of malformed userinfo", 0); return qfalse; } // Nico, make sure client ip is not empty or malformed (from combinedfixes) ip = Info_ValueForKey(userinfo, "ip"); if (!strcmp(ip, "") || !getParsedIp(ip, parsedIp)) { G_LogPrintf("Dropping client %d: malformed userinfo (empty or malformed ip)\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of malformed userinfo", 0); return qfalse; } // Nico, make sure client name is not empty (from combinedfixes) name = Info_ValueForKey(userinfo, "name"); if (!strcmp(name, "")) { G_LogPrintf("Dropping client %d: malformed userinfo (empty name)\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of malformed userinfo", 0); return qfalse; } // Nico, one ip in userinfo (from ETpub) count = 0; if (len > 4) { for (i = 0; userinfo[i + 3]; ++i) { if (userinfo[i] == '\\' && userinfo[i + 1] == 'i' && userinfo[i + 2] == 'p' && userinfo[i + 3] == '\\') { count++; } } } if (count > 1) { G_LogPrintf("Dropping client %d: malformed userinfo (too many IP fields)\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of malformed userinfo", 0); return qfalse; } // Nico, one cl_guid in userinfo (from ETpub) count = 0; if (len > 9) { for (i = 0; userinfo[i + 8]; ++i) { if (userinfo[i] == '\\' && userinfo[i + 1] == 'c' && userinfo[i + 2] == 'l' && userinfo[i + 3] == '_' && userinfo[i + 4] == 'g' && userinfo[i + 5] == 'u' && userinfo[i + 6] == 'i' && userinfo[i + 7] == 'd' && userinfo[i + 8] == '\\') { count++; } } } if (count > 1) { G_LogPrintf("Dropping client %d: malformed userinfo (too many cl_guid fields)\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of malformed userinfo", 0); return qfalse; } // Nico, one name in userinfo (from ETpub) count = 0; if (len > 6) { for (i = 0; userinfo[i + 5]; ++i) { if (userinfo[i] == '\\' && userinfo[i + 1] == 'n' && userinfo[i + 2] == 'a' && userinfo[i + 3] == 'm' && userinfo[i + 4] == 'e' && userinfo[i + 5] == '\\') { count++; } } } if (count > 1) { G_LogPrintf("Dropping client %d: malformed userinfo (too many name fields)\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of malformed userinfo", 0); return qfalse; } return qtrue; }