static void Player_SetName( lua_State *L, jpluaEntity_t *ent ) { const char *name = luaL_checkstring(L, 3); char info[MAX_INFO_STRING], oldName[MAX_NETNAME]; if (!name || !*name || strlen(name) >= MAX_NETNAME) return; Q_strncpyz(oldName, ent->client->pers.netname, sizeof(oldName)); ClientCleanName(name, ent->client->pers.netname, sizeof(ent->client->pers.netname)); if (!strcmp(oldName, ent->client->pers.netname)) return; Q_strncpyz(ent->client->pers.netnameClean, ent->client->pers.netname, sizeof(ent->client->pers.netnameClean)); Q_CleanString(ent->client->pers.netnameClean, STRIP_COLOUR); if (CheckDuplicateName(ent->s.number)) { Q_strncpyz(ent->client->pers.netnameClean, ent->client->pers.netname, sizeof(ent->client->pers.netnameClean)); Q_CleanString(ent->client->pers.netnameClean, STRIP_COLOUR); } // update clientinfo trap->GetConfigstring(CS_PLAYERS + ent->s.number, info, sizeof(info)); Info_SetValueForKey(info, "n", name); trap->SetConfigstring(CS_PLAYERS + ent->s.number, info); // update userinfo (in engine) trap->GetUserinfo(ent->s.number, info, sizeof(info)); Info_SetValueForKey(info, "name", name); trap->SetUserinfo(ent->s.number, info); }
/* =========== 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 ); }
/* =========== 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 );*/ }
/* =========== 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; char *s; char oldname[MAX_STRING_CHARS]; char userinfo[MAX_INFO_STRING]; gclient_t *client; char *ip = NULL; // Nico, used to store client ip char *rate = NULL; // Nico, used to store client rate char *snaps = NULL; // Nico, used to store client snaps char *name = NULL; // Nico, used to store client name char oldAuthToken[MAX_QPATH]; // Nico, used to see if auth token was changed ent = g_entities + clientNum; client = ent->client; client->ps.clientNum = clientNum; // Nico, flood protection if (ClientIsFlooding(ent)) { G_LogPrintf("Dropping client %d: flooded userinfo\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of flooded userinfo", 0); return; } trap_GetUserinfo(clientNum, userinfo, sizeof (userinfo)); // Nico, perform security checks on userinfo string if (!checkUserinfoString(clientNum, userinfo)) { return; } if (g_developer.integer || *g_log.string || g_dedicated.integer) { G_Printf("Userinfo: %s\n", userinfo); } // check for local client ip = Info_ValueForKey(userinfo, "ip"); Q_strncpyz(client->pers.ip, ip, IP_MAX_LENGTH); if (ip && !strcmp(ip, "localhost")) { client->pers.localClient = qtrue; level.fLocalHost = qtrue; client->sess.referee = RL_REFEREE; } // Nico, store rate and snaps rate = Info_ValueForKey(userinfo, "rate"); client->pers.rate = atoi(rate); snaps = Info_ValueForKey(userinfo, "snaps"); client->pers.snaps = atoi(snaps); // Nico, backup old auth token Q_strncpyz(oldAuthToken, client->pers.authToken, sizeof (oldAuthToken)); s = Info_ValueForKey(userinfo, "cg_uinfo"); sscanf(s, "%i %i %i %i %s %i %i %i %i %i %i %i %i %i", &client->pers.clientFlags, &client->pers.clientTimeNudge, &client->pers.clientMaxPackets, // Nico, max FPS &client->pers.maxFPS, // Nico, auth Token (char *)&client->pers.authToken, // Nico, load view angles on load &client->pers.loadViewAngles, // Nico, load weapon on load &client->pers.loadWeapon, // Nico, load position when player dies &client->pers.autoLoad, // Nico, cgaz &client->pers.cgaz, // Nico, hideme &client->pers.hideme, // Nico, client auto demo record setting &client->pers.autoDemo, // Nico, automatically load checkpoints &client->pers.autoLoadCheckpoints, // Nico, persistant specLock (int *)&client->sess.specLocked, // Nico, keep all demos &client->pers.keepAllDemos ); // Nico, check if auth token was changed if (oldAuthToken[0] != '\0' && Q_stricmp(oldAuthToken, client->pers.authToken) && client->sess.logged) { // Nico, auth token was changed => logout player if he was logged in CP("cp \"You are no longer logged in!\n\""); G_LogPrintf("ClientUserinfoChanged: authToken changed for client %d, forcing logout\n", clientNum); ent->client->sess.logged = qfalse; } client->pers.autoActivate = (client->pers.clientFlags & CGF_AUTOACTIVATE) ? PICKUP_TOUCH : PICKUP_ACTIVATE; client->pers.predictItemPickup = ((client->pers.clientFlags & CGF_PREDICTITEMS) != 0); if (client->pers.clientFlags & CGF_AUTORELOAD) { client->pers.bAutoReloadAux = qtrue; client->pmext.bAutoReload = qtrue; } else { client->pers.bAutoReloadAux = qfalse; client->pmext.bAutoReload = qfalse; } // Nico, pmove_fixed client->pers.pmoveFixed = client->pers.clientFlags & CGF_PMOVEFIXED; // Nico, autologin client->pers.autoLogin = client->pers.clientFlags & CGF_AUTOLOGIN; // // Nico, name handling // // Backup old name Q_strncpyz(oldname, client->pers.netname, sizeof (oldname)); // Get new name from userinfo string name = Info_ValueForKey(userinfo, "name"); // Clean the new name ClientCleanName(name, client->pers.netname, sizeof (client->pers.netname)); // Check it's valid if (CheckName(client->pers.netname) != qtrue) { // Invalid name, restore old name Q_strncpyz(client->pers.netname, oldname, sizeof (client->pers.netname)); Info_SetValueForKey(userinfo, "name", oldname); trap_SetUserinfo(clientNum, userinfo); CPx(clientNum, "print \"^1Invalid name, name change refused\n\""); G_LogPrintf("Client %d name change refused\n", clientNum); } else { // Name is valid // Now, check if name was changed or not if (client->pers.connected == CON_CONNECTED && strcmp(oldname, client->pers.netname) != 0) { // Name was changed // Now, check name changes limit if (g_maxNameChanges.integer > -1 && client->pers.nameChanges >= g_maxNameChanges.integer) { // Nico, limit reached, forbid name change Q_strncpyz(client->pers.netname, oldname, sizeof (client->pers.netname)); Info_SetValueForKey(userinfo, "name", oldname); trap_SetUserinfo(clientNum, userinfo); CPx(clientNum, "print \"^1You had too many namechanges\n\""); G_LogPrintf("Client %d name change refused\n", clientNum); return; } client->pers.nameChanges++; trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname)); } } // // Nico, end of name handling // client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; // To communicate it to cgame client->ps.stats[STAT_PLAYER_CLASS] = client->sess.playerType; // Gordon: Not needed any more as it's in clientinfo? // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds s = va("n\\%s\\t\\%i\\c\\%i\\w\\%i\\lw\\%i\\sw\\%i\\mu\\%i\\ref\\%i\\pm\\%i\\l\\%i\\h\\%i\\cc\\%i", client->pers.netname, client->sess.sessionTeam, client->sess.playerType, client->sess.playerWeapon, client->sess.latchPlayerWeapon, client->sess.latchPlayerWeapon2, client->sess.muted ? 1 : 0, client->sess.referee, client->pers.pmoveFixed ? 1 : 0, // Nico, pmove_fixed client->sess.logged ? 1 : 0, // Nico, login status client->pers.hideme, // Nico, hideme client->sess.countryCode// Nico, country code (GeoIP) ); trap_GetConfigstring(CS_PLAYERS + clientNum, oldname, sizeof (oldname)); trap_SetConfigstring(CS_PLAYERS + clientNum, s); if (!Q_stricmp(oldname, s)) { return; } G_LogPrintf("ClientUserinfoChanged: %i %s\n", clientNum, s); G_DPrintf("ClientUserinfoChanged: %i :: %s\n", clientNum, s); }
/** Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. */ char *ClientConnect(int clientNum, qboolean firstTime, qboolean isBot) { char name[MAX_NETNAME]; char *value, *nameError; gclient_t *client; char userinfo[MAX_INFO_STRING]; gentity_t *ent; ent = &g_entities[ clientNum ]; trap_GetUserinfo(clientNum, userinfo, sizeof(userinfo)); if (G_BanCheck(userinfo)) { return "You are banned from this server."; } value = Info_ValueForKey(userinfo, "ip"); nameError = Info_ValueForKey(userinfo, "name"); nameError = ClientCleanName(nameError, name, sizeof name); // we don't check password for bots and local client // NOTE: local client <-> "ip" "localhost" // this means this client is not running in our current process if (!isBot && strcmp(value, "localhost")) { // check for invalid player name if (nameError) { return nameError; } // check for a password value = Info_ValueForKey (userinfo, "password"); if (g_password.string[0] && Q_stricmp(g_password.string, "none") && strcmp(g_password.string, value) != 0) { return "Invalid password"; } } // if a player reconnects quickly after a disconnect, the client disconnect may never be called, thus flag can get lost in the ether if (ent->inuse) { G_LogPrintf("Forcing disconnect on active client: %i\n", clientNum); // so lets just fix up anything that should happen on a disconnect ClientDisconnect(clientNum); } // they can connect ent->client = level.clients + clientNum; client = ent->client; memset(client, 0, sizeof(*client)); client->pers.connected = CON_CONNECTING; // read or initialize the session data if (firstTime || level.newSession) { G_InitSessionData(client, userinfo); } G_ReadSessionData(client); if (isBot) { ent->r.svFlags |= SVF_BOT; ent->inuse = qtrue; if (!G_BotConnect(clientNum, !firstTime)) { return "BotConnectfailed"; } } // don't do the "xxx connected" messages if they were caried over from previous level if (firstTime) { ClientScreenPrint(NULL, "%s ^7connected", name); } // get and distribute relevent paramters G_LogPrintf("ClientConnect: %i\n", clientNum); ClientUserinfoChanged(clientNum); if (g_gametype.integer >= GT_TEAM && client->sess.sessionTeam != TEAM_SPECTATOR) { LogTeamChange(client, -1); } // count current clients and rank for scoreboard CalculateRanks(); return NULL; }
/** 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); }