Проверенный
Пользователь
- Сообщения
- 1,032
- Реакции
- 828
- Помог
- 10 раз(а)
Подскажите, как правильнее всего получить /rank игрока в статистики игроков.
Как я понимаю, для начало необходимо выполнить запрос с полями (AuthID, Skill), с сортировкой по Skill
Допустим, получим мы 5к строк. Неужели далее придется выполнить цикл по всем строкам, что бы выяснить позицию игрока в /rank'e ?
Как я понимаю, для начало необходимо выполнить запрос с полями (AuthID, Skill), с сортировкой по Skill
Допустим, получим мы 5к строк. Неужели далее придется выполнить цикл по всем строкам, что бы выяснить позицию игрока в /rank'e ?
C++:
#include <amxmodx>
#include <amxmisc>
#include <reapi>
#include <sqlx>
#pragma loadlib sqlite
#pragma semicolon 1
const INACTIVITY_DAYS = 30; // Удалять неактивных игроков через 'n' дней
const SECONDS_IN_DAY = 86400; // Количество секунд в одном дне
const Float:DATA_UPDATE_TIMER = 60.0; // Обновление данных игроков каждые 'n' секунд
const QUERY_STRLEN = 512;
const PATH_STRLEN = 64;
const MESSAGE_STRLEN = 512;
const TIME_STRLEN = 32;
const ELLIPSES_ARG = 4;
const AUTHID_STRLEN = 24;
const PLAYER_NAME_STRLEN = 32;
enum _:PLAYER_DATA {
em_sAuthID[AUTHID_STRLEN],
em_sName[PLAYER_NAME_STRLEN],
em_iKills,
em_iDeaths,
em_iHead,
Float:em_fSkill,
LastSeen
}
new Handle:g_SQLTuple, g_SQLQuery[QUERY_STRLEN];
new g_sLogsDir[PATH_STRLEN];
new g_sDataDir[PATH_STRLEN];
new g_sDataBaseFile[PATH_STRLEN + 32];
new g_ePlayersData[MAX_PLAYERS + 1][PLAYER_DATA];
new g_aLastDataUpdate[MAX_PLAYERS + 1];
public plugin_init() {
register_plugin("Stats", "2.0", "Javekson");
RegisterHookChain(RG_CBasePlayer_Killed, "CBasePlayer_Killed", .post = true);
RegisterHookChain(RG_CBasePlayer_SetClientUserInfoName, "CBasePlayer_SetClientUserInfoName", .post = true);
register_clcmd("say /rank", "ClCmdRank");
register_clcmd("say /top", "ClCmdTop");
set_task_ex(DATA_UPDATE_TIMER, "TaskDataUpdate", .flags = SetTask_Repeat);
}
public plugin_cfg() {
get_localinfo("amxx_logs", g_sLogsDir, charsmax(g_sLogsDir));
add(g_sLogsDir, charsmax(g_sLogsDir), "/stats");
if(!dir_exists(g_sLogsDir)) mkdir(g_sLogsDir);
get_localinfo("amxx_datadir", g_sDataDir, charsmax(g_sDataDir));
formatex(g_sDataBaseFile, charsmax(g_sDataBaseFile), "%s/stats.db", g_sDataDir);
if(!file_exists(g_sDataBaseFile)) {
new iFileID = fopen(g_sDataBaseFile, "a");
fclose(iFileID);
}
SQL_SetAffinity("sqlite");
g_SQLTuple = SQL_MakeDbTuple("", "", "", g_sDataBaseFile);
formatex(g_SQLQuery, charsmax(g_SQLQuery),
"CREATE TABLE IF NOT EXISTS stats( \
AuthID TEXT PRIMARY KEY, \
Name TEXT NOT NULL, \
Kills INTEGER DEFAULT 0, \
Deaths INTEGER DEFAULT 0, \
Head INTEGER DEFAULT 0, \
Skill FLOAT DEFAULT '100.0', \
LastSeen INTEGER DEFAULT 0)"
);
SQL_ThreadQuery(g_SQLTuple, "TableCreateQueryHandler", g_SQLQuery);
formatex(g_SQLQuery, charsmax(g_SQLQuery), "DELETE FROM stats WHERE LastSeen < %d", get_systime() - INACTIVITY_DAYS * SECONDS_IN_DAY);
SQL_ThreadQuery(g_SQLTuple, "DataDeleteQueryHandler", g_SQLQuery);
}
public TableCreateQueryHandler(iFailState, Handle:hQuery, sError[], iErrNum, aData[], iSize, Float:fQueueTime) {
if(iFailState != TQUERY_SUCCESS) {
SQL_GetQueryString(hQuery, g_SQLQuery, charsmax(g_SQLQuery));
QueryHandlerError(g_SQLQuery, sError, iErrNum, "TableCreateQueryHandler");
pause("a");
}
}
public DataDeleteQueryHandler(iFailState, Handle:hQuery, sError[], iErrNum, aData[], iSize, Float:fQueueTime) {
if(iFailState != TQUERY_SUCCESS) {
SQL_GetQueryString(hQuery, g_SQLQuery, charsmax(g_SQLQuery));
QueryHandlerError(g_SQLQuery, sError, iErrNum, "DataDeleteQueryHandler");
}
}
public client_putinserver(id) {
if(is_user_bot(id) || is_user_hltv(id)) {
return PLUGIN_CONTINUE;
}
new sAuthID[AUTHID_STRLEN], sPlayerName[PLAYER_NAME_STRLEN];
get_user_authid(id, sAuthID, charsmax(sAuthID));
get_user_name(id, sPlayerName, charsmax(sPlayerName));
g_ePlayersData[id][em_sAuthID] = sAuthID;
g_ePlayersData[id][em_sName] = sPlayerName;
g_ePlayersData[id][em_iKills] = 0;
g_ePlayersData[id][em_iDeaths] = 0;
g_ePlayersData[id][em_iHead] = 0;
g_ePlayersData[id][em_fSkill] = 100.0;
g_ePlayersData[id][LastSeen] = 0;
g_aLastDataUpdate[id] = get_systime();
new aData[1]; aData[0] = id;
formatex(g_SQLQuery, charsmax(g_SQLQuery), "SELECT * FROM stats WHERE AuthID = '%s'", g_ePlayersData[id][em_sAuthID]);
SQL_ThreadQuery(g_SQLTuple, "PlayerCheckQueryHandler", g_SQLQuery, aData, sizeof(aData));
return PLUGIN_CONTINUE;
}
public PlayerCheckQueryHandler(iFailState, Handle:hQuery, sError[], iErrNum, aData[], iSize, Float:fQueueTime) {
if(iFailState != TQUERY_SUCCESS) {
SQL_GetQueryString(hQuery, g_SQLQuery, charsmax(g_SQLQuery));
QueryHandlerError(g_SQLQuery, sError, iErrNum, "PlayerCheckQueryHandler");
return PLUGIN_CONTINUE;
}
new id = aData[0];
if(SQL_MoreResults(hQuery)) {
g_ePlayersData[id][em_iKills] = SQL_ReadResult(hQuery, 2);
g_ePlayersData[id][em_iDeaths] = SQL_ReadResult(hQuery, 3);
g_ePlayersData[id][em_iHead] = SQL_ReadResult(hQuery, 4);
SQL_ReadResult(hQuery, 5, g_ePlayersData[id][em_fSkill]);
return PLUGIN_CONTINUE;
}
new sQuoteStringPlayerName[PLAYER_NAME_STRLEN * 2];
SQL_QuoteString(Empty_Handle, sQuoteStringPlayerName, charsmax(sQuoteStringPlayerName), g_ePlayersData[id][em_sName]);
formatex(g_SQLQuery, charsmax(g_SQLQuery), "INSERT INTO stats(AuthID, Name) VALUES ('%s', '%s')",
g_ePlayersData[id][em_sAuthID], sQuoteStringPlayerName
);
SQL_ThreadQuery(g_SQLTuple, "AddDataQueryHandler", g_SQLQuery);
return PLUGIN_CONTINUE;
}
public AddDataQueryHandler(iFailState, Handle:hQuery, sError[], iErrNum, aData[], iSize, Float:fQueueTime) {
if(iFailState != TQUERY_SUCCESS) {
SQL_GetQueryString(hQuery, g_SQLQuery, charsmax(g_SQLQuery));
QueryHandlerError(g_SQLQuery, sError, iErrNum, "AddDataQueryHandler");
return PLUGIN_CONTINUE;
}
return PLUGIN_CONTINUE;
}
public client_disconnected(id) {
if(is_user_connected(id)) {
g_ePlayersData[id][LastSeen] = get_systime();
DataUpdate(id);
}
}
public CBasePlayer_Killed(const iVictim, iKiller, iGib) {
if(iVictim == iKiller) return PLUGIN_CONTINUE;
if(!is_user_connected(iKiller)) return PLUGIN_CONTINUE;
g_ePlayersData[iVictim][em_iDeaths]++;
g_ePlayersData[iKiller][em_iKills]++;
if(get_member(iVictim, m_bHeadshotKilled)) {
g_ePlayersData[iKiller][em_iHead]++;
}
new Float:fDelta = 1.0 / (1.0 + floatpower(10.0,(g_ePlayersData[iKiller][em_fSkill] - g_ePlayersData[iVictim][em_fSkill]) / 100.0));
new Float:fKillerKoeff = (g_ePlayersData[iKiller][em_iKills] < 100) ? 2.0 : 1.5;
new Float:fVictimkKoeff = (g_ePlayersData[iVictim][em_iKills] < 100) ? 2.0 : 1.5;
g_ePlayersData[iKiller][em_fSkill] += (fKillerKoeff * fDelta);
g_ePlayersData[iVictim][em_fSkill] -= (fVictimkKoeff * fDelta);
return PLUGIN_CONTINUE;
}
public CBasePlayer_SetClientUserInfoName(const id, sInfoBuffer[], sNewName[]) {
formatex(g_ePlayersData[id][em_sName], sizeof(g_ePlayersData[][em_sName]), "%s", sNewName);
}
public TaskDataUpdate() {
new aPlayersID[MAX_PLAYERS], iPlayersNum, iSysTime = get_systime();
get_players_ex(aPlayersID, iPlayersNum, GetPlayers_ExcludeBots | GetPlayers_ExcludeHLTV);
for(new i; i < iPlayersNum; i++) {
if(iSysTime - g_aLastDataUpdate[aPlayersID[i]] >= DATA_UPDATE_TIMER) {
DataUpdate(aPlayersID[i]);
g_aLastDataUpdate[aPlayersID[i]] = iSysTime;
}
}
}
public ClCmdRank(const id) {
return PLUGIN_CONTINUE;
}
public ClCmdTop(const id) {
return PLUGIN_CONTINUE;
}
DataUpdate(id) {
new sQuoteStringPlayerName[PLAYER_NAME_STRLEN * 2];
SQL_QuoteString(Empty_Handle, sQuoteStringPlayerName, charsmax(sQuoteStringPlayerName), g_ePlayersData[id][em_sName]);
formatex(g_SQLQuery, charsmax(g_SQLQuery),
"UPDATE stats SET \
Name = '%s', \
Kills = %d, \
Deaths = %d, \
Head = %d, \
Skill = %f, \
LastSeen = %d \
WHERE AuthID = '%s'",
sQuoteStringPlayerName,
g_ePlayersData[id][em_iKills],
g_ePlayersData[id][em_iDeaths],
g_ePlayersData[id][em_iHead],
g_ePlayersData[id][em_fSkill],
g_ePlayersData[id][LastSeen],
g_ePlayersData[id][em_sAuthID]
);
SQL_ThreadQuery(g_SQLTuple, "DataUpdateQueryHandler", g_SQLQuery);
}
public DataUpdateQueryHandler(iFailState, Handle:hQuery, sError[], iErrNum, aData[], iSize, Float:fQueueTime) {
if(iFailState != TQUERY_SUCCESS) {
SQL_GetQueryString(hQuery, g_SQLQuery, charsmax(g_SQLQuery));
QueryHandlerError(g_SQLQuery, sError, iErrNum, "DataUpdateQueryHandler");
return PLUGIN_CONTINUE;
}
return PLUGIN_CONTINUE;
}
public plugin_end() {
SQL_FreeHandle(g_SQLTuple);
}
stock QueryHandlerError(sQuery[], sError[], iErrNum, sHandlerName[]) {
Logging(g_sLogsDir, "stats_error_", "^"--------------------[Error %s]--------------------^"", sHandlerName);
Logging(g_sLogsDir, "stats_error_", "^"[Query]: %s^"", sQuery);
Logging(g_sLogsDir, "stats_error_", "^"[ErrorCode]: %d^"", iErrNum);
Logging(g_sLogsDir, "stats_error_", "^"[QueryError]: %s^"", sError);
}
stock Logging(const sLogsDir[], const sFileName[], const sMessage[], any:...) {
new sFmtMsg[MESSAGE_STRLEN], sTime[TIME_STRLEN], sLogFile[PATH_STRLEN + 32], iFileID;
vformat(sFmtMsg, charsmax(sFmtMsg), sMessage, ELLIPSES_ARG);
get_time("%d.%m.%Y.log", sTime, charsmax(sTime));
formatex(sLogFile, charsmax(sLogFile), "%s/%s%s", sLogsDir, sFileName, sTime);
iFileID = fopen(sLogFile, "at");
get_time("%d.%m.%Y - %H:%M:%S", sTime, charsmax(sTime));
fprintf(iFileID, "^"%s^" %s^n", sTime, sFmtMsg);
fclose(iFileID);
}