Удаление таймера при дисконекте

oleg_nelasy

Участник
Сообщения
664
Реакции
46
Есть код.

Тут вроде как все понятно и правильно:
public Action Event_Activate(Handle hEvent, const char[] sEvName, bool bDontBroadcast)
{
    int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
   
    Создаем таймер если его нет
    if(!g_hTaimer[iClient])
        g_hTaimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient));  
}

public Action TimerConnectTeam(Handle timer, any iClient)
{
    UnTimerConnectTeam(iClient, true);
}

stock void UnTimerConnectTeam(int iClient, const bool timer = false)
{
    if(!(iClient = GetClientOfUserId(iClient)) || !g_hTaimer[iClient])
        return;
   
    if(GetClientTeam(iClient) == CS_TEAM_NONE)
        ChangeClientTeam(iClient, CS_TEAM_SPECTATOR);
   
    После отработки кода удаляю таймер
    if(timer) g_hTaimer[iClient] = null;
    else if(g_hTaimer[iClient]) delete g_hTaimer[iClient];
}

При отключении игрока

C-подобный:
public void OnClientDisconnect(int iClient)
{
    Насколько я понял тут должна быть проверка если таймер есть
    И выглядеть она должна так if(g_hTaimer[iClient])?
   
    а в текушем варианте получается если таймера нет?
    if(!g_hTaimer[iClient])
    {
        Тут удаление таймера?
        g_hTaimer[iClient] = null;
        delete g_hTaimer[iClient];
    }
}
Сообщения автоматически склеены:

Или можно так?

if(g_hTaimer[iClient])
delete g_hTaimer[iClient];
 

oleg_nelasy

Участник
Сообщения
664
Реакции
46
C-подобный:
void DeleteTimer(int iClient){
    if(g_hTimer[iClient]){
        KillTimer(g_hTimer[iClient]);
        g_hTimer[iClient] = null;
    }
}
То-есть можно так?

C-подобный:
public Action Event_Activate(Handle hEvent, const char[] sEvName, bool bDontBroadcast)
{
    int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
  
    Создаем таймер если его нет
    if(!g_hTaimer[iClient])
        g_hTaimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient)); 
}

public Action TimerConnectTeam(Handle timer, any iClient)
{
    UnTimerConnectTeam(iClient, true);
}

stock void UnTimerConnectTeam(int iClient, const bool timer = false)
{
    if(!(iClient = GetClientOfUserId(iClient)) || !g_hTaimer[iClient])
        return;
  
    if(GetClientTeam(iClient) == CS_TEAM_NONE)
        ChangeClientTeam(iClient, CS_TEAM_SPECTATOR);
  
    После отработки кода удаляю таймер
    DeleteTimer(iClient);
}



public void OnClientDisconnect(int iClient)
{
    DeleteTimer(iClient);
}

void DeleteTimer(int iClient){
    if(g_hTimer[iClient]){
        KillTimer(g_hTimer[iClient]);
        g_hTimer[iClient] = null;
    }
}
 

Rolzzandik

Участник
Сообщения
492
Реакции
234
То-есть можно так?
да
только с названиями не попутай, а то у тебя
g_hTaimer
у меня g_hTimer

и еще
C-подобный:
if(!g_hTaimer[iClient])
        g_hTaimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient));
лучше превратить в такое
C-подобный:
if(g_hTaimer[iClient] == null)
        g_hTaimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient));
else {
DeleteTimer(iClient);
 g_hTaimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient));
}
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,521
Реакции
4,980
То-есть можно так?
Лучше вот так:
C-подобный:
public void Event_Activate(Event event, const char[] sEvName, bool bDontBroadcast)
{
    int client = GetClientOfUserId(event.GetInt("userid"));
    if(!g_hTimer[client]) g_hTimer[client] = CreateTimer(g_fTimer, Timer_ConnectTeam, GetClientUserId(client));
}

public Action Timer_ConnectTeam(Handle timer, any client)
{
    if((client = GetClientOfUserId(client)))    // если игрок всё ещё на сервере, возможно эта проверка не нужна
    {
        if(GetClientTeam(client) == CS_TEAM_NONE) ChangeClientTeam(client, CS_TEAM_SPECTATOR);
        g_hTimer[client] = null;    // обнуляем хэндл таймера
    }
    return Plugin_Stop;
}

public void OnClientDisconnect(int client)
{
    if(g_hTimer[client]) delete g_hTimer[client];    // если таймер существует, то его удаляем
}

лучше превратить в такое
а на кой пересоздавать таймер, если он уже запущен?

C-подобный:
    if(g_hTimer[iClient]){
        KillTimer(g_hTimer[iClient]);
        g_hTimer[iClient] = null;
    }
этот код равнозначен вот этой строке: if(g_hTimer[iClient]) delete g_hTimer[iClient];
Сообщения автоматически склеены:

лучше превратить в такое
C-подобный:
if(g_hTaimer[iClient] == null)
        g_hTaimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient));
else {
DeleteTimer(iClient);
 g_hTaimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient));
}
и к этому коду претензия имеется: рациональнее не дублировать лишний раз код и лучше упростить до
C-подобный:
if(g_hTimer[iClient]) delete g_hTimer[iClient];
g_hTimer[iClient] = CreateTimer(g_fTimer, TimerConnectTeam, GetClientUserId(iClient));
 
Последнее редактирование:

Rolzzandik

Участник
Сообщения
492
Реакции
234
а на кой пересоздавать таймер, если он уже запущен?
я чет подумал что у него, что ему надо единоразового выполнить что то по таймеру при заходе.
Хз, шиза
этот код равнозначен вот этой строке: if(g_hTimer[iClient]) delete g_hTimer[iClient];
у меня это почему то в свое время вызывало invalid handle...
и к этому коду претензия имеется: рациональнее не дублировать лишний раз код и лучше упростить до
Я лох
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,521
Реакции
4,980
у меня это почему то в свое время вызывало invalid handle...
delete делает всё то же самое: убивает таймер и обнуляет его хэндл
Там у тебя скорее всего в другом косяк был (возможно забывал обнулять хэндл таймера перед return Plugin_Stop).
 

oleg_nelasy

Участник
Сообщения
664
Реакции
46
Лучше вот так:
C-подобный:
public void Event_Activate(Event event, const char[] sEvName, bool bDontBroadcast)
{
    int client = GetClientOfUserId(event.GetInt("userid"));
    if(!g_hTimer[client]) g_hTimer[client] = CreateTimer(g_fTimer, Timer_ConnectTeam, GetClientUserId(client));
}

public Action Timer_ConnectTeam(Handle timer, any client)
{
    if((client = GetClientOfUserId(client)))    // если игрок всё ещё на сервере, возможно эта проверка не нужна
    {
        if(GetClientTeam(client) == CS_TEAM_NONE) ChangeClientTeam(client, CS_TEAM_SPECTATOR);
        g_hTimer[client] = null;    // обнуляем хэндл таймера
    }
    return Plugin_Stop;
}

public void OnClientDisconnect(int client)
{
    if(g_hTimer[client]) delete g_hTimer[client];    // если таймер существует, то его удаляем
}
Некогда не использовал return Plugin_Stop; Это остановить весть плагин или только этот код?
 

oleg_nelasy

Участник
Сообщения
664
Реакции
46
Если правильно понял return Plugin_Stop; Просто убьет таймер?
 

Сергей68

Неуместный юмор
Сообщения
420
Реакции
377
Если правильно понял return Plugin_Stop; Просто убьет таймер?
Все верно, перед этим нужно еще обнулить хэндл таймера как сказал @Grey83 двумя постами выше. (если он не одноразовый на карту допустим, если один раз используется, то в таком случае достаточно будет аргумента TIMER_FLAG_NO_MAPCHANGE)
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,521
Реакции
4,980
@oleg_nelasy, это останавливает таймер (обычно нужно только для повторяющегося или если требуется остановить выполнение посреди кода таймера).
 

oleg_nelasy

Участник
Сообщения
664
Реакции
46
@oleg_nelasy, это останавливает таймер (обычно нужно только для повторяющегося или если требуется остановить выполнение посреди кода таймера).
Спасибо понял

Сейчас такая проблема
Если делаю так:
public void OnClientDisconnect(int iClient)
{
    if(g_hTaimer[iClient]) delete g_hTaimer[iClient];    // если таймер существует, то его удаляем
}
public Action Event_Activate(Handle hEvent, const char[] sEvName, bool bDontBroadcast)
{
    int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
    if(!g_hTaimer[iClient]) g_hTaimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient));
}

public Action TimerConnectTeam(Handle timer, any iClient)
{
    if(GetClientTeam(iClient) == CS_TEAM_NONE) ChangeClientTeam(iClient, CS_TEAM_SPECTATOR);
    g_hTaimer[iClient] = null;    // обнуляем хэндл таймера
    return Plugin_Stop;
}

У клиента не правильный индекс. При получении GetClientTeam(iClient)


А если так:
public void OnClientDisconnect(int iClient)
{
    if(g_hTaimer[iClient])
    {
        g_hTaimer[iClient] = null;
        delete g_hTaimer[iClient];
    }
}

public Action Event_Activate(Handle hEvent, const char[] sEvName, bool bDontBroadcast)
{
    int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
    if(!g_hTaimer[iClient]) g_hTaimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient));
}

public Action TimerConnectTeam(Handle timer, any iClient)
{
    UnTimerConnectTeam(iClient, true);
}

stock void UnTimerConnectTeam(int iClient, const bool timer = false)
{
    if(!(iClient = GetClientOfUserId(iClient)) || !g_hTaimer[iClient])
        return;
   
    if(GetClientTeam(iClient) == CS_TEAM_NONE)
        ChangeClientTeam(iClient, CS_TEAM_SPECTATOR);
   
    if(g_hTaimer[iClient])
    {
        g_hTaimer[iClient] = null;
        delete g_hTaimer[iClient];
    }
}
    Тогда все отрабатывает правильно
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,521
Реакции
4,980
@Сергей68, если таймер с флагом TIMER_FLAG_NO_MAPCHANGE и при его создании хэндл таймера сохраняется в глобальную переменную, то в событии OnMapEnd() нужно обнулять хэндл в переменной и в конце выполнения его кода.
Если плагин не имеет этот флаг, то в конце его кода и перед прерыванием кода (кроме return Plugin_Continue для таймера с флагом TIMER_REPEAT) нужно обнулять сохранённый хэндл таймера только в коде самого таймера.
При внешнем управлении (убийстве таймера) достаточно просто выполнить delete hTimer.

Как-то так.
Сообщения автоматически склеены:

У клиента не правильный индекс. При получении GetClientTeam(iClient)
Это потому что ты вместо ClientID используешь UserID каллбэке таймера.
В моём коде сначала производится конвертация и проверка коректности, а потом уже выполняется код.
Сообщения автоматически склеены:

@oleg_nelasy, и ещё: ну переименуй ты переменную g_hTaimer
Ты английский специально коверкаешь?
 
Последнее редактирование:

Сергей68

Неуместный юмор
Сообщения
420
Реакции
377
если таймер с флагом TIMER_FLAG_NO_MAPCHANGE и при его создании хэндл таймера сохраняется в глобальную переменную, то в событии OnMapEnd() нужно обнулять хэндл в переменной и в конце выполнения его кода.
Ну про это я вроде и сказал, TIMER_FLAG_NO_MAPCHANGE ведь не используют если приравнивают его хэндл в переменную. А значит и обнулять его не надо, я создаю постоянный таймер односекундный с флагом TIMER_FLAG_NO_MAPCHANGE который работает на протяжении всей карты не обнуляясь и не удаляясь.
создав таймер, который еще не выполнял обратный вызов при смене карты, или создав повторяющийся таймер, не останавливая его, этот флаг гарантирует, что таймер больше не будет работать после изменения карты и будет остановлен

При внешнем управлении (убийстве таймера) достаточно просто выполнить delete hTimer.
Я думаю тут лучше уточнить что это надо делать вне таймера.
 

oleg_nelasy

Участник
Сообщения
664
Реакции
46
Я думаю тут лучше уточнить что это надо делать вне таймера.
Если я вас правильно понял. Таймер нужно вызвать для игрока при подключении после того как таймер отработает убит его. Если игрок вышел раньше чем отработал таймер убить.
Лучше вот так:
C-подобный:
public void Event_Activate(Event event, const char[] sEvName, bool bDontBroadcast)
{
    int client = GetClientOfUserId(event.GetInt("userid"));
    if(!g_hTimer[client]) g_hTimer[client] = CreateTimer(g_fTimer, Timer_ConnectTeam, GetClientUserId(client));
}

public Action Timer_ConnectTeam(Handle timer, any client)
{
    if((client = GetClientOfUserId(client)))    // если игрок всё ещё на сервере, возможно эта проверка не нужна
    {
        if(GetClientTeam(client) == CS_TEAM_NONE) ChangeClientTeam(client, CS_TEAM_SPECTATOR);
        g_hTimer[client] = null;    // обнуляем хэндл таймера
    }
    return Plugin_Stop;
}

public void OnClientDisconnect(int client)
{
    if(g_hTimer[client]) delete g_hTimer[client];    // если таймер существует, то его удаляем
}

Еще раз проверил не вижу разницы кроме проверки if((client = GetClientOfUserId(client))) Так как если таймер отработал игрок на сервере, а если он вышел то отработал дисконект где убил этот таймер. Индекс все еще почему-то не правильный.


C-подобный:
public void Event_Activate(Event hEvent, const char[] sEvName, bool bDontBroadcast)
{
    int iClient = GetClientOfUserId(hEvent.GetInt("userid"));
    if(!g_hTimer[iClient]) g_hTimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient));
}

public Action TimerConnectTeam(Handle timer, any iClient)
{
    if(GetClientTeam(iClient) == CS_TEAM_NONE) ChangeClientTeam(iClient, CS_TEAM_SPECTATOR);
    g_hTimer[iClient] = null;    // обнуляем хэндл таймера
    return Plugin_Stop;
}
 

Сергей68

Неуместный юмор
Сообщения
420
Реакции
377
@oleg_nelasy,
Не работает наверно потому-что:
Это потому что ты вместо ClientID используешь UserID каллбэке таймера.
В моём коде сначала производится конвертация и проверка коректности, а потом уже выполняется код.

GetClientUserId получает userid из индекса, а GetClientOfUserId получает индекс игрока из userid.

В твоем случае тебе нужно получить индекс из userid, в таймере используй GetClientOfUserId

any iClient
в твоем случае это userid, а ты его используешь как индекс.
 

Grey83

не пишу плагины с весны 2022
Сообщения
8,521
Реакции
4,980
Еще раз проверил не вижу разницы кроме проверки if((client = GetClientOfUserId(client)))
Это как раз конвертация UserID в ClientID и проверка:
client = GetClientOfUserId(client) преобразует UserID, получаемый в параметрах каллбэка таймера, в ClientID и сохраняет в ту же переменную, а проверка if((%некое_заначение%)) сравнивает полученное при преобразовании значение с нулём, после чего выполняется заключённый в фигурные скобки блок кода, если проверка вернула true.

Если заглянуть в API, то можем увидеть, что GetClientOfUserId() возвращает 0, когда UserID невалидный (а он становится невалидным когда игрок с этим UserID отключается от сервера).
 

Сергей68

Неуместный юмор
Сообщения
420
Реакции
377
Я бы наверно еще изменил этот момент:
C-подобный:
if(!g_hTaimer[iClient]) g_hTaimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient));
на это
C-подобный:
if(g_hTaimer[iClient]) delete g_hTaimer[iClient];
g_hTaimer[iClient] = CreateTimer(g_fTaimer, TimerConnectTeam, GetClientUserId(iClient));

Но только если ты хочешь гарантировано получить создание таймера при вызове функции, в том случае если таймер уже создался каким-то образом.
Я просто не понимаю что за Event_Activate
 

oleg_nelasy

Участник
Сообщения
664
Реакции
46
Это как раз конвертация UserID в ClientID и проверка:
client = GetClientOfUserId(client) преобразует UserID, получаемый в параметрах каллбэка таймера, в ClientID и сохраняет в ту же переменную, а проверка if((%некое_заначение%)) сравнивает полученное при преобразовании значение с нулём, после чего выполняется заключённый в фигурные скобки блок кода, если проверка вернула true.

Если заглянуть в API, то можем увидеть, что GetClientOfUserId() возвращает 0, когда UserID невалидный (а он становится невалидным когда игрок с этим UserID отключается от сервера).
Все понял осознал простите идиота.
Я просто не понимаю что за Event_Activate
Это насколько я понял подключение игрока к серверу. Как мне объяснили когда игрок полностью подключен к серверу. Игрок при подключении активирует таймер. Таймер убивается если он отработал. Или если не отработал то при дисконекте убивается. Вызывается только один раз поэтому при дисконекте в любом случаи таймер убьет.

Еще вот такой вопрос. Это подключение игрока. А можно отловить событие когда игрок подключился и ему открыло меню выбора команды с таймером от mp_force_pick_time. А то получается если игрок калькулятор то он еще догружается а таймер уже идет?

То есть не когда игрок сам вызвал смену команды. А когда ему предложили вобрать команду после подключения к серверу.
 
Сверху Снизу