Valve Developer Community:Sandbox

From Valve Developer Community
Revision as of 14:44, 28 November 2011 by Pizzahut (talk | contribs) (remember_the_names_2.sma)
Jump to navigation Jump to search

The Sandbox (Valve Developer Community:Sandbox) is a VDC namespace page designed for testing and experimenting with Wiki syntax. Feel free to try your skills at formatting here: click on edit, make your changes, and click 'Save page' when you are finished. Content added here will not stay permanently.

Please do not edit the "markup" text, at the top of the editing window, that reads "{{sandbox}}".


  1. define PLUGINNAME "Remember the names"
  1. define VERSION "0.4.2"
  1. define AUTHOR "JGHG"

/*

Copyleft 2005

Plugin topic: http://www.amxmodx.org/forums/viewtopic.php?p=92039


PLUGINNAME

==

Remembers names used by players.


In console you can type "amx_names <playername>" and you will have a list of the ten most common names ever used by that player.

The names are ordered by frequency and last time of use.


When someone enters the server a welcome msg is displayed to all clients (or admins only depending on compile).

If someone enters with a name less common than previously used names for that player,

the welcome msg will also contain up to three names most commonly used by the player.


Example: If I play 20 maps with the name "JGHG" and then 10 maps with "Johnny got his gun", and I enter another map

with the name "Faken Icker" the welcome msg should look like "We welcome Faken Icker, aka: JGHG, Johnny got his gun".


Add these sql info cvars to sql.cfg:

rtn_host "127.0.0.1"

rtn_user "myusername"

rtn_pass "thesecretpassword"

rtn_db "thedb"


You can replace ADMIN_CFG with whatever access level you want below. This access is a must to use "amx_names".

  • /
  1. define ACCESS_AMXNAMES ADMIN_KICK // check amxconst.inc for the other access flags...

/*

You can also replace ADMIN_ADMIN below with whatever access level you want to be required to get the welcome msg about a user logging in...

  • /
  1. define ACCESS_WELCOMEMSG ADMIN_KICK // you can replace ADMIN_CFG with ADMIN_ALL and everyone will receive the welcome msg

/*

Note to SQLITE module users:

If you're using this module it doesn't matter what you write in ANY of the cvars BUT rtn_db, in fact that is the only cvar that will exist.

The rtn_db cvar should contain the full path to the db file from your top server directory, one step above the mod dir.

Ie if you set rtn_db to "cstrike/addons/amxmodx/data/rememberthenames.db" it will end up as that file in that directory.


/JGHG



VERSIONS

==

070519 0.4.2 Fixed ' going straight to database engine

050330 0.4.1 Attempt to work out bugs.

050330 0.4 Fixed some issues with displaying names.

'amx_names' now displays the ten most common names used by specified player.

You can now also define (at compile time) what access will be required to recieve the welcome msgs.

050113 0.3.1 Test version made to be compatible with Sqlite module. No new features.

050112 0.3 Prints a public welcome msg when someone enters.

If someone enters with a name less common than previously used names for that player,

the welcome msg will also contain up to three names most commonly used by the player.

050111 0.2 Removed use of fakemeta, updated some stuff, fixed a real bug with displaying names... ingame names now display in a MOTD window.

050110 0.1 First version



CREDITS

=

Idea of Cryonic

  • /


  1. include <amxmodx>
  1. include <dbi>
  1. include <amxmisc>


  1. define CREATE_TABLE_STEAMIDS "CREATE TABLE IF NOT EXISTS steamids (steamid varchar(40) NOT NULL default , id int(11) NOT NULL auto_increment, PRIMARY KEY (id), UNIQUE KEY STEAMID (steamid)) TYPE=MyISAM;"
  1. define CREATE_TABLE_NAMES "CREATE TABLE IF NOT EXISTS names ( timestamp int(10) unsigned NOT NULL default '0', counter int(10) unsigned NOT NULL default '1', id int(11) NOT NULL default '0', name varchar(32) binary NOT NULL default , PRIMARY KEY (id,name)) TYPE=MyISAM; "
  1. define CREATE_TABLE_STEAMIDS_SQLITE "CREATE TABLE steamids (steamid VARCHAR NOT NULL DEFAULT UNIQUE, id INTEGER PRIMARY KEY)"
  1. define CREATE_TABLE_NAMES_SQLITE "CREATE TABLE names (timestamp INTEGER, counter INTEGER DEFAULT '1', id INTEGER, name VARCHAR BINARY NOT NULL DEFAULT )"
  1. define CVAR_HOST "rtn_host"
  1. define CVAR_USER "rtn_user"
  1. define CVAR_PASS "rtn_pass"
  1. define CVAR_DB "rtn_db"
  1. define QSIZE 511
  1. define NROFCOMMONNAMES 3
  1. define CVAR_NOADMINS "rtn_noadmins"


// Globals below

new g_query[QSIZE + 1]

new g_dbids[33] // stores the automatically generated id that is handled to each steamid by db

new g_lastname[33][64]

new bool:g_sqlite

new bool:g_inServer[33]

// Globals above


stock admin_print(access, const DESTINATION, const MSG[]) {

new const MAXPLAYERS = get_maxplayers()


new bool:mustBeAdmin

if (access & ADMIN_ADMIN) {

mustBeAdmin = true

access &= ~ADMIN_ADMIN

}

else

mustBeAdmin = false


for (new i = 1; i <= MAXPLAYERS; i++) {

// No offliners, bots or people without the required access.

if (!g_inServer[i] || !is_user_connected(i) || is_user_bot(i))

continue


if (mustBeAdmin && !is_user_admin(i))

continue


if (access > 0 && !(get_user_flags(i) & access))

continue


client_print(i, DESTINATION, MSG)

}

}


public plugin_modules() {

require_module("dbi")

}


public client_connect(id) {

g_lastname[id] = ""

g_dbids[id] = 0

g_inServer[id] = false

return PLUGIN_CONTINUE

}


public client_disconnect(id) {

g_inServer[id] = false

return PLUGIN_CONTINUE

}


jghg_free_result(Result:result) {

if (result > RESULT_NONE) {

//server_print("!!!REMOVE: %d", result)

dbi_free_result(result)

}

}


public client_authorized(id) {

if (is_user_bot(id))

return PLUGIN_CONTINUE


const LEN = 32

new steamid[LEN + 1], name[64]


get_user_authid(id, steamid, LEN)

get_user_name(id, name, 31)


new Sql:sql

if (!connect(sql))

return PLUGIN_CONTINUE


if (!getdbidfromsteamid(id, steamid, false, sql)) {

format(g_query, QSIZE, "INSERT INTO steamids (steamid) VALUES ('%s')", steamid)

//server_print("client_authorized: sending query: %s", g_query)

new Result:result = dbi_query(sql, g_query)

jghg_free_result(result)


// Do this another time to get the dbid from database.

if (!getdbidfromsteamid(id, steamid, false, sql))

log_amx("Failed getting dbid for %s (%s)!", steamid, name)

}

//server_print("client_authorized: dbid for %d is %d", id, g_dbids[id])


disconnect(sql)


return PLUGIN_CONTINUE

}


bool:getdbidfromsteamid(id, steamid[], bool:doConnect, &Sql:sql = SQL_FAILED) {

g_dbids[id] = 0

new bool:exists = false

if (doConnect) {

if (!connect(sql))

return false

}

format(g_query, QSIZE, "SELECT id FROM steamids WHERE steamid = '%s' LIMIT 1", steamid)

//server_print("getdbidfromsteamid: Does query: %s", g_query)

new Result:result = dbi_query(sql, g_query)

//server_print("Query done, result: %d", result)

if (result < RESULT_OK && dbi_check_error(sql)) {

}

else if (dbi_nextrow(result)) {

g_dbids[id] = dbi_field(result, 1)

exists = true

}

jghg_free_result(result)

if (doConnect)

dbi_close(sql)


return exists

}


name_to_db(id, name[], &Sql:sql = SQL_FAILED) {

if (g_dbids[id] == 0)

return


new bool:disconnect_ = false

if (sql == SQL_FAILED) {

disconnect_ = true

if (!connect(sql))

return

}


replace_all(name,64,"'","");


format(g_query, QSIZE, "SELECT counter FROM names WHERE id = '%d' AND name = '%s' LIMIT 1", g_dbids[id], name)


//server_print("name_to_db: Sending query: %s", g_query)

new Result:result = dbi_query(sql, g_query)

//server_print("name_to_db: result=%d rows=%d", result, dbi_num_rows(result))

new const TIMESTAMP = get_systime()


if (result < RESULT_OK && dbi_check_error(sql)) {

// Error...

}

else if (dbi_nextrow(result)) {

// Record already in db. Increase counter

new counter = dbi_field(result, 1)

//server_print("NAME ALREADY MATCHES, INCREASING COUNTER from %d to %d", next, counter, counter + 1)

jghg_free_result(result)


counter++


if (g_sqlite)

format(g_query, QSIZE, "UPDATE names SET counter = %d, timestamp = %d WHERE id = %d AND name = '%s'", counter, TIMESTAMP, g_dbids[id], name)

else

format(g_query, QSIZE, "UPDATE names SET counter = %d, timestamp = %d WHERE id = %d AND name = '%s' LIMIT 1", counter, TIMESTAMP, g_dbids[id], name)

//server_print("name_to_db: Sending query: %s", g_query)

result = dbi_query(sql, g_query)

jghg_free_result(result)

//server_print("%s is already in db for %d (%d)", name, id, g_dbids[id])

}

else {

jghg_free_result(result)


format(g_query, QSIZE, "INSERT INTO names (id, name, timestamp) VALUES(%d, '%s', %d)", g_dbids[id], name, TIMESTAMP)

//server_print("name_to_db: Sending query: %s", g_query)

result = dbi_query(sql, g_query)

jghg_free_result(result)

}


if (disconnect_)

disconnect(sql)

}


bool:connect(&Sql:sql) {

const LEN = 128

new host[LEN], user[LEN], pass[LEN], db[LEN], error_msg[LEN]


if (!g_sqlite) {

get_cvar_string(CVAR_HOST, host, LEN - 1)

get_cvar_string(CVAR_USER, user, LEN - 1)

get_cvar_string(CVAR_PASS, pass, LEN - 1)

}

get_cvar_string(CVAR_DB, db, LEN - 1)


//server_print("Passing this to dbi_connect(%s, %s, %s, %s, %d)", host, user, pass, db, LEN - 1)


sql = dbi_connect(host, user, pass, db, error_msg, LEN - 1)

//server_print("dbi_connect = %d", sql)

if (sql < SQL_OK && dbi_check_error(sql)) {

log_amx("[%s] ERROR - Can't connect to SQL db: %s", PLUGINNAME, error_msg)


return false

}

else {

//log_amx("[%s] Successfully connected to SQL db.", PLUGINNAME)

//server_print("[%s] Successfully connected to SQL db.", PLUGINNAME)

}


return true

}


dbi_check_error(&Sql:sql) {

new error[256] = ""

dbi_error(sql, error, 255)

if (error[0] && !equal(error, "Not an error")) {

log_amx("[%s] SQL error: %s", PLUGINNAME, error)


return true

}


return false

}


disconnect(&Sql:sql) {

if (sql > SQL_FAILED) {

dbi_close(sql)

sql = SQL_FAILED

}

}


public listnames(id, level, cid) {

if (!cmd_access(id, level, cid, 2))

return PLUGIN_HANDLED


new plname[33]

read_argv(1, plname, 32)


new player = cmd_target(id, plname, 2) // 2 = allow yourself

if (!player)

return PLUGIN_HANDLED

else if (is_user_bot(player)) {

console_print(id, "[%s] Nahh, bots don't count.", PLUGINNAME)

return PLUGIN_HANDLED

}


const SIZE = 1023

new names[SIZE + 1]

getnames(id, player, names, SIZE)


new title[40]

get_user_authid(player, title, 39)

format(title, 39, "Names used by %s", title)

if (id == 0) {

console_print(id, title)

console_print_byline(id, names)

}

else {

console_print(id, "Close the console to view the names as they are displayed in a MOTD window.")

show_motd(id, names, title)

}


return PLUGIN_HANDLED

}


console_print_byline(id, text[]) {

new line[256], starti = 0

new const TEXTLENGTH = strlen(text)


for (new i = 0; i < TEXTLENGTH; i++) {

if (text[i] != '^n')

line[i - starti] = text[i] //linelen += format(line[linelen], 127 - linelen, "%c", text[i])

else {

line[i - starti] = 0

console_print(id, line)

line[0] = 0

starti = i + 1

}

}

console_print(id, line)

}


getnames(id, player, nameshere[], const LEN) {

if (g_dbids[player] == 0) {

format(nameshere, LEN, "Couldn't retrieve user's db id at login.")

return

}

new Sql:sql

connect(sql)


format(g_query, QSIZE, "SELECT name, counter, timestamp FROM names WHERE id = %d ORDER BY counter DESC, timestamp DESC LIMIT 10", g_dbids[player])

new Result:result = dbi_query(sql, g_query)

if (result < RESULT_OK && dbi_check_error(sql)) {

console_print(id, "[%s] Oops, db operation failed.", PLUGINNAME)

}

else {

new name[64], counter, timestamp, time_[64]

new len = 0


if (id == 0) {

len = format(nameshere, LEN, "%31s%11s%20s^n", "Name", "Times used", "Last time used")

while (dbi_nextrow(result) && len < LEN) {

dbi_field(result, 1, name, 31)

counter = dbi_field(result, 2)

timestamp = dbi_field(result, 3)

format_time(time_, 31, "%y%m%d - %H:%M:%S", timestamp)

len += format(nameshere[len], LEN - len, "%31s%11d%20s^n", name, counter, time_)

}

//len += format(nameshere[len], LEN - len, "", name)

}

else {

len = format(nameshere, LEN, "

")

while (dbi_nextrow(result) && len < LEN) {

dbi_field(result, 1, name, 31)

counter = dbi_field(result, 2)

timestamp = dbi_field(result, 3)

format_time(time_, 31, "%y%m%d - %H:%M:%S", timestamp)

len += format(nameshere[len], LEN - len, "", name, counter, time_)

}

len += format(nameshere[len], LEN - len, "
NameTimes usedLast time used
%s%d%s

", name)

}


jghg_free_result(result)

}


disconnect(sql)

}


public client_infochanged(id) {

if (is_user_bot(id) || g_dbids[id] == 0)

return PLUGIN_CONTINUE


set_task(0.1, "checkname", id)


return PLUGIN_CONTINUE

}


public checkname(id) {

if (!is_user_connected(id) || is_user_bot(id) || g_dbids[id] == 0)

return


new name[64]

get_user_name(id, name, 63)

if (!equal(name, g_lastname[id])) {

//client_print(id, print_chat, "your name changed, name: %s", name)

name_to_db(id, name)


g_lastname[id] = name

}

}


/*stock bool:sqlite_table_exists(Sql:sql, table[]) {

new bool:exists

new query[128]

format(query, 127, "SELECT name FROM sqlite_master WHERE type='table' AND name='%s' LIMIT 1;", table)


new Result:result = dbi_query(sql, query)


if (dbi_nextrow(result))

exists = true

else

exists = false


if (result > RESULT_NONE)

dbi_free_result(result)


return exists

}*/


bool:create_tables() {

new Sql:sql

if (!connect(sql)) {

log_amx("[%s] Failed connecting to database. Pausing plugin.", PLUGINNAME)

pause("a")

return false

}


new Result:result


if (!g_sqlite || (g_sqlite && !sqlite_table_exists(sql, "steamids"))) {

format(g_query, QSIZE, "%s", g_sqlite ? CREATE_TABLE_STEAMIDS_SQLITE : CREATE_TABLE_STEAMIDS)


result = dbi_query(sql, g_query)


if (result <= RESULT_FAILED && dbi_check_error(sql)) {

log_amx("[%s] Error while creating steamid table. Pausing plugin.", PLUGINNAME)

disconnect(sql)

pause("a")

return false

}

jghg_free_result(result)

}



if (!g_sqlite || (g_sqlite && !sqlite_table_exists(sql, "names"))) {

format(g_query, QSIZE, "%s", g_sqlite ? CREATE_TABLE_NAMES_SQLITE : CREATE_TABLE_NAMES)


result = dbi_query(sql, g_query)


if (result <= RESULT_FAILED && dbi_check_error(sql)) {

log_amx("[%s] Error while creating names table. Pausing plugin.", PLUGINNAME)

disconnect(sql)

pause("a")

return false

}

jghg_free_result(result)

}


disconnect(sql)


return true

}

/*

stock jghg_is_user_admin(id, accessRequired = ADMIN_ADMIN) {

return get_user_flags(id) & accessRequired

}*/


public client_putinserver(id) {

g_inServer[id] = true

if (g_dbids[id] == 0 || is_user_bot(id) || (get_cvar_num(CVAR_NOADMINS) && is_user_admin(id))) {

return PLUGIN_CONTINUE

}


set_task(10.0, "delayed_printcommonnames", id)


return PLUGIN_CONTINUE

}


public delayed_printcommonnames(id) {

if (!is_user_connected(id))

return

// Get three most common names from db.

new commonnames[NROFCOMMONNAMES][64]

new gotNames = get_common_names(commonnames, id)

new currentName[64]

get_user_name(id, currentName, 31)

if (gotNames == 1 || (gotNames > 1 && equal(currentName, commonnames[0]))) {

new msg[128]

format(msg, 127, "We welcome %s", currentName)

admin_print(ACCESS_WELCOMEMSG, print_chat, msg) //client_print(0, print_chat, "We welcome %s", currentName)

server_print(msg)

return

}


new allNames[128], len = 0

for (new i = 0; i < gotNames && commonnames[i][0] != 0 && !equal(currentName, commonnames[i]); i++) {

len += format(allNames[len], 31 - len, "%s, ", commonnames[i])

//server_print("commonnames[%d] is %s", i, commonnames[i])

}

xs_strtrim(allNames, 2, false)


format(allNames, 127, "We welcome %s, aka: %s", currentName, allNames)

admin_print(ACCESS_WELCOMEMSG, print_chat, allNames) //client_print(0, print_chat, allNames)

server_print(allNames)

}


// by JGHG, adapted

// removes charstotrim number of charactes from stringtotrim's

// - beginning if fromleft is true

// - end if fromleft is false

// tested

stock xs_strtrim(stringtotrim[], charstotrim, bool:fromleft = true)

{

if (charstotrim <= 0)

return;


if (fromleft)

{

new maxlen = strlen(stringtotrim);

if (charstotrim > maxlen)

charstotrim = maxlen;


// In format, input and output regions can overlap

format(stringtotrim, maxlen, "%s", stringtotrim[charstotrim]);

}

else

{

new maxlen = strlen(stringtotrim) - charstotrim;

if (maxlen < 0)

maxlen = 0;


// In format, input and output regions can overlap

format(stringtotrim, maxlen, "%s", stringtotrim);

}

}


get_common_names(commonNames[NROFCOMMONNAMES][64], player) {

new namesCollected = 0

if (g_dbids[player] == 0) {

//format(nameshere, LEN, "Couldn't retrieve user's db id at login.")

return namesCollected

}

new Sql:sql

connect(sql)


format(g_query, QSIZE, "SELECT name FROM names WHERE id = %d ORDER BY counter DESC, timestamp DESC LIMIT %d", g_dbids[player], NROFCOMMONNAMES)

//server_print("get_common_names: Sending query: %s", g_query)

new Result:result = dbi_query(sql, g_query)

if (result < RESULT_OK && dbi_check_error(sql)) {

//console_print(id, "[%s] Oops, db operation failed.", PLUGINNAME)

disconnect(sql)

return namesCollected

}

//server_print("Done, rows gotten: %d", dbi_num_rows(result))


for (new i = 0; dbi_nextrow(result) && i < NROFCOMMONNAMES; i++) {

dbi_field(result, 1, commonNames[i], 31)

namesCollected++

}


jghg_free_result(result)

disconnect(sql)


return namesCollected

}


public plugin_init() {

register_plugin(PLUGINNAME, VERSION, AUTHOR)


register_concmd("amx_names", "listnames", ACCESS_AMXNAMES, "<playername> - lists the ten most common names ever used by this player.")


new dbitype[16]

dbi_type(dbitype, 15)

server_print("[%s] Using database type %s.", PLUGINNAME, dbitype)

if (equal(dbitype, "sqlite"))

g_sqlite = true

else

g_sqlite = false


if (!g_sqlite) {

register_cvar(CVAR_HOST, "127.0.0.1")

register_cvar(CVAR_USER, "")

register_cvar(CVAR_PASS, "")

}

register_cvar(CVAR_DB, "rememberthenames")


register_cvar(CVAR_NOADMINS, "0")



new sqlcfgpath[128]

get_configsdir(sqlcfgpath, 127)

format(sqlcfgpath, 127, "%s/sql.cfg", sqlcfgpath)

server_cmd("exec %s", sqlcfgpath)

server_exec() // To force execution of above cmd before progress? seems to do the trick...


if (create_tables())

server_print("[%s] Initialized successfully.", PLUGINNAME)

}