IS4Code / PawnPlus

A SA-MP plugin enhancing the capabilities of the Pawn programming language
MIT License
103 stars 17 forks source link

pawn_register_callback, handler_return and callback parameters #19

Closed George480 closed 5 years ago

George480 commented 5 years ago

A normal handler:

pawn_register_callback("OnPlayerConnect", "AntiFlood_OPC");

calls AntiFlood_OPC with correct playerid. A special handler with handler_return calls AntiFlood_OPC with correct return value but wrong playerid.

From wiki: flags specify how the function should be handled. handler_return will place an additional by-ref parameter before the callback parameters to specify what should be returned from the callback.

pawn_register_callback("OnPlayerConnect", "AntiFlood_OPC", handler_return, "d", 0);
forward public AntiFlood_OPC(break_hook_chain, playerid);
public AntiFlood_OPC(break_hook_chain, playerid)
{
    ...

    if (condition)
    {
        break_hook_chain = 1; // do not call other handlers for OnPlayerConnect
    }
    return break_hook_chain;
}

playerid has value of 3964 when debugging it. If I change value 0 from pawn_register_callback to something random, it is printed correctly so the issue is only with the callback parameters.

IS4Code commented 5 years ago

Hello, the function seems to be working as intended:

pawn_register_callback("OnPlayerConnect", "AntiFlood_OPC", handler_return, "d", 0);

This creates a closure from AntiFlood_OPC, storing the value of 0 inside. When your function is called, the first argument is the value 0 that you stored inside the callback, the second argument is the address of the variable where the return value will be provided, and the rest are the original arguments to the callback. Thus with pawn_register_callback("OnPlayerConnect", "AntiFlood_OPC", handler_return, "d", 0);, the function should be this:

public AntiFlood_OPC(always_zero, &break_hook_chain, playerid)
{
    ...
}

I should point out three things.

First, "d", 0 is pointless to have when registering the callback (unless it is not 0 and some dynamic value instead). The stored value is provided by-value to the public function and you will not be able to change it inside the function.

Second, returning a non-zero value is sufficient to break the chain, even without handler_return. handler_return is only useful if you want to inspect the value returned from the previous handler, or if you want to return 0 from the function and stop the chain at the same time.

Third, the parameter introduced by handler_return is by-ref, so it should be defined with the ampersand as &break_hook_chain. Since the way you defined AntiFlood_OPC is different from the way you told PawnPlus to call it, playerid contains the address of the variable where you are supposed to store the result from the function (if any).

George480 commented 5 years ago

Oh, I thought I had to pass a value for the return handler. I also used & but because it was passing the value 0, it was printing wrong values so I removed it.

I tried now your way and without handler_return and both work. Thank you.