26.04.2018, 09:59
(
Last edited by IS4; 08/08/2018 at 01:45 AM.
)
What is this about?
PawnPlus enhances the Pawn programming language and the standard library with new syntax, new functions and mechanisms which allow coding in Pawn using better techniques akin to modern programming languages.
Asynchronous programming
Having to wait for a certain event forces you to spread the code over several functions, making it harder to maintain. Using PawnPlus, waiting for a specific event is as easy as calling one function, be it a result from a MySQL query, a completed dialog, or just sleeping in milliseconds. The wait is non-blocking and no threading is involved, but the execution of your code will continue when the event is completed. Using PawnPlus can reduce loading player data with MySQL to this:
The await statement pauses the execution of the current code until the task it is provided with is completed. In the meantime, any other code can run, be it from the same script or another. It is very easy to implement a function like LoadPlayerAsync by dynamically registering a callback handler to any callback, making virtually any event convertible to tasks. More usage and examples.
Dynamically-allocated strings
Strings in Pawn are cumbersome at best. Having to always specify the size of the string buffer leads to either wasting space, allocating more memory for the string than necessary, or trimmed strings at best and buffer overflow at worst when the target buffer is smaller than needed. PawnPlus adds dynamic strings with their memory and size completely governed by the plugin, freeing you of needing to always have that on mind. They are also garbage-collected, meaning you don't have to free any intermediate values when you are done with them, preventing common memory leaks.
Since the memory assigned to the string is not stored in the AMX machine, the string value can be safely passed or returned from functions.
PawnPlus strings are not null-terminated, meaning they can even store the null character, making them viable option for storing binary data in memory. Their characters are cell-sized, making them able to hold any array. Most operations on dynamic strings are faster than on normal strings, because the length of the string is always available.
Moreover, it is very easy to adapt almost any SA-MP or plugin function to take a PawnPlus string instead. For example, SendClientMessage can be modified in this way:
The @ operator creates a new dynamic string from a string literal. + can be used to create a new string by joining two strings together. More examples.
Containers
PawnPlus adds the Map and the List containers (implemented as std::unordered_map and std::vector, respectively). These containers support standard operations like adding or removing elements, lookup and iterating. Moreover, inserting any value into the container also stores its tag, so it is possible to access elements in the containers in a type-safe way.
These containers are heterogenous, meaning they can store any number of values of any type, even arrays. The tag of the value can be also obtained. Lists are indexed by integers starting from 0, while maps can use any value as a key (even strings/arrays), and the tag of the key must also be correct when obtaining the value. Since the map is implemented using a hash table, accessing any key can be quite fast, because there is no need to traverse the whole map.
It is also really easy to pass objects of any type around, because this plugin also adds a type called variant. A variant is an object that can store a single cell, or a whole array, together with its tag. It can be for example obtained from a list and inserted into another list, without having to deal with the type. str_val also works with the variant and is able to correctly format the stored value. Variants can be even passed to native functions by calling pawn_call_native together with the "v" format specifier. This function also accepts a list ("l") and handles it as a variable number of arguments.
Threads
Even though asynchronous programming is preferred, there could also be a need for performing a parallel computation. However, since having to work with threads and synchronisation primitives is non-trivial, causing common code errors, this plugin uses a different approach. Any code that needs to be executed in parallel with the server is contained in a threaded block, which ensures that the code runs in parallel with the server, but once the block ends, the execution continues as normal.
A threaded block is initialized with synchronisation flags, specifying the nature of the thread and the way calls are handled. More information here.
Since the AMX machine is not designed to handle multiple threads accessing it, only at most one thread can execute the code in a given AMX script. However, the server will still not be blocked.
Callbacks and hooking
Several new functions were also added, allowing to register and unregister callbacks at runtime without having to actually place the public function in the code, which is useful for creating tasks. You can also call any native function (without specifying it in Pawn) with any number of arguments, and you can also hook any native function with whatever code you wish, modifying the arguments or the return value.
More information on the wiki. The plugin is currently in a pre-release stage, but all the features are implemented and should be fully working.
PawnPlus enhances the Pawn programming language and the standard library with new syntax, new functions and mechanisms which allow coding in Pawn using better techniques akin to modern programming languages.
Asynchronous programming
Having to wait for a certain event forces you to spread the code over several functions, making it harder to maintain. Using PawnPlus, waiting for a specific event is as easy as calling one function, be it a result from a MySQL query, a completed dialog, or just sleeping in milliseconds. The wait is non-blocking and no threading is involved, but the execution of your code will continue when the event is completed. Using PawnPlus can reduce loading player data with MySQL to this:
Code:
SendClientMessage(playerid, -1, "Loading..."); await LoadPlayerAsync(playerid); SendClientMessage(playerid, -1, "Loaded!");
Dynamically-allocated strings
Strings in Pawn are cumbersome at best. Having to always specify the size of the string buffer leads to either wasting space, allocating more memory for the string than necessary, or trimmed strings at best and buffer overflow at worst when the target buffer is smaller than needed. PawnPlus adds dynamic strings with their memory and size completely governed by the plugin, freeing you of needing to always have that on mind. They are also garbage-collected, meaning you don't have to free any intermediate values when you are done with them, preventing common memory leaks.
Since the memory assigned to the string is not stored in the AMX machine, the string value can be safely passed or returned from functions.
PawnPlus strings are not null-terminated, meaning they can even store the null character, making them viable option for storing binary data in memory. Their characters are cell-sized, making them able to hold any array. Most operations on dynamic strings are faster than on normal strings, because the length of the string is always available.
Moreover, it is very easy to adapt almost any SA-MP or plugin function to take a PawnPlus string instead. For example, SendClientMessage can be modified in this way:
Code:
native SendClientMessageStr(playerid, color, AmxString:message) = SendClientMessage; SendClientMessageStr(playerid, -1, @("Hello, id ") + str_val(playerid));
Containers
PawnPlus adds the Map and the List containers (implemented as std::unordered_map and std::vector, respectively). These containers support standard operations like adding or removing elements, lookup and iterating. Moreover, inserting any value into the container also stores its tag, so it is possible to access elements in the containers in a type-safe way.
These containers are heterogenous, meaning they can store any number of values of any type, even arrays. The tag of the value can be also obtained. Lists are indexed by integers starting from 0, while maps can use any value as a key (even strings/arrays), and the tag of the key must also be correct when obtaining the value. Since the map is implemented using a hash table, accessing any key can be quite fast, because there is no need to traverse the whole map.
It is also really easy to pass objects of any type around, because this plugin also adds a type called variant. A variant is an object that can store a single cell, or a whole array, together with its tag. It can be for example obtained from a list and inserted into another list, without having to deal with the type. str_val also works with the variant and is able to correctly format the stored value. Variants can be even passed to native functions by calling pawn_call_native together with the "v" format specifier. This function also accepts a list ("l") and handles it as a variable number of arguments.
Threads
Even though asynchronous programming is preferred, there could also be a need for performing a parallel computation. However, since having to work with threads and synchronisation primitives is non-trivial, causing common code errors, this plugin uses a different approach. Any code that needs to be executed in parallel with the server is contained in a threaded block, which ensures that the code runs in parallel with the server, but once the block ends, the execution continues as normal.
Code:
print("begin"); threaded(sync_explicit) { for(new i = 0; i < 10000; i++) printf("%d", i); } print("end");
Since the AMX machine is not designed to handle multiple threads accessing it, only at most one thread can execute the code in a given AMX script. However, the server will still not be blocked.
Callbacks and hooking
Several new functions were also added, allowing to register and unregister callbacks at runtime without having to actually place the public function in the code, which is useful for creating tasks. You can also call any native function (without specifying it in Pawn) with any number of arguments, and you can also hook any native function with whatever code you wish, modifying the arguments or the return value.
More information on the wiki. The plugin is currently in a pre-release stage, but all the features are implemented and should be fully working.