Calling local callbacks - Without using CallLocalFunction

Introduction

I've been doing some works related to hooking and found out that there's no need of using CallLocalFunction for calling out local callbacks. Most of the libraries or includes use hook method 7 however calls out any custom callbacks using CallLocalFunction. Until today, even I used to do the same thing. This tutorial will be explaining how to call out custom callbacks without using CallLocalFunction. This concept is better than the CallLocalFunction because it's faster than how CallLocalFunction works.

Requirements

For this tutorial, you will have to know hook method 7 or at least the way of normal hooking. If you got an idea on the latter one, the 7th method will be easier.

http://forum.sa-mp.com/showthread.php?t=441293 - Hook method 7 (Recommended)
http://forum.sa-mp.com/showthread.php?t=392061 - Hook method 1 (There are other hook method 1 tutorials too, you can find them.)

Tutorial

Before starting the tutorial, I'll first show how most of them (or how I used to) call out custom callbacks. Suppose that I want a callback called OnPlayerEnterHydra whenever a player tries to enter hydra. This callback will be having the parameters playerid which is the player who undergoes that callback and the next one called vehicleid which is the ID of the vehicle (not model, because model will be hydra) which undergoes this call.

pawn Code:
#include <a_samp> //Including a_samp

public OnPlayerEnterVehicle(playerid, vehicleid, ispassenger)
{
//This callback is triggered whenever a player attempts to enter a vehicle.
//I don't have to consider using 'ispassenger' parameter because hydra
//contains a seat for pilot only.

if(GetVehicleModel(vehicleid) == 520) //If the vehicle model is equal to hydra's model ID, then:
{
CallLocalFunction("OnPlayerEnterHydra", "ii", playerid, vehicleid);
//Calling a local function called "OnPlayerEnterHydra" with 2 integer
//formats ("ii") of values "playerid"'s value and "vehicleid"'s value.
//Know more about CallLocalFunction : wiki.sa-mp.com/wiki/CallLocalFunction
return 1; //You can either use return CallLocalFunction(...) if something has to be returned
//from what that function wanted to, or simply return 1 this callback.
}
//If hooking "OnPlayerEnterVehicle" (Not going detailed over hooking parts, you should know them.)
#if defined L_OnPlayerEnterVehicle
return L_OnPlayerEnterVehicle(playerid, vehicleid, ispassenger);
#else
return 1;
#endif
}

#if defined _ALS_OnPlayerEnterVehicle
#undef OnPlayerEnterVehicle
#else
#define _ALS_OnPlayerEnterVehicle
#endif

#define OnPlayerEnterVehicle L_OnPlayerEnterVehicle

#if defined L_OnPlayerEnterVehicle
forward L_OnPlayerEnterVehicle(playerid, vehicleid, ispassenger);
#endif

forward OnPlayerEnterHydra(playerid, vehicleid); //Forwarding my custom callback - OnPlayerEnterHydra or it cannot be implemented like this:


/*public OnPlayerEnterHydra(playerid, vehicleid)
{
SendClientMessage(playerid, -1, "You entering on a hydra!");
return 1;
}
*/

The above code is the normal way or the way most of them used to adapt. Now the concept what I'm sharing is to avoid CallLocalFunction directly by calling out the function itself. This will give error because the original part isn't implemented however the point of this tutorial is to avoid that error. To avoid it, implement the callback and hook it just like how normal SA-MP callbacks are hooked. It can be hooked either with hook method 7(which is recommended) or any other hook methods. Here's the concept explained:

pawn Code:
//On some event, instead of using CallLocalFunction I call it directly.
<call_back>; //Example : OnPlayerEnterHydra(playerid, vehicleid);


//Now, forwarding it:
forward <call_back>; //Example : forward OnPlayerEnterHydra(playerid, vehicleid);

//Since the callback isn't implemented, it will give an error. To avoid that,
//implement the callback and to use it on future, hook it.

public <call_back> //Example : public OnPlayerEnterHydra(playerid, vehicleid)
{
//Hooking it like just another callback.
#if defined Lib_<call_back> //Example : #if defined Lib_OnPlayerEnterVehicle
return Lib_<call_back>; //Example : return Lib_OnPlayerEnterVehicle(playerid, vehicleid);
#else
return 1;
#endif
}

#if defined _ALS_<call_back> //If _ALS_<call_back> is defined (Example : #if defined _ALS_OnPlayerEnterHydra)
#undef <call_back> //If so, undefine that callback. Example : #undef OnPlayerEnterHydra
#else //Else
#define _ALS_<call_back> //Define _ALS_<call_back> Example : #define _ALS_OnPlayerEnterHydra
#endif //Ending the #if statement.

#define <call_back> Lib_<call_back> //<call_back> has been undefined, it can be defined to any value now.
//Defining it to what we used it on the undefined callback earlier.
//Example : #define OnPlayerEnterHydra Lib_OnPlayerEnterHydra

#if defined Lib_<call_back> //If Lib_<call_back> has been defined anywhere:
//Example : #if defined Lib_OnPlayerEnterHydra
forward Lib_<call_back>; //Then, forward that. Example : Lib_OnPlayerEnterHydra(playerid, vehicleid);
#endif //Ending the #if statement.

I'm not sure if you're completely clear with the concept so for that I'm also including the earlier example with no CallLocalFunction used.

pawn Code:
#include <a_samp> //Including a_samp

public OnPlayerEnterVehicle(playerid, vehicleid, ispassenger)
{
//This callback is triggered whenever a player attempts to enter a vehicle.
//I don't have to consider using 'ispassenger' parameter because hydra
//contains a seat for pilot only.

if(GetVehicleModel(vehicleid) == 520) //If the vehicle model is equal to hydra's model ID, then:
{
OnPlayerEnterHydra(playerid, vehicleid); //Directly calling out this one rather than using CallLocalFunction.
//Can also be done as return OnPlayerEnterHydra(playerid, vehicleid);
return 1;
}
//If hooking "OnPlayerEnterVehicle" (Not going detailed over hooking parts, you should know them.)
#if defined L_OnPlayerEnterVehicle
return L_OnPlayerEnterVehicle(playerid, vehicleid, ispassenger);
#else
return 1;
#endif
}

#if defined _ALS_OnPlayerEnterVehicle
#undef OnPlayerEnterVehicle
#else
#define _ALS_OnPlayerEnterVehicle
#endif

#define OnPlayerEnterVehicle L_OnPlayerEnterVehicle

#if defined L_OnPlayerEnterVehicle
forward L_OnPlayerEnterVehicle(playerid, vehicleid, ispassenger);
#endif

forward OnPlayerEnterHydra(playerid, vehicleid); //Forwarding my custom callback - OnPlayerEnterHydra or it cannot be implemented like this:

//Since I've directly called, I should implement the callback to avoid errors.

public OnPlayerEnterHydra(playerid, vehicleid)
{
//But I also want this callback to be used after, so I hook it.
#if defined Lib_OnPlayerEnterHydra
return Lib_OnPlayerEnterHydra(playerid, vehicleid);
#else
return 1;
#endif
}

#if defined _ALS_OnPlayerEnterHydra
#undef OnPlayerEnterHydra
#else
#define _ALS_OnPlayerEnterHydra
#endif

#define OnPlayerEnterHydra Lib_OnPlayerEnterHydra

#if defined Lib_OnPlayerEnterHydra
forward Lib_OnPlayerEnterHydra(playerid, vehicleid);
#endif

//I've hooked my callback, now I can use it again like this:

public OnPlayerEnterHydra(playerid, vehicleid)
{
return 1;
}


Here's another example which isn't explained, it's meant for quick testing purposes only. You can run it as a filterscript and know it's results.
pawn Code:
//Callback hooked : OnFilterScriptInit
//My custom callback : MyCustomFSCall();

#include <a_samp>


new
rand_int;

public OnFilterScriptInit() {

print("Test : Started FS");
rand_int = 5;

if(rand_int == 5) {

return MyCustomFSCall();
}
#if defined Lib_OnFSInit
return Lib_OnFSInit();
#else
return 1;
#endif
}

#if defined _ALS_OnFilterScriptInit
#undef OnFilterScriptInit
#else
#define _ALS_OnFilterScriptInit
#endif

#define OnFilterScriptInit Lib_OnFSInit

#if defined Lib_OnFSInit
forward Lib_OnFSInit();
#endif

forward MyCustomFSCall();
public MyCustomFSCall() {

//Hooking it once more to use it again.
print("Test : Call 1");

#if defined Lib_MyCustomFSCall
return Lib_MyCustomFSCall();
#else
return 1;
#endif
}

#if defined _ALS_MyCustomFSCall
#undef MyCustomFSCall
#else
#define _ALS_MyCustomFSCall
#endif

#define MyCustomFSCall Lib_MyCustomFSCall

#if defined Lib_MyCustomFSCall
forward Lib_MyCustomFSCall();
#endif

public MyCustomFSCall() {

//Here's it, once again I'm using this callback. If you're building an include, you don't have to
//call your same callback once again, if you do, you will have to hook it to call it again separately.
printf("Test : Call 2");
return 1;
}

Conclusion

This tutorial is to share the concept of avoiding the usage of CallLocalFunction for local callback calls. CallRemoteFunction is used only for calling out functions remotely from other scripts. There's no need of that function if you're simply creating a custom callback for an include. Only in case if you want it to function remotely, use it. Going quickly through what I've posted, the steps are:


- Instead of CallLocalFunction, directly call your callback.
- Forward the custom callback like you do normally.
- Implement your custom callback and hook it, you're done.

NOTE : By performance, it means both the call and compile time. This method also saves memory as compared to CallLocalFunction.