R1KO
fuck society
- Сообщения
- 9,457
- Реакции
- 7,786
- Команда форума
- #1
Много кто из начинающих скриптеров просили пример статистики.
Однажды я написал его и потерял, вот нашел. Решил выложить сюда, может кому пригодится.
P.S. Код писал давно, так что на оптимизацию не особо смотрите.
Однажды я написал его и потерял, вот нашел. Решил выложить сюда, может кому пригодится.
P.S. Код писал давно, так что на оптимизацию не особо смотрите.
PHP:
#pragma semicolon 1
#include <sourcemod>
/*
Для начала определяемся с какой базой мы будем работать.
В даном случае мы юзаем sqlite. Тоесть локальную базу.
Мы сам задаем имена базы и таблиц, поэтому нам не нужен переключатель mysql/sqlite
Так же не нужно переменных для имен таблиц.
Начнем.
*/
new Handle:g_hDatabase; // создаем хандл для базы данных
new g_iClientID[MAXPLAYERS+1]; // Массив для хранения айди игроков
new g_iClientKills[MAXPLAYERS+1]; // Массив для хранения количества убийств игроков
new g_iClientDeaths[MAXPLAYERS+1]; // Массив для хранения количества смертей игроков
public OnPluginStart()
{
RegConsoleCmd("sm_top", Top_CMD);
RegConsoleCmd("sm_mystats", MyStats_CMD);
HookEvent("player_death", Event_OnPlayerDeath);
}
public OnMapStart()
{
if(g_hDatabase != INVALID_HANDLE) CloseHandle(g_hDatabase); // Если подключение активно - закрываем его
decl String:szError[255];
g_hDatabase = SQLite_UseDatabase("stats", szError, sizeof(szError)); // подключаемся к базе данных с названием stats
if(g_hDatabase == INVALID_HANDLE) // Если не удалось подключиться вырубаем плагин
{
SetFailState("[Stats] Unable to connect to database (%s)", szError);
return;
}
SQL_LockDatabase(g_hDatabase); // Блокируем базу от других запросов
// создаем таблицу
SQL_FastQuery(g_hDatabase, "CREATE TABLE IF NOT EXISTS `stats_table` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `auth` VARCHAR(32) PRIMARY KEY, `name` TEXT, `kills` INTEGER NOT NULL default '0', `deaths` INTEGER NOT NULL default '0');");
/*
CREATE TABLE IF NOT EXISTS `stats_table` // Если таблица отсутствует - она будет создана
(`id` INTEGER PRIMARY KEY AUTOINCREMENT, // Поле `id` айди игрока
`auth` VARCHAR(32) PRIMARY KEY, // Здесь будет храниться стим игрока
`name` TEXT, // Здесь будет храниться ник игрока
`kills` INTEGER NOT NULL default '0', // Здесь будут храниться убийства игрока
`deaths` INTEGER NOT NULL default '0');"); // Здесь будут храниться смерти игрока
PRIMARY KEY - значит что в этом поле не может быть совпадений
AUTOINCREMENT - это добавляет автонумерацию
NOT NULL - поле не может быть пустым
default '0' - если значение не записано оно станет равным 0
*/
SQL_UnlockDatabase(g_hDatabase); // Разблокируем базу.
}
public OnClientPostAdminCheck(iClient) // ловим подключение игрока
{
if(iClient)
{
g_iClientID[iClient] =
g_iClientKills[iClient] =
g_iClientDeaths[iClient] = 0; // Обнуляем всё на даного игрока
if(!IsFakeClient(iClient))
{
decl String:sAuth[32], String:sQuery[256];
GetClientAuthId(iClient, AuthId_Steam2, sAuth, sizeof(sAuth)); // получаем стим игрока
FormatEx(sQuery, sizeof(sQuery), "SELECT `id`, `kills`, `deaths` FROM `stats_table` WHERE `auth` = '%s';", sAuth);
// Получаем значения полей `id`, `kills`, `deaths` из таблицы `stats_table` по даному стиму
SQL_TQuery(g_hDatabase, SQL_LoadClientCallback, sQuery, GetClientUserId(iClient));
// Выполняем запрос.
// В данных передаем не индекс игрока, а его user id (GetClientUserId(iClient))
}
}
}
public SQL_LoadClientCallback(Handle:owner, Handle:hndl, const String:error[], any:UserID) // Обрабатывем результат запроса
{
if(hndl == INVALID_HANDLE)
{
LogError("[Stats] SQL_LoadClientCallback: %s", error);
}
else
{
new iClient = GetClientOfUserId(UserID);
if(iClient) // Если игрок еще в игре
{
if(SQL_FetchRow(hndl)) // получаем строку из результата запроса.
{
g_iClientID[iClient] = SQL_FetchInt(hndl, 0); // получаем значение первого поля (id игрока) и записываем в переменную.
g_iClientKills[iClient] = SQL_FetchInt(hndl, 1);
g_iClientDeaths[iClient] = SQL_FetchInt(hndl, 2);
/*
SQL_FetchInt(hndl, 0) - получает целое число из значение поля.
Второй аргумент - номер поля из запроса.
В запросе у нас было SELECT `id`, `kills`, `deaths`. Нумеруем начиная с 0.
id - 0, kills, deaths - 2
*/
}
else
{
CreateClient(iClient); // Если получить не удалось значит игрока нет базе. Создаем его.
}
}
}
}
CreateClient(iClient)
{
decl String:sName[MAX_NAME_LENGTH*2+1], String:sAuth[32], String:sQuery[256];
GetClientName(iClient, sAuth, sizeof(sAuth));
SQL_EscapeString(g_hDatabase, sAuth, sName, sizeof(sName)); // Экранируем строку имени игрока. Это нужно на случай если в нике игрока есть символ ' им выделяются значения.
/*
Например мы в зпросе передаем:
Записать в поле `name` = '%s', где %s - это строка "Я Вася"
В запрос уйдет `name` = 'Я Вася'
А если строка "Я ра'ковский"
В запрос уйдет `name` = 'Я ра' ковский
Тоесть не всё влезет в '. Потому кастомные строки лучше экарнировать.
*/
GetClientAuthId(iClient, AuthId_Steam2, sAuth, sizeof(sAuth));
FormatEx(sQuery, sizeof(sQuery), "INSERT INTO `stats_table` (`name`, `auth`) VALUES ('%s', '%s');", sName, sAuth);
SQL_TQuery(g_hDatabase, SQL_CreateClientCallback, sQuery, GetClientUserId(iClient));
}
public SQL_CreateClientCallback(Handle:owner, Handle:hndl, const String:error[], any:UserID) // Обрабатывем результат запроса
{
if(hndl == INVALID_HANDLE)
{
LogError("[Stats] SQL_CreateClientCallback: %s", error);
}
else
{
new iClient = GetClientOfUserId(UserID);
if(iClient)
{
// Если игрок добавился в базу и он еще в игре - получаем его ид.
g_iClientID[iClient] = SQL_GetInsertId(g_hDatabase);
}
}
}
public OnClientDisconnect(iClient) // Ловим выход игрока
{
if(iClient && !IsFakeClient(iClient)) SaveClient(iClient); // Сохраняем его статистику
}
SaveClient(iClient)
{
decl String:sName[32], String:sNameDB[150], String:sQuery[256];
GetClientName(iClient, sName, sizeof(sName));
SQL_EscapeString(g_hDatabase, sName, sNameDB, sizeof(sNameDB)); // Экранируем опасные символы в нике
FormatEx(sQuery, sizeof(sQuery), "UPDATE `stats_table` SET `name` = '%s', `kills` = '%i', `deaths` = '%i' WHERE `id` = '%i';", sNameDB, g_iClientKills[iClient], g_iClientDeaths[iClient], g_iClientID[iClient]);
// Обновляем в базе ник игрока, убиства и смерти
SQL_TQuery(g_hDatabase, SQL_SaveClientCallback, sQuery);
}
public SQL_SaveClientCallback(Handle:owner, Handle:hndl, const String:error[], any:UserID)
{
if(hndl == INVALID_HANDLE)
{
LogError("[Stats] SQL_MyStatsCallback: %s", error);
}
}
public Event_OnPlayerDeath(Handle:hEvent, const String:name[], bool:dontBroadcast)
{
new iClient = GetClientOfUserId(GetEventInt(hEvent, "userid")), // Получаем умершего
iAttacker = GetClientOfUserId(GetEventInt(hEvent, "attacker")); // Получаем убийцу
++g_iClientKills[iAttacker]; // Добавляем убийство атакующему
++g_iClientDeaths[iClient]; // Добавляем смерть жертве
}
public Action:MyStats_CMD(iClient, args) // Игрок смотрит свою статистику
{
// Тут у нас есть 2 способа.
// 1 - Вывести игроку то что у нас уже сохранено
// 2 - Вывести игроку подробную информацию
//
// Я покажу оба
// 1
PrintToChat(iClient, "У тебя %i Убийств и %i Смертей. Соотношение %2.0f", g_iClientKills[iClient], g_iClientDeaths[iClient], float(g_iClientKills[iClient]/g_iClientDeaths[iClient]));
// 2
SaveClient(iClient); // Обновляем игрока в базе данных
decl String:sQuery[256];
Format(sQuery, sizeof(sQuery), "SELECT * FROM `stats_table` WHERE `id` = '%i';", g_iClientID[iClient]);
SQL_TQuery(g_hDatabase, SQL_MyStatsCallback, sQuery, GetClientUserId(iClient));
return Plugin_Handled;
}
public SQL_MyStatsCallback(Handle:owner, Handle:hndl, const String:error[], any:UserID) // Обрабатывем результат запроса
{
if(hndl == INVALID_HANDLE)
{
LogError("[Stats] SQL_MyStatsCallback: %s", error);
}
else
{
new iClient = GetClientOfUserId(UserID);
if(iClient) PrintToChat(iClient, "...."); // Выводим полученные поля.
}
}
public Action:Top_CMD(iClient, args) // Игрок хочет посмоотреть топ
{
SaveClient(iClient); // Обновляем игрока в базе данных
decl String:sQuery[256];
FormatEx(sQuery, sizeof(sQuery), "SELECT `name`, `kills`, `deaths` FROM `stats_table` ORDER BY (`kills`/`deaths`) DESC LIMIT %i OFFSET %i;", 10, 0);
SQL_TQuery(g_hDatabase, SQL_TopCallback, sQuery, GetClientUserId(iClient));
return Plugin_Handled;
}
public SQL_TopCallback(Handle:owner, Handle:hndl, const String:error[], any:UserID)
{
if(hndl == INVALID_HANDLE) LogError("[Stats] Query Fail load top: %s", error);
else
{
new iClient = GetClientOfUserId(UserID);
if(iClient)
{
decl String:sDisplay[128], String:sName[MAX_NAME_LENGTH], iCount, Handle:hPanel;
iCount = SQL_GetRowCount(hndl);
hPanel = CreatePanel();
SetPanelTitle(hPanel, "ТОП 10");
DrawPanelItem(hPanel, " ", ITEMDRAW_SPACER|ITEMDRAW_RAWLINE);
DrawPanelText(hPanel, "-----------------------------");
DrawPanelItem(hPanel, " ", ITEMDRAW_SPACER|ITEMDRAW_RAWLINE);
for (new i = 1; i <= iCount; i++)
{
if(SQL_FetchRow(hndl))
{
SQL_FetchString(hndl, 0, sName, sizeof(sName)-1);
FormatEx(sDisplay, sizeof(sDisplay), "%i. %s [%d]", i, sName, SQL_FetchInt(hndl, 1));
DrawPanelText(hPanel, sDisplay);
}
}
DrawPanelItem(hPanel, " ", ITEMDRAW_SPACER|ITEMDRAW_RAWLINE);
DrawPanelText(hPanel, "-----------------------------");
DrawPanelItem(hPanel, " ", ITEMDRAW_SPACER|ITEMDRAW_RAWLINE);
SetPanelCurrentKey(hPanel, 10);
DrawPanelItem(hPanel, "Выход");
SendPanelToClient(hPanel, iClient, PanelHandler, 30);
CloseHandle(hPanel);
}
}
}
public PanelHandler(Handle:menu, MenuAction:action, param1, param2) {}