Простая статистика

Тема в разделе "Программирование / Скриптинг", создана пользователем R1KO, 24 авг 2015.

  1. R1KO

    R1KO Супер-модератор

    Сообщения:
    5.989
    Симпатии:
    2.987
    Много кто из начинающих скриптеров просили пример статистики.
    Однажды я написал его и потерял, вот нашел. Решил выложить сюда, может кому пригодится.

    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_HANDLECloseHandle(g_hDatabase); // Если подключение активно - закрываем его
        
        
    decl String:szError[255];
        
    g_hDatabase SQLite_UseDatabase("stats"szErrorsizeof(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(iClientAuthId_Steam2sAuthsizeof(sAuth)); // получаем стим игрока
                
    FormatEx(sQuerysizeof(sQuery), "SELECT `id`, `kills`, `deaths` FROM `stats_table` WHERE `auth` = '%s';"sAuth);
                
    // Получаем значения полей `id`, `kills`, `deaths` из таблицы `stats_table` по даному стиму
                
    SQL_TQuery(g_hDatabaseSQL_LoadClientCallbacksQueryGetClientUserId(iClient));
                
    // Выполняем запрос.
                // В данных передаем не индекс игрока, а его user id (GetClientUserId(iClient))
            
    }
        }
    }

    public 
    SQL_LoadClientCallback(Handle:ownerHandle: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(hndl0); // получаем значение первого поля (id игрока) и записываем в переменную.
                    
    g_iClientKills[iClient] = SQL_FetchInt(hndl1);
                    
    g_iClientDeaths[iClient] = SQL_FetchInt(hndl2);
                    
    /*
                    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(iClientsAuthsizeof(sAuth));
        
    SQL_EscapeString(g_hDatabasesAuthsNamesizeof(sName)); // Экранируем строку имени игрока. Это нужно на случай если в нике игрока есть символ '   им выделяются значения.
        /*
        Например мы в зпросе передаем:
        Записать в поле `name` = '%s', где %s - это строка "Я Вася"
        В запрос уйдет `name` = 'Я Вася'
        А если строка "Я ра'ковский"
        В запрос уйдет `name` = 'Я ра' ковский
        Тоесть не всё влезет в '. Потому кастомные строки лучше экарнировать.
        */
        
    GetClientAuthId(iClientAuthId_Steam2sAuthsizeof(sAuth));
        
    FormatEx(sQuerysizeof(sQuery), "INSERT INTO `stats_table` (`name`, `auth`) VALUES ('%s', '%s');"sNamesAuth);
        
    SQL_TQuery(g_hDatabaseSQL_CreateClientCallbacksQueryGetClientUserId(iClient));
    }

    public 
    SQL_CreateClientCallback(Handle:ownerHandle: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(iClientsNamesizeof(sName));
        
    SQL_EscapeString(g_hDatabasesNamesNameDBsizeof(sNameDB)); // Экранируем опасные символы в нике
        
    FormatEx(sQuerysizeof(sQuery), "UPDATE `stats_table` SET `name` = '%s', `kills` = '%i', `deaths` = '%i' WHERE `id` = '%i';"sNameDBg_iClientKills[iClient], g_iClientDeaths[iClient], g_iClientID[iClient]);
        
    // Обновляем в базе ник игрока, убиства и смерти
        
    SQL_TQuery(g_hDatabaseSQL_SaveClientCallbacksQuery);
    }

    public 
    SQL_SaveClientCallback(Handle:ownerHandle: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(iClientargs// Игрок смотрит свою статистику
    {
        
    // Тут у нас есть 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(sQuerysizeof(sQuery), "SELECT * FROM `stats_table` WHERE `id` = '%i';"g_iClientID[iClient]);
        
    SQL_TQuery(g_hDatabaseSQL_MyStatsCallbacksQueryGetClientUserId(iClient));

        return 
    Plugin_Handled;
    }

    public 
    SQL_MyStatsCallback(Handle:ownerHandle:hndl, const String:error[], any:UserID// Обрабатывем результат запроса
    {
        if(
    hndl == INVALID_HANDLE)
        {
            
    LogError("[Stats] SQL_MyStatsCallback: %s"error);
        }
        else
        {
            new 
    iClient GetClientOfUserId(UserID);
            if(
    iClientPrintToChat(iClient"...."); // Выводим полученные поля.
        
    }
    }

    public 
    Action:Top_CMD(iClientargs// Игрок хочет посмоотреть топ
    {
        
    SaveClient(iClient); // Обновляем игрока в базе данных
        
    decl String:sQuery[256];
        
    FormatEx(sQuerysizeof(sQuery), "SELECT `name`, `kills`, `deaths` FROM `stats_table` ORDER BY (`kills`/`deaths`) DESC LIMIT %i OFFSET %i;"100);
        
    SQL_TQuery(g_hDatabaseSQL_TopCallbacksQueryGetClientUserId(iClient));

        return 
    Plugin_Handled;
    }

    public 
    SQL_TopCallback(Handle:ownerHandle:hndl, const String:error[], any:UserID)
    {
        if(
    hndl == INVALID_HANDLELogError("[Stats] Query Fail load top: %s"error);
        else
        {
            new 
    iClient GetClientOfUserId(UserID);
            if(
    iClient)
            {
                
    decl String:sDisplay[128], String:sName[MAX_NAME_LENGTH], iCountHandle: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 
    1<= iCounti++)
                {
                    if(
    SQL_FetchRow(hndl))
                    {
                        
    SQL_FetchString(hndl0sNamesizeof(sName)-1);
                        
    FormatEx(sDisplaysizeof(sDisplay), "%i. %s [%d]"isNameSQL_FetchInt(hndl1));
                        
    DrawPanelText(hPanelsDisplay);
                    }
                }

                
    DrawPanelItem(hPanel" "ITEMDRAW_SPACER|ITEMDRAW_RAWLINE);
                
    DrawPanelText(hPanel"-----------------------------");
                
    DrawPanelItem(hPanel" "ITEMDRAW_SPACER|ITEMDRAW_RAWLINE);

                
    SetPanelCurrentKey(hPanel10);
                
    DrawPanelItem(hPanel"Выход");

                
    SendPanelToClient(hPaneliClientPanelHandler30);
                
    CloseHandle(hPanel);
            }
        }
    }

    public 
    PanelHandler(Handle:menuMenuAction:actionparam1param2) {}
     

    Вложения:

    • stats.sp
      Размер файла:
      11,3 КБ
      Просмотров:
      23
  2. SourceGod

    SourceGod

    Сообщения:
    47
    Симпатии:
    0
    R1KO, а что хранит в себе данные в плагинe warmod.? Допустим игрок заходит на сервер и пишет что не хватает столько из стольких игроков?
     
  3. R1KO

    R1KO Супер-модератор

    Сообщения:
    5.989
    Симпатии:
    2.987
    SourceGod, не совсем понял вопроса
     
  4. SourceGod

    SourceGod

    Сообщения:
    47
    Симпатии:
    0
    R1KO, есть плагин warmod он для проведения матчей.. Плагин везде сырой. Хочу новый написать на новом синтаксисе. Только не могу понять что именно он должен хронить в себе и как!?
     
  5. R1KO

    R1KO Супер-модератор

    Сообщения:
    5.989
    Симпатии:
    2.987
    SourceGod, ну по идее там всё хранится в переменных. Конечно если плагин не имеет статистики. Если имеет то рациональнее всего sql/mysql. Ну и всё зависит от того что нужно хранить. Если хочешь писать свой - начинай с основы, так сказать со "скелета", это система готовности, загрузка конфиги и команды управления матчем. А потом уже наращивай функционал.
     
  6. SourceGod

    SourceGod

    Сообщения:
    47
    Симпатии:
    0
    R1KO, допустим собралось 10 людей. И матч начинается счет 0:0 как сделать проверку на то что какая то из команд выиграла
     
  7. R1KO

    R1KO Супер-модератор

    Сообщения:
    5.989
    Симпатии:
    2.987
    SourceGod, в эвенте конца раунда есть параметр winner, можно по нему считать. Можно получать счет команда через GetTeamScore и сравнивать
     
  8. SourceGod

    SourceGod

    Сообщения:
    47
    Симпатии:
    0
    R1KO, спасибо.. Учту

    Добавлено через 2 минуты
    R1KO, кстати куда пропала кнопка спасибо?
     
    Последнее редактирование: 25 авг 2015