open.mp Plugin Development Guide.txt

(28 KB) Pobierz
Registering natives

//An array of the functions we wish to register with the abstract machine.
AMX_NATIVE_INFO PluginNatives[] =
{
    //Here we specify our native functions information and terminate the array with two null values.
    {"HelloWorld", HelloWorld},
    {0, 0}
};

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad( AMX *amx )
{
    //Here we register our natives to the abstract machine. Note how we're using -1. Normally this would have to be the number of
    //functions we're registering, but since we terminated the array with two null values, we can specify -1.
    return amx_Register(amx, PluginNatives, -1);
}

Getting a string and its length

//This function demonstrates: how to get a string (and its length) from PAWN.
//PAWN native: native PrintPawnString(const str[]);
cell AMX_NATIVE_CALL PrintPawnString(AMX* amx, cell* params)
{
    int
        len = NULL,
        ret = NULL;

    cell *addr  = NULL;

    //Get the address of our string param (str) and then get its length
    amx_GetAddr(amx, params[1], &addr);
    amx_StrLen(addr, &len);

    //if the length of input isnt 0
    if(len)
    {
        //We increase len because we want to make room for the terminating null char when we allocate memory.
        //Also because GetString's size parameter counts the null chracter, we have to specify the length
        //of the string + 1; otherwise our string will be truncated to make room for the null char (we'd lose 1 character).
        len++;

        //Allocate memory to hold our string we're passing (str) and then "get" the string using our allocated memory to store it.
        char* text = new char[ len ];
        amx_GetString(text, addr, 0, len);

        //Use logprintf to print out string (text). We dont use std::cout because it doesnt write to the server log (only the console).
        logprintf(text);

        //Deallocate our memory...
        delete[] text;
    }
    return 1;
}

Alternative way to get a string

I decided to add this section because i know people are going to be tempted by the simplicity of it, and i think they should know the risks and problems that can come from it.

//This function demonstrates: an alternative method to getting strings from pawn- and the possible risks that come with it.
//PAWN native: native PrintPawnString2(const str[]);
cell AMX_NATIVE_CALL PrintPawnString2(AMX* amx, cell* params)
{
    //This method is NOT recomended as the amx_StrParam macro uses the alloca function which is NOT a standard in C, OR C++.
    //Using this method comes with risks of overflowing the stack (If you allocate large amounts of memory) and also
    //gives you the risk of bugs (this function is machine AND compiler dependent- some implementations are said to be bugged).

    char* text = NULL;
    amx_StrParam(amx, params[1], text);

    //Check if text is null
    if(text != NULL)
    {
        //Use logprintf to print out string (text). We dont use std::cout because it doesnt write to the server log (only the console).
        logprintf(text);
    }
    return 1;
}

How to set a string

//This function demonstrates: how to modify a PAWN string.
//PAWN native: native SetPawnString(str[], len = sizeof(str));
cell AMX_NATIVE_CALL SetPawnString(AMX* amx, cell* params)
{
    const string message = "This is a string from C/++!!";
    cell* addr = NULL;

    //Get the address of our string parameter (str) and store our message
    amx_GetAddr(amx, params[1], &addr);
    amx_SetString(addr, message.c_str(), 0, 0, params[2]);
    return 1;
}

Casting and returning floats

//This function demonstrates: how to cast a float to a PAWN float, and return it.
//PAWN native: native Float:ReturnPawnFloatVal();
cell AMX_NATIVE_CALL ReturnPawnFloatVal(AMX* amx, cell* params)
{
    //Since PAWN is a typeless language it stores everything as a 32bit integer and relies on tags to handle special data.
    //A floating point number is no exception to this; It's a still 32bit int, but it has a Float tag to show that it shouldnt
    //be treated like an regular integer. So how do we convert a float to an 32bit integer (for PAWN) without losing data?
    //The answer is the amx_ftoc macro!

    //The macro amx_ftoc type casts a float into a cell while preserving its bit pattern (amx_ctof does the inverse).
    const float f = 22.624f;
    return amx_ftoc(f);
}

Passing parameters by reference

//This function demonstrates: How to pass parameters by reference.
//PAWN native: native SetPawnReferenceVars(&value1, &Float:value2);
cell AMX_NATIVE_CALL SetPawnReferenceVars(AMX* amx, cell* params)
{
    const int val = 65;
    const float val2 = 84.54f;

    cell* addr[2] = {NULL, NULL};

    //Get the addresses of "value1" and "value2"
    amx_GetAddr(amx, params[1], &addr[0]);
    amx_GetAddr(amx, params[2], &addr[1]);

    //Dereference our pointers and assign our values. Remember to ALWAYS use the macro "amx_ftoc" to convert floats into
    //cells (the appropriate float format for PAWN)!
    *addr[0] = val;
    *addr[1] = amx_ftoc(val2);

    return 1;
}

Getting and modding array values

//This function demonstrates: how to get and modify array values.
//PAWN native: native PrintPawnArray(arr[], size = sizeof(arr));
cell AMX_NATIVE_CALL PrintPawnArray(AMX* amx, cell* params)
{
    //Make sure there's something to print...
    if(params[2] > 0)
    {
        cell* addr = NULL;

        //Get the address of the first value in our PAWN array.
        amx_GetAddr(amx, params[1], &addr);

        for(int i = 0, l = params[2]; i < l; i++)
        {
            //This is pretty straight forward: We dereference the addr pointer to get our value to print.
            //You should know this already, but arrays and pointers are almost the same thing, so we can use pointer
            //arithmetic to add an offset OR just use the subscript operator (in the end *(addr+1) and addr[1] are the same).
            logprintf("arr[%d] = %d", i, *(addr + i));

            //If you wanted to modify the array you would just change its value by dereferencing addr and assigning a new value.
            //You should know this as well, im just adding it in for completeness. Here we change the first value of our array
            //to 5 (Note: Since its the first value, no offset it used).

            // *(addr) = 5;
        }
    }
    return 1;
}

Call a callback

//This function demonstrates: How to call a callback that is in a PAWN script.
//PAWN native: native EmitPawnCallback();
cell AMX_NATIVE_CALL EmitPawnCallback(AMX* amx, cell* params)
{
    int idx;

    const cell var = 3;
    const cell arr[] = {100, 4, 33};
    const string str = "Some random message from C++.";


    //Pawn callback: forward OnPawnCallbackEmitted(var, arr[], str[]);
    //Find our callback and store its place in the public function table (it's index) into our idx var.
    if(!amx_FindPublic(amx, "OnPawnCallbackEmitted", &idx))
    {
        cell
            ret,
            addr;

        //Here we push our arguments to our function. Note that if the function has multiple arguments you have to push your
        //values in reverse order! Thats why we're pushing the string first, then the array, and finally our integer.

        amx_PushString(amx, &addr, NULL, str.c_str(), NULL, NULL);
        //amx_PushArray(amx, NULL, NULL, arr, sizeof(arr) / sizeof(cell));

        cell
            amx_addr,
            *phys_addr;

        //For some reason amx_PushArray seems to be crashing the server, and i have NO idea why. My usage should be completely
        //valid judging from the implementers guide, and the code itself. Since the function isnt working we'll have to
        //use the old method and allocate the memory, set it, and push it all ourselves. This is pretty straight forward. We
        //allocate memory on the heap using amx_Allot (this returns 2 addresses- one of the location in the abstract machine
        //(amx_addr), and one relative to the actual server's address space (phsy_addr - which we can use in C++)). Once the
        //memory is allocated we use memcpy to copy the memory from our array to our phys_addr address location.
        amx_Allot(amx, sizeof(arr) / sizeof(cell), &amx_addr, &phys_addr);
        memcpy(phys_addr, arr, sizeof(arr));
        amx_Push(amx, amx_addr);

        //Push our integer value
        amx_Push(amx, var);

        //Execute our function using our previously obtained idx var.
        //Note: This function's second parameter is what the callback returned (Can be NULL if you're not interested in return values).
        amx_Exec(amx, &ret, idx);

        //Release our memory that we allocated. The function amx_Alloc allocates memory on the heap in the abstract machine.
        //The functions amx_PushString and amx_PushArray both use this function internally so you have to release the memory every time
        //you use one of those functions. NOTE: We used both amx_PushString and amx_PushArray, and yet we only have ONE release call.
        //This is because memory on the heap is allocated in ascending order! amx_Release release all the memory above a certain point
        //(the second parameter, amx_addr - which is our addr variable). Since it does this we ONLY store the address from the amx_PushString
        //call, as it'll delete everything from that point on.
        amx_Release(amx, addr);

        //Print the return value (for completeness).
        logprintf("EmitPawnCallback NOTE:  OnPawnCallbackEmitted callback returned %d!", ret);

    }
    return 1;

Calling nati...
Zgłoś jeśli naruszono regulamin