[Tutorial] String Formatting
#1

Contents
  • Introduction
  • Declaration
  • Getting the players name
  • Formatting the message
  • Other placeholders
  • String manipulation
  • Final result
  • References
  • Conclusion
Introduction
In this tutorial you will learn how to format strings in the best optimized way.
I wrote this tutorial because I have seen a lot of people doing this in a lot of horrible ways.
I will now show you how to make a simple message when a player joins the server, showing the player's name.
This is just used as an example on how to format strings. Strings are some of the most common code bits in pawn scripting, and can be used in a lot of different ways. The purpose with this tutorial is not to learn you how to make a join message, but how to format strings correctly and as optimized as possible.
- Now let's get started! We will use the "OnPlayerConnect" callback.

Declaration
We will start by declaring 2 variables.
pawn Code:
new name[MAX_PLAYER_NAME], string[23 + MAX_PLAYER_NAME];
The variable called "name[MAX_PLAYER_NAME]" will hold the name of the player. The variable has an array (the "[MAX_PLAYER_NAME]"). MAX_PLAYER_NAME is the same as 24, which is the max length of player names in SA-MP.
The variable "string[23 + MAX_PLAYER_NAME]" will hold the message, that will be sent when a player joins. It has the array "[23 + MAX_PLAYER_NAME]", which is the same as 47 (23+24) <-- Explaination later

Getting the players name
Now we want to tell the script to get the name of the player who just connected.
The "GetPlayerName" function is just perfect for this part.
pawn Code:
GetPlayerName(playerid, name, sizeof(name));
We just told the script to get the player's name, and store it in the variable called "name" which we declared above.
The "sizeof(name)" is, as the word almost says itself - it gets the size of the array in the variable called "name" which is MAX_PLAYER_NAME (24). This will represent the length of the playername.
We don't have to use the "sizeof" function, we could also do like this:
pawn Code:
GetPlayerName(playerid, name, 24);
but i prefer using "sizeof".
We now have the player's name stored in the variable "name".

Formatting the message
Ok, now we have to format the message that will be sent when a player connects to the server.
pawn Code:
format(string, sizeof(string), "%s has joined the server", name);
This line will tell the script to format and store the message in the wariable "string" which we declared above.
I already explained what the "sizeof" function does - tells the length of the message is the same as the array size of the variable "string" which is 23 + MAX_PLAYER_NAME (23+24=47).

The reason why the array size is 23+24 is that "%s has joined the server" contains 22 characters including spaces, but excluding "%s". When formatting, there is something called "the null terminator" which means messages have the character '\0' (NULL) at the end. Thats why we need the array size to be bigger than the actual message (23 in stead of 22 - one for the null terminator).

The "%s" is a placeholder for the playername, which has the length of MAX_PLAYER_NAME. thats why we add "+ MAX_PLAYER_NAME" in the array, so the message has a limit of 47 characters when showed in-game, and when playernames can't be longer than 24 characters, the message will never need more than 47 characters.

In the end we are adding ", name" (which is the variable containing the playername).
This tells the script that the "%s" is a placeholder for the playername.

The last thing we need to do now is sending the message to all connected players.
pawn Code:
SendClientMessageToAll(0xFFFF00FF, string);
This line will send a client message to all players with our formatted message, stored in the variable "string".
The message will now look like this in-game when a player joins the server:
Code:
krogsgaard20 has joined the server
(If the playername is krogsgaard20, of course)

Other placeholders
The reason that we used "%s" as placeholder, is because our message is a string. A string is a special type of array, one which is used to hold multiple characters to create a word or sentence or other human readable text.
Here is a list of all placeholders:
Quote:

%b Inserts a number at this position in binary radix
%c Inserts a single character.
%d Inserts an integer (whole) number
%f Inserts a floating point number.
%i Inserts an integer.
%s Inserts a string.
%x Inserts a number in hexadecimal notation.
%% Inserts the literal '%'

String manipulation
(Thanks to Schurman)
There are many functions to manipulate strings, and also format them. Here's a list:

pawn Code:
bool: ispacked(const string[])
memcpy(dest[], const source[], index=0, numbytes, maxlength=sizeof dest)
strcat(dest[], const source[], maxlength=sizeof dest)
strcmp(const string1[], const string2[],bool: ignorecase=false, length=cellmax)
strcopy(dest[], const source[], maxlength=sizeof dest)
bool: strdel(string[], start, end)
bool: strequal(const string1[], const string2[],bool: ignorecase=false,length=cellmax)
strfind(const string[], const sub[],bool: ignorecase=false, index=0)
strformat(dest[], size=sizeof dest, bool: pack=false, const format[], . . . )
bool: strins(string[], const substr[], index,maxlength=sizeof string)
strlen(const string[])
strmid(dest[], const source[],start=0, end=cellmax,maxlength=sizeof dest)
strpack(dest[], const source[],maxlength=sizeof dest)
strunpack(dest[], const source[],maxlength=sizeof dest)
strval(const string[], index=0)
uudecode(dest[], const source[], maxlength=sizeof dest)
uuencode(dest[], const source[], numbytes, maxlength=sizeof dest)
valstr(dest[], value, bool: pack=false)
Final result
Here is the final result of the script that we created in this tutorial:
pawn Code:
public OnPlayerConnect(playerid)
{
    new name[MAX_PLAYER_NAME], string[23 + MAX_PLAYER_NAME];
    GetPlayerName(playerid, name, sizeof(name));
    format(string, sizeof(string), "%s has joined the server", name);
    SendClientMessageToAll(0xFFFF00FF, string);
    return 1;
}
References
https://sampwiki.blast.hk/wiki/Format

Conclusion
Remember to always make array sizes as small as possible.
An array is memory that can be accessed dynamically, and unused arrays will cause CPU or RAM lagg on the server.
What i am trying with this tutorial is getting rid of those horrible scripts with array sizes way too big.

A lot of people are using 256 in all their arrays, which makes no sense, because the maximum length of client messages is 128, but do not use 128 if the message you want to format is shorter.
Reply
#2

Looks good, but [23 + MAX_PLAYER_NAME] is very uncommon, better to use new string[128] but I think it is unique
Reply
#3

Quote:
Originally Posted by krogsgaard20
View Post
A lot of people are using 256 in all their arrays, which makes no sense, because the maximum length of client messages is 128, but do not use 128 if the message you want to format is shorter.
Agreed here. ^ Thanks for pointing out to users how to use strings and arrays. Very good tutorial
Reply
#4

Quote:
Originally Posted by grand.Theft.Otto
View Post
Looks good, but [23 + MAX_PLAYER_NAME] is very uncommon, better to use new string[128] but I think it is unique
The reason for using "23 + MAX_PLAYER_NAME" and not 128 is because the smaller array sizes, the better. 128 is way too much for our small message in this tutorial. Allways make arrays as small as possible.
Reply
#5

Quote:
Originally Posted by krogsgaard20
View Post
The reason for using "23 + MAX_PLAYER_NAME" and not 128 is because the smaller array sizes, the better. 128 is way too much for our small message in this tutorial. Allways make arrays as small as possible.
True, using a 128 array size is over than you need.

@ krogsgaard20,
To be honest, that tutorial taught me alot.
Good tutorial and good job!
Reply
#6

Nice, this tutorial will help new pawners alot.
Reply
#7

pawn Code:
#if defined MAX_STRING
    #undef MAX_STRING
    #define MAX_STRING 128
#endif
Maybe that code i provided could help
Reply
#8

Quote:
Originally Posted by krogsgaard20
View Post
An array is memory that can be accessed dynamically, and unused arrays will cause CPU lagg on the server.
I don't think they cause CPU lag. They just consume more RAM and it takes longer for your script to compile.

And I think you should also have explained a little about the other format placeholders. More often that not, I see people doing something like:

pawn Code:
if(minute <= 9)
{
    format(string, sizeof(string), "0%d", minute)
}
else
{
    format(string, sizeof(string), "%d", minute)
}
While you could just as easily write that in a single line using:

pawn Code:
format(string, sizeof(string), "%02d", minute);
Which will make sure the placeholder is 2 digits long and which will also pad it with zeros if it's not.
Reply
#9

Quote:
Originally Posted by Lorenc_
View Post
pawn Code:
#if defined MAX_STRING
    #undef MAX_STRING
    #define MAX_STRING 128
#endif
Maybe that code i provided could help
And whats the use of it?
Reply
#10

Quote:
Originally Posted by Vince
View Post
I don't think they cause CPU lag. They just consume more RAM and it takes longer for your script to compile.

And I think you should also have explained a little about the other format placeholders. More often that not, I see people doing something like:

pawn Code:
if(minute <= 9)
{
    format(string, sizeof(string), "0%d", minute)
}
else
{
    format(string, sizeof(string), "%d", minute)
}
While you could just as easily write that in a single line using:

pawn Code:
format(string, sizeof(string), "%02d", minute);
Which will make sure the placeholder is 2 digits long and which will also pad it with zeros if it's not.
Oh, thank you for mentioning. I forgot explaining about the other placeholders. I added a list of them all.
Reply
#11

Quote:
Originally Posted by Macluawn
View Post
And whats the use of it?
Mostly, people define MAX_STRING has 256, and use it everywhere, so this decreases it to 128. Get it?
Reply
#12

Most people don't even use MAX_STRING for their string lengths...
Reply
#13

Quote:
Originally Posted by Calg00ne
View Post
Most people don't even use MAX_STRING for their string lengths...
It's used a lot in GF edits, and people that use MAX_STRING mostly use 256.
Reply
#14

Quote:
Originally Posted by krogsgaard20
View Post
A lot of people are using 256 in all their arrays, which makes no sense, because the maximum length of client messages is 128, but do not use 128 if the message you want to format is shorter.
Wrong. Since colour formating was added in 0.3c the client messages can be up to 150 (possibly 156) characters long before they will not be sent at all.
Reply
#15

Quote:
Originally Posted by [03]Garsino
View Post
Wrong. Since colour formating was added in 0.3c the client messages can be up to 150 (possibly 156) characters long before they will not be sent at all.
Hmm, im not quite sure you are right. Ill test this
Reply
#16

Quote:
Originally Posted by sim_sima
View Post
Hmm, im not quite sure you are right. Ill test this
I had this issue with SendClientMessageToAll atleast (if you use too many colour tags but keep within the limit it wont send :/)
Reply
#17

Quote:
Originally Posted by [03]Garsino
View Post
I had this issue with SendClientMessageToAll atleast (if you use too many colour tags but keep within the limit it wont send :/)
Hmm, maybe you need bigger array sizes to hold the colours.

By the way; I tested this out, but without colour tagging, and i got 128 characters as max length, but i dont know if array sizes have to be bigger if you use colour tags
Reply
#18

you didn't mention what krogsgaard20 said at all, you just copied and pasted stuff from the wiki.

%02d or %02i will make sure there's 2 digits, like formatting 2 will become 02 and 20 will remain 20 because that's 2 digits

%.02f will cut a floating number 2 places so 3.141414 will become 3.14, note that this doesn't do rounding just cutting off

also iirc to show a percent sign you need %%%% not %%
Reply
#19

I will add-on to this.

STRING MANIPULATION
There are many functions to manipulate strings, and also format them. Here's a list:

pawn Code:
bool: ispacked(const string[])

memcpy(dest[], const source[], index=0, numbytes, maxlength=sizeof dest)

strcat(dest[], const source[], maxlength=sizeof dest)

strcmp(const string1[], const string2[],bool: ignorecase=false, length=cellmax)

strcopy(dest[], const source[], maxlength=sizeof dest)

bool: strdel(string[], start, end)

bool: strequal(const string1[], const string2[],bool: ignorecase=false,length=cellmax)

strfind(const string[], const sub[],bool: ignorecase=false, index=0)

strformat(dest[], size=sizeof dest, bool: pack=false, const format[], . . . )

bool: strins(string[], const substr[], index,maxlength=sizeof string)

strlen(const string[])

strmid(dest[], const source[],start=0, end=cellmax,maxlength=sizeof dest)

strpack(dest[], const source[],maxlength=sizeof dest)

strunpack(dest[], const source[],maxlength=sizeof dest)

strval(const string[], index=0)

uudecode(dest[], const source[], maxlength=sizeof dest)

uuencode(dest[], const source[], numbytes, maxlength=sizeof dest)

valstr(dest[], value, bool: pack=false)
FORMATTING
%s - string

%d - decimal, can be used as an integer also (%i)

%f - FLOAT module.

%i - integer, can be used with %d
Reply
#20

Thank you everyone for your response.
Reply


Forum Jump:


Users browsing this thread: 3 Guest(s)