Skip to content

Examples

IllidanS4 edited this page Mar 9, 2018 · 8 revisions

Strings

Creating, concatenating, and passing dynamic strings

// Modify SendClientMessageToAll(color, const message[]), replacing any occurences of "const x[]" with "AmxString:x".
native SendClientMessageToAllStr(color, AmxString:message) = SendClientMessageToAll;

// Creating a dynamic string from a string buffer.
stock String:GetPlayerNameStr(playerid)
{
    new name[MAX_PLAYER_NAME];
    GetPlayerName(playerid, name, sizeof(name));
    return str_new(name);
}

public OnPlayerConnect(playerid)
{
    // Dynamic strings are tagged "String". The + operator can be used to concat them. str_val converts any value to a string.
    new String:name = GetPlayerNameStr(playerid);
    new String:msg = str_new("Player ")+name+str_new(" (")+str_val(playerid)+str_new(") has joined the server.");
    
    // Calls SendClientMessageToAll but passes a dynamic string instead of an array as the second parameter.
    SendClientMessageToAllStr(-1, msg);
}

Global vs. local strings

native print_s(AmxString:string) = print;

// "GlobalString" tag denoted a global string, whose lifetime can only end with a call to str_free.
// In contrast, local strings (tagged "String") are garbage collected as soon as the top-level callback ends.
new GlobalString:str;

public OnFilterScriptInit()
{
    str = str_new("Goodbye world!");
    // The lifetime of "str" would end here were it tagged "String". In this case however, the assignment
    // hides a call to str_to_global which moves the string to the global string pool (no copying involved).
}

public OnFilterScriptExit()
{
    print_s(str);
    // If you intend to work with the string before freeing it, consider using str_to_local instead.
    // That way, your script can safely return and forget the string, and it is guaranteed to be freed.
    str_free(str);
    // Set the variable to STRING_NULL so that it doesn't create a dangling pointer.
    // STRING_NULL is a special kind of a (global) string that can be used by any function expecting a string,
    // but its value is immutable.
    str = STRING_NULL;
}

Split and compare on dynamic strings

native SendClientMessageStr(playerid, color, AmxString:message) = SendClientMessage;

public OnPlayerCommandText(playerid, cmdtext[])
{
    new String:cmd = str_new(cmdtext);
    
    new String:name = cmd;
    new String:args = STRING_NULL;
    
    // Finding a space in a string and using it to split it.
    new len = str_len(cmd);
    for(new i = 0; i < len; i++)
    {
        if(str_getc(cmd, i) == ' ')
        {
            name = str_sub(cmd, 0, i);
            args = str_sub(cmd, i+1);
            break;
        }
    }
    
    // Comparing with a string value.
    if(name == str_new("/test"))
    {
        SendClientMessageStr(playerid, -1, args);
        return true;
    }
    return false;
}

Tasks

Waiting a specified amount of time

stock Countdown()
{
    SendClientMessageToAll(-1, "3");
    wait_ms(1000); // Non-blocking sleep (i.e. there is no code running and checking the time).
    SendClientMessageToAll(-1, "2");
    wait_ms(1000); // await task_ms(1000); can be also used
    SendClientMessageToAll(-1, "1");
    wait_ms(1000);
    SendClientMessageToAll(-1, "0");
}
// Note: Waiting blocks the execution of the whole code up to the first public function. To make a function that returns immediately, use CallLocalFunction.

Returning from an asynchronous function.

public OnFilterScriptInit()
{
    new ret = CallLocalFunction(#Func, "");
    printf("%d", ret);
}

forward Func();
public Func()
{
    yield 12;
    // The execution of the code is paused every time a call to wait_ms occurs, but the calling function still
    // expects a result. Call yield to provide the immediate result to the calling function.
    wait_ms(1000);
    return 13; // This value is lost. 12 is printed instead (immediately on script init).
}

Waiting for a user-defined event

// A MoveObject wrapper returning a waitable task, representing the event of finishing the movement of a certain object.
stock task:MoveObjectTask(objectid, Float:X, Float:Y, Float:Z, Float:Speed, Float:RotX = -1000.0, Float:RotY = -1000.0, Float:RotZ = -1000.0)
{
    // A task represents an abstract process which can be finished, optionally having a result.
    // task_new creates a new empty (unfinished) task.
    new task:t = task_new();

    // This registers a new handler for the OnObjectMoved callback. Public function SingleFireObjectTask
    // will be called every time OnObjectMoved is to be called (before it).
    // Additional parameters can be prepended to the handler, whose values are passed to pawn_register_callback.
    // Format specifier "e" doesn't require its value and is translated to the ID of the handler instance
    // (the same ID is also returned from pawn_register_callback).
    pawn_register_callback(#OnObjectMoved, #SingleFireObjectTask, "edd", t, objectid);
    MoveObject(objectid, X, Y, Z, Speed, RotX, RotY, RotZ);
    return t;
}

stock ObjectTest()
{
    new obj = CreateObject(19300, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
    print("Object created!");
    // awaits pauses the execution. The function will be executed when the task is completed.
    await MoveObjectTask(obj, 0.0, 0.0, 10.0, 5.0);
    print("Object moved!");
}

// The new handler of OnObjectMoved is extended with three new parameters (id, task, obj)
// whose values are determined in pawn_register_callback. The rest of the parameters comes
// from the callback itself.

// The point of this handler is to compare the third argument with the fourth, complete a task
// in the case of success, and unregister itself. It can be used for any single entity event
// (players, vehicles, actors, objects etc.).
forward SingleFireObjectTask(callback:id, task:task, obj, objectid);
public SingleFireObjectTask(callback:id, task:task, obj, objectid)
{
    if(obj == objectid)
    {
        // The handler is unregistered if the handled object triggered the callback.
        pawn_unregister_callback(id);
        // The respective task is set to completed.
        task_set_result(task, objectid);
    }
}

Combining strings and tasks

public OnFilterScriptInit()
{
    new String:str = str_new("Hello world!");
    str_to_global(str);
    // Since the lifetime of the string would normally end here (wait_ms returns from the callback),
    // it has to be moved to the global pool. You don't have to assign it to any global variable
    // because the instance remains the same and the reference will be stored in the AMX memory.
    wait_ms(1000);
    // After the wait, better move the string to the local pool again in case you forget to free it.
    str_to_local(str);
    print_s(str);
}

Waiting for a player return (by IP address)

stock task:WhenPlayerReturns(ip[])
{
    new task:t = task_new();
    pawn_register_callback(#OnPlayerConnect, #SingleFireIpTaskHandler, "eds", t, ip); //"e" does not need an argument
    return t;
}

// A handler that checks the IP address of a player and compares it to the one it is bound to.
forward SingleFireIpTaskHandler(callback:id, task:task, ip[], playerid);
public SingleFireIpTaskHandler(callback:id, task:task, ip[], playerid)
{
    new ip2[16];
    GetPlayerIp(playerid, ip2, sizeof ip2);
    if(!strcmp(ip, ip2))
    {
        pawn_unregister_callback(id);
        task_set_result(task, playerid);
    }
}

public OnPlayerConnect(playerid)
{
    new ip[16];
    GetPlayerIp(playerid, ip, sizeof ip);
    new task:t = task_new();
    pawn_register_callback(#OnPlayerDisconnect, #SingleFireIdTaskHandler, "eds", t, playerid);
    await t;
    // GetPlayerIp does not work on disconnect, so it is temporarily stored here.
    printf("%d disconnected!", playerid);
    playerid = await(WhenPlayerReturns(ip));
    printf("%d is back!", playerid);
}

forward SingleFireIdTaskHandler(callback:id, task:task, stored_id, test_id);
public SingleFireIdTaskHandler(callback:id, task:task, stored_id, test_id)
{
    if(stored_id == test_id)
    {
        pawn_unregister_callback(id);
        task_set_result(task, test_id);
    }
}
Clone this wiki locally