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

Статус
В этой теме нельзя размещать новые ответы.
Сообщения
154
Реакции
46
Помог
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 PRoSToTeM@ Kill_assist_rea running
[ 33] [ReAPI] Best player of 0.6 F@nt0M 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 F@nt0M 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)
Ошибка плавающая
 
Последнее редактирование модератором:
Сообщения
432
Реакции
409
Помог
14 раз(а)
Ошибки всегда именно по DB_QueryTop15 или могут быть другие? Где хостится сервер? Где БД? Какой пинг между сервером и БД?
 
Сообщения
154
Реакции
46
Помог
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
11 Сен 2019
Top отображается не до конца
Отсутствует информация по 10му игроку,а именно
- попаданий
- в голову
- точность
- скилл
- звание (опыт)
и так всегда
18791
 
Последнее редактирование:
Сообщения
1,175
Реакции
2,144
Помог
57 раз(а)
D1esel,
Top отображается не до конца
Может просто не влезает весь текст в буфер? Можно сделать вывод strlen() буфера в чат при показе, и посмотреть. Ведь если мотд генерируется внутри плагина (т.е. вывод идёт не через внешний php-скрипт), то размер ограничивается. А у вас вон русскоязычные ранги, каждый символ по две ячейки жрёт.
Код:
/**
 * The maximum buffer size that can be displayed in a MOTD.
 */
#define MAX_MOTD_LENGTH 1536
 
Сообщения
154
Реакции
46
Помог
5 раз(а)
BlackSignature, я прошу прощения за свою тупость, но в плагине csstatsx_sql.sma я не нашел переменной
Код:
/**
 * The maximum buffer size that can be displayed in a MOTD.
 */
#define MAX_MOTD_LENGTH 1536
Можно для дурачков, что и где необходимо поправить или добавить?
 
Сообщения
1,419
Реакции
2,508
Помог
59 раз(а)
BlackSignature, я прошу прощения за свою тупость, но в плагине csstatsx_sql.sma я не нашел переменной
Код:
/**
 * The maximum buffer size that can be displayed in a MOTD.
 */
#define MAX_MOTD_LENGTH 1536
Можно для дурачков, что и где необходимо поправить или добавить?
Это дефайн, в котором указана максимальная длина MOTD. Он не добавлен никуда. Если хочется вписать в плагин то можно вписать, от этого ничего не изменится, разве что текущий размер буффера меньше 1536.
 
Сообщения
1,175
Реакции
2,144
Помог
57 раз(а)
D1esel, если хочется разобраться, лимит ли это виноват, то в aes_statsx_cstrike.sma надо найти функцию SayTopFormer(), в ней в самом низу добавить
Код:
+ client_print(id, print_chat, "motd len: %i", strlen(theBuffer))
show_motd(id,theBuffer,title)
Обойти лимит без модификации плагина под работу через внешний php-скрипт не получится, а на коленке этого не сделать.
 
Сообщения
154
Реакции
46
Помог
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,"^"","^"^"");
}
 
Сообщения
1,175
Реакции
2,144
Помог
57 раз(а)
D1esel,
если хочется разобраться, лимит ли это виноват, то в aes_statsx_cstrike.sma надо найти функцию SayTopFormer(), в ней в самом низу добавить
 
Сообщения
154
Реакции
46
Помог
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 переводить, чтобы увидеть лимит?
 
Сообщения
1,175
Реакции
2,144
Помог
57 раз(а)
D1esel, /top15 в чат написать, оно одновременно с открытием MOTD должно выдать в чат размер (число).
 
Сообщения
1,175
Реакции
2,144
Помог
57 раз(а)
D1esel, ну, как и ожидалось, лимит.
Обойти лимит без модификации плагина под работу через внешний php-скрипт не получится, а на коленке этого не сделать.
Как вариант решения вопроса - можно убрать вывод рангов, или выводить одновременно меньше позиций (не 10, а 9, или даже 8)
 
Сообщения
154
Реакции
46
Помог
5 раз(а)
BlackSignature, увеличить лимит не получится, отредактировав переменную?
Код:
#define BUFF_LEN 1535

new theBuffer[BUFF_LEN + 1] = 0
 
Сообщения
1,175
Реакции
2,144
Помог
57 раз(а)
D1esel, нет, это ничего не даст. w0w же уже написал об этом.
 
Сообщения
1,175
Реакции
2,144
Помог
57 раз(а)
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
 
Сообщения
154
Реакции
46
Помог
5 раз(а)
Основная проблема все еще остается
Код:
L 09/17/2019 - 20:02:43: Start of error session.
L 09/17/2019 - 20:02:43: Info (map "de_dust2") (file "addons/amxmodx/logs/error_20190917.log")
L 09/17/2019 - 20:02:43: [MySQL] Thread worker was unable to start.
L 09/17/2019 - 20:02:43: [AMXX] Displaying debug trace (plugin "csstatsx_sql.amxx", version "0.7.4+2")
L 09/17/2019 - 20:02:43: [AMXX] Run time error 10: native error (native "SQL_ThreadQuery")
L 09/17/2019 - 20:02:43: [AMXX]    [0] csstatsx_sql.sma::DB_QueryTop15 (line 3095)
L 09/17/2019 - 20:02:43: [AMXX]    [1] csstatsx_sql.sma::Cache_Stats_UpdateQueue (line 3003)
L 09/17/2019 - 20:02:43: [AMXX]    [2] csstatsx_sql.sma::SQL_Handler (line 3290)
 
Сообщения
957
Реакции
1,184
Помог
52 раз(а)
Основная проблема все еще остается
Код:
L 09/17/2019 - 20:02:43: Start of error session.
L 09/17/2019 - 20:02:43: Info (map "de_dust2") (file "addons/amxmodx/logs/error_20190917.log")
L 09/17/2019 - 20:02:43: [MySQL] Thread worker was unable to start.
L 09/17/2019 - 20:02:43: [AMXX] Displaying debug trace (plugin "csstatsx_sql.amxx", version "0.7.4+2")
L 09/17/2019 - 20:02:43: [AMXX] Run time error 10: native error (native "SQL_ThreadQuery")
L 09/17/2019 - 20:02:43: [AMXX]    [0] csstatsx_sql.sma::DB_QueryTop15 (line 3095)
L 09/17/2019 - 20:02:43: [AMXX]    [1] csstatsx_sql.sma::Cache_Stats_UpdateQueue (line 3003)
L 09/17/2019 - 20:02:43: [AMXX]    [2] csstatsx_sql.sma::SQL_Handler (line 3290)
Судя по всему в БД необходимо увеличить лимит на одновременное кол-во подключений, воркер не может быть создан чаще всего из-за этого
 
Сообщения
49
Реакции
7
Помог
1 раз(а)
Точно такая же ошибка иногда появляется.
L 08/27/2021 - 02:58:16: [MySQL] Thread worker was unable to start.
L 08/27/2021 - 02:58:16: [AMXX] Displaying debug trace (plugin "csstatsx_sql.amxx", version "0.7.4+2")
L 08/27/2021 - 02:58:16: [AMXX] Run time error 10: native error (native "SQL_ThreadQuery")
L 08/27/2021 - 02:58:16: [AMXX] [0] csstatsx_sql.sma::DB_QueryTop15 (line 3095)
L 08/27/2021 - 02:58:16: [AMXX] [1] csstatsx_sql.sma::Cache_Stats_UpdateQueue (line 3003)
L 08/27/2021 - 02:58:16: [AMXX] [2] csstatsx_sql.sma::SQL_Handler (line 3290)
D1esel , решили проблему?
 
Статус
В этой теме нельзя размещать новые ответы.

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

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