HLMOD.HU Forrás Megtekintés - www.hlmod.hu
  1. /*
  2. * CSStatsX MySQL v. 0.5
  3. * by serfreeman1337 http://1337.uz/
  4. */
  5.  
  6. #include <amxmodx>
  7. #include <sqlx>
  8.  
  9. #include <fakemeta>
  10.  
  11. #define PLUGIN "CSStatsX MySQL"
  12. #define VERSION "0.5"
  13. #define AUTHOR "serfreeman1337" // AKA SerSQL1337
  14.  
  15. #define LASTUPDATE "22, February (02), 2016"
  16.  
  17. #if AMXX_VERSION_NUM < 183
  18. #define MAX_PLAYERS 32
  19. #define MAX_NAME_LENGTH 32
  20. new MaxClients
  21. #endif
  22.  
  23. /* - SQL - */
  24.  
  25. new Handle:sql
  26. new Handle:sql_con
  27.  
  28. /* - КОНСТАНТЫ - */
  29.  
  30. enum _:sql_que_type // тип sql запроса
  31. {
  32. SQL_DUMMY,
  33. SQL_LOAD, // загрузка статистики
  34. SQL_UPDATE, // обновление
  35. SQL_INSERT, // внесение новой записи
  36. SQL_UPDATERANK, // получение ранков игроков,
  37. SQL_GETSTATS // потоквый запрос на get_stats
  38. }
  39.  
  40. enum _:load_state_type // состояние получение статистики
  41. {
  42. LOAD_NO, // данных нет
  43. LOAD_WAIT, // ожидание данных
  44. LOAD_OK, // есть данные
  45. LOAD_NEW, // новая запись
  46. LOAD_NEWWAIT, // новая запись, ждем ответа
  47. LOAD_UPDATE // перезагрузить после обновления
  48. }
  49.  
  50. enum _:row_ids // столбцы таблицы
  51. {
  52. ROW_ID,
  53. ROW_STEAMID,
  54. ROW_NAME,
  55. ROW_IP,
  56. ROW_SKILL,
  57. ROW_KILLS,
  58. ROW_DEATHS,
  59. ROW_HS,
  60. ROW_TKS,
  61. ROW_SHOTS,
  62. ROW_HITS,
  63. ROW_DMG,
  64. ROW_BOMBDEF,
  65. ROW_BOMBDEFUSED,
  66. ROW_BOMBPLANTS,
  67. ROW_BOMBEXPLOSIONS,
  68. ROW_H0,
  69. ROW_H1,
  70. ROW_H2,
  71. ROW_H3,
  72. ROW_H4,
  73. ROW_H5,
  74. ROW_H6,
  75. ROW_H7,
  76. ROW_ONLINETIME,
  77. ROW_FIRSTJOIN,
  78. ROW_LASTJOIN
  79. }
  80.  
  81. new const row_names[row_ids][] = // имена столбцов
  82. {
  83. "id",
  84. "steamid",
  85. "name",
  86. "ip",
  87. "skill",
  88. "kills",
  89. "deaths",
  90. "hs",
  91. "tks",
  92. "shots",
  93. "hits",
  94. "dmg",
  95. "bombdef",
  96. "bombdefused",
  97. "bombplants",
  98. "bombexplosions",
  99. "h_0",
  100. "h_1",
  101. "h_2",
  102. "h_3",
  103. "h_4",
  104. "h_5",
  105. "h_6",
  106. "h_7",
  107. "connection_time",
  108. "first_join",
  109. "last_join"
  110. }
  111.  
  112. enum _:STATS
  113. {
  114. STATS_KILLS,
  115. STATS_DEATHS,
  116. STATS_HS,
  117. STATS_TK,
  118. STATS_SHOTS,
  119. STATS_HITS,
  120. STATS_DMG,
  121.  
  122. STATS_END
  123. }
  124.  
  125. enum _:KILL_EVENT
  126. {
  127. NORMAL,
  128. SUICIDE,
  129. WORLD,
  130. WORLDSPAWN
  131. }
  132.  
  133. const QUERY_LENGTH = 1472 // размер переменной sql запроса
  134.  
  135. #define STATS2_DEFAT 0
  136. #define STATS2_DEFOK 1
  137. #define STATS2_PLAAT 2
  138. #define STATS2_PLAOK 3
  139. #define STATS2_END 4
  140.  
  141. new const task_rankupdate = 31337
  142. new const task_confin = 21337
  143.  
  144. #define MAX_CWEAPONS 6
  145. #define MAX_WEAPONS CSW_P90 + 1 + MAX_CWEAPONS
  146. #define HIT_END HIT_RIGHTLEG + 1
  147.  
  148. /* - СТРУКТУРА ДАННЫХ - */
  149.  
  150. enum _:player_data_struct
  151. {
  152. PLAYER_ID, // ид игрока в базе данных
  153. PLAYER_LOADSTATE, // состояние загрузки статистики игрока
  154. PLAYER_RANK, // ранк игрока
  155. PLAYER_STATS[STATS_END], // статистика игрока
  156. PLAYER_STATSLAST[STATS_END], // разница в статистики
  157. PLAYER_HITS[HIT_END], // статистика попаданий
  158. PLAYER_HITSLAST[HIT_END], // разница в статистике попаданий
  159. PLAYER_STATS2[4], // статистика cstrike
  160. PLAYER_STATS2LAST[4], // разница
  161. Float:PLAYER_SKILL, // скилл
  162. PLAYER_ONLINE, // время онлайна
  163. // я не помню чо за diff и last, но без этого не работает XD
  164. Float:PLAYER_SKILLLAST,
  165. PLAYER_ONLINEDIFF,
  166. PLAYER_ONLINELAST
  167. }
  168.  
  169. enum _:stats_cache_struct // кеширование для get_stats
  170. {
  171. CACHE_STATS[8],
  172. CACHE_STATS2[8],
  173. CACHE_HITS[8],
  174. CACHE_NAME[32],
  175. CACHE_STEAMID[30],
  176. CACHE_SKILL,
  177. bool:CACHE_LAST
  178. }
  179.  
  180. enum _:cvar_set
  181. {
  182. CVAR_SQL_HOST,
  183. CVAR_SQL_USER,
  184. CVAR_SQL_PASS,
  185. CVAR_SQL_DB,
  186. CVAR_SQL_TABLE,
  187. CVAR_SQL_TYPE,
  188. CVAR_SQL_CREATE_DB,
  189.  
  190. CVAR_UPDATESTYLE,
  191. CVAR_RANK,
  192. CVAR_RANKFORMULA,
  193. CVAR_SKILLFORMULA,
  194. CVAR_RANKBOTS,
  195. CVAR_USEFORWARDS
  196. }
  197.  
  198. /* - ПЕРЕМЕННЫЕ - */
  199.  
  200. new player_data[MAX_PLAYERS + 1][player_data_struct]
  201. new statsnum
  202.  
  203. new cvar[cvar_set]
  204.  
  205. new Trie:stats_cache_trie // дерево кеша для get_stats // ключ - ранг
  206.  
  207. /* - CSSTATS CORE - */
  208.  
  209. #pragma dynamic 32768
  210.  
  211. // wstats
  212. new player_wstats[MAX_PLAYERS + 1][MAX_WEAPONS][STATS_END + HIT_END]
  213.  
  214. // wstats2
  215. new player_wstats2[MAX_PLAYERS + 1][STATS2_END]
  216.  
  217. // wrstats rstats
  218. new player_wrstats[MAX_PLAYERS + 1][MAX_WEAPONS][STATS_END + HIT_END]
  219.  
  220. // vstats
  221. new player_vstats[MAX_PLAYERS + 1][MAX_PLAYERS + 1][STATS_END + HIT_END + MAX_NAME_LENGTH]
  222.  
  223. // astats
  224. new player_astats[MAX_PLAYERS + 1][MAX_PLAYERS + 1][STATS_END + HIT_END + MAX_NAME_LENGTH]
  225.  
  226. new FW_Death
  227. new FW_Damage
  228. new FW_BPlanting
  229. new FW_BPlanted
  230. new FW_BExplode
  231. new FW_BDefusing
  232. new FW_BDefused
  233. new FW_GThrow
  234.  
  235. new dummy_ret
  236.  
  237. // осталось монитор прихуярить
  238.  
  239. new g_planter
  240. new g_defuser
  241.  
  242. #define WEAPON_INFO_SIZE 1 + (MAX_NAME_LENGTH * 2)
  243.  
  244. new Array:weapons_data // массив с инфой по оружию
  245. new Trie:log_ids_trie // дерево для быстрого определения id оружия по лог-коду
  246.  
  247. // макрос для помощи реагистрации инфы по оружию
  248. #define REG_INFO(%0,%1,%2)\
  249. weapon_info[0] = %0;\
  250. copy(weapon_info[1],MAX_NAME_LENGTH,%1);\
  251. copy(weapon_info[MAX_NAME_LENGTH ],MAX_NAME_LENGTH,%2);\
  252. ArrayPushArray(weapons_data,weapon_info);\
  253. TrieSetCell(log_ids_trie,%2,ArraySize(weapons_data) - 1)
  254.  
  255. public plugin_init()
  256. {
  257. register_plugin(PLUGIN,VERSION,AUTHOR)
  258. register_cvar("csstatsx_sql", VERSION, FCVAR_SERVER | FCVAR_SPONLY | FCVAR_UNLOGGED)
  259.  
  260. /*
  261. * хост mysql
  262. */
  263. cvar[CVAR_SQL_HOST] = register_cvar("csstats_sql_host","127.0.0.1",FCVAR_UNLOGGED|FCVAR_PROTECTED)
  264.  
  265. /*
  266. * пользователь mysql
  267. */
  268. cvar[CVAR_SQL_USER] = register_cvar("csstats_sql_user","root",FCVAR_UNLOGGED|FCVAR_PROTECTED)
  269.  
  270. /*
  271. * пароль mysql
  272. */
  273. cvar[CVAR_SQL_PASS] = register_cvar("csstats_sql_pass","",FCVAR_UNLOGGED|FCVAR_PROTECTED)
  274.  
  275. /*
  276. * название БД mysql или sqlite
  277. */
  278. cvar[CVAR_SQL_DB] = register_cvar("csstats_sql_db","amxx",FCVAR_UNLOGGED|FCVAR_PROTECTED)
  279.  
  280. /*
  281. * название таблицы в БД
  282. */
  283. cvar[CVAR_SQL_TABLE] = register_cvar("csstats_sql_table","csstats",FCVAR_UNLOGGED|FCVAR_PROTECTED)
  284.  
  285. /*
  286. * тип бд
  287. * mysql - база данных MySQL
  288. * sqlite - локальная база данных SQLite
  289. */
  290. cvar[CVAR_SQL_TYPE] = register_cvar("csstats_sql_type","mysql")
  291.  
  292. /*
  293. * отправка запроса на создание таблицы
  294. * 0 - не отправлять запрос
  295. * 1 - отправлять запрос при загрузке карты
  296. */
  297. cvar[CVAR_SQL_CREATE_DB] = register_cvar("csstats_sql_create_db","1")
  298.  
  299. /*
  300. * как вести учет игроков
  301. * -1 - не учитывать
  302. * 0 - по нику
  303. * 1 - по steamid
  304. * 2 - по ip
  305. */
  306. cvar[CVAR_RANK] = get_cvar_pointer("csstats_rank")
  307.  
  308. if(!cvar[CVAR_RANK])
  309. cvar[CVAR_RANK] = register_cvar("csstats_rank","1")
  310.  
  311. /*
  312. * запись статистики ботов
  313. * 0 - не записывать
  314. * 1 - записывать0
  315. */
  316. cvar[CVAR_RANKBOTS] = get_cvar_pointer("csstats_rankbots")
  317.  
  318. if(!cvar[CVAR_RANKBOTS])
  319. cvar[CVAR_RANKBOTS] = register_cvar("csstats_rankbots","1")
  320.  
  321. /*
  322. * как обновлять статистику игрока в БД
  323. * -2 - при смерти и дисконнекте
  324. * -1 - в конце раунда и дисконнекте
  325. * 0 - при дисконнекте
  326. * значение больше 0 - через указанное кол-во секунд и дисконнекте
  327. */
  328. cvar[CVAR_UPDATESTYLE] = register_cvar("csstats_sql_update","-2")
  329.  
  330. /*
  331. * включить собственные форварды для client_death, client_damage
  332. * 0 - выключить
  333. * 1 - включить, небоходимо, если csstats_sql используется в качестве замены модуля
  334. */
  335. cvar[CVAR_USEFORWARDS] = register_cvar("csstats_sql_forwards","0")
  336.  
  337. /*
  338. * формула расчета ранга
  339. * 0 - убйиства - смерти - тк
  340. * 1 - убийства
  341. * 2 - убийства + хедшоты
  342. * 3 - скилл
  343. */
  344. cvar[CVAR_RANKFORMULA] = register_cvar("csstats_sql_rankformula","0")
  345.  
  346. /*
  347. * формула расчета скилла
  348. * 0 - The ELO Method (http://fastcup.net/rating.html)
  349. */
  350. cvar[CVAR_SKILLFORMULA] = register_cvar("csstats_sql_skillformula","0")
  351.  
  352. #if AMXX_VERSION_NUM < 183
  353. MaxClients = get_maxplayers()
  354. #endif
  355.  
  356. register_logevent("LogEventHooK_RoundEnd", 2, "1=Round_End")
  357. register_logevent("LogEventHooK_RoundStart", 2, "1=Round_Start")
  358.  
  359. register_event("CurWeapon","EventHook_CurWeapon","b","1=1")
  360. register_event("Damage","EventHook_Damage","b","2!0")
  361. register_event("DeathMsg","EventHook_DeathMsg","a")
  362. register_event("BarTime","EventHook_BarTime","be")
  363. register_event("SendAudio","EventHook_SendAudio","a")
  364. register_event("TextMsg","EventHook_TextMsg","a")
  365. }
  366.  
  367. public plugin_cfg()
  368. {
  369. // форсируем выполнение exec addons/amxmodx/configs/amxx.cfg
  370. server_exec()
  371.  
  372. // читаем квары на подключение
  373. new host[128],user[64],pass[64],db[64],table[30],type[10]
  374. get_pcvar_string(cvar[CVAR_SQL_HOST],host,charsmax(host))
  375. get_pcvar_string(cvar[CVAR_SQL_USER],user,charsmax(user))
  376. get_pcvar_string(cvar[CVAR_SQL_PASS],pass,charsmax(pass))
  377. get_pcvar_string(cvar[CVAR_SQL_DB],db,charsmax(db))
  378. get_pcvar_string(cvar[CVAR_SQL_TABLE],table,charsmax(table))
  379. get_pcvar_string(cvar[CVAR_SQL_TYPE],type,charsmax(type))
  380.  
  381. SQL_SetAffinity(type)
  382.  
  383. sql = SQL_MakeDbTuple(host,user,pass,db)
  384.  
  385. // запрос на создание таблицы
  386. if(get_pcvar_num(cvar[CVAR_SQL_CREATE_DB]))
  387. {
  388. new query[QUERY_LENGTH],que_len
  389.  
  390. new sql_data[1]
  391. sql_data[0] = SQL_DUMMY
  392.  
  393. // запрос для mysql
  394. if(strcmp(type,"mysql") == 0)
  395. {
  396. que_len += formatex(query[que_len],charsmax(query) - que_len,"\
  397. CREATE TABLE IF NOT EXISTS `%s` (\
  398. `id` int(11) NOT NULL AUTO_INCREMENT,\
  399. `steamid` varchar(30) NOT NULL,\
  400. `name` varchar(32) NOT NULL,\
  401. `ip` varchar(16) NOT NULL,\
  402. `skill` float NOT NULL DEFAULT '0.0',\
  403. `kills` int(11) NOT NULL DEFAULT '0',\
  404. `deaths` int(11) NOT NULL DEFAULT '0',\
  405. `hs` int(11) NOT NULL DEFAULT '0',",table)
  406. que_len += formatex(query[que_len],charsmax(query) - que_len,"`tks` int(11) NOT NULL DEFAULT '0',\
  407. `shots` int(11) NOT NULL DEFAULT '0',\
  408. `hits` int(11) NOT NULL DEFAULT '0',\
  409. `dmg` int(11) NOT NULL DEFAULT '0',\
  410. `bombdef` int(11) NOT NULL DEFAULT '0',\
  411. `bombdefused` int(11) NOT NULL DEFAULT '0',\
  412. `bombplants` int(11) NOT NULL DEFAULT '0',\
  413. `bombexplosions` int(11) NOT NULL DEFAULT '0',\
  414. `h_0` int(11) NOT NULL DEFAULT '0',")
  415. que_len += formatex(query[que_len],charsmax(query) - que_len,"`h_1` int(11) NOT NULL DEFAULT '0',\
  416. `h_2` int(11) NOT NULL DEFAULT '0',\
  417. `h_3` int(11) NOT NULL DEFAULT '0',\
  418. `h_4` int(11) NOT NULL DEFAULT '0',\
  419. `h_5` int(11) NOT NULL DEFAULT '0',\
  420. `h_6` int(11) NOT NULL DEFAULT '0',\
  421. `h_7` int(11) NOT NULL DEFAULT '0',\
  422. `connection_time` int(11) NOT NULL,\
  423. `first_join` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,")
  424. que_len += formatex(query[que_len],charsmax(query) - que_len,"`last_join` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\
  425. PRIMARY KEY (id),\
  426. KEY `steamid` (`steamid`(16)),\
  427. KEY `name` (`name`(16)),\
  428. KEY `ip` (`ip`)\
  429. ) DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;")
  430. }
  431. // запрос для sqlite
  432. else if(strcmp(type,"sqlite") == 0)
  433. {
  434. que_len += formatex(query[que_len],charsmax(query) - que_len,"\
  435. CREATE TABLE IF NOT EXISTS `%s` (\
  436. `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,\
  437. `steamid` TEXT NOT NULL,\
  438. `name` TEXT NOT NULL,\
  439. `ip` TEXT NOT NULL,\
  440. `skill` REAL NOT NULL DEFAULT 0.0,\
  441. `kills` INTEGER NOT NULL DEFAULT 0,\
  442. `deaths` INTEGER NOT NULL DEFAULT 0,",table)
  443. que_len += formatex(query[que_len],charsmax(query) - que_len,"`hs` INTEGER NOT NULL DEFAULT 0,\
  444. `tks` INTEGER NOT NULL DEFAULT 0,\
  445. `shots` INTEGER NOT NULL DEFAULT 0,\
  446. `hits` INTEGER NOT NULL DEFAULT 0,\
  447. `dmg` INTEGER NOT NULL DEFAULT 0,\
  448. `bombdef` INTEGER NOT NULL DEFAULT 0,\
  449. `bombdefused` INTEGER NOT NULL DEFAULT 0,\
  450. `bombplants` INTEGER NOT NULL DEFAULT 0,\
  451. `bombexplosions` INTEGER NOT NULL DEFAULT 0,")
  452. que_len += formatex(query[que_len],charsmax(query) - que_len,"`h_0` INTEGER NOT NULL DEFAULT 0,\
  453. `h_1` INTEGER NOT NULL DEFAULT 0,\
  454. `h_2` INTEGER NOT NULL DEFAULT 0,\
  455. `h_3` INTEGER NOT NULL DEFAULT 0,\
  456. `h_4` INTEGER NOT NULL DEFAULT 0,\
  457. `h_5` INTEGER NOT NULL DEFAULT 0,\
  458. `h_6` INTEGER NOT NULL DEFAULT 0,\
  459. `h_7` INTEGER NOT NULL DEFAULT 0,")
  460. que_len += formatex(query[que_len],charsmax(query) - que_len,"`connection_time` INTEGER NOT NULL DEFAULT 0,\
  461. `first_join` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\
  462. `last_join` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00'\
  463. );")
  464. }
  465.  
  466. SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)
  467. }
  468.  
  469. // для поддержки utf8 ников требуется AMXX 1.8.3-dev-git3799 или выше
  470.  
  471. #if AMXX_VERSION_NUM >= 183
  472. SQL_SetCharset(sql,"utf8")
  473. #endif
  474.  
  475. // обновление статистики в БД каждые n сек
  476. if(get_pcvar_num(cvar[CVAR_UPDATESTYLE]) > 0)
  477. {
  478. set_task(
  479. float(get_pcvar_num(cvar[CVAR_UPDATESTYLE])),
  480. "DB_SaveAll",
  481. .flags = "b"
  482. )
  483. }
  484.  
  485. if(get_pcvar_num(cvar[CVAR_USEFORWARDS]))
  486. { FW_Death = CreateMultiForward("client_death",ET_IGNORE,FP_CELL,FP_CELL,FP_CELL,FP_CELL,FP_CELL)
  487. FW_Damage = CreateMultiForward("client_damage",ET_IGNORE,FP_CELL,FP_CELL,FP_CELL,FP_CELL,FP_CELL,FP_CELL)
  488. FW_BPlanting = CreateMultiForward("bomb_planting",ET_IGNORE,FP_CELL)
  489. FW_BPlanted = CreateMultiForward("bomb_planted",ET_IGNORE,FP_CELL)
  490. FW_BExplode = CreateMultiForward("bomb_explode",ET_IGNORE,FP_CELL,FP_CELL)
  491. FW_BDefusing = CreateMultiForward("bomb_defusing",ET_IGNORE,FP_CELL)
  492. FW_BDefused = CreateMultiForward("bomb_defused",ET_IGNORE,FP_CELL)
  493. FW_GThrow = CreateMultiForward("grenade_throw",ET_IGNORE,FP_CELL,FP_CELL,FP_CELL)
  494.  
  495. register_forward(FM_SetModel,"FMHook_SetModel",true)
  496. }
  497.  
  498. new weapon_info[WEAPON_INFO_SIZE]
  499.  
  500. //
  501.  
  502.  
  503.  
  504. log_ids_trie = TrieCreate()
  505. // is_meele + название + логнейм
  506. weapons_data = ArrayCreate(WEAPON_INFO_SIZE)
  507.  
  508. REG_INFO(false,"","")
  509. REG_INFO(false,"p228","p228")
  510. REG_INFO(false,"","")
  511. REG_INFO(false,"scout","scout")
  512. REG_INFO(false,"hegrenade","grenade")
  513. REG_INFO(false,"xm1014","xm1014")
  514. REG_INFO(false,"c4","weapon_c4")
  515. REG_INFO(false,"mac10","mac10")
  516. REG_INFO(false,"aug","aug")
  517. REG_INFO(false,"sgrenade","grenade")
  518. REG_INFO(false,"elite","elite")
  519. REG_INFO(false,"fiveseven","fiveseven")
  520. REG_INFO(false,"ump45","ump45")
  521. REG_INFO(false,"sg550","sg550")
  522. REG_INFO(false,"galil","galil")
  523. REG_INFO(false,"famas","famas")
  524. REG_INFO(false,"usp","usp")
  525. REG_INFO(false,"glock18","glock18")
  526. REG_INFO(false,"awp","awp")
  527. REG_INFO(false,"mp5navy","mp5navy")
  528. REG_INFO(false,"m249","m249")
  529. REG_INFO(false,"m3","m3")
  530. REG_INFO(false,"m4a1","m4a1")
  531. REG_INFO(false,"tmp","tmp")
  532. REG_INFO(false,"g3sg1","g3sg1")
  533. REG_INFO(false,"flashbang","flashbang")
  534. REG_INFO(false,"deagle","deagle")
  535. REG_INFO(false,"sg552","sg552")
  536. REG_INFO(false,"ak47","ak47")
  537. REG_INFO(true,"knife","knife")
  538. REG_INFO(false,"p90","p90")
  539. }
  540.  
  541. /*
  542. * загружаем статистику при подключении
  543. */
  544. public client_putinserver(id)
  545. {
  546. arrayset(player_data[id],0,player_data_struct)
  547. DB_LoadPlayerData(id)
  548. }
  549.  
  550. /*
  551. * сохраняем статистику при дисконнекте
  552. */
  553. #if AMXX_VERSION_NUM < 183
  554. public client_disconnect(id)
  555. #else
  556. public client_disconnected(id)
  557. #endif
  558. {
  559. DB_SavePlayerData(id)
  560.  
  561. reset_user_allstats(id)
  562. reset_user_wstats(id)
  563. }
  564.  
  565. //
  566. // Регистрация выстрелов
  567. //
  568. public EventHook_CurWeapon(player)
  569. {
  570. #define LASTWEAPON 0 // id посл. оружия
  571. #define LASTCLIP 1 // кол-во потронов посл. оружия
  572.  
  573. static event_tmp[MAX_PLAYERS + 1][LASTCLIP + 1] // помним послед
  574. static weapon_id; weapon_id = read_data(2)
  575. static clip_ammo; clip_ammo = read_data(3)
  576.  
  577. if(event_tmp[player][LASTWEAPON] != weapon_id) // оружие было изменено, запоминаем новое кол-во патронов
  578. {
  579. event_tmp[player][LASTWEAPON] = weapon_id
  580. event_tmp[player][LASTCLIP] = clip_ammo
  581. }
  582. else if(event_tmp[player][LASTCLIP] > clip_ammo) // кол-во патронов в магазине уменьшилось, регистрируем выстрел
  583. {
  584. Stats_SaveShot(player,weapon_id)
  585. event_tmp[player][LASTCLIP] = clip_ammo
  586. }
  587. }
  588.  
  589. //
  590. // Регистрация попадания
  591. //
  592. public EventHook_Damage(player)
  593. {
  594. static damage_take;damage_take = read_data(2)
  595. static dmg_inflictor;dmg_inflictor = pev(player,pev_dmg_inflictor)
  596.  
  597. if(pev_valid(dmg_inflictor) != 2)
  598. {
  599. return PLUGIN_CONTINUE
  600. }
  601.  
  602. if(!(0 < dmg_inflictor <= MaxClients))
  603. {
  604. // урон с гранаты на данным момент не учитывается
  605.  
  606. return PLUGIN_CONTINUE
  607. }
  608.  
  609. static weapon_id,last_hit
  610. get_user_attacker(player,weapon_id,last_hit)
  611.  
  612. Stats_SaveHit(dmg_inflictor,player,damage_take,weapon_id,last_hit)
  613.  
  614. return PLUGIN_CONTINUE
  615. }
  616.  
  617. //
  618. // Регистрация убийств
  619. //
  620. public EventHook_DeathMsg()
  621. {
  622. new killer_id = read_data(1)
  623. new victim_id = read_data(2)
  624.  
  625. /*
  626. * пропускаем эту проверку, т.к. в плагине все переменные обнуляются при подключении
  627. if(!is_user_connected(killer_id) || !is_user_connected(victim_id))
  628. {
  629. return PLUGIN_CONTINUE
  630. }
  631. */
  632.  
  633. // узнаем лог код оружия
  634. new log_name[32]
  635. read_data(4,log_name,charsmax(log_name))
  636.  
  637. if(!log_name[0]) // убийство без оружия
  638. {
  639. return PLUGIN_CONTINUE
  640. }
  641.  
  642. new weapon_id
  643.  
  644. // пробуем получить id оружия на основе его лог-кода
  645. if(!TrieGetCell(log_ids_trie,log_name,weapon_id))
  646. {
  647. return PLUGIN_CONTINUE // убийство неизвестным оружием
  648. }
  649.  
  650. new last_hit
  651.  
  652. if(pev_valid(victim_id) == 2)
  653. {
  654. // получаем hitbox смертельного попадания
  655. #if AMXX_VERSION_NUM >= 183
  656. last_hit = get_ent_data(victim_id,"CBaseMonster","m_LastHitGroup")
  657. #else
  658. #define m_LastHitGroup 75
  659. last_hit = get_pdata_int(victim_id,m_LastHitGroup)
  660. #endif
  661. }
  662.  
  663. Stats_SaveKill(killer_id,victim_id,weapon_id,last_hit) // сохраняем
  664.  
  665. return PLUGIN_CONTINUE
  666. }
  667.  
  668. //
  669. // Регистрация установки и дефьюза бомбы
  670. //
  671. public EventHook_BarTime(player)
  672. {
  673. new duration = read_data(1)
  674.  
  675. if(!duration)
  676. {
  677. return PLUGIN_CONTINUE
  678. }
  679.  
  680. if(duration == 3)
  681. {
  682. g_planter = player
  683. g_defuser = 0
  684.  
  685. if(FW_BPlanting)
  686. ExecuteForward(FW_BPlanting,dummy_ret,player)
  687. }
  688. else
  689. {
  690. g_defuser = player
  691.  
  692. Stats_SaveBDefusing(player)
  693. }
  694.  
  695. return PLUGIN_CONTINUE
  696. }
  697.  
  698. public EventHook_SendAudio(player)
  699. {
  700. new audio_code[16]
  701. read_data(2,audio_code,charsmax(audio_code))
  702.  
  703. if (!player && audio_code[7] == 'B')
  704. {
  705. if (audio_code[11]=='P' && g_planter)
  706. {
  707. Stats_SaveBPlanted(g_planter)
  708. }
  709. else if (audio_code[11] =='D' && g_defuser)
  710. {
  711. Stats_SaveBDefused(g_defuser)
  712. }
  713. }
  714. }
  715.  
  716. public EventHook_TextMsg(player)
  717. {
  718. new message[16]
  719. read_data(2,message,charsmax(message))
  720.  
  721. if (!player)
  722. {
  723. if (message[1]=='T' && message[8] == 'B' && g_planter)
  724. {
  725. Stats_SaveBExplode(g_planter)
  726.  
  727. g_planter = 0
  728. g_defuser = 0
  729. }
  730. }
  731. }
  732.  
  733. //
  734. // Форвард grenade_throw
  735. //
  736. public FMHook_SetModel(ent,model[])
  737. {
  738. new owner = pev(ent,pev_owner)
  739.  
  740. new Float:dmg_time
  741. pev(ent,pev_dmgtime,dmg_time)
  742.  
  743. if(dmg_time <= 0.0 || !is_user_connected(owner))
  744. {
  745. return FMRES_IGNORED
  746. }
  747.  
  748. new classname[32]
  749. pev(ent,pev_classname,classname,charsmax(classname))
  750.  
  751. if(strcmp(classname,"grenade") != 0) // реагируем только на гранаты
  752. {
  753. return FMRES_IGNORED
  754. }
  755.  
  756. new wId
  757.  
  758. if(model[9] == 'h') // модель хеешки
  759. {
  760. wId = CSW_HEGRENADE
  761. }
  762. else if(model[9] == 'f') // модель флешки
  763. {
  764. wId = CSW_FLASHBANG
  765. }
  766. else if(model[9] == 's') // модель смока
  767. {
  768. wId = CSW_SMOKEGRENADE
  769. }
  770.  
  771. ExecuteForward(FW_GThrow,dummy_ret,owner,ent,wId)
  772.  
  773. return FMRES_IGNORED
  774. }
  775.  
  776. //
  777. // Учет выстрелов
  778. //
  779. Stats_SaveShot(player,wpn_id)
  780. {
  781. player_wstats[player][0][STATS_SHOTS] ++
  782. player_wstats[player][wpn_id][STATS_SHOTS] ++
  783.  
  784. player_wrstats[player][0][STATS_SHOTS] ++
  785. player_wrstats[player][wpn_id][STATS_SHOTS] ++
  786.  
  787. return true
  788. }
  789.  
  790. //
  791. // Учет попадания
  792. //
  793. Stats_SaveHit(attacker,victim,damage,wpn_id,hit_place)
  794. {
  795. player_wstats[attacker][0][STATS_HITS] ++
  796. player_wstats[attacker][0][STATS_DMG] += damage
  797. player_wstats[attacker][0][hit_place + STATS_END] ++
  798.  
  799. player_wrstats[attacker][0][STATS_HITS] ++
  800. player_wrstats[attacker][0][STATS_DMG] += damage
  801. player_wrstats[attacker][0][hit_place + STATS_END] ++
  802.  
  803. player_wstats[attacker][wpn_id][STATS_DMG] += damage
  804. player_wrstats[attacker][wpn_id][STATS_DMG] += damage
  805. player_wstats[attacker][wpn_id][STATS_HITS] ++
  806. player_wrstats[attacker][wpn_id][STATS_HITS] ++
  807. player_wstats[attacker][wpn_id][hit_place + STATS_END] ++
  808. player_wrstats[attacker][wpn_id][hit_place + STATS_END] ++
  809.  
  810. player_vstats[attacker][victim][STATS_HITS] ++
  811. player_vstats[attacker][victim][STATS_DMG] += damage
  812. player_vstats[attacker][victim][hit_place + STATS_END] ++
  813. player_astats[victim][attacker][STATS_HITS] ++
  814. player_astats[victim][attacker][STATS_DMG] += damage
  815. player_astats[victim][attacker][hit_place + STATS_END] ++
  816. player_vstats[attacker][0][STATS_HITS] ++
  817. player_vstats[attacker][0][STATS_DMG] += damage
  818. player_vstats[attacker][0][hit_place + STATS_END] ++
  819. player_astats[victim][0][STATS_HITS] ++
  820. player_astats[victim][0][STATS_DMG] += damage
  821. player_astats[victim][0][hit_place + STATS_END] ++
  822.  
  823. // оружие, с которого убил для astats, vstats
  824. new weapon_info[WEAPON_INFO_SIZE]
  825. ArrayGetArray(weapons_data,wpn_id,weapon_info)
  826.  
  827. copy(player_vstats[attacker][victim][STATS_END + HIT_END],
  828. MAX_NAME_LENGTH - 1,
  829. weapon_info[1]
  830. )
  831.  
  832. copy(player_astats[victim][attacker][STATS_END + HIT_END],
  833. MAX_NAME_LENGTH - 1,
  834. weapon_info[1]
  835. )
  836.  
  837. if(FW_Damage)
  838. ExecuteForward(FW_Damage,dummy_ret,attacker,victim,damage,wpn_id,hit_place,is_tk(attacker,victim))
  839.  
  840. return true
  841. }
  842.  
  843. //
  844. // Учет смертей
  845. //
  846. Stats_SaveKill(killer,victim,wpn_id,hit_place)
  847. {
  848. if(killer == victim) // не учитываем суицид
  849. {
  850. return false
  851. }
  852.  
  853. if(!is_tk(killer,victim))
  854. {
  855. player_wstats[killer][0][STATS_KILLS] ++
  856. player_wstats[killer][wpn_id][STATS_KILLS] ++
  857.  
  858. player_wrstats[killer][0][STATS_KILLS] ++
  859. player_wrstats[killer][wpn_id][STATS_KILLS] ++
  860.  
  861. player_vstats[killer][victim][STATS_KILLS] ++
  862. player_astats[victim][killer][STATS_KILLS] ++
  863. player_vstats[killer][0][STATS_KILLS] ++
  864. player_astats[victim][0][STATS_KILLS] ++
  865.  
  866. if(hit_place == HIT_HEAD)
  867. {
  868. player_wstats[killer][0][STATS_HS] ++
  869. player_wstats[killer][wpn_id][STATS_HS] ++
  870.  
  871. player_wrstats[killer][0][STATS_HS] ++
  872. player_wrstats[killer][wpn_id][STATS_HS] ++
  873.  
  874. player_vstats[killer][victim][STATS_HS] ++
  875. player_astats[victim][killer][STATS_HS] ++
  876. player_vstats[killer][0][STATS_HS] ++
  877. player_astats[victim][0][STATS_HS] ++
  878. }
  879. }
  880. else
  881. {
  882. player_wstats[killer][0][STATS_TK] ++
  883. player_wstats[killer][wpn_id][STATS_TK] ++
  884.  
  885. player_wrstats[killer][0][STATS_TK] ++
  886. player_wrstats[killer][wpn_id][STATS_TK] ++
  887.  
  888. player_vstats[killer][victim][STATS_TK] ++
  889. player_astats[victim][killer][STATS_TK] ++
  890. player_vstats[killer][0][STATS_TK] ++
  891. player_astats[victim][0][STATS_TK] ++
  892. }
  893.  
  894. player_wstats[victim][0][STATS_DEATHS] ++
  895. player_wrstats[victim][0][STATS_DEATHS] ++
  896.  
  897. new victim_wpn_id = get_user_weapon(victim)
  898.  
  899. if(victim_wpn_id)
  900. {
  901. player_wstats[victim][victim_wpn_id][STATS_DEATHS] ++
  902. player_wrstats[victim][victim_wpn_id][STATS_DEATHS] ++
  903. }
  904.  
  905. if(FW_Death)
  906. ExecuteForward(FW_Death,dummy_ret,killer,victim,wpn_id,hit_place,is_tk(killer,victim))
  907.  
  908.  
  909. if(player_data[killer][PLAYER_LOADSTATE] == LOAD_OK && player_data[victim][PLAYER_LOADSTATE] == LOAD_OK) // скилл расчитывается только при наличии статистики из БД
  910. {
  911. switch(get_pcvar_num(cvar[CVAR_SKILLFORMULA])) // расчет скилла
  912. {
  913. case 0: // The ELO Method (http://fastcup.net/rating.html)
  914. {
  915. new Float:delta = 1.0 / (1.0 + floatpower(10.0,(player_data[killer][PLAYER_SKILL] - player_data[victim][PLAYER_SKILL]) / 100.0))
  916. new Float:koeff = 0.0
  917.  
  918. if(player_data[killer][PLAYER_STATS][STATS_KILLS] < 100)
  919. {
  920. koeff = 2.0
  921. }
  922. else
  923. {
  924. koeff = 1.5
  925. }
  926.  
  927. player_data[killer][PLAYER_SKILL] += (koeff * delta)
  928. player_data[victim][PLAYER_SKILL] -= (koeff * delta)
  929. }
  930. }
  931. }
  932.  
  933.  
  934. // обновляем статистику в БД при смерти
  935. if(get_pcvar_num(cvar[CVAR_UPDATESTYLE]) == -2)
  936. {
  937. DB_SavePlayerData(victim)
  938. }
  939.  
  940.  
  941.  
  942. return true
  943. }
  944.  
  945. //
  946. // Учет статистики по бомба
  947. //
  948. Stats_SaveBDefusing(id)
  949. {
  950. player_wstats2[id][STATS2_DEFAT] ++
  951.  
  952. if(FW_BDefusing)
  953. ExecuteForward(FW_BDefusing,dummy_ret,id)
  954.  
  955. return true
  956. }
  957.  
  958. Stats_SaveBDefused(id)
  959. {
  960. player_wstats2[id][STATS2_DEFOK] ++
  961.  
  962. if(FW_BDefused)
  963. ExecuteForward(FW_BDefused,dummy_ret,id)
  964.  
  965. return true
  966. }
  967.  
  968. Stats_SaveBPlanted(id)
  969. {
  970. player_wstats2[id][STATS2_PLAAT] ++
  971.  
  972. if(FW_BPlanted)
  973. ExecuteForward(FW_BPlanted,dummy_ret,id)
  974.  
  975. return true
  976. }
  977.  
  978. Stats_SaveBExplode(id)
  979. {
  980. player_wstats2[id][STATS2_PLAOK] ++
  981.  
  982. if(FW_BExplode)
  983. ExecuteForward(FW_BExplode,dummy_ret,id,g_defuser)
  984.  
  985. return true
  986. }
  987.  
  988. /*
  989. * изменение ника игрока
  990. */
  991. public client_infochanged(id)
  992. {
  993. new cur_name[32],new_name[32]
  994. get_user_name(id,cur_name,charsmax(cur_name))
  995. get_user_info(id,"name",new_name,charsmax(new_name))
  996.  
  997. if(strcmp(cur_name,new_name) != 0 && get_pcvar_num(cvar[CVAR_RANK]) == 0)
  998. {
  999. DB_SavePlayerData(id,true)
  1000. }
  1001. }
  1002.  
  1003. /*
  1004. * сбрасываем astats,vstats статистику в начале раунда
  1005. */
  1006. public LogEventHooK_RoundStart()
  1007. {
  1008. // сбрасываем wrstats, vstats, astats в начале раунда
  1009. new players[32],pnum
  1010. get_players(players,pnum)
  1011.  
  1012. for(new i,player ; i < pnum ; i++)
  1013. {
  1014. player = players[i]
  1015. reset_user_wstats(player)
  1016. }
  1017.  
  1018.  
  1019. }
  1020.  
  1021. //
  1022. // сохраняем статистику в конце раунда
  1023. //
  1024. public LogEventHooK_RoundEnd()
  1025. {
  1026. if(get_pcvar_num(cvar[CVAR_UPDATESTYLE]) == -1)
  1027. {
  1028. DB_SaveAll()
  1029. }
  1030. }
  1031.  
  1032. /*
  1033. * загрузка статистики игрока из базы данных
  1034. */
  1035. DB_LoadPlayerData(id)
  1036. {
  1037. // пропускаем HLTV
  1038. if(is_user_hltv(id))
  1039. {
  1040. return false
  1041. }
  1042.  
  1043. // пропускаем ботов, если отключена запись статистики ботов
  1044. if(!get_pcvar_num(cvar[CVAR_RANKBOTS]) && is_user_bot(id))
  1045. {
  1046. return false
  1047. }
  1048.  
  1049. new name[96],steamid[30],ip[16]
  1050.  
  1051. // узнаем ник, ид, айпи игрока
  1052. //get_user_name(id,name,charsmax(name))
  1053. get_user_info(id,"name",name,charsmax(name))
  1054. get_user_authid(id,steamid,charsmax(steamid))
  1055. get_user_ip(id,ip,charsmax(ip),true)
  1056.  
  1057. mysql_escape_string(name,charsmax(name))
  1058.  
  1059. // формируем SQL запрос
  1060. new query[QUERY_LENGTH],len,sql_data[2],tbl_name[32]
  1061. get_pcvar_string(cvar[CVAR_SQL_TABLE],tbl_name,charsmax(tbl_name))
  1062.  
  1063. sql_data[0] = SQL_LOAD
  1064. sql_data[1] = id
  1065. player_data[id][PLAYER_LOADSTATE] = LOAD_WAIT
  1066.  
  1067. len += formatex(query[len],charsmax(query)-len,"SELECT *,(")
  1068. len += DB_QueryBuildScore(query[len],charsmax(query)-len)
  1069. len += formatex(query[len],charsmax(query)-len,"),(")
  1070. len += DB_QueryBuildStatsnum(query[len],charsmax(query)-len)
  1071. len += formatex(query[len],charsmax(query)-len,")")
  1072.  
  1073. switch(get_pcvar_num(cvar[CVAR_RANK]))
  1074. {
  1075. case 0: // статистика по нику
  1076. {
  1077. len += formatex(query[len],charsmax(query)-len," FROM `%s` AS `a` WHERE `name` = '%s'",
  1078. tbl_name,name
  1079. )
  1080. }
  1081. case 1: // статистика по steamid
  1082. {
  1083. len += formatex(query[len],charsmax(query)-len," FROM `%s` AS `a` WHERE `steamid` = '%s'",
  1084. tbl_name,steamid
  1085. )
  1086. }
  1087. case 2: // статистика по ip
  1088. {
  1089. len += formatex(query[len],charsmax(query)-len," FROM `%s` AS `a` WHERE `ip` = '%s'",
  1090. tbl_name,ip
  1091. )
  1092. }
  1093. default:
  1094. {
  1095. return false
  1096. }
  1097. }
  1098.  
  1099. // отправка потокового запроса
  1100. SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)
  1101.  
  1102. return true
  1103. }
  1104.  
  1105.  
  1106. /*
  1107. * сохранение статистики игрока
  1108. */
  1109. DB_SavePlayerData(id,bool:reload = false)
  1110. {
  1111. if(player_data[id][PLAYER_LOADSTATE] < LOAD_OK) // игрок не загрузился
  1112. {
  1113. return false
  1114. }
  1115.  
  1116. new name[96],steamid[30],ip[16],query[QUERY_LENGTH],i
  1117. new tbl_name[32]
  1118. get_pcvar_string(cvar[CVAR_SQL_TABLE],tbl_name,charsmax(tbl_name))
  1119.  
  1120. new sql_data[2 + // 2
  1121. sizeof player_data[][PLAYER_STATS] + // 8
  1122. sizeof player_data[][PLAYER_HITS] + // 8
  1123. sizeof player_data[][PLAYER_STATS2] // 4
  1124. ]
  1125.  
  1126. sql_data[1] = id
  1127.  
  1128. // узнаем ник, ид, айпи игрока
  1129. //get_user_name(id,name,charsmax(name))
  1130. get_user_info(id,"name",name,charsmax(name))
  1131. get_user_authid(id,steamid,charsmax(steamid))
  1132. get_user_ip(id,ip,charsmax(ip),true)
  1133.  
  1134. mysql_escape_string(name,charsmax(name))
  1135.  
  1136. new stats[8],stats2[4],hits[8]
  1137. get_user_wstats(id,0,stats,hits)
  1138. get_user_stats2(id,stats2)
  1139.  
  1140. /*if(!stats[STATS_DEATHS] && !stats[STATS_SHOTS])
  1141. {
  1142. return false
  1143. }*/
  1144.  
  1145. switch(player_data[id][PLAYER_LOADSTATE])
  1146. {
  1147. case LOAD_OK: // обновление данных
  1148. {
  1149. if(reload)
  1150. {
  1151. player_data[id][PLAYER_LOADSTATE] = LOAD_UPDATE
  1152. }
  1153.  
  1154. sql_data[0] = SQL_UPDATE
  1155.  
  1156. new diffstats[sizeof player_data[][PLAYER_STATS]]
  1157. new diffstats2[sizeof player_data[][PLAYER_STATS2]]
  1158. new diffhits[sizeof player_data[][PLAYER_HITS]]
  1159. new len,to_save
  1160.  
  1161. len += formatex(query[len],charsmax(query) - len,"UPDATE `%s` SET",tbl_name)
  1162.  
  1163. // обновляем по разнице с предедущими данными
  1164. for(i = 0 ; i < sizeof player_data[][PLAYER_STATS] ; i++)
  1165. {
  1166. diffstats[i] = stats[i] - player_data[id][PLAYER_STATSLAST][i] // узнаем разницу
  1167. player_data[id][PLAYER_STATSLAST][i] = stats[i]
  1168.  
  1169. if(diffstats[i])
  1170. {
  1171. len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + %d",
  1172. !to_save ? " " : ",",
  1173. row_names[i + ROW_KILLS],
  1174. row_names[i + ROW_KILLS],
  1175. diffstats[i]
  1176. )
  1177.  
  1178. to_save ++
  1179. }
  1180. }
  1181.  
  1182. // обновляем по разнице с предедущими данными
  1183. for(i = 0 ; i < sizeof player_data[][PLAYER_STATS2] ; i++)
  1184. {
  1185. diffstats2[i] = stats2[i] - player_data[id][PLAYER_STATS2LAST][i] // узнаем разницу
  1186. player_data[id][PLAYER_STATS2LAST][i] = stats2[i]
  1187.  
  1188. if(diffstats2[i])
  1189. {
  1190. len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + %d",
  1191. !to_save ? " " : ",",
  1192. row_names[i + ROW_BOMBDEF],
  1193. row_names[i + ROW_BOMBDEF],
  1194. diffstats2[i]
  1195. )
  1196.  
  1197. to_save ++
  1198. }
  1199. }
  1200.  
  1201.  
  1202. //
  1203. player_data[id][PLAYER_ONLINE] += get_user_time(id) - player_data[id][PLAYER_ONLINEDIFF]
  1204. player_data[id][PLAYER_ONLINEDIFF] = get_user_time(id)
  1205.  
  1206. new diffonline = player_data[id][PLAYER_ONLINE]- player_data[id][PLAYER_ONLINELAST]
  1207. player_data[id][PLAYER_ONLINELAST] = player_data[id][PLAYER_ONLINE]
  1208.  
  1209. if(diffonline)
  1210. {
  1211. len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + %d",
  1212. !to_save ? " " : ",",
  1213. row_names[ROW_ONLINETIME],
  1214. row_names[ROW_ONLINETIME],
  1215. diffonline
  1216. )
  1217.  
  1218. to_save ++
  1219. }
  1220.  
  1221. new Float:diffskill = player_data[id][PLAYER_SKILL] - player_data[id][PLAYER_SKILLLAST]
  1222. player_data[id][PLAYER_SKILLLAST] = _:player_data[id][PLAYER_SKILL]
  1223.  
  1224. if(diffskill != 0.0)
  1225. {
  1226. len += formatex(query[len],charsmax(query) - len,"%s`%s` = `%s` + %.2f",
  1227. !to_save ? " " : ",",
  1228. row_names[ROW_SKILL],
  1229. row_names[ROW_SKILL],
  1230. diffskill
  1231. )
  1232.  
  1233. to_save ++
  1234. }
  1235.  
  1236. if(stats[STATS_HITS])
  1237. {
  1238. // запрос на сохранение мест попаданий
  1239. for(i = 0; i < sizeof player_data[][PLAYER_HITS] ; i++)
  1240. {
  1241. diffhits[i] = hits[i] - player_data[id][PLAYER_HITSLAST][i] // узнаем разницу
  1242. player_data[id][PLAYER_HITSLAST][i] = hits[i]
  1243.  
  1244. if(diffhits[i])
  1245. {
  1246. len += formatex(query[len],charsmax(query) - len,",`%s` = `%s` + '%d'",
  1247. row_names[i + ROW_H0],row_names[i + ROW_H0],
  1248. diffhits[i]
  1249. )
  1250. }
  1251. }
  1252. }
  1253.  
  1254. // обновляем время последнего подключения, ник, ип и steamid
  1255. len += formatex(query[len],charsmax(query) - len,",\
  1256. `last_join` = CURRENT_TIMESTAMP,\
  1257. `%s` = '%s',\
  1258. `%s` = '%s'",
  1259.  
  1260.  
  1261. row_names[ROW_STEAMID],steamid,
  1262. row_names[ROW_IP],ip,
  1263.  
  1264. row_names[ROW_ID],player_data[id][PLAYER_ID]
  1265. )
  1266.  
  1267. if(!reload) // не обновляем ник при его смене
  1268. {
  1269. len += formatex(query[len],charsmax(query) - len,",`%s` = '%s'",
  1270. row_names[ROW_NAME],name
  1271. )
  1272. }
  1273.  
  1274. len += formatex(query[len],charsmax(query) - len,"WHERE `%s` = '%d'",row_names[ROW_ID],player_data[id][PLAYER_ID])
  1275.  
  1276. if(!to_save) // нечего сохранять
  1277. {
  1278. if(player_data[id][PLAYER_LOADSTATE] == LOAD_UPDATE)
  1279. {
  1280. player_data[id][PLAYER_LOADSTATE] = LOAD_NO
  1281. DB_LoadPlayerData(id)
  1282. }
  1283.  
  1284. return false
  1285. }
  1286.  
  1287. // stats
  1288. for(i = 0 ; i < sizeof player_data[][PLAYER_STATS] ; i++)
  1289. {
  1290. sql_data[i + 2] = diffstats[i]
  1291. }
  1292.  
  1293. // hits
  1294. for(i = 0 ; i < sizeof player_data[][PLAYER_HITS] ; i++)
  1295. {
  1296. sql_data[2 + i + sizeof player_data[][PLAYER_STATS]] = diffhits[i]
  1297. }
  1298.  
  1299. // stats2
  1300. for(i = 0 ; i < sizeof player_data[][PLAYER_STATS2] ; i++)
  1301. {
  1302. sql_data[2 + i + sizeof player_data[][PLAYER_STATS] + sizeof player_data[][PLAYER_HITS]] = diffstats[i]
  1303. }
  1304.  
  1305.  
  1306. }
  1307. case LOAD_NEW: // запрос на добавление новой записи
  1308. {
  1309. sql_data[0] = SQL_INSERT
  1310.  
  1311. new Float:skill
  1312.  
  1313. switch(get_pcvar_num(cvar[CVAR_SKILLFORMULA]))
  1314. {
  1315. case 0: skill = 100.0
  1316. }
  1317.  
  1318. formatex(query,charsmax(query),"INSERT INTO `%s` \
  1319. (`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`)\
  1320. VALUES('%s','%s','%s','%.2f','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d','%d',CURRENT_TIMESTAMP)\
  1321. ",tbl_name,
  1322.  
  1323. row_names[ROW_STEAMID],
  1324. row_names[ROW_NAME],
  1325. row_names[ROW_IP],
  1326. row_names[ROW_SKILL],
  1327. row_names[ROW_KILLS],
  1328. row_names[ROW_DEATHS],
  1329. row_names[ROW_HS],
  1330. row_names[ROW_TKS],
  1331. row_names[ROW_SHOTS],
  1332. row_names[ROW_HITS],
  1333. row_names[ROW_DMG],
  1334. row_names[ROW_BOMBDEF],
  1335. row_names[ROW_BOMBDEFUSED],
  1336. row_names[ROW_BOMBPLANTS],
  1337. row_names[ROW_BOMBEXPLOSIONS],
  1338. row_names[ROW_LASTJOIN],
  1339.  
  1340. steamid,name,ip,skill,
  1341.  
  1342. stats[STATS_KILLS] - player_data[id][PLAYER_STATSLAST][STATS_KILLS],
  1343. stats[STATS_DEATHS] - player_data[id][PLAYER_STATSLAST][STATS_DEATHS],
  1344. stats[STATS_HS] - player_data[id][PLAYER_STATSLAST][STATS_HS],
  1345. stats[STATS_TK] - player_data[id][PLAYER_STATSLAST][STATS_TK],
  1346. stats[STATS_SHOTS] - player_data[id][PLAYER_STATSLAST][STATS_SHOTS],
  1347. stats[STATS_HITS] - player_data[id][PLAYER_STATSLAST][STATS_HITS],
  1348. stats[STATS_DMG] - player_data[id][PLAYER_STATSLAST][STATS_DMG],
  1349.  
  1350. stats2[STATS2_DEFAT] - player_data[id][PLAYER_STATS2LAST][STATS2_DEFAT],
  1351. stats2[STATS2_DEFOK] - player_data[id][PLAYER_STATS2LAST][STATS2_DEFOK],
  1352. stats2[STATS2_PLAAT] - player_data[id][PLAYER_STATS2LAST][STATS2_PLAAT],
  1353. stats2[STATS2_PLAOK] - player_data[id][PLAYER_STATS2LAST][STATS2_PLAOK]
  1354. )
  1355.  
  1356. // stats
  1357. for(i = 0 ; i < sizeof player_data[][PLAYER_STATS] ; i++)
  1358. {
  1359. sql_data[i + 2] = stats[i]
  1360. }
  1361.  
  1362. // hits
  1363. for(i = 0 ; i < sizeof player_data[][PLAYER_HITS] ; i++)
  1364. {
  1365. sql_data[2 + i + sizeof player_data[][PLAYER_STATS]] = hits[i]
  1366. }
  1367.  
  1368. // stats2
  1369. for(i = 0 ; i < sizeof player_data[][PLAYER_STATS2] ; i++)
  1370. {
  1371. sql_data[2 + i + sizeof player_data[][PLAYER_STATS] + sizeof player_data[][PLAYER_HITS]] = stats2[i]
  1372. }
  1373.  
  1374. if(reload)
  1375. {
  1376. player_data[id][PLAYER_LOADSTATE] = LOAD_UPDATE
  1377. }
  1378. else
  1379. {
  1380. player_data[id][PLAYER_LOADSTATE] = LOAD_NEWWAIT
  1381. }
  1382. }
  1383. }
  1384.  
  1385. if(query[0])
  1386. {
  1387. SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)
  1388. }
  1389.  
  1390. return true
  1391. }
  1392.  
  1393. #define falos false
  1394.  
  1395. /*
  1396. * получение новых позиции в топе игроков
  1397. */
  1398. public DB_GetPlayerRanks()
  1399. {
  1400. new players[32],pnum
  1401. get_players(players,pnum)
  1402.  
  1403. new query[QUERY_LENGTH],len,tbl_name[32]
  1404. get_pcvar_string(cvar[CVAR_SQL_TABLE],tbl_name,charsmax(tbl_name))
  1405.  
  1406. // строим SQL запрос
  1407. len += formatex(query[len],charsmax(query) - len,"SELECT `id`,(")
  1408. len += DB_QueryBuildScore(query[len],charsmax(query) - len)
  1409. len += formatex(query[len],charsmax(query) - len,") FROM `%s` as `a` WHERE `id` IN(",tbl_name)
  1410.  
  1411. new bool:letsgo
  1412.  
  1413. for(new i,player,bool:y ; i < pnum ; i++)
  1414. {
  1415. player = players[i]
  1416.  
  1417. if(player_data[player][PLAYER_ID])
  1418. {
  1419. len += formatex(query[len],charsmax(query) - len,"%s'%d'",y ? "," : "",player_data[player][PLAYER_ID])
  1420. y = true
  1421. letsgo = true
  1422. }
  1423. }
  1424.  
  1425. len += formatex(query[len],charsmax(query) - len,")")
  1426.  
  1427. if(letsgo)
  1428. {
  1429. new data[1] = SQL_UPDATERANK
  1430. SQL_ThreadQuery(sql,"SQL_Handler",query,data,sizeof data)
  1431. }
  1432. }
  1433.  
  1434. /*
  1435. * сохранение статистики всех игроков
  1436. */
  1437. public DB_SaveAll()
  1438. {
  1439. new players[32],pnum
  1440. get_players(players,pnum)
  1441.  
  1442. for(new i ; i < pnum ; i++)
  1443. {
  1444. DB_SavePlayerData(players[i])
  1445. }
  1446. }
  1447.  
  1448.  
  1449. /*
  1450. * запрос на просчет ранка
  1451. */
  1452. DB_QueryBuildScore(sql_que[] = "",sql_que_len = 0,bool:only_rows = falos)
  1453. {
  1454. // стандартная формула csstats (убийства-смерти-tk)
  1455.  
  1456. if(only_rows)
  1457. {
  1458. switch(get_pcvar_num(cvar[CVAR_RANKFORMULA]))
  1459. {
  1460. case 1: return formatex(sql_que,sql_que_len,"`kills`")
  1461. case 2: return formatex(sql_que,sql_que_len,"`kills`+`hs`")
  1462. case 3: return formatex(sql_que,sql_que_len,"`skill`")
  1463. default: return formatex(sql_que,sql_que_len,"`kills`-`deaths`-`tks`")
  1464. }
  1465. }
  1466. else
  1467. {
  1468. new tbl_name[32]
  1469. get_pcvar_string(cvar[CVAR_SQL_TABLE],tbl_name,charsmax(tbl_name))
  1470.  
  1471. switch(get_pcvar_num(cvar[CVAR_RANKFORMULA]))
  1472. {
  1473. case 1: return formatex(sql_que,sql_que_len,"SELECT COUNT(*) FROM %s WHERE (kills)>=(a.kills)",tbl_name)
  1474. case 2: return formatex(sql_que,sql_que_len,"SELECT COUNT(*) FROM %s WHERE (kills+hs)>=(a.kills+a.hs)",tbl_name)
  1475. case 3: return formatex(sql_que,sql_que_len,"SELECT COUNT(*) FROM %s WHERE (skill)>=(a.skill)",tbl_name)
  1476. default: return formatex(sql_que,sql_que_len,"SELECT COUNT(*) FROM %s WHERE (kills-deaths-tks)>=(a.kills-a.deaths-a.tks)",tbl_name)
  1477. }
  1478.  
  1479.  
  1480. }
  1481.  
  1482. return 0
  1483. }
  1484.  
  1485. /*
  1486. * запрос на общее кол-во записей в БД
  1487. */
  1488. DB_QueryBuildStatsnum(sql_que[] = "",sql_que_len = 0)
  1489. {
  1490. new tbl_name[32]
  1491. get_pcvar_string(cvar[CVAR_SQL_TABLE],tbl_name,charsmax(tbl_name))
  1492.  
  1493. return formatex(sql_que,sql_que_len,"SELECT COUNT(*) FROM %s WHERE 1",tbl_name)
  1494. }
  1495.  
  1496. /*
  1497. * запрос на выборку статистики по позиции
  1498. * index - начальная позиция
  1499. * index_count - кол-во выбираемых записей
  1500. */
  1501. DB_QueryBuildGetstats(query[],query_max,len = 0,index,index_count = 2)
  1502. {
  1503. new tbl_name[32]
  1504. get_pcvar_string(cvar[CVAR_SQL_TABLE],tbl_name,charsmax(tbl_name))
  1505.  
  1506. // строим запрос
  1507. len += formatex(query[len],query_max-len,"SELECT *")
  1508.  
  1509. // запрос на ранк
  1510. len += formatex(query[len],query_max-len,",(")
  1511. len += DB_QueryBuildScore(query[len],query_max-len,true)
  1512. len += formatex(query[len],query_max-len,") as `rank`")
  1513.  
  1514. // запрашиваем следующию запись
  1515. // если есть, то возврашаем нативом index + 1
  1516. len += formatex(query[len],query_max-len," FROM `%s` as `a` ORDER BY `rank` DESC LIMIT %d,%d",
  1517. tbl_name,index,index_count
  1518. )
  1519.  
  1520. return len
  1521. }
  1522.  
  1523. /*
  1524. * чтение результата get_stats запроса
  1525. */
  1526. DB_ReadGetStats(Handle:sqlQue,name[] = "",name_len = 0,authid[] = "",authid_len = 0,stats[8] = 0,hits[8] = 0,stats2[4] = 0,&stats_count = 0,index)
  1527. {
  1528. stats_count = SQL_NumResults(sqlQue)
  1529.  
  1530. switch(get_pcvar_num(cvar[CVAR_RANK]))
  1531. {
  1532. case 0: SQL_ReadResult(sqlQue,ROW_NAME,authid,authid_len)
  1533. case 1: SQL_ReadResult(sqlQue,ROW_STEAMID,authid,authid_len)
  1534. case 2: SQL_ReadResult(sqlQue,ROW_IP,authid,authid_len)
  1535. }
  1536.  
  1537. SQL_ReadResult(sqlQue,ROW_NAME,name,name_len)
  1538.  
  1539. new i
  1540.  
  1541. for(i = ROW_KILLS ; i <= ROW_H7 ; i++)
  1542. {
  1543. switch(i)
  1544. {
  1545. case ROW_KILLS..ROW_DMG:
  1546. {
  1547. stats[i - ROW_KILLS] = SQL_ReadResult(sqlQue,i)
  1548. }
  1549. case ROW_BOMBDEF..ROW_BOMBEXPLOSIONS:
  1550. {
  1551. stats2[i - ROW_BOMBDEF] = SQL_ReadResult(sqlQue,i)
  1552. }
  1553. case ROW_H0..ROW_H7:
  1554. {
  1555. hits[i - ROW_H0] = SQL_ReadResult(sqlQue,i)
  1556. }
  1557. }
  1558.  
  1559. }
  1560.  
  1561. // кеширование данных
  1562. new stats_cache[stats_cache_struct]
  1563.  
  1564. if(!stats_cache_trie)
  1565. {
  1566. stats_cache_trie = TrieCreate()
  1567. }
  1568.  
  1569. copy(stats_cache[CACHE_NAME],charsmax(stats_cache[CACHE_NAME]),name)
  1570. copy(stats_cache[CACHE_STEAMID],charsmax(stats_cache[CACHE_STEAMID]),authid)
  1571.  
  1572. for(i = 0; i < sizeof player_data[][PLAYER_STATS] ; i++)
  1573. {
  1574. stats_cache[CACHE_STATS][i] = stats[i]
  1575. }
  1576.  
  1577. for(i = 0; i < sizeof player_data[][PLAYER_STATS2] ; i++)
  1578. {
  1579. stats_cache[CACHE_STATS2][i] = stats2[i]
  1580. }
  1581.  
  1582. stats_cache[CACHE_LAST] = SQL_NumResults(sqlQue) <= 1
  1583. SQL_ReadResult(sqlQue,ROW_SKILL,stats_cache[CACHE_SKILL])
  1584.  
  1585. new index_str[10]
  1586. num_to_str(index,index_str,charsmax(index_str))
  1587.  
  1588. TrieSetArray(stats_cache_trie,index_str,stats_cache,stats_cache_struct)
  1589. // кешироавние данных
  1590.  
  1591. SQL_NextRow(sqlQue)
  1592.  
  1593. return SQL_MoreResults(sqlQue)
  1594. }
  1595.  
  1596. /*
  1597. * обновляем кеш для get_stats
  1598. */
  1599. Cache_Stats_Update()
  1600. {
  1601. if(!stats_cache_trie)
  1602. return false
  1603.  
  1604. TrieClear(stats_cache_trie)
  1605.  
  1606. return true
  1607. }
  1608.  
  1609. /*
  1610. * обработка ответов на SQL запросы
  1611. */
  1612. public SQL_Handler(failstate,Handle:sqlQue,err[],errNum,data[],dataSize){
  1613. // есть ошибки
  1614. switch(failstate)
  1615. {
  1616. case TQUERY_CONNECT_FAILED: // ошибка соединения с mysql сервером
  1617. {
  1618. log_amx("SQL connection failed")
  1619. log_amx("[ %d ] %s",errNum,err)
  1620.  
  1621. return PLUGIN_HANDLED
  1622. }
  1623. case TQUERY_QUERY_FAILED: // ошибка SQL запроса
  1624. {
  1625. new lastQue[QUERY_LENGTH]
  1626. SQL_GetQueryString(sqlQue,lastQue,charsmax(lastQue)) // узнаем последний SQL запрос
  1627.  
  1628. log_amx("SQL query failed")
  1629. log_amx("[ %d ] %s",errNum,err)
  1630. log_amx("[ SQL ] %s",lastQue)
  1631.  
  1632. return PLUGIN_HANDLED
  1633. }
  1634. }
  1635.  
  1636. switch(data[0])
  1637. {
  1638. case SQL_LOAD: // загрзука статистики игрока
  1639. {
  1640. new id = data[1]
  1641.  
  1642. if(!is_user_connected(id))
  1643. {
  1644. return PLUGIN_HANDLED
  1645. }
  1646.  
  1647. if(SQL_NumResults(sqlQue)) // считываем статистику
  1648. {
  1649. player_data[id][PLAYER_LOADSTATE] = LOAD_OK
  1650. player_data[id][PLAYER_ID] = SQL_ReadResult(sqlQue,ROW_ID)
  1651.  
  1652. // общая статистика
  1653. player_data[id][PLAYER_STATS][STATS_KILLS] = SQL_ReadResult(sqlQue,ROW_KILLS)
  1654. player_data[id][PLAYER_STATS][STATS_DEATHS] = SQL_ReadResult(sqlQue,ROW_DEATHS)
  1655. player_data[id][PLAYER_STATS][STATS_HS] = SQL_ReadResult(sqlQue,ROW_HS)
  1656. player_data[id][PLAYER_STATS][STATS_TK] = SQL_ReadResult(sqlQue,ROW_TKS)
  1657. player_data[id][PLAYER_STATS][STATS_SHOTS] = SQL_ReadResult(sqlQue,ROW_SHOTS)
  1658. player_data[id][PLAYER_STATS][STATS_HITS] = SQL_ReadResult(sqlQue,ROW_HITS)
  1659. player_data[id][PLAYER_STATS][STATS_DMG] = SQL_ReadResult(sqlQue,ROW_DMG)
  1660.  
  1661. // статистика cstrike
  1662. player_data[id][PLAYER_STATS2][STATS2_DEFAT] = SQL_ReadResult(sqlQue,ROW_BOMBDEF)
  1663. player_data[id][PLAYER_STATS2][STATS2_DEFOK] = SQL_ReadResult(sqlQue,ROW_BOMBDEFUSED)
  1664. player_data[id][PLAYER_STATS2][STATS2_PLAAT] = SQL_ReadResult(sqlQue,ROW_BOMBPLANTS)
  1665. player_data[id][PLAYER_STATS2][STATS2_PLAOK] = SQL_ReadResult(sqlQue,ROW_BOMBEXPLOSIONS)
  1666.  
  1667. // время онлайн
  1668. player_data[id][PLAYER_ONLINE] = player_data[id][PLAYER_ONLINELAST] = SQL_ReadResult(sqlQue,ROW_ONLINETIME)
  1669.  
  1670. // скилл
  1671. SQL_ReadResult(sqlQue,ROW_SKILL,player_data[id][PLAYER_SKILL])
  1672. player_data[id][PLAYER_SKILLLAST] = _:player_data[id][PLAYER_SKILL]
  1673.  
  1674. // доп. запросы
  1675. player_data[id][PLAYER_RANK] = SQL_ReadResult(sqlQue,row_ids) // ранк игрока
  1676. statsnum = SQL_ReadResult(sqlQue,row_ids + 1) // общее кол-во игроков в БД
  1677.  
  1678. // статистика попаданий
  1679. for(new i ; i < sizeof player_data[][PLAYER_HITS] ; i++)
  1680. {
  1681. player_data[id][PLAYER_HITS][i] = SQL_ReadResult(sqlQue,ROW_H0 + i)
  1682. }
  1683.  
  1684. }
  1685. else // помечаем как нового игрока
  1686. {
  1687. player_data[id][PLAYER_LOADSTATE] = LOAD_NEW
  1688.  
  1689. DB_SavePlayerData(id) // добавляем запись в базу данных
  1690. }
  1691. }
  1692. case SQL_INSERT: // запись новых данных
  1693. {
  1694. new id = data[1]
  1695.  
  1696. if(is_user_connected(id))
  1697. {
  1698. if(player_data[id][PLAYER_LOADSTATE] == LOAD_UPDATE)
  1699. {
  1700. player_data[id][PLAYER_LOADSTATE] = LOAD_NO
  1701. DB_LoadPlayerData(id)
  1702.  
  1703. return PLUGIN_HANDLED
  1704. }
  1705.  
  1706. player_data[id][PLAYER_ID] = SQL_GetInsertId(sqlQue) // первичный ключ
  1707. player_data[id][PLAYER_LOADSTATE] = LOAD_OK // данные загружены
  1708.  
  1709. // я упрлся 0)0)0
  1710.  
  1711. // сравниваем статистику
  1712. for(new i ; i < sizeof player_data[][PLAYER_STATS] ; i++)
  1713. {
  1714. player_data[id][PLAYER_STATS][i] = data[2 + i]
  1715. }
  1716.  
  1717. // статистика по попаданиям
  1718. for(new i ; i < sizeof player_data[][PLAYER_HITS] ; i++)
  1719. {
  1720. player_data[id][PLAYER_HITS][i] = data[2 + i + sizeof player_data[][PLAYER_STATS]]
  1721. }
  1722.  
  1723. // пииздец
  1724. for(new i ; i < sizeof player_data[][PLAYER_STATS2] ; i++)
  1725. {
  1726. player_data[id][PLAYER_STATS2][i] = data[
  1727. 2 + i + sizeof player_data[][PLAYER_STATS] + sizeof player_data[][PLAYER_HITS]
  1728. ]
  1729. }
  1730.  
  1731. // дефолтное значение для скилла
  1732. switch(get_pcvar_num(cvar[CVAR_SKILLFORMULA]))
  1733. {
  1734. case 0: player_data[id][PLAYER_SKILL] = _:player_data[id][PLAYER_SKILLLAST] = _:100.0
  1735. }
  1736.  
  1737. // обновляем счетчик общего кол-ва записей
  1738. statsnum++
  1739. }
  1740.  
  1741. // обновляем позици игроков
  1742. // действие с задержкой, что-бы учесть изменения при множественном обновлении данных
  1743. if(!task_exists(task_rankupdate))
  1744. {
  1745. set_task(1.0,"DB_GetPlayerRanks",task_rankupdate)
  1746. }
  1747. }
  1748. case SQL_UPDATE: // обновление данных
  1749. {
  1750. new id = data[1]
  1751.  
  1752. if(is_user_connected(id))
  1753. {
  1754. // сравниваем статистику
  1755. for(new i ; i < sizeof player_data[][PLAYER_STATS] ; i++)
  1756. {
  1757. player_data[id][PLAYER_STATS][i] += data[2 + i]
  1758. }
  1759.  
  1760. // сравниваем статистику
  1761. for(new i ; i < sizeof player_data[][PLAYER_HITS] ; i++)
  1762. {
  1763. player_data[id][PLAYER_HITS][i] += data[2 + i + sizeof player_data[][PLAYER_STATS]]
  1764. }
  1765.  
  1766. // пииздец
  1767. for(new i ; i < sizeof player_data[][PLAYER_STATS2] ; i++)
  1768. {
  1769. player_data[id][PLAYER_STATS2][i] += data[
  1770. 2 + i + sizeof player_data[][PLAYER_STATS] + sizeof player_data[][PLAYER_HITS]
  1771. ]
  1772. }
  1773.  
  1774. if(player_data[id][PLAYER_LOADSTATE] == LOAD_UPDATE)
  1775. {
  1776. player_data[id][PLAYER_LOADSTATE] = LOAD_NO
  1777. DB_LoadPlayerData(id)
  1778. }
  1779. }
  1780.  
  1781. // обновляем позици игроков
  1782. // действие с задержкой, что-бы учесть изменения при множественном обновлении данных
  1783. if(!task_exists(task_rankupdate))
  1784. {
  1785. set_task(0.1,"DB_GetPlayerRanks",task_rankupdate)
  1786. }
  1787. }
  1788. case SQL_UPDATERANK:
  1789. {
  1790. while(SQL_MoreResults(sqlQue))
  1791. {
  1792. new pK = SQL_ReadResult(sqlQue,0)
  1793. new rank = SQL_ReadResult(sqlQue,1)
  1794.  
  1795. for(new i ; i < MAX_PLAYERS ; i++)
  1796. {
  1797. if(player_data[i][PLAYER_ID] == pK) // задаем ранк по первичному ключу
  1798. {
  1799. player_data[i][PLAYER_RANK] = rank
  1800. }
  1801. }
  1802.  
  1803. SQL_NextRow(sqlQue)
  1804. }
  1805.  
  1806. Cache_Stats_Update()
  1807. }
  1808. case SQL_GETSTATS: // потоковый get_stats
  1809. {
  1810. new id = data[1]
  1811.  
  1812. if(!is_user_connected(id))
  1813. {
  1814. return PLUGIN_HANDLED
  1815. }
  1816.  
  1817. new index = data[5]
  1818. new name[32],authid[30]
  1819.  
  1820. // кешируем ответ
  1821. while(DB_ReadGetStats(sqlQue,name,charsmax(name),authid,charsmax(authid),.index = index ++))
  1822. {
  1823. }
  1824.  
  1825. // вызываем хандлер другого плагина
  1826. if(callfunc_begin_i(data[3],data[2]))
  1827. {
  1828. callfunc_push_int(id)
  1829. callfunc_push_int(data[4])
  1830. callfunc_end()
  1831. }
  1832. }
  1833. }
  1834.  
  1835. return PLUGIN_HANDLED
  1836. }
  1837.  
  1838. /*
  1839. *
  1840. * API
  1841. *
  1842. */
  1843.  
  1844. #define CHECK_PLAYER(%1) \
  1845. if (%1 < 1 || %1 > MaxClients) { \
  1846. log_error(AMX_ERR_NATIVE, "Player out of range (%d)", %1); \
  1847. return 0; \
  1848. } else { \
  1849. if (!is_user_connected(%1) || pev_valid(%1) != 2) { \
  1850. log_error(AMX_ERR_NATIVE, "Invalid player %d", %1); \
  1851. return 0; \
  1852. } \
  1853. }
  1854.  
  1855. #define CHECK_PLAYERRANGE(%1) \
  1856. if(%1 < 0 || %1 > MaxClients) {\
  1857. log_error(AMX_ERR_NATIVE,"Player out of range (%d)",%1);\
  1858. return 0;\
  1859. }
  1860.  
  1861. #define CHECK_WEAPON(%1) \
  1862. if(%1 < 0 || %1 > ArraySize(weapons_data)){\
  1863. log_error(AMX_ERR_NATIVE,"Invalid weapon id %d",%1);\
  1864. return 0;\
  1865. }
  1866.  
  1867. public plugin_natives()
  1868. {
  1869. // default csstats
  1870. register_library("xstats")
  1871.  
  1872. register_native("get_user_wstats","native_get_user_wstats")
  1873. register_native("get_user_wrstats","native_get_user_wrstats")
  1874. register_native("get_user_stats","native_get_user_stats")
  1875. register_native("get_user_rstats","native_get_user_rstats")
  1876. register_native("get_user_vstats","native_get_user_vstats")
  1877. register_native("get_user_astats","native_get_user_astats")
  1878. register_native("reset_user_wstats","native_reset_user_wstats")
  1879. register_native("get_stats","native_get_stats")
  1880. register_native("get_statsnum","native_get_statsnum")
  1881. register_native("get_user_stats2","native_get_user_stats2")
  1882. register_native("get_stats2","native_get_stats2")
  1883.  
  1884. register_native("xmod_is_melee_wpn","native_xmod_is_melee_wpn")
  1885. register_native("xmod_get_wpnname","native_xmod_get_wpnname")
  1886. register_native("xmod_get_wpnlogname","native_xmod_get_wpnlogname")
  1887. register_native("xmod_get_maxweapons","native_xmod_get_maxweapons")
  1888. register_native("xmod_get_stats_size","native_get_statsnum")
  1889. register_native("get_map_objectives","native_get_map_objectives")
  1890.  
  1891. register_native("custom_weapon_add","native_custom_weapon_add")
  1892. register_native("custom_weapon_dmg","native_custom_weapon_dmg")
  1893. register_native("custom_weapon_shot","native_custom_weapon_shot")
  1894.  
  1895. // csstats mysql
  1896. register_native("get_statsnum_sql","native_get_statsnum")
  1897. register_native("get_user_stats_sql","native_get_user_stats")
  1898. register_native("get_stats_sql","native_get_stats")
  1899. register_native("get_stats_sql_thread","native_get_stats_thread")
  1900. register_native("get_user_skill","native_get_user_skill")
  1901. register_native("get_skill","native_get_skill")
  1902. }
  1903.  
  1904. /*
  1905. * Функция возрващает скилл игрока
  1906. *
  1907. * native get_user_skill(player,&Float:skill)
  1908. */
  1909. public native_get_user_skill(plugin_id,params)
  1910. {
  1911. new id = get_param(1)
  1912. CHECK_PLAYER(id)
  1913.  
  1914. set_float_byref(2,player_data[id][PLAYER_SKILL])
  1915.  
  1916. return true
  1917. }
  1918.  
  1919.  
  1920. /*
  1921. * Получение скилла по позиции
  1922. *
  1923. * native get_skill(index,&Float:skill)
  1924. */
  1925. public native_get_skill(plugin_id,params)
  1926. {
  1927. new index = get_param(1) // индекс в статистике
  1928.  
  1929. // кеширование
  1930. new index_str[10],stats_cache[stats_cache_struct]
  1931. num_to_str(index,index_str,charsmax(index_str))
  1932.  
  1933. // есть информация в кеше
  1934. if(stats_cache_trie && TrieGetArray(stats_cache_trie,index_str,stats_cache,stats_cache_struct))
  1935. {
  1936. set_float_byref(2,Float:stats_cache[CACHE_SKILL])
  1937. return !stats_cache[CACHE_LAST] ? index + 1 : 0
  1938. }
  1939. // кеширование
  1940.  
  1941. return 0
  1942. }
  1943.  
  1944. /*
  1945. * Добавление кастомного оружия для статистики
  1946. *
  1947. * native custom_weapon_add(const wpnname[], melee = 0, const logname[] = "")
  1948. */
  1949. public native_custom_weapon_add(plugin_id,params)
  1950. {
  1951. if(ArraySize(weapons_data) >= MAX_WEAPONS)
  1952. {
  1953. return 0
  1954. }
  1955.  
  1956. new weapon_name[MAX_NAME_LENGTH],weapon_log[MAX_NAME_LENGTH],is_melee
  1957. get_string(1,weapon_name,charsmax(weapon_name))
  1958.  
  1959. if(params >= 2) // задан флаг is_melee
  1960. is_melee = get_param(2)
  1961.  
  1962. if(params == 3) // указан лог код
  1963. {
  1964. get_string(3,weapon_log,charsmax(weapon_log))
  1965. }
  1966. else // копируем название оружия для лог кода
  1967. {
  1968. copy(weapon_log,charsmax(weapon_log),weapon_name)
  1969. }
  1970.  
  1971. // регистриурем
  1972. new weapon_info[WEAPON_INFO_SIZE]
  1973. REG_INFO(is_melee,weapon_name,weapon_info)
  1974.  
  1975. return ArraySize(weapons_data) - 1
  1976. }
  1977.  
  1978. /*
  1979. * Учет урона кастомного оружия
  1980. *
  1981. * native custom_weapon_dmg(weapon, att, vic, damage, hitplace = 0)
  1982. */
  1983. public native_custom_weapon_dmg(plugin_id,params)
  1984. {
  1985. new weapon_id = get_param(1)
  1986.  
  1987. CHECK_WEAPON(weapon_id)
  1988.  
  1989. new att = get_param(2)
  1990.  
  1991. CHECK_PLAYER(att)
  1992.  
  1993. new vic = get_param(3)
  1994.  
  1995. CHECK_PLAYER(vic)
  1996.  
  1997. new dmg = get_param(4)
  1998.  
  1999. if(dmg < 1)
  2000. {
  2001. log_error(AMX_ERR_NATIVE,"Invalid damage %d", dmg)
  2002.  
  2003. return 0
  2004. }
  2005.  
  2006. new hit_place = get_param(5)
  2007.  
  2008. return Stats_SaveHit(att,vic,dmg,weapon_id,hit_place)
  2009. }
  2010.  
  2011. /*
  2012. * Регистрация выстрела кастомного оружия
  2013. *
  2014. * native custom_weapon_shot(weapon, index)
  2015. */
  2016. public native_custom_weapon_shot(plugin_id,params)
  2017. {
  2018. new weapon_id = get_param(1)
  2019.  
  2020. CHECK_WEAPON(weapon_id)
  2021.  
  2022. new id = get_param(2)
  2023.  
  2024. CHECK_PLAYER(id)
  2025.  
  2026. return Stats_SaveShot(id,weapon_id)
  2027. }
  2028.  
  2029. /*
  2030. * Возвращает true, если оружие рукопашного боя
  2031. *
  2032. * native xmod_is_melee_wpn(wpnindex)
  2033. */
  2034. public native_xmod_is_melee_wpn(plugin_id,params)
  2035. {
  2036. new wpn_id = get_param(1)
  2037.  
  2038. CHECK_WEAPON(wpn_id)
  2039.  
  2040. new weapon_info[WEAPON_INFO_SIZE]
  2041. ArrayGetArray(weapons_data,wpn_id,weapon_info)
  2042.  
  2043. return weapon_info[0]
  2044. }
  2045.  
  2046. /*
  2047. * Получение полного названия оружия
  2048. *
  2049. * native xmod_get_wpnname(wpnindex, name[], len)
  2050. */
  2051. public native_xmod_get_wpnname(plugin_id,params)
  2052. {
  2053. new wpn_id = get_param(1)
  2054.  
  2055. CHECK_WEAPON(wpn_id)
  2056.  
  2057. new weapon_info[WEAPON_INFO_SIZE]
  2058. ArrayGetArray(weapons_data,wpn_id,weapon_info)
  2059.  
  2060. new weapon_name[MAX_NAME_LENGTH]
  2061. copy(weapon_name,charsmax(weapon_name),weapon_info[1])
  2062.  
  2063. set_string(2,weapon_name,get_param(3))
  2064.  
  2065. return strlen(weapon_name)
  2066. }
  2067.  
  2068. /*
  2069. * Получение лог кода для оружия
  2070. *
  2071. * native xmod_get_wpnlogname(wpnindex, name[], len)
  2072. */
  2073. public native_xmod_get_wpnlogname(plugin_id,params)
  2074. {
  2075. new wpn_id = get_param(1)
  2076.  
  2077. CHECK_WEAPON(wpn_id)
  2078.  
  2079. new weapon_info[WEAPON_INFO_SIZE]
  2080. ArrayGetArray(weapons_data,wpn_id,weapon_info)
  2081.  
  2082. new weapon_name[MAX_NAME_LENGTH]
  2083. copy(weapon_name,charsmax(weapon_name),weapon_info[MAX_NAME_LENGTH])
  2084.  
  2085. set_string(2,weapon_name,get_param(3))
  2086.  
  2087. return strlen(weapon_name)
  2088. }
  2089.  
  2090. /*
  2091. * Возврашение общего количества оружия для статистики
  2092. *
  2093. * native xmod_get_maxweapons()
  2094. */
  2095. public native_xmod_get_maxweapons(plugin_id,params)
  2096. {
  2097. return ArraySize(weapons_data)
  2098. }
  2099.  
  2100. public native_get_map_objectives(plugin_id,params)
  2101. {
  2102. return false
  2103. }
  2104.  
  2105. /*
  2106. * Статистика за текущую сессию
  2107. *
  2108. * native get_user_wstats(index, wpnindex, stats[8], bodyhits[8])
  2109. */
  2110. public native_get_user_wstats(plugin_id,params)
  2111. {
  2112. new id = get_param(1)
  2113.  
  2114. CHECK_PLAYER(id)
  2115.  
  2116. new wpn_id = get_param(2)
  2117.  
  2118. CHECK_WEAPON(wpn_id)
  2119.  
  2120. new stats[8],bh[8]
  2121. get_user_wstats(id,wpn_id,stats,bh)
  2122.  
  2123. set_array(3,stats,STATS_END)
  2124. set_array(4,bh,HIT_END)
  2125.  
  2126. return (stats[STATS_DEATHS] || stats[STATS_SHOTS])
  2127. }
  2128.  
  2129. /*
  2130. * Статистика за текущий раунд
  2131. *
  2132. * native get_user_wrstats(index, wpnindex, stats[8], bodyhits[8])
  2133. */
  2134. public native_get_user_wrstats(plugin_id,params)
  2135. {
  2136. new id = get_param(1)
  2137.  
  2138. CHECK_PLAYER(id)
  2139.  
  2140. new wpn_id = get_param(2)
  2141.  
  2142. CHECK_WEAPON(wpn_id)
  2143.  
  2144. if(wpn_id != 0 && !(0 < wpn_id < MAX_WEAPONS))
  2145. {
  2146. log_error(AMX_ERR_NATIVE,"Weapon index out of bounds (%d)",id)
  2147.  
  2148. return false
  2149. }
  2150.  
  2151. new stats[8],bh[8]
  2152. get_user_wrstats(id,wpn_id,stats,bh)
  2153.  
  2154. set_array(3,stats,STATS_END)
  2155. set_array(4,bh,HIT_END)
  2156.  
  2157. return (stats[STATS_DEATHS] || stats[STATS_SHOTS])
  2158. }
  2159.  
  2160.  
  2161. /*
  2162. * Получение статистики игрока
  2163. *
  2164. * native get_user_stats(index, stats[8], bodyhits[8])
  2165. */
  2166. public native_get_user_stats(plugin_id,params)
  2167. {
  2168. new id = get_param(1)
  2169.  
  2170. CHECK_PLAYER(id)
  2171.  
  2172. if(player_data[id][PLAYER_LOADSTATE] < LOAD_OK) // данные отсутствуют
  2173. {
  2174. return 0
  2175. }
  2176.  
  2177. set_array(2,player_data[id][PLAYER_STATS],8)
  2178. set_array(3,player_data[id][PLAYER_HITS],8)
  2179.  
  2180. return player_data[id][PLAYER_RANK]
  2181. }
  2182.  
  2183. /*
  2184. * Статистика за текущий раунд
  2185. *
  2186. * native get_user_rstats(index, stats[8], bodyhits[8])
  2187. */
  2188. public native_get_user_rstats(plugin_id,params)
  2189. {
  2190. new id = get_param(1)
  2191.  
  2192. CHECK_PLAYER(id)
  2193.  
  2194. new stats[8],bh[8]
  2195. get_user_rstats(id,stats,bh)
  2196.  
  2197. set_array(2,stats,STATS_END)
  2198. set_array(3,bh,HIT_END)
  2199.  
  2200. return (stats[STATS_DEATHS] || stats[STATS_SHOTS])
  2201. }
  2202. /*
  2203. * Статистика по жертвам
  2204. *
  2205. * native get_user_vstats(index, victim, stats[8], bodyhits[8], wpnname[] = "", len = 0);
  2206. */
  2207. public native_get_user_vstats(plugin_id,params)
  2208. {
  2209. if(params != 6)
  2210. {
  2211. log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 6, passed %d",params)
  2212.  
  2213. return false
  2214. }
  2215.  
  2216. new id = get_param(1)
  2217. new victim = get_param(2)
  2218.  
  2219. CHECK_PLAYER(id)
  2220. CHECK_PLAYERRANGE(victim)
  2221.  
  2222. new stats[STATS_END],hits[HIT_END],wname[MAX_NAME_LENGTH]
  2223. unpack_vstats(id,victim,stats,hits,wname,charsmax(wname))
  2224.  
  2225. set_array(3,stats,STATS_END)
  2226. set_array(4,hits,HIT_END)
  2227. set_string(5,wname,get_param(6))
  2228.  
  2229. return (stats[STATS_KILLS] || stats[STATS_HITS])
  2230. }
  2231.  
  2232.  
  2233. unpack_vstats(killer,victim,stats[STATS_END],hits[HIT_END],vname[],vname_len)
  2234. {
  2235. new i,stats_i
  2236.  
  2237. for(i = 0; i < STATS_END ; i++,stats_i++)
  2238. {
  2239. stats[i]= player_vstats[killer][victim][stats_i]
  2240. }
  2241.  
  2242. for(i = 0; i < HIT_END ; i++,stats_i++)
  2243. {
  2244. hits[i]= player_vstats[killer][victim][stats_i]
  2245. }
  2246.  
  2247. copy(vname,vname_len,player_vstats[killer][victim][stats_i])
  2248. }
  2249.  
  2250. unpack_astats(attacker,victim,stats[STATS_END],hits[HIT_END],vname[],vname_len)
  2251. {
  2252. new i,stats_i
  2253.  
  2254. for(i = 0; i < STATS_END ; i++,stats_i++)
  2255. {
  2256. stats[i]= player_astats[victim][attacker][stats_i]
  2257. }
  2258.  
  2259. for(i = 0; i < HIT_END ; i++,stats_i++)
  2260. {
  2261. hits[i]= player_astats[victim][attacker][stats_i]
  2262. }
  2263.  
  2264. copy(vname,vname_len,player_astats[victim][attacker][stats_i])
  2265. }
  2266.  
  2267. public plugin_precache()
  2268. {
  2269. new amxx_version[10]
  2270. get_amxx_verstring(amxx_version,charsmax(amxx_version))
  2271.  
  2272. if(contain(amxx_version,"1.8.1") != -1)
  2273. {
  2274. log_amx("idite nahooy")
  2275.  
  2276. server_cmd("quit")
  2277. server_exec()
  2278. }
  2279. }
  2280.  
  2281. /*
  2282. * Статистика по врагам
  2283. *
  2284. * native get_user_astats(index, victim, stats[8], bodyhits[8], wpnname[] = "", len = 0);
  2285. */
  2286. public native_get_user_astats(plugin_id,params)
  2287. {
  2288. if(params != 6)
  2289. {
  2290. log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 6, passed %d",params)
  2291.  
  2292. return false
  2293. }
  2294.  
  2295. new id = get_param(1)
  2296. new attacker = get_param(2)
  2297.  
  2298. CHECK_PLAYER(id)
  2299. CHECK_PLAYERRANGE(attacker)
  2300.  
  2301. new stats[STATS_END],hits[HIT_END],wname[MAX_NAME_LENGTH]
  2302. unpack_astats(attacker,id,stats,hits,wname,charsmax(wname))
  2303.  
  2304. set_array(3,stats,STATS_END)
  2305. set_array(4,hits,HIT_END)
  2306. set_string(5,wname,get_param(6))
  2307.  
  2308. return (stats[STATS_KILLS] || stats[STATS_HITS])
  2309. }
  2310.  
  2311. public native_reset_user_wstats()
  2312. {
  2313. new id = get_param(1)
  2314.  
  2315. CHECK_PLAYER(id)
  2316.  
  2317. return reset_user_wstats(id)
  2318. }
  2319.  
  2320. /*
  2321. * Возвращает общее количество записей в базе данных
  2322. *
  2323. * native get_statsnum()
  2324. */
  2325. public native_get_statsnum(plugin_id,params)
  2326. {
  2327. return statsnum
  2328. }
  2329.  
  2330. /*
  2331. * Получение статистик по позиции
  2332. *
  2333. * native get_stats(index, stats[8], bodyhits[8], name[], len, authid[] = "", authidlen = 0);
  2334. */
  2335. public native_get_stats(plugin_id,params)
  2336. {
  2337. if(params < 5)
  2338. {
  2339. log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 5, passed %d",params)
  2340.  
  2341. return false
  2342. }
  2343. else if(params > 5 && params != 7)
  2344. {
  2345. log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 7, passed %d",params)
  2346.  
  2347. return false
  2348. }
  2349.  
  2350. new index = get_param(1) // индекс в статистике
  2351.  
  2352. // кеширование
  2353. new index_str[10],stats_cache[stats_cache_struct]
  2354. num_to_str(index,index_str,charsmax(index_str))
  2355.  
  2356. // есть информация в кеше
  2357. if(stats_cache_trie && TrieGetArray(stats_cache_trie,index_str,stats_cache,stats_cache_struct))
  2358. {
  2359. set_array(2,stats_cache[CACHE_STATS],sizeof stats_cache[CACHE_STATS])
  2360. set_array(3,stats_cache[CACHE_HITS],sizeof stats_cache[CACHE_HITS])
  2361. set_string(4,stats_cache[CACHE_NAME],get_param(5))
  2362.  
  2363. if(params == 7)
  2364. {
  2365. set_string(6,stats_cache[CACHE_STEAMID],get_param(7))
  2366. }
  2367.  
  2368. return !stats_cache[CACHE_LAST] ? index + 1 : 0
  2369. }
  2370. // кеширование
  2371.  
  2372. /*
  2373. * прямой запрос в БД, в случае если нету данных в кеше
  2374. */
  2375.  
  2376. // открываем соединение с БД для получения актуальных данных
  2377. if(!DB_OpenConnection())
  2378. {
  2379. return false // ошибка открытия соединения
  2380. }
  2381. else
  2382. {
  2383. // задание на сброс содеинения
  2384. // чтобы не открывать новые и успеть получить сразу несколько данных за одно соединение
  2385. if(!task_exists(task_confin))
  2386. {
  2387. set_task(0.1,"DB_CloseConnection",task_confin)
  2388. }
  2389. }
  2390.  
  2391. // подготавливаем запрос в БД
  2392. new query[QUERY_LENGTH]
  2393. DB_QueryBuildGetstats(query,charsmax(query),.index = index)
  2394. new Handle:sqlQue = SQL_PrepareQuery(sql_con,query)
  2395.  
  2396. // ошибка выполнения запроса
  2397. if(!SQL_Execute(sqlQue))
  2398. {
  2399. new errNum,err[256]
  2400. errNum = SQL_QueryError(sqlQue,err,charsmax(err))
  2401.  
  2402. log_amx("MySQL query failed")
  2403. log_amx("[ %d ] %s",errNum,err)
  2404. log_amx("[ SQL ] %s",query)
  2405.  
  2406. SQL_FreeHandle(sqlQue)
  2407.  
  2408. return 0
  2409. }
  2410.  
  2411. // читаем результат
  2412. new name[32],steamid[30],stats[8],hits[8],stats2[4],stats_count
  2413.  
  2414. DB_ReadGetStats(sqlQue,
  2415. name,charsmax(name),
  2416. steamid,charsmax(steamid),
  2417. stats,
  2418. hits,
  2419. stats2,
  2420. stats_count,
  2421. index
  2422. )
  2423.  
  2424. // статистики нет
  2425. if(!stats_count)
  2426. {
  2427. return false
  2428. }
  2429.  
  2430. SQL_FreeHandle(sqlQue)
  2431.  
  2432. // возвращаем данные натива
  2433. set_array(2,stats,sizeof player_data[][PLAYER_STATS])
  2434. set_array(3,hits,sizeof player_data[][PLAYER_HITS])
  2435. set_string(4,name,get_param(5))
  2436.  
  2437. if(params == 7)
  2438. {
  2439. set_string(6,steamid,get_param(7))
  2440. }
  2441.  
  2442. return stats_count ? index + 1 : 0
  2443. }
  2444.  
  2445. /*
  2446. * Потоковый запрос на получение статистик по позиции
  2447. * id - для кого мы запрашиваем
  2448. * position - позиция
  2449. * top - кол-во
  2450. * callback[] - хандлер ответа
  2451. *
  2452. * native get_stats_sql_thread(id,position,top,callback[]);
  2453. */
  2454. public native_get_stats_thread(plugin_id,params)
  2455. {
  2456. if(params < 4)
  2457. {
  2458. log_error(AMX_ERR_NATIVE,"Bad arguments num, expected 4, passed %d",params)
  2459.  
  2460. return false
  2461. }
  2462.  
  2463. new id = get_param(1)
  2464. new position = min(statsnum,get_param(2))
  2465. new top = get_param(3)
  2466.  
  2467. new start_index = max((position - top),0)
  2468.  
  2469. new callback[20]
  2470. get_string(4,callback,charsmax(callback))
  2471.  
  2472. new func_id = get_func_id(callback,plugin_id)
  2473.  
  2474. if(func_id == -1)
  2475. {
  2476. log_error(AMX_ERR_NATIVE,"Unable to locate ^"%s^" handler.",callback)
  2477.  
  2478. return false
  2479. }
  2480.  
  2481. new query[QUERY_LENGTH]
  2482. DB_QueryBuildGetstats(query,charsmax(query),.index = start_index,.index_count = top)
  2483.  
  2484.  
  2485. new sql_data[6]
  2486.  
  2487. sql_data[0] = SQL_GETSTATS
  2488. sql_data[1] = id
  2489. sql_data[2] = plugin_id
  2490. sql_data[3] = func_id
  2491. sql_data[4] = position
  2492. sql_data[5] = start_index
  2493.  
  2494. SQL_ThreadQuery(sql,"SQL_Handler",query,sql_data,sizeof sql_data)
  2495.  
  2496. return true
  2497. }
  2498.  
  2499. /*
  2500. *
  2501. * ВСЯКАЯ ХРЕНЬ ДЛЯ САМОСТОЯТЕЛЬНОГО ПРОСЧЕТА СТАТИСТИКИ
  2502. *
  2503. */
  2504.  
  2505. is_tk(killer,victim)
  2506. {
  2507. if(killer == victim)
  2508. return true
  2509.  
  2510. return false
  2511. }
  2512.  
  2513. get_user_wstats(index, wpnindex, stats[8], bh[8])
  2514. {
  2515. for(new i ; i < STATS_END ; i++)
  2516. {
  2517. stats[i] = player_wstats[index][wpnindex][i]
  2518. }
  2519.  
  2520. #define krisa[%1] player_wstats[index][wpnindex][STATS_END + %1]
  2521.  
  2522. for(new i ; i < HIT_END ; i++)
  2523. {
  2524. bh[i] = krisa[i]
  2525. }
  2526. }
  2527.  
  2528. get_user_wrstats(index, wpnindex, stats[8], bh[8])
  2529. {
  2530. for(new i ; i < STATS_END ; i++)
  2531. {
  2532. stats[i] = player_wrstats[index][wpnindex][i]
  2533. }
  2534.  
  2535. for(new i ; i < HIT_END ; i++)
  2536. {
  2537. bh[i] = player_wrstats[index][wpnindex][STATS_END + i]
  2538. }
  2539. }
  2540.  
  2541. get_user_rstats(index, stats[8], bh[8])
  2542. {
  2543. for(new i ; i < STATS_END ; i++)
  2544. {
  2545. stats[i] = player_wrstats[index][0][i]
  2546. }
  2547.  
  2548. for(new i ; i < HIT_END ; i++)
  2549. {
  2550. bh[i] = player_wrstats[index][0][STATS_END + i]
  2551. }
  2552. }
  2553.  
  2554. get_user_stats2(index, stats[4])
  2555. {
  2556. for(new i ; i < STATS2_END ; i++)
  2557. {
  2558. stats[i] = player_wstats2[index][i]
  2559. }
  2560.  
  2561. return true
  2562. }
  2563.  
  2564. reset_user_wstats(index)
  2565. {
  2566. for(new i ; i < MAX_WEAPONS ; i++)
  2567. {
  2568. arrayset(player_wrstats[index][i],0,sizeof player_wrstats[][])
  2569. }
  2570.  
  2571. for(new i ; i < MAX_PLAYERS + 1 ;i++)
  2572. {
  2573. arrayset(player_vstats[index][i],0,sizeof player_vstats[][])
  2574. arrayset(player_vstats[i][index],0,sizeof player_vstats[][])
  2575. arrayset(player_astats[index][i],0,sizeof player_astats[][])
  2576. }
  2577.  
  2578. return true
  2579. }
  2580.  
  2581. reset_user_allstats(index)
  2582. {
  2583. for(new i ; i < MAX_WEAPONS ; i++)
  2584. {
  2585. arrayset(player_wstats[index][i],0,sizeof player_wstats[][])
  2586. }
  2587.  
  2588. arrayset(player_wstats2[index],0,sizeof player_wstats2[])
  2589.  
  2590. return true
  2591. }
  2592.  
  2593. public DB_OpenConnection()
  2594. {
  2595. if(sql_con != Empty_Handle)
  2596. {
  2597. return true
  2598. }
  2599.  
  2600. new errNum,err[256]
  2601. sql_con = SQL_Connect(sql,errNum,err,charsmax(err))
  2602.  
  2603. #if AMXX_VERSION_NUM >= 183
  2604. SQL_SetCharset(sql_con,"utf8")
  2605. #endif
  2606.  
  2607. if(errNum)
  2608. {
  2609. log_amx("MySQL query failed")
  2610. log_amx("[ %d ] %s",errNum,err)
  2611.  
  2612. return false
  2613. }
  2614.  
  2615. return true
  2616. }
  2617.  
  2618. public DB_CloseConnection()
  2619. {
  2620. if(sql_con != Empty_Handle)
  2621. {
  2622. SQL_FreeHandle(sql_con)
  2623. sql_con = Empty_Handle
  2624. }
  2625. }
  2626.  
  2627. public native_get_user_stats2(plugin_id,params)
  2628. {
  2629. new id = get_param(1)
  2630.  
  2631. if(!(0 < id <= MaxClients)) // неверно задан айди игрока
  2632. {
  2633. log_error(AMX_ERR_NATIVE,"Player index out of bounds (%d)",id)
  2634.  
  2635. return false
  2636. }
  2637.  
  2638. set_array(2,player_data[id][PLAYER_STATS2],sizeof player_data[][PLAYER_STATS2])
  2639.  
  2640. return true
  2641. }
  2642.  
  2643. public native_get_stats2(plugin_id,params)
  2644. {
  2645. return 0
  2646. }
  2647.  
  2648. /********* mysql escape functions ************/
  2649. mysql_escape_string(dest[],len)
  2650. {
  2651. //copy(dest, len, source);
  2652. replace_all(dest,len,"\\","\\\\");
  2653. replace_all(dest,len,"\0","\\0");
  2654. replace_all(dest,len,"\n","\\n");
  2655. replace_all(dest,len,"\r","\\r");
  2656. replace_all(dest,len,"\x1a","\Z");
  2657. replace_all(dest,len,"'","\'");
  2658. replace_all(dest,len,"^"","\^"");
  2659. }
  2660. /* AMXX-Studio Notes - DO NOT MODIFY BELOW HERE
  2661. *{\\ rtf1\\ ansi\\ deff0{\\ fonttbl{\\ f0\\ fnil Tahoma;}}\n\\ viewkind4\\ uc1\\ pard\\ lang1038\\ f0\\ fs16 \n\\ par }
  2662. */
  2663.