/* =========== 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 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 Q_strncpyz(model, Info_ValueForKey(userinfo, "model"), sizeof(model)); // 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 0 if(ent->r.svFlags & SVF_BOT) { Com_sprintf(userinfo, sizeof(userinfo), "n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\g_redteam\\%s\\g_blueteam\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d", client->pers.netname, team, model, headModel, redTeam, blueTeam, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, Info_ValueForKey(userinfo, "skill"), teamTask, teamLeader); } else #endif { Com_sprintf(userinfo, sizeof(userinfo), "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, team, model, "", redTeam, blueTeam, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader); } trap_SetConfigstring(CS_PLAYERS + clientNum, userinfo); #ifdef G_LUA // Lua API callbacks // This only gets called when the ClientUserinfo is changed, replicating // ETPro's behaviour. G_LuaHook_ClientUserinfoChanged(clientNum); #endif // this is not the userinfo, more like the configstring actually G_LogPrintf("ClientUserinfoChanged: %i %s\n", clientNum, s); }
/* ================== SV_DirectConnect A "connect" OOB command has been received ================== */ void SV_DirectConnect( netadr_t from, const Cmd::Args& args ) { char userinfo[ MAX_INFO_STRING ]; int i; client_t *cl, *newcl; client_t temp; sharedEntity_t *ent; int clientNum; int version; int qport; int challenge; const char *password; int startIndex; bool denied; char reason[ MAX_STRING_CHARS ]; int count; const char *ip; #ifdef HAVE_GEOIP const char *country = nullptr; #endif if ( args.Argc() < 2 ) { return; } Log::Debug( "SVC_DirectConnect ()" ); Q_strncpyz( userinfo, args.Argv(1).c_str(), sizeof( userinfo ) ); // DHM - Nerve :: Update Server allows any protocol to connect // NOTE TTimo: but we might need to store the protocol around for potential non http/ftp clients version = atoi( Info_ValueForKey( userinfo, "protocol" ) ); if ( version != PROTOCOL_VERSION ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\nServer uses protocol version %i (yours is %i).", PROTOCOL_VERSION, version ); Log::Debug( " rejected connect from version %i", version ); return; } challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) ); qport = atoi( Info_ValueForKey( userinfo, "qport" ) ); // quick reject for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { // DHM - Nerve :: This check was allowing clients to reconnect after zombietime(2 secs) //if ( cl->state == CS_FREE ) { //continue; //} if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { if ( ( svs.time - cl->lastConnectTime ) < ( sv_reconnectlimit->integer * 1000 ) ) { Log::Debug( "%s: reconnect rejected: too soon", NET_AdrToString( from ) ); return; } break; } } if ( NET_IsLocalAddress( from ) ) { ip = "localhost"; } else { ip = NET_AdrToString( from ); } if ( ( strlen( ip ) + strlen( userinfo ) + 4 ) >= MAX_INFO_STRING ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\nUserinfo string length exceeded. " "Try removing setu cvars from your config." ); return; } Info_SetValueForKey( userinfo, "ip", ip, false ); // see if the challenge is valid (local clients don't need to challenge) if ( !NET_IsLocalAddress( from ) ) { int ping; for ( i = 0; i < MAX_CHALLENGES; i++ ) { if ( NET_CompareAdr( from, svs.challenges[ i ].adr ) ) { if ( challenge == svs.challenges[ i ].challenge ) { break; // good } } } if ( i == MAX_CHALLENGES ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]No or bad challenge for address." ); return; } // force the IP address key/value pair, so the game can filter based on it Info_SetValueForKey( userinfo, "ip", NET_AdrToString( from ), false ); if ( svs.challenges[ i ].firstPing == 0 ) { ping = svs.time - svs.challenges[ i ].pingTime; svs.challenges[ i ].firstPing = ping; } else { ping = svs.challenges[ i ].firstPing; } #ifdef HAVE_GEOIP country = NET_GeoIP_Country( &from ); if ( country ) { Log::Notice( "Client %i connecting from %s with %i challenge ping\n", i, country, ping ); } else { Log::Notice( "Client %i connecting from somewhere unknown with %i challenge ping\n", i, ping ); } #else Log::Notice( "Client %i connecting with %i challenge ping\n", i, ping ); #endif svs.challenges[ i ].connected = true; // never reject a LAN client based on ping if ( !Sys_IsLANAddress( from ) ) { if ( sv_minPing->value && ping < sv_minPing->value ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]Server is for high pings only" ); Log::Debug( "Client %i rejected on a too low ping", i ); return; } if ( sv_maxPing->value && ping > sv_maxPing->value ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]Server is for low pings only" ); Log::Debug( "Client %i rejected on a too high ping: %i", i, ping ); return; } } } else { // force the "ip" info key to "localhost" Info_SetValueForKey( userinfo, "ip", "localhost", false ); } newcl = &temp; memset( newcl, 0, sizeof( client_t ) ); // if there is already a slot for this IP address, reuse it for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if ( cl->state == clientState_t::CS_FREE ) { continue; } if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { Log::Notice( "%s:reconnect\n", NET_AdrToString( from ) ); newcl = cl; // this doesn't work because it nukes the players userinfo // // disconnect the client from the game first so any flags the // // player might have are dropped // VM_Call( gvm, GAME_CLIENT_DISCONNECT, newcl - svs.clients ); // goto gotnewcl; } } // find a client slot // if "sv_privateClients" is set > 0, then that number // of client slots will be reserved for connections that // have "password" set to the value of "sv_privatePassword" // Info requests will report the maxclients as if the private // slots didn't exist, to prevent people from trying to connect // to a full server. // This is to allow us to reserve a couple slots here on our // servers so we can play without having to kick people. // check for privateClient password password = Info_ValueForKey( userinfo, "password" ); if ( !strcmp( password, sv_privatePassword->string ) ) { startIndex = 0; } else { // skip past the reserved slots startIndex = sv_privateClients->integer; } newcl = nullptr; for ( i = startIndex; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[ i ]; if ( cl->state == clientState_t::CS_FREE ) { newcl = cl; break; } } if ( !newcl ) { if ( NET_IsLocalAddress( from ) ) { count = 0; for ( i = startIndex; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[ i ]; if ( SV_IsBot(cl) ) { count++; } } // if they're all bots if ( count >= sv_maxclients->integer - startIndex ) { SV_DropClient( &svs.clients[ sv_maxclients->integer - 1 ], "only bots on server" ); newcl = &svs.clients[ sv_maxclients->integer - 1 ]; } else { Com_Error( errorParm_t::ERR_FATAL, "server is full on local connect" ); } } else { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n%s", sv_fullmsg->string ); Log::Debug( "Rejected a connection." ); return; } } // we got a newcl, so reset the reliableSequence and reliableAcknowledge cl->reliableAcknowledge = 0; cl->reliableSequence = 0; gotnewcl: // build a new connection // accept the new client // this is the only place a client_t is ever initialized *newcl = std::move(temp); clientNum = newcl - svs.clients; ent = SV_GentityNum( clientNum ); newcl->gentity = ent; ent->r.svFlags = 0; #ifdef HAVE_GEOIP if ( country ) { Info_SetValueForKey( userinfo, "geoip", country, false ); } #endif // save the challenge newcl->challenge = challenge; // save the address Netchan_Setup( netsrc_t::NS_SERVER, &newcl->netchan, from, qport ); // init the netchan queue // Save the pubkey Q_strncpyz( newcl->pubkey, Info_ValueForKey( userinfo, "pubkey" ), sizeof( newcl->pubkey ) ); Info_RemoveKey( userinfo, "pubkey", false ); // save the userinfo Q_strncpyz( newcl->userinfo, userinfo, sizeof( newcl->userinfo ) ); // get the game a chance to reject this connection or modify the userinfo denied = gvm.GameClientConnect( reason, sizeof( reason ), clientNum, true, false ); // firstTime = true if ( denied ) { NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "print\n[err_dialog]%s", reason ); Log::Debug( "Game rejected a connection: %s.", reason ); return; } SV_UserinfoChanged( newcl ); // DHM - Nerve :: Clear out firstPing now that client is connected svs.challenges[ i ].firstPing = 0; // send the connect packet to the client NET_OutOfBandPrint( netsrc_t::NS_SERVER, from, "connectResponse" ); Log::Debug( "Going from CS_FREE to CS_CONNECTED for %s", newcl->name ); newcl->state = clientState_t::CS_CONNECTED; newcl->nextSnapshotTime = svs.time; newcl->lastPacketTime = svs.time; newcl->lastConnectTime = svs.time; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit newcl->gamestateMessageNum = -1; // if this was the first client on the server, or the last client // the server can hold, send a heartbeat to the master. count = 0; for ( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if ( svs.clients[ i ].state >= clientState_t::CS_CONNECTED ) { count++; } } if ( count == 1 || count == sv_maxclients->integer ) { SV_Heartbeat_f(); } }
// called on a first-time connect void G_InitClientSessionData( gclient_t *client, char *userinfo, qboolean isBot ) { clientSession_t *sess = &client->sess; const char *value; client->sess.siegeDesiredTeam = TEAM_FREE; // initial team determination if ( level.gametype >= GT_TEAM ) { if ( g_teamAutoJoin.integer && !(g_entities[client - level.clients].r.svFlags & SVF_BOT) ) { sess->sessionTeam = PickTeam( -1 ); BroadcastTeamChange( client, -1 ); } else { // always spawn as spectator in team games if ( !isBot ) { sess->sessionTeam = TEAM_SPECTATOR; } else { // bots choose their team on creation value = Info_ValueForKey( userinfo, "team" ); if ( value[0] == 'r' || value[0] == 'R' ) { sess->sessionTeam = TEAM_RED; } else if ( value[0] == 'b' || value[0] == 'B' ) { sess->sessionTeam = TEAM_BLUE; } else { sess->sessionTeam = PickTeam( -1 ); } BroadcastTeamChange( client, -1 ); } } } else { value = Info_ValueForKey( userinfo, "team" ); if ( value[0] == 's' ) { // a willing spectator, not a waiting-in-line sess->sessionTeam = TEAM_SPECTATOR; } else { switch ( level.gametype ) { default: case GT_FFA: case GT_HOLOCRON: case GT_JEDIMASTER: case GT_SINGLE_PLAYER: if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer ) { sess->sessionTeam = TEAM_SPECTATOR; } else if ( g_teamAutoJoin.integer == 2 ) { // force joining in all gametypes sess->sessionTeam = TEAM_FREE; } else if ( !isBot ) { sess->sessionTeam = TEAM_SPECTATOR; } else { // bots automatically join the game sess->sessionTeam = TEAM_FREE; } break; case GT_DUEL: // if the game is full, go into a waiting mode if ( level.numNonSpectatorClients >= 2 ) { sess->sessionTeam = TEAM_SPECTATOR; } else { sess->sessionTeam = TEAM_FREE; } break; case GT_POWERDUEL: { int loners = 0, doubles = 0; G_PowerDuelCount( &loners, &doubles, qtrue ); if ( !doubles || loners > (doubles / 2) ) { sess->duelTeam = DUELTEAM_DOUBLE; } else { sess->duelTeam = DUELTEAM_LONE; } sess->sessionTeam = TEAM_SPECTATOR; } break; } } } if ( sess->sessionTeam == TEAM_SPECTATOR ) { sess->spectatorState = SPECTATOR_FREE; } else { sess->spectatorState = SPECTATOR_NOT; } sess->spectatorTime = level.time; sess->siegeClass[0] = '\0'; G_WriteClientSessionData( client ); }
void G_LogWeaponOutput(void) { #ifdef LOGGING_WEAPONS int i,j,curwp; float pershot; fileHandle_t weaponfile; char string[1024]; int totalpickups[WP_NUM_WEAPONS]; int totaltime[WP_NUM_WEAPONS]; int totaldeaths[WP_NUM_WEAPONS]; int totaldamageMOD[MOD_MAX]; int totalkillsMOD[MOD_MAX]; int totaldamage[WP_NUM_WEAPONS]; int totalkills[WP_NUM_WEAPONS]; int totalshots[WP_NUM_WEAPONS]; int percharacter[WP_NUM_WEAPONS]; char info[1024]; char mapname[128]; char *nameptr, *unknownname="<Unknown>"; if (!g_statLog.integer) { return; } G_LogPrintf("*****************************Weapon Log:\n" ); memset(totalpickups, 0, sizeof(totalpickups)); memset(totaltime, 0, sizeof(totaltime)); memset(totaldeaths, 0, sizeof(totaldeaths)); memset(totaldamageMOD, 0, sizeof(totaldamageMOD)); memset(totalkillsMOD, 0, sizeof(totalkillsMOD)); memset(totaldamage, 0, sizeof(totaldamage)); memset(totalkills, 0, sizeof(totalkills)); memset(totalshots, 0, sizeof(totalshots)); for (i=0; i<MAX_CLIENTS; i++) { if (G_WeaponLogClientTouch[i]) { // Ignore any entity/clients we don't care about! for (j=0;j<WP_NUM_WEAPONS;j++) { totalpickups[j] += G_WeaponLogPickups[i][j]; totaltime[j] += G_WeaponLogTime[i][j]; totaldeaths[j] += G_WeaponLogDeaths[i][j]; totalshots[j] += G_WeaponLogFired[i][j]; } for (j=0;j<MOD_MAX;j++) { totaldamageMOD[j] += G_WeaponLogDamage[i][j]; totalkillsMOD[j] += G_WeaponLogKills[i][j]; } } } // Now total the weapon data from the MOD data. for (j=0; j<MOD_MAX; j++) { if (j <= MOD_SENTRY) { curwp = weaponFromMOD[j]; totaldamage[curwp] += totaldamageMOD[j]; totalkills[curwp] += totalkillsMOD[j]; } } G_LogPrintf( "\n****Data by Weapon:\n" ); for (j=0; j<WP_NUM_WEAPONS; j++) { G_LogPrintf("%15s: Pickups: %4d, Time: %5d, Deaths: %5d\n", weaponNameFromIndex[j], totalpickups[j], (int)(totaltime[j]/1000), totaldeaths[j]); } G_LogPrintf( "\n****Combat Data by Weapon:\n" ); for (j=0; j<WP_NUM_WEAPONS; j++) { if (totalshots[j] > 0) { pershot = (float)(totaldamage[j])/(float)(totalshots[j]); } else { pershot = 0; } G_LogPrintf("%15s: Damage: %6d, Kills: %5d, Dmg per Shot: %f\n", weaponNameFromIndex[j], totaldamage[j], totalkills[j], pershot); } G_LogPrintf( "\n****Combat Data By Damage Type:\n" ); for (j=0; j<MOD_MAX; j++) { G_LogPrintf("%25s: Damage: %6d, Kills: %5d\n", modNames[j], totaldamageMOD[j], totalkillsMOD[j]); } G_LogPrintf("\n"); // Write the whole weapon statistic log out to a file. trap_FS_FOpenFile( g_statLogFile.string, &weaponfile, FS_APPEND ); if (!weaponfile) { //failed to open file, let's not crash, shall we? return; } // Write out the level name trap_GetServerinfo(info, sizeof(info)); strncpy(mapname, Info_ValueForKey( info, "mapname" ), sizeof(mapname)-1); mapname[sizeof(mapname)-1] = '\0'; Com_sprintf(string, sizeof(string), "\n\n\nLevel:\t%s\n\n\n", mapname); trap_FS_Write( string, strlen( string ), weaponfile); // Combat data per character // Start with Pickups per character Com_sprintf(string, sizeof(string), "Weapon Pickups per Player:\n\n"); trap_FS_Write( string, strlen( string ), weaponfile); Com_sprintf(string, sizeof(string), "Player"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0; j<WP_NUM_WEAPONS; j++) { Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); // Cycle through each player, give their name and the number of times they picked up each weapon. for (i=0; i<MAX_CLIENTS; i++) { if (G_WeaponLogClientTouch[i]) { // Ignore any entity/clients we don't care about! if ( g_entities[i].client ) { nameptr = g_entities[i].client->pers.netname; } else { nameptr = unknownname; } trap_FS_Write(nameptr, strlen(nameptr), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogPickups[i][j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); } } // Sum up the totals. Com_sprintf(string, sizeof(string), "\n***TOTAL:"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", totalpickups[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n\n\n"); trap_FS_Write(string, strlen(string), weaponfile); // Weapon fires per character Com_sprintf(string, sizeof(string), "Weapon Shots per Player:\n\n"); trap_FS_Write( string, strlen( string ), weaponfile); Com_sprintf(string, sizeof(string), "Player"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0; j<WP_NUM_WEAPONS; j++) { Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); // Cycle through each player, give their name and the number of times they picked up each weapon. for (i=0; i<MAX_CLIENTS; i++) { if (G_WeaponLogClientTouch[i]) { // Ignore any entity/clients we don't care about! if ( g_entities[i].client ) { nameptr = g_entities[i].client->pers.netname; } else { nameptr = unknownname; } trap_FS_Write(nameptr, strlen(nameptr), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogFired[i][j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); } } // Sum up the totals. Com_sprintf(string, sizeof(string), "\n***TOTAL:"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", totalshots[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n\n\n"); trap_FS_Write(string, strlen(string), weaponfile); // Weapon time per character Com_sprintf(string, sizeof(string), "Weapon Use Time per Player:\n\n"); trap_FS_Write( string, strlen( string ), weaponfile); Com_sprintf(string, sizeof(string), "Player"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0; j<WP_NUM_WEAPONS; j++) { Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); // Cycle through each player, give their name and the number of times they picked up each weapon. for (i=0; i<MAX_CLIENTS; i++) { if (G_WeaponLogClientTouch[i]) { // Ignore any entity/clients we don't care about! if ( g_entities[i].client ) { nameptr = g_entities[i].client->pers.netname; } else { nameptr = unknownname; } trap_FS_Write(nameptr, strlen(nameptr), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogTime[i][j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); } } // Sum up the totals. Com_sprintf(string, sizeof(string), "\n***TOTAL:"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", totaltime[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n\n\n"); trap_FS_Write(string, strlen(string), weaponfile); // Weapon deaths per character Com_sprintf(string, sizeof(string), "Weapon Deaths per Player:\n\n"); trap_FS_Write( string, strlen( string ), weaponfile); Com_sprintf(string, sizeof(string), "Player"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0; j<WP_NUM_WEAPONS; j++) { Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); // Cycle through each player, give their name and the number of times they picked up each weapon. for (i=0; i<MAX_CLIENTS; i++) { if (G_WeaponLogClientTouch[i]) { // Ignore any entity/clients we don't care about! if ( g_entities[i].client ) { nameptr = g_entities[i].client->pers.netname; } else { nameptr = unknownname; } trap_FS_Write(nameptr, strlen(nameptr), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogDeaths[i][j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); } } // Sum up the totals. Com_sprintf(string, sizeof(string), "\n***TOTAL:"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", totaldeaths[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n\n\n"); trap_FS_Write(string, strlen(string), weaponfile); // Weapon damage per character Com_sprintf(string, sizeof(string), "Weapon Damage per Player:\n\n"); trap_FS_Write( string, strlen( string ), weaponfile); Com_sprintf(string, sizeof(string), "Player"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0; j<WP_NUM_WEAPONS; j++) { Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); // Cycle through each player, give their name and the number of times they picked up each weapon. for (i=0; i<MAX_CLIENTS; i++) { if (G_WeaponLogClientTouch[i]) { // Ignore any entity/clients we don't care about! // We must grab the totals from the damage types for the player and map them to the weapons. memset(percharacter, 0, sizeof(percharacter)); for (j=0; j<MOD_MAX; j++) { if (j <= MOD_SENTRY) { curwp = weaponFromMOD[j]; percharacter[curwp] += G_WeaponLogDamage[i][j]; } } if ( g_entities[i].client ) { nameptr = g_entities[i].client->pers.netname; } else { nameptr = unknownname; } trap_FS_Write(nameptr, strlen(nameptr), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", percharacter[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); } } // Sum up the totals. Com_sprintf(string, sizeof(string), "\n***TOTAL:"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", totaldamage[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n\n\n"); trap_FS_Write(string, strlen(string), weaponfile); // Weapon kills per character Com_sprintf(string, sizeof(string), "Weapon Kills per Player:\n\n"); trap_FS_Write( string, strlen( string ), weaponfile); Com_sprintf(string, sizeof(string), "Player"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0; j<WP_NUM_WEAPONS; j++) { Com_sprintf(string, sizeof(string), "\t%s", weaponNameFromIndex[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); // Cycle through each player, give their name and the number of times they picked up each weapon. for (i=0; i<MAX_CLIENTS; i++) { if (G_WeaponLogClientTouch[i]) { // Ignore any entity/clients we don't care about! // We must grab the totals from the damage types for the player and map them to the weapons. memset(percharacter, 0, sizeof(percharacter)); for (j=0; j<MOD_MAX; j++) { if (j <= MOD_SENTRY) { curwp = weaponFromMOD[j]; percharacter[curwp] += G_WeaponLogKills[i][j]; } } if ( g_entities[i].client ) { nameptr = g_entities[i].client->pers.netname; } else { nameptr = unknownname; } trap_FS_Write(nameptr, strlen(nameptr), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", percharacter[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); } } // Sum up the totals. Com_sprintf(string, sizeof(string), "\n***TOTAL:"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0;j<WP_NUM_WEAPONS;j++) { Com_sprintf(string, sizeof(string), "\t%d", totalkills[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n\n\n"); trap_FS_Write(string, strlen(string), weaponfile); // Damage type damage per character Com_sprintf(string, sizeof(string), "Typed Damage per Player:\n\n"); trap_FS_Write( string, strlen( string ), weaponfile); Com_sprintf(string, sizeof(string), "Player"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0; j<MOD_MAX; j++) { Com_sprintf(string, sizeof(string), "\t%s", modNames[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); // Cycle through each player, give their name and the number of times they picked up each weapon. for (i=0; i<MAX_CLIENTS; i++) { if (G_WeaponLogClientTouch[i]) { // Ignore any entity/clients we don't care about! if ( g_entities[i].client ) { nameptr = g_entities[i].client->pers.netname; } else { nameptr = unknownname; } trap_FS_Write(nameptr, strlen(nameptr), weaponfile); for (j=0;j<MOD_MAX;j++) { Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogDamage[i][j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); } } // Sum up the totals. Com_sprintf(string, sizeof(string), "\n***TOTAL:"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0;j<MOD_MAX;j++) { Com_sprintf(string, sizeof(string), "\t%d", totaldamageMOD[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n\n\n"); trap_FS_Write(string, strlen(string), weaponfile); // Damage type kills per character Com_sprintf(string, sizeof(string), "Damage-Typed Kills per Player:\n\n"); trap_FS_Write( string, strlen( string ), weaponfile); Com_sprintf(string, sizeof(string), "Player"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0; j<MOD_MAX; j++) { Com_sprintf(string, sizeof(string), "\t%s", modNames[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); // Cycle through each player, give their name and the number of times they picked up each weapon. for (i=0; i<MAX_CLIENTS; i++) { if (G_WeaponLogClientTouch[i]) { // Ignore any entity/clients we don't care about! if ( g_entities[i].client ) { nameptr = g_entities[i].client->pers.netname; } else { nameptr = unknownname; } trap_FS_Write(nameptr, strlen(nameptr), weaponfile); for (j=0;j<MOD_MAX;j++) { Com_sprintf(string, sizeof(string), "\t%d", G_WeaponLogKills[i][j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n"); trap_FS_Write(string, strlen(string), weaponfile); } } // Sum up the totals. Com_sprintf(string, sizeof(string), "\n***TOTAL:"); trap_FS_Write(string, strlen(string), weaponfile); for (j=0;j<MOD_MAX;j++) { Com_sprintf(string, sizeof(string), "\t%d", totalkillsMOD[j]); trap_FS_Write(string, strlen(string), weaponfile); } Com_sprintf(string, sizeof(string), "\n\n\n"); trap_FS_Write(string, strlen(string), weaponfile); trap_FS_FCloseFile(weaponfile); #endif //LOGGING_WEAPONS }
int UI_ParseInfos( char *buf, int max, char *infos[] ) { char *token; int count; char key[MAX_TOKEN_CHARS]; char info[MAX_INFO_STRING]; count = 0; while ( 1 ) { token = COM_Parse( (const char **)&buf ); if ( !token[0] ) { break; } if ( strcmp( token, "{" ) ) { Com_Printf( "Missing { in info file\n" ); break; } if ( count == max ) { Com_Printf( "Max infos exceeded\n" ); break; } info[0] = '\0'; while ( 1 ) { token = COM_ParseExt( (const char **)&buf, qtrue ); if ( !token[0] ) { Com_Printf( "Unexpected end of info file\n" ); break; } if ( !strcmp( token, "}" ) ) { break; } Q_strncpyz( key, token, sizeof(key) ); token = COM_ParseExt( (const char **)&buf, qfalse ); if ( !token[0] ) { strcpy( token, "<NULL>" ); } Info_SetValueForKey( info, key, token ); } //NOTE: extra space for arena number infos[count] = (char *)UI_Alloc( strlen( info ) + strlen( "\\num\\" ) + strlen( va( "%d", MAX_ARENAS ) ) + 1 ); if ( infos[count] ) { strcpy( infos[count], info ); #ifdef _DEBUG if ( trap->Cvar_VariableValue( "com_buildScript" ) ) { const char *botFile = Info_ValueForKey( info, "personality" ); if ( botFile && botFile[0] ) { int fh = 0; trap->FS_Open( botFile, &fh, FS_READ ); if ( fh ) { trap->FS_Close( fh ); } } } #endif count++; } } return count; }
/* * 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 ); // 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_Gametype_ScoreEvent( cl, "userinfochanged", oldname ); }
/* =============== G_AddRandomBot =============== */ void G_AddRandomBot( int team ) { int i, n, num; float skill; char *value, netname[36], *teamstr; gclient_t *cl; num = 0; for ( n = 0; n < g_numBots ; n++ ) { value = Info_ValueForKey( g_botInfos[n], "name" ); // for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } if ( !Q_stricmp( value, cl->pers.netname ) ) { break; } } if (i >= g_maxclients.integer) { num++; } } num = random() * num; for ( n = 0; n < g_numBots ; n++ ) { value = Info_ValueForKey( g_botInfos[n], "name" ); // for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; if ( cl->pers.connected != CON_CONNECTED ) { continue; } if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { continue; } if ( team >= 0 && cl->sess.sessionTeam != team ) { continue; } if ( !Q_stricmp( value, cl->pers.netname ) ) { break; } } if (i >= g_maxclients.integer) { num--; if (num <= 0) { skill = trap_Cvar_VariableValue( "g_spSkill" ); if (team == TEAM_RED) teamstr = "red"; else if (team == TEAM_BLUE) teamstr = "blue"; else teamstr = ""; Q_strncpyz(netname, value, sizeof(netname)); Q_CleanStr(netname); trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f %s %i\n", netname, skill, teamstr, 0) ); return; } } } }
/* ================== SCR_DrawClients draws clients list with selected values from userinfo ================== */ void SCR_DrawClients(void) { extern sb_showclients; int uid_w, clients; int i, y, x; char line[128], buf[64]; if (!sb_showclients || cls.state == ca_disconnected) return; // prepare clients = 0; uid_w = 3; for (i=0; i < MAX_CLIENTS; i++) { int w; if (!cl.players[i].name[0]) continue; clients++; w = strlen(va("%d", cl.players[i].userid)); if (w > uid_w) uid_w = w; } y = (vid.height - sb_lines - 8 * (clients + 6)); y = max (y, 0); x = (vid.width - 320) / 2 + 4; strlcpy (line, " # ", sizeof (line)); snprintf (buf, sizeof (buf), "%*.*s ", uid_w, uid_w, "uid"); strlcat (line, buf, sizeof (line)); snprintf (buf, sizeof (buf), "%-*.*s ", 16-uid_w, 16-uid_w, "name"); strlcat (line, buf, sizeof (line)); strlcat (line, "team skin rate", sizeof (line)); Draw_String (x, y, line); y += 8; strlcpy (line, "\x1D\x1F \x1D", sizeof (line)); snprintf (buf, sizeof (buf), "%*.*s", uid_w-2, uid_w-2, "\x1E\x1E\x1E\x1E"); strlcat (line, buf, sizeof (line)); strlcat (line, "\x1F \x1D", sizeof (line)); snprintf (buf, sizeof (buf), "%*.*s", 16-uid_w-2, 16-uid_w-2, "\x1E\x1E\x1E\x1E\x1E\x1E\x1E\x1E\x1E\x1E\x1E\x1E"); strlcat (line, buf, sizeof (line)); strlcat (line, "\x1F \x1D\x1E\x1E\x1F \x1D\x1E\x1E\x1E\x1E\x1E\x1E\x1F \x1D\x1E\x1E\x1F", sizeof (line)); Draw_String(x, y, line); y += 8; for (i=0; i < MAX_CLIENTS; i++) { if (!cl.players[i].name[0]) continue; if (y > vid.height - 8) break; line[0] = 0; snprintf (buf, sizeof (buf), "%2d ", i); strlcat (line, buf, sizeof (line)); snprintf (buf, sizeof (buf), "%*d ", uid_w, cl.players[i].userid); strlcat(line, buf, sizeof (line)); snprintf (buf, sizeof (buf), "%-*.*s ", 16-uid_w, 16-uid_w, cl.players[i].name); strlcat (line, buf, sizeof (line)); snprintf(buf, sizeof (buf), "%-4.4s ", Info_ValueForKey(cl.players[i].userinfo, "team")); strlcat (line, buf, sizeof (line)); if (cl.players[i].spectator) strlcpy (buf, "<spec> ", sizeof (buf)); else snprintf (buf, sizeof (buf), "%-8.8s ", Info_ValueForKey(cl.players[i].userinfo, "skin")); strlcat (line, buf, sizeof (line)); snprintf (buf, sizeof (buf), "%4d", min(9999, atoi(Info_ValueForKey(cl.players[i].userinfo, "rate")))); strlcat (line, buf, sizeof (line)); Draw_String (x, y, line); y += 8; } }
int Pickup_PersistantPowerup( gentity_t *ent, gentity_t *other ) { int clientNum; char userinfo[MAX_INFO_STRING]; float handicap; int max; other->client->ps.stats[STAT_PERSISTANT_POWERUP] = ent->item - bg_itemlist; other->client->persistantPowerup = ent; if (ent->item->giTag == PW_GUARD) { clientNum = other->client->ps.clientNum; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); handicap = atof( Info_ValueForKey( userinfo, "handicap" ) ); if( handicap<=0.0f || handicap>100.0f) { handicap = 100.0f; } max = (int)(2 * handicap); other->health = max; other->client->ps.stats[STAT_HEALTH] = max; other->client->ps.stats[STAT_MAX_HEALTH] = max; other->client->ps.stats[STAT_ARMOR] = max; other->client->pers.maxHealth = max; } else if (ent->item->giTag == PW_SCOUT) { clientNum = other->client->ps.clientNum; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); handicap = atof( Info_ValueForKey( userinfo, "handicap" ) ); if( handicap<=0.0f || handicap>100.0f) { handicap = 100.0f; } other->client->pers.maxHealth = handicap; other->client->ps.stats[STAT_ARMOR] = 0; } else if (ent->item->giTag == PW_DOUBLER) { clientNum = other->client->ps.clientNum; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); handicap = atof( Info_ValueForKey( userinfo, "handicap" ) ); if( handicap<=0.0f || handicap>100.0f) { handicap = 100.0f; } other->client->pers.maxHealth = handicap; } else if (ent->item->giTag == PW_ARMORREGEN) { clientNum = other->client->ps.clientNum; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); handicap = atof( Info_ValueForKey( userinfo, "handicap" ) ); if( handicap<=0.0f || handicap>100.0f) { handicap = 100.0f; } other->client->pers.maxHealth = handicap; memset(other->client->ammoTimes, 0, sizeof(other->client->ammoTimes)); } else { clientNum = other->client->ps.clientNum; trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); handicap = atof( Info_ValueForKey( userinfo, "handicap" ) ); if( handicap<=0.0f || handicap>100.0f) { handicap = 100.0f; } other->client->pers.maxHealth = handicap; } return -1; }
/* * 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 }
qboolean G_ParseMapSettings(int handle, config_t *config) { pc_token_t token; char serverinfo[MAX_INFO_STRING]; char *mapname; trap_GetServerinfo(serverinfo, sizeof(serverinfo)); mapname = Info_ValueForKey(serverinfo, "mapname"); if (!trap_PC_ReadToken(handle, &token)) { G_Printf("Malformed map config\n"); } G_Printf("Map settings for: %s\n", token.string); G_Printf("Current map: %s\n", mapname); if (!Q_stricmp(token.string, "default")) { G_Printf("Setting rules for map: %s\n", token.string); return G_ParseSettings(handle, qtrue, config); } else if (!Q_stricmp(token.string, mapname)) { fileHandle_t f; char *code, *signature; qboolean res = qfalse; G_Printf("Setting rules for map: %s\n", token.string); res = G_ParseSettings(handle, qtrue, config); if (res && strlen(config->mapscripthash)) { char sdir[MAX_QPATH]; int flen = 0; trap_Cvar_VariableStringBuffer("g_mapScriptDirectory", sdir, sizeof(sdir)); flen = trap_FS_FOpenFile(va("%s/%s.script", sdir, mapname), &f, FS_READ); if (flen < 0) { // FIXME: handle this properly.. //return G_ConfigError(handle, "Cannot open mapscript file for hash verification: %s/%s.script", sdir, mapname); G_Printf("Cannot open mapscript file for hash verification: %s/%s.script", sdir, mapname); return res; } code = malloc(flen + 1); trap_FS_Read(code, flen, f); *(code + flen) = '\0'; trap_FS_FCloseFile(f); signature = G_SHA1(code); free(code); if (Q_stricmp(config->mapscripthash, signature)) { return G_ConfigError(handle, "Invalid mapscript hash for map: %s hash given in config: \"%s\" scripts actual hash \"%s\"", mapname, config->mapscripthash, signature); } G_Printf("Hash is valid for map: %s\n", mapname); } return res; } else { G_Printf("Ignoring rules for map: %s\n", token.string); return G_ParseSettings(handle, qfalse, config); } }
void CRMLandScape::LoadMiscentDef(const char *td) { char miscentDef[MAX_QPATH]; CGenericParser2 parse; CGPGroup *basegroup, *classes, *items, *model; CGPValue *pair; Com_sprintf(miscentDef, MAX_QPATH, "ext_data/RMG/%s.miscents", Info_ValueForKey(td, "miscentDef")); Com_DPrintf("CG_Terrain: Loading and parsing miscentDef %s.....\n", Info_ValueForKey(td, "miscentDef")); if(!Com_ParseTextFile(miscentDef, parse)) { Com_sprintf(miscentDef, MAX_QPATH, "ext_data/arioche/%s.miscents", Info_ValueForKey(td, "miscentDef")); if(!Com_ParseTextFile(miscentDef, parse)) { Com_Printf("Could not open %s\n", miscentDef); return; } } // The whole file.... basegroup = parse.GetBaseParseGroup(); // The root { } struct classes = basegroup->GetSubGroups(); while(classes) { items = classes->GetSubGroups(); while(items) { if(!Q_stricmp(items->GetName(), "miscent")) { int height, maxheight; // Height must exist - the rest are optional height = atol(items->FindPairValue("height", "0")); maxheight = atol(items->FindPairValue("maxheight", "255")); model = items->GetSubGroups(); while(model) { if(!Q_stricmp(model->GetName(), "model")) { CRandomModel hd; // Set defaults hd.SetModel(""); hd.SetFrequency(1.0f); hd.SetMinScale(1.0f); hd.SetMaxScale(1.0f); pair = model->GetPairs(); while(pair) { if(!Q_stricmp(pair->GetName(), "name")) { hd.SetModel(pair->GetTopValue()); } else if(!Q_stricmp(pair->GetName(), "frequency")) { hd.SetFrequency((float)atof(pair->GetTopValue())); } else if(!Q_stricmp(pair->GetName(), "minscale")) { hd.SetMinScale((float)atof(pair->GetTopValue())); } else if(!Q_stricmp(pair->GetName(), "maxscale")) { hd.SetMaxScale((float)atof(pair->GetTopValue())); } pair = (CGPValue *)pair->GetNext(); } AddModel(height, maxheight, &hd); } model = (CGPGroup *)model->GetNext(); } } items = (CGPGroup *)items->GetNext(); } classes = (CGPGroup *)classes->GetNext(); } Com_ParseTextFileDestroy(parse); }
// Parses fireteam servercommand void CG_ParseFireteams() { int i, j; const char* s; const char* p; int clnts[2]; qboolean onFireteam2; qboolean isLeader2; // qboolean onFireteam = CG_IsOnFireteam( cg.clientNum ) ? qtrue : qfalse; // qboolean isLeader = CG_IsFireTeamLeader( cg.clientNum ) ? qtrue : qfalse; for ( i = 0; i < MAX_CLIENTS; i++ ) { cgs.clientinfo[i].fireteamData = NULL; } for ( i = 0; i < MAX_FIRETEAMS; i++ ) { char hexbuffer[11] = "0x00000000"; p = CG_ConfigString( CS_FIRETEAMS + i ); /* s = Info_ValueForKey(p, "n"); if(!s || !*s) { cg.fireTeams[i].inuse = qfalse; continue; } else { cg.fireTeams[i].inuse = qtrue; }*/ // Q_strncpyz(cg.fireTeams[i].name, s, 32); // CG_Printf("Fireteam: %s\n", cg.fireTeams[i].name); j = atoi( Info_ValueForKey( p, "id" ) ); if ( j == -1 ) { cg.fireTeams[i].inuse = qfalse; continue; } else { cg.fireTeams[i].inuse = qtrue; cg.fireTeams[i].ident = j; } s = Info_ValueForKey( p, "l" ); cg.fireTeams[i].leader = atoi( s ); s = Info_ValueForKey( p, "c" ); Q_strncpyz( hexbuffer + 2, s, 9 ); sscanf( hexbuffer, "%x", &clnts[1] ); Q_strncpyz( hexbuffer + 2, s + 8, 9 ); sscanf( hexbuffer, "%x", &clnts[0] ); for ( j = 0; j < MAX_CLIENTS; j++ ) { if ( COM_BitCheck( clnts, j ) ) { cg.fireTeams[i].joinOrder[j] = qtrue; cgs.clientinfo[j].fireteamData = &cg.fireTeams[i]; // CG_Printf("%s\n", cgs.clientinfo[j].name); } else { cg.fireTeams[i].joinOrder[j] = qfalse; } } } CG_SortClientFireteam(); onFireteam2 = CG_IsOnFireteam( cg.clientNum ) ? qtrue : qfalse; isLeader2 = CG_IsFireTeamLeader( cg.clientNum ) ? qtrue : qfalse; }
// Called on a first-time connect void G_InitClientSessionData( gclient_t *client, char *userinfo, qboolean isBot ) { clientSession_t *sess = &client->sess; const char *value; client->sess.siegeDesiredTeam = TEAM_FREE; // initial team determination if ( level.gametype >= GT_TEAM ) { if ( g_teamAutoJoin.integer && !(g_entities[client-level.clients].r.svFlags & SVF_BOT) ) { sess->sessionTeam = PickTeam( -1 ); client->ps.fd.forceDoInit = 1; //every time we change teams make sure our force powers are set right } else { // always spawn as spectator in team games if (!isBot) { sess->sessionTeam = TEAM_SPECTATOR; } else { //Bots choose their team on creation value = Info_ValueForKey( userinfo, "team" ); if (value[0] == 'r' || value[0] == 'R') { sess->sessionTeam = TEAM_RED; } else if (value[0] == 'b' || value[0] == 'B') { sess->sessionTeam = TEAM_BLUE; } else { sess->sessionTeam = PickTeam( -1 ); } client->ps.fd.forceDoInit = 1; //every time we change teams make sure our force powers are set right } } } else { value = Info_ValueForKey( userinfo, "team" ); if ( value[0] == 's' ) { // a willing spectator, not a waiting-in-line sess->sessionTeam = TEAM_SPECTATOR; } else { switch ( level.gametype ) { default: case GT_FFA: case GT_SINGLE_PLAYER: if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer ) { sess->sessionTeam = TEAM_SPECTATOR; } else { sess->sessionTeam = TEAM_FREE; } break; case GT_DUEL: // if the game is full, go into a waiting mode if ( level.numNonSpectatorClients >= 2 ) { sess->sessionTeam = TEAM_SPECTATOR; } else { sess->sessionTeam = TEAM_FREE; } break; case GT_POWERDUEL: //sess->duelTeam = DUELTEAM_LONE; //default { int loners = 0; int doubles = 0; G_PowerDuelCount(&loners, &doubles, qtrue); if (!doubles || loners > (doubles/2)) { sess->duelTeam = DUELTEAM_DOUBLE; } else { sess->duelTeam = DUELTEAM_LONE; } } sess->sessionTeam = TEAM_SPECTATOR; break; } } } sess->spectatorState = SPECTATOR_FREE; AddTournamentQueue(client); sess->siegeClass[0] = 0; G_WriteClientSessionData( client ); }
/* ================ G_InitSessionData Called on a first-time connect ================ */ void G_InitSessionData( gclient_t *client, char *userinfo, qboolean isBot ) { clientSession_t *sess; const char *value; sess = &client->sess; // initial team determination if ( g_gametype.integer >= GT_TEAM ) { if ( g_teamAutoJoin.integer ) { sess->sessionTeam = PickTeam( -1 ); BroadcastTeamChange( client, -1 ); } else { // always spawn as spectator in team games if (!isBot) { sess->sessionTeam = TEAM_SPECTATOR; } else { //Bots choose their team on creation value = Info_ValueForKey( userinfo, "team" ); if (value[0] == 'r' || value[0] == 'R') { sess->sessionTeam = TEAM_RED; } else if (value[0] == 'b' || value[0] == 'B') { sess->sessionTeam = TEAM_BLUE; } else { sess->sessionTeam = PickTeam( -1 ); } BroadcastTeamChange( client, -1 ); } } } else { value = Info_ValueForKey( userinfo, "team" ); if ( value[0] == 's' ) { // a willing spectator, not a waiting-in-line sess->sessionTeam = TEAM_SPECTATOR; } else { switch ( g_gametype.integer ) { default: case GT_FFA: case GT_HOLOCRON: case GT_JEDIMASTER: case GT_SINGLE_PLAYER: if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer ) { sess->sessionTeam = TEAM_SPECTATOR; } else { sess->sessionTeam = TEAM_FREE; } break; case GT_TOURNAMENT: // if the game is full, go into a waiting mode if ( level.numNonSpectatorClients >= 2 ) { sess->sessionTeam = TEAM_SPECTATOR; } else { sess->sessionTeam = TEAM_FREE; } break; } } } sess->spectatorState = SPECTATOR_FREE; sess->spectatorTime = level.time; G_WriteClientSessionData( client ); }
/* ======================= UI_CalcPostGameStats ======================= */ static void UI_CalcPostGameStats(void) { char map[MAX_QPATH]; char fileName[MAX_QPATH]; char info[MAX_INFO_STRING]; fileHandle_t f; int size, game, time, adjustedTime; postGameInfo_t oldInfo; postGameInfo_t newInfo; qboolean newHigh = qfalse; trap_GetConfigString(CS_SERVERINFO, info, sizeof(info)); Q_strncpyz(map, Info_ValueForKey(info, "mapname"), sizeof(map)); game = atoi(Info_ValueForKey(info, "g_gametype")); // compose file name Com_sprintf(fileName, MAX_QPATH, "games/%s_%i.game", map, game); // see if we have one already memset(&oldInfo, 0, sizeof(postGameInfo_t)); if(trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) { // if so load it size = 0; trap_FS_Read(&size, sizeof(int), f); if(size == sizeof(postGameInfo_t)) { trap_FS_Read(&oldInfo, sizeof(postGameInfo_t), f); } trap_FS_FCloseFile(f); } newInfo.accuracy = atoi(UI_Argv(3)); newInfo.impressives = atoi(UI_Argv(4)); newInfo.excellents = atoi(UI_Argv(5)); newInfo.defends = atoi(UI_Argv(6)); newInfo.assists = atoi(UI_Argv(7)); newInfo.gauntlets = atoi(UI_Argv(8)); newInfo.baseScore = atoi(UI_Argv(9)); newInfo.perfects = atoi(UI_Argv(10)); newInfo.redScore = atoi(UI_Argv(11)); newInfo.blueScore = atoi(UI_Argv(12)); time = atoi(UI_Argv(13)); newInfo.captures = atoi(UI_Argv(14)); newInfo.time = (time - trap_Cvar_VariableValue("ui_matchStartTime")) / 1000; adjustedTime = uiInfo.mapList[ui_currentMap.integer].timeToBeat[game]; if(newInfo.time < adjustedTime) { newInfo.timeBonus = (adjustedTime - newInfo.time) * 10; } else { newInfo.timeBonus = 0; } if(newInfo.redScore > newInfo.blueScore && newInfo.blueScore <= 0) { newInfo.shutoutBonus = 100; } else { newInfo.shutoutBonus = 0; } newInfo.skillBonus = trap_Cvar_VariableValue("g_spSkill"); if(newInfo.skillBonus <= 0) { newInfo.skillBonus = 1; } newInfo.score = newInfo.baseScore + newInfo.shutoutBonus + newInfo.timeBonus; newInfo.score *= newInfo.skillBonus; // see if the score is higher for this one newHigh = (newInfo.redScore > newInfo.blueScore && newInfo.score > oldInfo.score); if(newHigh) { // if so write out the new one uiInfo.newHighScoreTime = uiInfo.uiDC.realTime + 20000; if(trap_FS_FOpenFile(fileName, &f, FS_WRITE) >= 0) { size = sizeof(postGameInfo_t); trap_FS_Write(&size, sizeof(int), f); trap_FS_Write(&newInfo, sizeof(postGameInfo_t), f); trap_FS_FCloseFile(f); } } if(newInfo.time < oldInfo.time) { uiInfo.newBestTime = uiInfo.uiDC.realTime + 20000; } // put back all the ui overrides trap_Cvar_Set("capturelimit", UI_Cvar_VariableString("ui_saveCaptureLimit")); trap_Cvar_Set("fraglimit", UI_Cvar_VariableString("ui_saveFragLimit")); trap_Cvar_Set("cg_drawTimer", UI_Cvar_VariableString("ui_drawTimer")); trap_Cvar_Set("g_doWarmup", UI_Cvar_VariableString("ui_doWarmup")); trap_Cvar_Set("g_Warmup", UI_Cvar_VariableString("ui_Warmup")); trap_Cvar_Set("sv_pure", UI_Cvar_VariableString("ui_pure")); trap_Cvar_Set("g_friendlyFire", UI_Cvar_VariableString("ui_friendlyFire")); UI_SetBestScores(&newInfo, qtrue); UI_ShowPostGame(newHigh); }
/* =============== UI_LoadArenas =============== */ static void UI_LoadArenas( void ) { int numdirs; vmCvar_t arenasFile; char filename[128]; char dirlist[1024]; char* dirptr; int i, n; int dirlen; char *type; char *tag; int singlePlayerNum, specialNum, otherNum; ui_numArenas = 0; UI_trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM ); if( *arenasFile.string ) { UI_LoadArenasFromFile(arenasFile.string); } else { UI_LoadArenasFromFile("scripts/arenas.txt"); } // get all arenas from .arena files numdirs = UI_trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = strlen(dirptr); strcpy(filename, "scripts/"); strcat(filename, dirptr); UI_LoadArenasFromFile(filename); } UI_trap_Print( va( "%i arenas parsed\n", ui_numArenas ) ); if (outOfMemory) UI_trap_Print(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all arenas\n"); // set initial numbers for( n = 0; n < ui_numArenas; n++ ) { Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", n ) ); } // go through and count single players levels ui_numSinglePlayerArenas = 0; ui_numSpecialSinglePlayerArenas = 0; for( n = 0; n < ui_numArenas; n++ ) { // determine type type = Info_ValueForKey( ui_arenaInfos[n], "type" ); // if no type specified, it will be treated as "ffa" if( !*type ) { continue; } if( strstr( type, "single" ) ) { // check for special single player arenas (training, final) tag = Info_ValueForKey( ui_arenaInfos[n], "special" ); if( *tag ) { ui_numSpecialSinglePlayerArenas++; continue; } ui_numSinglePlayerArenas++; } } n = ui_numSinglePlayerArenas % ARENAS_PER_TIER; if( n != 0 ) { ui_numSinglePlayerArenas -= n; UI_trap_Print( va( "%i arenas ignored to make count divisible by %i\n", n, ARENAS_PER_TIER ) ); } // go through once more and assign number to the levels singlePlayerNum = 0; specialNum = singlePlayerNum + ui_numSinglePlayerArenas; otherNum = specialNum + ui_numSpecialSinglePlayerArenas; for( n = 0; n < ui_numArenas; n++ ) { // determine type type = Info_ValueForKey( ui_arenaInfos[n], "type" ); // if no type specified, it will be treated as "ffa" if( *type ) { if( strstr( type, "single" ) ) { // check for special single player arenas (training, final) tag = Info_ValueForKey( ui_arenaInfos[n], "special" ); if( *tag ) { Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", specialNum++ ) ); continue; } Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", singlePlayerNum++ ) ); continue; } } Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", otherNum++ ) ); } }
/* * only called when pers.spectator changes * note that resp.spectator should be the opposite of pers.spectator here */ void spectator_respawn (edict_t *ent) { int i, numspec; // if the user wants to become a spectator, make sure he doesn't // exceed max_spectators if (ent->client->pers.spectator) { char *value = Info_ValueForKey (ent->client->pers.userinfo, "spectator"); if (*spectator_password->string && strcmp(spectator_password->string, "none") && strcmp(spectator_password->string, value)) { gi.cprintf(ent, PRINT_HIGH, "Spectator password incorrect.\n"); ent->client->pers.spectator = false; gi.WriteByte (svc_stufftext); gi.WriteString ("spectator 0\n"); gi.unicast(ent, true); return; } // count spectators for (i = 1, numspec = 0; i <= maxclients->value; i++) if (g_edicts[i].inuse && g_edicts[i].client->pers.spectator) numspec++; if (numspec >= maxspectators->value) { gi.cprintf(ent, PRINT_HIGH, "Server spectator limit is full."); ent->client->pers.spectator = false; // reset his spectator var gi.WriteByte (svc_stufftext); gi.WriteString ("spectator 0\n"); gi.unicast(ent, true); return; } } else { // he was a spectator and wants to join the game // he must have the right password char *value = Info_ValueForKey (ent->client->pers.userinfo, "password"); if (*password->string && strcmp(password->string, "none") && strcmp(password->string, value)) { gi.cprintf(ent, PRINT_HIGH, "Password incorrect.\n"); ent->client->pers.spectator = true; gi.WriteByte (svc_stufftext); gi.WriteString ("spectator 1\n"); gi.unicast(ent, true); return; } } // clear client on respawn ent->client->resp.score = ent->client->pers.score = 0; ent->svflags &= ~SVF_NOCLIENT; PutClientInServer (ent); // add a teleportation effect if (!ent->client->pers.spectator) { // send effect gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGIN); gi.multicast (ent->s.origin, MULTICAST_PVS); // hold in place briefly ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; ent->client->ps.pmove.pm_time = 14; } ent->client->respawn_time = level.time; if (ent->client->pers.spectator) gi.bprintf (PRINT_HIGH, "%s has moved to the sidelines\n", ent->client->pers.netname); else gi.bprintf (PRINT_HIGH, "%s joined the game\n", ent->client->pers.netname); }
/* * 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; char message[MAX_STRING_CHARS]; 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 ); 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; }
/* =========== PutClientInServer Called when a player connects to a server or respawns in a deathmatch. ============ */ void PutClientInServer (edict_t *ent) { vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; client_persistant_t saved; client_respawn_t resp; // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client SelectSpawnPoint (ent, spawn_origin, spawn_angles); index = ent-g_edicts-1; client = ent->client; // deathmatch wipes most client data every spawn if (deathmatch->value) { char userinfo[MAX_INFO_STRING]; resp = client->resp; memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); InitClientPersistant (client); ClientUserinfoChanged (ent, userinfo); } else if (coop->value) { // int n; char userinfo[MAX_INFO_STRING]; resp = client->resp; memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); // this is kind of ugly, but it's how we want to handle keys in coop // for (n = 0; n < game.num_items; n++) // { // if (itemlist[n].flags & IT_KEY) // resp.coop_respawn.inventory[n] = client->pers.inventory[n]; // } resp.coop_respawn.game_helpchanged = client->pers.game_helpchanged; resp.coop_respawn.helpchanged = client->pers.helpchanged; client->pers = resp.coop_respawn; ClientUserinfoChanged (ent, userinfo); if (resp.score > client->pers.score) client->pers.score = resp.score; } else { memset (&resp, 0, sizeof(resp)); } // clear everything but the persistant data saved = client->pers; memset (client, 0, sizeof(*client)); client->pers = saved; if (client->pers.health <= 0) InitClientPersistant(client); client->resp = resp; // copy some data from the client to the entity FetchClientEntData (ent); // clear entity values ent->groundentity = NULL; ent->client = &game.clients[index]; ent->takedamage = DAMAGE_AIM; ent->movetype = MOVETYPE_WALK; ent->viewheight = 22; ent->inuse = true; ent->classname = "player"; ent->mass = 200; ent->solid = SOLID_BBOX; ent->deadflag = DEAD_NO; ent->air_finished = level.time + 12; ent->clipmask = MASK_PLAYERSOLID; ent->model = "players/male/tris.md2"; ent->pain = player_pain; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags &= ~FL_NO_KNOCKBACK; ent->svflags &= ~SVF_DEADMONSTER; VectorCopy (mins, ent->mins); VectorCopy (maxs, ent->maxs); VectorClear (ent->velocity); // clear playerstate values memset (&ent->client->ps, 0, sizeof(client->ps)); client->ps.pmove.origin[0] = spawn_origin[0]*8; client->ps.pmove.origin[1] = spawn_origin[1]*8; client->ps.pmove.origin[2] = spawn_origin[2]*8; if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) { client->ps.fov = 90; } else { client->ps.fov = atoi(Info_ValueForKey(client->pers.userinfo, "fov")); if (client->ps.fov < 1) client->ps.fov = 90; else if (client->ps.fov > 160) client->ps.fov = 160; } client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model); // clear entity state values ent->s.effects = 0; ent->s.modelindex = 255; // will use the skin specified model ent->s.modelindex2 = 255; // custom gun model // sknum is player num and weapon number // weapon number will be added in changeweapon ent->s.skinnum = ent - g_edicts - 1; ent->s.frame = 0; VectorCopy (spawn_origin, ent->s.origin); ent->s.origin[2] += 1; // make sure off ground VectorCopy (ent->s.origin, ent->s.old_origin); // set the delta angle for (i=0 ; i<3 ; i++) { client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]); } ent->s.angles[PITCH] = 0; ent->s.angles[YAW] = spawn_angles[YAW]; ent->s.angles[ROLL] = 0; VectorCopy (ent->s.angles, client->ps.viewangles); VectorCopy (ent->s.angles, client->v_angle); // spawn a spectator if (client->pers.spectator) { client->chase_target = NULL; client->resp.spectator = true; ent->movetype = MOVETYPE_NOCLIP; ent->solid = SOLID_NOT; ent->svflags |= SVF_NOCLIENT; ent->client->ps.gunindex = 0; gi.linkentity (ent); return; } else client->resp.spectator = false; if (!KillBox (ent)) { // could't spawn in? } gi.linkentity (ent); // force the current weapon up client->newweapon = client->pers.weapon; ChangeWeapon (ent); }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; char *key; char *s; char *botname; char *model; char *headmodel; char userinfo[MAX_INFO_STRING]; // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); trap_BotFreeClient( clientNum ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%.2f", skill) ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "visor/default"; } Info_SetValueForKey( userinfo, key, model ); key = "team_model"; Info_SetValueForKey( userinfo, key, model ); key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "5"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Print( S_COLOR_RED "Error: bot has no aifile specified\n" ); trap_BotFreeClient( clientNum ); return; } Info_SetValueForKey( userinfo, "characterfile", s ); if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey( userinfo, "team", team ); // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if( delay == 0 ) { ClientBegin( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* =========== 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. ============ */ qboolean ClientConnect (edict_t *ent, char *userinfo) { char *value; // check to see if they are on the banned IP list value = Info_ValueForKey (userinfo, "ip"); if (SV_FilterPacket(value)) { Info_SetValueForKey(userinfo, "rejmsg", "Banned."); return false; } // check for a spectator value = Info_ValueForKey (userinfo, "spectator"); if (deathmatch->value && *value && strcmp(value, "0")) { int i, numspec; if (*spectator_password->string && strcmp(spectator_password->string, "none") && strcmp(spectator_password->string, value)) { Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect."); return false; } // count spectators for (i = numspec = 0; i < maxclients->value; i++) if (g_edicts[i+1].inuse && g_edicts[i+1].client->pers.spectator) numspec++; if (numspec >= maxspectators->value) { Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full."); return false; } } else { // check for a password value = Info_ValueForKey (userinfo, "password"); if (*password->string && strcmp(password->string, "none") && strcmp(password->string, value)) { Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect."); return false; } } // they can connect ent->client = game.clients + (ent - g_edicts - 1); // if there is already a body waiting for us (a loadgame), just // take it, otherwise spawn one from scratch if (ent->inuse == false) { // clear the respawning variables InitClientResp (ent->client); if (!game.autosaved || !ent->client->pers.weapon) InitClientPersistant (ent->client); } ClientUserinfoChanged (ent, userinfo); if (game.maxclients > 1) gi.dprintf ("%s connected\n", ent->client->pers.netname); ent->svflags = 0; // make sure we start with known default ent->client->pers.connected = true; return true; }
void UI_LoadArenas( void ) { int numdirs, i, n, dirlen; char filename[MAX_QPATH], *dirptr; static char dirlist[MAPSBUFSIZE]; const char *type; dirlist[0] = '\0'; ui_numArenas = 0; uiInfo.mapCount = 0; // get all arenas from .arena files numdirs = trap->FS_GetFileList( "scripts", ".arena", dirlist, ARRAY_LEN( dirlist ) ); dirptr = dirlist; for ( i = 0; i < numdirs; i++, dirptr += dirlen + 1 ) { dirlen = strlen( dirptr ); strcpy( filename, "scripts/" ); strcat( filename, dirptr ); UI_LoadArenasFromFile( filename ); } // trap->Print( va( "%i arenas parsed\n", ui_numArenas ) ); if ( UI_OutOfMemory() ) { trap->Print( S_COLOR_YELLOW"WARNING: not anough memory in pool to load all arenas\n" ); } for ( n = 0; n < ui_numArenas; n++ ) { // determine type uiInfo.mapList[uiInfo.mapCount].cinematic = -1; uiInfo.mapList[uiInfo.mapCount].mapLoadName = String_Alloc( Info_ValueForKey( ui_arenaInfos[n], "map" ) ); uiInfo.mapList[uiInfo.mapCount].mapName = String_Alloc( Info_ValueForKey( ui_arenaInfos[n], "longname" ) ); uiInfo.mapList[uiInfo.mapCount].levelShot = -1; uiInfo.mapList[uiInfo.mapCount].imageName = String_Alloc( va( "levelshots/%s", uiInfo.mapList[uiInfo.mapCount].mapLoadName ) ); uiInfo.mapList[uiInfo.mapCount].typeBits = 0; type = Info_ValueForKey( ui_arenaInfos[n], "type" ); // if no type specified, it will be treated as "ffa" if ( *type ) { if ( strstr( type, "ffa" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_FFA); //Raz: JK2 gametypes uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_JEDIMASTER); } if ( strstr( type, "coop" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_SINGLE_PLAYER); } if ( strstr( type, "holocron" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_HOLOCRON); } if ( strstr( type, "jedimaster" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_JEDIMASTER); } if ( strstr( type, "duel" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_DUEL); uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_POWERDUEL); } if ( strstr( type, "powerduel" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_DUEL); uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_POWERDUEL); } if ( strstr( type, "siege" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_SIEGE); } if ( strstr( type, "ctf" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_CTF); //Raz: JK2 gametypes uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_CTY); } if ( strstr( type, "cty" ) ) { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_CTY); } } else { uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_FFA); //Raz: JK2 gametypes uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_JEDIMASTER); } uiInfo.mapCount++; if ( uiInfo.mapCount >= MAX_MAPS ) { break; } } }
/* ============= CG_Obituary ============= */ static void CG_Obituary( entityState_t *ent ) { int mod; int target, attacker; char *message; char *message2; const char *targetInfo; const char *attackerInfo; char targetName[32]; char attackerName[32]; gender_t gender; clientInfo_t *ci; target = ent->otherEntityNum; attacker = ent->otherEntityNum2; mod = ent->eventParm; if ( target < 0 || target >= MAX_CLIENTS ) { CG_Error( "CG_Obituary: target out of range" ); } ci = &cgs.clientinfo[target]; if ( attacker < 0 || attacker >= MAX_CLIENTS ) { attacker = ENTITYNUM_WORLD; attackerInfo = NULL; } else { attackerInfo = CG_ConfigString( CS_PLAYERS + attacker ); } targetInfo = CG_ConfigString( CS_PLAYERS + target ); if ( !targetInfo ) { return; } Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof(targetName) - 2); strcat( targetName, S_COLOR_WHITE ); message2 = ""; // check for single client messages switch( mod ) { case MOD_SUICIDE: message = "suicides"; break; case MOD_FALLING: message = "cratered"; break; case MOD_CRUSH: message = "was squished"; break; case MOD_WATER: message = "sank like a rock"; break; // [ERGO MOD START] case MOD_HIGH_PULSE: message = "exercised too hard"; break; // [ERGO MOD END] case MOD_SLIME: message = "melted"; break; case MOD_LAVA: message = "does a back flip into the lava"; break; case MOD_TARGET_LASER: message = "saw the light"; break; case MOD_TRIGGER_HURT: message = "was in the wrong place"; break; default: message = NULL; break; } if (attacker == target) { gender = ci->gender; switch (mod) { #ifdef MISSIONPACK case MOD_KAMIKAZE: message = "goes out with a bang"; break; #endif case MOD_GRENADE_SPLASH: if ( gender == GENDER_FEMALE ) message = "tripped on her own grenade"; else if ( gender == GENDER_NEUTER ) message = "tripped on its own grenade"; else message = "tripped on his own grenade"; break; case MOD_ROCKET_SPLASH: if ( gender == GENDER_FEMALE ) message = "blew herself up"; else if ( gender == GENDER_NEUTER ) message = "blew itself up"; else message = "blew himself up"; break; case MOD_PLASMA_SPLASH: if ( gender == GENDER_FEMALE ) message = "melted herself"; else if ( gender == GENDER_NEUTER ) message = "melted itself"; else message = "melted himself"; break; case MOD_BFG_SPLASH: message = "should have used a smaller gun"; break; #ifdef MISSIONPACK case MOD_PROXIMITY_MINE: if( gender == GENDER_FEMALE ) { message = "found her prox mine"; } else if ( gender == GENDER_NEUTER ) { message = "found it's prox mine"; } else { message = "found his prox mine"; } break; #endif default: if ( gender == GENDER_FEMALE ) message = "killed herself"; else if ( gender == GENDER_NEUTER ) message = "killed itself"; else message = "killed himself"; break; } } if (message) { CG_Printf( "%s %s.\n", targetName, message); return; } // check for kill messages from the current clientNum if ( attacker == cg.snap->ps.clientNum ) { char *s; if ( cgs.gametype < GT_TEAM ) { s = va("You fragged %s\n%s place with %i", targetName, CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ), cg.snap->ps.persistant[PERS_SCORE] ); } else { s = va("You fragged %s", targetName ); } #ifdef MISSIONPACK if (!(cg_singlePlayerActive.integer && cg_cameraOrbit.integer)) { CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); } #else CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); #endif // print the text message as well } // check for double client messages if ( !attackerInfo ) { attacker = ENTITYNUM_WORLD; strcpy( attackerName, "noname" ); } else { Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof(attackerName) - 2); strcat( attackerName, S_COLOR_WHITE ); // check for kill messages about the current clientNum if ( target == cg.snap->ps.clientNum ) { Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) ); } } if ( attacker != ENTITYNUM_WORLD ) { switch (mod) { case MOD_GRAPPLE: message = "was caught by"; break; case MOD_GAUNTLET: message = "was pummeled by"; break; case MOD_MACHINEGUN: message = "was machinegunned by"; break; case MOD_SHOTGUN: message = "was gunned down by"; break; case MOD_GRENADE: message = "ate"; message2 = "'s grenade"; break; case MOD_GRENADE_SPLASH: message = "was shredded by"; message2 = "'s shrapnel"; break; case MOD_ROCKET: message = "ate"; message2 = "'s rocket"; break; case MOD_ROCKET_SPLASH: message = "almost dodged"; message2 = "'s rocket"; break; case MOD_PLASMA: message = "was melted by"; message2 = "'s plasmagun"; break; case MOD_PLASMA_SPLASH: message = "was melted by"; message2 = "'s plasmagun"; break; case MOD_RAILGUN: message = "was railed by"; break; case MOD_LIGHTNING: message = "was electrocuted by"; break; case MOD_BFG: case MOD_BFG_SPLASH: message = "was blasted by"; message2 = "'s BFG"; break; #ifdef MISSIONPACK case MOD_NAIL: message = "was nailed by"; break; case MOD_CHAINGUN: message = "got lead poisoning from"; message2 = "'s Chaingun"; break; case MOD_PROXIMITY_MINE: message = "was too close to"; message2 = "'s Prox Mine"; break; case MOD_KAMIKAZE: message = "falls to"; message2 = "'s Kamikaze blast"; break; case MOD_JUICED: message = "was juiced by"; break; #endif case MOD_TELEFRAG: message = "tried to invade"; message2 = "'s personal space"; break; default: message = "was killed by"; break; } if (message) { CG_Printf( "%s %s %s%s\n", targetName, message, attackerName, message2); return; } } // we don't know what it was CG_Printf( "%s died.\n", targetName ); }
const char *UI_GetBotNameByNumber( int num ) { const char *info = UI_GetBotInfoByNumber( num ); return info ? Info_ValueForKey( info, "name" ) : "Kyle"; }
/* * 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_SUPPORT int incoming = 0; #endif char userinfo[MAX_INFO_STRING], *name; client_t *cl, *newcl; int i, version, game_port, challenge; qboolean 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 ? 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( "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_SUPPORT 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_SUPPORT if( socket->type == SOCKET_TCP ) { tvs.incoming[incoming].active = qfalse; tvs.incoming[incoming].socket.open = qfalse; } #endif }
/* ================= SV_UserinfoChanged Pull specific info from a newly changed userinfo string into a more C friendly form. ================= */ void SV_UserinfoChanged( client_t *cl ) { const char *val; int i; // name for C code Q_strncpyz( cl->name, Info_ValueForKey( cl->userinfo, "name" ), sizeof( cl->name ) ); // rate command // if the client is on the same subnet as the server and we aren't running an // Internet server, assume that they don't need a rate choke if ( Sys_IsLANAddress( cl->netchan.remoteAddress ) && isLanOnly.Get() && sv_lanForceRate->integer == 1 ) { cl->rate = 99999; // lans should not rate limit } else { val = Info_ValueForKey( cl->userinfo, "rate" ); if ( strlen( val ) ) { i = atoi( val ); cl->rate = i; if ( cl->rate < 1000 ) { cl->rate = 1000; } else if ( cl->rate > 90000 ) { cl->rate = 90000; } } else { cl->rate = 5000; } } // snaps command val = Info_ValueForKey( cl->userinfo, "snaps" ); if ( strlen( val ) ) { i = atoi( val ); if ( i < 1 ) { i = 1; } else if ( i > sv_fps->integer ) { i = sv_fps->integer; } cl->snapshotMsec = 1000 / i; } else { cl->snapshotMsec = 50; } // TTimo // maintain the IP information // this is set in SV_DirectConnect (directly on the server, not transmitted), may be lost when client updates its userinfo // the banning code relies on this being consistently present // zinx - modified to always keep this consistent, instead of only // when "ip" is 0-length, so users can't supply their own IP address //Log::Debug("Maintain IP address in userinfo for '%s'", cl->name); if ( !NET_IsLocalAddress( cl->netchan.remoteAddress ) ) { Info_SetValueForKey( cl->userinfo, "ip", NET_AdrToString( cl->netchan.remoteAddress ), false ); #ifdef HAVE_GEOIP Info_SetValueForKey( cl->userinfo, "geoip", NET_GeoIP_Country( &cl->netchan.remoteAddress ), false ); #endif } else { // force the "ip" info key to "localhost" for local clients Info_SetValueForKey( cl->userinfo, "ip", "localhost", false ); #ifdef HAVE_GEOIP Info_SetValueForKey( cl->userinfo, "geoip", nullptr, false ); #endif } }
void CL_SystemInfoChanged(void) { char *systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[CS_SYSTEMINFO]; const char *s, *t; char key[BIG_INFO_KEY]; char value[BIG_INFO_VALUE]; qboolean gameSet; // NOTE: when the serverId changes, any further messages we send to the server will use this new serverId // in some cases, outdated cp commands might get sent with this news serverId cl.serverId = atoi(Info_ValueForKey(systemInfo, "sv_serverid")); memset(&entLastVisible, 0, sizeof(entLastVisible)); // don't set any vars when playing a demo if (clc.demoplaying) { return; } s = Info_ValueForKey(systemInfo, "sv_cheats"); cl_connectedToCheatServer = atoi(s); //bani if (!cl_connectedToCheatServer) { Cvar_SetCheatState(); } // check pure server string s = Info_ValueForKey(systemInfo, "sv_paks"); t = Info_ValueForKey(systemInfo, "sv_pakNames"); FS_PureServerSetLoadedPaks(s, t); s = Info_ValueForKey(systemInfo, "sv_referencedPaks"); t = Info_ValueForKey(systemInfo, "sv_referencedPakNames"); FS_PureServerSetReferencedPaks(s, t); gameSet = qfalse; // scan through all the variables in the systeminfo and locally set cvars to match s = systemInfo; while (s) { int cvar_flags; Info_NextPair(&s, key, value); if (!key[0]) { break; } // ehw! if (!Q_stricmp(key, "fs_game")) { if (FS_CheckDirTraversal(value)) { Com_Printf(S_COLOR_YELLOW "WARNING: Server sent invalid fs_game value %s\n", value); continue; } gameSet = qtrue; } if ((cvar_flags = Cvar_Flags(key)) == CVAR_NONEXISTENT) { Cvar_Get(key, value, CVAR_SERVER_CREATED | CVAR_ROM); } else { // If this cvar may not be modified by a server discard the value. if (!(cvar_flags & (CVAR_SYSTEMINFO | CVAR_SERVER_CREATED | CVAR_USER_CREATED))) { if (Q_stricmp(key, "g_synchronousClients") && Q_stricmp(key, "pmove_fixed") && Q_stricmp(key, "pmove_msec")) { Com_DPrintf(S_COLOR_YELLOW "WARNING: server is not allowed to set %s=%s\n", key, value); continue; } } Cvar_SetSafe(key, value); } } // if game folder should not be set and it is set at the client side if (!gameSet && *Cvar_VariableString("fs_game")) { Cvar_Set("fs_game", ""); } // big hack to clear the image cache on a pure change //cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" ); if (Cvar_VariableValue("sv_pure")) { if (!cl_connectedToPureServer && cls.state <= CA_CONNECTED) { CL_PurgeCache(); } cl_connectedToPureServer = qtrue; } else { if (cl_connectedToPureServer && cls.state <= CA_CONNECTED) { CL_PurgeCache(); } cl_connectedToPureServer = qfalse; } }
/** * @brief kick a user off of the server */ static void Svcmd_Kick_f(void) { gclient_t *cl; int timeout; char sTimeout[MAX_TOKEN_CHARS]; char name[MAX_TOKEN_CHARS]; // make sure server is running if (!G_Is_SV_Running()) { G_Printf("Server is not running.\n"); return; } if (trap_Argc() < 2 || trap_Argc() > 3) { G_Printf("Usage: kick <player name> [timeout]\n"); return; } if (trap_Argc() == 3) { trap_Argv(2, sTimeout, sizeof(sTimeout)); timeout = atoi(sTimeout); } else { timeout = 300; } trap_Argv(1, name, sizeof(name)); cl = G_GetPlayerByName(name); //ClientForString( name ); if (!cl) { if (!Q_stricmp(name, "all")) { int i; for (i = 0, cl = level.clients; i < level.numConnectedClients; i++, cl++) { // dont kick localclients ... if (cl->pers.localClient) { continue; } if (timeout != -1) { char *ip; char userinfo[MAX_INFO_STRING]; trap_GetUserinfo(cl->ps.clientNum, userinfo, sizeof(userinfo)); ip = Info_ValueForKey(userinfo, "ip"); // use engine banning system, mods may choose to use their own banlist if (USE_ENGINE_BANLIST) { // kick but dont ban bots, they arent that lame if ((g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT)) { timeout = 0; } trap_DropClient(cl->ps.clientNum, "player kicked", timeout); } else { trap_DropClient(cl->ps.clientNum, "player kicked", 0); // kick but dont ban bots, they arent that lame if (!(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT)) { AddIPBan(ip); } } } else { trap_DropClient(cl->ps.clientNum, "player kicked", 0); } } } return; } else { // dont kick localclients ... if (cl->pers.localClient) { G_Printf("Cannot kick host player\n"); return; } if (timeout != -1) { char *ip; char userinfo[MAX_INFO_STRING]; trap_GetUserinfo(cl->ps.clientNum, userinfo, sizeof(userinfo)); ip = Info_ValueForKey(userinfo, "ip"); // use engine banning system, mods may choose to use their own banlist if (USE_ENGINE_BANLIST) { // kick but dont ban bots, they arent that lame if ((g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT)) { timeout = 0; } trap_DropClient(cl->ps.clientNum, "player kicked", timeout); } else { trap_DropClient(cl->ps.clientNum, "player kicked", 0); // kick but dont ban bots, they arent that lame if (!(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT)) { AddIPBan(ip); } } } else { trap_DropClient(cl->ps.clientNum, "player kicked", 0); } } }
/* =========== ClientSpawn Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void ClientSpawn(gentity_t * ent) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; clientPersistant_t saved; clientSession_t savedSess; int persistant[MAX_PERSISTANT]; gentity_t *spawnPoint; int flags; int savedPing; // char *savedAreaBits; int accuracy_hits, accuracy_shots; int eventSequence; char userinfo[MAX_INFO_STRING]; index = ent - g_entities; client = ent->client; // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if(client->sess.sessionTeam == TEAM_SPECTATOR) { spawnPoint = SelectSpectatorSpawnPoint(spawn_origin, spawn_angles); } else if(g_gametype.integer >= GT_CTF) { // all base oriented team games use the CTF spawn points spawnPoint = SelectCTFSpawnPoint(client->sess.sessionTeam, client->pers.teamState.state, spawn_origin, spawn_angles); } else { do { // the first spawn should be at a good looking spot if(!client->pers.initialSpawn && client->pers.localClient) { client->pers.initialSpawn = qtrue; spawnPoint = SelectInitialSpawnPoint(spawn_origin, spawn_angles); } else { // don't spawn near existing origin if possible spawnPoint = SelectSpawnPoint(client->ps.origin, spawn_origin, spawn_angles); } // Tim needs to prevent bots from spawning at the initial point // on q3dm0... if((spawnPoint->flags & FL_NO_BOTS) && (ent->r.svFlags & SVF_BOT)) { continue; // try again } // just to be symetric, we have a nohumans option... if((spawnPoint->flags & FL_NO_HUMANS) && !(ent->r.svFlags & SVF_BOT)) { continue; // try again } break; } while(1); } client->pers.teamState.state = TEAM_ACTIVE; // always clear the kamikaze flag ent->s.eFlags &= ~EF_KAMIKAZE; // toggle the teleport bit so the client knows to not lerp // and never clear the voted flag flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED); flags ^= EF_TELEPORT_BIT; // clear everything but the persistant data saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; // savedAreaBits = client->areabits; accuracy_hits = client->accuracy_hits; accuracy_shots = client->accuracy_shots; for(i = 0; i < MAX_PERSISTANT; i++) { persistant[i] = client->ps.persistant[i]; } eventSequence = client->ps.eventSequence; Com_Memset(client, 0, sizeof(*client)); client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; // client->areabits = savedAreaBits; client->accuracy_hits = accuracy_hits; client->accuracy_shots = accuracy_shots; client->lastkilled_client = -1; for(i = 0; i < MAX_PERSISTANT; i++) { client->ps.persistant[i] = persistant[i]; } client->ps.eventSequence = eventSequence; // increment the spawncount so the client will detect the respawn client->ps.persistant[PERS_SPAWN_COUNT]++; client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam; client->airOutTime = level.time + 12000; trap_GetUserinfo(index, userinfo, sizeof(userinfo)); // set max health client->pers.maxHealth = atoi(Info_ValueForKey(userinfo, "handicap")); if(client->pers.maxHealth < 1 || client->pers.maxHealth > 100) { client->pers.maxHealth = 100; } // clear entity values client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->ps.eFlags = flags; ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[index]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; VectorCopy(playerMins, ent->r.mins); VectorCopy(playerMaxs, ent->r.maxs); client->ps.clientNum = index; /* client->ps.stats[STAT_WEAPONS] = (1 << WP_MACHINEGUN); if(g_gametype.integer == GT_TEAM) { client->ps.ammo[WP_MACHINEGUN] = 50; } else { client->ps.ammo[WP_MACHINEGUN] = 100; } */ /* client->ps.stats[STAT_WEAPONS] |= (1 << WP_GAUNTLET); client->ps.ammo[WP_GAUNTLET] = -1; */ // health will count down towards max_health ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25; client->ps.stats[STAT_ARMOR] = client->ps.stats[STAT_MAX_HEALTH]; G_SetOrigin(ent, spawn_origin); VectorCopy(spawn_origin, client->ps.origin); // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd(client - level.clients, &ent->client->pers.cmd); SetClientViewAngle(ent, spawn_angles); if(ent->client->sess.sessionTeam == TEAM_SPECTATOR) { } else { G_KillBox(ent); trap_LinkEntity(ent); // force the base weapon up //client->ps.weapon = WP_MACHINEGUN; client->ps.weaponstate = WEAPON_READY; } // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = TORSO_STAND; client->ps.legsAnim = LEGS_IDLE; if(level.intermissiontime) { MoveClientToIntermission(ent); } else { // fire the targets of the spawn point G_UseTargets(spawnPoint, ent); #ifdef G_LUA // Lua API callbacks if(spawnPoint && spawnPoint->luaTrigger) { G_LuaHook_EntityTrigger(spawnPoint->luaTrigger, spawnPoint->s.number, ent->s.number); } #endif // select the highest weapon number available, after any // spawn given items have fired client->ps.weapon = 0; for(i = WP_NUM_WEAPONS - 1; i > 0; i--) { if(client->ps.stats[STAT_WEAPONS] & (1 << i)) { client->ps.weapon = i; break; } } } #if defined(ACEBOT) if(ent->r.svFlags & SVF_BOT) { ACESP_SetupBotState(ent); } #endif #ifdef G_LUA // Lua API callbacks G_LuaHook_ClientSpawn(ent->s.number); #endif // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.time - 100; ent->client->pers.cmd.serverTime = level.time; ClientThink(ent - g_entities); // positively link the client, even if the command times are weird if(ent->client->sess.sessionTeam != TEAM_SPECTATOR) { BG_PlayerStateToEntityState(&client->ps, &ent->s, qtrue); VectorCopy(ent->client->ps.origin, ent->r.currentOrigin); trap_LinkEntity(ent); } // run the presend to set anything else ClientEndFrame(ent); // clear entity state values BG_PlayerStateToEntityState(&client->ps, &ent->s, qtrue); }