Raised This Month: $ Target: $400
 0% 

How to deal with this global timer...


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
Chdata
Veteran Member
Join Date: Aug 2012
Location: Computer Chair, Illinois
Old 03-28-2015 , 13:29   How to deal with this global timer...
Reply With Quote #1

PHP Code:
new Handle:SandvichTimers[TF_MAX_PLAYERS] = {INVALID_HANDLE,...};

public 
OnMapStart()
{
    for (new 
1<= MaxClientsi++)
    {
        
SandvichTimers[i]       = INVALID_HANDLE;
    }
}

public 
OnEventRoundStart(Handle:hEvent, const String:szStrbool:bBool)
{
    for (new 
ionplay 1ionplay <= MaxClientsionplay++)
    {
        
ClearTimer(SandvichTimers[ionplay]);
    }
}

public 
OnClientDisconnect(client)
{
    
ClearTimer(SandvichTimers[client]);
}

OnHeavyEatSandVich(iClient)
{
    
SandvichTimers[iClient] = CreateTimer(1.0tSandvichHealGetClientUserId(iClient), TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT);
}

public 
Action:tSandvichHeal(Handle:hTimerany:ref)
{
    new 
iClient GetClientOfUserId(ref);

    if (!
iClient || !IsClientInGame(iClient) || TF2_GetPlayerClass(iClient) != TFClass_Heavy)
    {
        
SandvichTimers[iClient] = INVALID_HANDLE;
        return 
Plugin_Stop;
    }

    static 
timer[TF_MAX_PLAYERS] = 0;

    
AddPlayerHealth(iClient10000.0true);

    
// Perform healing 4 times (+1 because we did before this timer was made)
    
if (++timer[iClient] >= 4// Note: 3 would be the normal duration of eating
    
{
        
timer[iClient] = 0;
        
SandvichTimers[iClient] = INVALID_HANDLE;
        return 
Plugin_Stop;
    }

    return 
Plugin_Continue;
}


stock ClearTimer(&Handle:hTimer)
{
    if (
hTimer != INVALID_HANDLE)
    {
        
KillTimer(hTimer);
        
hTimer INVALID_HANDLE;
    }

My problem starts with

public Action:tSandvichHeal(

GetClientOfUserId returns 0 if the client is "no longer in the server".

If it returns 0, that means I cannot clear SandvichTimers[iClient] and can only clear SandvichTimers[0]. If I were to use CreateDataTimer so I can remember the iClient, it's possible for another player to connect to the server as the old player disconnects, thus fire the timer on someone when I don't want it to. [Same problem would happen if I pass the client index instead of the userid].

If I don't try to clear SandvichTimers during tSandvichHeal, then ClearTimer will try to close an invalid handle. If I do clear it, there's no guarantee that I will clear it properly.

I'm not sure if OnClientDisconnect really solves the issue either.

I'm not sure how to set this up.
__________________

Last edited by Chdata; 03-28-2015 at 16:58.
Chdata is offline
Miu
Veteran Member
Join Date: Nov 2013
Old 03-28-2015 , 14:38   Re: How to deal with this global timer...
Reply With Quote #2

your question is sort of confusing, but I'm guessing you want to clear the correct SandvichTimer index in the timer if the userid is not valid? just pass the client id along with the userid?
Miu is offline
Peace-Maker
SourceMod Plugin Approver
Join Date: Aug 2008
Location: Germany
Old 03-28-2015 , 14:46   Re: How to deal with this global timer...
Reply With Quote #3

When closing and clearing the handle in OnClientDisconnect, the timer won't fire with an invalid userid. So you should never get a clientindex of 0.
__________________
Peace-Maker is offline
ici
Member
Join Date: Jan 2014
Old 03-28-2015 , 15:56   Re: How to deal with this global timer...
Reply With Quote #4

PHP Code:
new Handle:gH_SandvichTimers[TF_MAX_PLAYERS] = {INVALID_HANDLE, ...};
new 
g_Timers[TF_MAX_PLAYERS];

public 
OnMapStart()
{
    
ClearAllTimers();
}

public 
OnClientDisconnect(client)
{
    
ClearClientTimer(client);
}

public 
Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{
    
ClearAllTimers();
}

public 
Action:Timer_SandvichHeal(Handle:timerany:userid)
{
    new 
client GetClientOfUserId(userid);
    
    if (!
client || !IsClientInGame(client) || !IsPlayerAlive(client) || TF2_GetPlayerClass(client) != TFClass_Heavy) {
        
g_Timers[client] = 0;
        
gH_SandvichTimers[client] = INVALID_HANDLE;
        return 
Plugin_Stop;
    }
    
    
AddPlayerHealth(client10000.0true);
    
    if (++
g_Timers[client] >= 4) {
        
g_Timers[client] = 0;
        
gH_SandvichTimers[client] = INVALID_HANDLE;
        return 
Plugin_Stop;
    }
    
    return 
Plugin_Continue;
}

OnHeavyEatSandVich(client)
{
    
gH_SandvichTimers[client] = CreateTimer(1.0Timer_SandvichHealGetClientUserId(client), TIMER_REPEAT);
}

ClearClientTimer(client)
{
    if (
gH_SandvichTimers[client] != INVALID_HANDLE) {
        
KillTimer(gH_SandvichTimers[client]);
        
gH_SandvichTimers[client] = INVALID_HANDLE;
        
g_Timers[client] = 0;
    }
}

ClearAllTimers()
{
    for (new 
1<= MaxClientsi++) {
        if (
gH_SandvichTimers[i] != INVALID_HANDLE) {
            
KillTimer(gH_SandvichTimers[i]);
            
gH_SandvichTimers[i] = INVALID_HANDLE;
            
g_Timers[i] = 0;
        }
    }


Last edited by ici; 03-28-2015 at 15:58.
ici is offline
KyleS
SourceMod Plugin Approver
Join Date: Jul 2009
Location: Segmentation Fault.
Old 03-28-2015 , 16:18   Re: How to deal with this global timer...
Reply With Quote #5

PHP Code:
new Handle:SandvichTimers[MAX_PLAYERS+1] = {INVALID_HANDLE,...};

public 
OnEventRoundStart(Handle:hEvent, const String:szStr[], bool:bBool)
{
    for (new 
MaxClients0; --i)
    {
        
OnClientDisconnect(i);
    }
}

public 
OnClientDisconnect(client)
{
    
tSandvichHeal(INVALID_HANDLEclient);
    
ReplaceHandle(SandvichTimers[client], INVALID_HANDLE);
}

OnHeavyEatSandVich(iClient)
{
    
ReplaceHandle(SandvichTimers[iClient], CreateTimer(1.0tSandvichHealiClientTIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT));
}

public 
Action:tSandvichHeal(Handle:hTimerany:iClient)
{
    static 
timer[MAXPLAYERS+1];
    if (!
IsClientInGame(iClient) || TF2_GetPlayerClass(iClient) != TFClass_Heavy || ++timer[iClient] >= 4)
    {
        
timer[iClient] = 0;
        
ReplaceHandle(SandvichTimers[iClient], INVALID_HANDLE);
        return 
Plugin_Stop;
    }

    
AddPlayerHealth(iClient10000.0true);
    return 
Plugin_Continue;
}

static 
stock ReplaceHandle(Handle:&hNewHandle:hSource/* Probably Illegal on newer compilers from an array. */
{
    if (
hNew != INVALID_HANDLE)
    {
        
CloseHandle(hNew);
    }

    
hNew hSource;

KyleS is offline
Chdata
Veteran Member
Join Date: Aug 2012
Location: Computer Chair, Illinois
Old 03-28-2015 , 17:20   Re: How to deal with this global timer...
Reply With Quote #6

That's an interesting one, KyleS.

Sometimes, for OnClientDisconnect, IsClientInGame() can still return true.

Should I check IsClientConnected in the timer?

I'm not sure what calling the timer func is for OnClientDisconnected otherwise. It'll either try to heal someone as they're disconnecting (which should always be the case if they just ate a sandvich - they're only not InGame if they're still connecting to the server) or fire ReplaceHandle twice which does the same thing. In some cases it'll reset the static timer there if you disconnect near the last iteration. Maybe I should check if hTimer == INVALID_HANDLE?

I didn't know it was safe to closehandle a timer from within the timer ;O

Thanks for the help, this might solve issues I've been having with lots of other timers.

Edit: Nope, you can't close timers from within the same timer. I changed the replacehandles in the timer to just set the global handle to INVALID_HANDLE.

It seems like it's good now though.
__________________

Last edited by Chdata; 03-29-2015 at 15:02.
Chdata is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 18:12.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode