Portal SAMP
[Ajuda] OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk - Versão de Impressão

+- Portal SAMP (https://portalsamp.com)
+-- Fórum: SA-MP (https://portalsamp.com/forumdisplay.php?fid=5)
+--- Fórum: Área de suporte (https://portalsamp.com/forumdisplay.php?fid=6)
+--- Tópico: [Ajuda] OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk (/showthread.php?tid=3477)

Páginas: 1 2


OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk - hiwyn - 27/03/2023

Com esse set no OnPlayerUpdate
Código:
public OnPlayerUpdate(playerid)
{
    AFKTime[playerid] = gettime();
}

E depois um check simples numa outra função que roda a cada 1 segundo
Código:
// ongamemodeinit
SetTimerEx("PlayerOneSec", 1000, true, "i", playerid);


Código:
public PlayerOneSec(i) {
        // check paused
        if(paused[i] == false) {
            // 3 segundos sem resposta.
            if(GetTickCount() - AFKTime[i] > 3) { OnPlayerPause(i); }
        }
        if(paused[i] == true) {
            if(GetTickCount() - AFKTime[i] < 3) { OnPlayerUnpause(i); }
        }

O problema é que isso está consumindo muita CPU do meu servidor
[Imagem: Screenshot_from_2023-03-26_23-46-20.png?...height=134]

Alguém sabe uma forma mais leve de fazer isso ou alguma forma de melhorar essa verificação?


RE: OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk - well - 27/03/2023

Já pensou em criar uma callback e criar um timer pra chama-la a cada segundo?

Edit.
OnPlayerUpdate pode ser chamada até 30 vezes por segundo, então acho que fica inviável para o que tu deseja fazer.


RE: OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk - xbruno1000x - 28/03/2023

O ideal é realmente criar um timer independente. A OnPlayerUpdate é chamada muitas vezes por segundo, e a detecção de AFK não tem uma necessidade tão grande.


RE: OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk - hiwyn - 28/03/2023

@well e @xbruno1000x
eu já uso uma função de 1 segundo, ela valida se o player tá afk (editei o tópico pra ficar mais claro, eu tinha explicado mal)
o onplayerupdate só seta o afktime

o problema é que se eu tirar o

Código:
AFKTime[playerid] = gettime();

do OnPlayerUpdate e passar pra função que é chamada em 1 segundo
Código:
public PlayerOneSec(i) {
    AFKTime[playerid] = gettime();
        // check paused
        if(paused[i] == false) {
            // 3 segundos sem resposta.
            if(GetTickCount() - AFKTime[i] > 3) { OnPlayerPause(i); }
        }
        if(paused[i] == true) {
            if(GetTickCount() - AFKTime[i] < 3) { OnPlayerUnpause(i); }
        }

o player fica afk, da pause, mas o servidor nunca detecta
parece que quando o player pausa a função de PlayerOneSec não chega nele e a OnPlayerUpdate chega
então se eu tiro do OnPlayerUpdate para de funcionar

vcs sabem o pq isso acontece?
e como arrumar?


RE: OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk - RodrigoMSR - 28/03/2023

Na prática esse código na OnPlayerUpdate não deveria causar grandes impactos na performance. De qualquer forma, use a função GetTickCount, ela é muito mais rápida que gettime. Será necessário adaptar algumas coisas no código, já que ela retorna milissegundos, e cuidado com o overflow que acontece com o servidor ligado por muitos dias (veja na wiki).


RE: OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk - hiwyn - 28/03/2023

(28/03/2023 18:32)RodrigoMSR Escreveu: Na prática esse código na OnPlayerUpdate não deveria causar grandes impactos na performance. De qualquer forma, use a função GetTickCount, ela é muito mais rápida que gettime. Será necessário adaptar algumas coisas no código, já que ela retorna milissegundos, e cuidado com o overflow que acontece com o servidor ligado por muitos dias (veja na wiki).


Troquei pra GetTickCount como você sugeriu, ta funcionando agora eu to preocupado com o overflow, não entendi direito, o que eu devo adaptar no codigo pra não ter problema com isso?

Código:
public OnPlayerUpdate(playerid)

{

    AFKTime[playerid] = GetTickCount();

}

Código:
// ongamemodeinit

SetTimerEx("PlayerOneSec", 1000, true, "i", playerid);

Código:
public PlayerOneSec(i) {

        // check paused

        if(paused[i] == false) {

            // 3 segundos sem resposta.

            if(GetTickCount() - AFKTime[i] > 3000) { OnPlayerPause(i); }

        }

        if(paused[i] == true) {

            if(GetTickCount() - AFKTime[i] < 3000) { OnPlayerUnpause(i); }

        }



RE: OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk - RodrigoMSR - 29/03/2023

https://team.sa-mp.com/wiki/GetTickCount.html

Após o servidor ficar 24 dias ligado, a função começará a retornar números negativos, o que pode quebrar os sistemas que estão usando ela. Se reiniciar o servidor periodicamente, não terá problema. Outra solução é usar a função GetTickDiff criada pela comunidade: https://gist.github.com/ziggi/5d7d8dc42f54531feba7ae924c608e73


RE: OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk - hiwyn - 29/03/2023

(29/03/2023 01:08)RodrigoMSR Escreveu: https://team.sa-mp.com/wiki/GetTickCount.html

Após o servidor ficar 24 dias ligado, a função começará a retornar números negativos, o que pode quebrar os sistemas que estão usando ela. Se reiniciar o servidor periodicamente, não terá problema. Outra solução é usar a função GetTickDiff criada pela comunidade: https://gist.github.com/ziggi/5d7d8dc42f54531feba7ae924c608e73

só trocar o PlayerOneSec

de:
Código:
public PlayerOneSec(i) {

        // check paused

        if(paused[i] == false) {

            // 3 segundos sem resposta.

            if(GetTickCount() - AFKTime[i] > 3000) { OnPlayerPause(i); }

        }

        if(paused[i] == true) {

            if(GetTickCount() - AFKTime[i] < 3000) { OnPlayerUnpause(i); }

        }
}

pra:
de:
Código:
public PlayerOneSec(i) {
    new interval = GetTickDiff(GetTickCount(), AFKTime[playerid]);
    if(paused[i] == false) {
        // 3 segundos sem resposta.
        if(interval > 3000) { OnPlayerPause(i); }
    }
    if(paused[i] == true) {
        if(interval < 3000) { OnPlayerUnpause(i); }
    }
}

certo?



uma duvida:
com mais essa adição de stock, o `GetTickCount` não ficaria mais pesado que o `gettime`?


RE: OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk - RodrigoMSR - 29/03/2023

Certo.

GetTickDiff está no timer de 1 segundo, não na OnPlayerUpdate, então mesmo que seja um pouco mais lento, não vai afetar em nada. E mesmo se estivesse, ainda é melhor que usar gettime.

Benchmark:

Código:
new tick = GetTickCount();
for(new i = 0; i < 2000000; i++)
{
    gettime();
}
printf("gettime: %dms", GetTickCount() - tick);


tick = GetTickCount();
for(new i = 0; i < 2000000; i++)
{
    GetTickDiff(GetTickCount(), tick);
}
printf("GetTickCount + GetTickDiff: %dms", GetTickCount() - tick);

Resultado (Linux):

gettime: 1676ms
GetTickCount + GetTickDiff: 87ms

No Windows a diferença é bem menor, mas no Linux foi quase 20x mais lento.


RE: OnPlayerUpdate + gettime usando muita CPU pra detectar players pausados/afk - hiwyn - 29/03/2023

(29/03/2023 11:11)RodrigoMSR Escreveu: Certo.

GetTickDiff está no timer de 1 segundo, não na OnPlayerUpdate, então mesmo que seja um pouco mais lento, não vai afetar em nada. E mesmo se estivesse, ainda é melhor que usar gettime.

Benchmark:

Código:
new tick = GetTickCount();
for(new i = 0; i < 2000000; i++)
{
    gettime();
}
printf("gettime: %dms", GetTickCount() - tick);


tick = GetTickCount();
for(new i = 0; i < 2000000; i++)
{
    GetTickDiff(GetTickCount(), tick);
}
printf("GetTickCount + GetTickDiff: %dms", GetTickCount() - tick);

Resultado (Linux):

gettime: 1676ms
GetTickCount + GetTickDiff: 87ms

No Windows a diferença é bem menor, mas no Linux foi quase 20x mais lento.

muito obrigado, melhor impossivel!

eu fiz certo a resolução pra fugir do overflow?

mais uma coisa, pensei numa parada pra deixar o onplayerupdate mais leve
Código:
public OnPlayerUpdate(playerid)
{
    if(updatingAFKTime[playerid] == false) {
        AFKTime[playerid] = GetTickCount();
        updatingAFKTime[playerid] = true;
        SetTimerEx("ResetUpdatingAFKTime", 250, false, "i", playerid);
    }
    return true;
}

public ResetUpdatingAFKTime(playerid) {
    updatingAFKTime[playerid] = false;
}

Será que é bom?