/** * @brief Swaps active players on teams */ void G_swapTeams(void) { int i; gclient_t *cl; for (i = TEAM_AXIS; i <= TEAM_ALLIES; i++) { G_teamReset(i, qtrue); } for (i = 0; i < level.numConnectedClients; i++) { cl = level.clients + level.sortedClients[i]; if (cl->sess.sessionTeam == TEAM_AXIS) { cl->sess.sessionTeam = TEAM_ALLIES; } else if (cl->sess.sessionTeam == TEAM_ALLIES) { cl->sess.sessionTeam = TEAM_AXIS; } else { continue; } G_UpdateCharacter(cl); ClientUserinfoChanged(level.sortedClients[i]); ClientBegin(level.sortedClients[i]); } AP("cp \"^1Teams have been swapped!\n\""); }
/** * @brief Shuffle active players onto teams */ void G_shuffleTeams(void) { int i; team_t cTeam; //, cMedian = level.numNonSpectatorClients / 2; int cnt = 0; int sortClients[MAX_CLIENTS]; gclient_t *cl; G_teamReset(TEAM_AXIS, qtrue); G_teamReset(TEAM_ALLIES, qtrue); for (i = 0; i < level.numConnectedClients; i++) { cl = level.clients + level.sortedClients[i]; if (cl->sess.sessionTeam != TEAM_AXIS && cl->sess.sessionTeam != TEAM_ALLIES) { continue; } sortClients[cnt++] = level.sortedClients[i]; } qsort(sortClients, cnt, sizeof(int), G_SortPlayersByXP); for (i = 0; i < cnt; i++) { cl = level.clients + sortClients[i]; // cTeam = (i % 2) + TEAM_AXIS; cTeam = (((i + 1) % 4) - ((i + 1) % 2)) / 2 + TEAM_AXIS; if (cl->sess.sessionTeam != cTeam) { G_LeaveTank(g_entities + sortClients[i], qfalse); G_RemoveClientFromFireteams(sortClients[i], qtrue, qfalse); if (g_landminetimeout.integer) { G_ExplodeMines(g_entities + sortClients[i]); } G_FadeItems(g_entities + sortClients[i], MOD_SATCHEL); } cl->sess.sessionTeam = cTeam; G_UpdateCharacter(cl); ClientUserinfoChanged(sortClients[i]); ClientBegin(sortClients[i]); } AP("cp \"^1Teams have been shuffled!\n\""); }
/* =========== 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; int savedTeam; qboolean update = qfalse; save_position_t *pos = NULL; index = ent - g_entities; client = ent->client; G_UpdateSpawnCounts(); client->pers.lastSpawnTime = level.time; if (client->sess.sessionTeam != TEAM_AXIS && client->sess.sessionTeam != TEAM_ALLIES) { spawnPoint = SelectSpectatorSpawnPoint(spawn_origin, spawn_angles); } else { spawnPoint = SelectPlayerSpawnPoint(client->sess.sessionTeam, spawn_origin, spawn_angles, client->sess.spawnObjectiveIndex); } client->pers.teamState.state = TEAM_ACTIVE; // toggle the teleport bit so the client knows to not lerp flags = ent->client->ps.eFlags & EF_TELEPORT_BIT; flags ^= EF_TELEPORT_BIT; flags |= (client->ps.eFlags & EF_VOTED); // clear everything but the persistant data ent->s.eFlags &= ~EF_MOUNTEDTANK; // Nico, notify timerun_stop notify_timerun_stop(ent, 0); ent->client->sess.timerunActive = qfalse; saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; savedTeam = client->ps.teamNum; for (i = 0 ; i < MAX_PERSISTANT ; i++) { persistant[i] = client->ps.persistant[i]; } memset(client, 0, sizeof (*client)); client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; client->ps.teamNum = savedTeam; for (i = 0 ; i < MAX_PERSISTANT ; i++) { client->ps.persistant[i] = persistant[i]; } // 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->ps.persistant[PERS_HWEAPON_USE] = 0; client->airOutTime = level.time + 12000; // 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; // DHM - Nerve :: Init to -1 on first spawn; ent->props_frame_state = -1; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; VectorCopy(playerMins, ent->r.mins); VectorCopy(playerMaxs, ent->r.maxs); // Ridah, setup the bounding boxes and viewheights for prediction VectorCopy(ent->r.mins, client->ps.mins); VectorCopy(ent->r.maxs, client->ps.maxs); client->ps.crouchViewHeight = CROUCH_VIEWHEIGHT; client->ps.standViewHeight = DEFAULT_VIEWHEIGHT; client->ps.deadViewHeight = DEAD_VIEWHEIGHT; client->ps.crouchMaxZ = client->ps.maxs[2] - (client->ps.standViewHeight - client->ps.crouchViewHeight); client->ps.runSpeedScale = 0.8; client->ps.sprintSpeedScale = 1.1; client->ps.crouchSpeedScale = 0.25; client->ps.weaponstate = WEAPON_READY; // Rafael client->ps.friction = 1.0; // done. // TTimo // retrieve from the persistant storage (we use this in pmoveExt_t beause we need it in bg_*) client->pmext.bAutoReload = client->pers.bAutoReloadAux; // done client->ps.clientNum = index; trap_GetUsercmd(client - level.clients, &ent->client->pers.cmd); // NERVE - SMF - moved this up here if (client->sess.playerType != client->sess.latchPlayerType) { update = qtrue; } client->sess.playerType = client->sess.latchPlayerType; if (client->sess.playerWeapon != client->sess.latchPlayerWeapon) { client->sess.playerWeapon = client->sess.latchPlayerWeapon; update = qtrue; } client->sess.playerWeapon2 = client->sess.latchPlayerWeapon2; if (update) { ClientUserinfoChanged(index); } G_UpdateCharacter(client); SetWolfSpawnWeapons(client); client->pers.maxHealth = 125; client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; client->pers.cmd.weapon = ent->client->ps.weapon; // dhm - end ent->health = client->ps.stats[STAT_HEALTH] = 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; SetClientViewAngle(ent, spawn_angles); if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { trap_LinkEntity(ent); } client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; client->latched_wbuttons = 0; //----(SA) added // fire the targets of the spawn point G_UseTargets(spawnPoint, ent); // 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); // set idle animation on weapon ent->client->ps.weapAnim = ((ent->client->ps.weapAnim & ANIM_TOGGLEBIT) ^ ANIM_TOGGLEBIT) | PM_IdleAnimForWeapon(ent->client->ps.weapon); // clear entity state values BG_PlayerStateToEntityState(&client->ps, &ent->s, qtrue); // show_bug.cgi?id=569 G_ResetMarkers(ent); // RF, start the scripting system if (client->sess.sessionTeam != TEAM_SPECTATOR) { // RF, call entity scripting event G_Script_ScriptEvent(ent, "playerstart", ""); } // Nico, autoload position if (ent->client->pers.autoLoad && !ent->client->sess.lastDieWasASelfkill && (ent->client->sess.sessionTeam == TEAM_AXIS || ent->client->sess.sessionTeam == TEAM_ALLIES)) { if (ent->client->sess.sessionTeam == TEAM_ALLIES) { pos = ent->client->sess.alliesSaves; } else { pos = ent->client->sess.axisSaves; } if (pos->valid) { VectorCopy(pos->origin, ent->client->ps.origin); // Nico, load angles if cg_loadViewAngles = 1 if (ent->client->pers.loadViewAngles) { SetClientViewAngle(ent, pos->vangles); } // Nico, load saved weapon if cg_loadWeapon = 1 if (ent->client->pers.loadWeapon) { ent->client->ps.weapon = pos->weapon; } VectorClear(ent->client->ps.velocity); if (ent->client->ps.stats[STAT_HEALTH] < 100 && ent->client->ps.stats[STAT_HEALTH] > 0) { ent->health = 100; } } } }
/* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *ClientConnect(int clientNum, qboolean firstTime) { char *value; gclient_t *client; char userinfo[MAX_INFO_STRING]; gentity_t *ent; char userinfo2[MAX_INFO_STRING]; // Nico, used in connections limit check int i = 0; int clientNum2; // Nico, used in connections limit check int conn_per_ip = 1; // Nico, connections per IP counter char ip[20], ip2[20]; // Nico, used in connections limit check char parsedIp[20], parsedIp2[20]; // Nico, used in connections limit check char cs_name[MAX_NETNAME]; ent = &g_entities[clientNum]; trap_GetUserinfo(clientNum, userinfo, sizeof (userinfo)); // IP filtering // show_bug.cgi?id=500 // recommanding PB based IP / GUID banning, the builtin system is pretty limited // check to see if they are on the banned IP list value = Info_ValueForKey(userinfo, "ip"); if (G_FilterIPBanPacket(value)) { return "You are banned from this server."; } // Nico, check maximum connections per IP (from ETpub) // (prevents fakeplayers DOS http://aluigi.altervista.org/fakep.htm ) // note: value is the client ip if (!getParsedIp(value, parsedIp)) { return "Invalid IP address"; } Q_strncpyz(ip, parsedIp, sizeof (ip)); for (i = 0; i < level.numConnectedClients; ++i) { clientNum2 = level.sortedClients[i]; if (clientNum == clientNum2) { continue; } trap_GetUserinfo(clientNum2, userinfo2, sizeof (userinfo2)); value = Info_ValueForKey(userinfo2, "ip"); if (!getParsedIp(value, parsedIp2)) { continue; } Q_strncpyz(ip2, parsedIp2, sizeof (ip2)); if (strcmp(ip, ip2) == 0) { conn_per_ip++; } } if (conn_per_ip > g_maxConnsPerIP.integer) { G_LogPrintf("%s: possible DoS attack, rejecting client from %s (%d connections already)\n", GAME_VERSION, ip, g_maxConnsPerIP.integer); return "Too many connections from your IP."; } // Nico, end of check maximum connections per IP // Nico, check name value = Info_ValueForKey(userinfo, "name"); Q_strncpyz(cs_name, value, sizeof (cs_name)); if (CheckName(cs_name) != qtrue) { return "Bad name: extended ASCII characters or too long name. Please change your name."; } // we don't check password for bots and local client // NOTE: local client <-> "ip" "localhost" // this means this client is not running in our current process if (strcmp(Info_ValueForKey(userinfo, "ip"), "localhost") != 0) { // check for a password value = Info_ValueForKey(userinfo, "password"); if (g_password.string[0] && Q_stricmp(g_password.string, "none") && strcmp(g_password.string, value) != 0 && (!sv_privatepassword.string[0] || strcmp(sv_privatepassword.string, value) != 0)) { return "Invalid password"; } } // Gordon: porting q3f flag bug fix // If a player reconnects quickly after a disconnect, the client disconnect may never be called, thus flag can get lost in the ether if (ent->inuse) { G_LogPrintf("Forcing disconnect on active client: %d\n", (int)(ent - g_entities)); // so lets just fix up anything that should happen on a disconnect ClientDisconnect(ent - g_entities); } // they can connect ent->client = level.clients + clientNum; client = ent->client; memset(client, 0, sizeof (*client)); client->pers.connected = CON_CONNECTING; // read or initialize the session data if (firstTime) { G_InitSessionData(client); client->pers.enterTime = level.time; client->ps.persistant[PERS_SCORE] = 0; } else { G_ReadSessionData(client); } client->pers.enterTime = level.time; if (firstTime) { // force into spectator client->sess.sessionTeam = TEAM_SPECTATOR; client->sess.spectatorState = SPECTATOR_FREE; client->sess.spectatorClient = 0; // unlink the entity - just in case they were already connected trap_UnlinkEntity(ent); } // Nico, GeoIP if (gidb != NULL) { value = Info_ValueForKey (userinfo, "ip"); if (!strcmp(value, "localhost")) { client->sess.countryCode = 0; } else { char realIP[IP_MAX_LENGTH] = {0};// Nico, used to store IP without :port unsigned long ip; // Nico, remove :port from IP sscanf(value, "%15[0-9.]:%*d", realIP); ip = GeoIP_addr_to_num(realIP); if (((ip & 0xFF000000) == 0x0A000000) || ((ip & 0xFFF00000) == 0xAC100000) || ((ip & 0xFFFF0000) == 0xC0A80000) || ( ip == 0x7F000001) ) { client->sess.countryCode = 246; } else { unsigned int ret = GeoIP_seek_record(gidb, ip); if (ret > 0) { client->sess.countryCode = ret; } else { client->sess.countryCode = 246; G_LogPrintf("GeoIP: This IP:%s cannot be located\n", realIP); } } } } else { client->sess.countryCode = 255; } // Nico, end of GeoIP // get and distribute relevent paramters G_LogPrintf("ClientConnect: %i\n", clientNum); G_UpdateCharacter(client); ClientUserinfoChanged(clientNum); // don't do the "xxx connected" messages if they were caried over from previous level // TAT 12/10/2002 - Don't display connected messages in single player if (firstTime) { trap_SendServerCommand(-1, va("cpm \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname)); } // count current clients and rank for scoreboard CalculateRanks(); return NULL; }