[Tutorial] How to make a Callback
#1

I went through a bit of problems when making a multi-parameter callback, so i thought id share the things i went through. So lets get started.

This isn't a from the ground up plugin example, If that is what you are looking for, click here.

So, in your plugin, you have your code, and now you want a callback to Pawn. (inb4 "Your doing it wrong.. etc etc etc")

Step 1: Setup
Now what i did to help organize these callbacks, is to make a separate function.

Plugin:
Код:
int AnEventForCallingBack(int number,char string[])
{

}
This will be our function within the plugin itself. With this callback, we want to send a string, and a number to our callback that we will call "OnSomethingHappens". The callback in pawn would look something like this:

Pawn:
pawn Код:
public OnSomethingHappens(number1,string1[])
{
}
The last step to setting this all up, is the function definition for the plugin (and for pawn if your not using an include.)

Plugin:
Код:
int AnEventForCallingBack(int number1, char string1[]);
This goes outside of any function, above all of the functions. I place mine below my includes, and variables.

Pawn:
pawn Код:
forward OnSomethingHappens(number1,string1[]);
These are crucial to work.

There. The easy part is done. Now to make the plugin send the data to the callback.

Step 2: Plugin Function

When sending multiple variables to a callback using the amx_Push functions, you may not get things right the first time.. like i did. So lets look at how to do the plugin's function.

First you need to send the data from wherever in your plugin, to the function. We are going to be utilizing a string and number, both just put in somewhere for tutorial purposes, and not really useful practically.

Код:
Somewhere in the plugin:
AnEventForCallingBack(5,"This is a string.");
So we call our plugin's function. Great! Now to send it to pawn.

Our Plugin function needs to push the integer and string to our callback "OnSomethingHappens" in Pawn. But before we can do that, we need to make sure we have something to send.

Код:
int AnEventForCallingBack(int number1,char string1[])
{
    if (strlen(string1) == 0)
        return 0;
    if (number1 == NULL)
        return 0;
    return 1;
}
You should be checking each value, and if they are null or not. For example, checking if the length of your string is 0, by using strlen. if any of these variables are null or not given, then you should stop the function from sending bad data. Now if all checks are good, it returns 1, or a true boolean value (even though its an integer...)

Now for the amx stuff. Im not an expert at the amx address and all that, so as long as you use the right variable types in this next part, it should work just fine.

Код:
int AnEventForCallingBack(int number1,char string1[])
{
    if (strlen(string1) == 0)
        return 0;
    if (number1 == NULL)
        return 0;
    int idx; 
    cell ret, amx_Address,*phys_addr;
    int amxerr = amx_FindPublic(pAMX, "OnSomethingHappens", &idx);
    if (amxerr == AMX_ERR_NONE)
    {
        return 1;
    }
    return 0;
}
Okay so now our variables actually contain data, getting to the rest of the plugin's function. This is where, if you are following along with your own example, you would replace "OnSomethingHappens" with your own callback name. Also note, we moved return 1; inside the completed if statement. What this does is return 0 if there is a AMX Error, and 1 if there is no error, and it completes.

To make the plugin finally call your data, you utilize the amx_Push functions. There are 3 types (I believe); amx_Push, amx_PushString, and amx_PushArray. We will be using amx_Push, and amx_PushString.


Код:
int AnEventForCallingBack(int number1,char string1[])
{
    if (strlen(string1) == 0)
        return 0;
    if (number1 == NULL)
        return 0;
    int idx; 
    cell ret, amx_Address,*phys_addr;
    int amxerr = amx_FindPublic(pAMX, "OnSomethingHappens", &idx);
    if (amxerr == AMX_ERR_NONE)
    {
        amx_PushString(pAMX,&amx_Address,&phys_addr,string1,0,0);
	amx_Push(pAMX, number1);
        return 1;
    }
    return 0;
}
One thing to keep in mind if you are using multiple parameters, is to push them in reverse order. This caused me several hours of needless debugging, and now im hoping to save you the same. So we call our string first, then the integer. Now, just to execute it.

Код:
int AnEventForCallingBack(int number1,char string1[])
{
    if (strlen(string1) == 0)
        return 0;
    if (number1 == NULL)
        return 0;
    int idx; 
    cell ret, amx_Address,*phys_addr;
    int amxerr = amx_FindPublic(pAMX, "OnSomethingHappens", &idx);
    if (amxerr == AMX_ERR_NONE)
    {
        amx_PushString(pAMX,&amx_Address,&phys_addr,string1,0,0);
	amx_Push(pAMX, number1);
	amx_Exec(pAMX, &ret, idx);
        return 1;
    }
    return 0;
}
If i am not mistaken, the variable ret is your return, however, your not expecting a return value from the Pawn call back, so NULL is a valid replacement, and you can go without the ret variable.


Step 3: Pawn, Compile and Test
Now go put a printf in your callback, compile it, and give it a shot. See what happens!

Hope this helps at least 1 person with their project!
Reply
#2

Nice,and thanks
Reply
#3

This looks interesting, hopefully i can try this out.

Thanks a lot dude, your help is well appreciated!
Reply
#4

And how to make the argument Float?
Reply
#5

Quote:
Originally Posted by kacper55331
Посмотреть сообщение
And I make the argument Float?
function(Decimal hi)

I'm pretty sure.
Reply
#6

Quote:
Originally Posted by Lorenc_
Посмотреть сообщение
function(Decimal hi)

I'm pretty sure.
i think in C++ float still exists, but you use amx_Push. I would try that before anything. Ill tinker in a bit and revise the OP.

EDIT: Now that i think about it, i don't think that is possible with Pawn. Think about all the SAMP callbacks.. none of them return floats..

EDIT2: You might be able to pass it as a string, then use floatstr to change it to a float.
Reply
#7

You can do floats with amx_ftoc

Код:
float some_float;
some_float = ...;
...
amx_Push(amx, amx_ftoc(some_float));
amx_Exec(...);
Reply
#8

I tried to do ProXdectory(GodFather) the plugin, but there is a need Float-type arguments.
Код:
// SendClienMessageEx(Float:radi, playerid, string[], col1, col2, col3, col4, col5, bool:echo=false)
int _SendClientMessageEx(float radi[], int playerid, char string[], int c1, int c2, int c3, int c4, int c5, int echo, AMX *amx)
{
    int index;
    if(amx_FindPublic(amx, "SendClientMessageEx", &index) == AMX_ERR_NONE)
    {
        cell retVal,
		amx_addr,
		amx_ret,
		* amx_physAddr;
		int numb;

		amx_Push(amx, echo);
		amx_Push(amx, c5);
		amx_Push(amx, c4);
		amx_Push(amx, c3);
		amx_Push(amx, c2);
		amx_Push(amx, c1);
		amx_PushString(amx, &amx_addr, &amx_physAddr, string, 0, 0);
		amx_Push(amx, playerid);
		amx_PushArray(amx, &amx_addr, &amx_physAddr, radi, 0);

		if(amx_Exec(amx, &retVal, &index) == AMX_ERR_NONE)
		{
				return true;
		} 
		else
		{
			amx_Release(amx, amx_addr);
			return false;
		}
        return true;
    }
    return false;
}
Reply
#9

Quote:
Originally Posted by kacper55331
Посмотреть сообщение
I tried to do ProXdectory(GodFather) the plugin, but there is a need Float-type arguments.
Код:
// SendClienMessageEx(Float:radi, playerid, string[], col1, col2, col3, col4, col5, bool:echo=false)
int _SendClientMessageEx(float radi[], int playerid, char string[], int c1, int c2, int c3, int c4, int c5, int echo, AMX *amx)
{
    int index;
    if(amx_FindPublic(amx, "SendClientMessageEx", &index) == AMX_ERR_NONE)
    {
        cell retVal,
		amx_addr,
		amx_ret,
		* amx_physAddr;
		int numb;

		amx_Push(amx, echo);
		amx_Push(amx, c5);
		amx_Push(amx, c4);
		amx_Push(amx, c3);
		amx_Push(amx, c2);
		amx_Push(amx, c1);
		amx_PushString(amx, &amx_addr, &amx_physAddr, string, 0, 0);
		amx_Push(amx, playerid);
		amx_PushArray(amx, &amx_addr, &amx_physAddr, radi, 0);

		if(amx_Exec(amx, &retVal, &index) == AMX_ERR_NONE)
		{
				return true;
		} 
		else
		{
			amx_Release(amx, amx_addr);
			return false;
		}
        return true;
    }
    return false;
}
Did you try this?

Quote:
Originally Posted by 0x5A656578
Посмотреть сообщение
You can do floats with amx_ftoc

Код:
float some_float;
some_float = ...;
...
amx_Push(amx, amx_ftoc(some_float));
amx_Exec(...);
Perhaps give it a shot, and let us know?
Reply
#10

What about things like GetPlayerPos? Can you possibly give some examples?
Reply
#11

The 'GetPlayerPos' is not an callback but rather a usual function, the references are gained by Amx_GetAddr.
Though I suggest you to be familiar with C++ quite a bit and know how AMX works so you don't just do it with trial-and-error approach(which is not good at all) and actually understand why something is done!
Example:
Код:
cell *pFirstParam = NULL;
if(amx_GetAddr(amx, params[1], &pFirstParam) == AMX_ERR_NONE)
{ 
    *pFirstParam = 123;
}
Reply
#12

I am quite familiar with C++ and either I do quite a bit know how AMX works, don't worry about that. The reason I asked this here is because I am also using a callback to call PAWN functions in C++.

http://forum.sa-mp.com/showpost.php?...1&postcount=17 (example here)

And yes I know, amx_FindNative is also a possibility.

Thanks for the example.
Reply
#13

Quote:
Originally Posted by RyDeR`
Посмотреть сообщение
I am quite familiar with C++ and either I do quite a bit know how AMX works, don't worry about that. The reason I asked this here is because I am also using a callback to call PAWN functions in C++.

http://forum.sa-mp.com/showpost.php?...1&postcount=17 (example here)

And yes I know, amx_FindNative is also a possibility.

Thanks for the example.
Hey man you might want to check out THIS.

Its a bit rushed, so sorry about that (i wanted to make a nice plugin tutorial that would cover some frequently asked questions, but this is as good as it gets for now :\).
Reply
#14

Am I the only one with 'error: identifier 'pAMX' is undefined' while I included the SA:MP SDK etc..?
Reply
#15

Quote:
Originally Posted by gamer_Z
Посмотреть сообщение
Am I the only one with 'error: identifier 'pAMX' is undefined' while I included the SA:MP SDK etc..?
pAMX is an AMX variable. You have to assign it on amx load (it's a pointer in which script the natives are).

PS: Thanks for sharing Kyosaur!
Reply
#16

okay what I did was
Код:
AMX * pAMX
in the global script and
Код:
pAMX = amx
in PLUGIN_CALL AmxLoad( AMX *amx ) , that will hopefully be good,

anyone interested in a drift counter plugin?
I just need help with this line:
pawn Код:
if(Invoke::callNative(&PAWN::GetPlayerState, playerid) == PLAYER_STATE_DRIVER){
Invoke::callNative(&PAWN::GetVehicleVelocity, PlayerVehicleID, &SpeedX, &SpeedY, &SpeedZ);
Invoke:: is underscored by a red line, what to do? ^^
Reply
#17

bro perfection 10 out of 10
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)