/* AMX Mod (X) * PsychoStats Interface Plugin * * by Jason Morriss * and JTP10181 * http://www.psychostats.com/ * * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * In addition, as a special exception, the author gives permission to * link the code of this program with the Half-Life Game Engine ("HL * Engine") and Modified Game Libraries ("MODs") developed by Valve, * L.L.C ("Valve"). You must obey the GNU General Public License in all * respects for all of the code used other than the HL Engine and MODs * from Valve. If you modify this file, you may extend this exception * to your version of the file, but you are not obligated to do so. If * you do not wish to do so, delete this exception statement from your * version. */ //Do Not TOUCH. This is for debugging only. //#define DEBUG //#define MYSQLDEBUG #if defined DEBUG new debug_authid[] = "STEAM_0:0:123456"; new debug_name[] = "Stormtrooper"; new debug_ipaddr[] = "0.0.0.0"; #endif // *** DO NOT EDIT BELOW HERE *** #include #if defined AMXX_VERSION #include #include #else #include #include #endif #include // GLOBAL VARIABLES new ps_version[] = "1.3"; new pshost[64], psuser[32], pspass[32], psdb[32], plrtbl[32], plrprofiletbl[48], psuniqueid[16], psurl[255], psrankurl[255], psadminpw[64]; new plriplogged[33]; public plugin_init() { register_plugin("PsychoStats PIP", ps_version, "Stormtrooper"); register_cvar("ps_version", ps_version); register_cvar("ps_db_host","127.0.0.1"); register_cvar("ps_db_user","ps2"); register_cvar("ps_db_pass","ps2"); register_cvar("ps_db_name","ps_stats"); register_cvar("ps_db_plrtable","pstats_plr"); register_cvar("ps_stats_url",""); register_cvar("ps_rank_url",""); register_cvar("ps_uniqueid","worldid"); register_cvar("ps_admin_password",""); register_cvar("ps_logip","1"); register_concmd("ps_password", "cmdPassword", 0, "[] - sets your password in the PsychoStats database"); register_concmd("ps_setadmin", "cmdSetAdmin", ADMIN_RCON, " - sets you as ADMIN in the Psychostats database"); register_concmd("ps_admin", "cmdSetAdmin", ADMIN_RCON, " - sets you as ADMIN in the Psychostats database"); register_concmd("ps_username", "cmdUsername", 0, " - sets your username in the PsychoStats database"); register_clcmd("say", "HandleSay"); #if defined AMXX_VERSION new configsDir[128]; get_configsdir(configsDir, 127); server_cmd("exec %s/ps.cfg", configsDir); #else server_cmd("exec addons/amx/ps.cfg"); #endif } public plugin_cfg() { //Forces server to clear the exec buffer and load all the cvars server_exec(); } //Called by ps_setadmin, to give yourself admin access on the stats. public cmdSetAdmin(id,level,cid) { if (!cmd_access(id,level,cid,2)) return PLUGIN_HANDLED; new cmd[128], error[256], curacl[4], plrmatch[65], authid[33], name[65], ipaddr[16], plrid[16], userpw[65]; load_pscvars(); // Load our config read_argv(1, userpw, 64); // Get the password specified by the user // Load the user information _getuserinfo(id, authid, name, ipaddr, plrmatch); if (equal(psadminpw, "")) { console_print(id, "[PsychoStats] Server admin has disabled this feature."); return PLUGIN_HANDLED; } console_print(id, "[PsychoStats] Setting ADMIN flag for %s '%s'", psuniqueid, plrmatch); if (!equal(psadminpw,userpw)) { console_print(id, "[PsychoStats] Invalid admin password."); return PLUGIN_HANDLED; } // Connect to mysql server #if defined AMXX_VERSION new Sql:dbh = dbi_connect(pshost, psuser, pspass, psdb, error, 255); new Result:result if (dbh <= SQL_FAILED ) { #else new dbh = mysql_connect(pshost, psuser, pspass, psdb, error, 255); if (dbh < 1) { #endif console_print(id, "[PsychoStats] MYSQL connect error. Contact your admin: '%s'", error); server_print("[PsychoStats] MYSQL connect error: '%s' (%s,%s,%s)", error, pshost, psuser, psdb); return PLUGIN_HANDLED; } format(cmd, 127, "SELECT plrid, accesslevel FROM %s WHERE %s='%s'", plrtbl, psuniqueid, plrmatch); #if defined AMXX_VERSION if ( (result = _query(dbh, cmd, id)) == RESULT_FAILED ) return PLUGIN_HANDLED; if (dbi_nextrow(result) > 0) { dbi_field(result, 1, plrid, 15); // Player ID matching the STEAMID dbi_field(result, 2, curacl, 3); // user's current accesslevel } dbi_free_result(result) #else if (!_query(dbh, cmd, id)) return PLUGIN_HANDLED; if (mysql_nextrow(dbh) > 0) { mysql_getfield(dbh, 1, plrid, 15); // Player ID matching the STEAMID mysql_getfield(dbh, 2, curacl, 3); // user's current accesslevel } #endif // If no plrid was returned then there is no match in the database for the current user if (equal(plrid,"")) { console_print(id, "[PsychoStats] No player matches your %s '%s' in the database yet.", psuniqueid, plrmatch); console_print(id, "[PsychoStats] Please try again after the server stats have updated."); return PLUGIN_HANDLED; } format(cmd,127,"UPDATE %s SET accesslevel=10 WHERE plrid='%s'", plrtbl, plrid); #if defined AMXX_VERSION if ( (result = _query(dbh, cmd, id)) == RESULT_FAILED ) return PLUGIN_HANDLED; #else if (!_query(dbh, cmd, id)) return PLUGIN_HANDLED; #endif console_print(id, "[PsychoStats] Accesslevel successfully updated for %s", plrmatch); if (!equal(psurl,"")) console_print(id, "[PsychoStats] Game Stats: %s", psurl); #if defined AMXX_VERSION dbi_close(dbh); #else mysql_close(dbh); #endif return PLUGIN_HANDLED; } //Called by ps_username, to assign a username to a player profile public cmdUsername(id,level,cid) { if (!cmd_access(id,level,cid,2)) return PLUGIN_HANDLED; new cmd[128], error[256], plrmatch[65], authid[33], name[65], ipaddr[16], plrid[16], curpw[33], username[65]; new sqlpw[33], pwhash[33], dbpw[33], sqluser[71]; load_pscvars(); // Load our config // Load the user information _getuserinfo(id, authid, name, ipaddr, plrmatch); new args = read_argc() - 1; // Total arg's given if (args > 1) { // If we got more than 1 we have read_argv(1, curpw, 32); read_argv(2, username, 64); } else { // we only have 1 console_print(id, "[PsychoStats] You must provide your password and new username."); return PLUGIN_HANDLED; } console_print(id, "[PsychoStats] Changing username for %s '%s'", psuniqueid, plrmatch); // Connect to mysql server #if defined AMXX_VERSION new Sql:dbh = dbi_connect(pshost, psuser, pspass, psdb, error, 255); new Result:result if (dbh <= SQL_FAILED ) { #else new dbh = mysql_connect(pshost, psuser, pspass, psdb, error, 255); if (dbh < 1) { #endif console_print(id, "[PsychoStats] MYSQL connect error. Contact your admin: '%s'", error); server_print("[PsychoStats] MYSQL connect error: '%s' (%s,%s,%s)", error, pshost, psuser, psdb); return PLUGIN_HANDLED; } // determine if the username specified already exists or not copy(sqluser,64,username); quote_sql(sqluser, 70); format(cmd, 127, "SELECT plrid FROM %s WHERE `username`='%s' LIMIT 1", plrtbl, sqluser); #if defined AMXX_VERSION if ( (result = _query(dbh, cmd, id)) == RESULT_FAILED ) return PLUGIN_HANDLED; if (dbi_nextrow(result) > 0) { dbi_field(result, 1, plrid, 16); // Player ID matching the STEAMID } dbi_free_result(result) #else if (!_query(dbh, cmd, id)) return PLUGIN_HANDLED; if (mysql_nextrow(dbh) > 0) { mysql_getfield(dbh, 1, plrid, 16); // Player ID matching the STEAMID } #endif if (!equal(plrid,"")) { console_print(id, "[PsychoStats] Username '%s' already exists. Please try another name.", username); return PLUGIN_HANDLED; } // Since I don't know of any md5 functions in the AMX code, i'll just let the SQL server do it for me // We're converting the curpw that the user gave into a hash so we can compare it with the current pw // We also load the current password for the user here copy(sqlpw,32,curpw); quote_sql(sqlpw, 32); format(cmd, 127, "SELECT plrid, `password`, MD5('%s') as pw FROM %s WHERE %s='%s' LIMIT 1", sqlpw, plrtbl, psuniqueid, plrmatch); #if defined AMXX_VERSION if ( (result = _query(dbh, cmd, id)) == RESULT_FAILED ) return PLUGIN_HANDLED; if (dbi_nextrow(result) > 0) { dbi_field(result, 1, plrid, 16); // Player ID matching the STEAMID dbi_field(result, 2, dbpw, 32); // user's current pw (might be blank) dbi_field(result, 3, pwhash, 32); // the converted curpw (it's an md5 hash now) } dbi_free_result(result) #else if (!_query(dbh, cmd, id)) return PLUGIN_HANDLED; if (mysql_nextrow(dbh) > 0) { mysql_getfield(dbh, 1, plrid, 16); // Player ID matching the STEAMID mysql_getfield(dbh, 2, dbpw, 32); // user's current pw (might be blank) mysql_getfield(dbh, 3, pwhash, 32); // the converted curpw (it's an md5 hash now) } #endif // If no plrid was returned then there is no match in the database for the current user if (equal(plrid,"")) { console_print(id, "[PsychoStats] No player matches your %s '%s' in the database yet.", psuniqueid, plrmatch); console_print(id, "[PsychoStats] Please try again after the server stats have updated."); return PLUGIN_HANDLED; } // we must verify they supplied a valid password if (!equali(pwhash, dbpw)) { // both pw's are md5 hashes console_print(id, "[PsychoStats] Authentication failure for %s", authid); console_print(id, "[PsychoStats] Passwords do not match!"); console_print(id, "[PsychoStats] Usage: ps_username "); return PLUGIN_HANDLED; } copy(sqluser,64,username); quote_sql(sqluser, 70); format(cmd,127,"UPDATE %s SET `username`='%s' WHERE plrid='%s'", plrtbl, sqluser, plrid); #if defined AMXX_VERSION if ( (result = _query(dbh, cmd, id)) == RESULT_FAILED ) return PLUGIN_HANDLED; #else if (!_query(dbh, cmd, id)) return PLUGIN_HANDLED; #endif console_print(id, "[PsychoStats] Username successfully updated for %s '%s'", psuniqueid, plrmatch); if (!equal(psurl,"")) console_print(id, "[PsychoStats] Game Stats: %s", psurl); #if defined AMXX_VERSION dbi_close(dbh); #else mysql_close(dbh); #endif return PLUGIN_HANDLED; } //Called by ps_password, clients can set thier stats password public cmdPassword(id,level,cid) { if (!cmd_access(id,level,cid,2)) return PLUGIN_HANDLED; new cmd[128], error[256], plrmatch[65], authid[33], name[65], ipaddr[16], plrid[16], oldpw[33], curpw[33], newpw[33]; new sqlpw[33]; load_pscvars(); // Load our config // Load the user information _getuserinfo(id, authid, name, ipaddr, plrmatch); new args = read_argc() - 1; // Total arg's given if (args > 1) { // If we got more than 1 we have read_argv(1, oldpw, 32); read_argv(2, newpw, 32); } else { // we only have 1 read_argv(1, newpw, 32); } new msg[255]; msg = (args == 1) ? "[PsychoStats] Setting initial user password for player matching %s '%s'" : "[PsychoStats] Changing password for player matching %s '%s'"; console_print(id, msg, psuniqueid, plrmatch); // Connect to mysql server #if defined AMXX_VERSION new Sql:dbh = dbi_connect(pshost, psuser, pspass, psdb, error, 255); new Result:result if (dbh <= SQL_FAILED ) { #else new dbh = mysql_connect(pshost, psuser, pspass, psdb, error, 255); if (dbh < 1) { #endif console_print(id, "[PsychoStats] MYSQL connect error. Contact your admin: '%s'", error); server_print("[PsychoStats] MYSQL connect error: '%s' (%s,%s,%s)", error, pshost, psuser, psdb); return PLUGIN_HANDLED; } // Since I don't know of any md5 functions in the AMX code, i'll just let the SQL server do it for me // We're converting the oldpw that the user gave into a hash so we can compare it with the current pw // We also load the current password for the user here copy(sqlpw,32,oldpw); quote_sql(sqlpw, 32); format(cmd, 127, "SELECT plrid, password, MD5('%s') as pw FROM %s WHERE %s='%s' LIMIT 1", sqlpw, plrtbl, psuniqueid, plrmatch); #if defined AMXX_VERSION if ( (result = _query(dbh, cmd, id)) == RESULT_FAILED ) return PLUGIN_HANDLED; if (dbi_nextrow(result) > 0) { dbi_field(result, 1, plrid, 16); // Player ID matching the STEAMID dbi_field(result, 2, curpw, 32); // user's current pw (might be blank) dbi_field(result, 3, oldpw, 32); // the converted oldpw (it's an md5 hash now) } dbi_free_result(result); #else if (!_query(dbh, cmd, id)) return PLUGIN_HANDLED; if (mysql_nextrow(dbh) > 0) { mysql_getfield(dbh, 1, plrid, 16); // Player ID matching the STEAMID mysql_getfield(dbh, 2, curpw, 32); // user's current pw (might be blank) mysql_getfield(dbh, 3, oldpw, 32); // the converted oldpw (it's an md5 hash now) } #endif // If no plrid was returned then there is no match in the database for the current user if (equal(plrid,"")) { console_print(id, "[PsychoStats] No player matches your %s '%s' in the database yet.", psuniqueid, plrmatch); console_print(id, "[PsychoStats] Please try again after the server stats have updated."); return PLUGIN_HANDLED; } // If there's a current password for the user we must verify they supplied a valid password if (!equal(curpw, "")) { if (args == 1) { console_print(id, "[PsychoStats] Authentication failure for %s", authid); console_print(id, "[PsychoStats] You must provide your current password in order to change it. If you don't know what it is please contact your admin."); console_print(id, "[PsychoStats] Usage: %s %s", "ps_password", " ") return PLUGIN_HANDLED; } else if (!equali(curpw, oldpw)) { // both pw's are md5 hashes console_print(id, "[PsychoStats] Authentication failure for %s", authid); console_print(id, "[PsychoStats] Passwords do not match!"); console_print(id, "[PsychoStats] Usage: %s %s", "ps_password", "[] ") return PLUGIN_HANDLED; } } copy(sqlpw,32,newpw); quote_sql(sqlpw, 32); format(cmd,127,"UPDATE %s SET `password`=MD5('%s') WHERE plrid='%s'", plrtbl, sqlpw, plrid); #if defined AMXX_VERSION if ( (result = _query(dbh, cmd, id)) == RESULT_FAILED ) return PLUGIN_HANDLED; #else if (!_query(dbh, cmd, id)) return PLUGIN_HANDLED; #endif console_print(id, "[PsychoStats] Password successfully updated for %s '%s'", psuniqueid, plrmatch); if (!equal(psurl,"")) console_print(id, "[PsychoStats] Game Stats: %s", psurl); #if defined AMXX_VERSION dbi_close(dbh); #else mysql_close(dbh); #endif return PLUGIN_HANDLED; } //This code is all for the PSRank in game MOTD popups public HandleSay(id) { new Speech[128]; read_args(Speech,127); remove_quotes(Speech); load_pscvars(); /* register_clcmd("say /statsme","cmdStatsMe",0,"- displays your stats") register_clcmd("say /stats","cmdStats",0,"- displays others stats") register_clcmd("say /top15","cmdTop15",0,"- displays top 15 players") register_clcmd("say /rank","cmdRank",0,"- displays your server stats") */ if(containi(Speech, "/psstats") == 0) { show_motd(id, psrankurl, "Stats: Powered by PsychoStats"); return PLUGIN_HANDLED; } if(containi(Speech, "/pstop15") == 0 || containi(Speech, "/pstop10") == 0) { new smalltt[256]; format (smalltt,255,"%s/smalltopten.php",psrankurl); show_motd(id, smalltt, "Top 10: Powered by PsychoStats"); return PLUGIN_HANDLED; } if(containi(Speech, "/psrank") == 0 || containi(Speech, "/psstatsme") == 0) { new cmd[128], error[255], rankurl[256], plrmatch[65], authid[33], name[65], ipaddr[16], plrid[16]; _getuserinfo(id, authid, name, ipaddr, plrmatch); // Connect to mysql server #if defined AMXX_VERSION new Sql:dbh = dbi_connect(pshost, psuser, pspass, psdb, error, 255); new Result:result if (dbh <= SQL_FAILED ) { #else new dbh = mysql_connect(pshost, psuser, pspass, psdb, error, 255); if (dbh < 1) { #endif console_print(id, "[PsychoStats] MYSQL connect error. Contact your admin: '%s'", error); server_print("[PsychoStats] MYSQL connect error: '%s' (%s,%s,%s)", error, pshost, psuser, psdb); return PLUGIN_HANDLED; } format(cmd, 127, "SELECT plrid FROM %s WHERE %s='%s'", plrtbl, psuniqueid, plrmatch); #if defined AMXX_VERSION if ( (result = _query(dbh, cmd, id)) == RESULT_FAILED ) return PLUGIN_HANDLED; if (dbi_nextrow(result) > 0) { dbi_field(result, 1, plrid, 15); } dbi_free_result(result); #else if (!_query(dbh, cmd, id)) return PLUGIN_HANDLED; if (mysql_nextrow(dbh) > 0) { mysql_getfield(dbh, 1, plrid, 15); } #endif if (equal(plrid,"")) { client_print(id,print_chat,"[PsychoStats] No player matches your %s '%s' in the database.", psuniqueid, plrmatch); format(rankurl,255,"%s/index.php?search=%s",psrankurl, plrmatch); } else { format(rankurl,255,"%s/player.php?id=%s",psrankurl,plrid); } show_motd(id, rankurl, "Rank: Powered by PsychoStats") return PLUGIN_HANDLED; } if(containi(Speech, "/search") == 0) { new arg1[32], arg2[32]; parse(Speech,arg1,31,arg2,31); new rankurl[256]; format(rankurl,255,"%s/index.php?search=%s",psrankurl,arg2); show_motd(id, rankurl, "Stats Search: Powered by PsychoStats"); return PLUGIN_HANDLED; } return PLUGIN_CONTINUE; } //Below is all code to assist in IP tracking and make it more reliable public logip(id) { if (!plriplogged[id] && get_cvar_num("ps_logip")) { new name[32], authid[32], team[32], address[32] get_user_name(id,name,31); get_user_authid(id,authid,31); get_user_team(id,team,31); get_user_ip(id,address,31); if (equal(authid,"STEAM_ID_PENDING")) return PLUGIN_CONTINUE if (get_user_userid(id) == -1) return PLUGIN_CONTINUE if (equal(address,"")) return PLUGIN_CONTINUE if (equal(team, "UNASSIGNED")) copy(team,31,"") log_message("^"%s<%d><%s><%s>^" triggered ^"address^" (address ^"%s^")", name,get_user_userid(id),authid,team,address); plriplogged[id] = 1; } return PLUGIN_CONTINUE } public client_disconnect(id) { //Only need to log it again if the user did something on the server if ((get_user_frags(id) > 0) || (get_user_deaths(id) > 0) || (get_user_time(id,1) > 30)) { //Set to 0 to make it log it agian plriplogged[id] = 0; logip(id); } //Set to 0 to make sure next client with this id gets logged plriplogged[id] = 0; } public client_connect(id) { plriplogged[id] = 0; logip(id); } public client_putinserver(id) { logip(id); } public client_infochanged(id) { logip(id); } public client_authorized(id) { logip(id); } // Support functions // This can be called from another function to load the cvars into variables load_pscvars() { get_cvar_string("ps_db_host", pshost, 63); get_cvar_string("ps_db_user", psuser, 31); get_cvar_string("ps_db_pass", pspass, 31); get_cvar_string("ps_db_name", psdb, 31); get_cvar_string("ps_db_plrtable", plrtbl, 31); get_cvar_string("ps_stats_url", psurl, 254); get_cvar_string("ps_rank_url", psrankurl, 254); get_cvar_string("ps_admin_password", psadminpw, 63); get_cvar_string("ps_uniqueid", psuniqueid, 15); format(plrprofiletbl, 47, "%s%s", plrtbl, "_profile"); // pstats_plr_profile if (equal(psrankurl,"")) copy(psrankurl, 254, psurl); } // Load the users information public _getuserinfo(id, authid[], name[], ipaddr[], plrmatch[]) { #if defined DEBUG copy(authid, 32, debug_authid); copy(name, 64, debug_name); copy(ipaddr, 15, debug_ipaddr); #else get_user_authid(id, authid, 32); get_user_name(id, name, 64); get_user_ip(id, ipaddr, 15, 1); // 1 = do not include port #endif // Determine which field we want to match players on and store it in 'plrmatch' if (equali(psuniqueid, "name")) { copy(plrmatch, 64, name); } else if (equali(psuniqueid, "ipaddr")) { copy(plrmatch, 15, ipaddr); } else { // always default to 'worldid' copy(plrmatch, 33, authid); } } // short-cut function for performing a mysql_query. Returns false on error. #if defined AMXX_VERSION public Result:_query(Sql:dbh, cmd[], id) { new Result:result = dbi_query(dbh, cmd); if (result == RESULT_FAILED) { new error[255]; dbi_error(dbh, error, 254); console_print(id, "[PsychoStats] SQL error. Contact your admin: '%s'", error); server_print("[PsychoStats] SQL error: '%s' (%s,%s,%s)", error, pshost, psuser, psdb); } return result; } #else public _query(dbh, cmd[], id) { if (mysql_query(dbh, cmd) < 1) { new error[255]; mysql_error(dbh, error, 254); console_print(id, "[PsychoStats] SQL error. Contact your admin: '%s'", error); server_print("[PsychoStats] SQL error: '%s' (%s,%s,%s)", error, pshost, psuser, psdb); return false; } return true; } #endif // quotes the string given to be used safely in a mysql_query() call quote_sql(string[],len) { new charnum = 0; while ( replace( string[charnum] ,len,"'","\'") != 0) { charnum += contain(string[charnum],"\'") + 2; } while ( replace( string[charnum] ,len,"`","\`") != 0) { charnum += contain(string[charnum],"\`") + 2; } }