Raised This Month: $51 Target: $400
 12% 

[TUT/INFO] set_tasks: when they are and when they aren't necessary


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
Hawk552
AMX Mod X Moderator
Join Date: Aug 2005
Old 08-12-2006 , 12:34   [TUT/INFO] set_tasks: when they are and when they aren't necessary
Reply With Quote #1

This tutorial is intended for beginner-intermediate level scripters.

set_task can sometimes be an extremely useful function, but other times it is really overkill. What few people know is that it is a very expensive operation, the CPU has to literally check every cycle if it is time for the set_task to be executed. Usually, there are ways around a set_task.

set_task also has the restriction of being unable to be called more than every 0.1 seconds. This means that if you pass 0.01 in parameter 1, it will be clamped at 0.1.

But what can we do to avoid it? Sometimes you really have to use a set_task, such as here:

Code:
#include <amxmodx> #include <amxmisc> #include <engine> public plugin_init() {     register_plugin("ROFL","1.0","Hawk552")         register_event("ResetHUD","EventResetHUD","be") } public EventResetHUD(id)     set_task(1.0,"DelayedResetHUD",id)     public DelayedResetHUD(id)     DispatchSpawn(id)

But why? There is no real way to escape delaying the function once without doing something more CPU intensive or unreliable. We could create an ent and make it think in 1 second (which will be covered later), but that not only adds an unnecissary spawn for such a small thing, it also is more unreliable (i.e. the server could crash due to too many ents, or it could be failed to be spawned and the function simply has to end there).

But there are other times where escaping a set_task is not only easier, but less resource consuming.

One of the commonly known ways of doing this is the forward client_PreThink / FM_PlayerPreThink. This forward is called every frame on a client before rendering physics. In this forward, you would do things that generally must be constantly updated, like velocity or changing rendering.

Here is an example:

Code:
#include <amxmodx> #include <amxmisc> #include <engine> public plugin_init()     register_plugin("ROFL","1.0","Hawk552") public client_PreThink(id) {     static Float:Velocity[3]     entity_get_vector(id,EV_VEC_velocity,Velocity)         Velocity[2] = -floatabs(Velocity[2])         entity_set_vector(id,EV_VEC_velocity,Velocity) }

Why is a set_task unsuitable here? For two reasons: 1) the half-life engine will allow you to have timers anyway (to a varying degree), and 2) it will look very glitchy since if the client has 60 FPS, the server is only checking every 10 frames if their velocity is what it should be.

One of the less practiced ways of avoiding set_tasks is to spawn an entity, set its nextthink to whatever you want, and register_think/FM_Think it. But when is this necessary? Pretty much whenever you use the "b" flag in set_task, it's better to use this method. It's also preferred when you have already spawned an entity (such as an NPC) and want to make it say, disappear, in x seconds.

Here is an example:

Engine:
Code:
#include <amxmodx> #include <amxmisc> #include <engine> new g_Classname[] = "roflmao" public plugin_init() {     register_plugin("ROFL","1.0","Hawk552")         new Ent = create_entity("info_target")     entity_set_string(Ent,EV_SZ_classname,g_Classname)     entity_set_float(Ent,EV_FL_nextthink,20.0)         register_think(g_Classname,"RoflmaoThink") } public RoflmaoThink(Ent) {     log_amx("ROFLMAO thinks.")         entity_set_float(Ent,EV_FL_nextthink,20.0) }

Fakemeta:
Code:
#include <amxmodx> #include <amxmisc> #include <fakemeta> new g_Classname[] = "roflmao" public plugin_init() {     register_plugin("ROFL","1.0","Hawk552")         new Ent = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,"info_target"))     set_pev(Ent,pev_classname,g_Classname)     set_pev(Ent,pev_nextthink,20.0)         register_forward(FM_Think,"ForwardThink") } public ForwardThink(Ent) {     static Classname[33]     pev(Ent,pev_classname,Classname,32)         if(!equal(Classname,g_Classname))         return FMRES_IGNORED         log_amx("ROFLMAO thinks.")         set_pev(Ent,pev_nextthink,20.0)         return FMRES_IGNORED }

This is useful, as said, for escaping the usage of flag "b" on set_task, or executing set_task at the end of the function called.

The final way to escape a set_task is when using cooldowns. For example, you want a player to be able to use a special attack every 30 seconds.

Code:
#include <amxmodx> #include <amxmisc> #include <fakemeta> // this is how often you want your players to be able to use your attack #define ATTACK_COOLDOWN 20.0 new Float:g_LastAttack[33] // optimization new Float:g_Cooldown = ATTACK_COOLDOWN public plugin_init() {     register_plugin("ROFL","1.0","Hawk552")         register_clcmd("myattack","CmdMyAttack") } public CmdMyAttack(id) {     // to use engine instead, use this:     /* new Float:Time = halflife_time() */     new Float:Time     global_get(glb_time,Time)            if(!is_user_alive(id) || Time - g_Cooldown < g_LastAttack[id])         return         g_LastAttack[id] = Time         // your code here }

I'm not going to push this any longer, so if you have any questions about it then feel free to ask.
__________________
Hawk552 is offline
Send a message via AIM to Hawk552
ConManKilla07
BANNED
Join Date: Aug 2006
Old 08-12-2006 , 13:26   Re: set_tasks: when they are and when they aren't necessary
Reply With Quote #2

Another Great Tutorioal by Hawk gj! But can you go into further detail about "the forward client_PreThink / FM_PlayerPreThink" . You explained how to use it instead of set_tesk , but can you furher explaine exactly what they are and what they do please? I've seen them both used in scripts , but ive never known exactly what they do . Thank you.
+karma~
ConManKilla07 is offline
Zenith77
Veteran Member
Join Date: Aug 2005
Old 08-12-2006 , 14:25   Re: set_tasks: when they are and when they aren't necessary
Reply With Quote #3

There are the same, just client_PreThink is called via the engine module, and FM_PlayerPreThink is hooked through fakemeta. PreThink is called each frame. So if a player has 60 FPS (Frames Per Second) then PreThink will be called 60 times a second. This is good if you need to modify a player's velocity, get what buttons they're pressing, etc.
__________________
Quote:
Originally Posted by phorelyph View Post
your retatred
Zenith77 is offline
Hawk552
AMX Mod X Moderator
Join Date: Aug 2005
Old 08-12-2006 , 14:38   Re: set_tasks: when they are and when they aren't necessary
Reply With Quote #4

I said it here:

Quote:
One of the commonly known ways of doing this is the forward client_PreThink / FM_PlayerPreThink. This forward is called every frame on a client before rendering physics. In this forward, you would do things that generally must be constantly updated, like velocity or changing rendering.
but whatever
__________________
Hawk552 is offline
Send a message via AIM to Hawk552
ConManKilla07
BANNED
Join Date: Aug 2006
Old 08-12-2006 , 15:02   Re: set_tasks: when they are and when they aren't necessary
Reply With Quote #5

Would I be able to use client_prethink to change a players speed ? Becasue im having some dificulty with the way I changed the speed in one of my scripts . Every time they change weapons there speed resets it's self . And then sometimes it wont work at all , or it will start at wierd times , like out of no where . Which is very unreliable .
ConManKilla07 is offline
Hawk552
AMX Mod X Moderator
Join Date: Aug 2005
Old 08-12-2006 , 15:02   Re: set_tasks: when they are and when they aren't necessary
Reply With Quote #6

Quote:
Originally Posted by ConManKilla07
Would I be able to use client_prethink to change a players speed ? Becasue im having some dificulty with the way I changed the speed in one of my scripts . Every time they change weapons there speed resets it's self . And then sometimes it wont work at all , or it will start at wierd times , like out of no where . Which is very unreliable .
yes, but it's a bad way. Hook CurWeapon.
__________________
Hawk552 is offline
Send a message via AIM to Hawk552
ConManKilla07
BANNED
Join Date: Aug 2006
Old 08-12-2006 , 15:09   Re: set_tasks: when they are and when they aren't necessary
Reply With Quote #7

Thank you Hawk ! I'd give you more karma but I already gave you some
ConManKilla07 is offline
SweatyBanana
BANNED
Join Date: Sep 2005
Location: LOL
Old 08-12-2006 , 15:16   Re: set_tasks: when they are and when they aren't necessary
Reply With Quote #8

Nice tutorial.

It really helps..

I am learning fakemeta.
SweatyBanana is offline
Send a message via AIM to SweatyBanana Send a message via Yahoo to SweatyBanana
watch
Senior Member
Join Date: Sep 2005
Old 08-14-2006 , 20:32   Re: set_tasks: when they are and when they aren't necessary
Reply With Quote #9

nice tutorial, started going through my tasks and replacing them

although shouldn't
Code:
entity_set_float(Ent,EV_FL_nextthink,20.0)
be
Code:
entity_set_float(ent, EV_FL_nextthink, halflife_time() + 20.0)

otherwise the think starts getting called every 0.01 secs or w/e because the nextthink is still at 20.0
__________________
Code:
#include <amusing_small_signiture>

Last edited by watch; 08-14-2006 at 20:36.
watch is offline
prody
New Member
Join Date: Jul 2006
Old 10-29-2006 , 15:34   Re: set_tasks: when they are and when they aren't necessary
Reply With Quote #10

Yes, if you set nextthink to a time that has passed think will be instantly called.

So if you set nextthink to 20.0 and hook think and reset it to 20.0 it will lead to calls every tick

In the Engine example you just add halflife_time() to the float (like watch said).

With FakeMeta it should be:
Code:
#include <amxmodx> #include <amxmisc> #include <fakemeta> new g_Classname[] = "roflmao" public plugin_init() {     register_plugin("ROFL","1.0","Hawk552")         new Ent = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,"info_target"))     set_pev(Ent,pev_classname,g_Classname)     set_pev(Ent,pev_nextthink,20.0)         register_forward(FM_Think,"ForwardThink") } public ForwardThink(Ent) {     static Classname[33]     pev(Ent,pev_classname,Classname,32)         if(!equal(Classname,g_Classname))         return FMRES_IGNORED     static Float: s_glbtime         log_amx("ROFLMAO thinks.")     global_get(glb_time, s_glbtime)     set_pev(Ent,pev_nextthink, s_glbtime + 20.0)         return FMRES_IGNORED }

Btw nextthink is called at the time passed since mapchange in seconds, thats why you can't use engfunc(EngFunc_Time) which returns the time since the server was started and is not reset at mapchange.

Last edited by prody; 10-29-2006 at 15:39.
prody 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 06:19.


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