SourcePawn [SourcePawn] Урок 6 - Таймеры

Pushistik↯❤

Команда форума
Регистрация
6 Июл 2017
Сообщения
393
Реакции
97
Баллы
28
[SourcePawn] Урок 6 - Таймеры
<= К содержанию
Таймеры нужны для выполнения действий через определенный промежуток времени.
Таймер создается функцией CreateTimer и имеет тип Handle.
PHP:
Handle CreateTimer(float interval, Timer func, any data, int flags)
  • float interval - задает время, через которое будет выполнен обратный вызов таймера.
  • Timer func - имя обратного вызова таймера.
  • any data - данные, которые будут переданы в таймер.
  • int flags - флаги таймера.
  • Подробнее о флагах:
    • TIMER_REPEAT - Если установлен, таймер будет выполнятся до тех пора пока обратный вызов не вернет Plugin_Stop или не будет уничтожен с помощью KillTimer. Проще говоря, используется для создания повторяющихся таймеров.
    • TIMER_FLAG_NO_MAPCHANGE - Если установлен, таймер будет уничтожен при смене карты.
    • TIMER_DATA_HNDL_CLOSE - Таймер автоматически будет вызывать CloseHandle() для данных переданных в него после выполнения таймера. (ранее TIMER_HNDL_CLOSE)
      PHP:
      void KillTimer(Handle timer, bool autoClose = false)
    Функция уничтожает таймер.
    • Handle timer - дескриптор таймера.
    • bool autoClose - Если установлено true, то данные, переданные в CreateTimer() будут закрыты с помощью CloseHandle(), если TIMER_DATA_HNDL_CLOSE не был указан.
      PHP:
      void TriggerTimer(Handle timer, bool reset = false)
    Функция преждевременно выполняет обратный вызов таймера.
    • Handle timer - дескриптор таймера.
    • bool reset - Если установлено true, то прошедшее время сбросится и отсчет начнется заново.

    Виды обратных вызовов:
  • Используется если в таймер не передавалось никаких данных.
    PHP:
    function Action(Handle timer)
  • Используется если в таймер были переданы данные, для которых не нужно вызывать CloseHandle(). Например int типа.
    PHP:
    function Action(Handle timer, any data)
  • Используется если в таймер были переданы данные, для которых нужно вызывать CloseHandle(). Производные от типа Handle, (в основном DataPack но могут быть и другие).
    PHP:
    function Action(Handle timer, Handle hndl)
Несколько примеров:
PHP:
/*
Выведем каждому игроку при входе приветствие с задержкой в 10 сек.
*/

public void OnClientPutInServer(int iClient)    // Игрок подключается
{
    CreateTimer(10.0, Timer_Welcome, GetClientUserId(iClient), TIMER_FLAG_NO_MAPCHANGE);
    // Через 10 секунд будет вызван каллбек Timer_Welcome
    /* В качестве данных передадим userid игрока,
        потому что 10 секунд это достаточно большой интервал,
        чтобы игрок отключился и вместо него подключился другой с таким же индексом.
    */
}

public Action Timer_Welcome(Handle hTimer, any UserId) // Каллбек нашего таймера
{
    int iClient = GetClientOfUserId(UserId);    // Получаем индекс из userid
    if(iClient)    // Если iClient != 0 значит он всё еще на сервере
    {
        // Выводим игроку сообщение
        PrintToChat(iClient, "Добро пожаловать на наш сервер, %N!", iClient);
    }

    return Plugin_Stop;
}
PHP:
/*
Будем выводить каждому игроку сообщение с интервалом в 60 сек.
И уничтожим таймер как только все игроки покинули сервер.
*/

public void OnMapStart()    // Старт карты
{
    CreateTimer(60.0, Timer_Message, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
    // Интервал 60 сек
    /*
        Никаких данных не передаем, поэтому пропускаем параметр с помощью символа [B]_[/B] либо просто пишем 0.
        Так же передаем 2 флага:
        TIMER_REPEAT - нужен для того чтобы таймер повторялся.
        TIMER_FLAG_NO_MAPCHANGE - нужен для того чтобы таймер был уничтожен при смене карты, ведь при запуске новой карты мы создадим новый.
    */
}

public Action Timer_Message(Handle hTimer) // Каллбек нашего таймера
{
    int i, iCount;
    for(i = 1; i <= MaxClients; ++i)    // Цикл по всем игрокам
    {
        if(IsClientInGame(i) && !IsFakeClient(i))    // Проверяем что игрок в игре и не бот
        {
            // Выводим игроку сообщение
            PrintToChat(iClient, "Прошла еще одна минута карты!");
            ++iCount; // Прибавляем 1 к счетчику игроков
        }
    }
 
    if(iCount == 0)    // Если нет игроков
    {
        return Plugin_Stop;    // Останавливаем таймер
    }

    return Plugin_Continue; // Позволяем таймеру выполнятся дальше
}

// В данном случае таймер вновь будет запущен при смене карты
PHP:
/*
Передадим разные данные в таймер
*/

public void OnMapStart()
{
    DataPack hPack = new DataPack();
    hPack.WriteCell(5);
    hPack.WriteFloat(7.5);
    hPack.WriteString("string");
    CreateTimer(5.0, Timer_ReadDataPack, hPack, TIMER_DATA_HNDL_CLOSE|TIMER_FLAG_NO_MAPCHANGE);
}

public Action Timer_ReadDataPack(Handle hTimer, Handle hDataPack)
{
    DataPack hPack = view_as<DataPack>(hDataPack);    // Преобразуем Handle в DataPack
    hPack.Reset();
    LogMessage("Position: %i -> %i", hPack.ReadCell());
    LogMessage("Position: %i -> %.2f", hPack.ReadFloat());
    char szBuffer[256];
    hPack.ReadString(szBuffer, sizeof(szBuffer));
    LogMessage("Position: %i -> %s", szBuffer);
    /*
        Удалять hDataPack в данном случае не нужно,
        потому что таймеру был передан флаг TIMER_DATA_HNDL_CLOSE и по завершению выполнения
        hDataPack автоматически будет удален.
    */

    return Plugin_Stop;
}
Так же есть фукнция CreateDataTimer, которая создает таймер, в качестве данных которому будет передан DataPack и автоматически устанаваливает флаг TIMER_DATA_HNDL_CLOSE.
Её исходный код:
PHP:
/**
* Creates a timer associated with a new datapack, and returns the datapack.
* @note The datapack is automatically freed when the timer ends.
* @note The position of the datapack is not reset or changed for the timer function.
*
* @param interval            Interval from the current game time to execute the given function.
* @param func                Function to execute once the given interval has elapsed.
* @param datapack            The newly created datapack is passed though this by-reference
*                            parameter to the timer callback function.
* @param flags                Timer flags.
* @return                    Handle to the timer object.  You do not need to call CloseHandle().
*/
stock Handle CreateDataTimer(float interval, Timer func, Handle &datapack, int flags=0)
{
    datapack = new DataPack();
    flags |= TIMER_DATA_HNDL_CLOSE;
    return CreateTimer(interval, func, datapack, flags);
}
Пример её использования:
PHP:
public void OnMapStart()
{
    DataPack hPack;
    CreateDataTimer(5.0, Timer_ReadDataPack, hPack, TIMER_FLAG_NO_MAPCHANGE);
    hPack.WriteCell(5);
    hPack.WriteFloat(7.5);
    hPack.WriteString("string");
}

public Action Timer_ReadDataPack(Handle hTimer, Handle hDataPack)
{
    DataPack hPack = view_as<DataPack>(hDataPack);    // Преобразуем Handle в DataPack
    hPack.Reset();
    LogMessage("Position: %i -> %i", hPack.ReadCell());
    LogMessage("Position: %i -> %.2f", hPack.ReadFloat());
    char szBuffer[256];
    hPack.ReadString(szBuffer, sizeof(szBuffer));
    LogMessage("Position: %i -> %s", szBuffer);
    /*
        Удалять hDataPack в данном случае не нужно,
        потому что таймеру был передан флаг TIMER_DATA_HNDL_CLOSE и по завершению выполнения
        hDataPack автоматически будет удален.
    */

    return Plugin_Stop;
}
Еще примерчик:
PHP:
/*
При написании игроком команды выводим ему сообщение в чат, и повторно команду он сможет написать только через 30 сек в этом же раунде.
Конечно это можно сделать куда оптимальнее и без таймеров но это просто как пример.
*/


#define DELAY    30.0    // Вынесем задержку в макрос для более удобно корректировки

Handle g_hTimer[MAXPLAYERS+1];

public void OnPluginStart()
{
    RegConsoleCmd("sm_timer", Timer_CMD);
    HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy);
}

public void Event_RoundStart(Event hEvent, const char[] sEvName, bool bDontBroadcast)    // Начался новый раунд
{
    for(int i = 1; i <= MaxClients; ++i)    // Цикл по всем игрокам
    {
        if(g_hTimer[i])    // Проверяем что таймер активен
        {
            KillTimer(g_hTimer[i]);    // Уничтожаем таймер
            g_hTimer[i] = null;        // Обнуляем значения дескриптора
        }
    }
}

public Action Timer_CMD(int iClient, int iArgs) // Игрок написал команду
{
    if(iClient)
    {
        if(g_hTimer[i])    // Если таймер активен
        {
            PrintToChat(iClient, "Команда еще недоступна!");
            return Plugin_Handled;
        }

        // Выводим игроку сообщение
        PrintToChat(iClient, "Вы ввели команду и сможете её повторно использовать через %i сек.", DELAY);
        g_hTimer[iClient] = CreateTimer(DELAY, Timer_Delay, iClient);    // Можем передать сразу индекс т.к. если игрок выйдет мы сразу уничтожим его таймер
    }

    return Plugin_Handled;
}

public void OnClientDisconnect(int iClient)    // Игрок отключился
{
    if(g_hTimer[iClient])    // Проверяем что таймер активен и уничтожаем
    {
        KillTimer(g_hTimer[iClient]);    // Уничтожаем таймер
        g_hTimer[iClient] = null;        // Обнуляем значения дескриптора
    }
}

public Action Timer_Delay(Handle hTimer, any iClient) // Каллбек нашего таймера
{
    g_hTimer[iClient] = null;        // Обнуляем значения дескриптора
    return Plugin_Stop;
}

<= К содержанию
 
Последнее редактирование:
Сверху Снизу