Run time error plugin "csstatsx_sql.amxx", version "0.7.4+2"

Сообщения
10
Реакции
0
Предупреждения
5
Ошибка
Run time error 10: native error (native "SQL_ThreadQuery")
ОС
Linux
Amx Mod X
v1.9.0.5249
Билд
1880
ReGamedll
5.9.0.363-dev
Версия Metamod
v1.3.0.128
Список метамодулей
[ 1] Rechecker      RUN   -    rechecker_mm_i386.so        v2.5            ini  Chlvl ANY  
[ 2] Reunion RUN - reunion_mm_i386.so v0.1.0.133 ini Start Never
[ 3] Revoice RUN - revoice_mm_i386.so v0.1.0.32 ini Start Never
[ 4] AMX Mod X RUN - amxmodx_mm_i386.so v1.9.0.5249 ini Start ANY
[ 5] ReSemiclip RUN - resemiclip_mm_i386.so v2.3.9 ini Chlvl ANY
[ 6] WHBlocker RUN - whblocker_mm_i386.so v1.5.696 ini Chlvl ANY
[ 7] MySQL RUN - mysql_amxx_i386.so v1.9.0.5249 pl4 ANY ANY
[ 8] CStrike RUN - cstrike_amxx_i386.so v1.9.0.5249 pl4 ANY ANY
[ 9] CSX RUN - csx_amxx_i386.so v1.9.0.5249 pl4 ANY ANY
[10] ReAimDetector RUN - reaimdetector_amxx_i386.so v0.2.2 pl4 ANY Never
[11] ReAPI RUN - reapi_amxx_i386.so v5.9.0.171-dev pl4 ANY Never
[12] Engine RUN - engine_amxx_i386.so v1.9.0.5249 pl4 ANY ANY
[13] FakeMeta RUN - fakemeta_amxx_i386.so v1.9.0.5249 pl4 ANY ANY
[14] Fun RUN - fun_amxx_i386.so v1.9.0.5249 pl4 ANY ANY
[15] Ham Sandwich RUN - hamsandwich_amxx_i386.so v1.9.0.5249 pl4 ANY ANY
[16] ReSRDetector RUN - resrdetector_mm_i386.so v0.1.0 ini Chlvl ANY
[17] hackdetector RUN - hackdetector_amxx_i386.so v0.15.328.lite pl4 ANY ANY
Список плагинов
[  1] Time for info           1.0         neugomon          time_for_info.a  running  
[ 2] Ultimate Chats Control 5.2(c) neygomon ucc.amxx running
[ 3] UCC Addon: VoteGAG 1.2 neygomon ucc_votegag.amx running
[ 4] ReAimDetector API 0.2.1 ReHLDS Team reaimdetector.a running
[ 5] HackDetector 0.15.lite Lev @ AGHL.RU De hackdetector.am running
[ 6] New Kick Menu 1.4.2 Radius newkickmenu.amx running
[ 7] Menus Front-End 1.5 untest neugomon menufront_end.a running
[ 8] admin_loader 0.3.7 kanagava admin_loader_fo running
[ 9] WebHS 0.1 kanagava fb_web_online.a running
[ 10] FreshBans 1.4.0b kanagava fresh_bans_140_ running
[ 11] fb_forwards 0.1.4 Kanagava & Realu fb_forwards.amx running
[ 12] Lite VoteBan 1.4 neygomon lite_voteban.am running
[ 13] AMXX reloadadmins 0.1 9 rpamm!? reloadadmins.am running
[ 14] AES: StatsX 0.5.9 [REA serfreeman1337/s aes_statsx_cstr running
[ 15] Lite Recoder 1.0.1 neygomon lite_recoder.am running
[ 16] FM ResetScore 0.1 neygomon fm_rs.amxx running
[ 17] AFK Control 1.4.1 [Rnd neygomon afk_control.amx running
[ 18] Unlimited Team Changes 1.3 HamletEagle unlimited_teamc running
[ 19] Lite Translit 2.8 neygomon lite_translit.a running
[ 20] Steam Bonus 1.3e Gudaus steam_bonus.amx running
[ 21] Admin ESP Mini 1.5 KoST admin_esp_mini. running
[ 22] Stats Auto Reset 1.0 Leo_[BH] stats_auto_rese running
[ 23] Top Awards 0.11h Safety1st top_awards.amxx running
[ 24] Block grande info 1.0 neygomon BlockGrenadeInf running
[ 25] [ReAPI] No Team Flash 0.0.2 Vaqtincha noteamflash_lit running
[ 26] Style C4 Timer 2.0 OciXCrom crx_c4timer.amx running
[ 27] Ping Checker 26.0.1 RC1 h1k3 ping_checker.am running
[ 28] Mode 2x2 2.5re s1lent mode2x2.amxx running
[ 29] [ReAPI] Parachute 1.1 ReHLDS Team parachute.amxx running
[ 30] [ReAPI] Custom Models 1.6.1 neugomon custom_models.a running
[ 31] unknown unknown unknown 51round_restart running
[ 32] KiLL Assist poka_4to_b [email protected] Kill_assist_rea running
[ 33] [ReAPI] Best player of 0.6 [email protected] best_player_of_ running
[ 34] Colored Flashbangs 1.0 v3x colored_flashba running
[ 35] Kills Counter 0.3 serfreeman1337 kills_counter.a running
[ 36] SpecList 1.2a FatalisDK speclist_rus.am running
[ 37] [ReAMX] SlayLosers RBS 18.10.13 SKAJIbnEJIb & d3 slaylosers_rbs. running
[ 38] C4 Sprites Timer 0.1.0 ConnorMcLeod c4_timer_spr.am running
[ 39] Info Rank 0.1 by CepeH9 info_rank_v0.1. running
[ 40] Hud Info 2.1 ill/Jack Daniel` hud_info.amxx running
[ 41] Admin Commands Log 1.2 w0w admin_commands_ running
[ 42] Killer ScreenFade 0.0.5 Vaqtincha reapi_killer_sc running
[ 43] unknown unknown unknown money_kill_knif running
[ 44] Spec Money Save 0.2 [email protected] spec_money_save running
[ 45] Real Nade Drops 0.4 VEN realnadedrops.a running
[ 46] Welcom 1.0 4el welcome_music.a running
[ 47] Admin Base 1.9.0.5249 AMXX Dev Team admin.amxx running
[ 48] Admin Commands 1.9.0.5249 AMXX Dev Team admincmd.amxx running
[ 49] Admin Help 1.9.0.5249 AMXX Dev Team adminhelp.amxx running
[ 50] Slots Reservation 1.9.0.5249 AMXX Dev Team adminslots.amxx running
[ 51] Multi-Lingual System 1.9.0.5249 AMXX Dev Team multilingual.am running
[ 52] Commands Menu 1.9.0.5249 AMXX Dev Team cmdmenu.amxx running
[ 53] Players Menu 1.9.0.5249 AMXX Dev Team plmenu.amxx running
[ 54] Maps Menu 1.9.0.5249 AMXX Dev Team mapsmenu.amxx running
[ 55] Plugin Menu 1.9.0.5249 AMXX Dev Team pluginmenu.amxx running
[ 56] Admin Chat 1.9.0.5249 AMXX Dev Team adminchat.amxx running
[ 57] Anti Flood 1.9.0.5249 AMXX Dev Team antiflood.amxx running
[ 58] Scrolling Message 1.9.0.5249 AMXX Dev Team scrollmsg.amxx running
[ 59] Info. Messages 1.9.0.5249 AMXX Dev Team imessage.amxx running
[ 60] Admin Votes 1.9.0.5249 AMXX Dev Team adminvote.amxx running
[ 61] NextMap 1.9.0.5249 AMXX Dev Team nextmap.amxx running
[ 62] Nextmap Chooser 1.9.0.5249 AMXX Dev Team mapchooser.amxx running
[ 63] TimeLeft 1.9.0.5249 AMXX Dev Team timeleft.amxx running
[ 64] Pause Plugins 1.9.0.5249 AMXX Dev Team pausecfg.amxx running
[ 65] Stats Configuration 1.9.0.5249 AMXX Dev Team statscfg.amxx running
[ 66] Restrict Weapons 1.9.0.5249 AMXX Dev Team restmenu.amxx running
[ 67] StatsX 1.9.0.5249 AMXX Dev Team statsx.amxx running
[ 68] CSStatsX SQL 0.7.4+2 serfreeman1337 csstatsx_sql.am debug
[ 69] Advanced Experience Sy 0.5.9 [REA serfreeman1337/s aes_main.amxx running
[ 70] AES: CStrike Addon 0.5.9 [REA serfreeman1337/s aes_exp_cstrike running
[ 71] AES: Informer 0.5.9 [REA serfreeman1337/s aes_informer.am running
[ 72] AES: Admin Tools 0.5.9 [REA serfreeman1337/s aes_exp_editor. running
[ 73] AES: Bonus System 0.5.9 Vega serfreeman1337/s aes_bonus_syste running
[ 74] AES: Bonus CSTRIKE 0.5.9.1 [R serfreeman1337/s aes_bonus_cstri running
[ 75] AMXBans: Screens Gm 1.6 Larte Team amxbans_ssban.a running
[ 76] Block Pickup Gold 0.1 Vaqtincha BlockPickupGold running
[ 77] V.I.P Custom Weapons 1.0.0 Vaqtincha vip_custom.amxx running
[ 78] V.I.P Custom KNIFE 1.0.0 Vaqtincha custom_knife.am running
[ 79] V.I.P Custom DEAGLE 1.0.0 Vaqtincha custom_deagle.a running
[ 80] V.I.P Custom M4A1 1.0.0 Vaqtincha custom_m4a1.amx running
[ 81] V.I.P Custom AK-47 1.0.0 Vaqtincha custom_ak47.amx running
[ 82] V.I.P Custom AWP 1.0.0 Vaqtincha custom_awp.amxx running
82 plugins, 82 running
В плагине есть ошибка, кто может подсказать как исправить?
Код:
L 09/09/2019 - 18:02:03: [AMXX] Displaying debug trace (plugin "csstatsx_sql.amxx", version "0.7.4+2")
L 09/09/2019 - 18:02:03: [AMXX] Run time error 10: native error (native "SQL_ThreadQuery")
L 09/09/2019 - 18:02:03: [AMXX]    [0] csstatsx_sql.sma::DB_QueryTop15 (line 3095)
L 09/09/2019 - 18:02:03: [AMXX]    [1] csstatsx_sql.sma::Cache_Stats_UpdateQueue (line 3003)
L 09/09/2019 - 18:02:03: [AMXX]    [2] csstatsx_sql.sma::SQL_Handler (line 3290)
Ошибка плавающая
 
Последнее редактирование модератором:
Сообщения
315
Реакции
257
Ошибки всегда именно по DB_QueryTop15 или могут быть другие? Где хостится сервер? Где БД? Какой пинг между сервером и БД?
 
Сообщения
10
Реакции
0
Предупреждения
5
voed,
1.всегда по DB_QueryTop15
2.
Код:
  6     2 ms     1 ms     1 ms  msk-ix-gw2.mnogobyte.net [195.208.208.246]
  7     5 ms     3 ms     2 ms  ip-core.mnogobyte.net [77.220.167.189]
  8     2 ms     2 ms     2 ms  77.220.187.150
3.
Код:
  6     2 ms     2 ms     2 ms  msk-m9-b1-xe1-0-0.fiord.ru [195.208.208.147]
  7     2 ms     2 ms     2 ms  msk-m9-b1-ae12-vlan712.fiord.net [62.140.243.140]
  8     2 ms     2 ms     2 ms  gw.ipserver.su [62.140.245.78]
  9     3 ms     2 ms     3 ms  59.fiord.gm-h.ws [195.88.208.59]
4.про пинг не подскажу, нет доступа ssh
Среда в 20:48
Top отображается не до конца
Отсутствует информация по 10му игроку,а именно
- попаданий
- в голову
- точность
- скилл
- звание (опыт)
и так всегда
18791
 
Последнее редактирование:
Сообщения
408
Реакции
782
D1esel,
Top отображается не до конца
Может просто не влезает весь текст в буфер? Можно сделать вывод strlen() буфера в чат при показе, и посмотреть. Ведь если мотд генерируется внутри плагина (т.е. вывод идёт не через внешний php-скрипт), то размер ограничивается. А у вас вон русскоязычные ранги, каждый символ по две ячейки жрёт.
Код:
/**
 * The maximum buffer size that can be displayed in a MOTD.
 */
#define MAX_MOTD_LENGTH 1536
 
Сообщения
10
Реакции
0
Предупреждения
5
BlackSignature, я прошу прощения за свою тупость, но в плагине csstatsx_sql.sma я не нашел переменной
Код:
/**
 * The maximum buffer size that can be displayed in a MOTD.
 */
#define MAX_MOTD_LENGTH 1536
Можно для дурачков, что и где необходимо поправить или добавить?
 

w0w

Сообщения
1.119
Реакции
2.158
BlackSignature, я прошу прощения за свою тупость, но в плагине csstatsx_sql.sma я не нашел переменной
Код:
/**
 * The maximum buffer size that can be displayed in a MOTD.
 */
#define MAX_MOTD_LENGTH 1536
Можно для дурачков, что и где необходимо поправить или добавить?
Это дефайн, в котором указана максимальная длина MOTD. Он не добавлен никуда. Если хочется вписать в плагин то можно вписать, от этого ничего не изменится, разве что текущий размер буффера меньше 1536.
 
Сообщения
408
Реакции
782
D1esel, если хочется разобраться, лимит ли это виноват, то в aes_statsx_cstrike.sma надо найти функцию SayTopFormer(), в ней в самом низу добавить
Код:
+ client_print(id, print_chat, "motd len: %i", strlen(theBuffer))
show_motd(id,theBuffer,title)
Обойти лимит без модификации плагина под работу через внешний php-скрипт не получится, а на коленке этого не сделать.
 
Сообщения
10
Реакции
0
Предупреждения
5
BlackSignature, описанной функции SayTopFormer нет, либо она называется как-то по другому
Код:
/*
*    CSStatsX SQL                        v. 0.7.4+2
*    by serfreeman1337        https://github.com/serfreeman1337
*/

#include <amxmodx>
#include <sqlx>

//#define REAPI

#if !defined REAPI
    #include <hamsandwich>
#else
    #include <reapi>
#endif

#include <fakemeta>

#define PLUGIN "CSStatsX SQL"
#define VERSION "0.7.4+2"
#define AUTHOR "serfreeman1337"

#define LASTUPDATE "14, May(05), 2019"

#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 CSX_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.4+1
    CVAR_PAUSE
}


// 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][CSX_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][CSX_MAX_WEAPONS][STATS_END + HIT_END]

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

// wrstats rstats
new player_wrstats[MAX_PLAYERS + 1][CSX_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

// 0.7.4+2

new evts_guns_bitsum

// am i doing it right ?
new const evt_for_wpn[29] = {
    0, CSW_AWP, CSW_G3SG1, CSW_AK47, CSW_SCOUT, CSW_M249, CSW_M4A1, CSW_SG552, CSW_AUG, CSW_SG550,
    CSW_M3, CSW_XM1014, CSW_USP, CSW_MAC10, CSW_UMP45, CSW_FIVESEVEN, CSW_P90, CSW_DEAGLE, CSW_P228,
    0, CSW_GLOCK18, CSW_MP5NAVY, CSW_TMP, CSW_ELITE, CSW_ELITE, 0, 0, CSW_GALIL, CSW_FAMAS
}

// макрос для помощи реагистрации инфы по оружию
#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)
    
    #if AMXX_VERSION_NUM >= 183

    //
    // For AMXX 1.8.3 and higher
    // Configuration file: amxmodx/configs/plugins/plugin-csstatsx_sql.cfg
    //
    
        create_cvar("csstatsx_sql", VERSION, FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_UNLOGGED|FCVAR_SPONLY, "Plugin version^nDo not edit this cvar")
        cvar[CVAR_SQL_HOST] = create_cvar("csstats_sql_host", "localhost", FCVAR_PROTECTED, "MySQL host")
        cvar[CVAR_SQL_USER] = create_cvar("csstats_sql_user", "root", FCVAR_PROTECTED, "MySQL user")
        cvar[CVAR_SQL_PASS] = create_cvar("csstats_sql_pass", "", FCVAR_PROTECTED, "MySQL user password")
        cvar[CVAR_SQL_DB] = create_cvar("csstats_sql_db", "amxx", FCVAR_PROTECTED, "DB Name")
        cvar[CVAR_SQL_TABLE] = create_cvar("csstats_sql_table", "csstats", FCVAR_PROTECTED, "Table name")
        cvar[CVAR_SQL_TYPE] = create_cvar("csstats_sql_type", "mysql", FCVAR_NONE, "Database type^n\
                                                mysql - MySQL^n\
                                                sqlite - SQLite")
        cvar[CVAR_SQL_CREATE_DB] = create_cvar("csstats_sql_create_db", "1", FCVAR_NONE, "Auto create tables^n\
                                                0 - don't send create table query^n\
                                                1 - send create table query on map load")
        cvar[CVAR_UPDATESTYLE] = create_cvar("csstats_sql_update", "-1", FCVAR_NONE, "How to update player stats in db^n\
                                                -2 - on death and disconnect^n\
                                                -1 - on round end and disconnect^n\
                                                0 - on disconnect^n\
                                                higher than 0 - every n seconds and disconnect")
        cvar[CVAR_USEFORWARDS] = create_cvar("csstats_sql_forwards", "0", FCVAR_NONE, "Enable own forwards for client_death, client_damage^n\
                                                0 - disable^n\
                                                1 - enable. required if you want replace csx module")
        cvar[CVAR_RANKFORMULA] = create_cvar("csstats_sql_rankformula", "0", FCVAR_NONE, "How to rank player^n\
                                                0 - kills- deaths - tk^n\
                                                1 - kills^n\
                                                2 - kills + hs^n\
                                                3 - skill^n\
                                                4 - online time")
        cvar[CVAR_SKILLFORMULA] = create_cvar("csstats_sql_skillformula", "0", FCVAR_NONE, "Skill formula^n\
                                                0 - The ELO Method")
        cvar[CVAR_WEAPONSTATS] = create_cvar("csstats_sql_weapons", "0", FCVAR_NONE, "Enable weapon stats (/rankstats)^n\
                                                0 - disable^n\
                                                1 - enable^n\
                                                This will create new table csstats_weapons in your database^n\
                                                NOTE: table will be created only if you set cvar csstats_sql_create_db to 1")
        cvar[CVAR_MAPSTATS] = create_cvar("csstats_sql_maps", "0", FCVAR_NONE, "Enable player session stats (/sestats)^n\
                                                0 - disable^n\
                                                1 - enable^n\
                                                NOTE: you need to import csstats_maps.sql^n\
                                                Check install instructions")
        cvar[CVAR_AUTOCLEAR] = create_cvar("csstats_sql_autoclear", "0", FCVAR_NONE, "Number of inactive days after which player's stats will be retested. (prune function)")
        cvar[CVAR_CACHETIME] = create_cvar("csstats_sql_cachetime", "-1", FCVAR_NONE, "Cache option^n\
                                                -1 - enabled^n\
                                                0 - disabled^n\
                                                NOTE: Doesn't work with csstats_sql_update -2 or 0")
        cvar[CVAR_AUTOCLEAR_DAY] = create_cvar("csstats_sql_autoclear_day", "0", FCVAR_NONE, "Full stats reset in specified day of month")
        cvar[CVAR_ASSISTHP] = create_cvar("csstats_sql_assisthp", "50", FCVAR_NONE, "Minimum damage to count assist^n0 - disable this feature")
        // csx
        cvar[CVAR_RANK] = get_cvar_pointer("csstats_rank")
        
        if(!cvar[CVAR_RANK])
            cvar[CVAR_RANK] = create_cvar("csstats_rank", "1", FCVAR_NONE, "Rank mode^n\
                                            0 - by nick^n\
                                            1 - by authid^n\
                                            2 - by ip")
        cvar[CVAR_RANKBOTS] = get_cvar_pointer("csstats_rankbots")
        
        if(!cvar[CVAR_RANKBOTS])
            cvar[CVAR_RANKBOTS] = create_cvar("csstats_rankbots", "1", FCVAR_NONE, "Rank bots^n\
                                                0 - do not rank bots^n\
                                                1 - rank bots")
        cvar[CVAR_PAUSE] = get_cvar_pointer("csstats_pause")
        
        if(!cvar[CVAR_PAUSE]) {
            cvar[CVAR_PAUSE] = create_cvar("csstats_pause", "0", FCVAR_NONE, "Pause stats^n\
                                                0 - do not pause stats^n\
                                                1 - pause stats")
        }
        // i am retarded ?
        hook_cvar_change(cvar[CVAR_PAUSE], "CvarHook_PauseStats")


        // csx

        AutoExecConfig()
    #else
    
    //
    // For AMX Mod X 1.8.2
    // Write cvars in amxx.cfg !
    //
    
        register_cvar("csstatsx_sql", VERSION, FCVAR_SERVER | FCVAR_SPONLY | FCVAR_UNLOGGED)
        /*
        * хост mysql
        */
        cvar[CVAR_SQL_HOST] = register_cvar("csstats_sql_host","localhost",FCVAR_UNLOGGED|FCVAR_PROTECTED)
        
        /*
        * пользователь mysql
        */
        cvar[CVAR_SQL_USER] = register_cvar("csstats_sql_user","root",FCVAR_UNLOGGED|FCVAR_PROTECTED)
        
        /*
        * пароль mysql
        */
        cvar[CVAR_SQL_PASS] = register_cvar("csstats_sql_pass","",FCVAR_UNLOGGED|FCVAR_PROTECTED)
        
        /*
        * название БД mysql или sqlite
        */
        cvar[CVAR_SQL_DB] = register_cvar("csstats_sql_db","amxx",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","1")
        
        /*
        * как обновлять статистику игрока в БД
        *    -2             - при смерти и дисконнекте
        *    -1            - в конце раунда и дисконнекте
        *    0             - при дисконнекте
        *    значение больше 0     - через указанное кол-во секунд и дисконнекте
        */
        cvar[CVAR_UPDATESTYLE] = register_cvar("csstats_sql_update","-1")
        
        /*
        * включить собственные форварды для client_death, client_damage
        *    0            - выключить
        *    1            - включить, небоходимо, если csstats_sql используется в качестве замены модуля
        */
        cvar[CVAR_USEFORWARDS] = register_cvar("csstats_sql_forwards","0")
        
        /*
        * формула расчета ранга
        *    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","0")
        
        /*
        * использование кеша для 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","50")
        
        cvar[CVAR_PAUSE] = get_cvar_pointer("csstats_pause")
        
        if(!cvar[CVAR_PAUSE])
            cvar[CVAR_PAUSE] = register_cvar("csstats_pause", "0")
        
        MaxClients = get_maxplayers()
    #endif
}


#if AMXX_VERSION_NUM >= 183
// sure i am
new bool:pause_stats
public CvarHook_PauseStats(pcvar, const old_value[], const new_value[]) {
    pause_stats = (str_to_num(new_value) > 0)
}
#endif

is_stats_paused() {
    #if AMXX_VERSION_NUM >= 183
    return pause_stats
    #else
    return get_pcvar_num(cvar[CVAR_PAUSE])
    #endif
}

public plugin_init()
{
    register_logevent("LogEventHooK_RoundEnd", 2, "1=Round_End")
    register_logevent("LogEventHooK_RoundStart", 2, "1=Round_Start")
    
    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")
    
    #if !defined REAPI
    RegisterHam(Ham_Spawn,"player","HamHook_PlayerSpawn",true)
    #else
    RegisterHookChain(RG_CBasePlayer_Spawn, "RGHook_PlayerSpawn", true)
    #endif
    
    // credits to ConnorMcLeod (https://forums.alliedmods.net/showpost.php?p=1725505&postcount=49)
    new const evts_guns[][] = {
        "events/awp.sc", "events/g3sg1.sc", "events/ak47.sc", "events/scout.sc", "events/m249.sc",
        "events/m4a1.sc", "events/sg552.sc", "events/aug.sc", "events/sg550.sc", "events/m3.sc",
        "events/xm1014.sc", "events/usp.sc", "events/mac10.sc", "events/ump45.sc", "events/fiveseven.sc",
        "events/p90.sc", "events/deagle.sc", "events/p228.sc", "events/glock18.sc", "events/mp5n.sc",
        "events/tmp.sc", "events/elite_left.sc", "events/elite_right.sc", "events/galil.sc", "events/famas.sc"
    }
    
    for(new i ; i < sizeof(evts_guns) ; i++) {
        evts_guns_bitsum |= (1 << engfunc(EngFunc_PrecacheEvent, 1, evts_guns[i]))
    }
    
    register_forward(FM_PlaybackEvent, "FMHook_OnEventPlayback")
}

#if AMXX_VERSION_NUM < 183
    public plugin_cfg()
#else
    public OnConfigsExecuted()
#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",
            type)
            
        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)
        
        #if !defined REAPI
        register_forward(FM_SetModel,"FMHook_SetModel",true)
        #else
        RegisterHookChain(RG_ThrowFlashbang, "RGHook_ThrowFlashbang", true)
        RegisterHookChain(RG_ThrowHeGrenade, "RGHook_ThrowHeGrenade", true)
        RegisterHookChain(RG_ThrowSmokeGrenade, "RGHook_ThrowSmokeGrenade", true)
        #endif
    }
    
    // 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[i])
    }
}

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()
{
    if(!is_ready) {
        return
    }
    
    // выполняем накопившиеся запросы при смене карты или выключении серваре
    DB_FlushQuery()
    
    if(sql_con != Empty_Handle) {
        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 < CSX_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)
}

#if !defined REAPI
public HamHook_PlayerSpawn(id)
#else
public RGHook_PlayerSpawn(id)
#endif
{
    reset_user_wstats(id)
}

//
// Shots registration
//
public FMHook_OnEventPlayback(flags, invoker, eventid) {
    if(!(evts_guns_bitsum & (1 << eventid)) || !(1 <= invoker <= MaxClients))
        return FMRES_IGNORED
    
    Stats_SaveShot(invoker, evt_for_wpn[eventid])
        
    return FMRES_HANDLED
}

//
// Регистрация попадания
//
public EventHook_Damage(player)
{
    static damage_take;damage_take = read_data(2)
    
    // thanks voed
    static weapon_id,last_hit,attacker,bool:alive
    attacker = get_user_attacker(player,weapon_id,last_hit)
    alive = (is_user_alive(player) ? true : false)
    
    if(!is_user_connected(attacker)) {
        
        if(!alive) {
            Stats_SaveKill(0,player,0,0)
        }
        
        return PLUGIN_CONTINUE
    }
    
    if(0 <= last_hit < HIT_END)
    {
        Stats_SaveHit(attacker,player,damage_take,weapon_id,last_hit)
    }
    
    if(!alive) {
        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[i]
        
        // считаем статистику побед по командам
        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[i]
        
        // считаем статистику побед по командам
        if(player_data[player][PLAYER_STATS3][STATS3_CURRENTTEAM] == 2)
        {
            player_data[player][PLAYER_STATS3][STATS3_WINCT] ++
        }
    }
}

//
// Форвард grenade_throw
//
#if !defined REAPI
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
}
#else
public RGHook_ThrowFlashbang(const index) {
    ExecuteForward(FW_GThrow, dummy_ret, index, GetHookChainReturn(ATYPE_INTEGER), CSW_FLASHBANG)
}
public RGHook_ThrowHeGrenade(const index) {
    ExecuteForward(FW_GThrow, dummy_ret, index, GetHookChainReturn(ATYPE_INTEGER), CSW_HEGRENADE)
}
public RGHook_ThrowSmokeGrenade(const index) {
    ExecuteForward(FW_GThrow, dummy_ret, index, GetHookChainReturn(ATYPE_INTEGER), CSW_SMOKEGRENADE)
}
#endif
//
// Учет ассистов
//
Stats_SaveAssist(player,victim,assisted)
{
    ExecuteForward(FW_Assist,dummy_ret,player,victim,assisted)
    
    if(is_stats_paused()) {
        return false
    }
    
    player_data[player][PLAYER_STATS3][STATS3_ASSIST] ++
    
    return true
}

//
// Учет выстрелов
//
Stats_SaveShot(player,wpn_id)
{
    if(is_stats_paused()) {
        return false
    }
    
    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)
{
    if(FW_Damage)
        ExecuteForward(FW_Damage,dummy_ret,attacker,victim,damage,wpn_id,hit_place,is_tk(attacker,victim))
        
    if(is_stats_paused()) {
        return false
    }
    
    if(attacker == victim) {
        return false
    }
    
    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]
    )
        
    return true
}

//
// Учет смертей
//
Stats_SaveKill(killer,victim,wpn_id,hit_place)
{
    if(FW_Death)
        ExecuteForward(FW_Death,dummy_ret,killer,victim,wpn_id,hit_place,is_tk(killer,victim))
        
    if(is_stats_paused()) {
        return false
    }
    
    if(killer == victim || !killer) // не учитываем суицид
    {
        player_wstats[victim][0][STATS_DEATHS] ++
        player_wrstats[victim][0][STATS_DEATHS] ++
        
        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][i][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(player_data[killer][PLAYER_LOADSTATE] == LOAD_OK && player_data[victim][PLAYER_LOADSTATE] == LOAD_OK) // скилл расчитывается только при наличии статистики из БД
    {
        switch(get_pcvar_num(cvar[CVAR_SKILLFORMULA])) // расчет скилла
        {
            case -1: // Pre 0.7.4+1 ELO
            {
                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)
            }
            case 0: // The ELO Method (http://fastcup.net/rating.html)
            {
                // thanks In-line
                new Float:delta = 1.0 / (1.0 + floatpower(10.0,(player_data[killer][PLAYER_SKILL] - player_data[victim][PLAYER_SKILL]) / 100.0))
                new Float:killer_koeff = (player_data[killer][PLAYER_STATS][STATS_KILLS] < 100) ? 2.0 : 1.5
                new Float:victim_koeff = (player_data[victim][PLAYER_STATS][STATS_KILLS] < 100) ? 2.0 : 1.5
                
                player_data[killer][PLAYER_SKILL] += (killer_koeff * delta)
                player_data[victim][PLAYER_SKILL] -= (victim_koeff * delta)
            }
        }
    }
    
    
    // обновляем статистику в БД при смерти
    if(get_pcvar_num(cvar[CVAR_UPDATESTYLE]) == -2)
    {
        DB_SavePlayerData(victim)
    }
    
    
    
    return true
}

//
// Учет статистики по бомба
//
Stats_SaveBDefusing(id)
{
    if(FW_BDefusing)
        ExecuteForward(FW_BDefusing,dummy_ret,id)
        
    if(is_stats_paused()) {
        return false
    }
    
    player_wstats2[id][STATS2_DEFAT] ++
        
    return true
}

Stats_SaveBDefused(id)
{
    if(FW_BDefused)
        ExecuteForward(FW_BDefused,dummy_ret,id)
        
    if(is_stats_paused()) {
        return false
    }
    
    player_wstats2[id][STATS2_DEFOK] ++
        
    return true
}

Stats_SaveBPlanted(id)
{
    if(FW_BPlanted)
        ExecuteForward(FW_BPlanted,dummy_ret,id)
        
    if(is_stats_paused()) {
        return false
    }
    
    player_wstats2[id][STATS2_PLAAT] ++
        
    return true
}

Stats_SaveBExplode(id)
{
    if(FW_BExplode)
        ExecuteForward(FW_BExplode,dummy_ret,id,g_defuser)
        
    if(is_stats_paused()) {
        return false
    }
    
    player_wstats2[id][STATS2_PLAOK] ++
        
    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[i]
        
        // определяем в какой команде игрок
        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[i] = stats[i] - player_data[id][PLAYER_STATSLAST][i] // узнаем разницу
                player_data[id][PLAYER_STATSLAST][i] = stats[i]
                
                if(diffstats[i])
                {
                    len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + %d",
                        !to_save ? " " : ",",
                        row_names[i + ROW_KILLS],
                        row_names[i + ROW_KILLS],
                        diffstats[i]
                    )
                    
                    to_save ++
                }
            }
            
            // обновляем по разнице с предедущими данными
            for(i = 0 ; i < sizeof player_data[][PLAYER_STATS2] ; i++)
            {
                diffstats2[i] = stats2[i] - player_data[id][PLAYER_STATS2LAST][i] // узнаем разницу
                player_data[id][PLAYER_STATS2LAST][i] = stats2[i]
                
                if(diffstats2[i])
                {
                    len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + %d",
                        !to_save ? " " : ",",
                        row_names[i + ROW_BOMBDEF],
                        row_names[i + ROW_BOMBDEF],
                        diffstats2[i]
                    )
                    
                    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[i] = hits[i] - player_data[id][PLAYER_HITSLAST][i] // узнаем разницу
                    player_data[id][PLAYER_HITSLAST][i] = hits[i]
                    
                    if(diffhits[i])
                    {
                        len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + '%d'",
                            !to_save ? " " : ",",
                            row_names[i + ROW_H0],row_names[i + ROW_H0],
                            diffhits[i]
                        )
                    }
                }
            }
            
            // 0.7
            new diffstats3[STATS3_END]
            
            for(i = STATS3_CONNECT ; i < sizeof player_data[][PLAYER_STATS3] ; i++)
            {
                diffstats3[i] = player_data[id][PLAYER_STATS3][i] - player_data[id][PLAYER_STATS3LAST][i]
                
                if(diffstats3[i])
                {
                    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[i]
                    )
                    
                    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]
            
            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][i] += diffstats[i]
                }
                
                for(new i ; i < sizeof player_data[][PLAYER_HITS] ; i++)
                {
                    player_data[id][PLAYER_HITS][i] += diffhits[i]
                }
                
                for(new i ; i < sizeof player_data[][PLAYER_STATS2] ; i++)
                {
                    player_data[id][PLAYER_STATS2][i] += diffstats2[i]
                }
                
                for(i = STATS3_CONNECT ; i < sizeof player_data[][PLAYER_STATS3] ; i++)
                {
                    player_data[id][PLAYER_STATS3LAST][i] = player_data[id][PLAYER_STATS3][i]
                }
                
                player_data[id][PLAYER_ONLINELAST] = player_data[id][PLAYER_ONLINE]
            }
        }
        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][i] = stats[i]
            }
                
            for(new i ; i < sizeof player_data[][PLAYER_HITS] ; i++)
            {
                player_data[id][PLAYER_HITS][i] = hits[i]
            }
                
            for(new i ; i < sizeof player_data[][PLAYER_STATS2] ; i++)
            {
                player_data[id][PLAYER_STATS2][i] = stats2[i]
            }
                
            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 < CSX_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[i]
        
        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[i])
    }
}

/*
* запрос на просчет ранка
*/
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][i] = 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][i] = player_data[id][PLAYER_STATS3LAST][i] = SQL_ReadResult(sqlQue,(i - 1) + ROW_CONNECTS)
                    
                    // плюсуем стату подключений
                    if(i == STATS3_CONNECT)
                    {
                        player_data[id][PLAYER_STATS3][i] ++
                    }
                }
                
                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)
                }
                
                // плюсуем стату подключений
                player_data[id][PLAYER_STATS3][STATS3_CONNECT] ++
            }
            
            // обновляем позици игроков
            // действие с задержкой, что-бы учесть изменения при множественном обновлении данных
            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[i]
                
                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[i][PLAYER_ID] == pK)    // задаем ранк по первичному ключу
                    {
                        player_data[i][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][i] = SQL_ReadResult(sqlQue,i + ROW_WEAPON_KILLS)
                }
                
                player_awstats[id][wpn][load_index] = _:LOAD_OK
                    
                SQL_NextRow(sqlQue)
            }
            
            // помечаем статистику по другим оружиям как новую
            for(new wpn ; wpn < CSX_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[i] = 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[i]
        }
    }
    
    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[i] = player_awstats[player_id][weapon_id][i] + 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][i] + 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) >= CSX_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 < CSX_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[i]= player_vstats[killer][victim][stats_i]
    }
    
    for(i = 0; i < HIT_END ; i++,stats_i++)
    {
        hits[i]= 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[i]= player_astats[victim][attacker][stats_i]
    }
    
    for(i = 0; i < HIT_END ; i++,stats_i++)
    {
        hits[i]= 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[i] = player_wstats[index][wpnindex][i]
    }
    
    #define krisa[%1] player_wstats[index][wpnindex][STATS_END + %1]
    
    for(new i ; i < HIT_END ; i++)
    {
        bh[i] = krisa[i]
    }
}

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

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

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

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

reset_user_allstats(index)
{
    for(new i ; i < CSX_MAX_WEAPONS ; i++)
    {
        arrayset(player_wstats[index][i],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(errNum)
    {
        log_amx("SQL query failed")
        log_amx("[ %d ] %s",errNum,err)
            
        return false
    }
    
    #if AMXX_VERSION_NUM >= 183
    SQL_SetCharset(sql_con,"utf8")
    #endif
    
    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,"^"","^"^"");
}
 
Сообщения
10
Реакции
0
Предупреждения
5
BlackSignature, добавил, скомпилил, закинул на сервак, перезагрузил
Код:
/*
*    AES: StatsX                 v. 0.5
*    by serfreeman1337        http://1337.uz/
*/

#include <amxmodx>

#define AES            // расскомментируйте для поддержки AES (http://1337.uz/advanced-experience-system/)
#define CSSTATSX_SQL        // расскомментируйте для поддержки CSstatsX SQL (http://1337.uz/csstatsx-sql/)

#if defined AES
    #include <aes_v>

    native Float:aes_get_exp_for_stats_f(stats[8],stats2[4])
#endif

#if defined CSSTATSX_SQL
    #include <csstatsx_sql>
    #include <time>
    native xmod_get_wpnname(wpnindex, name[], len);
    native xmod_get_maxweapons();
    native get_user_wstats(index, wpnindex, stats[8], bodyhits[8]);
#else
    #include <csx>
#endif

#define PLUGIN "AES: StatsX"
#define VERSION "0.5.9 [REAPI]"
#define AUTHOR "serfreeman1337/sonyx"
#define LASTUPDATE "12, March (03), 2018"

#if AMXX_VERSION_NUM < 183
    #include <colorchat>

    #define print_team_default DontChange
    #define print_team_grey Grey
    #define print_team_red Red
    #define print_team_blue Blue

    #define MAX_NAME_LENGTH    32
    #define MAX_PLAYERS 32

    #define client_disconnected client_disconnect
#endif

/* - CVARS - */
enum _:cvars {
    CVAR_MOTD_DESC,
    CVAR_CHAT_DESC,
    CVAR_SESTATS_DESC,
    CVAR_SKILL,
    CVAR_MOTD_SKILL_FMT
}

new cvar[cvars]

/* - RANDOM STUFF */

// User stats parms id
#define STATS_KILLS             0
#define STATS_DEATHS            1
#define STATS_HS                2
#define STATS_TKS               3
#define STATS_SHOTS             4
#define STATS_HITS              5
#define STATS_DAMAGE            6

#define MAX_TOP            10

/* - SKILL - */

new const g_skill_letters[][] = {
    "L-",
    "L",
    "L+",
    "M-",
    "M",
    "M+",
    "H-",
    "H",
    "H+",
    "P-",
    "P",
    "P+",
    "G"
}

new const g_skill_class[][] = {
    "Lm",
    "L",
    "Lp",
    "Mm",
    "M",
    "Mp",
    "Hm",
    "H",
    "Hp",
    "Pm",
    "P",
    "Pp",
    "G"
}

// Global player flags.
new const BODY_PART[8][] =
{
    "WHOLEBODY",
    "AES_HEAD",
    "AES_CHEST",
    "AES_STOMACH",
    "AES_LARM",
    "AES_RARM",
    "AES_LLEG",
    "AES_RLEG"
}

new Float:g_skill_opt[sizeof g_skill_letters]

#define BUFF_LEN 1535

new theBuffer[BUFF_LEN + 1] = 0

#define MENU_LEN 512

new g_MenuStatus[MAX_PLAYERS + 1][2]

public SayStatsMe           = 0 // displays user's stats and rank
public SayRankStats         = 0 // displays user's rank stats
public SayRank              = 0 // displays user's rank
public SayTop15             = 0 // displays first 15 players
public SayStatsAll          = 0 // displays all players stats and rank
public SayHot    = 0    // displays top from current players
#if defined CSSTATSX_SQL
public SaySeStats    = 0 // displays players match history
#endif

#if defined AES
    new aes_track_mode
#endif

public plugin_precache()
{
    register_plugin(PLUGIN, VERSION, AUTHOR)

    /*
    // Отображение /top15 и /rank
    // ВАЖНО! Motd окно не может показывать больше 1534-х символов, а сообщение в чат больше 192-х.
    // Если что то отображается криво или не полностью, то нужно уменьшить количество пунктов. (Топ не показывает больше 10-ти игроков)
    //   * - Ранг
    //   a - Ник (Only /top15)
    //   b - Убийста
    //   c - Смерти
    //   d - Попаданий
    //   e - Выстрелов
    //   f - В голову
    //   g - Точность
    //   h - Эффективность
    //   i - Скилл
    //   j - Звание Army Ranks
    //   k - K:D
    //   l - HS:K
    //   m - HS %
    //   n - онлайн время
    */

    cvar[CVAR_MOTD_DESC] = register_cvar("aes_statsx_top","*abcdfgij")
    cvar[CVAR_CHAT_DESC] = register_cvar("aes_statsx_rank","bci")

    //
    // o - изменение скилла
    // p - дата сессии
    // q - карта
    //
    cvar[CVAR_SESTATS_DESC] = register_cvar("aes_statsx_sestats","poqnbckfl")

    // Настройка скилла. Значения схожи со значениями эффективности.
    // Значения: L- L L+ M- M M+ H- H H+ P- P P+ G
    cvar[CVAR_SKILL] = register_cvar("aes_statsx_skill","60.0 75.0 85.0 100.0 115.0 130.0 140.0 150.0 165.0 180.0 195.0 210.0")

    /*
    * Как выводить скилл в motd
    *    0 - html (картинка с буквой + скилл)
    *    1 - буква (скилл)
    *    2 - буква
    *    3 - скилл
    */
    cvar[CVAR_MOTD_SKILL_FMT] = register_cvar("aes_statsx_motd_skill","0")

    register_dictionary("statsx.txt")
    register_dictionary("statsx_aes.txt")

    #if defined CSSTATSX_SQL
        register_dictionary("time.txt")
    #endif
}

public plugin_init()
{
    register_clcmd("say","Say_Catch")
    register_clcmd("say_team","Say_Catch")

    register_menucmd(register_menuid("Stats Menu"), 1023, "actionStatsMenu")
}

#if AMXX_VERSION_NUM < 183
    public plugin_cfg()
#else
    public OnAutoConfigsBuffered()
#endif
{
    new levelString[512],stPos,ePos,rawPoint[20],cnt
    get_pcvar_string(cvar[CVAR_SKILL],levelString,charsmax(levelString))

    // парсер значений для скилла
    do {
        ePos = strfind(levelString[stPos]," ")

        formatex(rawPoint,ePos,levelString[stPos])
        g_skill_opt[cnt] = str_to_float(rawPoint)

        stPos += ePos + 1

        cnt++

        // narkoman wole suka
        if(cnt > sizeof g_skill_letters - 1)
            break
    } while (ePos != -1)

    new addStast[] = "amx_statscfg add ^"%s^" %s"

    server_cmd(addStast, "ST_SAY_STATSME", "SayStatsMe")
    server_cmd(addStast, "ST_SAY_RANKSTATS", "SayRankStats")
    server_cmd(addStast, "ST_SAY_RANK", "SayRank")
    server_cmd(addStast, "ST_SAY_TOP15", "SayTop15")
    server_cmd(addStast, "ST_SAY_STATS", "SayStatsAll")
    server_cmd(addStast, "AES_SAY_HOT", "SayHot")

    #if defined CSSTATSX_SQL
        server_cmd(addStast, "CSXSQL_SESTATS_CFG", "SaySeStats")
    #endif

    #if defined AES
        aes_track_mode = get_cvar_num("aes_track_mode")
    #endif
}

#if defined CSSTATSX_SQL
//
// Команда /sestats
//
public SeStats_Show(id,player_id)
{
    if(!SaySeStats)
    {
        client_print_color(id,print_team_red,"%L %L",id,"STATS_TAG", id,"DISABLED_MSG")

        return PLUGIN_HANDLED
    }

    new plr_db,sestats_data[2]

    sestats_data[0] = id
    sestats_data[1] = player_id

    plr_db = get_user_stats_id(player_id)

    if(!plr_db|| !get_sestats_thread_sql(plr_db,"SeStats_ShowHandler",sestats_data,sizeof sestats_data,10))
    {
        client_print_color(id,print_team_red,"%L %L",id,"STATS_TAG", id,"AES_STATS_INFO2")

        return PLUGIN_HANDLED
    }

    return PLUGIN_HANDLED
}

public SeStats_ShowHandler(CSXSQL_SESTATS:sestats_array,sestats_data[])
{
    new id = sestats_data[0]
    new player_id = sestats_data[1]

    if(!is_user_connected(id) || !is_user_connected(player_id))
    {
        get_sestats_free(sestats_array)
        return PLUGIN_HANDLED
    }

    new len,title[64]

    // заголовок
    len += formatex(theBuffer[len],BUFF_LEN - len,"%L",id,"AES_META")
    len += formatex(theBuffer[len],BUFF_LEN - len,"%L",id,"AES_STYLE")

    if(id == player_id)
    {
        formatex(title,charsmax(title),"%L %L",
            id,"YOURS",
            id,"CSXSQL_SETITLE"
        )

    }
    else
    {
        new name[MAX_NAME_LENGTH]
        get_user_name(player_id,name,charsmax(name))

        formatex(title,charsmax(title),"%L %s",
            id,"CSXSQL_SETITLE",
            name
        )
    }

    len += formatex(theBuffer[len],BUFF_LEN - len,"%L",id,"CSXSQL_SEHTML",title)

    // таблица со статистикой
    new row_str[512],cell_str[MAX_NAME_LENGTH * 3],row_len
    new desc_str[10],desc_char[4],bool:odd

    get_pcvar_string(cvar[CVAR_SESTATS_DESC],desc_str,charsmax(desc_str))
    trim(desc_str)

    new desc_length = strlen(desc_str)

    len += parse_top_desc_header(id,theBuffer,BUFF_LEN,len,false,desc_str)

    new stats[8],bh[8]

    for(new i,length = get_sestats_read_count(sestats_array) ; i < length ; i++)
    {
        get_sestats_read_stats(sestats_array,i,stats,bh)

        for(new desc_index ; desc_index < desc_length ; desc_index++)
        {
            cell_str[0] = 0
            desc_char[0] = desc_str[desc_index]

            switch(desc_char[0])
            {
                // время
                case 'p':
                {
                    new stime = get_sestats_read_stime(sestats_array,i)
                    format_time(cell_str,charsmax(cell_str),"%m/%d/%Y - %H:%M:%S",stime)
                }
                // изменение скилла
                case 'o':
                {
                    new Float:skill = get_sestats_read_skill(sestats_array,i)

                    formatex(cell_str,charsmax(cell_str),"%s%.2f",
                        skill > 0.0 ? "+" : "",
                        skill
                    )
                }
                // карта
                case 'q':
                {
                    get_sestats_read_map(sestats_array,i,cell_str,charsmax(cell_str))
                }
                // убийства
                case 'b':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats[STATS_KILLS])
                }
                // смерти
                case 'c':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats[STATS_DEATHS])
                }
                // попадания
                case 'd':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats[STATS_HITS])
                }
                // выстрелы
                case 'e':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats[STATS_SHOTS])
                }
                // хедшоты
                case 'f':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats[STATS_HS])
                }
                // точнсть
                case 'g':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f%%",
                        accuracy(stats)
                    )
                }
                // эффективность
                case 'h':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f%%",
                        effec(stats)
                    )
                }
                // K:D
                case 'k':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f",
                        kd_ratio(stats)
                    )
                }
                // HS:K
                case 'l':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f",
                        hsk_ratio(stats)
                    )
                }
                // HS effec
                case 'm':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f%%",
                        effec_hs(stats)
                    )
                }
                // время в игре
                case 'n':
                {
                    new ot = get_sestats_read_online(sestats_array,i)
                    func_format_ot(ot,cell_str,charsmax(cell_str),id)
                }
                default: continue
            }

            // выводим отформатированные данные
            row_len += formatex(row_str[row_len],charsmax(row_str)-row_len,"%L",id,"AES_BODY_CELL",cell_str)
        }
        row_len = 0

        row_len = len
        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"%L",id,"AES_BODY_ROW",odd ? " id=b" : " id=q",row_str)

        if(len >= BUFF_LEN)
        {
            theBuffer[row_len] = 0
            break
        }

        row_len = 0

        odd ^= true
    }

    get_sestats_free(sestats_array)

    formatex(title,charsmax(title),"%L",id,"CSXSQL_SETITLE")
    show_motd(id,theBuffer,title)

    return PLUGIN_HANDLED
}
#endif

//
// Команда /hot
//
public ShowCurrentTop(id)
{
    if(!SayHot)
    {
        client_print_color(id,print_team_red,"%L %L",id,"STATS_TAG", id,"DISABLED_MSG")

        return PLUGIN_HANDLED
    }

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

    new current_top[MAX_PLAYERS][2]

    for(new i,stats[8],bh[8] ; i < pnum ; i++)
    {
        current_top[i][0] = players[i]

        #if !defined CSSTATSX_SQL
            current_top[i][1] = get_user_stats(players[i],stats,bh)
        #else
            current_top[i][1] = get_user_stats_sql(players[i],stats,bh)
        #endif
    }

    SortCustom2D(current_top,sizeof current_top,"Sort_CurrentTop")

    new len,title[64]
    formatex(title,charsmax(title),"%L",id,"AES_HOT_PLAYERS")

    // заголовок
    len += formatex(theBuffer[len],BUFF_LEN - len,"%L",id,"AES_META")
    len += formatex(theBuffer[len],BUFF_LEN - len,"%L",id,"AES_STYLE")
    len += formatex(theBuffer[len],BUFF_LEN - len,"%L",id,"AES_TOP_BODY",id,"AES_HOT_PLAYERS")

    // таблица со статистикой
    new row_str[512],cell_str[MAX_NAME_LENGTH * 3],row_len
    new desc_str[10],desc_char[4],bool:odd

    get_pcvar_string(cvar[CVAR_MOTD_DESC],desc_str,charsmax(desc_str))
    trim(desc_str)

    // TODO: AES RANKS
    replace(desc_str,charsmax(desc_str),"j","")

    new desc_length = strlen(desc_str)
    new skill_out = get_pcvar_num(cvar[CVAR_MOTD_SKILL_FMT])

    len += parse_top_desc_header(id,theBuffer,BUFF_LEN,len,false,desc_str)

    for(new i,stats[8],bh[8],player_id ,name[MAX_NAME_LENGTH] ; i < sizeof current_top ; i++)
    {
        player_id = current_top[i][0]

        if(!player_id)
        {
            continue
        }

        get_user_name(player_id,name,charsmax(name))

        #if !defined CSSTATSX_SQL
            get_user_stats(player_id,stats,bh)
        #else
            get_user_stats_sql(player_id,stats,bh)
        #endif

        for(new desc_index ; desc_index < desc_length ; desc_index++)
        {
            cell_str[0] = 0
            desc_char[0] = desc_str[desc_index]

            switch(desc_char[0]){
                // ранк
                case '*':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",current_top[i][1])
                }
                // ник
                case 'a':
                {

                    formatex(cell_str,charsmax(cell_str),"%s",name)

                    replace_all(cell_str,charsmax(cell_str),"<","&lt")
                    replace_all(cell_str,charsmax(cell_str),">","&gt")
                }
                // убийства
                case 'b':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats[STATS_KILLS])
                }
                // смерти
                case 'c':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats[STATS_DEATHS])
                }
                // попадания
                case 'd':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats[STATS_HITS])
                }
                // выстрелы
                case 'e':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats[STATS_SHOTS])
                }
                // хедшоты
                case 'f':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats[STATS_HS])
                }
                // точнсть
                case 'g':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f%%",
                        accuracy(stats)
                    )
                }
                // эффективность
                case 'h':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f%%",
                        effec(stats)
                    )
                }

                // скилл
                case 'i':{
                    new Float:skill ,skill_id

                    #if defined CSSTATSX_SQL
                        // используем скилл из csstatsx sql (ELO)
                        get_user_skill(player_id,skill)
                    #else
                        // используем K:D для скилла
                        skill = effec(stats)
                    #endif

                    skill_id =  aes_statsx_get_skill_id(skill)

                    switch(skill_out)
                    {
                        // html
                        case 0:
                        {
                            formatex(cell_str,charsmax(cell_str),"%L",
                                id,
                                "AES_SKILL_FMT",


                                g_skill_class[skill_id],
                                skill
                            )
                        }
                        // буква (скилл)
                        case 1:
                        {
                            formatex(cell_str,charsmax(cell_str),"%s (%.2f)",
                                g_skill_letters[skill_id],
                                skill
                            )
                        }
                        // буква
                        case 2:
                        {
                            formatex(cell_str,charsmax(cell_str),"%s",
                                g_skill_letters[skill_id]
                            )
                        }
                        // скилл
                        case 3:
                        {
                            formatex(cell_str,charsmax(cell_str),"%.2f",
                                skill
                            )
                        }
                    }




                }
                #if defined AES
                // опыт и ранг
                case 'j':
                {
                    // TODO: AES RANKS
                    formatex(cell_str,charsmax(cell_str),"lyl")
                }
                #endif
                // K:D
                case 'k':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f",
                        kd_ratio(stats)
                    )
                }
                // HS:K
                case 'l':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f",
                        hsk_ratio(stats)
                    )
                }
                // HS effec
                case 'm':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f%%",
                        effec_hs(stats)
                    )
                }
                #if defined CSSTATSX_SQL
                // время в игре
                case 'n':
                {
                    new ot = get_user_gametime(player_id)
                    func_format_ot(ot,cell_str,charsmax(cell_str),id)
                }
                #endif
                default: continue
            }

            // выводим отформатированные данные
            row_len += formatex(row_str[row_len],charsmax(row_str)-row_len,"%L",id,"AES_BODY_CELL",cell_str)
        }

        row_len = len
        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"%L",id,"AES_BODY_ROW",odd ? " id=b" : " id=q",row_str)

        if(len >= BUFF_LEN)
        {
            theBuffer[row_len] = 0
        }

        row_len = 0
        odd ^= true
    }

    show_motd(id,theBuffer,title)

    return PLUGIN_HANDLED
}

public Sort_CurrentTop(const elem1[], const elem2[])
{
    if(elem1[1] < elem2[1])
    {
        return -1
    }
    else if(elem1[1] > elem2[1])
    {
        return 1
    }

    return 0
}

// Ловим сообщения чата
public Say_Catch(id){
    new msg[191]
    read_args(msg,190)

    trim(msg)
    remove_quotes(msg)
    if(msg[0] == '/'){
        if(strcmp(msg[1],"rank",true) == 0)
        {
            return RankSay(id)
        }
        if(containi(msg[1],"top") == 0)
        {
            replace(msg,190,"/top","")

            return SayTop(id,str_to_num(msg))
        }
        if(strcmp(msg[1],"hot",true) == 0 || strcmp(msg[1],"topnow",true) == 0)
        {
            return ShowCurrentTop(id)
        }
        if(strcmp(msg[1],"rankstats",true) == 0)
        {
            return RankStatsSay(id,id)
        }

        if(strcmp(msg[1],"stats",true) == 0)
        {
            arrayset(g_MenuStatus[id],0,2)
            return ShowStatsMenu(id,0)
        }

        if(strcmp(msg[1],"statsme",true) == 0)
        {
            return StatsMeSay(id,id)
        }

        #if defined CSSTATSX_SQL
        if(strcmp(msg[1],"sestats",true) == 0 || strcmp(msg[1],"history",true) == 0)
        {
            return SeStats_Show(id,id)
        }
        #endif
    }

    return PLUGIN_CONTINUE
}

//
// Команда /rank
//
public RankSay(id){
    // команда /rank выключена
    if(!SayRank)
    {
        client_print_color(id,print_team_red,"%L %L",id,"STATS_TAG", id,"DISABLED_MSG")

        return PLUGIN_HANDLED
    }

    new message[191],len,rank,stats_num,stats[8],bh[8]

    len += formatex(message[len],charsmax(message)- len,"%L ",id,"STATS_TAG")

    #if defined CSSTATSX_SQL
        rank = get_user_stats_sql(id,stats,bh)
        stats_num = get_statsnum_sql()
    #else
        rank = get_user_stats(id,stats,bh)
        stats_num = get_statsnum()
    #endif

    if(rank > 0)
    {
        len += formatex(message[len],charsmax(message) - len,"%L ",id,"AES_YOUR_RANK_IS",rank,stats_num)
        len += parse_rank_desc(id,message[len],charsmax(message)-len,stats)
    }
    else
    {
        len += formatex(message[len],charsmax(message) - len,"%L ",id,"AES_STATS_INFO2")
    }

    client_print_color(id,print_team_default,message)

    return PLUGIN_HANDLED
}

//
// Формирование сообщения /rank
//
parse_rank_desc(id,msg[],maxlen,stats[8]){
    new cnt,theChar[4],len

    new desc_str[10]
    get_pcvar_string(cvar[CVAR_CHAT_DESC],desc_str,charsmax(desc_str))

    // Проверяем всё флаги
    for(new i,length = strlen(desc_str) ; i < length ; ++i){
        theChar[0] = desc_str[i]    // фз почему напрямую не рабатает

        // если это первое значение, то рисуем в начале скобку, иначе запятую с пробелом
        if(cnt != length)
            len += formatex(msg[len],maxlen - len,cnt <= 0 ? "(" : ", ")

        // добавляем в сообщение информацию в соотв. с флагами
        switch(theChar[0]){
             // убийства
            case 'b':
            {
                len += formatex(msg[len],maxlen - len,"%L ^3%d^1",id,"KILLS",stats[0])
            }
             // смерти
            case 'c':
            {
                len += formatex(msg[len],maxlen - len,"%L ^3%d^1",id,"DEATHS",stats[1])
            }
             // попадания
            case 'd':
            {
                len += formatex(msg[len],maxlen - len,"%L ^3%d^1",id,"HITS",stats[5])
            }
            // выстрелы
            case 'e':
            {
                len += formatex(msg[len],maxlen - len,"%L ^3%d^1",id,"SHOTS",stats[4])
            }
            // хедшоты
            case 'f':
            {
                len += formatex(msg[len],maxlen - len,"%L ^3%d^1",id,"STATS_HS",stats[2])
            }
            // точность
            case 'g':
            {
                len += formatex(msg[len],maxlen - len,"%L ^3%.2f%%^1",id,"ACC",accuracy(stats))
            }
            // эффективность
            case 'h':
            {
                len += formatex(msg[len],maxlen - len,"%L ^3%d%%^1",id,"EFF",effec(stats))
            }
            // скилл
            case 'i':
            {
                new Float:skill,skill_id

                #if defined CSSTATSX_SQL
                    get_user_skill(id,skill)
                #else
                    skill = effec(stats)
                #endif

                skill_id = aes_statsx_get_skill_id(skill)

                len += formatex(msg[len],maxlen - len,"%L ^3%s^1 (%.2f)",id,"STATS_SKILL",
                    g_skill_letters[skill_id],
                    skill
                )

            }
            #if defined AES
            case 'j':{ // ранг и опыт
                new Float:player_exp = aes_get_player_exp(id)

                if(player_exp == -1.0)// без ранга
                {
                    len += formatex(msg[len],maxlen - len,"%L ^4---^1",id,"STATS_RANK")
                }
                else
                {
                    new level_str[AES_MAX_LEVEL_LENGTH ]
                    new player_level = aes_get_player_level(id)
                    aes_get_level_name(player_level,level_str,charsmax(level_str),id)

                    len += formatex(msg[len],maxlen - len,"%L ^3%L^1",id,"STATS_RANK",id,"AES_RANK",
                        level_str,player_exp
                    )
                }
            }
            #endif
            // K:D
            case 'k':
            {
                len += formatex(msg[len],maxlen - len,"%L ^3%.2f^1",
                    id,"AES_KS",
                    kd_ratio(stats)
                )
            }
            // HS:K
            case 'l':
            {
                len += formatex(msg[len],maxlen - len,"%L ^3%.2f^1",
                    id,"AES_HSK",
                    hsk_ratio(stats)
                )
            }
            // HS effec
            case 'm':
            {
                len += formatex(msg[len],maxlen - len,"%L ^3%.2f^1%%",
                    id,"AES_HSP",
                    effec_hs(stats)
                )
            }
            #if defined CSSTATSX_SQL
            // время в игре
            case 'n':
            {
                new ot = get_user_gametime(id)

                len += formatex(msg[len],maxlen - len,"%L: ^3",id,"AES_TIME")
                len += func_format_ot(ot,msg[len],maxlen - len,id)
                len += formatex(msg[len],maxlen - len,"^1")
            }
            #endif
        }

        theChar[0] = 0
        cnt ++
    }

    // завершаем всё сообщение скобкой, если была подстановка параметров
    if(cnt)
    {
        len += formatex(msg[len],maxlen - len,")")
    }

    return len
}

#if defined CSSTATSX_SQL
func_format_ot(ot,string[],len,idLang = LANG_SERVER)
{
    new d,h,m,s

    d = (ot / SECONDS_IN_DAY)
    ot -= (d * SECONDS_IN_DAY)
    h = (ot / SECONDS_IN_HOUR)
    ot -= (h * SECONDS_IN_HOUR)
    m = (ot / SECONDS_IN_MINUTE)
    ot -= (m * SECONDS_IN_MINUTE)
    s = ot

    if(d)
    {
        return formatex(string,len,"%L",idLang,"AES_STATS_DESC1",d,h,m)
    }
    else if(h)
    {
        return formatex(string,len,"%L",idLang,"AES_STATS_DESC2",h,m)
    }
    else if(m)
    {
        return formatex(string,len,"%L",idLang,"AES_STATS_DESC3",m)
    }

    return formatex(string,len,"%L",idLang,"AES_STATS_DESC4",s)
}
#endif
//
// Формирование окна /rankstats
//     id - кому показывать
//     player_id - кого показывать
//
public RankStatsSay(id,player_id){
    // Команда /rankstats выключена
    if(!SayRankStats)
    {
        client_print_color(id,print_team_default,"%L %L",id,"STATS_TAG", id,"DISABLED_MSG")

        return PLUGIN_HANDLED
    }

    if(!is_user_connected(player_id))
    {
        client_print_color(id,print_team_default,"%L %L",id,"STATS_TAG",id,"AES_STATS_INFO2")

        return PLUGIN_HANDLED
    }

    new len,motd_title[MAX_NAME_LENGTH]
    new name[MAX_NAME_LENGTH],rank,stats[8],bh[8],stats_num,Float:skill,skill_id,skill_str[64]

    #if defined CSSTATSX_SQL
        new stats3[STATS3_END]
        get_user_stats3_sql(player_id,stats3)
    #endif

    theBuffer[0] = 0

    formatex(motd_title,charsmax(motd_title),"%L",id,"RANKSTATS_TITLE")

    len += formatex(theBuffer[len],BUFF_LEN-len,"%L",id,"AES_META")
    len += formatex(theBuffer[len],BUFF_LEN-len,"%L",id,"AES_STYLE")

    #if defined CSSTATSX_SQL
        rank = get_user_stats_sql(player_id,stats,bh)
        stats_num = get_statsnum_sql()
        get_user_skill(player_id,skill)
    #else
        rank = get_user_stats(player_id,stats,bh)
        stats_num = get_statsnum()
        skill = effec(stats)
    #endif

    skill_id = aes_statsx_get_skill_id(skill)


    if(id == player_id)
    {
        formatex(name,charsmax(name),"%L",id,"AES_YOU")
    }
    else
    {
        get_user_name(player_id,name,charsmax(name))
    }

    switch(get_pcvar_num(cvar[CVAR_MOTD_SKILL_FMT]))
    {
        // html
        case 0:
        {
            formatex(skill_str,charsmax(skill_str),"%L",
                id,
                "AES_SKILL_FMT",

                g_skill_class[skill_id],
                skill
            )
        }
        // буква (скилл)
        case 1:
        {
            formatex(skill_str,charsmax(skill_str),"%s (%.2f)",
                g_skill_letters[skill_id],
                skill
            )
        }
        // буква
        case 2:
        {
            formatex(skill_str,charsmax(skill_str),"%s",
                g_skill_letters[skill_id]
            )
        }
        // скилл
        case 3:
        {
            formatex(skill_str,charsmax(skill_str),"%.2f",
                skill
            )
        }
    }

    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<table cellspacing=10 cellpadding=0><tr>")

    new bool:is_wstats = false

    #if defined CSSTATSX_SQL
        is_wstats = (get_user_wstats_sql(player_id,0,stats,bh) == -1) ? false : true
    #endif

    //
    // Общая статистика
    //
    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<td valign=top width=%d%% class=q><table cellspacing=0><tr><th colspan=2>",
        is_wstats ? 40 : 50
    )

    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"%L",
        id,"AES_RANKSTATS_TSTATS",
        name,rank,stats_num
    )

    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=q><td>%L<td>%d (%L %d (%.2f%%))",id,"AES_KILLS",stats[STATS_KILLS],id,"AES_HS",stats[STATS_HS],effec_hs(stats))
    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=b><td>%L<td>%d (%L %.2f)",id,"AES_DEATHS",stats[STATS_DEATHS],id,"AES_KS",kd_ratio(stats))
    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=q><td>%L<td>%d",id,"AES_HITS",stats[STATS_HITS])
    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=b><td>%L<td>%d",id,"AES_SHOTS",stats[STATS_SHOTS])
    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=q><td>%L<td>%d",id,"AES_DMG",stats[STATS_DAMAGE])
    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=b><td>%L<td>%.2f%%",id,"AES_ACC",accuracy(stats))
    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=q><td>%L<td>%.2f%%",id,"AES_EFF",effec(stats))
    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=b><td>%L<td>%s",id,"AES_SKILL",skill_str)

    #if !defined CSSTATSX_SQL
        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=q><td height=18px><td>")
    #else
        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=q><td>%L<td>",id,"AES_TIME")
        len += func_format_ot(
            get_user_gametime(player_id),
            theBuffer[len],charsmax(theBuffer)-len,
            id
        )

        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=b><td>%L<td>%d",id,"CSXSQL_JOINS",stats3[STATS3_CONNECT])

        /*
        new from = get_systime() - get_user_lastjoin_sql(player_id)
        new from_str[40]
        get_time_length(id,from,timeunit_seconds,from_str,charsmax(from_str))

        len += formatex(theBuffer[len],charsmax(theBuffer)-len," (%L. %s %L)",
            id,"LAST",
            from_str,
            id,"AGO"
        )
        */

        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=q><td>%L<td>%d (%L, %L)",id,"CSXSQL_ROUNDS",
            (stats3[STATS3_ROUNDT] + stats3[STATS3_ROUNDCT]),
            id,"CSXSQL_AS_T",stats3[STATS3_ROUNDT],
            id,"CSXSQL_AS_CT",stats3[STATS3_ROUNDCT]
        )

        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=b><td>%L<td>%d (%L, %L)",id,"CSXSQL_WINS",
            (stats3[STATS3_WINT] + stats3[STATS3_WINCT]),
            id,"CSXSQL_AS_T",stats3[STATS3_WINT],
            id,"CSXSQL_AS_CT",stats3[STATS3_WINCT]
        )

        new firstjoin = get_user_firstjoin_sql(player_id)

        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=q><td>%L<td>",id,"CSXSQL_FIRSTJOIN")

        if(firstjoin > 0)
        {
            len += format_time(theBuffer[len],charsmax(theBuffer)-len,"%m/%d/%Y - %H:%M:%S",firstjoin)
        }
        else
        {
            len += formatex(theBuffer[len],charsmax(theBuffer)-len,"-")
        }
    #endif
    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"</td></tr></table></td>")

    #if !defined CSSTATSX_SQL
        //
        // Статистика по попаданиям
        //
        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<td valign=top width=50%% class=q><table cellspacing=0><tr><th colspan=2>")
        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"%L",id,"AES_RANKSTATS_THITS")

        new theSwitcher

        for (new i = 1; i < 8; i++)
        {
            len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=%s><td>%L<td>%d",
                theSwitcher ? "b" : "q",
                id,BODY_PART[i],bh[i]
            )

            theSwitcher = theSwitcher ? false : true
        }


        // mne tak nadoel etot kod :(
        for(new i = 0 ; i < 2; ++i){
            len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=%s><td height=18px><td>",theSwitcher ? "b" : "q")

            theSwitcher = theSwitcher ? false : true
        }

        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"</td>")
    #else
        // статистика по оружию выключена
        if(!is_wstats)
        {
            //
            // Статистика по попаданиям
            //
            len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<td valign=top width=50%% class=q><table cellspacing=0><tr><th colspan=2>")
            len += formatex(theBuffer[len],charsmax(theBuffer)-len,"%L",id,"AES_RANKSTATS_THITS")

            new theSwitcher

            for (new i = 1; i < 8; i++)
            {
                len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=%s><td>%L<td>%d",
                    theSwitcher ? "b" : "q",
                    id,BODY_PART[i],bh[i]
                )

                theSwitcher = theSwitcher ? false : true
            }


            // mne tak nadoel etot kod :(
            for(new i = 0 ; i < 5; ++i){
                len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=%s><td height=18px><td>",theSwitcher ? "b" : "q")

                theSwitcher = theSwitcher ? false : true
            }

            len += formatex(theBuffer[len],charsmax(theBuffer)-len,"</td>")
        }
        else
        {
            //
            // Статистика по используемому оружию
            //
            len += formatex(theBuffer[len],BUFF_LEN-len,"<td valign=top width=60%% class=q><table cellspacing=0 width=100%%><tr><th>%L<th>%L<th>%L<th>%L<th>%L<th>%L<th>%L",
                id,"AES_WEAPON",
                id,"AES_KILLS",
                id,"AES_DEATHS",
                id,"AES_HITS",
                id,"AES_SHOTS",
                id,"AES_DMG",
                id,"AES_ACC"
            )

            new bool:odd
            new wpn_stats[9],Array:wpn_stats_array = ArrayCreate(sizeof wpn_stats)

            for (new wpnId = 1,max_w = xmod_get_maxweapons_sql() ; wpnId < max_w ; wpnId++)
            {
                if (get_user_wstats_sql(player_id, wpnId, stats,bh))
                {
                    wpn_stats[0] = stats[0]
                    wpn_stats[1] = stats[1]
                    wpn_stats[2] = stats[2]
                    wpn_stats[3] = stats[3]
                    wpn_stats[4] = stats[4]
                    wpn_stats[5] = stats[5]
                    wpn_stats[6] = stats[6]
                    wpn_stats[7] = stats[7]
                    wpn_stats[8] = wpnId

                    ArrayPushArray(wpn_stats_array,wpn_stats)
                }
            }

            // сортируем по кол-ву убийств
            ArraySort(wpn_stats_array,"Sort_WeaponStats")

            for(new lena,i,wpnId,wpnName[MAX_NAME_LENGTH],length = ArraySize(wpn_stats_array) ; i < length && charsmax(theBuffer)-len > 0; i++)
            {
                ArrayGetArray(wpn_stats_array,i,wpn_stats)

                wpnId = wpn_stats[8]
                stats[0] = wpn_stats[0]
                stats[1] = wpn_stats[1]
                stats[2] = wpn_stats[2]
                stats[3] = wpn_stats[3]
                stats[4] = wpn_stats[4]
                stats[5] = wpn_stats[5]
                stats[6] = wpn_stats[6]
                stats[7] = wpn_stats[7]

                xmod_get_wpnname(wpnId,wpnName,charsmax(wpnName))

                lena = len

                len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=%s><td>%s<td>%d<td>%d<td>%d<td>%d<td>%d<td>%0.1f%%",
                    odd ? "b" : "q",
                    wpnName,
                    stats[STATS_KILLS],
                    stats[STATS_DEATHS],
                    stats[STATS_HITS],
                    stats[STATS_SHOTS],
                    stats[STATS_DAMAGE],
                    accuracy(stats)
                )

                // LENA FIX
                if(len >= BUFF_LEN)
                {
                    len = lena
                    theBuffer[len] = 0

                    break
                }

                odd ^= true
            }

            ArrayDestroy(wpn_stats_array)
        }
    #endif

    show_motd(id,theBuffer,motd_title)

    return PLUGIN_HANDLED
}

#if defined CSSTATSX_SQL
    public Sort_WeaponStats(Array:array, item1, item2)
    {
        new wpn_stats1[9],wpn_stats2[9]
        ArrayGetArray(array,item1,wpn_stats1)
        ArrayGetArray(array,item2,wpn_stats2)

        if(wpn_stats1[0] > wpn_stats2[0])
        {
            return -1
        }
        else if(wpn_stats1[0] < wpn_stats2[0])
        {
            return 1
        }

        return 0
    }
#endif


//
// Личная статистка за карту
//
// id - кому показывать
// stId - кого показывать
public StatsMeSay(id,player_id){
    if(!SayStatsMe){
        client_print_color(id,0,"%L %L",id,"STATS_TAG", id,"DISABLED_MSG")

        return PLUGIN_HANDLED
    }

    new len,stats[8],bh[8],motd_title[64]

    formatex(motd_title,charsmax(motd_title),"%L",id,"STATS_TITLE")

    if(id != player_id){
        new name[32]
        get_user_name(player_id,name,charsmax(name))
    }

    theBuffer[0] = 0

    get_user_wstats(player_id,0,stats,bh)

    len += formatex(theBuffer[len],BUFF_LEN-len,"%L%L",id,"AES_META",id,"AES_STYLE")
    len += formatex(theBuffer[len],BUFF_LEN-len,"%L",id,"AES_STATS_BODY")

    len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<table cellspacing=10 cellpadding=0><tr>")

    len += formatex(theBuffer[len],BUFF_LEN-len,"<td valign=top width=20%% class=q><table cellspacing=0 width=100%%><tr><th colspan=2>%L<tr><td>%L<td>%d<tr class=b><td>%L<td>%d<tr><td>%L<td>%d<tr class=b><td>%L<td>%d<tr><td>%L<td>%d<tr class=b><td>%L<td>%d<tr><td>%L<td>%0.2f%%<tr class=b><td>%L<td>%0.2f%%</table>",
        id,"AES_STATS_HEADER1",
        id,"AES_KILLS",stats[STATS_KILLS],
        id,"AES_HS",stats[STATS_HS],
        id,"AES_DEATHS",stats[STATS_DEATHS],
        id,"AES_HITS",stats[STATS_HITS],
        id,"AES_SHOTS",stats[STATS_SHOTS],
        id,"AES_DMG",stats[STATS_DAMAGE],
        id,"AES_ACC",accuracy(stats),
        id,"AES_EFF",effec(stats))

    len += formatex(theBuffer[len],BUFF_LEN-len,"<td valign=top width=80%% class=q><table cellspacing=0 width=100%%><tr><th>%L<th>%L<th>%L<th>%L<th>%L<th>%L<th>%L",
        id,"AES_WEAPON",
        id,"AES_KILLS",
        id,"AES_DEATHS",
        id,"AES_HITS",
        id,"AES_SHOTS",
        id,"AES_DMG",
        id,"AES_ACC"
    )

    new bool:odd

    for (new wpnName[32],wpnId = 1 ; wpnId < xmod_get_maxweapons() && charsmax(theBuffer)-len > 0 ; wpnId++)
    {
        if (get_user_wstats(player_id, wpnId, stats,bh))
        {
            xmod_get_wpnname(wpnId,wpnName,charsmax(wpnName))

            len += formatex(theBuffer[len],charsmax(theBuffer)-len,"<tr id=%s><td>%s<td>%d<td>%d<td>%d<td>%d<td>%d<td>%0.1f%%",
                odd ? "b" : "q",
                wpnName,
                stats[STATS_KILLS],
                stats[STATS_DEATHS],
                stats[STATS_HITS],
                stats[STATS_SHOTS],
                stats[STATS_DAMAGE],
                accuracy(stats)
            )

            odd ^= true
        }
    }

    show_motd(id,theBuffer,motd_title)

    return PLUGIN_HANDLED
}

// Формирование окна /top
// В Pos указывается с какой позиции рисовать
public SayTop(id,Pos)
{
    if(!SayTop15){
        client_print_color(id,0,"%L %L",id,"STATS_TAG", id,"DISABLED_MSG")

        return PLUGIN_HANDLED
    }

    if(Pos == 15 || Pos <= 0)
        Pos = 10

    #if defined CSSTATSX_SQL
        if(!get_stats_sql_thread(id,Pos,MAX_TOP,"SayTopHandler"))
        {
            client_print_color(id,print_team_red,"%L %L",id,"STATS_TAG",id,"AES_STATS_INFO1")
        }
    #else
        SayTopHandler(id,Pos)
    #endif

    return PLUGIN_HANDLED
}

enum _:stats_former_array
{
    STATSF_NAME[MAX_NAME_LENGTH],
    STATSF_AUTHID[30],
    STATSF_DATA[8],
    STATSF_DATA2[4],
    STATSF_BH[8],
    STATSF_RANK

    #if defined CSSTATSX_SQL
    ,STATSF_OT
    #endif
}

//
// Сбор статистики
//
public SayTopHandler(id,Pos)
{
    new Array:stats_array = ArrayCreate(stats_former_array)
    new stats_info[stats_former_array],last_rank

    #if defined CSSTATSX_SQL
        new size = min(get_statsnum_sql(),Pos)
    #else
        new size = min(get_statsnum(),Pos)
    #endif

    #if defined AES
        new Array:authids_array = ArrayCreate(sizeof stats_info[STATSF_AUTHID])
    #endif

    new rank,stats[8],stats2[4],bh[8],name[MAX_NAME_LENGTH],authid[30]

    for(new i = size - MAX_TOP < 0 ? 0 : size - MAX_TOP; i < size ; i++){
        #if defined CSSTATSX_SQL
            rank = get_stats_sql(i,stats,bh,name,charsmax(name),authid,charsmax(authid))
            get_stats2_sql(i,stats2)
            get_stats_gametime(i,stats_info[STATSF_OT])
        #else
            rank = get_stats(i,stats,bh,name,charsmax(name),authid,charsmax(authid))
            get_stats2(i,stats2)
        #endif

        if(!rank)
            rank = last_rank

        for(new i ; i < 8 ; i++)
        {
            stats_info[STATSF_DATA][i] = stats[i]
            stats_info[STATSF_BH][i] = bh[i]
        }

        for(new i ; i < 4 ; i++)
        {
            stats_info[STATSF_DATA2][i] = stats2[i]
        }

        copy(stats_info[STATSF_NAME],
            charsmax(stats_info[STATSF_NAME]),
            name
        )

        copy(stats_info[STATSF_AUTHID],
            charsmax(stats_info[STATSF_AUTHID]),
            authid
        )

        last_rank = rank
        stats_info[STATSF_RANK] = rank

        // формируем статистику
        ArrayPushArray(stats_array,stats_info)

        #if defined AES
            ArrayPushString(authids_array,authid)
        #endif
    }

    new stats_data[2]

    stats_data[0] = _:stats_array

    #if defined AES
        stats_data[1] = _:authids_array

        if(!ArraySize(authids_array) || !aes_find_stats_thread(id,authids_array,"SayTopFormer",stats_data,sizeof stats_data))
        {
            new Array:empty_aes_stats = ArrayCreate()
            SayTopFormer(id,empty_aes_stats,stats_data)
        }
    #else
        SayTopFormer(id,stats_data)
    #endif
}

aes_statsx_get_skill_id(Float:skill)
{
    for(new i ; i < sizeof g_skill_opt ; i++)
    {
        if(skill < g_skill_opt[i])
        {
            return i
        }
    }

    return (sizeof g_skill_opt - 1)
}

#if defined AES
    Float:aes_search_exp_in_stats(Array:aes_stats_array,stats_size,name[],authid[],&level)
    {
        for(new i,aes_stats[aes_stats_struct] ; i < stats_size ; i++)
        {
            ArrayGetArray(aes_stats_array,i,aes_stats)
            level = aes_stats[AES_S_LEVEL]

            switch(aes_track_mode)
            {
                case 0:
                {
                    if(strcmp(aes_stats[AES_S_NAME],name,true) == 0)
                    {
                        return aes_stats[AES_S_EXP]
                    }
                }
                case 1:
                {
                    if(strcmp(aes_stats[AES_S_STEAMID],authid,true) == 0)
                    {
                        return aes_stats[AES_S_EXP]
                    }
                }
                case 2:
                {
                    if(strcmp(aes_stats[AES_S_IP],authid,true) == 0)
                    {
                        return aes_stats[AES_S_EXP]
                    }
                }
            }

        }

        return -1.0
    }
#endif

#if !defined AES
public SayTopFormer(id,stats_data[])
#else
public SayTopFormer(id,Array:aes_stats_array,stats_data[])
#endif
{
    theBuffer[0] = 0

    new Array:stats_array = Array:stats_data[0]

    new len,title[64]
    formatex(title,charsmax(title),"%L",id,"AES_PLAYER_TOP")

    // заголовок
    len += formatex(theBuffer[len],BUFF_LEN - len,"%L",id,"AES_META")
    len += formatex(theBuffer[len],BUFF_LEN - len,"%L",id,"AES_STYLE")
    len += formatex(theBuffer[len],BUFF_LEN - len,"%L",id,"AES_TOP_BODY",id,"AES_PLAYER_TOP")

    // таблица со статистикой
    new stats_info[stats_former_array],row_str[512],cell_str[MAX_NAME_LENGTH * 3],row_len
    new desc_str[10],desc_char[4],bool:odd

    get_pcvar_string(cvar[CVAR_MOTD_DESC],desc_str,charsmax(desc_str))
    trim(desc_str)

    new desc_length = strlen(desc_str)
    new skill_out = get_pcvar_num(cvar[CVAR_MOTD_SKILL_FMT])

    len += parse_top_desc_header(id,theBuffer,BUFF_LEN,len,false,desc_str)

    #if defined AES
        new aes_stats_size = ArraySize(aes_stats_array)
        new aes_last_iter
    #endif

    for(new stats_index,length = ArraySize(stats_array);stats_index < length; stats_index ++){
        ArrayGetArray(stats_array,stats_index,stats_info)

        for(new desc_index ; desc_index < desc_length ; desc_index++)
        {
            cell_str[0] = 0
            desc_char[0] = desc_str[desc_index]

            switch(desc_char[0]){
                // ранк
                case '*':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats_info[STATSF_RANK])
                }
                // ник
                case 'a':
                {
                    formatex(cell_str,charsmax(cell_str),"%s",stats_info[STATSF_NAME])

                    replace_all(cell_str,charsmax(cell_str),"<","&lt")
                    replace_all(cell_str,charsmax(cell_str),">","&gt")
                }
                // убийства
                case 'b':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats_info[STATSF_DATA][STATS_KILLS])
                }
                // смерти
                case 'c':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats_info[STATSF_DATA][STATS_DEATHS])
                }
                // попадания
                case 'd':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats_info[STATSF_DATA][STATS_HITS])
                }
                // выстрелы
                case 'e':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats_info[STATSF_DATA][STATS_SHOTS])
                }
                // хедшоты
                case 'f':
                {
                    formatex(cell_str,charsmax(cell_str),"%d",stats_info[STATSF_DATA][STATS_HS])
                }
                // точнсть
                case 'g':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f%%",
                        accuracy(stats_info[STATSF_DATA])
                    )
                }
                // эффективность
                case 'h':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f%%",
                        effec(stats_info[STATSF_DATA])
                    )
                }

                // скилл
                case 'i':{
                    new Float:skill ,skill_id

                    #if defined CSSTATSX_SQL
                        // используем скилл из csstatsx sql (ELO)
                        get_skill(stats_info[STATSF_RANK] - 1,skill)
                    #else
                        // используем K:D для скилла
                        skill = effec(stats_info[STATSF_DATA])
                    #endif

                    skill_id =  aes_statsx_get_skill_id(skill)

                    switch(skill_out)
                    {
                        // html
                        case 0:
                        {
                            formatex(cell_str,charsmax(cell_str),"%L",
                                id,
                                "AES_SKILL_FMT",


                                g_skill_class[skill_id],
                                skill
                            )
                        }
                        // буква (скилл)
                        case 1:
                        {
                            formatex(cell_str,charsmax(cell_str),"%s (%.2f)",
                                g_skill_letters[skill_id],
                                skill
                            )
                        }
                        // буква
                        case 2:
                        {
                            formatex(cell_str,charsmax(cell_str),"%s",
                                g_skill_letters[skill_id]
                            )
                        }
                        // скилл
                        case 3:
                        {
                            formatex(cell_str,charsmax(cell_str),"%.2f",
                                skill
                            )
                        }
                    }




                }
                #if defined AES
                // опыт и ранг
                case 'j':
                {
                    new level = 0
                    new Float:exp = -1.0

                    if(aes_stats_size)
                        exp = aes_search_exp_in_stats(aes_stats_array,aes_stats_size,stats_info[STATSF_NAME],stats_info[STATSF_AUTHID],level)

                    // не нашли стату aes для этого игрока
                    if(exp == -1.0)
                    {
                        // расчитываем на основе статы cstrike
                        new stats[8],stats2[4]

                        // кек
                        for(new i ; i < 8 ; i++)
                        {
                            stats[i] = stats_info[STATSF_DATA][i]
                        }

                        for(new i ; i < 4 ; i++)
                        {
                            stats2[i] = stats_info[STATSF_DATA2][i]
                        }

                        new Float:exp = aes_get_exp_for_stats_f(stats,stats2)

                        if(exp != -1.0)
                        {
                            new level = aes_get_exp_level(exp)

                            new level_str[AES_MAX_LEVEL_LENGTH]
                            aes_get_level_name(level,level_str,charsmax(level_str),id)

                            formatex(cell_str,charsmax(cell_str),"%L",
                                id,"AES_RANK",
                                level_str,
                                exp + 0.005
                            )
                        }
                        else // расчет по стате выключен
                        {
                            formatex(cell_str,charsmax(cell_str),"-")
                        }
                    }
                    else
                    {
                        new level_str[AES_MAX_LEVEL_LENGTH]
                        aes_get_level_name(level,level_str,charsmax(level_str),id)

                        formatex(cell_str,charsmax(cell_str),"%L",
                            id,"AES_RANK",
                            level_str,
                            exp + 0.005
                        )

                        aes_last_iter ++
                    }
                }
                #endif
                // K:D
                case 'k':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f",
                        kd_ratio(stats_info[STATSF_DATA])
                    )
                }
                // HS:K
                case 'l':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f",
                        hsk_ratio(stats_info[STATSF_DATA])
                    )
                }
                // HS effec
                case 'm':
                {
                    formatex(cell_str,charsmax(cell_str),"%.2f%%",
                        effec_hs(stats_info[STATSF_DATA])
                    )
                }
                #if defined CSSTATSX_SQL
                // время в игре
                case 'n':
                {
                    new ot = stats_info[STATSF_OT]
                    func_format_ot(ot,cell_str,charsmax(cell_str),id)
                }
                #endif
                default: continue
            }

            // выводим отформатированные данные
            row_len += formatex(row_str[row_len],charsmax(row_str)-row_len,"%L",id,"AES_BODY_CELL",cell_str)
        }

        row_len = 0

        len += formatex(theBuffer[len],charsmax(theBuffer)-len,"%L",id,"AES_BODY_ROW",odd ? " id=b" : " id=q",row_str)
        odd ^= true
    }

    ArrayDestroy(stats_array)

    #if defined AES
        ArrayDestroy(Array:stats_data[1])
        ArrayDestroy(aes_stats_array)
    #endif
    client_print(id, print_chat, "motd len: %i", strlen(theBuffer))
    show_motd(id,theBuffer,title)
}

// Stats formulas
Float:accuracy(izStats[])
{
    if (!izStats[STATS_SHOTS])
        return (0.0)

    return (100.0 * float(izStats[STATS_HITS]) / float(izStats[STATS_SHOTS]))
}

Float:effec(izStats[])
{
    if (!izStats[STATS_KILLS])
        return (0.0)

    return (100.0 * float(izStats[STATS_KILLS]) / float(izStats[STATS_KILLS] + izStats[STATS_DEATHS]))
}

Float:effec_hs(stats[])
{
    if (!stats[STATS_KILLS])
        return float(stats[STATS_HS])

    return (100.0 * float(stats[STATS_HS]) / float(stats[STATS_KILLS] + stats[STATS_HS]))
}

Float:kd_ratio(stats[])
{
    if(!stats[STATS_DEATHS])
    {
        return float(stats[STATS_KILLS])
    }

    return float(stats[STATS_KILLS]) / float(stats[STATS_DEATHS])
}

Float:hsk_ratio(stats[])
{
    if(!stats[STATS_KILLS])
    {
        return float(stats[STATS_HS])
    }

    return float(stats[STATS_HS]) / float(stats[STATS_KILLS])
}


// Формируем заголовок таблицы для топа игроков
parse_top_desc_header(id,buff[],maxlen,len,bool:isAstats,desc_str[]){
    new tmp[256],len2,theChar[4],lCnt

    lCnt = isAstats != true ? strlen(desc_str) : 0//strlen(aStatsDescCap)

    for(new i ; i < lCnt ; ++i){
        theChar[0] = isAstats != true ? desc_str[i] : desc_str[i]//aStatsDescCap[i]

        switch(theChar[0]){
            case '*':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_POS")
            }
            case 'a':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_PLAYER")
            }
            case 'b':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_KILLS")
            }
            case 'c':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_DEATHS")
            }
            case 'd':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_HITS")
            }
            case 'e':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_SHOTS")
            }
            case 'f':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_HS")
            }
            case 'g':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_ACC")
            }
            case 'h':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_EFF")
            }
            case 'i':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_SKILL")
            }
            #if !defined NO_AES
            case 'j':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_ARMYRANKS")
            }
            #endif
            case 'k':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_KS")
            }
            case 'l':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_HSK")
            }
            case 'm':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_HSP")
            }
            #if defined CSSTATSX_SQL
            case 'n':{
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"AES_TIME")
            }
            case 'p':
            {
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"CSXSQL_DATE")
            }
            case 'o':
            {
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"CSXSQL_SKILLCHANGE")
            }
            case 'q':
            {
                len2 += formatex(tmp[len2],charsmax(tmp)-len2,"%L",id,"AES_HEADER_CELL","",id,"CSXSQL_MAP")
            }
            #endif
        }

        theChar[0] = 0
    }

    return formatex(buff[len],maxlen-len,"%L",id,"AES_TOP_HEADER_ROW",tmp)
}

// формирование меню для просмотра статистики игроков
public ShowStatsMenu(id,page){
    if(!SayStatsAll){
        client_print_color(id,0,"%L %L",id,"STATS_TAG", id,"DISABLED_MSG")

        return PLUGIN_HANDLED
    }

    new menuKeys,menuText[512],menuLen
    new tName[42],players[32],pCount

    get_players(players,pCount)

    new maxPages = ((pCount - 1) / 7) + 1 // находим макс. кол-во страниц

    // отображаем с начала, если такой страницы не существует
    if(page > maxPages)
        page = 0

    // начальный индекс игрока согласно странице
    new usrIndex = (7 * page)

    menuLen += formatex(menuText[menuLen],MENU_LEN - 1 - menuLen,"%L %L\R\y%d/%d^n",
        id,"MENU_TAG",id,"MENU_TITLE",page + 1,maxPages)

    // добавляем игроков в меню
    while(usrIndex < pCount){
        get_user_name(players[usrIndex],tName,31)
        menuKeys |= (1 << usrIndex % 7)

        menuLen += formatex(menuText[menuLen],MENU_LEN - 1 - menuLen,"^n\r%d.\w %s",
            (usrIndex % 7) + 1,tName)

        usrIndex ++

        // перываем заполнение
        // если данная страница уже заполнена
        if(!(usrIndex % 7))
            break
    }

    // вариант просмотра статистики

    switch(g_MenuStatus[id][0])
    {
        case 0:menuLen += formatex(menuText[menuLen],MENU_LEN - 1 - menuLen,"^n^n\r%d.\w %L",8,id,"MENU_RANK")
        case 1: menuLen += formatex(menuText[menuLen],MENU_LEN - 1 - menuLen,"^n^n\r%d.\w %L",8,id,"MENU_STATS")
        #if defined CSSTATSX_SQL
        case 2: menuLen += formatex(menuText[menuLen],MENU_LEN - 1 - menuLen,"^n^n\r%d.\w %L",8,id,"CSXSQL_SETITLE")
        #endif

    }

    menuKeys |= MENU_KEY_8

    if(!(usrIndex % 7)){
        menuLen += formatex(menuText[menuLen],MENU_LEN - 1 - menuLen,"^n^n\r%d.\w %L",9,id,"MORE")
        menuKeys |= MENU_KEY_9
    }

    if((7 * page)){
        menuLen += formatex(menuText[menuLen],MENU_LEN - 1 - menuLen,"^n^n\r%d.\w %L",0,id,"BACK")
        menuKeys |= MENU_KEY_0
    }else{
        menuLen += formatex(menuText[menuLen],MENU_LEN - 1 - menuLen,"^n^n\r%d.\w %L",0,id,"EXIT")
        menuKeys |= MENU_KEY_0
    }


    show_menu(id,menuKeys,menuText,-1,"Stats Menu")

    return PLUGIN_HANDLED
}

public actionStatsMenu(id,key){
    switch(key){
        case 0..6:{
            new usrIndex = key + (7 * g_MenuStatus[id][1]) + 1

            if(!is_user_connected(id)){
                ShowStatsMenu(id,g_MenuStatus[id][1])

                return PLUGIN_HANDLED
            }

            switch(g_MenuStatus[id][0])
            {
                case 0: RankStatsSay(id,usrIndex)
                case 1: StatsMeSay(id,usrIndex)
                #if defined CSSTATSX_SQL
                case 2: SeStats_Show(id,usrIndex)
                #endif
            }

            ShowStatsMenu(id,g_MenuStatus[id][1])
        }
        case 7:{
            g_MenuStatus[id][0] ++
            #if defined CSSTATSX_SQL
            if(g_MenuStatus[id][0] > 2)
                g_MenuStatus[id][0] = 0
            #else
            if(g_MenuStatus[id][0] > 1)
                g_MenuStatus[id][0] = 0
            #endif

            ShowStatsMenu(id,g_MenuStatus[id][1])
        }
        case 8:{
            g_MenuStatus[id][1] ++
            ShowStatsMenu(id,g_MenuStatus[id][1])
        }
        case 9:{
            if(g_MenuStatus[id][1]){
                g_MenuStatus[id][1] --
                ShowStatsMenu(id,g_MenuStatus[id][1])
            }
        }
    }

    return PLUGIN_HANDLED
}
плагин в debug переводить, чтобы увидеть лимит?
 
Сообщения
408
Реакции
782
D1esel, /top15 в чат написать, оно одновременно с открытием MOTD должно выдать в чат размер (число).
 
Сообщения
408
Реакции
782
D1esel, ну, как и ожидалось, лимит.
Обойти лимит без модификации плагина под работу через внешний php-скрипт не получится, а на коленке этого не сделать.
Как вариант решения вопроса - можно убрать вывод рангов, или выводить одновременно меньше позиций (не 10, а 9, или даже 8)
 
Сообщения
10
Реакции
0
Предупреждения
5
BlackSignature, увеличить лимит не получится, отредактировав переменную?
Код:
#define BUFF_LEN 1535

new theBuffer[BUFF_LEN + 1] = 0
 
Сообщения
408
Реакции
782
D1esel, можно попробовать в aes_statsx_cstrike.sma, в функции SayTop
Код:
    if(Pos == 15 || Pos <= 0)
-         Pos = 10
+         Pos = MAX_TOP
ну и сам MAX_TOP уменьшить
Код:
- #define MAX_TOP            10
+ #define MAX_TOP            8
 

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

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