CSstatsX SQL

CSstatsX SQL 0.7.4+2

Нет прав для скачивания
Сообщения
77
Реакции
127
Помог
12 раз(а)
Пользователь serfreeman1337 разместил новый ресурс:

CSstatsX SQL - Запись игровой статистики Counter-Strike в БД MySQL или SQLite.

Плагин может выступать в качестве полной замены модуля CSX. После установки он начнет самостоятельность считать статистику игроков и записывать её в базу данных, но чтобы все ваши плагины статистик начали использовать его данные придется немного их изменить, об этом ниже. Может работать с локальной базой данных SQLite. Из новых функций есть подсчет времени нахождения игрока на сервере и расчет скилла по формуле ELO, запись статистики по...
Узнать больше об этом ресурсе...
 
Сообщения
2,291
Реакции
1,737
Помог
31 раз(а)
После перезагрузки сервера нет подключения к базе
[csstatsx_sql.amxx] SQL connection failed
[csstatsx_sql.amxx] [ 2002 ] Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

после смены карты все прекрасно подключается, в чем может быть проблема?
 
Сообщения
92
Реакции
23
Всем привет, есть вопрос :) Нормально ли всё будет работать, если сделать одну статистику сразу на несколько серверов?
 
Сообщения
342
Реакции
65
Помог
6 раз(а)
Обратите внимание, если вы хотите заключить сделку с этим пользователем, он заблокирован
Сообщения
2,713
Реакции
2,993
Помог
59 раз(а)
igor_grusha, с учётом того, что все сервера завязаны на одной бд и при подключении к серверу игрок получает данные с бд (или создаёт) то все нормально.
При смерти и выходе с сервера данные пеетягиваются обратно в бд.

Однако, если имеется задержка в работе с бд, возможны проблемы. Но с полной уверенностью я утверждать не могу.
 
Сообщения
52
Реакции
34
Ни у кого не наблюдалось отсутствия учёта урона от гранат? Вообще нигде не показывает, не в /me, не в /rankstats, не в /statsme.
Вариант с заменой модуля, ReAmxmodx
 
Сообщения
52
Реакции
34
wopox1337, как я понял, решение в замене строчек кода не найдено в той теме, только перейти на вариант "без замены модуля"?
 
Сообщения
2,713
Реакции
2,993
Помог
59 раз(а)
Vincent, там есть частичное решение, которое есть возможность доработать.
 
Сообщения
52
Реакции
34
wopox1337, частичное - это учёт убийств с грены, но так же не будет отображаться урон от гранат, когда например /me прописываешь, правильно понял?
и вообще, давно хотел узнать подробнее - какой вариант лучше-то, с заменой модуля или без? есть какие-то существенные отличия?
 
Сообщения
342
Реакции
65
Помог
6 раз(а)
Обратите внимание, если вы хотите заключить сделку с этим пользователем, он заблокирован
Vincent, так в /me отображается урон, это отдельный плагин-же, про второй вопрос, хочешь правь плагины, не хочешь, ставь модуль
 
Сообщения
92
Реакции
23
Что делать с этими плагинами? Заменять/удалять/оставлять как есть? Я сделал полную замену с модулем, поставил statsx.amxx с архива

miscstats.amxx ; bunch of events announcement for Counter-Strike
stats_logging.amxx ; weapons stats logging (CSX Module required!)
 
Сообщения
80
Реакции
54
Помог
1 раз(а)
igor_grusha, miscstats.amxx - это просто звуки и худ-сообщения за разные события: установка бомбы, убийства и т.д. Он никак не связан со статистикой.
stats_logging.amxx, как и написано у тебя, ведёт логирование используемого оружия. Вот тут даже не знаю, но вроде бы подобный функционал есть в csstatsx_sql. Лично я этот плаг не удалял, стоит себе, работает (может и впустую).
 
Сообщения
92
Реакции
23
Поставил статистику с одной бд на несколько серверов, пока что всё ок :)
 
Сообщения
212
Реакции
25
Помог
1 раз(а)
Ребята помогите пожалуйста.
Как надо привильно в настройках прописать чтоб небыло ошибок в logs файле
Использую dummy._csx

Код:
#include <amxmodx>
#include <sqlx>

#include <fakemeta>
#include <hamsandwich>

#define PLUGIN "CSStatsX SQL"
#define VERSION "2017"
#define AUTHOR "serfreeman1337" // AKA SerSQL1337

#define LASTUPDATE "06, July (07), 2016"

#if AMXX_VERSION_NUM < 183
#define MAX_PLAYERS 32
#define MAX_NAME_LENGTH 32
new MaxClients
#endif

/* - SQL - */

new Handle:sql
new Handle:sql_con

/* - КОНСТАНТЫ - */

enum _:sql_que_type // тип sql запроса
{
SQL_DUMMY,
SQL_INITDB, // автоматическое созданием таблиц
SQL_LOAD, // загрузка статистики
SQL_UPDATE, // обновление
SQL_INSERT, // внесение новой записи
SQL_UPDATERANK, // получение ранков игроков,
SQL_GETSTATS, // потоквый запрос на get_stats

// 0.7
SQL_GETWSTATS, // статистика по оружию
SQL_GETSESSID, // id сессии статистики за карту
SQL_GETSESTATS, // статистика по картам
SQL_AUTOCLEAR // чистка БД от неактивных записей
}

enum _:load_state_type // состояние получение статистики
{
LOAD_NO, // данных нет
LOAD_WAIT, // ожидание данных
LOAD_NEWWAIT, // новая запись, ждем ответа
LOAD_UPDATE, // перезагрузить после обновления
LOAD_NEW, // новая запись
LOAD_OK // есть данные
}

enum _:row_ids // столбцы таблицы
{
ROW_ID,
ROW_STEAMID,
ROW_NAME,
ROW_IP,
ROW_SKILL,
ROW_KILLS,
ROW_DEATHS,
ROW_HS,
ROW_TKS,
ROW_SHOTS,
ROW_HITS,
ROW_DMG,
ROW_BOMBDEF,
ROW_BOMBDEFUSED,
ROW_BOMBPLANTS,
ROW_BOMBEXPLOSIONS,
ROW_H0,
ROW_H1,
ROW_H2,
ROW_H3,
ROW_H4,
ROW_H5,
ROW_H6,
ROW_H7,
ROW_ONLINETIME,

// 0.7
ROW_CONNECTS,
ROW_ROUNDT,
ROW_WINT,
ROW_ROUNDCT,
ROW_WINCT,

// 0.7.2
ROW_ASSISTS,

ROW_FIRSTJOIN,
ROW_LASTJOIN,

// 0.7
ROW_SESSIONID,
ROW_SESSIONNAME
}

new const row_names[row_ids][] = // имена столбцов
{
"id",
"steamid",
"name",
"ip",
"skill",
"kills",
"deaths",
"hs",
"tks",
"shots",
"hits",
"dmg",
"bombdef",
"bombdefused",
"bombplants",
"bombexplosions",
"h_0",
"h_1",
"h_2",
"h_3",
"h_4",
"h_5",
"h_6",
"h_7",
"connection_time",

// 0.7
"connects",
"roundt",
"wint",
"roundct",
"winct",

// 0.7.2
"assists",

"first_join",
"last_join",

// 0.7
"session_id",
"session_map"
}

enum _:STATS
{
STATS_KILLS,
STATS_DEATHS,
STATS_HS,
STATS_TK,
STATS_SHOTS,
STATS_HITS,
STATS_DMG,

STATS_END
}

enum _:KILL_EVENT
{
NORMAL,
SUICIDE,
WORLD,
WORLDSPAWN
}

const QUERY_LENGTH = 1472 // размер переменной sql запроса

#define STATS2_DEFAT 0
#define STATS2_DEFOK 1
#define STATS2_PLAAT 2
#define STATS2_PLAOK 3
#define STATS2_END 4

new const task_rankupdate = 31337
new const task_confin = 21337
new const task_flush = 11337

#define MAX_CWEAPONS 6
#define MAX_WEAPONS CSW_P90 + 1 + MAX_CWEAPONS
#define HIT_END HIT_RIGHTLEG + 1

// 0.7

enum _:row_weapons_ids // столбцы таблицы
{
ROW_WEAPON_ID,
ROW_WEAPON_PLAYER,
ROW_WEAPON_NAME,
ROW_WEAPON_KILLS,
ROW_WEAPON_DEATHS,
ROW_WEAPON_HS,
ROW_WEAPON_TKS,
ROW_WEAPON_SHOTS,
ROW_WEAPON_HITS,
ROW_WEAPON_DMG,
ROW_WEAPON_H0,
ROW_WEAPON_H1,
ROW_WEAPON_H2,
ROW_WEAPON_H3,
ROW_WEAPON_H4,
ROW_WEAPON_H5,
ROW_WEAPON_H6,
ROW_WEAPON_H7
}

new const row_weapons_names[row_weapons_ids][] = // имена столбцов
{
"id",
"player_id",
"weapon",
"kills",
"deaths",
"hs",
"tks",
"shots",
"hits",
"dmg",
"h_0",
"h_1",
"h_2",
"h_3",
"h_4",
"h_5",
"h_6",
"h_7"
}

enum _:row_maps_ids
{
ROW_MAP_ID,
ROW_MAP_SESSID,
ROW_MAP_PLRID,
ROW_MAP_MAP,
ROW_MAP_SKILL,
ROW_MAP_KILLS,
ROW_MAP_DEATHS,
ROW_MAP_HS,
ROW_MAP_TKS,
ROW_MAP_SHOTS,
ROW_MAP_HITS,
ROW_MAP_DMG,
ROW_MAP_BOMBDEF,
ROW_MAP_BOMBDEFUSED,
ROW_MAP_BOMBPLANTS,
ROW_MAP_BOMBEXPLOSIONS,
ROW_MAP_H0,
ROW_MAP_H1,
ROW_MAP_H2,
ROW_MAP_H3,
ROW_MAP_H4,
ROW_MAP_H5,
ROW_MAP_H6,
ROW_MAP_H7,
ROW_MAP_ONLINETIME,
ROW_MAP_CONNECTS,
ROW_MAP_ROUNDT,
ROW_MAP_WINT,
ROW_MAP_ROUNDCT,
ROW_MAP_WINCT,
ROW_MAP_ASSISTS,
ROW_MAP_FIRSTJOIN,
ROW_MAP_LASTJOIN,
}

/* - СТРУКТУРА ДАННЫХ - */

// 0.7
enum _:STATS3_END
{
STATS3_CURRENTTEAM, // тек. команда игрока (определяется в начале раунда)

STATS3_CONNECT, // подключения к серверу
STATS3_ROUNDT, // раунды за теров
STATS3_WINT, // побед за теров
STATS3_ROUNDCT, // раунды за спецов
STATS3_WINCT, // побед за спецов

// 0.7.2
STATS3_ASSIST // помощь в убийстве
}


enum _:sestats_array_struct
{
SESTATS_ID,
SESTATS_PLAYERID,
SESTATS_MAP[MAX_NAME_LENGTH],
SESTATS_STATS[8],
SESTATS_HITS[8],
SESTATS_STATS2[4],
SESTATS_STATS3[STATS3_END],
Float:SESTATS_SKILL,
SESTATS_ONLINETIME,
SESTATS_FIRSTJOIN,
SESTATS_LASTJOIN
}

enum _:player_data_struct
{
PLAYER_ID, // ид игрока в базе данных
PLAYER_LOADSTATE, // состояние загрузки статистики игрока
PLAYER_RANK, // ранк игрока
PLAYER_STATS[STATS_END], // статистика игрока
PLAYER_STATSLAST[STATS_END], // разница в статистики
PLAYER_HITS[HIT_END], // статистика попаданий
PLAYER_HITSLAST[HIT_END], // разница в статистике попаданий
PLAYER_STATS2[4], // статистика cstrike
PLAYER_STATS2LAST[4], // разница
Float:PLAYER_SKILL, // скилл
PLAYER_ONLINE, // время онлайна
// я не помню чо за diff и last, но без этого не работает XD
Float:PLAYER_SKILLLAST,
PLAYER_ONLINEDIFF,
PLAYER_ONLINELAST,

PLAYER_NAME[MAX_NAME_LENGTH * 3],
PLAYER_STEAMID[30],
PLAYER_IP[16],

// 0.7
PLAYER_STATS3[STATS3_END], // stast3
PLAYER_STATS3LAST[STATS3_END], // stast3
PLAYER_FIRSTJOIN,
PLAYER_LASTJOIN
}

enum _:stats_cache_struct // кеширование для get_stats
{
CACHE_NAME[32],
CACHE_STEAMID[30],
CACHE_STATS[8],
CACHE_HITS[8],
CACHE_SKILL,
bool:CACHE_LAST,

// 0.5.1
CACHE_ID,
CACHE_TIME,

// 0.7
CACHE_STATS2[4],
CACHE_STATS3[STATS3_END],
CACHE_FIRSTJOIN,
CACHE_LASTJOIN
}

enum _:cvar_set
{
CVAR_SQL_HOST,
CVAR_SQL_USER,
CVAR_SQL_PASS,
CVAR_SQL_DB,
CVAR_SQL_TABLE,
CVAR_SQL_TYPE,
CVAR_SQL_CREATE_DB,

CVAR_UPDATESTYLE,
CVAR_RANK,
CVAR_RANKFORMULA,
CVAR_SKILLFORMULA,
CVAR_RANKBOTS,
CVAR_USEFORWARDS,

// 0.7
CVAR_WEAPONSTATS,
CVAR_MAPSTATS,

CVAR_AUTOCLEAR,
CVAR_CACHETIME,
CVAR_AUTOCLEAR_DAY,

// 0.7.2
CVAR_ASSISTHP
}


// 0.7
enum _:stats_cache_queue_struct
{
CACHE_QUE_START,
CACHE_QUE_TOP,
}

#define MAX_DATA_PARAMS 32

/* - ПЕРЕМЕННЫЕ - */

// 0.7
new session_id,session_map[MAX_NAME_LENGTH]

new player_data[MAX_PLAYERS + 1][player_data_struct]
new flush_que[QUERY_LENGTH * 3],flush_que_len
new statsnum

//
// Общая стата по оружию
//
// 1ый STATS_END + HIT_END - текущая общая статистика по оружию игрока
// 2ой STATS_END + HIT_END - последнее значение player_wstats, использует для расчета разницы
// последний индекс - определяет INSERT или UPDATE для запроса
//
new player_awstats[MAX_PLAYERS + 1][MAX_WEAPONS][((STATS_END + HIT_END) * 2) + 1]

new cvar[cvar_set]

new Trie:stats_cache_trie // дерево кеша для get_stats // ключ - ранг

new tbl_name[32]

/* - CSSTATS CORE - */

#pragma dynamic 32768

// wstats
new player_wstats[MAX_PLAYERS + 1][MAX_WEAPONS][STATS_END + HIT_END]

// wstats2
new player_wstats2[MAX_PLAYERS + 1][STATS2_END]

// wrstats rstats
new player_wrstats[MAX_PLAYERS + 1][MAX_WEAPONS][STATS_END + HIT_END]

// vstats
new player_vstats[MAX_PLAYERS + 1][MAX_PLAYERS + 1][STATS_END + HIT_END + MAX_NAME_LENGTH]

// astats
new player_astats[MAX_PLAYERS + 1][MAX_PLAYERS + 1][STATS_END + HIT_END + MAX_NAME_LENGTH]

new FW_Death
new FW_Damage
new FW_BPlanting
new FW_BPlanted
new FW_BExplode
new FW_BDefusing
new FW_BDefused
new FW_GThrow

// 0.7.2
new FW_Assist

// 0.7.3
new FW_Initialized

new dummy_ret

// осталось монитор прихуярить

new g_planter
new g_defuser

#define WEAPON_INFO_SIZE 1 + (MAX_NAME_LENGTH * 2)

new Array:weapons_data // массив с инфой по оружию
new Trie:log_ids_trie // дерево для быстрого определения id оружия по лог-коду

// 0.7
new Array:stats_cache_queue

// 0.7.1
new bool:weapon_stats_enabled,bool:map_stats_enabled

// 0.7.3
new init_seq = -1
new bool:is_ready = false

// макрос для помощи реагистрации инфы по оружию
#define REG_INFO(%0,%1,%2)\
weapon_info[0] = %0;\
copy(weapon_info[1],MAX_NAME_LENGTH,%1);\
copy(weapon_info[MAX_NAME_LENGTH ],MAX_NAME_LENGTH,%2);\
ArrayPushArray(weapons_data,weapon_info);\
TrieSetCell(log_ids_trie,%2,ArraySize(weapons_data) - 1)

public plugin_precache()
{
register_plugin(PLUGIN,VERSION,AUTHOR)
register_cvar("csmysql", VERSION, FCVAR_SERVER | FCVAR_SPONLY | FCVAR_UNLOGGED)

/*
* хост mysql
*/
cvar[CVAR_SQL_HOST] = register_cvar("csstats_sql_host","db1.myarena(пример)",FCVAR_UNLOGGED|FCVAR_PROTECTED)

/*
* пользователь mysql
*/
cvar[CVAR_SQL_USER] = register_cvar("csstats_sql_user","ky_1(пример)",FCVAR_UNLOGGED|FCVAR_PROTECTED)

/*
* пароль mysql
*/
cvar[CVAR_SQL_PASS] = register_cvar("csstats_sql_pass","dsfdsfsfds(пример)",FCVAR_UNLOGGED|FCVAR_PROTECTED)

/*
* название БД mysql или sqlite
*/
cvar[CVAR_SQL_DB] = register_cvar("csstats_sql_db","ky_1(пример)",FCVAR_UNLOGGED|FCVAR_PROTECTED)

/*
* название таблицы в БД
*/
cvar[CVAR_SQL_TABLE] = register_cvar("csstats_sql_table","csstats",FCVAR_UNLOGGED|FCVAR_PROTECTED)

/*
* тип бд
* mysql - база данных MySQL
* sqlite - локальная база данных SQLite
*/
cvar[CVAR_SQL_TYPE] = register_cvar("csstats_sql_type","mysql")

/*
* отправка запроса на создание таблицы
* 0 - не отправлять запрос
* 1 - отправлять запрос при загрузке карты
*/
cvar[CVAR_SQL_CREATE_DB] = register_cvar("csstats_sql_create_db","1")

/*
* как вести учет игроков
* -1 - не учитывать
* 0 - по нику
* 1 - по steamid
* 2 - по ip
*/
cvar[CVAR_RANK] = get_cvar_pointer("csstats_rank")

if(!cvar[CVAR_RANK])
cvar[CVAR_RANK] = register_cvar("csstats_rank","1")

/*
* запись статистики ботов
* 0 - не записывать
* 1 - записывать0
*/
cvar[CVAR_RANKBOTS] = get_cvar_pointer("csstats_rankbots")

if(!cvar[CVAR_RANKBOTS])
cvar[CVAR_RANKBOTS] = register_cvar("csstats_rankbots","0")

/*
* как обновлять статистику игрока в БД
* -2 - при смерти и дисконнекте
* -1 - в конце раунда и дисконнекте
* 0 - при дисконнекте
* значение больше 0 - через указанное кол-во секунд и дисконнекте
*/
cvar[CVAR_UPDATESTYLE] = register_cvar("csstats_sql_update","3")

/*
* включить собственные форварды для client_death, client_damage
* 0 - выключить
* 1 - включить, небоходимо, если csstats_sql используется в качестве замены модуля
*/
cvar[CVAR_USEFORWARDS] = register_cvar("csstats_sql_forwards","1")

/*
* формула расчета ранга
* 0 - убйиства - смерти - тк
* 1 - убийства
* 2 - убийства + хедшоты
* 3 - скилл
* 4 - время онлайн
*/
cvar[CVAR_RANKFORMULA] = register_cvar("csstats_sql_rankformula","0")

/*
* формула расчета скилла
* 0 - The ELO Method (http://fastcup.net/rating.html)
*/
cvar[CVAR_SKILLFORMULA] = register_cvar("csstats_sql_skillformula","0")

// 0.7

/*
* ведение статистики по оружию
*/
cvar[CVAR_WEAPONSTATS] = register_cvar("csstats_sql_weapons","0")

/*
* ведение статистики по картам
*/
cvar[CVAR_MAPSTATS] = register_cvar("csstats_sql_maps","0")

/*
* автоматическое удаление неактвиных игроков в БД
*/
cvar[CVAR_AUTOCLEAR] = register_cvar("csstats_sql_autoclear","15")

/*
* использование кеша для get_stats
* -1 - обновлять в конце раунда или по времени csstats_sql_update
* 0 - отключить использование кеша
*/
cvar[CVAR_CACHETIME] = register_cvar("csstats_sql_cachetime","-1")

/*
* автоматическая очистка всей игровой статистики в БД в определенный день
*/
cvar[CVAR_AUTOCLEAR_DAY] = register_cvar("csstats_sql_autoclear_day","0")

/*
* урон для засчитывания ассиста
*/
cvar[CVAR_ASSISTHP] = register_cvar("csstats_sql_assisthp","0")

#if AMXX_VERSION_NUM < 183
MaxClients = get_maxplayers()
#endif
}

public plugin_init()
{
register_logevent("LogEventHooK_RoundEnd", 2, "1=Round_End")
register_logevent("LogEventHooK_RoundStart", 2, "1=Round_Start")

register_event("CurWeapon","EventHook_CurWeapon","b","1=1")
register_event("Damage","EventHook_Damage","b","2!0")
register_event("BarTime","EventHook_BarTime","be")
register_event("SendAudio","EventHook_SendAudio","a")
register_event("TextMsg","EventHook_TextMsg","a")

register_srvcmd("csstats_sql_reset","SrvCmd_DBReset")

new weapon_info[WEAPON_INFO_SIZE]

//
log_ids_trie = TrieCreate()
// is_meele + название + логнейм
weapons_data = ArrayCreate(WEAPON_INFO_SIZE)

REG_INFO(false,"","")
REG_INFO(false,"p228","p228")
REG_INFO(false,"","")
REG_INFO(false,"scout","scout")
REG_INFO(false,"hegrenade","grenade")
REG_INFO(false,"xm1014","xm1014")
REG_INFO(false,"c4","weapon_c4")
REG_INFO(false,"mac10","mac10")
REG_INFO(false,"aug","aug")
REG_INFO(false,"sgrenade","grenade")
REG_INFO(false,"elite","elite")
REG_INFO(false,"fiveseven","fiveseven")
REG_INFO(false,"ump45","ump45")
REG_INFO(false,"sg550","sg550")
REG_INFO(false,"galil","galil")
REG_INFO(false,"famas","famas")
REG_INFO(false,"usp","usp")
REG_INFO(false,"glock18","glock18")
REG_INFO(false,"awp","awp")
REG_INFO(false,"mp5navy","mp5navy")
REG_INFO(false,"m249","m249")
REG_INFO(false,"m3","m3")
REG_INFO(false,"m4a1","m4a1")
REG_INFO(false,"tmp","tmp")
REG_INFO(false,"g3sg1","g3sg1")
REG_INFO(false,"flashbang","flashbang")
REG_INFO(false,"deagle","deagle")
REG_INFO(false,"sg552","sg552")
REG_INFO(false,"ak47","ak47")
REG_INFO(true,"knife","knife")
REG_INFO(false,"p90","p90")

RegisterHam(Ham_Spawn,"player","HamHook_PlayerSpawn",true)
}

#if AMXX_VERSION_NUM < 183
public plugin_cfg()
#else
public OnAutoConfigsBuffered()
#endif
{
#if AMXX_VERSION_NUM < 183
// форсируем выполнение exec addons/amxmodx/configs/amxx.cfg
server_exec()
#endif

// читаем квары на подключение
new host[128],user[64],pass[64],db[64],type[10]
get_pcvar_string(cvar[CVAR_SQL_HOST],host,charsmax(host))
get_pcvar_string(cvar[CVAR_SQL_USER],user,charsmax(user))
get_pcvar_string(cvar[CVAR_SQL_PASS],pass,charsmax(pass))
get_pcvar_string(cvar[CVAR_SQL_DB],db,charsmax(db))
get_pcvar_string(cvar[CVAR_SQL_TABLE],tbl_name,charsmax(tbl_name))
get_pcvar_string(cvar[CVAR_SQL_TYPE],type,charsmax(type))

// и снова здравствуй wopox3
if(!SQL_SetAffinity(type))
{
new error_msg[128]
formatex(error_msg,charsmax(error_msg),"failed to use ^"%s^" for db driver",
error_msg)

set_fail_state(error_msg)

return
}

sql = SQL_MakeDbTuple(host,user,pass,db)

// для поддержки utf8 ников требуется AMXX 1.8.3-dev-git3799 или выше

#if AMXX_VERSION_NUM >= 183
SQL_SetCharset(sql,"utf8")
#endif

weapon_stats_enabled = get_pcvar_num(cvar[CVAR_WEAPONSTATS]) == 1? true : false
map_stats_enabled = get_pcvar_num(cvar[CVAR_MAPSTATS]) == 1 ? true : false

new query[QUERY_LENGTH * 2],que_len

new sql_data[1]
sql_data[0] = SQL_INITDB

// запрос на создание таблицы
if(get_pcvar_num(cvar[CVAR_SQL_CREATE_DB]))
{
// запрос для mysql
if(strcmp(type,"mysql") == 0)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
CREATE TABLE IF NOT EXISTS `%s` (\
`%s` int(11) NOT NULL AUTO_INCREMENT,\
`%s` varchar(30) NOT NULL,\
`%s` varchar(32) NOT NULL,\
`%s` varchar(16) NOT NULL,\
`%s` float NOT NULL DEFAULT '0.0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',",

tbl_name,

row_names[ROW_ID],
row_names[ROW_STEAMID],
row_names[ROW_NAME],
row_names[ROW_IP],
row_names[ROW_SKILL],
row_names[ROW_KILLS],
row_names[ROW_DEATHS],
row_names[ROW_HS]
)

que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',",

row_names[ROW_TKS],
row_names[ROW_SHOTS],
row_names[ROW_HITS],
row_names[ROW_DMG],
row_names[ROW_BOMBDEF],
row_names[ROW_BOMBDEFUSED],
row_names[ROW_BOMBPLANTS],
row_names[ROW_BOMBEXPLOSIONS],
row_names[ROW_H0]
)

que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',",

row_names[ROW_H1],
row_names[ROW_H2],
row_names[ROW_H3],
row_names[ROW_H4],
row_names[ROW_H5],
row_names[ROW_H6],
row_names[ROW_H7],
row_names[ROW_ONLINETIME]
)

que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',",

row_names[ROW_CONNECTS],
row_names[ROW_ROUNDT],
row_names[ROW_WINT],
row_names[ROW_ROUNDCT],
row_names[ROW_WINCT],
row_names[ROW_ASSISTS]
)

que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\
`%s` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\
`%s` int(11) DEFAULT NULL,\
`%s` varchar(32) DEFAULT NULL,\
PRIMARY KEY (%s),\
KEY `%s` (`%s`(16)),\
KEY `%s` (`%s`(16)),\
KEY `%s` (`%s`)\
) DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;",

row_names[ROW_FIRSTJOIN],
row_names[ROW_LASTJOIN],

row_names[ROW_SESSIONID],
row_names[ROW_SESSIONNAME],

row_names[ROW_ID],
row_names[ROW_STEAMID],row_names[ROW_STEAMID],
row_names[ROW_NAME],row_names[ROW_NAME],
row_names[ROW_IP],row_names[ROW_IP]
)
}
// запрос для sqlite
else if(strcmp(type,"sqlite") == 0)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
CREATE TABLE IF NOT EXISTS `%s` (\
`%s` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,\
`%s` TEXT NOT NULL,\
`%s` TEXT NOT NULL,\
`%s` TEXT NOT NULL,\
`%s` REAL NOT NULL DEFAULT 0.0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,",

tbl_name,

row_names[ROW_ID],
row_names[ROW_STEAMID],
row_names[ROW_NAME],
row_names[ROW_IP],
row_names[ROW_SKILL],
row_names[ROW_KILLS],
row_names[ROW_DEATHS]
)

que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,",

row_names[ROW_HS],
row_names[ROW_TKS],
row_names[ROW_SHOTS],
row_names[ROW_HITS],
row_names[ROW_DMG],
row_names[ROW_BOMBDEF],
row_names[ROW_BOMBDEFUSED],
row_names[ROW_BOMBPLANTS],
row_names[ROW_BOMBEXPLOSIONS]
)

que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,",

row_names[ROW_H0],
row_names[ROW_H1],
row_names[ROW_H2],
row_names[ROW_H3],
row_names[ROW_H4],
row_names[ROW_H5],
row_names[ROW_H6],
row_names[ROW_H7]
)

// 0.7

que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,",

row_names[ROW_ONLINETIME],
row_names[ROW_CONNECTS],
row_names[ROW_ROUNDT],
row_names[ROW_WINT],
row_names[ROW_ROUNDCT],
row_names[ROW_WINCT],
row_names[ROW_ASSISTS]
)

que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\
`%s` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',\
`%s` INTEGER,\
`%s` TEXT\
);",

row_names[ROW_FIRSTJOIN],
row_names[ROW_LASTJOIN],
row_names[ROW_SESSIONID],
row_names[ROW_SESSIONNAME]
)
}
else
{
set_fail_state("invalid ^"csstats_sql_type^" cvar value")
}

DB_AddInitSeq()
SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)

if(weapon_stats_enabled)
{
que_len = 0

// запрос для mysql
if(strcmp(type,"mysql") == 0)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
CREATE TABLE IF NOT EXISTS `%s_weapons` (\
`%s` int(11) NOT NULL AUTO_INCREMENT,\
`%s` int(11) NOT NULL,\
`%s` varchar(32) NOT NULL,\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',",

tbl_name,
row_weapons_names[ROW_WEAPON_ID],
row_weapons_names[ROW_WEAPON_PLAYER],
row_weapons_names[ROW_WEAPON_NAME],
row_weapons_names[ROW_WEAPON_KILLS],
row_weapons_names[ROW_WEAPON_DEATHS],
row_weapons_names[ROW_WEAPON_HS]
)
que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',",

row_weapons_names[ROW_WEAPON_TKS],
row_weapons_names[ROW_WEAPON_SHOTS],
row_weapons_names[ROW_WEAPON_HITS],
row_weapons_names[ROW_WEAPON_DMG]
)
que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',\
`%s` int(11) NOT NULL DEFAULT '0',",

row_weapons_names[ROW_WEAPON_H0],
row_weapons_names[ROW_WEAPON_H1],
row_weapons_names[ROW_WEAPON_H2],
row_weapons_names[ROW_WEAPON_H3],
row_weapons_names[ROW_WEAPON_H4],
row_weapons_names[ROW_WEAPON_H5],
row_weapons_names[ROW_WEAPON_H6],
row_weapons_names[ROW_WEAPON_H7]
)
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
PRIMARY KEY (%s),\
KEY `%s` (`%s`(16))\
) DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;",

row_weapons_names[ROW_WEAPON_ID],
row_weapons_names[ROW_WEAPON_NAME],
row_weapons_names[ROW_WEAPON_NAME]
)
}
// запрос для sqlite
else if(strcmp(type,"sqlite") == 0)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
CREATE TABLE IF NOT EXISTS `%s_weapons` (\
`%s` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,\
`%s` INTEGER NOT NULL,\
`%s` TEXT NOT NULL,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,",

tbl_name,
row_weapons_names[ROW_WEAPON_ID],
row_weapons_names[ROW_WEAPON_PLAYER],
row_weapons_names[ROW_WEAPON_NAME],
row_weapons_names[ROW_WEAPON_KILLS],
row_weapons_names[ROW_WEAPON_DEATHS]
)
que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,",

row_weapons_names[ROW_WEAPON_HS],
row_weapons_names[ROW_WEAPON_TKS],
row_weapons_names[ROW_WEAPON_SHOTS],
row_weapons_names[ROW_WEAPON_HITS],
row_weapons_names[ROW_WEAPON_DMG]
)
que_len += formatex(query[que_len],charsmax(query) - que_len,"`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0,\
`%s` INTEGER NOT NULL DEFAULT 0",

row_weapons_names[ROW_WEAPON_H0],
row_weapons_names[ROW_WEAPON_H1],
row_weapons_names[ROW_WEAPON_H2],
row_weapons_names[ROW_WEAPON_H3],
row_weapons_names[ROW_WEAPON_H4],
row_weapons_names[ROW_WEAPON_H5],
row_weapons_names[ROW_WEAPON_H6],
row_weapons_names[ROW_WEAPON_H7]
)
que_len += formatex(query[que_len],charsmax(query) - que_len,");")
}
else
{
set_fail_state("invalid ^"csstats_sql_type^" cvar value")
}

if(que_len)
{
DB_AddInitSeq()
SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)
}
}
}

DB_AutoClearOpt()

// обновление статистики в БД каждые n сек
if(get_pcvar_num(cvar[CVAR_UPDATESTYLE]) > 0)
{
set_task(
float(get_pcvar_num(cvar[CVAR_UPDATESTYLE])),
"DB_SaveAll",
.flags = "b"
)
}

if(get_pcvar_num(cvar[CVAR_USEFORWARDS]))
{ FW_Death = CreateMultiForward("client_death",ET_IGNORE,FP_CELL,FP_CELL,FP_CELL,FP_CELL,FP_CELL)
FW_Damage = CreateMultiForward("client_damage",ET_IGNORE,FP_CELL,FP_CELL,FP_CELL,FP_CELL,FP_CELL,FP_CELL)
FW_BPlanting = CreateMultiForward("bomb_planting",ET_IGNORE,FP_CELL)
FW_BPlanted = CreateMultiForward("bomb_planted",ET_IGNORE,FP_CELL)
FW_BExplode = CreateMultiForward("bomb_explode",ET_IGNORE,FP_CELL,FP_CELL)
FW_BDefusing = CreateMultiForward("bomb_defusing",ET_IGNORE,FP_CELL)
FW_BDefused = CreateMultiForward("bomb_defused",ET_IGNORE,FP_CELL)
FW_GThrow = CreateMultiForward("grenade_throw",ET_IGNORE,FP_CELL,FP_CELL,FP_CELL)

register_forward(FM_SetModel,"FMHook_SetModel",true)
}

// 0.7.2
FW_Assist = CreateMultiForward("client_assist_sql",ET_IGNORE,FP_CELL,FP_CELL,FP_CELL)

// 0.7.3
FW_Initialized = CreateMultiForward("csxsql_initialized",ET_IGNORE)

// 0.7

//
// запрос на получение ID сессии статистики за карту
//
if(map_stats_enabled)
{
new query[128],sql_data[1] = SQL_GETSESSID

formatex(query,charsmax(query),"SELECT MAX(`session_id`) FROM `%s_maps`",
tbl_name
)

DB_AddInitSeq()
SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)
}

// защита от ретардов, которые не читаю README
if(
(get_pcvar_num(cvar[CVAR_UPDATESTYLE]) == -2) ||
(get_pcvar_num(cvar[CVAR_UPDATESTYLE]) == 0)
)
{
// выключаем кеширование
set_pcvar_num(cvar[CVAR_CACHETIME],0)
}

DB_InitSeq()
}

//
// последовательность перед началом работы плагина
//
DB_AddInitSeq()
{
init_seq --
}

//
// проверяем выполнение последовательности инициализации
//
DB_InitSeq()
{
if(init_seq ==0)
{
log_amx("!?!?!?!?!?")
return
}

init_seq ++

// все выполнено, начинаем работу
if(init_seq == 0)
{
ExecuteForward(FW_Initialized,dummy_ret)
}
}

//
// Функция очистки БД от неактивных игроков
//
DB_AutoClearOpt()
{
// 0.7
new autoclear_days = get_pcvar_num(cvar[CVAR_AUTOCLEAR])

if(autoclear_days > 0)
{
DB_ClearTables(autoclear_days)
}

// полные сброс статистики в определенный день
autoclear_days = get_pcvar_num(cvar[CVAR_AUTOCLEAR_DAY])

if(autoclear_days > 0)
{
new s_data[10]
get_time("%d",s_data,charsmax(s_data))

if(str_to_num(s_data) == autoclear_days)
{
s_data[0] = 0
get_vaultdata("csxsql_clear",s_data,charsmax(s_data))

// проверяем не было ли сброса
if(!str_to_num(s_data))
{
set_vaultdata("csxsql_clear","1")
DB_ClearTables(-1)
}
}
/// очищяем проверку на сброс
else
{
set_vaultdata("csxsql_clear","0")
}
}
}

//
// Начало работы с БД
//
public csxsql_initialized()
{
is_ready = true

new players[MAX_PLAYERS],pnum
get_players(players,pnum)

// загружаем стату игроков
for(new i ; i < pnum ; i++)
{
client_putinserver(players)
}
}

public SrvCmd_DBReset()
{
DB_ClearTables(-1)
}

//
// Очистка таблиц от неактивных записей
//
DB_ClearTables(by_days)
{
if(by_days == -1)
{
log_amx("database reset")
}

new query[QUERY_LENGTH],que_len

new type[10]
get_pcvar_string(cvar[CVAR_SQL_TYPE],type,charsmax(type))

if(strcmp(type,"mysql") == 0)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"DELETE `%s`",
tbl_name
)

if(weapon_stats_enabled)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,",`%s_weapons`",tbl_name)
}

if(map_stats_enabled)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,",`%s_maps`",tbl_name)
}

que_len += formatex(query[que_len],charsmax(query) - que_len," FROM `%s`",
tbl_name
)

if(weapon_stats_enabled)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
LEFT JOIN `%s_weapons` ON `%s`.`%s` = `%s_weapons`.`%s`",
tbl_name,
tbl_name,row_names[ROW_ID],
tbl_name,row_weapons_names[ROW_WEAPON_PLAYER]
)
}

if(map_stats_enabled)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
LEFT JOIN `%s_maps` ON `%s`.`%s` = `%s_maps`.`%s`",
tbl_name,
tbl_name,row_names[ROW_ID],
tbl_name,row_weapons_names[ROW_WEAPON_PLAYER]
)
}

if(by_days > 0)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"WHERE `%s`.`%s` <= DATE_SUB(NOW(),INTERVAL %d DAY);",
tbl_name,row_names[ROW_LASTJOIN],by_days
)
}
else
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"WHERE 1")
}
}
else if(strcmp(type,"sqlite") == 0)
{
if(weapon_stats_enabled)
{
if(by_days > 0)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
DELETE FROM `%s_weapons` WHERE `%s` IN (\
SELECT `%s` FROM `%s` WHERE `%s` <= DATETIME('now','-%d day')\
);",
tbl_name,row_weapons_names[ROW_WEAPON_PLAYER],
row_names[ROW_ID],tbl_name,row_names[ROW_LASTJOIN],
by_days
)
}
else
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
DELETE FROM `%s_weapons` WHERE `%s` IN (\
SELECT `%s` FROM `%s` WHERE 1\
);",
tbl_name,row_weapons_names[ROW_WEAPON_PLAYER],
row_names[ROW_ID],tbl_name
)
}
}

if(map_stats_enabled)
{
if(by_days > 0)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
DELETE FROM `%s_maps` WHERE `%s` IN (\
SELECT `%s` FROM `%s` WHERE `%s` <= DATETIME('now','-%d day')\
);",
tbl_name,row_weapons_names[ROW_WEAPON_PLAYER],
row_names[ROW_ID],tbl_name,row_names[ROW_LASTJOIN],
by_days
)
}
else
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
DELETE FROM `%s_maps` WHERE `%s` IN (\
SELECT `%s` FROM `%s` WHERE 1\
);",
tbl_name,row_weapons_names[ROW_WEAPON_PLAYER],
row_names[ROW_ID],tbl_name
)
}
}

if(by_days > 0)
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
DELETE FROM `%s` WHERE `%s` <= DATETIME('now','-%d day');",
tbl_name,row_names[ROW_LASTJOIN],by_days
)
}
else
{
que_len += formatex(query[que_len],charsmax(query) - que_len,"\
DELETE FROM `%s` WHERE 1;",tbl_name
)
}
}

new sql_data[1]
sql_data[0] = SQL_AUTOCLEAR

DB_AddInitSeq()
SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)
}

public plugin_end()
{
// выполняем накопившиеся запросы при смене карты или выключении серваре
DB_FlushQuery()

SQL_FreeHandle(sql)

if(sql_con != Empty_Handle)
{
SQL_FreeHandle(sql_con)
}

if(stats_cache_trie)
{
TrieDestroy(stats_cache_trie)
}

TrieDestroy(log_ids_trie)
ArrayDestroy(weapons_data)
}

/*
* загружаем статистику при подключении
*/
public client_putinserver(id)
{
// ждем начала работы с БД
if(!is_ready)
{
return PLUGIN_CONTINUE
}

reset_user_allstats(id)
reset_user_wstats(id)

arrayset(player_data[id],0,player_data_struct)

for(new wpn ; wpn < MAX_WEAPONS ; wpn ++)
{
arrayset(player_awstats[id][wpn],0,sizeof player_awstats[][])
}

DB_LoadPlayerData(id)

return PLUGIN_CONTINUE
}

/*
* сохраняем статистику при дисконнекте
*/
#if AMXX_VERSION_NUM < 183
public client_disconnect(id)
#else
public client_disconnected(id)
#endif
{
DB_SavePlayerData(id)
}

public HamHook_PlayerSpawn(id)
{
reset_user_wstats(id)
}

//
// Регистрация выстрелов
//
public EventHook_CurWeapon(player)
{
#define LASTWEAPON 0 // id посл. оружия
#define LASTCLIP 1 // кол-во потронов посл. оружия

static event_tmp[MAX_PLAYERS + 1][LASTCLIP + 1] // помним послед
static weapon_id; weapon_id = read_data(2)
static clip_ammo; clip_ammo = read_data(3)

if(event_tmp[player][LASTWEAPON] != weapon_id) // оружие было изменено, запоминаем новое кол-во патронов
{
event_tmp[player][LASTWEAPON] = weapon_id
event_tmp[player][LASTCLIP] = clip_ammo
}
else if(event_tmp[player][LASTCLIP] > clip_ammo) // кол-во патронов в магазине уменьшилось, регистрируем выстрел
{
Stats_SaveShot(player,weapon_id)
event_tmp[player][LASTCLIP] = clip_ammo
}
}

//
// Регистрация попадания
//
public EventHook_Damage(player)
{
static damage_take;damage_take = read_data(2)
static dmg_inflictor;dmg_inflictor = pev(player,pev_dmg_inflictor)

if(pev_valid(dmg_inflictor) != 2)
{
return PLUGIN_CONTINUE
}

if(!(0 < dmg_inflictor <= MaxClients))
{
// урон с гранаты на данным момент не учитывается

return PLUGIN_CONTINUE
}

static weapon_id,last_hit,attacker
attacker = get_user_attacker(player,weapon_id,last_hit)

if(0 <= last_hit < HIT_END)
{
Stats_SaveHit(dmg_inflictor,player,damage_take,weapon_id,last_hit)
}

if(!is_user_alive(player))
{
if(is_user_connected(attacker))
{
Stats_SaveKill(attacker,player,weapon_id,last_hit)
}
}

return PLUGIN_CONTINUE
}

//
// Регистрация установки и дефьюза бомбы
//
public EventHook_BarTime(player)
{
new duration = read_data(1)

if(!duration)
{
return PLUGIN_CONTINUE
}

if(duration == 3)
{
g_planter = player
g_defuser = 0

if(FW_BPlanting)
ExecuteForward(FW_BPlanting,dummy_ret,player)
}
else
{
g_defuser = player

Stats_SaveBDefusing(player)
}

return PLUGIN_CONTINUE
}

public EventHook_SendAudio(player)
{
new audio_code[16]
read_data(2,audio_code,charsmax(audio_code))

if (!player && audio_code[7] == 'B')
{
if (audio_code[11]=='P' && g_planter)
{
Stats_SaveBPlanted(g_planter)
}
else if (audio_code[11] =='D' && g_defuser)
{
Stats_SaveBDefused(g_defuser)

Event_CTWin()
}
}
}

public EventHook_TextMsg(player)
{
new message[16]
read_data(2,message,charsmax(message))

if (!player)
{
// #Target_Bombed
if ((message[1]=='T' && message[8] == 'B') && g_planter)
{
Stats_SaveBExplode(g_planter)

g_planter = 0
g_defuser = 0

Event_TWin()
}
// #Terrorists_Win -- #Hostages_Not_R
else if(
(message[2] == 'e' && message[12] == 'W') ||
(message[1] == 'H' && message[14] == 'R')
)
{
Event_TWin()
}
// #Target_Saved -- #CTs_Win -- #All_Hostages_R
else if(
(message[1] == 'T' && message[8] == 'S') ||
(message[2] == 'T' && message[5] == 'W') ||
(message[1] == 'A' && message[14] == 'R')
)
{
Event_CTWin()
}

}
}

//
// Победа TERRORIST
//
Event_TWin()
{
new players[MAX_PLAYERS],pnum
get_players(players,pnum)

for(new i,player ; i < pnum ; i++)
{
player = players

// считаем статистику побед по командам
if(player_data[player][PLAYER_STATS3][STATS3_CURRENTTEAM] == 1)
{
player_data[player][PLAYER_STATS3][STATS3_WINT] ++
}
}
}

//
// Победа CT
//
Event_CTWin()
{
new players[MAX_PLAYERS],pnum
get_players(players,pnum)

for(new i,player ; i < pnum ; i++)
{
player = players

// считаем статистику побед по командам
if(player_data[player][PLAYER_STATS3][STATS3_CURRENTTEAM] == 2)
{
player_data[player][PLAYER_STATS3][STATS3_WINCT] ++
}
}
}

//
// Форвард grenade_throw
//
public FMHook_SetModel(ent,model[])
{
new owner = pev(ent,pev_owner)

new Float:dmg_time
pev(ent,pev_dmgtime,dmg_time)

if(dmg_time <= 0.0 || !is_user_connected(owner))
{
return FMRES_IGNORED
}

new classname[32]
pev(ent,pev_classname,classname,charsmax(classname))

if(strcmp(classname,"grenade") != 0) // реагируем только на гранаты
{
return FMRES_IGNORED
}

new wId

if(model[9] == 'h') // модель хеешки
{
wId = CSW_HEGRENADE
}
else if(model[9] == 'f') // модель флешки
{
wId = CSW_FLASHBANG
}
else if(model[9] == 's') // модель смока
{
wId = CSW_SMOKEGRENADE
}

ExecuteForward(FW_GThrow,dummy_ret,owner,ent,wId)

return FMRES_IGNORED
}

//
// Учет ассистов
//
Stats_SaveAssist(player,victim,assisted)
{
player_data[player][PLAYER_STATS3][STATS3_ASSIST] ++

ExecuteForward(FW_Assist,dummy_ret,player,victim,assisted)

return true
}

//
// Учет выстрелов
//
Stats_SaveShot(player,wpn_id)
{
player_wstats[player][0][STATS_SHOTS] ++
player_wstats[player][wpn_id][STATS_SHOTS] ++

player_wrstats[player][0][STATS_SHOTS] ++
player_wrstats[player][wpn_id][STATS_SHOTS] ++

return true
}

//
// Учет попадания
//
Stats_SaveHit(attacker,victim,damage,wpn_id,hit_place)
{
player_wstats[attacker][0][STATS_HITS] ++
player_wstats[attacker][0][STATS_DMG] += damage
player_wstats[attacker][0][hit_place + STATS_END] ++

player_wrstats[attacker][0][STATS_HITS] ++
player_wrstats[attacker][0][STATS_DMG] += damage
player_wrstats[attacker][0][hit_place + STATS_END] ++

player_wstats[attacker][wpn_id][STATS_DMG] += damage
player_wrstats[attacker][wpn_id][STATS_DMG] += damage
player_wstats[attacker][wpn_id][STATS_HITS] ++
player_wrstats[attacker][wpn_id][STATS_HITS] ++
player_wstats[attacker][wpn_id][hit_place + STATS_END] ++
player_wrstats[attacker][wpn_id][hit_place + STATS_END] ++

player_vstats[attacker][victim][STATS_HITS] ++
player_vstats[attacker][victim][STATS_DMG] += damage
player_vstats[attacker][victim][hit_place + STATS_END] ++
player_astats[victim][attacker][STATS_HITS] ++
player_astats[victim][attacker][STATS_DMG] += damage
player_astats[victim][attacker][hit_place + STATS_END] ++
player_vstats[attacker][0][STATS_HITS] ++
player_vstats[attacker][0][STATS_DMG] += damage
player_vstats[attacker][0][hit_place + STATS_END] ++
player_astats[victim][0][STATS_HITS] ++
player_astats[victim][0][STATS_DMG] += damage
player_astats[victim][0][hit_place + STATS_END] ++

// оружие, с которого убил для astats, vstats
new weapon_info[WEAPON_INFO_SIZE]
ArrayGetArray(weapons_data,wpn_id,weapon_info)

copy(player_vstats[attacker][victim][STATS_END + HIT_END],
MAX_NAME_LENGTH - 1,
weapon_info[1]
)

copy(player_astats[victim][attacker][STATS_END + HIT_END],
MAX_NAME_LENGTH - 1,
weapon_info[1]
)

if(FW_Damage)
ExecuteForward(FW_Damage,dummy_ret,attacker,victim,damage,wpn_id,hit_place,is_tk(attacker,victim))

return true
}

//
// Учет смертей
//
Stats_SaveKill(killer,victim,wpn_id,hit_place)
{
if(killer == victim) // не учитываем суицид
{
return false
}

if(!is_tk(killer,victim))
{
player_wstats[killer][0][STATS_KILLS] ++
player_wstats[killer][wpn_id][STATS_KILLS] ++

player_wrstats[killer][0][STATS_KILLS] ++
player_wrstats[killer][wpn_id][STATS_KILLS] ++

player_vstats[killer][victim][STATS_KILLS] ++
player_astats[victim][killer][STATS_KILLS] ++
player_vstats[killer][0][STATS_KILLS] ++
player_astats[victim][0][STATS_KILLS] ++

if(hit_place == HIT_HEAD)
{
player_wstats[killer][0][STATS_HS] ++
player_wstats[killer][wpn_id][STATS_HS] ++

player_wrstats[killer][0][STATS_HS] ++
player_wrstats[killer][wpn_id][STATS_HS] ++

player_vstats[killer][victim][STATS_HS] ++
player_astats[victim][killer][STATS_HS] ++
player_vstats[killer][0][STATS_HS] ++
player_astats[victim][0][STATS_HS] ++
}
}
else
{
player_wstats[killer][0][STATS_TK] ++
player_wstats[killer][wpn_id][STATS_TK] ++

player_wrstats[killer][0][STATS_TK] ++
player_wrstats[killer][wpn_id][STATS_TK] ++

player_vstats[killer][victim][STATS_TK] ++
player_astats[victim][killer][STATS_TK] ++
player_vstats[killer][0][STATS_TK] ++
player_astats[victim][0][STATS_TK] ++
}

player_wstats[victim][0][STATS_DEATHS] ++
player_wrstats[victim][0][STATS_DEATHS] ++

// смотрим ассисты
for(new i = 1,assist_hp = get_pcvar_num(cvar[CVAR_ASSISTHP]); (assist_hp) && (i <= MAX_PLAYERS) ; i++)
{
if(i == killer)
{
continue
}

if(player_astats[victim][STATS_DMG] >= assist_hp)
{
Stats_SaveAssist(i,victim,killer)
}
}

new victim_wpn_id = get_user_weapon(victim)

if(victim_wpn_id)
{
player_wstats[victim][victim_wpn_id][STATS_DEATHS] ++
player_wrstats[victim][victim_wpn_id][STATS_DEATHS] ++
}

if(FW_Death)
ExecuteForward(FW_Death,dummy_ret,killer,victim,wpn_id,hit_place,is_tk(killer,victim))

if(player_data[killer][PLAYER_LOADSTATE] == LOAD_OK && player_data[victim][PLAYER_LOADSTATE] == LOAD_OK) // скилл расчитывается только при наличии статистики из БД
{
switch(get_pcvar_num(cvar[CVAR_SKILLFORMULA])) // расчет скилла
{
case 0: // The ELO Method (http://fastcup.net/rating.html)
{
new Float:delta = 1.0 / (1.0 + floatpower(10.0,(player_data[killer][PLAYER_SKILL] - player_data[victim][PLAYER_SKILL]) / 100.0))
new Float:koeff = 0.0

if(player_data[killer][PLAYER_STATS][STATS_KILLS] < 100)
{
koeff = 2.0
}
else
{
koeff = 1.5
}

player_data[killer][PLAYER_SKILL] += (koeff * delta)
player_data[victim][PLAYER_SKILL] -= (koeff * delta)
}
}
}


// обновляем статистику в БД при смерти
if(get_pcvar_num(cvar[CVAR_UPDATESTYLE]) == -2)
{
DB_SavePlayerData(victim)
}



return true
}

//
// Учет статистики по бомба
//
Stats_SaveBDefusing(id)
{
player_wstats2[id][STATS2_DEFAT] ++

if(FW_BDefusing)
ExecuteForward(FW_BDefusing,dummy_ret,id)

return true
}

Stats_SaveBDefused(id)
{
player_wstats2[id][STATS2_DEFOK] ++

if(FW_BDefused)
ExecuteForward(FW_BDefused,dummy_ret,id)

return true
}

Stats_SaveBPlanted(id)
{
player_wstats2[id][STATS2_PLAAT] ++

if(FW_BPlanted)
ExecuteForward(FW_BPlanted,dummy_ret,id)

return true
}

Stats_SaveBExplode(id)
{
player_wstats2[id][STATS2_PLAOK] ++

if(FW_BExplode)
ExecuteForward(FW_BExplode,dummy_ret,id,g_defuser)

return true
}

/*
* изменение ника игрока
*/
public client_infochanged(id)
{
new cur_name[MAX_NAME_LENGTH],new_name[MAX_NAME_LENGTH]
get_user_name(id,cur_name,charsmax(cur_name))
get_user_info(id,"name",new_name,charsmax(new_name))

if(strcmp(cur_name,new_name) != 0)
{
copy(player_data[id][PLAYER_NAME],charsmax(player_data[][PLAYER_NAME]),new_name)
mysql_escape_string(player_data[id][PLAYER_NAME],charsmax(player_data[][PLAYER_NAME]))

if(get_pcvar_num(cvar[CVAR_RANK]) == 0)
{
DB_SavePlayerData(id,true)
}
}
}

/*
* сбрасываем astats,vstats статистику в начале раунда
*/
public LogEventHooK_RoundStart()
{
// сбрасываем wrstats, vstats, astats в начале раунда
new players[32],pnum
get_players(players,pnum)

for(new i,player ; i < pnum ; i++)
{
player = players

// определяем в какой команде игрок
switch(get_user_team(player))
{
// статистика сыгранных раундов по командам
case 1:
{
player_data[player][PLAYER_STATS3][STATS3_ROUNDT] ++
player_data[player][PLAYER_STATS3][STATS3_CURRENTTEAM] = 1
}
case 2:
{
player_data[player][PLAYER_STATS3][STATS3_ROUNDCT] ++
player_data[player][PLAYER_STATS3][STATS3_CURRENTTEAM] = 2
}
}
}


}

//
// сохраняем статистику в конце раунда
//
public LogEventHooK_RoundEnd()
{
if(get_pcvar_num(cvar[CVAR_UPDATESTYLE]) == -1)
{
DB_SaveAll()
}
}

/*
* загрузка статистики игрока из базы данных
*/
DB_LoadPlayerData(id)
{
// пропускаем HLTV
if(is_user_hltv(id))
{
return false
}

// пропускаем ботов, если отключена запись статистики ботов
if( is_user_bot(id) && !get_pcvar_num(cvar[CVAR_RANKBOTS]))
{
return false
}

get_user_info(id,"name",player_data[id][PLAYER_NAME],charsmax(player_data[][PLAYER_NAME]))
mysql_escape_string(player_data[id][PLAYER_NAME],charsmax(player_data[][PLAYER_NAME]))

get_user_authid(id,player_data[id][PLAYER_STEAMID],charsmax(player_data[][PLAYER_STEAMID]))
get_user_ip(id,player_data[id][PLAYER_IP],charsmax(player_data[][PLAYER_IP]),true)

// формируем SQL запрос
new query[QUERY_LENGTH],len,sql_data[2]

sql_data[0] = SQL_LOAD
sql_data[1] = id
player_data[id][PLAYER_LOADSTATE] = LOAD_WAIT

len += formatex(query[len],charsmax(query)-len,"SELECT *,(")
len += DB_QueryBuildScore(query[len],charsmax(query)-len)
len += formatex(query[len],charsmax(query)-len,"),(")
len += DB_QueryBuildStatsnum(query[len],charsmax(query)-len)
len += formatex(query[len],charsmax(query)-len,")")

switch(get_pcvar_num(cvar[CVAR_RANK]))
{
case 0: // статистика по нику
{
len += formatex(query[len],charsmax(query)-len," FROM `%s` AS `a` WHERE `name` = '%s'",
tbl_name,player_data[id][PLAYER_NAME]
)
}
case 1: // статистика по steamid
{
len += formatex(query[len],charsmax(query)-len," FROM `%s` AS `a` WHERE `steamid` = '%s'",
tbl_name,player_data[id][PLAYER_STEAMID]
)
}
case 2: // статистика по ip
{
len += formatex(query[len],charsmax(query)-len," FROM `%s` AS `a` WHERE `ip` = '%s'",
tbl_name,player_data[id][PLAYER_IP]
)
}
default:
{
return false
}
}

// отправка потокового запроса
SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)

return true
}

//
// Загрузка статистики по оружию
//
DB_LoadPlayerWstats(id)
{
if(!player_data[id][PLAYER_ID])
{
return false
}

new query[QUERY_LENGTH],sql_data[2]

sql_data[0] = SQL_GETWSTATS
sql_data[1] = id

formatex(query,charsmax(query),"SELECT * FROM `%s_weapons` WHERE `player_id` = '%d'",
tbl_name,player_data[id][PLAYER_ID]
)

SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)

return true

}

/*
* сохранение статистики игрока
*/
DB_SavePlayerData(id,bool:reload = false)
{
if(player_data[id][PLAYER_LOADSTATE] < LOAD_NEW) // игрок не загрузился
{
return false
}

new query[QUERY_LENGTH],i,len
new sql_data[2]

sql_data[1] = id

new stats[8],stats2[4],hits[8]
get_user_wstats(id,0,stats,hits)
get_user_stats2(id,stats2)

switch(player_data[id][PLAYER_LOADSTATE])
{
case LOAD_OK: // обновление данных
{
if(reload)
{
player_data[id][PLAYER_LOADSTATE] = LOAD_UPDATE
}

sql_data[0] = SQL_UPDATE

new diffstats[sizeof player_data[][PLAYER_STATS]]
new diffstats2[sizeof player_data[][PLAYER_STATS2]]
new diffhits[sizeof player_data[][PLAYER_HITS]]
new to_save

len += formatex(query[len],charsmax(query) - len,"UPDATE `%s` SET",tbl_name)

// обновляем по разнице с предедущими данными
for(i = 0 ; i < sizeof player_data[][PLAYER_STATS] ; i++)
{
diffstats = stats - player_data[id][PLAYER_STATSLAST] // узнаем разницу
player_data[id][PLAYER_STATSLAST] = stats

if(diffstats)
{
len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + %d",
!to_save ? " " : ",",
row_names[i + ROW_KILLS],
row_names[i + ROW_KILLS],
diffstats
)

to_save ++
}
}

// обновляем по разнице с предедущими данными
for(i = 0 ; i < sizeof player_data[][PLAYER_STATS2] ; i++)
{
diffstats2 = stats2 - player_data[id][PLAYER_STATS2LAST] // узнаем разницу
player_data[id][PLAYER_STATS2LAST] = stats2

if(diffstats2)
{
len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + %d",
!to_save ? " " : ",",
row_names[i + ROW_BOMBDEF],
row_names[i + ROW_BOMBDEF],
diffstats2
)

to_save ++
}
}


new Float:diffskill = player_data[id][PLAYER_SKILL] - player_data[id][PLAYER_SKILLLAST]
player_data[id][PLAYER_SKILLLAST] = _:player_data[id][PLAYER_SKILL]

if(diffskill != 0.0)
{
len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + %.2f",
!to_save ? " " : ",",
row_names[ROW_SKILL],
row_names[ROW_SKILL],
diffskill
)

to_save ++
}

if(stats[STATS_HITS])
{
// запрос на сохранение мест попаданий
for(i = 0; i < sizeof player_data[][PLAYER_HITS] ; i++)
{
diffhits = hits - player_data[id][PLAYER_HITSLAST] // узнаем разницу
player_data[id][PLAYER_HITSLAST] = hits

if(diffhits)
{
len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + '%d'",
!to_save ? " " : ",",
row_names[i + ROW_H0],row_names[i + ROW_H0],
diffhits
)
}
}
}

// 0.7
new diffstats3[STATS3_END]

for(i = STATS3_CONNECT ; i < sizeof player_data[][PLAYER_STATS3] ; i++)
{
diffstats3 = player_data[id][PLAYER_STATS3] - player_data[id][PLAYER_STATS3LAST]
player_data[id][PLAYER_STATS3LAST] = player_data[id][PLAYER_STATS3]

if(diffstats3)
{
len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + '%d'",
!to_save ? " " : ",",
row_names[(i - 1) + ROW_CONNECTS],row_names[(i - 1) + ROW_CONNECTS],
diffstats3
)

to_save ++
}
}

// не сохраняем только подключения
to_save --

// 0.7 задаем поля для тригерром статистики по картам
if(session_id)
{
len += formatex(query[len],charsmax(query) - len,"%s`%s` = '%d',`%s` = '%s'",
to_save <= 0 ? " " : ",",
row_names[ROW_SESSIONID],session_id,
row_names[ROW_SESSIONNAME],session_map
)
}

//
player_data[id][PLAYER_ONLINE] += get_user_time(id) - player_data[id][PLAYER_ONLINEDIFF]
player_data[id][PLAYER_ONLINEDIFF] = get_user_time(id)

new diffonline = player_data[id][PLAYER_ONLINE]- player_data[id][PLAYER_ONLINELAST]
player_data[id][PLAYER_ONLINELAST] = player_data[id][PLAYER_ONLINE]

if(diffonline)
{
len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + %d",
to_save <= 0 ? " " : ",",
row_names[ROW_ONLINETIME],
row_names[ROW_ONLINETIME],
diffonline
)

//to_save ++
}

// обновляем время последнего подключения, ник, ип и steamid
len += formatex(query[len],charsmax(query) - len,",\
`last_join` = CURRENT_TIMESTAMP,\
`%s` = '%s',\
`%s` = '%s'",


row_names[ROW_STEAMID],player_data[id][PLAYER_STEAMID],
row_names[ROW_IP],player_data[id][PLAYER_IP],

row_names[ROW_ID],player_data[id][PLAYER_ID]
)

if(!reload) // не обновляем ник при его смене
{
len += formatex(query[len],charsmax(query) - len,",`%s` = '%s'",
row_names[ROW_NAME],player_data[id][PLAYER_NAME]
)
}

len += formatex(query[len],charsmax(query) - len,"WHERE `%s` = '%d'",row_names[ROW_ID],player_data[id][PLAYER_ID])

if(to_save <= 0) // нечего сохранять
{
if(player_data[id][PLAYER_LOADSTATE] == LOAD_UPDATE) // релоад для обновления ника
{
player_data[id][PLAYER_LOADSTATE] = LOAD_NO
DB_LoadPlayerData(id)
}

return false
}
else
{
//
// Сравниваем статистику
//
for(new i ; i < sizeof player_data[][PLAYER_STATS] ; i++)
{
player_data[id][PLAYER_STATS] += diffstats
}

for(new i ; i < sizeof player_data[][PLAYER_HITS] ; i++)
{
player_data[id][PLAYER_HITS] += diffhits
}

for(new i ; i < sizeof player_data[][PLAYER_STATS2] ; i++)
{
player_data[id][PLAYER_STATS2] += diffstats2
}
}
}
case LOAD_NEW: // запрос на добавление новой записи
{
sql_data[0] = SQL_INSERT

new Float:skill

switch(get_pcvar_num(cvar[CVAR_SKILLFORMULA]))
{
case 0: skill = 100.0
}

formatex(query,charsmax(query),"INSERT INTO `%s` \
(`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`)\
VALUES('%s','%s','%s','%.2f','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d',CURRENT_TIMESTAMP)\
",tbl_name,

row_names[ROW_STEAMID],
row_names[ROW_NAME],
row_names[ROW_IP],
row_names[ROW_SKILL],
row_names[ROW_KILLS],
row_names[ROW_DEATHS],
row_names[ROW_HS],
row_names[ROW_TKS],
row_names[ROW_SHOTS],
row_names[ROW_HITS],
row_names[ROW_DMG],
row_names[ROW_BOMBDEF],
row_names[ROW_BOMBDEFUSED],
row_names[ROW_BOMBPLANTS],
row_names[ROW_BOMBEXPLOSIONS],
row_names[ROW_LASTJOIN],

player_data[id][PLAYER_STEAMID],
player_data[id][PLAYER_NAME],
player_data[id][PLAYER_IP],

skill,

stats[STATS_KILLS],
stats[STATS_DEATHS],
stats[STATS_HS],
stats[STATS_TK],
stats[STATS_SHOTS],
stats[STATS_HITS],
stats[STATS_DMG],

stats2[STATS2_DEFAT],
stats2[STATS2_DEFOK],
stats2[STATS2_PLAAT],
stats2[STATS2_PLAOK]
)

//
// Сравниваем статистику
//
for(new i ; i < sizeof player_data[][PLAYER_STATS] ; i++)
{
player_data[id][PLAYER_STATS] = stats
}

for(new i ; i < sizeof player_data[][PLAYER_HITS] ; i++)
{
player_data[id][PLAYER_HITS] = hits
}

for(new i ; i < sizeof player_data[][PLAYER_STATS2] ; i++)
{
player_data[id][PLAYER_STATS2] = stats2
}

player_data[id][PLAYER_SKILL] = _:player_data[id][PLAYER_SKILLLAST] = _:skill

if(reload)
{
player_data[id][PLAYER_LOADSTATE] = LOAD_UPDATE
}
else
{
player_data[id][PLAYER_LOADSTATE] = LOAD_NEWWAIT
}
}
}

if(query[0])
{
if(weapon_stats_enabled)
{
DB_SavePlayerWstats(id)
}

switch(sql_data[0])
{
// накапливаем запросы
case SQL_UPDATE:
{
// запросов достаточно, сбрасываем их
DB_AddQuery(query,len)

return true
}
}

SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)
}

return true
}

//
// Сохранение статистики по оружию
//
public DB_SavePlayerWstats(id)
{
if(player_data[id][PLAYER_LOADSTATE] < LOAD_OK) // игрок не загрузился
{
return false
}

new query[QUERY_LENGTH],len,log[MAX_NAME_LENGTH],wpn,stats_index,stats_index_last,to_save
new diff[STATS_END + HIT_END]

const load_index = sizeof player_awstats[][] - 1

// по всем оружиям
for(wpn = 0; wpn < MAX_WEAPONS ; wpn++)
{
Info_Weapon_GetLog(wpn,log,charsmax(log))

if(!log[0])
{
continue
}

to_save = 0
len = 0

// расчитываем разницу статисткии
for(stats_index = 0; stats_index < STATS_END + HIT_END; stats_index++)
{
stats_index_last = stats_index + (STATS_END + HIT_END)

diff[stats_index] = player_wstats[id][wpn][stats_index] - player_awstats[id][wpn][stats_index_last]
player_awstats[id][wpn][stats_index_last] = player_wstats[id][wpn][stats_index]
}

switch(player_awstats[id][wpn][load_index])
{
// новая статистика оружия
case LOAD_NEW:
{
new id_row

// строим запрос
len += formatex(query[len],charsmax(query) - len,"INSERT INTO `%s_weapons` (`%s`,`%s`",
tbl_name,

row_weapons_names[ROW_WEAPON_PLAYER],
row_weapons_names[ROW_WEAPON_NAME]
)

for(stats_index = 0; stats_index < STATS_END + HIT_END; stats_index++)
{
id_row = ROW_WEAPON_KILLS + stats_index

if(diff[stats_index])
{
len += formatex(query[len],charsmax(query) - len,",`%s`",
row_weapons_names[id_row]
)

to_save ++
}
}

if(to_save)
{
len += formatex(query[len],charsmax(query) - len,") VALUES('%d','%s'",
player_data[id][PLAYER_ID],
log
)

for(stats_index = 0; stats_index < STATS_END + HIT_END; stats_index++)
{
id_row = ROW_WEAPON_KILLS + stats_index

if(diff[stats_index])
{
len += formatex(query[len],charsmax(query) - len,",'%d'",
diff[stats_index]
)
}
}

len += formatex(query[len],charsmax(query) - len,")")
player_awstats[id][wpn][load_index] = _:LOAD_OK
}


}
// обновляем статистику
case LOAD_OK:
{
new id_row

// строим запрос
len += formatex(query[len],charsmax(query) - len,"UPDATE `%s_weapons` SET",tbl_name)

for(stats_index = 0; stats_index < STATS_END + HIT_END; stats_index++)
{
id_row = ROW_WEAPON_KILLS + stats_index

if(diff[stats_index])
{
len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + '%d'",
to_save ? "," : "",
row_weapons_names[id_row],
row_weapons_names[id_row],
diff[stats_index]
)

to_save ++
}
}

len += formatex(query[len],charsmax(query) - len,"WHERE `%s` = '%s' AND `%s` = '%d'",
row_weapons_names[ROW_WEAPON_NAME],log,
row_weapons_names[ROW_WEAPON_PLAYER],player_data[id][PLAYER_ID]
)
}
}

if(to_save)
{
DB_AddQuery(query,len)
}
}

return true
}

DB_AddQuery(query[],len)
{
if((flush_que_len + len + 1) > charsmax(flush_que))
{
DB_FlushQuery()
}

flush_que_len += formatex(
flush_que[flush_que_len],
charsmax(flush_que) - flush_que_len,
"%s%s",flush_que_len ? ";" : "",
query
)

// задание на сброс накопленных запросов
remove_task(task_flush)
set_task(0.1,"DB_FlushQuery",task_flush)
}

//
// Сброс накопленных запросов
//
public DB_FlushQuery()
{
if(flush_que_len)
{
new sql_data[1] = SQL_UPDATE
SQL_ThreadQuery(sql,"SQL_Handler",flush_que,sql_data,sizeof sql_data)

flush_que_len = 0
}
}

#define falos false

/*
* получение новых позиции в топе игроков
*/
public DB_GetPlayerRanks()
{
new players[32],pnum
get_players(players,pnum)

new query[QUERY_LENGTH],len

// строим SQL запрос
len += formatex(query[len],charsmax(query) - len,"SELECT `id`,(")
len += DB_QueryBuildScore(query[len],charsmax(query) - len)
len += formatex(query[len],charsmax(query) - len,") FROM `%s` as `a` WHERE `id` IN(",tbl_name)

new bool:letsgo

for(new i,player,bool:y ; i < pnum ; i++)
{
player = players

if(player_data[player][PLAYER_ID])
{
len += formatex(query[len],charsmax(query) - len,"%s'%d'",y ? "," : "",player_data[player][PLAYER_ID])
y = true
letsgo = true
}
}

len += formatex(query[len],charsmax(query) - len,")")

if(letsgo)
{
new data[1] = SQL_UPDATERANK
SQL_ThreadQuery(sql,"SQL_Handler",query,data,sizeof data)
}
}

new bool:update_cache = false

/*
* сохранение статистики всех игроков
*/
public DB_SaveAll()
{
new players[32],pnum
get_players(players,pnum)

if(get_pcvar_num(cvar[CVAR_CACHETIME]) == -1)
{
update_cache = true
}

for(new i ; i < pnum ; i++)
{
DB_SavePlayerData(players)
}
}

/*
* запрос на просчет ранка
*/
DB_QueryBuildScore(sql_que[] = "",sql_que_len = 0,bool:only_rows = falos,overide_order = 0)
{
// стандартная формула csstats (убийства-смерти-tk)

if(only_rows)
{
switch(overide_order ? overide_order : get_pcvar_num(cvar[CVAR_RANKFORMULA]))
{
case 1: return formatex(sql_que,sql_que_len,"`kills`")
case 2: return formatex(sql_que,sql_que_len,"`kills`+`hs`")
case 3: return formatex(sql_que,sql_que_len,"`skill`")
case 4: return formatex(sql_que,sql_que_len,"`connection_time`")
default: return formatex(sql_que,sql_que_len,"`kills`-`deaths`-`tks`")
}
}
else
{
switch(overide_order ? overide_order : get_pcvar_num(cvar[CVAR_RANKFORMULA]))
{
case 1: return formatex(sql_que,sql_que_len,"SELECT COUNT(*) FROM %s WHERE (kills)>=(a.kills)",tbl_name)
case 2: return formatex(sql_que,sql_que_len,"SELECT COUNT(*) FROM %s WHERE (kills+hs)>=(a.kills+a.hs)",tbl_name)
case 3: return formatex(sql_que,sql_que_len,"SELECT COUNT(*) FROM %s WHERE (skill)>=(a.skill)",tbl_name)
case 4: return formatex(sql_que,sql_que_len,"SELECT COUNT(*) FROM %s WHERE (connection_time)>=(a.connection_time)",tbl_name)
default: return formatex(sql_que,sql_que_len,"SELECT COUNT(*) FROM %s WHERE (kills-deaths-tks)>=(a.kills-a.deaths-a.tks)",tbl_name)
}


}

return 0
}

/*
* запрос на общее кол-во записей в БД
*/
DB_QueryBuildStatsnum(sql_que[] = "",sql_que_len = 0)
{
return formatex(sql_que,sql_que_len,"SELECT COUNT(*) FROM %s WHERE 1",tbl_name)
}

/*
* запрос на выборку статистики по позиции
* index - начальная позиция
* index_count - кол-во выбираемых записей
*/
DB_QueryBuildGetstats(query[],query_max,len = 0,index,index_count = 2,overide_order = 0)
{
// строим запрос
len += formatex(query[len],query_max-len,"SELECT *")

// запрос на ранк
len += formatex(query[len],query_max-len,",(")
len += DB_QueryBuildScore(query[len],query_max-len,true,overide_order)
len += formatex(query[len],query_max-len,") as `rank`")

// запрашиваем следующию запись
// если есть, то возврашаем нативом index + 1
len += formatex(query[len],query_max-len," FROM `%s` as `a` ORDER BY `rank` DESC LIMIT %d,%d",
tbl_name,index,index_count
)

return len
}

/*
* чтение результата get_stats запроса
*/
DB_ReadGetStats(Handle:sqlQue,name[] = "",name_len = 0,authid[] = "",authid_len = 0,stats[8] = 0,hits[8] = 0,stats2[4] = 0,stats3[STATS3_END] = 0,&stats_count = 0,index)
{
stats_count = SQL_NumResults(sqlQue)

if(!stats_count)
{
return false
}

new stats_cache[stats_cache_struct]

switch(get_pcvar_num(cvar[CVAR_RANK]))
{
case 0: SQL_ReadResult(sqlQue,ROW_NAME,stats_cache[CACHE_STEAMID],charsmax(stats_cache[CACHE_STEAMID]))
case 1: SQL_ReadResult(sqlQue,ROW_STEAMID,stats_cache[CACHE_STEAMID],charsmax(stats_cache[CACHE_STEAMID]))
case 2: SQL_ReadResult(sqlQue,ROW_IP,stats_cache[CACHE_STEAMID],charsmax(stats_cache[CACHE_STEAMID]))
}

SQL_ReadResult(sqlQue,ROW_NAME,stats_cache[CACHE_NAME],charsmax(stats_cache[CACHE_NAME]))

copy(name,name_len,stats_cache[CACHE_NAME])
copy(authid,authid_len,stats_cache[CACHE_STEAMID])

new i

for(i = ROW_SKILL ; i <= ROW_LASTJOIN ; i++)
{
switch(i)
{
case ROW_SKILL: SQL_ReadResult(sqlQue,i,stats_cache[CACHE_SKILL])
case ROW_KILLS..ROW_DMG:
{
stats_cache[CACHE_STATS][i - ROW_KILLS] = stats[i - ROW_KILLS] = SQL_ReadResult(sqlQue,i)
}
case ROW_BOMBDEF..ROW_BOMBEXPLOSIONS:
{
stats_cache[CACHE_STATS2][i - ROW_BOMBDEF] = stats2[i - ROW_BOMBDEF] = SQL_ReadResult(sqlQue,i)
}
case ROW_H0..ROW_H7:
{
stats_cache[CACHE_HITS][i - ROW_H0] = hits[i - ROW_H0] = SQL_ReadResult(sqlQue,i)
}
// 0.7
case ROW_CONNECTS..ROW_ASSISTS:
{
stats_cache[CACHE_STATS3][((i - ROW_CONNECTS) + 1)] = stats3[((i - ROW_CONNECTS) + 1)] = SQL_ReadResult(sqlQue,i)
}
case ROW_FIRSTJOIN..ROW_LASTJOIN:
{
new date_str[32]
SQL_ReadResult(sqlQue,i,date_str,charsmax(date_str))

stats_cache[(CACHE_FIRSTJOIN + (i - ROW_FIRSTJOIN))] = parse_time(date_str,"%Y-%m-%d %H:%M:%S")
}
}

}

// кеширование данных
if(!stats_cache_trie)
{
stats_cache_trie = TrieCreate()
}

stats_cache[CACHE_LAST] = SQL_NumResults(sqlQue) <= 1
SQL_ReadResult(sqlQue,ROW_SKILL,stats_cache[CACHE_SKILL])
stats_cache[CACHE_ID] = SQL_ReadResult(sqlQue,ROW_ID)
stats_cache[CACHE_TIME] = SQL_ReadResult(sqlQue,ROW_ONLINETIME)

new index_str[10]
num_to_str(index,index_str,charsmax(index_str))

TrieSetArray(stats_cache_trie,index_str,stats_cache,stats_cache_struct)
// кешироавние данных

SQL_NextRow(sqlQue)

return SQL_MoreResults(sqlQue)
}

//
// Задаем очередь для обновления кеша
//
Cache_Stats_SetQueue(start_index,top)
{
// очередь уже создана
if(Cache_Stats_CheckQueue(start_index,top))
{
return false
}

if(!stats_cache_queue)
{
stats_cache_queue = ArrayCreate(stats_cache_queue_struct)
}

new length = ArraySize(stats_cache_queue)

new cache_queue_info[stats_cache_queue_struct]
cache_queue_info[CACHE_QUE_START] = start_index
cache_queue_info[CACHE_QUE_TOP] = top

if(!length) // новая очередь
{
ArrayPushArray(stats_cache_queue,cache_queue_info)
}
else // в топ
{
ArrayInsertArrayBefore(stats_cache_queue,0,cache_queue_info)
}

length ++

if(length > 5) // максимум 5 заданий в очереди
{
ArrayDeleteItem(stats_cache_queue,5)
length --
}

return true
}

//
// Обновление кеша через очередь
//
Cache_Stats_UpdateQueue()
{
if(!stats_cache_queue)
{
return false
}

for(new i,length = ArraySize(stats_cache_queue),cache_queue_info[stats_cache_queue_struct] ; i < length ; i++)
{
ArrayGetArray(stats_cache_queue,i,cache_queue_info)
DB_QueryTop15(0,-1,-1,-1,cache_queue_info[CACHE_QUE_START],cache_queue_info[CACHE_QUE_TOP],-1)
}

return true
}

Cache_Stats_CheckQueue(start_index,top)
{
if(!stats_cache_queue)
{
return false
}

for(new i,length = ArraySize(stats_cache_queue),cache_queue_info[stats_cache_queue_struct] ; i < length ; i++)
{
ArrayGetArray(stats_cache_queue,i,cache_queue_info)

if(start_index == cache_queue_info[0] &&
top == cache_queue_info[1]
)
{
return true
}
}

return false
}

//
// Потоковый запрос на Top15
//
DB_QueryTop15(id,plugin_id,func_id,position,start_index,top,params)
{
// кеширование
if((get_pcvar_num(cvar[CVAR_CACHETIME]) != 0) && stats_cache_trie)
{
Cache_Stats_SetQueue(start_index,top)

new bool:use_cache = true

// проверяем что требуемые данные есть в кеше
for(new i = start_index,index_str[10]; i < (start_index + top) ; i++)
{
num_to_str(i,index_str,charsmax(index_str))

if(!TrieKeyExists(stats_cache_trie,index_str))
{
use_cache = false
}
}

// юзаем кеш
if(use_cache)
{
// вызываем хандлер другого плагина

if(func_id > -1)
{
if(callfunc_begin_i(func_id,plugin_id))
{
callfunc_push_int(id)
callfunc_push_int(position)
callfunc_end()
}
}

return true
}
}
// кеширование

// строим новый запрос
new query[QUERY_LENGTH]

if(params == 5)
{
DB_QueryBuildGetstats(query,charsmax(query),.index = start_index,.index_count = top,.overide_order = get_param(5))
}
else
{
DB_QueryBuildGetstats(query,charsmax(query),.index = start_index,.index_count = top)
}

new sql_data[6]

sql_data[0] = SQL_GETSTATS
sql_data[1] = id
sql_data[2] = plugin_id
sql_data[3] = func_id
sql_data[4] = position
sql_data[5] = start_index

SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)

return true
}

/*
* обновляем кеш для get_stats
*/
Cache_Stats_Update()
{
if(!stats_cache_trie)
return false

TrieClear(stats_cache_trie)

return true
}

/*
* обработка ответов на SQL запросы
*/
public SQL_Handler(failstate,Handle:sqlQue,err[],errNum,data[],dataSize){
// есть ошибки
switch(failstate)
{
case TQUERY_CONNECT_FAILED: // ошибка соединения с mysql сервером
{
log_amx("SQL connection failed")
log_amx("[ %d ] %s",errNum,err)

return PLUGIN_HANDLED
}
case TQUERY_QUERY_FAILED: // ошибка SQL запроса
{
new lastQue[QUERY_LENGTH]
SQL_GetQueryString(sqlQue,lastQue,charsmax(lastQue)) // узнаем последний SQL запрос

log_amx("SQL query failed")
log_amx("[ %d ] %s",errNum,err)
log_amx("[ SQL ] %s",lastQue)

return PLUGIN_HANDLED
}
}

switch(data[0])
{
case SQL_INITDB:
{
DB_InitSeq()
}
case SQL_LOAD: // загрзука статистики игрока
{
new id = data[1]

if(!is_user_connected(id))
{
return PLUGIN_HANDLED
}

if(SQL_NumResults(sqlQue)) // считываем статистику
{
player_data[id][PLAYER_LOADSTATE] = LOAD_OK
player_data[id][PLAYER_ID] = SQL_ReadResult(sqlQue,ROW_ID)

// общая статистика
player_data[id][PLAYER_STATS][STATS_KILLS] = SQL_ReadResult(sqlQue,ROW_KILLS)
player_data[id][PLAYER_STATS][STATS_DEATHS] = SQL_ReadResult(sqlQue,ROW_DEATHS)
player_data[id][PLAYER_STATS][STATS_HS] = SQL_ReadResult(sqlQue,ROW_HS)
player_data[id][PLAYER_STATS][STATS_TK] = SQL_ReadResult(sqlQue,ROW_TKS)
player_data[id][PLAYER_STATS][STATS_SHOTS] = SQL_ReadResult(sqlQue,ROW_SHOTS)
player_data[id][PLAYER_STATS][STATS_HITS] = SQL_ReadResult(sqlQue,ROW_HITS)
player_data[id][PLAYER_STATS][STATS_DMG] = SQL_ReadResult(sqlQue,ROW_DMG)

// статистика cstrike
player_data[id][PLAYER_STATS2][STATS2_DEFAT] = SQL_ReadResult(sqlQue,ROW_BOMBDEF)
player_data[id][PLAYER_STATS2][STATS2_DEFOK] = SQL_ReadResult(sqlQue,ROW_BOMBDEFUSED)
player_data[id][PLAYER_STATS2][STATS2_PLAAT] = SQL_ReadResult(sqlQue,ROW_BOMBPLANTS)
player_data[id][PLAYER_STATS2][STATS2_PLAOK] = SQL_ReadResult(sqlQue,ROW_BOMBEXPLOSIONS)

// время онлайн
player_data[id][PLAYER_ONLINE] = player_data[id][PLAYER_ONLINELAST] = SQL_ReadResult(sqlQue,ROW_ONLINETIME)

// скилл
SQL_ReadResult(sqlQue,ROW_SKILL,player_data[id][PLAYER_SKILL])
player_data[id][PLAYER_SKILLLAST] = _:player_data[id][PLAYER_SKILL]

// посл подключение и первое подкчлючение
new date_str[32]

SQL_ReadResult(sqlQue,ROW_FIRSTJOIN,date_str,charsmax(date_str))
player_data[id][PLAYER_FIRSTJOIN] = parse_time(date_str,"%Y-%m-%d %H:%M:%S")
SQL_ReadResult(sqlQue,ROW_LASTJOIN,date_str,charsmax(date_str))
player_data[id][PLAYER_LASTJOIN] = parse_time(date_str,"%Y-%m-%d %H:%M:%S")

// доп. запросы
player_data[id][PLAYER_RANK] = SQL_ReadResult(sqlQue,row_ids) // ранк игрока
statsnum = SQL_ReadResult(sqlQue,row_ids + 1) // общее кол-во игроков в БД

// статистика попаданий
for(new i ; i < sizeof player_data[][PLAYER_HITS] ; i++)
{
player_data[id][PLAYER_HITS] = SQL_ReadResult(sqlQue,ROW_H0 + i)
}

// 0.7
for(new i = STATS3_CONNECT ; i < sizeof player_data[][PLAYER_STATS3] ; i++)
{
player_data[id][PLAYER_STATS3] = player_data[id][PLAYER_STATS3LAST] = SQL_ReadResult(sqlQue,(i - 1) + ROW_CONNECTS)

// плюсуем стату подключений
if(i == STATS3_CONNECT)
{
player_data[id][PLAYER_STATS3] ++
}
}

if(weapon_stats_enabled)
{
DB_LoadPlayerWstats(id)
}
}
else // помечаем как нового игрока
{
player_data[id][PLAYER_LOADSTATE] = LOAD_NEW

DB_SavePlayerData(id) // добавляем запись в базу данных
}
}
case SQL_INSERT: // запись новых данных
{
new id = data[1]

if(is_user_connected(id))
{
if(player_data[id][PLAYER_LOADSTATE] == LOAD_UPDATE)
{
player_data[id][PLAYER_LOADSTATE] = LOAD_NO
DB_LoadPlayerData(id)

return PLUGIN_HANDLED
}

player_data[id][PLAYER_ID] = SQL_GetInsertId(sqlQue) // первичный ключ
player_data[id][PLAYER_LOADSTATE] = LOAD_OK // данные загружены

// я упрлся 0)0)0

// обновляем счетчик общего кол-ва записей
statsnum++

if(weapon_stats_enabled)
{
DB_LoadPlayerWstats(id)
}
}

// обновляем позици игроков
// действие с задержкой, что-бы учесть изменения при множественном обновлении данных
if(!task_exists(task_rankupdate))
{
set_task(1.0,"DB_GetPlayerRanks",task_rankupdate)
}
}
case SQL_UPDATE: // обновление данных
{
// обновляем позици игроков
// действие с задержкой, что-бы учесть изменения при множественном обновлении данных
if(!task_exists(task_rankupdate))
{
set_task(0.1,"DB_GetPlayerRanks",task_rankupdate)
}

new players[MAX_PLAYERS],pnum
get_players(players,pnum)

for(new i,player ; i < pnum ; i++)
{
player = players

if(player_data[player][PLAYER_LOADSTATE] == LOAD_UPDATE)
{
player_data[player][PLAYER_LOADSTATE] = LOAD_NO
DB_LoadPlayerData(player)
}
}

if(update_cache)
{
update_cache = false

Cache_Stats_Update()
Cache_Stats_UpdateQueue()
}
}
case SQL_UPDATERANK:
{
while(SQL_MoreResults(sqlQue))
{
new pK = SQL_ReadResult(sqlQue,0)
new rank = SQL_ReadResult(sqlQue,1)

for(new i ; i < MAX_PLAYERS ; i++)
{
if(player_data[PLAYER_ID] == pK) // задаем ранк по первичному ключу
{
player_data[PLAYER_RANK] = rank
}
}

SQL_NextRow(sqlQue)
}
}
case SQL_GETSTATS: // потоковый get_stats
{
new id = data[1]

if(id && !is_user_connected(id))
{
return PLUGIN_HANDLED
}

new index = data[5]
new name[32],authid[30]

// кешируем ответ
while(DB_ReadGetStats(sqlQue,name,charsmax(name),authid,charsmax(authid),.index = index ++))
{
}

if(data[3] > -1)
{
// вызываем хандлер другого плагина
if(callfunc_begin_i(data[3],data[2]))
{
callfunc_push_int(id)
callfunc_push_int(data[4])
callfunc_end()
}
}
}

// 0.7
case SQL_GETWSTATS:
{
new id = data[1]

if(!is_user_connected(id))
{
return PLUGIN_HANDLED
}

const load_index = sizeof player_awstats[][] - 1

// загружаем статистику по оружию
while(SQL_MoreResults(sqlQue))
{
new log[MAX_NAME_LENGTH]
SQL_ReadResult(sqlQue,ROW_WEAPON_NAME,log,charsmax(log))

new wpn = Info_Weapon_GetId(log)

if(wpn == -1)
{
continue
}

for(new i ; i < STATS_END + HIT_END ; i++)
{
player_awstats[id][wpn] = SQL_ReadResult(sqlQue,i + ROW_WEAPON_KILLS)
}

player_awstats[id][wpn][load_index] = _:LOAD_OK

SQL_NextRow(sqlQue)
}

// помечаем статистику по другим оружиям как новую
for(new wpn ; wpn < MAX_WEAPONS ; wpn++)
{
if(_:player_awstats[id][wpn][load_index] != _:LOAD_OK)
{
player_awstats[id][wpn][load_index] = _:LOAD_NEW
}
}
}
case SQL_GETSESSID:
{
session_id = SQL_ReadResult(sqlQue,0) + 1
get_mapname(session_map,charsmax(session_map))

DB_InitSeq()
}
// get_sestats_thread_sql
case SQL_GETSESTATS:
{
new Array:sestats_array = ArrayCreate(sestats_array_struct)
new sestats_data[sestats_array_struct]

while(SQL_MoreResults(sqlQue))
{
arrayset(sestats_data,0,sestats_array_struct)

// заполняем массив со статой сессии
for(new i = ROW_MAP_ID ; i <= ROW_MAP_LASTJOIN ; i++)
{
switch(i)
{
case ROW_MAP_ID: sestats_data[SESTATS_ID] = SQL_ReadResult(sqlQue,i)
case ROW_MAP_PLRID: sestats_data[SESTATS_PLAYERID] = SQL_ReadResult(sqlQue,i)
case ROW_MAP_MAP: SQL_ReadResult(sqlQue,i,sestats_data[SESTATS_MAP],charsmax(sestats_data[SESTATS_MAP]))
case ROW_MAP_SKILL: SQL_ReadResult(sqlQue,i,sestats_data[SESTATS_SKILL])
case ROW_MAP_KILLS..ROW_MAP_DMG: sestats_data[SESTATS_STATS][(i - ROW_MAP_KILLS)] = SQL_ReadResult(sqlQue,i)
case ROW_MAP_H0..ROW_MAP_H7: sestats_data[SESTATS_HITS][(i - ROW_MAP_H0)] = SQL_ReadResult(sqlQue,i)
case ROW_MAP_BOMBDEF..ROW_MAP_BOMBEXPLOSIONS: sestats_data[SESTATS_STATS2][(i - ROW_MAP_BOMBDEF)] = SQL_ReadResult(sqlQue,i)
case ROW_MAP_ROUNDT..ROW_MAP_ASSISTS: sestats_data[SESTATS_STATS3][((i - ROW_MAP_ROUNDT) + 1)] = SQL_ReadResult(sqlQue,i)
case ROW_MAP_ONLINETIME: sestats_data[SESTATS_ONLINETIME] = SQL_ReadResult(sqlQue,i)
case ROW_MAP_FIRSTJOIN,ROW_LASTJOIN:
{
new date_str[32]
SQL_ReadResult(sqlQue,i,date_str,charsmax(date_str))

sestats_data[(SESTATS_FIRSTJOIN + (i - ROW_MAP_FIRSTJOIN))] = parse_time(date_str,"%Y-%m-%d %H:%M:%S")
}
}
}

ArrayPushArray(sestats_array,sestats_data)

SQL_NextRow(sqlQue)
}

new func_id = data[1]
new plugin_id = data[2]

if(callfunc_begin_i(func_id,plugin_id))
{
callfunc_push_int(int:sestats_array)

// передаваемые данные
if(dataSize > 3)
{
new cb_data[MAX_DATA_PARAMS]

for(new i ; i < (dataSize - 3) ; i++)
{
cb_data = data[(3 + i)]
}

callfunc_push_array(cb_data,(dataSize - 3))
callfunc_push_int((dataSize - 3))
}

callfunc_end()
}
else
{
log_amx("get_sestats_thread_sql callback function failed")
}
}
case SQL_AUTOCLEAR:
{
if(SQL_AffectedRows(sqlQue))
{
log_amx("deleted %d inactive entries",
SQL_AffectedRows(sqlQue)
)
}

DB_InitSeq()
}
}

return PLUGIN_HANDLED
}

//
// Поиск ID оружия по его лог коду
//
Info_Weapon_GetId(weapon[])
{
new weapon_info[WEAPON_INFO_SIZE]
new length = ArraySize(weapons_data)

for(new i ; i < length; i++)
{
ArrayGetArray(weapons_data,i,weapon_info)

new weapon_name[MAX_NAME_LENGTH]
copy(weapon_name,charsmax(weapon_name),weapon_info[MAX_NAME_LENGTH])

if(strcmp(weapon_name,weapon) == 0)
{
return i
}
}

return -1
}

//
// Поиск лог кода по ID оружия
//
Info_Weapon_GetLog(wpn_id,weapon_name[],len)
{
if(!(0 < wpn_id < ArraySize(weapons_data)))
{
formatex(weapon_name,len,"")
return
}

new weapon_info[WEAPON_INFO_SIZE]
ArrayGetArray(weapons_data,wpn_id,weapon_info)

copy(weapon_name,len,weapon_info[MAX_NAME_LENGTH])
}

/*
*
* API
*
*/

#define CHECK_PLAYER(%1) \
if (%1 < 1 || %1 > MaxClients) { \
log_error(AMX_ERR_NATIVE, "Player out of range (%d)", %1); \
return 0; \
} else { \
if (!is_user_connected(%1) || pev_valid(%1) != 2) { \
log_error(AMX_ERR_NATIVE, "Invalid player %d", %1); \
return 0; \
} \
}

#define CHECK_PLAYERRANGE(%1) \
if(%1 < 0 || %1 > MaxClients) {\
log_error(AMX_ERR_NATIVE,"Player out of range (%d)",%1);\
return 0;\
}

#define CHECK_WEAPON(%1) \
if(!(0 <= %1 < ArraySize(weapons_data))){\
log_error(AMX_ERR_NATIVE,"Invalid weapon id %d",%1);\
return 0;\
}

public plugin_natives()
{
// default csstats
register_library("xstats")

register_native("get_user_wstats","native_get_user_wstats")
register_native("get_user_wrstats","native_get_user_wrstats")
register_native("get_user_stats","native_get_user_stats")
register_native("get_user_rstats","native_get_user_rstats")
register_native("get_user_vstats","native_get_user_vstats")
register_native("get_user_astats","native_get_user_astats")
register_native("reset_user_wstats","native_reset_user_wstats")
register_native("get_stats","native_get_stats")
register_native("get_statsnum","native_get_statsnum")
register_native("get_user_stats2","native_get_user_stats2")
register_native("get_stats2","native_get_stats2")

register_native("xmod_is_melee_wpn","native_xmod_is_melee_wpn")
register_native("xmod_get_wpnname","native_xmod_get_wpnname")
register_native("xmod_get_wpnlogname","native_xmod_get_wpnlogname")
register_native("xmod_get_maxweapons","native_xmod_get_maxweapons")
register_native("xmod_get_stats_size","native_get_statsnum")
register_native("get_map_objectives","native_get_map_objectives")

register_native("custom_weapon_add","native_custom_weapon_add")
register_native("custom_weapon_dmg","native_custom_weapon_dmg")
register_native("custom_weapon_shot","native_custom_weapon_shot")

register_library("csstatsx_sql")

// csstats mysql
register_native("get_statsnum_sql","native_get_statsnum")
register_native("get_user_stats_sql","native_get_user_stats")
register_native("get_stats_sql","native_get_stats")
register_native("get_stats_sql_thread","native_get_stats_thread")
register_native("get_stats2_sql","native_get_stats2")
register_native("get_user_skill","native_get_user_skill")
register_native("get_skill","native_get_skill")

// 0.5.1
register_native("get_user_gametime","native_get_user_gametime")
register_native("get_stats_gametime","native_get_stats_gametime")
register_native("get_user_stats_id","native_get_user_stats_id")
register_native("get_stats_id","native_get_stats_id")
register_native("update_stats_cache","native_update_stats_cache")

// 0.7
register_native("get_user_stats3_sql","native_get_user_stats3")
register_native("get_stats3_sql","native_get_stats3")
register_native("get_user_wstats_sql","native_get_user_wstats_sql")
register_native("get_sestats_thread_sql","native_get_sestats_thread_sql")
register_native("get_sestats_read_count","native_get_sestats_read_count")
register_native("get_sestats_read_stats","native_get_sestats_read_stats")
register_native("get_sestats_read_stats2","native_get_sestats_read_stats2")
register_native("get_sestats_read_stats3","native_get_sestats_read_stats3")
register_native("get_sestats_read_online","native_get_sestats_read_online")
register_native("get_sestats_read_skill","native_get_sestats_read_skill")
register_native("get_sestats_read_map","native_get_sestats_read_map")
register_native("get_sestats_read_stime","native_get_sestats_read_stime")
register_native("get_sestats_read_etime","native_get_sestats_read_etime")
register_native("get_sestats_free","native_get_sestats_free")
register_native("get_user_firstjoin_sql","native_get_user_firstjoin_sql")
register_native("get_user_lastjoin_sql","native_get_user_lastjoin_sql")

// 0.7.2
register_native("xmod_get_maxweapons_sql","native_xmod_get_maxweapons")
}

public native_get_user_firstjoin_sql(plugin_id,params)
{
new id = get_param(1)
CHECK_PLAYERRANGE(id)

if(player_data[id][PLAYER_LOADSTATE] == LOAD_NO)
{
return -1
}

return player_data[id][PLAYER_FIRSTJOIN]
}

public native_get_user_lastjoin_sql(plugin_id,params)
{
new id = get_param(1)
CHECK_PLAYERRANGE(id)

if(player_data[id][PLAYER_LOADSTATE] == LOAD_NO)
{
return -1
}

return player_data[id][PLAYER_LASTJOIN]
}

public native_get_sestats_read_stime(plugin_id,params)
{
new Array:sestats = Array:get_param(1)
new index = get_param(2)

new sestats_size = ArraySize(sestats)

if(!(0 <= index < sestats_size))
{
log_error(AMX_ERR_NATIVE,"Stats index out of range (%d)",index)
return 0
}

new sestats_data[sestats_array_struct]
ArrayGetArray(sestats,index,sestats_data)

return sestats_data[SESTATS_FIRSTJOIN]
}

public native_get_sestats_read_etime(plugin_id,params)
{
new Array:sestats = Array:get_param(1)
new index = get_param(2)

new sestats_size = ArraySize(sestats)

if(!(0 <= index < sestats_size))
{
log_error(AMX_ERR_NATIVE,"Stats index out of range (%d)",index)
return 0
}

new sestats_data[sestats_array_struct]
ArrayGetArray(sestats,index,sestats_data)

return sestats_data[SESTATS_LASTJOIN]
}

public native_get_sestats_free(plugin_id,params)
{
new sestats = get_param_byref(1)
ArrayDestroy(Array:sestats)
set_param_byref(1,0)
return true
}

public native_get_sestats_read_map(plugin_id,params)
{
new Array:sestats = Array:get_param(1)
new index = get_param(2)
new length = get_param(4)

new sestats_size = ArraySize(sestats)

if(!(0 <= index < sestats_size))
{
log_error(AMX_ERR_NATIVE,"Stats index out of range (%d)",index)
return 0
}

new sestats_data[sestats_array_struct]
ArrayGetArray(sestats,index,sestats_data)

return set_string(3,sestats_data[SESTATS_MAP],length)
}

public Float:native_get_sestats_read_skill(plugin_id,params)
{
new Array:sestats = Array:get_param(1)
new index = get_param(2)

new sestats_size = ArraySize(sestats)

if(!(0 <= index < sestats_size))
{
log_error(AMX_ERR_NATIVE,"Stats index out of range (%d)",index)
return 0.0
}

new sestats_data[sestats_array_struct]
ArrayGetArray(sestats,index,sestats_data)

return sestats_data[SESTATS_SKILL]
}

public native_get_sestats_read_online(plugin_id,params)
{
new Array:sestats = Array:get_param(1)
new index = get_param(2)

new sestats_size = ArraySize(sestats)

if(!(0 <= index < sestats_size))
{
log_error(AMX_ERR_NATIVE,"Stats index out of range (%d)",index)
return 0
}

new sestats_data[sestats_array_struct]
ArrayGetArray(sestats,index,sestats_data)

return sestats_data[SESTATS_ONLINETIME]
}

public native_get_sestats_read_stats(plugin_id,params)
{
new Array:sestats = Array:get_param(1)
new index = get_param(2)

new sestats_size = ArraySize(sestats)

if(!(0 <= index < sestats_size))
{
log_error(AMX_ERR_NATIVE,"Stats index out of range (%d)",index)
return 0
}

new sestats_data[sestats_array_struct]
ArrayGetArray(sestats,index,sestats_data)

set_array(3,sestats_data[SESTATS_STATS],8)
set_array(4,sestats_data[SESTATS_HITS],8)

index ++

return (index >= sestats_size) ? 0 : index
}

public native_get_sestats_read_stats2(plugin_id,params)
{
new Array:sestats = Array:get_param(1)
new index = get_param(2)

new sestats_size = ArraySize(sestats)

if(!(0 <= index < sestats_size))
{
log_error(AMX_ERR_NATIVE,"Stats index out of range (%d)",index)
return 0
}

new sestats_data[sestats_array_struct]
ArrayGetArray(sestats,index,sestats_data)

set_array(3,sestats_data[SESTATS_STATS2],4)

index ++

return (index >= sestats_size) ? 0 : index
}

public native_get_sestats_read_stats3(plugin_id,params)
{
new Array:sestats = Array:get_param(1)
new index = get_param(2)

new sestats_size = ArraySize(sestats)

if(!(0 < index < sestats_size))
{
log_error(AMX_ERR_NATIVE,"Stats index out of range (%d)",index)
return 0
}

new sestats_data[sestats_array_struct]
ArrayGetArray(sestats,index,sestats_data)

set_array(3,sestats_data[SESTATS_STATS3],STATS3_END)

index ++

return (index >= sestats_size) ? 0 : index
}

public native_get_sestats_read_count(plugin_id,params)
{
new Array:sestats = Array:get_param(1)

return ArraySize(sestats)
}

public native_get_sestats_thread_sql(plugin_id,params)
{
// статистика по картам выключена
if(session_id == 0)
{
return false
}

new callback_func[32]
get_string(2,callback_func,charsmax(callback_func))

new func_id = get_func_id(callback_func,plugin_id)

// функция ответа не найдена
if(func_id == -1)
{
log_error(AMX_ERR_NATIVE,"Callback function ^"%s^" not found",callback_func)
return false
}

new data_size = get_param(4)

if(data_size > MAX_DATA_PARAMS)
{
log_error(AMX_ERR_NATIVE,"Max data size %d reached.",MAX_DATA_PARAMS)
return false
}

// подготавливаем данные
new sql_data[3 + MAX_DATA_PARAMS],data_array[MAX_DATA_PARAMS]

sql_data[0] = SQL_GETSESTATS
sql_data[1] = func_id
sql_data[2] = plugin_id

// передаваемые данные
if(data_size)
{
get_array(3,data_array,data_size)

for(new i ; i < data_size ; i++)
{
sql_data[i + 3] = data_array
}
}

new player_db_id = get_param(1) // ищем по ID игрока
new limit = get_param(5) // лимит на выборку

new query[QUERY_LENGTH]

formatex(query,charsmax(query),"SELECT * FROM `%s_maps` WHERE `%s` = '%d' ORDER BY `%s` DESC LIMIT %d",
tbl_name,
row_weapons_names[ROW_WEAPON_PLAYER],player_db_id,
row_names[ROW_FIRSTJOIN],limit
)
SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,3 + data_size)

return true
}

public native_get_user_wstats_sql(plugin_id,params)
{
if(params != 4)
{
log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 4, passed %d",params)

return false
}

if(!weapon_stats_enabled)
{
return -1
}

new player_id = get_param(1)
CHECK_PLAYERRANGE(player_id)

new weapon_id = get_param(2)
CHECK_WEAPON(weapon_id)

new stats[8],bh[8]

const stats_index_last = (STATS_END + HIT_END)

for(new i ; i < STATS_END ; i++)
{
stats = player_awstats[player_id][weapon_id] + player_awstats[player_id][weapon_id][i + stats_index_last]
}

// игрок не пользовался этим оружием
if(!stats[STATS_DEATHS] && !stats[STATS_SHOTS])
{
return false
}

for(new i = STATS_END ; i < (STATS_END + HIT_END) ; i ++)
{
bh[(i - STATS_END)] = player_awstats[player_id][weapon_id] + player_awstats[player_id][weapon_id][i + stats_index_last]
}

set_array(3,stats,sizeof stats)
set_array(4,bh,sizeof bh)

return true
}

public native_update_stats_cache()
{
return Cache_Stats_Update()
}

/*
* Функция возвращает онлайн время игрока
*
* native get_user_gametime(id)
*/
public native_get_user_gametime(plugin_id,params)
{
new id = get_param(1)
CHECK_PLAYERRANGE(id)

if(player_data[id][PLAYER_LOADSTATE] == LOAD_NO)
{
return -1
}

return player_data[id][PLAYER_ONLINE]
}

/*
* Получение времени по позиции
*
* native get_stats_gametime(index,&game_time)
*/
public native_get_stats_gametime(plugin_id,params)
{
new index = get_param(1) // индекс в статистике

// кеширование
new index_str[10],stats_cache[stats_cache_struct]
num_to_str(index,index_str,charsmax(index_str))

// есть информация в кеше
if(stats_cache_trie && TrieGetArray(stats_cache_trie,index_str,stats_cache,stats_cache_struct))
{
set_param_byref(2,stats_cache[CACHE_TIME])
return !stats_cache[CACHE_LAST] ? index + 1 : 0
}
// кеширование

return 0
}


/*
* Функция возрващает ID игрока в БД
*
* native get_user_stats_id(id)
*/
public native_get_user_stats_id(plugin_id,params)
{
new id = get_param(1)
CHECK_PLAYERRANGE(id)

return player_data[id][PLAYER_ID]
}

/*
* Получение ID по позиции
*
* native get_stats_id(index,&db_id)
*/
public native_get_stats_id(plugin_id,params)
{
new index = get_param(1) // индекс в статистике

// кеширование
new index_str[10],stats_cache[stats_cache_struct]
num_to_str(index,index_str,charsmax(index_str))

// есть информация в кеше
if(stats_cache_trie && TrieGetArray(stats_cache_trie,index_str,stats_cache,stats_cache_struct))
{
set_param_byref(2,stats_cache[CACHE_ID])
return !stats_cache[CACHE_LAST] ? index + 1 : 0
}
// кеширование

return 0
}

/*
* Функция возрващает скилл игрока
*
* native get_user_skill(player,&Float:skill)
*/
public native_get_user_skill(plugin_id,params)
{
new id = get_param(1)
CHECK_PLAYERRANGE(id)

set_float_byref(2,player_data[id][PLAYER_SKILL])

return true
}


/*
* Получение скилла по позиции
*
* native get_skill(index,&Float:skill)
*/
public native_get_skill(plugin_id,params)
{
new index = get_param(1) // индекс в статистике

// кеширование
new index_str[10],stats_cache[stats_cache_struct]

if(index < 0)
index = 0

num_to_str(index,index_str,charsmax(index_str))

// есть информация в кеше
if(stats_cache_trie && TrieGetArray(stats_cache_trie,index_str,stats_cache,stats_cache_struct))
{
set_float_byref(2,Float:stats_cache[CACHE_SKILL])
return !stats_cache[CACHE_LAST] ? index + 1 : 0
}
// кеширование

return 0
}

/*
* Добавление кастомного оружия для статистики
*
* native custom_weapon_add(const wpnname[], melee = 0, const logname[] = "")
*/
public native_custom_weapon_add(plugin_id,params)
{
if(ArraySize(weapons_data) >= MAX_WEAPONS)
{
return 0
}

new weapon_name[MAX_NAME_LENGTH],weapon_log[MAX_NAME_LENGTH],is_melee
get_string(1,weapon_name,charsmax(weapon_name))

if(params >= 2) // задан флаг is_melee
is_melee = get_param(2)

if(params == 3) // указан лог код
{
get_string(3,weapon_log,charsmax(weapon_log))
}
else // копируем название оружия для лог кода
{
copy(weapon_log,charsmax(weapon_log),weapon_name)
}

// регистриурем
new weapon_info[WEAPON_INFO_SIZE]
REG_INFO(is_melee,weapon_name,weapon_info)

return ArraySize(weapons_data) - 1
}

/*
* Учет урона кастомного оружия
*
* native custom_weapon_dmg(weapon, att, vic, damage, hitplace = 0)
*/
public native_custom_weapon_dmg(plugin_id,params)
{
new weapon_id = get_param(1)

CHECK_WEAPON(weapon_id)

new att = get_param(2)

CHECK_PLAYERRANGE(att)

new vic = get_param(3)

CHECK_PLAYERRANGE(vic)

new dmg = get_param(4)

if(dmg < 1)
{
log_error(AMX_ERR_NATIVE,"Invalid damage %d", dmg)

return 0
}

new hit_place = get_param(5)

return Stats_SaveHit(att,vic,dmg,weapon_id,hit_place)
}

/*
* Регистрация выстрела кастомного оружия
*
* native custom_weapon_shot(weapon, index)
*/
public native_custom_weapon_shot(plugin_id,params)
{
new weapon_id = get_param(1)

CHECK_WEAPON(weapon_id)

new id = get_param(2)

CHECK_PLAYERRANGE(id)

return Stats_SaveShot(id,weapon_id)
}

/*
* Возвращает true, если оружие рукопашного боя
*
* native xmod_is_melee_wpn(wpnindex)
*/
public native_xmod_is_melee_wpn(plugin_id,params)
{
new wpn_id = get_param(1)

CHECK_WEAPON(wpn_id)

new weapon_info[WEAPON_INFO_SIZE]
ArrayGetArray(weapons_data,wpn_id,weapon_info)

return weapon_info[0]
}

/*
* Получение полного названия оружия
*
* native xmod_get_wpnname(wpnindex, name[], len)
*/
public native_xmod_get_wpnname(plugin_id,params)
{
new wpn_id = get_param(1)

CHECK_WEAPON(wpn_id)

new weapon_info[WEAPON_INFO_SIZE]
ArrayGetArray(weapons_data,wpn_id,weapon_info)

new weapon_name[MAX_NAME_LENGTH]
copy(weapon_name,charsmax(weapon_name),weapon_info[1])

set_string(2,weapon_name,get_param(3))

return strlen(weapon_name)
}

/*
* Получение лог кода для оружия
*
* native xmod_get_wpnlogname(wpnindex, name[], len)
*/
public native_xmod_get_wpnlogname(plugin_id,params)
{
new wpn_id = get_param(1)
CHECK_WEAPON(wpn_id)

new weapon_name[MAX_NAME_LENGTH]
Info_Weapon_GetLog(wpn_id,weapon_name,get_param(3))

set_string(2,weapon_name,get_param(3))

return strlen(weapon_name)
}

/*
* Возврашение общего количества оружия для статистики
*
* native xmod_get_maxweapons()
*/
public native_xmod_get_maxweapons(plugin_id,params)
{
return ArraySize(weapons_data)
}

public native_get_map_objectives(plugin_id,params)
{
return false
}

/*
* Статистика за текущую сессию
*
* native get_user_wstats(index, wpnindex, stats[8], bodyhits[8])
*/
public native_get_user_wstats(plugin_id,params)
{
new id = get_param(1)

CHECK_PLAYERRANGE(id)

new wpn_id = get_param(2)

CHECK_WEAPON(wpn_id)

new stats[8],bh[8]
get_user_wstats(id,wpn_id,stats,bh)

set_array(3,stats,STATS_END)
set_array(4,bh,HIT_END)

return (stats[STATS_DEATHS] || stats[STATS_SHOTS])
}

/*
* Статистика за текущий раунд
*
* native get_user_wrstats(index, wpnindex, stats[8], bodyhits[8])
*/
public native_get_user_wrstats(plugin_id,params)
{
new id = get_param(1)

CHECK_PLAYERRANGE(id)

new wpn_id = get_param(2)

CHECK_WEAPON(wpn_id)

if(wpn_id != 0 && !(0 < wpn_id < MAX_WEAPONS))
{
log_error(AMX_ERR_NATIVE,"Weapon index out of bounds (%d)",id)

return false
}

new stats[8],bh[8]
get_user_wrstats(id,wpn_id,stats,bh)

set_array(3,stats,STATS_END)
set_array(4,bh,HIT_END)

return (stats[STATS_DEATHS] || stats[STATS_SHOTS])
}


/*
* Получение статистики игрока
*
* native get_user_stats(index, stats[8], bodyhits[8])
*/
public native_get_user_stats(plugin_id,params)
{
new id = get_param(1)

CHECK_PLAYERRANGE(id)

if(player_data[id][PLAYER_LOADSTATE] < LOAD_OK) // данные отсутствуют
{
return 0
}

set_array(2,player_data[id][PLAYER_STATS],8)
set_array(3,player_data[id][PLAYER_HITS],8)

return player_data[id][PLAYER_RANK]
}

/*
* Статистика за текущий раунд
*
* native get_user_rstats(index, stats[8], bodyhits[8])
*/
public native_get_user_rstats(plugin_id,params)
{
new id = get_param(1)

CHECK_PLAYERRANGE(id)

new stats[8],bh[8]
get_user_rstats(id,stats,bh)

set_array(2,stats,STATS_END)
set_array(3,bh,HIT_END)

return (stats[STATS_DEATHS] || stats[STATS_SHOTS])
}
/*
* Статистика по жертвам
*
* native get_user_vstats(index, victim, stats[8], bodyhits[8], wpnname[] = "", len = 0);
*/
public native_get_user_vstats(plugin_id,params)
{
if(params != 6)
{
log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 6, passed %d",params)

return false
}

new id = get_param(1)
new victim = get_param(2)

CHECK_PLAYERRANGE(id)
CHECK_PLAYERRANGE(victim)

new stats[STATS_END],hits[HIT_END],wname[MAX_NAME_LENGTH]
unpack_vstats(id,victim,stats,hits,wname,charsmax(wname))

set_array(3,stats,STATS_END)
set_array(4,hits,HIT_END)
set_string(5,wname,get_param(6))

return (stats[STATS_KILLS] || stats[STATS_HITS])
}


unpack_vstats(killer,victim,stats[STATS_END],hits[HIT_END],vname[],vname_len)
{
new i,stats_i

for(i = 0; i < STATS_END ; i++,stats_i++)
{
stats= player_vstats[killer][victim][stats_i]
}

for(i = 0; i < HIT_END ; i++,stats_i++)
{
hits= player_vstats[killer][victim][stats_i]
}

copy(vname,vname_len,player_vstats[killer][victim][stats_i])
}

unpack_astats(attacker,victim,stats[STATS_END],hits[HIT_END],vname[],vname_len)
{
new i,stats_i

for(i = 0; i < STATS_END ; i++,stats_i++)
{
stats= player_astats[victim][attacker][stats_i]
}

for(i = 0; i < HIT_END ; i++,stats_i++)
{
hits= player_astats[victim][attacker][stats_i]
}

copy(vname,vname_len,player_astats[victim][attacker][stats_i])
}

/*
* Статистика по врагам
*
* native get_user_astats(index, victim, stats[8], bodyhits[8], wpnname[] = "", len = 0);
*/
public native_get_user_astats(plugin_id,params)
{
if(params != 6)
{
log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 6, passed %d",params)

return false
}

new id = get_param(1)
new attacker = get_param(2)

CHECK_PLAYERRANGE(id)
CHECK_PLAYERRANGE(attacker)

new stats[STATS_END],hits[HIT_END],wname[MAX_NAME_LENGTH]
unpack_astats(attacker,id,stats,hits,wname,charsmax(wname))

set_array(3,stats,STATS_END)
set_array(4,hits,HIT_END)
set_string(5,wname,get_param(6))

return (stats[STATS_KILLS] || stats[STATS_HITS])
}

public native_reset_user_wstats()
{
new id = get_param(1)

CHECK_PLAYERRANGE(id)

return reset_user_wstats(id)
}

/*
* Возвращает общее количество записей в базе данных
*
* native get_statsnum()
*/
public native_get_statsnum(plugin_id,params)
{
return statsnum
}

/*
* Получение статистик по позиции
*
* native get_stats(index, stats[8], bodyhits[8], name[], len, authid[] = "", authidlen = 0);
*/
public native_get_stats(plugin_id,params)
{
// ждем начала работы с БД
if(!is_ready)
{
return 0
}

if(params < 5)
{
log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 5, passed %d",params)

return 0
}
else if(params > 5 && params != 7)
{
log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 7, passed %d",params)

return 0
}

new index = get_param(1) // индекс в статистике

// кеширование
new index_str[10],stats_cache[stats_cache_struct]
num_to_str(index,index_str,charsmax(index_str))

// есть информация в кеше
if(stats_cache_trie && TrieGetArray(stats_cache_trie,index_str,stats_cache,stats_cache_struct))
{
set_array(2,stats_cache[CACHE_STATS],sizeof stats_cache[CACHE_STATS])
set_array(3,stats_cache[CACHE_HITS],sizeof stats_cache[CACHE_HITS])
set_string(4,stats_cache[CACHE_NAME],get_param(5))

if(params == 7)
{
set_string(6,stats_cache[CACHE_STEAMID],get_param(7))
}

return !stats_cache[CACHE_LAST] ? index + 1 : 0
}
// кеширование

/*
* прямой запрос в БД, в случае если нету данных в кеше
*/

// открываем соединение с БД для получения актуальных данных
if(!DB_OpenConnection())
{
return false // ошибка открытия соединения
}
else
{
// задание на сброс содеинения
// чтобы не открывать новые и успеть получить сразу несколько данных за одно соединение
if(!task_exists(task_confin))
{
set_task(0.1,"DB_CloseConnection",task_confin)
}
}

// подготавливаем запрос в БД
new query[QUERY_LENGTH]
DB_QueryBuildGetstats(query,charsmax(query),.index = index)
new Handle:sqlQue = SQL_PrepareQuery(sql_con,query)

// ошибка выполнения запроса
if(!SQL_Execute(sqlQue))
{
new errNum,err[256]
errNum = SQL_QueryError(sqlQue,err,charsmax(err))

log_amx("SQL query failed")
log_amx("[ %d ] %s",errNum,err)
log_amx("[ SQL ] %s",query)

SQL_FreeHandle(sqlQue)

return 0
}

// читаем результат
new name[32],steamid[30],stats[8],hits[8],stats_count

DB_ReadGetStats(sqlQue,
name,charsmax(name),
steamid,charsmax(steamid),
stats,
hits,
.stats_count = stats_count,
.index = index
)

// статистики нет
if(!stats_count)
{
return false
}

SQL_FreeHandle(sqlQue)

// возвращаем данные натива
set_array(2,stats,sizeof player_data[][PLAYER_STATS])
set_array(3,hits,sizeof player_data[][PLAYER_HITS])
set_string(4,name,get_param(5))

if(params == 7)
{
set_string(6,steamid,get_param(7))
}

return stats_count > 1 ? index + 1 : 0
}

/*
* Получение статистик по позиции
*
* native get_stats2_sql(index, stats[4], authid[] = "", authidlen = 0)
*/
public native_get_stats2(plugin_id,params)
{
// ждем начала работы с БД
if(!is_ready)
{
return 0
}

if(params < 2)
{
log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 2, passed %d",params)

return false
}
else if(params > 2 && params != 4)
{
log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 4, passed %d",params)

return false
}

new index = get_param(1) // индекс в статистике

// кеширование
new index_str[10],stats_cache[stats_cache_struct]
num_to_str(index,index_str,charsmax(index_str))

// есть информация в кеше
if(stats_cache_trie && TrieGetArray(stats_cache_trie,index_str,stats_cache,stats_cache_struct))
{
set_array(2,stats_cache[CACHE_STATS2],sizeof stats_cache[CACHE_STATS2])

if(params == 4)
{
set_string(3,stats_cache[CACHE_STEAMID],get_param(4))
}

return !stats_cache[CACHE_LAST] ? index + 1 : 0
}
// кеширование

/*
* прямой запрос в БД, в случае если нету данных в кеше
*/

// открываем соединение с БД для получения актуальных данных
if(!DB_OpenConnection())
{
return false // ошибка открытия соединения
}
else
{
// задание на сброс содеинения
// чтобы не открывать новые и успеть получить сразу несколько данных за одно соединение
if(!task_exists(task_confin))
{
set_task(0.1,"DB_CloseConnection",task_confin)
}
}

// подготавливаем запрос в БД
new query[QUERY_LENGTH]
DB_QueryBuildGetstats(query,charsmax(query),.index = index)
new Handle:sqlQue = SQL_PrepareQuery(sql_con,query)

// ошибка выполнения запроса
if(!SQL_Execute(sqlQue))
{
new errNum,err[256]
errNum = SQL_QueryError(sqlQue,err,charsmax(err))

log_amx("SQL query failed")
log_amx("[ %d ] %s",errNum,err)
log_amx("[ SQL ] %s",query)

SQL_FreeHandle(sqlQue)

return 0
}

// читаем результат
new name[32],steamid[30],stats2[4],stats_count

DB_ReadGetStats(sqlQue,
name,charsmax(name),
steamid,charsmax(steamid),
.stats2 = stats2,
.stats_count = stats_count,
.index = index
)

// статистики нет
if(!stats_count)
{
return false
}

SQL_FreeHandle(sqlQue)

// возвращаем данные натива
set_array(2,stats2,sizeof player_data[][PLAYER_STATS2])

if(params == 4)
{
set_string(3,steamid,get_param(4))
}

return stats_count > 1 ? index + 1 : 0
}

/*
* Потоковый запрос на получение статистик по позиции
* id - для кого мы запрашиваем
* position - позиция
* top - кол-во
* callback[] - хандлер ответа
*
* native get_stats_sql_thread(id,position,top,callback[]);
*/
public native_get_stats_thread(plugin_id,params)
{
// ждем начала работы с БД
if(!is_ready)
{
return false
}

if(params < 4)
{
log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 4, passed %d",params)

return false
}

new id = get_param(1)
new position = min(statsnum,get_param(2))
new top = get_param(3)
new start_index = max((position - top),0)

new callback[20]
get_string(4,callback,charsmax(callback))

new func_id = get_func_id(callback,plugin_id)

if(func_id == -1)
{
log_error(AMX_ERR_NATIVE,"Unable to locate ^"%s^" handler.",callback)

return false
}

return DB_QueryTop15(id,plugin_id,func_id,position,start_index,top,params)
}

// 0.7
/*
* Получение статистик по позиции
*
* native get_stats3_sql(index, stats3[STATS3_END], authid[] = "", authidlen = 0)
*/
public native_get_stats3(plugin_id,params)
{
// ждем начала работы с БД
if(!is_ready)
{
return 0
}

if(params < 2)
{
log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 2, passed %d",params)

return false
}
else if(params > 2 && params != 4)
{
log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 4, passed %d",params)

return false
}

new index = get_param(1) // индекс в статистике

// кеширование
new index_str[10],stats_cache[stats_cache_struct]
num_to_str(index,index_str,charsmax(index_str))

// есть информация в кеше
if(stats_cache_trie && TrieGetArray(stats_cache_trie,index_str,stats_cache,stats_cache_struct))
{
set_array(2,stats_cache[CACHE_STATS3],sizeof stats_cache[CACHE_STATS3])

if(params == 4)
{
set_string(3,stats_cache[CACHE_STEAMID],get_param(4))
}

return !stats_cache[CACHE_LAST] ? index + 1 : 0
}
// кеширование

/*
* прямой запрос в БД, в случае если нету данных в кеше
*/

// открываем соединение с БД для получения актуальных данных
if(!DB_OpenConnection())
{
return false // ошибка открытия соединения
}
else
{
// задание на сброс содеинения
// чтобы не открывать новые и успеть получить сразу несколько данных за одно соединение
if(!task_exists(task_confin))
{
set_task(0.1,"DB_CloseConnection",task_confin)
}
}

// подготавливаем запрос в БД
new query[QUERY_LENGTH]
DB_QueryBuildGetstats(query,charsmax(query),.index = index)
new Handle:sqlQue = SQL_PrepareQuery(sql_con,query)

// ошибка выполнения запроса
if(!SQL_Execute(sqlQue))
{
new errNum,err[256]
errNum = SQL_QueryError(sqlQue,err,charsmax(err))

log_amx("SQL query failed")
log_amx("[ %d ] %s",errNum,err)
log_amx("[ SQL ] %s",query)

SQL_FreeHandle(sqlQue)

return 0
}

// читаем результат
new name[32],steamid[30],stats3[STATS3_END],stats_count

DB_ReadGetStats(sqlQue,
name,charsmax(name),
steamid,charsmax(steamid),
.stats3 = stats3,
.stats_count = stats_count,
.index = index
)

// статистики нет
if(!stats_count)
{
return false
}

SQL_FreeHandle(sqlQue)

// возвращаем данные натива
set_array(2,stats3,sizeof player_data[][PLAYER_STATS3])

if(params == 4)
{
set_string(3,steamid,get_param(4))
}

return stats_count > 1 ? index + 1 : 0
}

/*
* Получение статистик по позиции
*
* native get_user_stats3_sql(id,stats3[STATS3_END])
*/
public native_get_user_stats3(plugin_id,params)
{
new id = get_param(1)

CHECK_PLAYERRANGE(id)

if(player_data[id][PLAYER_LOADSTATE] < LOAD_OK) // данные отсутствуют
{
return 0
}

set_array(2,player_data[id][PLAYER_STATS3],STATS3_END)

return player_data[id][PLAYER_RANK]
}

public native_get_user_stats2(plugin_id,params)
{
new id = get_param(1)

CHECK_PLAYERRANGE(id)

set_array(2,player_data[id][PLAYER_STATS2],sizeof player_data[][PLAYER_STATS2])

return true
}

/*
*
* ВСЯКАЯ ХРЕНЬ ДЛЯ САМОСТОЯТЕЛЬНОГО ПРОСЧЕТА СТАТИСТИКИ
*
*/

is_tk(killer,victim)
{
if(killer == victim)
return true

return false
}

get_user_wstats(index, wpnindex, stats[8], bh[8])
{
for(new i ; i < STATS_END ; i++)
{
stats = player_wstats[index][wpnindex]
}

#define krisa[%1] player_wstats[index][wpnindex][STATS_END + %1]

for(new i ; i < HIT_END ; i++)
{
bh = krisa
}
}

get_user_wrstats(index, wpnindex, stats[8], bh[8])
{
for(new i ; i < STATS_END ; i++)
{
stats = player_wrstats[index][wpnindex]
}

for(new i ; i < HIT_END ; i++)
{
bh = player_wrstats[index][wpnindex][STATS_END + i]
}
}

get_user_rstats(index, stats[8], bh[8])
{
for(new i ; i < STATS_END ; i++)
{
stats = player_wrstats[index][0]
}

for(new i ; i < HIT_END ; i++)
{
bh = player_wrstats[index][0][STATS_END + i]
}
}

get_user_stats2(index, stats[4])
{
for(new i ; i < STATS2_END ; i++)
{
stats = player_wstats2[index]
}

return true
}

reset_user_wstats(index)
{
for(new i ; i < MAX_WEAPONS ; i++)
{
arrayset(player_wrstats[index],0,sizeof player_wrstats[][])
}

for(new i ; i < MAX_PLAYERS + 1 ;i++)
{
arrayset(player_vstats[index],0,sizeof player_vstats[][])
arrayset(player_astats[index],0,sizeof player_astats[][])
}

return true
}

reset_user_allstats(index)
{
for(new i ; i < MAX_WEAPONS ; i++)
{
arrayset(player_wstats[index],0,sizeof player_wstats[][])
}

arrayset(player_wstats2[index],0,sizeof player_wstats2[])

return true
}

public DB_OpenConnection()
{
if(!is_ready)
{
return false
}

if(sql_con != Empty_Handle)
{
return true
}

new errNum,err[256]
sql_con = SQL_Connect(sql,errNum,err,charsmax(err))

#if AMXX_VERSION_NUM >= 183
SQL_SetCharset(sql_con,"utf8")
#endif

if(errNum)
{
log_amx("SQL query failed")
log_amx("[ %d ] %s",errNum,err)

return false
}

return true
}

public DB_CloseConnection()
{
if(sql_con != Empty_Handle)
{
SQL_FreeHandle(sql_con)
sql_con = Empty_Handle
}
}

/********* mysql escape functions ************/
mysql_escape_string(dest[],len)
{
//copy(dest, len, source);
replace_all(dest,len,"\\","\\\\");
replace_all(dest,len,"\0","\\0");
replace_all(dest,len,"\n","\\n");
replace_all(dest,len,"\r","\\r");
replace_all(dest,len,"\x1a","\Z");
replace_all(dest,len,"'","''");
replace_all(dest,len,"^"","^"^"");
}


Вот что в логах пишет

Код:
L 07/10/2017 - 18:54:13: Start of error session.
L 07/10/2017 - 18:54:13: Info (map "de_inferno") (file "addons/amxmodx/logs/error_20170710.log")
L 07/10/2017 - 18:54:13: [MySQL] Thread worker was unable to start.
L 07/10/2017 - 18:54:13: [AMXX] Run time error 10 (plugin "csstatsx_sql.amxx") (native "SQL_ThreadQuery") - debug not enabled!
L 07/10/2017 - 18:54:13: [AMXX] To enable debug mode, add "debug" after the plugin name in plugins.ini (without quotes).
 

Пользователи, просматривающие эту тему

Сейчас на форуме нет ни одного пользователя.
Сверху Снизу