PHP код:
/*
These functions need to appear just ONCE ever, regardless of how many hooked
functions you have (so could be in another file already). They define our two
states "hooked" and "unhooked", and a function for fall-back when neither state
is defined. They also might not appear in the final AMX, but I've not tested
this theory (the lack of the "public" keyword MIGHT invoke the correct bug, I
don't know about with states). Anyway, these functions are never used.
*/
#include <a_samp>
#if !defined _ALS_
forward public _ALS_();
_ALS_()<_ALS_:unhooked>{}
_ALS_()<_ALS_:hooked>{}
_ALS_()<>{}
#endif
/*
Here is our first hooked function, it is always a good idea to hook a ScriptInit
function so you can set "_ALS_" state, but you can also do that in any other
callback, this is just merely a small optimisation.
*/
public OnGameModeInit()
{
/*
Now we set "_ALS_" to "hooked". It doesn't matter if the next function in
the chain exists or not (we don't even need to check), just set it to
hooked and leave it. In fact, given that other libraries may have come
before yours, it might already be set to "hooked", but that's fine - we can
just set it again.
*/
state _ALS_:hooked;
/*
Now call the next callback in the chain. This previously required
"CallLocalFunction" to avoid a compiler error if the function didn't exist,
but not any more - this is the major improvement in this method over other
methods as you will see shortly.
*/
return Streamer_OnGameModeInit();
}
/*
Forward the next callback in the chain.
*/
forward Streamer_OnGameModeInit();
/*
Normal ALS redefinition checks, see previous topics on ALS for more information.
*/
#if defined _ALS_OnGameModeInit
#undef OnGameModeInit
#else
#define _ALS_OnGameModeInit
#endif
/*
Now this is where it starts getting interesting. Because we call
"Streamer_OnGameModeInit" (obviously "Streamer_" is our example library here),
the code MUST include this function, but because we are hooking it, we don't
know if it will or not - so we create it! But if we create it, then what's in
the next library (or mode) in the chain? The answer is they are BOTH the next
function in the chain thanks to the magic of states.
The first line here defines "OnGameModeInit" when "_ALS_" is set to "unhooked",
this is actually NEVER the case, but is still important as it tells the compiler
which automata our "Streamer_OnGameModeInit" function is controlled by (i.e.
"_ALS_"). The second line is the "fallback" function - if "_ALS_" is in a state
for which there is no specific implementation, this one will get called instead
and just instantly return (maybe you can now see the trick). If "_ALS_" is set
to "hooked" and there is no other function, then the compiler will identify this
fallback function as the correct one to call (or the runtime will rather), if
the hooked function DOES exist, then that more specialised version will be
called instead.
If we didn't have the "_ALS_:unhooked" function, then the fallback wouldn't work
as what's it falling back from?
*/
public Streamer_OnGameModeInit() <_ALS_:unhooked> return 1;
public Streamer_OnGameModeInit() <> return 1;
/*
Because we are now using states, we need a slightly more complex redefinition of
the next callback's function definition - we need to transparently add the
"_ALS_:hooked" state to it, so that's exactly what this line does.
Remember that "_ALS_" only needs to be defined once in a mode ever, and
"Streamer_" is just the standard unique ALS prefix I've used in this example -
it needs to be unique.
*/
#define OnGameModeInit(%0) Streamer_OnGameModeInit(%0)<_ALS_:hooked>