Skip to content

INTERACTION.md

Nexius edited this page Jun 3, 2025 · 14 revisions

Pawn-Lua interaction

amx allows developers to enrich their gamemodes and other scripts with Lua code, which is easier and more efficient to write than Pawn. To make this possible, a new Pawn function, lua was added to call Lua functions, and a Lua function called pawn correspondingly calls public Pawn functions.

A resource that uses the interaction functions will contain both one or more .amx files (<amx/> in meta.xml) and serverside MTA scripts (<script/>). Both Pawn and Lua scripts can only call other-language scripts that are in the same resource.

Registering prototypes

Before you can call a function with lua or pawn you need to define its prototype, which consists of the types of its arguments and return value. Each type corresponds to a single letter:

Letter Type Letter Type Letter Type
b boolean p player m menu
i integer v vehicle g gang zone
f floating point o object a 3D text label
s string u pickup k native marker
c color y actor z bot
t team x textdraw

Pawn functions are registered with amxRegisterPawnPrototypes:

native amxRegisterPawnPrototypes(const prototype[][]);

Lua functions are registered with amxRegisterLuaPrototypes:

bool amxRegisterLuaPrototypes(table prototypes)

Both functions associate a number of function names with their argument types and (optionally) return type. To specify a return type, prepend the function name with the type letter followed by a colon (:), for example: f:testfn. If you do not specify a return type (i.e. only specify the name, testfn), "i" will be assumed.

See the syntax sections of the two registration functions for the precise syntax to use.

Calling other-language functions

Use lua to call a Lua function from Pawn:

native lua(const fnName[], {Float,_}:...);

Use pawn to call a Pawn function from Lua:

variant pawn(string fnName, ...)

The functions have the same syntax: a string containing the name of the function, followed by the arguments to the function. amx takes care of any necessary argument and return value conversions: for example an .amx vehicle ID passed to lua will arrive in the Lua function as an MTA vehicle element, and vice versa (provided the correct prototype was registered for the Lua function).

Passing arguments by reference

It is possible to pass arguments by-reference from Pawn to Lua - however this is not possible in the opposite direction.

To make an argument be passed by reference, modifications in both the Lua function's prototype and body are necessary. In the prototype, prepend the type letter with a &. In the function's code, write _[argname] instead of argname for reading and writing the argument (argname holds the memory address in the .amx of the argument).

Cross-language calling limitations

Some limitations apply to cross-language calling.

  • Only scalar values (numbers, players, vehicles...) and strings can be passed as arguments; Pawn arrays and Lua tables are not supported.
  • Functions can only return scalar values (no strings or other arrays).
  • As stated in the previous section, by-reference arguments can only be passed from Pawn to Lua, not from Lua to Pawn.

Example

This example code demonstrates registering prototypes and calling other-language functions, with arguments passed by value and by reference.

test.pwn

#include <a_samp>
#include <a_amx>

main()
{
    new prototypes[][] =
    {
       "p:testfn", { "p", "f", "s" },
       ""
    };
    amxRegisterPawnPrototypes(prototypes);
}

public testfn(playerid, Float:f, str[])
{
    printf("pawn> testfn: %d %.1f %s", playerid, f, str);
    return playerid;
}

public OnGameModeInit()
{
    new
        vehicleid = CreateVehicle(415, 0.0, 0.0, 3.0, -90.0, 0, 1, 5000),
        vehicletype;

    // vehicletype is passed by reference
    new success = lua("getVehicleType", vehicleid, vehicletype, "Test text from Pawn");
    if (success) printf("pawn> Vehicle type: %d", vehicletype);

    SetGameModeText("Blank Script");
    AddPlayerClass(0, 1958.3783, 1343.1572, 15.3746, 269.1425, 0, 0, 0, 0, 0, 0);
    return 1;
}

public OnPlayerRequestClass(playerid, classid)
{
    SetPlayerPos(playerid, 1958.3783, 1343.1572, 15.3746);
    SetPlayerCameraPos(playerid, 1958.3783, 1343.1572, 15.3746);
    SetPlayerCameraLookAt(playerid, 1958.3783, 1343.1572, 15.3746);
    return 1;
}

test.lua

function getVehicleType(vehicle, pVehicleType, str)
    print('lua> ' .. str)
    print('lua> ' .. _[pVehicleType])
    local model = getElementModel(vehicle)
    if model then
        _[pVehicleType] = model
        return true
    else
        return false
    end
end

addEventHandler('onAMXStart', root,
    function()
        -- Note that we are calling pawn() from the onAMXStart event instead of in the main script body
        -- Calling it from the main body would fail as the Pawn functions haven't yet been registered at that point
        local player = pawn('testfn', getRandomPlayer(), 0.8, 'Test string from Lua')
        if player then
            print('lua> ' .. getClientName(player))
        else
            print('lua> No random player')
        end
    end
)

amxRegisterLuaPrototypes({
    ['b:getVehicleType'] = { 'v', '&i', 's' }
})

Sample output

lua> Test text from Pawn
lua> 0
pawn> Vehicle type: 415
pawn> testfn: 1 0.8 Test string from Lua
lua> arc_
Clone this wiki locally