[Plugin] PawnPlus
#1

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:
Code:
SendClientMessage(playerid, -1, "Loading...");
await LoadPlayerAsync(playerid);
SendClientMessage(playerid, -1, "Loaded!");
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:
Code:
native SendClientMessageStr(playerid, color, AmxString:message) = SendClientMessage;

SendClientMessageStr(playerid, -1, @("Hello, id ") + str_val(playerid));
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.

Code:
print("begin");
threaded(sync_explicit)
{
    for(new i = 0; i < 10000; i++) printf("%d", i);
}
print("end");
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.
Reply
#2

Interesting.
Reply
#3

Great features, i haven't tried it yet but looks good.
Reply
#4

Nice
Reply
#5

Nice implementation! Was expecting this to use a custom compiler, but it’s actually just a plug-in! Neat!
Reply
#6

Love it, nice work.
Reply
#7

Nice work
Reply
#8

Don't understand the thread stuff
Quote:

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.

So the server will still work as fine, but the AMX script (the gamemode) behind it will stop working until the block will finish computing.
So it works in parallel with the server not the gamemode script.
Reply
#9

Yes, it works in parallel with the server. I was trying to make it work for the script too, but it is more tricky.
Reply
#10

Will try this, nice!
Reply
#11

IS4, I can not find the problem:
Code:
new String:msg = str_new("~p~")+str_val(speed)+str_new(" ~b~km/h");
PlayerTextDrawSetString(playerid,PlayerInfo[playerid][speedometru],msg);
me send error
Code:
: error 035: argument type mismatch (argument 3) line error PlayerTextDrawSetString(playerid,PlayerInfo[playerid][speedometru],msg);
Reply
#12

Quote:
Originally Posted by Moldova
View Post
IS4, I can not find the problem:
Code:
new String:msg = str_new("~p~")+str_val(speed)+str_new(" ~b~km/h");
PlayerTextDrawSetString(playerid,PlayerInfo[playerid][speedometru],msg);
me send error
Code:
: error 035: argument type mismatch (argument 3) line error PlayerTextDrawSetString(playerid,PlayerInfo[playerid][speedometru],msg);
You cannot call a standard native function directly with a dynamic string argument, that is simply not possible in Pawn. However, you can easily port the native function to use the dynamic string:
Code:
native PlayerTextDrawSetStringStr(playerid, PlayerText:text, AmxString:string) = PlayerTextDrawSetString;
Calling PlayerTextDrawSetStringStr with a String parameter will work.
Reply
#13

Can you please check map_remove?

for an example:
Code:
new Map:m = map_new();
map_add(m, 1, 123);
map_add(m, 2, 456);

map_remove(m, 1); // returns false
printf("%d", map_get(1)); // prints, 123
Reply
#14

Quote:
Originally Posted by Ivan_Ino
View Post
Can you please check map_remove?

for an example:
Code:
new Map:m = map_new();
map_add(m, 1, 123);
map_add(m, 2, 456);

map_remove(m, 1); // returns false
printf("%d", map_get(1)); // prints, 123
Thanks for reporting; there has been a bug in map_remove.

0.7.2 released to fix this issue. I have also added another set of functions, map_has_key, to check if the specified map contains a given key.
Reply
#15

Quote:
Originally Posted by IS4
View Post
Thanks for reporting; there has been a bug in map_remove.

0.7.2 released to fix this issue. I have also added another set of functions, map_has_key, to check if the specified map contains a given key.
nice job! it was fast and efficient
Reply
#16

This is amaizing stuff but i have a question. I love threads because they offer great things but let say i would do a textdraw that show distance between 2 points(a player and enything else) i can perform the calculations in the thread and meantime show the resultated distance in that textdraw? I mean, its safe to do so?

Edit: Like calculations inside a timer/constant update function so the distance will shown acording to the player position
Reply
#17

Quote:
Originally Posted by Banditul18
View Post
This is amaizing stuff but i have a question. I love threads because they offer great things but let say i would do a textdraw that show distance between 2 points(a player and enything else) i can perform the calculations in the thread and meantime show the resultated distance in that textdraw? I mean, its safe to do so?

Edit: Like calculations inside a timer/constant update function so the distance will shown acording to the player position
I wouldn't recommend that. GetPlayerPos and TextDrawSetString are not thread-safe. Threads in PawnPlus are meant to be used for CPU-intensive code, so if you simply want to calculate a distance, you don't need to use threads. OnPlayerUpdate is sufficient for this, and tasks are better suited for cooperative multitasking.
Reply
#18

Just need better and more documentation and examples...
specially about configuring it and using such APIs:

Code:
native String:str_format(const format[], {StringTags,Float,_}:...);
native String:str_format_s(StringTag:format, {StringTags,Float,_}:...);
Reply
#19

PHP Code:
          ShowPlayerDialogString(playeridDIALOG_ID_SP_TD_POSITIONDIALOG_STYLE_INPUT,
            
str_new("Change textdraw string content"),
            
str_new("Please enter the location in width and height format.\n \n\
              "
COLOR_DEFAULT"Good to know:\n\
              "
COLOR_HIGHLIGHT" - If you enter a height less than 1.0, only the shadow will be visible.\n\
               - If the coordination is off your visual screen, only the shadow will be visible.\n\
               - The width and height are estimated in float (decimal) values in a canvas of 640x480.\n\
               - You are the only one who will use this texdraw, so you can design it to fit your resolution perfectly.\n \n\
              "
COLOR_DEFAULT"Current position: "COLOR_HIGHLIGHT) +
            
str_float(characterSpeedometerTextdraws[playerid][profile][textdraw][speedometerPosWidth]) + str_new("x") +
            
str_float(characterSpeedometerTextdraws[playerid][profile][textdraw][speedometerPosHeight]),
            
COLOR_HIGHLIGHT"Submit""Back"); 

Not really sure why str_float() (shown in the snippet above) is undefined.

Code:
../include/vehicles/vehicleSpeedometers.inc(187) -- (197) : error 017: undefined symbol "str_float"
Pawn compiler 3.2.3664      Copyright © 1997-2017, ITB CompuPhase


1 Error.
[Finished in 0.6s]
Reply
#20

Version 0.8 released!
  • Tasks can store an error/exception state, can be used to signal cancellation or other exceptional events. Waiting for any result or error is performed via task_wait (now used together with task_get_result in task_await).
  • Iterators completely reworked, now are much safer and more powerful, allow for inserting or erasing values, and keep track of their collection's lifetime and modifications.
  • Tag system enhanced with dynamically defined tags and tag operations which can be called on values or variants. Properly defined tag operations can automatically collect unused objects or isolate their data. Tag operations can be invoked dynamically.
  • Minor additions to string and AMX functions.
  • AMX is fully cleaned after saving the sleep state, the stack no longer leaks.
  • String coercion operators fixed and definable with PP_SYNTAX_STRING_OP.
Quote:
Originally Posted by Netherland
View Post
Just need better and more documentation and examples...
specially about configuring it and using such APIs:
Documentation for most of the functions is available on the GitHub wiki. If you are unsure about a specific feature/function/parameter, do not be afraid to ask here or on GitHub.

Quote:
Originally Posted by DBZdabIt3Bro7
View Post
Not really sure why str_float() (shown in the snippet above) is undefined.
Sorry for that, I merged str_int and str_float into str_val versions ago and forgot to remove the original functions in other places.

I also notice you use str_new for constant strings. This is okay, but it computes the size of the string each time you use it. You may use str_new_static which determines the size at compile-time via sizeof.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)