24.05.2011, 06:39
(
Последний раз редактировалось leong124; 27.03.2013 в 21:27.
Причина: recommend a better method
)
Transparent Hooking Method with States(THMS)
Better MethodBetter methods of hooking functions are now found. It makes use of the behaviour of macros to modify the name of the hooked functions. It's simpler and faster, so I recommend that method (by ipsBruno): http://forum.sa-mp.com/showpost.php?...2&postcount=14
What is it?
This is an improved method of the original transparent hooking method (named by ******) for the library scripters, so that it is more efficient than the original one, while they still do not need to tell the users to put the initialisation code into their scripts.
Inspiration
Yesterday I was inspired by ******' automata tutorial:(Click).
I wonder if we can use automata to change the states so that we don't need to add any prefix to the hooked callbacks when we write libraries, without duplication of the callback. However I failed to make that to become true. I still need a suffix. (Click)
Then I tried to mix that idea with the ALS method this morning and I succeeded to make this.
I think this should be the same as the one ****** released recently as most of the code here is developed by him.
Code
pawn Код:
#include <a_samp>
#define THMS(%0_%1(%2)) %0_%1(%2) <%0:y>
#define chain%0_%1(%2)%3; {state %0:y;%0_%1(%2);}
#define redirect%0_%1(%2)%3; forward%0_%1(%2);stock%0@%1(%2)<%0:y>{}public%0_%1(%2)<%0:n>%3;public%0_%1(%2)<>%3;
public CallbackName(parameters)
{
//Your initialisation code.
chain prefix_CallbackName(parameters);
return 1;
}
redirect prefix_CallbackName(parameters) return 1;
#if defined _ALS_CallbackName
#undef CallbackName
#else
#define _ALS_CallbackName
#endif
#define CallbackName(parameters) THMS(prefix_CallbackName(parameters))
I'll explain it a little if you don't understand why it works:
pawn Код:
#define THMS(%0_%1(%2)) %0_%1(%2) <%0:y>
pawn Код:
#define chain%0_%1(%2)%3; {state %0:y;%0_%1(%2);}
pawn Код:
#define redirect%0_%1(%2)%3; forward%0_%1(%2);stock%0@%1(%2)<%0:y>{}public%0_%1(%2)<%0:n>%3;public%0_%1(%2)<>%3;
The first function created,
pawn Код:
stock prefix@CallbackName(parameters)<prefix:y> {}
The second one,
pawn Код:
public prefix_CallbackName(parameters)<prefix:n> return 1;
The last one,
pawn Код:
public prefix_CallbackName(parameters)<> return 1;
Note that the "return 1" can be changed to "return 0" or some other values in
pawn Код:
redirect prefix_CallbackName(parameters) return 1;
pawn Код:
public CallbackName(parameters)
{
//Your initialisation code.
chain prefix_CallbackName(parameters);
return 1;
}
The
pawn Код:
chain prefix_CallbackName(parameters);
pawn Код:
redirect prefix_CallbackName(parameters) return 1;
Again, check it above for what it does.
pawn Код:
#if defined _ALS_CallbackName
#undef CallbackName
#else
#define _ALS_CallbackName
#endif
pawn Код:
#define CallbackName(parameters) THMS(prefix_CallbackName(parameters))
Testing
I've tested this method that no errors were found and multiple hooking does work(i've tested it with triple hooking ). It also have backward compatibility that it supports includes with the original ALS method. Thanks to ******, the run-time errors are fixed. It should work now, but if you find any bugs/problems on this please feel free to tell it here.
Note
Due to the removal of CallLocalFunction, you don't need to use funcidx to check if the callback exists and call it IMHO. I've made a benchmark on it and it's as slow as the original ALS hooking.
Benchmark
I used Slice's benchmark macro to test it here, and I test the speed by calling the original OnFilterScriptInit callback:
Testing filterscript:
pawn Код:
#include <a_samp>
#include <testhook>
#pragma tabsize 0
#define START_BENCH(%0); {new __a=%0,__b=0,__c,__d=GetTickCount(),__e=1;do{}\
while(__d==GetTickCount());__c=GetTickCount();__d=__c;while(__c-__d<__a||\
__e){if(__e){if(__c-__d>=__a){__e=0;__c=GetTickCount();do{}while(__c==\
GetTickCount());__c=GetTickCount();__d=__c;__b=0;}}{
#define FINISH_BENCH(%0); }__b++;__c=GetTickCount();}printf(" Bench for "\
%0": executes, by average, %.2f times/ms.",floatdiv(__b,__a));}
public OnFilterScriptInit()
{
return 1;
}
public OnRconCommand(cmd[])
{
if(!strcmp(cmd,"test",false))
{
START_BENCH(10000);
CallLocalFunction("OnFilterScriptInit","");
FINISH_BENCH("ALS");
}
return 1;
}
pawn Код:
#include <a_samp>
public OnFilterScriptInit()
{
CallLocalFunction("testhook_OnFilterScriptInit","");
return 1;
}
#if defined _ALS_OnFilterScriptInit
#undef OnFilterScriptInit
#else
#define _ALS_OnFilterScriptInit
#endif
#define OnFilterScriptInit testhook_OnFilterScriptInit
forward testhook_OnFilterScriptInit();
pawn Код:
#include <a_samp>
#define THMS(%0_%1(%2)) %0_%1(%2) <%0:y>
#define chain%0_%1(%2)%3; {state %0:y;%0_%1(%2);}
#define redirect%0_%1(%2)%3; forward%0_%1(%2);stock%0@%1(%2)<%0:y>{}public%0_%1(%2)<%0:n>%3;public%0_%1(%2)<>%3;
public OnFilterScriptInit()
{
chain testhook_OnFilterScriptInit();
return 1;
}
redirect testhook_OnFilterScriptInit() return 1;
#if defined _ALS_OnFilterScriptInit
#undef OnFilterScriptInit
#else
#define _ALS_OnFilterScriptInit
#endif
#define OnFilterScriptInit() THMS(testhook_OnFilterScriptInit())
Код:
Bench for THMS: executes, by average, 516.88 times/ms. Bench for ALS: executes, by average, 401.54 times/ms.
My server crashed when I try to test it with y_hooks so feel free to test it.
Issues
- You can't forward the hooked function after the THMS macro. You'll need to undefine the macro first if you want to. However, forwarding the function is unnecessary, as the function is forwarded in the redirect marco.
- You can't call the original function after the THMS macro. If you try to, you will notice that a state label is put there by the macro. You'll also need to undefine the macro before doing so.
- You can't call the hooked function by CallRemoteFunction. Seems that states cannot be recognised in other scripts (i.e. it is not global).
- ****** - Inspired me by his great tutorial on automata, and even helped me to fix the bugs.
- Slice - For his great benchmarking macro.
- leong124 - For inventing this hooking method.