/* ================== Cmd_TeamTask_f ================== */ void Cmd_TeamTask_f( gentity_t *ent ) { char userinfo[MAX_INFO_STRING]; char arg[MAX_TOKEN_CHARS]; int task; int client = ent->client - level.clients; if ( trap_Argc() != 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); task = atoi( arg ); trap_GetUserinfo(client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, "teamtask", va("%d", task)); trap_SetUserinfo(client, userinfo); ClientUserinfoChanged(client); }
/* * think_MoveTypeSwitcher - Used to add a delay to bunnyhop style changes */ void think_MoveTypeSwitcher( edict_t *ent ) { edict_t *owner; if( ent->s.ownerNum > 0 && ent->s.ownerNum <= gs.maxclients ) { owner = &game.edicts[ent->s.ownerNum]; if( owner->r.client ) { owner->r.client->movestyle = owner->r.client->movestyle_latched; ClientUserinfoChanged( owner, owner->r.client->userinfo ); G_PrintMsg( owner, "Your movement style has been updated to %i\n", owner->r.client->movestyle ); } } G_FreeEdict( ent ); }
// *** Player Un-Mute *** int G_UnMute_v(gentity_t *ent, unsigned int dwVoteIndex, char *arg, char *arg2, qboolean fRefereeCmd) { if (fRefereeCmd) { // handled elsewhere return G_NOTFOUND; } // Vote request (vote is being initiated) if (arg) { int pid; if (!vote_allow_muting.integer && ent && !ent->client->sess.referee) { G_voteDisableMessage(ent, arg); return G_INVALID; } else if (G_voteDescription(ent, fRefereeCmd, dwVoteIndex)) { return G_INVALID; } else if ((pid = ClientNumberFromString(ent, arg2)) == -1) { return G_INVALID; } if (!level.clients[pid].sess.muted) { G_refPrintf(ent, "Player is not muted!"); return G_INVALID; } Com_sprintf(level.voteInfo.vote_value, VOTE_MAXSTRING, "%d", pid); Com_sprintf(arg2, VOTE_MAXSTRING, "%s", level.clients[pid].pers.netname); // Vote action (vote has passed) } else { int pid = atoi(level.voteInfo.vote_value); // Mute a player if (level.clients[pid].sess.referee != RL_RCON) { trap_SendServerCommand(pid, va("cpm \"^3You have been un-muted\"")); level.clients[pid].sess.muted = qfalse; AP(va("cp \"%s\n^3has been un-muted!\n\"", level.clients[pid].pers.netname)); ClientUserinfoChanged(pid); } else { G_Printf("Cannot un-mute a referee.\n"); } } return G_OK; }
// TAT 11/6/2002 // Local func to actual do skill upgrade, used by both MP skill system, and SP scripted skill system static void G_UpgradeSkill( gentity_t *ent, skillType_t skill ) { int i, cnt = 0; // See if this is the first time we've reached this skill level for( i = 0; i < SK_NUM_SKILLS; i++ ) { if( i == skill ) continue; if( ent->client->sess.skill[skill] <= ent->client->sess.skill[i] ) break; } G_DebugAddSkillLevel( ent, skill ); if( i == SK_NUM_SKILLS ) { // increase rank ent->client->sess.rank++; } if( ent->client->sess.rank >=4 ) { // Gordon: count the number of maxed out skills for( i = 0; i < SK_NUM_SKILLS; i++ ) { if( ent->client->sess.skill[ i ] >= 4 ) { cnt++; } } ent->client->sess.rank = cnt + 3; if( ent->client->sess.rank > 10 ) { ent->client->sess.rank = 10; } } ClientUserinfoChanged( ent-g_entities ); // Give em rightaway if( skill == SK_BATTLE_SENSE && ent->client->sess.skill[skill] == 1 ) { if( AddWeaponToPlayer( ent->client, WP_BINOCULARS, 1, 0, qfalse ) ) { ent->client->ps.stats[STAT_KEYS] |= ( 1 << INV_BINOCS ); } } else if( skill == SK_FIRST_AID && ent->client->sess.playerType == PC_MEDIC && ent->client->sess.skill[skill] == 4 ) { AddWeaponToPlayer( ent->client, WP_MEDIC_ADRENALINE, ent->client->ps.ammo[BG_FindAmmoForWeapon(WP_MEDIC_ADRENALINE)], ent->client->ps.ammoclip[BG_FindClipForWeapon(WP_MEDIC_ADRENALINE)], qfalse ); } }
/** * Login handler */ static void *loginHandler(void *data) { int code; int len = 0; struct query_s *queryStruct = (struct query_s *)data; gentity_t *ent = queryStruct->ent; code = API_query(queryStruct->cmd, queryStruct->result, queryStruct->query, sizeof (queryStruct->query)); len = strlen(queryStruct->result); if (code == 0) { if (len > 0 && ent && ent->client) { ent->client->sess.logged = qtrue; CP(va("print \"%s^w: you are now logged in!\n\"", GAME_VERSION_COLORED)); ClientUserinfoChanged(ent->client->ps.clientNum); // Notify client that he is now logged in trap_SendServerCommand(ent - g_entities, "client_login"); // Start recording a new temp demo. trap_SendServerCommand(ent - g_entities, "tempDemoStart"); } else { CP(va("print \"%s^w: login failed!\n\"", GAME_VERSION_COLORED)); } } else { CP(va("print \"%s^w: login failed!\n\"", GAME_VERSION_COLORED)); } free(queryStruct->result); free(queryStruct); // Decrease global active thread counter activeThreadsCounter--; G_DPrintf("%s: decreasing threads counter to %d\n", GAME_VERSION, activeThreadsCounter); return NULL; }
/* =========== 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"); // check for a password value = Info_ValueForKey (userinfo, "password"); if (strcmp(password->string, value) != 0) 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 //ZOID -- force team join ent->client->resp.ctf_team = -1; //ZOID 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->client->pers.connected = true; return true; }
/* ================ vmMain This is the only way control passes into the module. This must be the very first function compiled into the .q3vm file ================ */ Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) { switch ( command ) { case GAME_INIT: G_InitGame( arg0, arg1, arg2 ); return 0; case GAME_SHUTDOWN: G_ShutdownGame( arg0 ); return 0; case GAME_CLIENT_CONNECT: return (intptr_t)ClientConnect( arg0, arg1, arg2 ); case GAME_CLIENT_THINK: ClientThink( arg0 ); return 0; case GAME_CLIENT_USERINFO_CHANGED: ClientUserinfoChanged( arg0 ); return 0; case GAME_CLIENT_DISCONNECT: ClientDisconnect( arg0 ); return 0; case GAME_CLIENT_BEGIN: ClientBegin( arg0 ); return 0; case GAME_CLIENT_COMMAND: ClientCommand( arg0 ); return 0; case GAME_RUN_FRAME: G_RunFrame( arg0 ); return 0; case GAME_CONSOLE_COMMAND: return ConsoleCommand(); case BOTAI_START_FRAME: return BotAIStartFrame( arg0 ); } return -1; }
void VM::VMHandleSyscall(uint32_t id, Util::Reader reader) { int major = id >> 16; int minor = id & 0xffff; if (major == VM::QVM) { switch (minor) { case GAME_STATIC_INIT: IPC::HandleMsg<GameStaticInitMsg>(VM::rootChannel, std::move(reader), [] (int milliseconds) { VM::InitializeProxies(milliseconds); FS::Initialize(); VM::VMInit(); }); break; case GAME_INIT: IPC::HandleMsg<GameInitMsg>(VM::rootChannel, std::move(reader), [](int levelTime, int randomSeed, bool cheats, bool inClient) { g_cheats.integer = cheats; G_InitGame(levelTime, randomSeed, inClient); }); break; case GAME_SHUTDOWN: IPC::HandleMsg<GameShutdownMsg>(VM::rootChannel, std::move(reader), [](bool restart) { G_ShutdownGame(restart); }); break; case GAME_CLIENT_CONNECT: IPC::HandleMsg<GameClientConnectMsg>(VM::rootChannel, std::move(reader), [](int clientNum, bool firstTime, int isBot, bool& denied, std::string& reason) { const char* deniedStr = isBot ? ClientBotConnect(clientNum, firstTime, TEAM_NONE) : ClientConnect(clientNum, firstTime); denied = deniedStr != nullptr; if (denied) reason = deniedStr; }); break; case GAME_CLIENT_THINK: IPC::HandleMsg<GameClientThinkMsg>(VM::rootChannel, std::move(reader), [](int clientNum) { ClientThink(clientNum); }); break; case GAME_CLIENT_USERINFO_CHANGED: IPC::HandleMsg<GameClientUserinfoChangedMsg>(VM::rootChannel, std::move(reader), [](int clientNum) { ClientUserinfoChanged(clientNum, false); }); break; case GAME_CLIENT_DISCONNECT: IPC::HandleMsg<GameClientDisconnectMsg>(VM::rootChannel, std::move(reader), [](int clientNum) { ClientDisconnect(clientNum); }); break; case GAME_CLIENT_BEGIN: IPC::HandleMsg<GameClientBeginMsg>(VM::rootChannel, std::move(reader), [](int clientNum) { ClientBegin(clientNum); }); break; case GAME_CLIENT_COMMAND: IPC::HandleMsg<GameClientCommandMsg>(VM::rootChannel, std::move(reader), [](int clientNum, std::string command) { Cmd::PushArgs(command); ClientCommand(clientNum); Cmd::PopArgs(); }); break; case GAME_RUN_FRAME: IPC::HandleMsg<GameRunFrameMsg>(VM::rootChannel, std::move(reader), [](int levelTime) { G_RunFrame(levelTime); }); break; case GAME_SNAPSHOT_CALLBACK: G_Error("GAME_SNAPSHOT_CALLBACK not implemented"); break; case BOTAI_START_FRAME: G_Error("BOTAI_START_FRAME not implemented"); break; case GAME_MESSAGERECEIVED: G_Error("GAME_MESSAGERECEIVED not implemented"); break; default: G_Error("VMMain(): unknown game command %i", minor); } } else if (major < VM::LAST_COMMON_SYSCALL) { VM::HandleCommonSyscall(major, minor, std::move(reader), VM::rootChannel); } else { G_Error("unhandled VM major syscall number %i", major); } }
/* =========== ClientBotConnect Cut-down version of ClientConnect. Doesn't do things not relevant to bots (which are local GUIDless clients). ============ */ const char *ClientBotConnect( int clientNum, bool firstTime, team_t team ) { const char *userInfoError; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; gentity_t *ent; ent = &g_entities[ clientNum ]; client = &level.clients[ clientNum ]; ent->client = client; memset( client, 0, sizeof( *client ) ); trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); client->pers.localClient = true; G_AddressParse( "localhost", &client->pers.ip ); Q_strncpyz( client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", sizeof( client->pers.guid ) ); client->pers.admin = nullptr; client->pers.pubkey_authenticated = true; client->pers.connected = CON_CONNECTING; // read or initialize the session data if ( firstTime ) { G_InitSessionData( client, userinfo ); } G_ReadSessionData( client ); // get and distribute relevant parameters G_namelog_connect( client ); userInfoError = ClientUserinfoChanged( clientNum, false ); if ( userInfoError != nullptr ) { return userInfoError; } ent->r.svFlags |= SVF_BOT; // can happen during reconnection if ( !ent->botMind ) { G_BotSetDefaults( clientNum, team, client->sess.botSkill, client->sess.botTree ); } G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\" \"%s^7\" [BOT]", clientNum, client->pers.ip.str[0] ? client->pers.ip.str : "127.0.0.1", client->pers.guid, client->pers.netname, client->pers.netname ); // don't do the "xxx connected" messages if they were caried over from previous level if ( firstTime ) { trap_SendServerCommand( -1, va( "print_tr %s %s", QQ( N_("$1$^7 connected") ), Quote( client->pers.netname ) ) ); } // count current clients and rank for scoreboard CalculateRanks(); return nullptr; }
/* =========== 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 nullptr 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 true the very first time a client connects to the server machine, but false on map changes and tournement restarts. ============ */ const char *ClientConnect( int clientNum, bool firstTime ) { const char *value; const char *userInfoError; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; char pubkey[ RSA_STRING_LENGTH ]; gentity_t *ent; char reason[ MAX_STRING_CHARS ] = { "" }; int i; const char *country; ent = &g_entities[ clientNum ]; client = &level.clients[ clientNum ]; // ignore if client already connected if ( client->pers.connected != CON_DISCONNECTED ) { return nullptr; } ent->client = client; memset( client, 0, sizeof( *client ) ); trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); value = Info_ValueForKey( userinfo, "ip" ); // check for local client if ( !strcmp( value, "localhost" ) ) { client->pers.localClient = true; } G_AddressParse( value, &client->pers.ip ); trap_GetPlayerPubkey( clientNum, pubkey, sizeof( pubkey ) ); if ( strlen( pubkey ) != RSA_STRING_LENGTH - 1 ) { return "Invalid pubkey key"; } trap_GenFingerprint( pubkey, sizeof( pubkey ), client->pers.guid, sizeof( client->pers.guid ) ); client->pers.admin = G_admin_admin( client->pers.guid ); client->pers.pubkey_authenticated = false; if ( client->pers.admin ) { Com_GMTime( &client->pers.admin->lastSeen ); } // check for admin ban if ( G_admin_ban_check( ent, reason, sizeof( reason ) ) ) { return va( "%s", reason ); // reason is local } // check for a password value = Info_ValueForKey( userinfo, "password" ); if ( g_password.string[ 0 ] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value ) != 0 ) { return "Invalid password"; } // if a player reconnects quickly after a disconnect, the client disconnect may never be called, thus flag can get lost in the ether if ( ent->inuse ) { G_LogPrintf( "Forcing disconnect on active client: %i", (int)( ent - g_entities ) ); // so lets just fix up anything that should happen on a disconnect ClientDisconnect( ent-g_entities ); } for ( i = 0; i < level.maxclients; i++ ) { if ( level.clients[ i ].pers.connected == CON_DISCONNECTED ) { continue; } if ( !( g_entities[i].r.svFlags & SVF_BOT ) && !Q_stricmp( client->pers.guid, level.clients[ i ].pers.guid ) ) { if ( !G_ClientIsLagging( level.clients + i ) ) { trap_SendServerCommand( i, "cp \"Your GUID is not secure\"" ); return "Duplicate GUID"; } trap_DropClient( i, "Ghost" ); } } client->pers.connected = CON_CONNECTING; // read or initialize the session data if ( firstTime ) { G_InitSessionData( client, userinfo ); } G_ReadSessionData( client ); // get and distribute relevent paramters G_namelog_connect( client ); userInfoError = ClientUserinfoChanged( clientNum, false ); if ( userInfoError != nullptr ) { return userInfoError; } G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\" \"%s^7\"", clientNum, client->pers.ip.str[0] ? client->pers.ip.str : "127.0.0.1", client->pers.guid, client->pers.netname, client->pers.netname ); country = Info_ValueForKey( userinfo, "geoip" ); Q_strncpyz( client->pers.country, country, sizeof( client->pers.country ) ); G_SendClientPmoveParams(clientNum); // don't do the "xxx connected" messages if they were caried over from previous level if ( firstTime ) { if ( g_geoip.integer && country && *country ) { trap_SendServerCommand( -1, va( "print_tr %s %s %s", QQ( N_("$1$^7 connected from $2$") ), Quote( client->pers.netname ), Quote( country ) ) ); } else { trap_SendServerCommand( -1, va( "print_tr %s %s", QQ( N_("$1$^7 connected") ), Quote( client->pers.netname ) ) ); } } // count current clients and rank for scoreboard CalculateRanks(); // if this is after !restart keepteams or !restart switchteams, apply said selection if ( client->sess.restartTeam != TEAM_NONE ) { G_ChangeTeam( ent, client->sess.restartTeam ); client->sess.restartTeam = TEAM_NONE; } return nullptr; }
// *** Referee voting *** int G_Referee_v(gentity_t *ent, unsigned int dwVoteIndex, char *arg, char *arg2, qboolean fRefereeCmd) { // Vote request (vote is being initiated) if (arg) { int pid; if (!vote_allow_referee.integer && ent && !ent->client->sess.referee) { G_voteDisableMessage(ent, arg); return(G_INVALID); } if (!ent->client->sess.referee && level.numPlayingClients < 3) { G_refPrintf(ent, "Sorry, not enough clients in the game to vote for a referee"); return(G_INVALID); } if (ent->client->sess.referee && trap_Argc() == 2) { G_playersMessage(ent); return(G_INVALID); } else if (trap_Argc() == 2) { pid = ent - g_entities; } else if (G_voteDescription(ent, fRefereeCmd, dwVoteIndex)) { return(G_INVALID); } else if ((pid = ClientNumberFromString(ent, arg2)) == -1) { return(G_INVALID); } if (level.clients[pid].sess.referee) { G_refPrintf(ent, "[lof]%s [lon]is already a referee!", level.clients[pid].pers.netname); return(-1); } Com_sprintf(level.voteInfo.vote_value, VOTE_MAXSTRING, "%d", pid); Com_sprintf(arg2, VOTE_MAXSTRING, "%s", level.clients[pid].pers.netname); // Vote action (vote has passed) } else { // Voting in a new referee gclient_t *cl = &level.clients[atoi(level.voteInfo.vote_value)]; if (cl->pers.connected == CON_DISCONNECTED) { AP("print \"Player left before becoming referee\n\""); } else { cl->sess.referee = RL_REFEREE; // FIXME: Differentiate voted refs from passworded refs cl->sess.spec_invite = TEAM_AXIS | TEAM_ALLIES; AP(va("cp \"%s^7 is now a referee\n\"", cl->pers.netname)); ClientUserinfoChanged(atoi(level.voteInfo.vote_value)); } } return(G_OK); }
// *** Un-Referee voting *** int G_Unreferee_v(gentity_t *ent, unsigned int dwVoteIndex, char *arg, char *arg2, qboolean fRefereeCmd) { // Vote request (vote is being initiated) if (arg) { int pid; if (!vote_allow_referee.integer && ent && !ent->client->sess.referee) { G_voteDisableMessage(ent, arg); return(G_INVALID); } if (ent->client->sess.referee && trap_Argc() == 2) { G_playersMessage(ent); return(G_INVALID); } else if (trap_Argc() == 2) { pid = ent - g_entities; } else if (G_voteDescription(ent, fRefereeCmd, dwVoteIndex)) { return(G_INVALID); } else if ((pid = ClientNumberFromString(ent, arg2)) == -1) { return(G_INVALID); } if (level.clients[pid].sess.referee == RL_NONE) { G_refPrintf(ent, "[lof]%s [lon]isn't a referee!", level.clients[pid].pers.netname); return(G_INVALID); } if (level.clients[pid].sess.referee == RL_RCON) { G_refPrintf(ent, "[lof]%s's [lon]status cannot be removed", level.clients[pid].pers.netname); return(G_INVALID); } if (level.clients[pid].pers.localClient) { G_refPrintf(ent, "[lof]%s's [lon]is the Server Host", level.clients[pid].pers.netname); return(G_INVALID); } Com_sprintf(level.voteInfo.vote_value, VOTE_MAXSTRING, "%d", pid); Com_sprintf(arg2, VOTE_MAXSTRING, "%s", level.clients[pid].pers.netname); // Vote action (vote has passed) } else { // Stripping of referee status gclient_t *cl = &level.clients[atoi(level.voteInfo.vote_value)]; cl->sess.referee = RL_NONE; cl->sess.spec_invite = 0; AP(va("cp \"%s^7\nis no longer a referee\n\"", cl->pers.netname)); ClientUserinfoChanged(atoi(level.voteInfo.vote_value)); } return(G_OK); }
/** * @brief G_ResetXP * @param[in,out] ent */ void G_ResetXP(gentity_t *ent) { int i = 0; int ammo[MAX_WEAPONS], ammoclip[MAX_WEAPONS]; int oldWeapon; //, newWeapon; if (!ent || !ent->client) { return; } #ifdef FEATURE_RATING if (!g_skillRating.integer) #endif { ent->client->sess.rank = 0; } for (i = 0; i < SK_NUM_SKILLS; i++) { ent->client->sess.skillpoints[i] = 0.0f; ent->client->sess.skill[i] = 0; } G_CalcRank(ent->client); ent->client->ps.stats[STAT_XP] = 0; ent->client->ps.persistant[PERS_SCORE] = 0; // zero out all weapons and grab the default weapons for a player of this XP level. // backup.. Com_Memcpy(ammo, ent->client->ps.ammo, sizeof(ammo)); Com_Memcpy(ammoclip, ent->client->ps.ammoclip, sizeof(ammoclip)); oldWeapon = ent->client->ps.weapon; // Check weapon validity, maybe dump some weapons for this (now) unskilled player.. // It also sets the (possibly new) amounts of ammo for weapons. SetWolfSpawnWeapons(ent->client); // restore.. //newWeapon = ent->client->ps.weapon; for (i = WP_NONE; i < WP_NUM_WEAPONS; ++i) //i<MAX_WEAPONS { // only restore ammo for valid weapons.. // ..they might have lost some weapons because of skill changes. // Also restore to the old amount of ammo, because.. // ..SetWolfSpawnWeapons sets amount of ammo for a fresh spawning player, // which is usually more than the player currently has left. if (COM_BitCheck(ent->client->ps.weapons, i)) { if (ammo[i] < ent->client->ps.ammo[i]) { ent->client->ps.ammo[i] = ammo[i]; } if (ammoclip[i] < ent->client->ps.ammoclip[i]) { ent->client->ps.ammoclip[i] = ammoclip[i]; } } else { ent->client->ps.ammo[i] = 0; ent->client->ps.ammoclip[i] = 0; } } // check if the old weapon is still valid. // If so, restore to the last used weapon.. if (COM_BitCheck(ent->client->ps.weapons, oldWeapon)) { ent->client->ps.weapon = oldWeapon; } ClientUserinfoChanged(ent - g_entities); }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; // char *headmodel; char userinfo[MAX_INFO_STRING]; int preTeam = 0; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); 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("%1.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 = "kyle/default"; } 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 = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "saber1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "single_1"; } Info_SetValueForKey( userinfo, key, s ); key = "saber2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "none"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "personality"); if (!*s ) { Info_SetValueForKey( userinfo, "personality", "botfiles/default.jkb" ); } else { Info_SetValueForKey( userinfo, "personality", s ); } // 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.\n" ); trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "UNABLE_TO_ADD_BOT"))); return; } // initialize the bot settings 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, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); if (g_gametype.integer >= GT_TEAM) { if (team && Q_stricmp(team, "red") == 0) { bot->client->sess.sessionTeam = TEAM_RED; } else if (team && Q_stricmp(team, "blue") == 0) { bot->client->sess.sessionTeam = TEAM_BLUE; } else { bot->client->sess.sessionTeam = PickTeam( -1 ); } } if (g_gametype.integer == GT_SIEGE) { bot->client->sess.siegeDesiredTeam = bot->client->sess.sessionTeam; bot->client->sess.sessionTeam = TEAM_SPECTATOR; } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if (bot->client->sess.sessionTeam != preTeam) { trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (bot->client->sess.sessionTeam == TEAM_SPECTATOR) { bot->client->sess.sessionTeam = preTeam; } if (bot->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { if (g_gametype.integer == GT_SIEGE) { if (bot->client->sess.sessionTeam == TEAM_BLUE) { team = "Blue"; } else { team = "s"; } } else { team = "Blue"; } } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); ClientUserinfoChanged( clientNum ); } if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) { int loners = 0; int doubles = 0; bot->client->sess.duelTeam = 0; G_PowerDuelCount(&loners, &doubles, qtrue); if (!doubles || loners > (doubles/2)) { bot->client->sess.duelTeam = DUELTEAM_DOUBLE; } else { bot->client->sess.duelTeam = DUELTEAM_LONE; } bot->client->sess.sessionTeam = TEAM_SPECTATOR; SetTeam(bot, "s"); } else { if( delay == 0 ) { ClientBegin( clientNum, qfalse ); 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; }
/* * ClientConnect * Called when a player begins connecting to the server. * The game can refuse entrance to a client by returning false. * If the client is allowed, the connection process will continue * and eventually get to ClientBegin() * Changing levels will NOT cause this to be called again, but * loadgames will. */ bool ClientConnect( edict_t *ent, char *userinfo, bool fakeClient, bool tvClient ) { char *value; assert( ent ); assert( userinfo && Info_Validate( userinfo ) ); assert( Info_ValueForKey( userinfo, "ip" ) && Info_ValueForKey( userinfo, "socket" ) ); // verify that server gave us valid data if( !Info_Validate( userinfo ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Invalid userinfo" ); return false; } if( !Info_ValueForKey( userinfo, "ip" ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Error: Server didn't provide client IP" ); return false; } if( !Info_ValueForKey( userinfo, "ip" ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Error: Server didn't provide client socket" ); return false; } // check to see if they are on the banned IP list value = Info_ValueForKey( userinfo, "ip" ); if( SV_FilterPacket( value ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "You're banned from this server" ); return false; } // check for a password value = Info_ValueForKey( userinfo, "password" ); if( !fakeClient && ( *password->string && ( !value || strcmp( password->string, value ) ) ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_PASSWORD ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); if( value && value[0] ) { Info_SetValueForKey( userinfo, "rejmsg", "Incorrect password" ); } else { Info_SetValueForKey( userinfo, "rejmsg", "Password required" ); } return false; } // they can connect G_InitEdict( ent ); ent->s.modelindex = 0; ent->r.solid = SOLID_NOT; ent->r.client = game.clients + PLAYERNUM( ent ); ent->r.svflags = ( SVF_NOCLIENT | ( fakeClient ? SVF_FAKECLIENT : 0 ) ); memset( ent->r.client, 0, sizeof( gclient_t ) ); ent->r.client->ps.playerNum = PLAYERNUM( ent ); ent->r.client->connecting = true; ent->r.client->isTV = tvClient == true; ent->r.client->team = TEAM_SPECTATOR; G_Client_UpdateActivity( ent->r.client ); // activity detected ClientUserinfoChanged( ent, userinfo ); if( !fakeClient ) { char message[MAX_STRING_CHARS]; Q_snprintfz( message, sizeof( message ), "%s%s connected", ent->r.client->netname, S_COLOR_WHITE ); G_PrintMsg( NULL, "%s\n", message ); G_Printf( "%s%s connected from %s\n", ent->r.client->netname, S_COLOR_WHITE, ent->r.client->ip ); } // let the gametype scripts know this client just connected G_Gametype_ScoreEvent( ent->r.client, "connect", NULL ); G_CallVotes_ResetClient( PLAYERNUM( ent ) ); return true; }
/* * G_ClientRespawn */ void G_ClientRespawn( edict_t *self, bool ghost ) { int i; edict_t *spawnpoint; vec3_t hull_mins, hull_maxs; vec3_t spawn_origin, spawn_angles; gclient_t *client; int old_team; G_DeathAwards( self ); G_SpawnQueue_RemoveClient( self ); self->r.svflags &= ~SVF_NOCLIENT; //if invalid be spectator if( self->r.client->team < 0 || self->r.client->team >= GS_MAX_TEAMS ) self->r.client->team = TEAM_SPECTATOR; // force ghost always to true when in spectator team if( self->r.client->team == TEAM_SPECTATOR ) ghost = true; old_team = self->s.team; if( self->r.client->teamstate.is_coach ) ghost = true; GClip_UnlinkEntity( self ); client = self->r.client; memset( &client->resp, 0, sizeof( client->resp ) ); memset( &client->ps, 0, sizeof( client->ps ) ); client->resp.timeStamp = level.time; client->ps.playerNum = PLAYERNUM( self ); // clear entity values memset( &self->snap, 0, sizeof( self->snap ) ); memset( &self->s, 0, sizeof( self->s ) ); memset( &self->olds, 0, sizeof( self->olds ) ); memset( &self->invpak, 0, sizeof( self->invpak ) ); self->s.number = self->olds.number = ENTNUM( self ); // relink client struct self->r.client = &game.clients[PLAYERNUM( self )]; // update team self->s.team = client->team; self->deadflag = DEAD_NO; self->s.type = ET_PLAYER; self->groundentity = NULL; self->takedamage = DAMAGE_AIM; self->think = player_think; self->pain = player_pain; self->die = player_die; self->viewheight = playerbox_stand_viewheight; self->r.inuse = true; self->mass = PLAYER_MASS; self->air_finished = level.time + ( 12 * 1000 ); self->r.clipmask = MASK_PLAYERSOLID; self->waterlevel = 0; self->watertype = 0; self->flags &= ~FL_NO_KNOCKBACK; self->r.svflags &= ~SVF_CORPSE; self->enemy = NULL; self->r.owner = NULL; self->max_health = 100; self->health = self->max_health; if( AI_GetType( self->ai ) == AI_ISBOT ) { self->think = NULL; self->classname = "bot"; } else if( self->r.svflags & SVF_FAKECLIENT ) self->classname = "fakeclient"; else self->classname = "player"; VectorCopy( playerbox_stand_mins, self->r.mins ); VectorCopy( playerbox_stand_maxs, self->r.maxs ); VectorClear( self->velocity ); VectorClear( self->avelocity ); VectorCopy( self->r.mins, hull_mins ); VectorCopy( self->r.maxs, hull_maxs ); trap_CM_RoundUpToHullSize( hull_mins, hull_maxs, NULL ); if( self->r.maxs[2] > hull_maxs[2] ) self->viewheight -= (self->r.maxs[2] - hull_maxs[2]); client->ps.POVnum = ENTNUM( self ); // set movement info client->ps.pmove.stats[PM_STAT_MAXSPEED] = (short)DEFAULT_PLAYERSPEED; client->ps.pmove.stats[PM_STAT_JUMPSPEED] = (short)DEFAULT_JUMPSPEED; client->ps.pmove.stats[PM_STAT_DASHSPEED] = (short)DEFAULT_DASHSPEED; if( ghost ) { self->r.solid = SOLID_NOT; self->movetype = MOVETYPE_NOCLIP; if( self->s.team == TEAM_SPECTATOR ) self->r.svflags |= SVF_NOCLIENT; } else { self->r.client->resp.takeStun = true; self->r.solid = SOLID_YES; self->movetype = MOVETYPE_PLAYER; client->ps.pmove.stats[PM_STAT_FEATURES] = static_cast<unsigned short>(PMFEAT_DEFAULT); if( !g_allow_bunny->integer ) client->ps.pmove.stats[PM_STAT_FEATURES] &= ~( PMFEAT_AIRCONTROL|PMFEAT_FWDBUNNY ); } ClientUserinfoChanged( self, client->userinfo ); if( old_team != self->s.team ) G_Teams_UpdateMembersList(); SelectSpawnPoint( self, &spawnpoint, spawn_origin, spawn_angles ); VectorCopy( spawn_origin, client->ps.pmove.origin ); VectorCopy( spawn_origin, self->s.origin ); VectorCopy( self->s.origin, self->s.old_origin ); // set angles self->s.angles[PITCH] = 0; self->s.angles[YAW] = anglemod( spawn_angles[YAW] ); self->s.angles[ROLL] = 0; VectorCopy( self->s.angles, client->ps.viewangles ); // set the delta angle for( i = 0; i < 3; i++ ) client->ps.pmove.delta_angles[i] = ANGLE2SHORT( client->ps.viewangles[i] ) - client->ucmd.angles[i]; // don't put spectators in the game if( !ghost ) { if( KillBox( self ) ) { } } self->s.attenuation = ATTN_NORM; self->s.teleported = true; // hold in place briefly client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; client->ps.pmove.pm_time = 14; client->ps.pmove.stats[PM_STAT_NOUSERCONTROL] = CLIENT_RESPAWN_FREEZE_DELAY; client->ps.pmove.stats[PM_STAT_NOAUTOATTACK] = 1000; // set race stats to invisible client->ps.stats[STAT_TIME_SELF] = STAT_NOTSET; client->ps.stats[STAT_TIME_BEST] = STAT_NOTSET; client->ps.stats[STAT_TIME_RECORD] = STAT_NOTSET; client->ps.stats[STAT_TIME_ALPHA] = STAT_NOTSET; client->ps.stats[STAT_TIME_BETA] = STAT_NOTSET; BOT_Respawn( self ); self->r.client->level.respawnCount++; G_UseTargets( spawnpoint, self ); GClip_LinkEntity( self ); // let the gametypes perform their changes if( game.asEngine != NULL ) GT_asCallPlayerRespawn( self, old_team, self->s.team ); else G_Gametype_GENERIC_ClientRespawn( self, old_team, self->s.team ); }
// Request for ref status or lists ref commands. void G_ref_cmd(gentity_t *ent, unsigned int dwCommand, qboolean fValue) { char arg[MAX_TOKEN_CHARS]; // Roll through ref commands if already a ref if (ent == NULL || ent->client->sess.referee) { voteInfo_t votedata; trap_Argv(1, arg, sizeof(arg)); memcpy(&votedata, &level.voteInfo, sizeof(voteInfo_t)); if (Cmd_CallVote_f(ent, 0, qtrue)) { memcpy(&level.voteInfo, &votedata, sizeof(voteInfo_t)); return; } else { memcpy(&level.voteInfo, &votedata, sizeof(voteInfo_t)); if (G_refCommandCheck(ent, arg)) { return; } else { G_refHelp_cmd(ent); } } return; } if (ent) { if (!Q_stricmp(refereePassword.string, "none") || !refereePassword.string[0]) { CP("cpm \"Sorry, referee status disabled on this server.\n\""); return; } if (trap_Argc() < 2) { CP("cpm \"Usage: ref [password]\n\""); return; } trap_Argv(1, arg, sizeof(arg)); if (Q_stricmp(arg, refereePassword.string)) { CP("cpm \"Invalid referee password!\n\""); return; } ent->client->sess.referee = 1; ent->client->sess.spec_invite = TEAM_AXIS | TEAM_ALLIES; AP(va("cp \"%s\n^3has become a referee\n\"", ent->client->pers.netname)); ClientUserinfoChanged(ent - g_entities); } }
//[TABBot] //added bot type varible static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname, int bottype) { //static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { //[/TABBot] int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; // char *headmodel; char userinfo[MAX_INFO_STRING]; int preTeam = 0; //[DuelGuns][EnhancedImpliment] /* char *firearm; // ** change gun model qboolean bot_dualguns = qfalse; int gunoption=0; */ //[/DuelGuns][EnhancedImpliment] // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); 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("%1.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 = "kyle/default"; } 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 = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "saber1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "single_1"; } Info_SetValueForKey( userinfo, key, s ); key = "saber2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "none"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "personality"); if (!*s ) { Info_SetValueForKey( userinfo, "personality", "botfiles/default.jkb" ); } else { Info_SetValueForKey( userinfo, "personality", s ); } //[DuelGuns][EnhancedImpliment] /* // if(1)//f_dualguns.integer) // { key = "dualgun"; s = Info_ValueForKey(botinfo, key); if (*s) { gunoption = atoi(s); } if(gunoption>0) bot_dualguns = qtrue; // } firearm = Info_ValueForKey( botinfo, "firearm"); if (!*firearm) { Info_SetValueForKey( userinfo, "firearm", botname ); } else { Info_SetValueForKey( userinfo, "firearm", firearm); } */ //[/DuelGuns][EnhancedImpliment] //[RGBSabers] key = "rgb_saber1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "255,0,0"; } Info_SetValueForKey( userinfo, key, s ); key = "rgb_saber2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "0,255,255"; } Info_SetValueForKey( userinfo, key, s ); key = "rgb_script1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "none"; } Info_SetValueForKey( userinfo, key, s ); key = "rgb_script2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "none"; } Info_SetValueForKey( userinfo, key, s ); //[/RGBSabers] //[ClientPlugInDetect] //set it so that the bots are assumed to have the OJP client plugin //this should be CURRENT_OJPENHANCED_CLIENTVERSION Info_SetValueForKey( userinfo, "ojp_clientplugin", CURRENT_OJPENHANCED_CLIENTVERSION ); //[/ClientPlugInDetect] // 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.\n" ); trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "UNABLE_TO_ADD_BOT"))); return; } //[DuelGuns][EnhancedImpliment] /* if(bot_dualguns) { g_entities[clientNum].client->ps.dualguns = 1; } */ //[/DuelGuns][EnhancedImpliment] // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { //[AdminSys] if( PickTeam(clientNum, qtrue) == TEAM_RED) { //if( PickTeam(clientNum) == TEAM_RED) { //[/AdminSys] team = "red"; } else { team = "blue"; } } else { team = "red"; } } // Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); //[TABBot] Info_SetValueForKey( userinfo, "bottype", va( "%i", bottype) ); //[/TABBot] bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); //[NewGameTypes][EnhancedImpliment] //if (g_gametype.integer >= GT_TEAM && g_gametype.integer != GT_RPG) if (g_gametype.integer >= GT_TEAM) //[/NewGameTypes][EnhancedImpliment] { if (team && Q_stricmp(team, "red") == 0) { bot->client->sess.sessionTeam = TEAM_RED; } else if (team && Q_stricmp(team, "blue") == 0) { bot->client->sess.sessionTeam = TEAM_BLUE; } else { //[AdminSys] bot->client->sess.sessionTeam = PickTeam( -1, qtrue ); //bot->client->sess.sessionTeam = PickTeam( -1 ); //[/AdminSys] } } if (g_gametype.integer == GT_SIEGE) { bot->client->sess.siegeDesiredTeam = bot->client->sess.sessionTeam; bot->client->sess.sessionTeam = TEAM_SPECTATOR; } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if (bot->client->sess.sessionTeam != preTeam) { trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (bot->client->sess.sessionTeam == TEAM_SPECTATOR) { bot->client->sess.sessionTeam = preTeam; } if (bot->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { if (g_gametype.integer == GT_SIEGE) { if (bot->client->sess.sessionTeam == TEAM_BLUE) { team = "Blue"; } else { team = "s"; } } else { team = "Blue"; } } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); ClientUserinfoChanged( clientNum ); } if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) { int loners = 0; int doubles = 0; bot->client->sess.duelTeam = 0; G_PowerDuelCount(&loners, &doubles, qtrue); if (!doubles || loners > (doubles/2)) { bot->client->sess.duelTeam = DUELTEAM_DOUBLE; } else { bot->client->sess.duelTeam = DUELTEAM_LONE; } bot->client->sess.sessionTeam = TEAM_SPECTATOR; SetTeam(bot, "s"); } else { if( delay == 0 ) { ClientBegin( clientNum, qfalse ); //UNIQUEFIX - what's the purpose of this? //ClientUserinfoChanged( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); //UNIQUEFIX - what's the purpose of this? //ClientUserinfoChanged( clientNum ); } }
/* =========== 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 guid[ 33 ]; char reason[ MAX_STRING_CHARS ] = {""}; ent = &g_entities[ clientNum ]; client = &level.clients[ clientNum ]; ent->client = client; memset( client, 0, sizeof( *client ) ); trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); value = Info_ValueForKey( userinfo, "cl_guid" ); Q_strncpyz( guid, value, sizeof( guid ) ); // check for admin ban if( G_admin_ban_check( userinfo, reason, sizeof( reason ) ) ) { return va( "%s", reason ); } // IP filtering // https://zerowing.idsoftware.com/bugzilla/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" ); Q_strncpyz( client->pers.ip, value, sizeof( client->pers.ip ) ); if( G_FilterPacket( value ) ) return "You are banned from this server."; // check for a password value = Info_ValueForKey( userinfo, "password" ); if( g_password.string[ 0 ] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value ) != 0 ) return "Invalid password"; // add guid to session so we don't have to keep parsing userinfo everywhere if( !guid[0] ) { Q_strncpyz( client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", sizeof( client->pers.guid ) ); } else { Q_strncpyz( client->pers.guid, guid, sizeof( client->pers.guid ) ); } // check for local client if( !strcmp( client->pers.ip, "localhost" ) ) client->pers.localClient = qtrue; client->pers.adminLevel = G_admin_level( ent ); client->pers.connected = CON_CONNECTING; // read or initialize the session data if( firstTime || level.newSession ) G_InitSessionData( client, userinfo ); G_ReadSessionData( client ); // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s\"\n", clientNum, client->pers.ip, client->pers.guid, client->pers.netname ); // don't do the "xxx connected" messages if they were caried over from previous level if( firstTime ) trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname ) ); // count current clients and rank for scoreboard CalculateRanks( ); G_admin_namelog_update( client, qfalse ); return NULL; }
void ServerGame::clientUserInfoChanged( int clientNum ) const { ClientUserinfoChanged( clientNum ); }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { gentity_t *bot = NULL; int clientNum, preTeam = TEAM_FREE; char userinfo[MAX_INFO_STRING] = {0}, *botinfo = NULL, *key = NULL, *s = NULL, *botname = NULL, *model = NULL; // have the server allocate a client slot clientNum = trap->BotAllocateClient(); if ( clientNum == -1 ) { // trap->Print( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); // trap->Print( S_COLOR_RED "Start server with more 'open' slots.\n" ); trap->SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "UNABLE_TO_ADD_BOT"))); return; } // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { trap->BotFreeClient( clientNum ); trap->Print( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); 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, "ip", "localhost" ); 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" ); else Info_SetValueForKey( userinfo, "handicap", "100" ); key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) model = DEFAULT_MODEL"/default"; Info_SetValueForKey( userinfo, key, model ); key = "sex"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = Info_ValueForKey( botinfo, "gender" ); if ( !*s ) s = "male"; Info_SetValueForKey( userinfo, key, 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 = "4"; Info_SetValueForKey( userinfo, key, s ); key = "saber1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = DEFAULT_SABER; Info_SetValueForKey( userinfo, key, s ); key = "saber2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "none"; Info_SetValueForKey( userinfo, key, s ); key = "forcepowers"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = DEFAULT_FORCEPOWERS; Info_SetValueForKey( userinfo, key, s ); key = "cg_predictItems"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "1"; Info_SetValueForKey( userinfo, key, s ); key = "char_color_red"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "255"; Info_SetValueForKey( userinfo, key, s ); key = "char_color_green"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "255"; Info_SetValueForKey( userinfo, key, s ); key = "char_color_blue"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "255"; Info_SetValueForKey( userinfo, key, s ); key = "teamtask"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "0"; Info_SetValueForKey( userinfo, key, s ); key = "personality"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "botfiles/default.jkb"; Info_SetValueForKey( userinfo, key, s ); // initialize the bot settings if ( !team || !*team ) { if ( level.gametype >= GT_TEAM ) { if ( PickTeam( clientNum ) == TEAM_RED) team = "red"; else team = "blue"; } else team = "red"; } Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; // bot->r.svFlags |= SVF_BOT; // bot->inuse = qtrue; // register the userinfo trap->SetUserinfo( clientNum, userinfo ); if ( level.gametype >= GT_TEAM ) { if ( team && !Q_stricmp( team, "red" ) ) bot->client->sess.sessionTeam = TEAM_RED; else if ( team && !Q_stricmp( team, "blue" ) ) bot->client->sess.sessionTeam = TEAM_BLUE; else bot->client->sess.sessionTeam = PickTeam( -1 ); } if ( level.gametype == GT_SIEGE ) { bot->client->sess.siegeDesiredTeam = bot->client->sess.sessionTeam; bot->client->sess.sessionTeam = TEAM_SPECTATOR; } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) return; if ( bot->client->sess.sessionTeam != preTeam ) { trap->GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); if ( bot->client->sess.sessionTeam == TEAM_SPECTATOR ) bot->client->sess.sessionTeam = preTeam; if ( bot->client->sess.sessionTeam == TEAM_RED ) team = "Red"; else { if ( level.gametype == GT_SIEGE ) team = (bot->client->sess.sessionTeam == TEAM_BLUE) ? "Blue" : "s"; else team = "Blue"; } Info_SetValueForKey( userinfo, "team", team ); trap->SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); if ( !ClientUserinfoChanged( clientNum ) ) return; } if (level.gametype == GT_DUEL || level.gametype == GT_POWERDUEL) { int loners = 0; int doubles = 0; bot->client->sess.duelTeam = 0; G_PowerDuelCount(&loners, &doubles, qtrue); if (!doubles || loners > (doubles/2)) { bot->client->sess.duelTeam = DUELTEAM_DOUBLE; } else { bot->client->sess.duelTeam = DUELTEAM_LONE; } bot->client->sess.sessionTeam = TEAM_SPECTATOR; SetTeam(bot, "s"); } else { if( delay == 0 ) { ClientBegin( clientNum, qfalse ); return; } AddBotToSpawnQueue( clientNum, delay ); } }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, const char *pclass, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; char userinfo[MAX_INFO_STRING]; int preTeam = 0; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); 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("%1.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 = "munro/main/default"; //RPG-X MODEL SYSTEM } Info_SetValueForKey( userinfo, key, model ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" ); return; } // 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; } // initialize the bot settings 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, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); if (g_gametype.integer >= GT_TEAM) { if (team && Q_stricmp(team, "red") == 0) { bot->client->sess.sessionTeam = TEAM_RED; } else if (team && Q_stricmp(team, "blue") == 0) { bot->client->sess.sessionTeam = TEAM_BLUE; } else { bot->client->sess.sessionTeam = PickTeam( -1 ); } } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if (bot->client->sess.sessionTeam != preTeam) { trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (bot->client->sess.sessionTeam == TEAM_SPECTATOR) { bot->client->sess.sessionTeam = preTeam; } if (bot->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { team = "Blue"; } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); ClientUserinfoChanged( clientNum ); } if( delay == 0 ) { ClientBegin( clientNum, qfalse, qfalse, qfalse ); return; } AddBotToSpawnQueue( clientNum, delay ); }
void DBall_ClientBegin(edict_t *ent) { int team1, team2, unassigned; edict_t *other; char *p; static char value[512]; int j; team1 = 0; team2 = 0; unassigned = 0; if (!ent) { return; } for (j = 1; j <= game.maxclients; j++) { other = &g_edicts[j]; if (!other->inuse) { continue; } if (!other->client) { continue; } if (other == ent) /* don't count the new player */ { continue; } strcpy(value, Info_ValueForKey(other->client->pers.userinfo, "skin")); p = strchr(value, '/'); if (p) { if (!strcmp(dball_team1_skin->string, value)) { team1++; } else if (!strcmp(dball_team2_skin->string, value)) { team2++; } else { unassigned++; } } else { unassigned++; } } if (team1 > team2) { gi.dprintf("assigned to team 2\n"); Info_SetValueForKey(ent->client->pers.userinfo, "skin", dball_team2_skin->string); } else { gi.dprintf("assigned to team 1\n"); Info_SetValueForKey(ent->client->pers.userinfo, "skin", dball_team1_skin->string); } ClientUserinfoChanged(ent, ent->client->pers.userinfo); if (unassigned) { gi.dprintf("%d unassigned players present!\n", unassigned); } }
/* =========== 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; char *userInfoError; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; gentity_t *ent; char reason[ MAX_STRING_CHARS ] = {""}; int i; ent = &g_entities[ clientNum ]; client = &level.clients[ clientNum ]; // ignore if client already connected if( client->pers.connected != CON_DISCONNECTED ) return NULL; ent->client = client; memset( client, 0, sizeof( *client ) ); trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); value = Info_ValueForKey( userinfo, "cl_guid" ); Q_strncpyz( client->pers.guid, value, sizeof( client->pers.guid ) ); value = Info_ValueForKey( userinfo, "ip" ); // check for local client if( !strcmp( value, "localhost" ) ) client->pers.localClient = qtrue; G_AddressParse( value, &client->pers.ip ); client->pers.admin = G_admin_admin( client->pers.guid ); // check for admin ban if( G_admin_ban_check( ent, reason, sizeof( reason ) ) ) { return va( "%s", reason ); } // check for a password value = Info_ValueForKey( userinfo, "password" ); if( g_password.string[ 0 ] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value ) != 0 ) return "Invalid password"; // add guid to session so we don't have to keep parsing userinfo everywhere for( i = 0; i < sizeof( client->pers.guid ) - 1 && isxdigit( client->pers.guid[ i ] ); i++ ); if( i < sizeof( client->pers.guid ) - 1 ) return "Invalid GUID"; for( i = 0; i < level.maxclients; i++ ) { if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) continue; if( !Q_stricmp( client->pers.guid, level.clients[ i ].pers.guid ) ) { if( !G_ClientIsLagging( level.clients + i ) ) { trap_SendServerCommand( i, "cp \"Your GUID is not secure\"" ); return "Duplicate GUID"; } trap_DropClient( i, "Ghost" ); } } client->pers.connected = CON_CONNECTING; // read or initialize the session data if( firstTime || level.newSession ) G_InitSessionData( client, userinfo ); G_ReadSessionData( client ); // get and distribute relevent paramters G_namelog_connect( client ); userInfoError = ClientUserinfoChanged( clientNum, qfalse ); if( userInfoError != NULL ) return userInfoError; G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\" \"%c%s%c^7\"\n", clientNum, client->pers.ip.str, client->pers.guid, client->pers.netname, DECOLOR_OFF, client->pers.netname, DECOLOR_ON ); // don't do the "xxx connected" messages if they were caried over from previous level if( firstTime ) trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname ) ); if( client->pers.admin ) G_admin_authlog( ent ); // count current clients and rank for scoreboard CalculateRanks( ); // if this is after !restart keepteams or !restart switchteams, apply said selection if ( client->sess.restartTeam != TEAM_NONE ) { G_ChangeTeam( ent, client->sess.restartTeam ); client->sess.restartTeam = TEAM_NONE; } return NULL; }
/** * @brief Local func to actual do skill upgrade, used by both MP skill system, and SP scripted skill system * @param[in,out] ent * @param[in] skill */ void G_UpgradeSkill(gentity_t *ent, skillType_t skill) { int i; #ifdef FEATURE_LUA // *LUA* API callbacks if (G_LuaHook_UpgradeSkill(g_entities - ent, skill)) { return; } #endif // See if this is the first time we've reached this skill level for (i = 0; i < SK_NUM_SKILLS; i++) { if (i == skill) { continue; } if (ent->client->sess.skill[skill] <= ent->client->sess.skill[i]) { break; } } G_DebugAddSkillLevel(ent, skill); #ifdef FEATURE_RATING if (g_skillRating.integer) { ent->client->sess.rank = (int)(MAX(ent->client->sess.mu - 3 * ent->client->sess.sigma, 0.f) / (2 * MU) * NUM_EXPERIENCE_LEVELS); if (ent->client->sess.rank > 10) { ent->client->sess.rank = 10; } } else { #endif if (i == SK_NUM_SKILLS) { // increase rank ent->client->sess.rank++; } if (ent->client->sess.rank >= 4) { int cnt = 0; // count the number of maxed out skills for (i = 0; i < SK_NUM_SKILLS; i++) { if (ent->client->sess.skill[i] >= 4) { cnt++; } } ent->client->sess.rank = cnt + 3; if (ent->client->sess.rank > 10) { ent->client->sess.rank = 10; } } #ifdef FEATURE_RATING } #endif ClientUserinfoChanged(ent - g_entities); // Give em rightaway if (skill == SK_BATTLE_SENSE && ent->client->sess.skill[skill] == 1) { AddWeaponToPlayer(ent->client, WP_BINOCULARS, 1, 0, qfalse); ent->client->ps.stats[STAT_KEYS] |= (1 << INV_BINOCS); } else if (skill == SK_FIRST_AID && ent->client->sess.playerType == PC_MEDIC && ent->client->sess.skill[skill] == 4) { AddWeaponToPlayer(ent->client, WP_MEDIC_ADRENALINE, ent->client->ps.ammo[GetWeaponTableData(WP_MEDIC_ADRENALINE)->ammoIndex], ent->client->ps.ammoclip[GetWeaponTableData(WP_MEDIC_ADRENALINE)->clipIndex], qfalse); } }
/* =========== 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 < MAX_ITEMS; n++) { if (itemlist[n].flags & IT_KEY) resp.coop_respawn.inventory[n] = client->pers.inventory[n]; } 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); else if (Q_stricmp(level.mapname, "zboss") == 0) { char userinfo[MAX_INFO_STRING]; int health = client->pers.health; memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); InitClientPersistant(client); ClientUserinfoChanged (ent, userinfo); client->pers.health = health; } 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; #ifdef WITH_ACEBOT // ACEBOT_ADD ent->is_bot = false; ent->last_node = -1; ent->is_jumping = false; // ACEBOT_END #endif 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.skinnum = ent - g_edicts - 1; ent->s.modelindex = 255; // will use the skin specified model ent->s.modelindex2 = 255; // custom gun model ent->s.frame = 0; #ifdef WITH_ACEBOT //botchat> ent->last_insult = level.time; ent->last_taunt = level.time; ent->last_chat = level.time; //<botchat #endif 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); if (!KillBox (ent)) { // could't spawn in? } gi.linkentity (ent); // force the current weapon up client->newweapon = client->pers.weapon; ChangeWeapon (ent); }
/* =========== ClientDisconnect Called when a player drops from the server. Will not be called between levels. This should NOT be called directly by any game logic, call trap_DropClient(), which will call this and do server system housekeeping. ============ */ void ClientDisconnect( int clientNum ) { gentity_t *ent; gentity_t *tent; int i; // cleanup if we are kicking a bot that // hasn't spawned yet G_RemoveQueuedBotBegin( clientNum ); ent = g_entities + clientNum; if ( !ent->client ) { return; } // stop any following clients for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].sess.sessionTeam == TEAM_SPECTATOR && level.clients[i].sess.spectatorState == SPECTATOR_FOLLOW && level.clients[i].sess.spectatorClient == clientNum ) { StopFollowing( &g_entities[i] ); } } // send effect if they were completely connected if ( ent->client->pers.connected == CON_CONNECTED && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.clientNum = ent->s.clientNum; // They don't get to take powerups with them! // Especially important for stuff like CTF flags TossClientItems( ent ); #ifdef MISSIONPACK TossClientPersistantPowerups( ent ); if( g_gametype.integer == GT_HARVESTER ) { TossClientCubes( ent ); } #endif } G_LogPrintf( "ClientDisconnect: %i\n", clientNum ); // if we are playing in tourney mode and losing, give a win to the other player if ( (g_gametype.integer == GT_TOURNAMENT ) && !level.intermissiontime && !level.warmupTime && level.sortedClients[1] == clientNum ) { level.clients[ level.sortedClients[0] ].sess.wins++; ClientUserinfoChanged( level.sortedClients[0] ); } trap_UnlinkEntity (ent); ent->s.modelindex = 0; ent->inuse = qfalse; ent->classname = "disconnected"; ent->client->pers.connected = CON_DISCONNECTED; ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE; ent->client->sess.sessionTeam = TEAM_FREE; trap_SetConfigstring( CS_PLAYERS + clientNum, ""); CalculateRanks(); if ( ent->r.svFlags & SVF_BOT ) { BotAIShutdownClient( clientNum, qfalse ); } }
/////////////////////////////////////////////////////////////////////// // Modified version of id's code /////////////////////////////////////////////////////////////////////// void ACESP_PutClientInServer(edict_t *bot, qboolean respawn, int team) { vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; client_persistant_t saved; client_respawn_t resp; char *s; int spawn_style; int spawn_health; // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client SelectSpawnPoint (bot, spawn_origin, spawn_angles, &spawn_style, &spawn_health); index = bot-g_edicts-1; client = bot->client; // deathmatch wipes most client data every spawn if (deathmatch->value) { char userinfo[MAX_INFO_STRING]; resp = bot->client->resp; memcpy (userinfo, client->pers.userinfo, sizeof(userinfo)); InitClientPersistant (client, spawn_style); ClientUserinfoChanged (bot, userinfo); } else memset(&resp, 0, sizeof(resp)); // clear everything but the persistant data saved = client->pers; memset(client, 0, sizeof(*client)); client->pers = saved; client->resp = resp; // copy some data from the client to the entity FetchClientEntData (bot); // clear entity values bot->groundentity = NULL; bot->client = &game.clients[index]; bot->takedamage = DAMAGE_AIM; bot->movetype = MOVETYPE_WALK; bot->viewheight = 24; bot->classname = "bot"; bot->mass = 200; bot->solid = SOLID_BBOX; bot->deadflag = DEAD_NO; bot->air_finished = level.time + 12; bot->clipmask = MASK_PLAYERSOLID; bot->model = "players/male/tris.md2"; bot->pain = player_pain; bot->die = player_die; bot->waterlevel = 0; bot->watertype = 0; bot->flags &= ~FL_NO_KNOCKBACK; bot->svflags &= ~SVF_DEADMONSTER; bot->is_jumping = false; if (ctf->value) { client->resp.ctf_team = team; client->resp.ctf_state = CTF_STATE_START; s = Info_ValueForKey(client->pers.userinfo, "skin"); CTFAssignSkin(bot, s); } VectorCopy(mins, bot->mins); VectorCopy(maxs, bot->maxs); VectorClear(bot->velocity); // clear playerstate values memset(&bot->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; //ZOID client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; //ZOID 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; } // Knightmare- fix for null model? if (client->pers.weapon && client->pers.weapon->view_model) client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model); // clear entity state values bot->s.effects = 0; bot->s.skinnum = bot - g_edicts - 1; bot->s.modelindex = MAX_MODELS-1; // will use the skin specified model bot->s.modelindex2 = MAX_MODELS-1; // custom gun model bot->s.frame = 0; VectorCopy(spawn_origin, bot->s.origin); bot->s.origin[2] += 1; // make sure off ground // set the delta angle for (int i = 0; i < 3; i++) client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]); bot->s.angles[PITCH] = 0; bot->s.angles[YAW] = spawn_angles[YAW]; bot->s.angles[ROLL] = 0; VectorCopy(bot->s.angles, client->ps.viewangles); VectorCopy(bot->s.angles, client->v_angle); // force the current weapon up client->newweapon = client->pers.weapon; ChangeWeapon (bot); bot->enemy = NULL; bot->movetarget = NULL; bot->state = STATE_MOVE; // Set the current node bot->current_node = ACEND_FindClosestReachableNode(bot,NODE_DENSITY, NODE_ALL); bot->goal_node = bot->current_node; bot->next_node = bot->current_node; bot->next_move_time = level.time; bot->suicide_timeout = level.time + 15.0; // If we are not respawning hold off for up to three seconds before releasing into game if (!respawn) { bot->think = ACESP_HoldSpawn; //bot->nextthink = level.time + 0.1; //mxd bot->nextthink = level.time + random()*3.0f; // up to three seconds } else { if (!KillBox(bot)) { // could't spawn in? } gi.linkentity(bot); bot->think = ACEAI_Think; bot->nextthink = level.time + FRAMETIME; // send effect gi.WriteByte(svc_muzzleflash); gi.WriteShort(bot-g_edicts); gi.WriteByte(MZ_LOGIN); gi.multicast(bot->s.origin, MULTICAST_PVS); } }
/* =========== 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, qboolean isBot ) { char *value; // char *areabits; gclient_t *client; char userinfo[MAX_INFO_STRING]; gentity_t *ent; ent = &g_entities[ clientNum ]; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // IP filtering // https://zerowing.idsoftware.com/bugzilla/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_FilterPacket( value ) ) { return "You are banned from this server."; } // 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 ( !( ent->r.svFlags & SVF_BOT ) && (strcmp(value, "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) { return "Invalid password"; } } // they can connect ent->client = level.clients + clientNum; client = ent->client; // areabits = client->areabits; memset( client, 0, sizeof(*client) ); client->pers.connected = CON_CONNECTING; // read or initialize the session data if ( firstTime || level.newSession ) { G_InitSessionData( client, userinfo ); } G_ReadSessionData( client ); if( isBot ) { ent->r.svFlags |= SVF_BOT; ent->inuse = qtrue; if( !G_BotConnect( clientNum, !firstTime ) ) { return "BotConnectfailed"; } } // get and distribute relevent paramters G_LogPrintf( "ClientConnect: %i\n", clientNum ); ClientUserinfoChanged( clientNum ); // don't do the "xxx connected" messages if they were caried over from previous level if ( firstTime ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname) ); } if ( g_gametype.integer >= GT_TEAM && client->sess.sessionTeam != TEAM_SPECTATOR ) { BroadcastTeamChange( client, -1 ); } // count current clients and rank for scoreboard CalculateRanks(); // for statistics // client->areabits = areabits; // if ( !client->areabits ) // client->areabits = G_Alloc( (trap_AAS_PointReachabilityAreaIndex( NULL ) + 7) / 8 ); return NULL; }