11.08.2010, 13:39
(
Last edited by Lenny the Cup; 11/08/2010 at 08:26 PM.
Reason: https://sampforum.blast.hk/showthread.php?pid=787733#pid787733
)
Following is a "tutorial" of what you should make sure you know, and even though it does look really boring this is some very useful piece of information that will help you to get better much faster rather than just fumbling around "in the dark", not knowing what stuff actually means.
I've put this together basing it from a C++ tutorial, but I have modified many parts to make sure it applies to pawn, and pawn only.
If you have more questions and/or concerns (Or if you think something is wrong!), feel free to contact me via PM if you don't want to post a reply I wrote this primarily for people that were interested in scripting on LS-RP, and thought why not give people here a chance too!
Structure of a script
Probably the best way to start learning a programming language is by writing a program. Therefore, here is our first pawn gamemode:
The first panel (with code tags) shows the script for our first gamemode. The second one (with quote tags) shows the result of the program once compiled and executed.
This gamemode is the typical program that programmer apprentices write for the first time, and its result is the printing on screen of the "Hello World!" sentence. It is one of the simplest gamemode that can be written in pawn, but it already contains the fundamental components that every pawn script has. We are going to look line by line at the code we have just written:
// my first gamemode
You may have noticed that not all the lines of this gamemode perform actions when the code is executed. There were lines containing only comments (those beginning by //). There were lines with directives for the compiler's preprocessor (those beginning by #). Then there were lines that began the declaration of a function (in this case, the OnGameModeInit function) and, finally lines with statements (like the insertion into print), which were all included within the block delimited by the braces ({ }) of the function.
The program has been structured in different lines in order to be more readable, but in pawn, we do not have strict rules on how to separate instructions in different lines. For example, instead of
We could have written:
All in just one line and this would have had exactly the same meaning as the previous code.
In pawn, the separation between statements is specified with an ending semicolon ( ; ) at the end of each one, so the separation in different lines does not matter at all for this purpose. We can write many statements per line or write a single statement that takes many script lines. The division of code in different lines serves only to make it more legible and schematic for the humans that may read it.
Let us add an additional instruction to our first gamemode:
In this case, we performed two insertions into print in two different statements. Once again, the separation in different lines of code has been done just to give greater readability to the program, since OnGameModeInit could have been perfectly valid defined this way:
We were also free to divide the code into more lines if we considered it more convenient:
And the result would again have been exactly the same as in the previous examples.
Preprocessor directives (those that begin by #) are out of this general rule since they are not statements. They are lines read and processed by the preprocessor and do not produce any code by themselves. Preprocessor directives must be specified in their own line and do not have to end with a semicolon ( ; ).
Comments
Comments are parts of the script disregarded by the compiler. They simply do nothing. Their purpose is only to allow the scripter to insert notes or descriptions embedded within the source code.
pawn supports two ways to insert comments:
The first of them, known as line comment, discards everything from where the pair of slash signs (//) is found up to the end of that same line. The second one, known as block comment, discards everything between the /* characters and the first appearance of the */ characters, with the possibility of including more than one line.
We are going to add comments to our second program:
If you include comments within the script of your gamemodes or filterscripts without using the comment characters combinations //, /* or */, the compiler will take them as if they were pawn expressions, most likely causing one or several error messages when you compile it.
Variables and Data Types
The usefulness of the "Hello World" gamemode shown in the previous section is quite questionable. We had to write several lines of script, compile it, and then execute the resulting program just to obtain a simple sentence written on the screen as result. It certainly would have been much faster to type the output sentence by ourselves. However, programming is not limited only to printing simple texts on the screen. In order to go a little further on and to become able to write scripts that perform useful tasks that really save us work we need to introduce the concept of variable.
Let us think that I ask you to retain the number 5 in your mental memory, and then I ask you to memorize also the number 2 at the same time. You have just stored two different values in your memory. Now, if I ask you to add 1 to the first number I said, you should be retaining the numbers 6 (that is 5+1) and 2 in your memory. Values that we could now for example subtract and obtain 4 as result.
The whole process that you have just done with your mental memory is a simile of what a computer can do with two variables. The same process can be expressed in C++ with the following instruction set
Obviously, this is a very simple example since we have only used two small integer values, but consider that your computer can store millions of numbers like these at the same time and conduct sophisticated mathematical operations with them.
Therefore, we can define a variable as a portion of memory to store a determined value.
Each variable needs an identifier that distinguishes it from the others. For example, in the previous piece of script the variable identifiers were a, b and result, but we could have called the variables any names we wanted to invent, as long as they were valid identifiers.
Identifiers
A valid identifier is a sequence of one or more letters, digits or underscore characters (_). Neither spaces nor punctuation marks or symbols can be part of an identifier. Only letters, digits and single underscore characters are valid. In addition, variable identifiers always have to begin with a letter. They can also begin with an underline character (_ ), but in some cases these may be reserved for compiler specific keywords or external identifiers, as well as identifiers containing two successive underscore characters anywhere. In no case they can begin with a digit.
Another rule that you have to consider when inventing your own identifiers is that they cannot match any keyword of the pawn language nor your compiler's specific ones, which are reserved keywords. some examples for reserved keywords are: bool, break, case, default, do, else, enum, false, float, for, if, new, public, return, sizeof, static, switch, true, while
Your includes may also include some additional specific reserved keywords.
Very important: The pawn language is a "case sensitive" language. That means that an identifier written in capital letters is not equivalent to another one with the same name but written in small letters. Thus, for example, the RESULT variable is not the same as the result variable or the Result variable. These are three different variable identifiers.
Variables
A variable is basically a bit of memory, it's where data is stored and can be changed and read as required. Variables are one or more cells, a cell is 32 bits (4 bytes) big and by default signed so they can store from -2147483648 to 2147483647 (although -2147483648 gives odd results if displayed). A variable made from more than one cell is called an array, strings are a special type of array where each cell holds a character of the string.
There are many things you can do with a variable, here are a few examples (Read the comments for explanations!)
Arrays
An array is a variable in which you can store multiple pieces of data at once and access them dynamically. An array is declared to a set size at compile time so you need to know how many pieces of data you need to store in advance, a good example of this is the very common MAX_PLAYERS array, this will have one slot for every possibly connected player, so you know data for one player will not interfere with data for another player.
That code will declare an array 5 slots big, so you can store 5 pieces of normal data at once in that single variable.
To set a value in an array you need to say which part of the array you want to store the data in, this CAN be done with another variable:
This will declare an array with 5 slots and give the THIRD slot a value of 7, given that variables always start as 0 this will make the values in the array:
Why is it not:
you're wondering? It's because counting actually starts from the number 0, not 1. Consider the following:
If you go through the list then after the number 2 you have already had one number (the 2), this means that if you are counting the numbers by the time you reach the number 4 you are already at one, you're not at one when you reach the 2, you're at zero. Thus the 2 is at position zero and the 4 is at position one, and thus it follows that the 6 is at position two, which is where the 7 in the first example above is. If we label the slots for the first example we get:
There are five slots but as you can see, and this is very important, there is no slot five, doing the following will give you an error:
As mentioned above the array index (the index is the slot to which you're writing) can be anything, a number, a variable, or even a function which returns a value.
Once you have an array and an index you can use that block exactly as if it were any other variable:
Strings
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. A character is one byte big (although there are extended sets where a character is multiple bytes but these are not well defined in pawn) and by default a character takes up one cell (one normal variable or one array slot). Characters are encoded in a system called ASCII, the character "A" is represented by the number 65, telling the system to display a number will give 65, telling the system to display a character will give a capital a. Obviously is a single character takes up a single cell multiple characters (i.e. text) will take up multiple cells, collections of cells, as just explained, are called arrays.
Strings in PAWN (and other languages) are what's called "NULL terminated", this means that when 0 is reached, the string ends. This is not the same as the character "0", represented by the number 48, this is the NULL character, represented by the number 0. This means that you can have a string array 20 cells large but only have a string 3 characters long if the fourth character is the NULL character, signalling the end of the string. You can not however have a string 20 characters long as the NULL character MUST be in the string, so in a 20 cell array you can have a 19 character string and a NULL termination character.
That code declares a new string with enough space for a 15 character string and sets it initially to the 5 character string "hello", the double quotes around the text indicate that it's a string. Internally the array will look like:
The "x"s mean anything, in this example they will all be 0 but as they're after the null character is doesn't matter what they are, they won't affect the string.
Strings can be manipulated like normal arrays, for example:
Will change the character in slot 1 to the character represented by the number 97 (a lower case "a"), resulting in the string reading "hallo". This can be written much more readably and easy to edit as:
The single quotes around the "a" mean it's a character, not a string, characters don't need to be NULL terminated as they're only ever one cell long, they can also be used interchangeably with numbers if you know what they represent.
'\0' is one character, however it is a special character which modifies the next character, \0 means NULL, that code is the same as doing:
But is NOT the same as doing:
The first and second versions will result in the string being simply:
The third version will result in the string being:
Most importantly of all string functions, pawn often makes use of the function format, here is an example:
I've put this together basing it from a C++ tutorial, but I have modified many parts to make sure it applies to pawn, and pawn only.
If you have more questions and/or concerns (Or if you think something is wrong!), feel free to contact me via PM if you don't want to post a reply I wrote this primarily for people that were interested in scripting on LS-RP, and thought why not give people here a chance too!
Structure of a script
Probably the best way to start learning a programming language is by writing a program. Therefore, here is our first pawn gamemode:
pawn Code:
// my first gamemode
#include <a_samp>
main()
{
print("Hello World!");
return 1;
}
Quote:
Originally Posted by SA-MP Server Console
...<Standard SA-MP dedicated server stuff>...
Hello World! |
This gamemode is the typical program that programmer apprentices write for the first time, and its result is the printing on screen of the "Hello World!" sentence. It is one of the simplest gamemode that can be written in pawn, but it already contains the fundamental components that every pawn script has. We are going to look line by line at the code we have just written:
// my first gamemode
- This is a comment line. All lines beginning with two slash signs (//) are considered comments and do not have any effect on the behavior of the program. The scripter can use them to include short explanations or observations within the source code itself. In this case, the line is a brief description of what our gamemode is.
- Lines beginning with a hash sign (#) are directives for the preprocessor. They are not regular code lines with expressions but indications for the compiler's preprocessor. In this case the directive #include <a_samp> tells the preprocessor to include the a_samp standard file. This specific file (a_samp) includes the declarations of the basic standard input-output library in pawn, and it is included because its functionality is going to be used later in the program.
- This line corresponds to the beginning of the definition of a function that occurs when the program (Server) is loaded.
The word main is followed in the script by a pair of parentheses (()). That is because it is a function declaration: In pawn, what differentiates a function declaration from other types of expressions are these parentheses that follow its name. Optionally, these parentheses may enclose a list of parameters within them.
Right after these parentheses we can find the body of the main function enclosed in braces ({ }). What is contained within these braces is what the function does when it is executed.
- This line is a pawn statement. A statement is a simple or compound expression that can actually produce some effect. In fact, this statement performs the only action that generates a visible effect in our first program.
print is an output function in pawn, and the meaning of the entire statement is to insert a sequence of characters (in this case the Hello World sequence of characters) into the standard output stream (print, which usually corresponds to the screen).
print is declared in the a_samp standard file, so that's why we needed to include that specific file in our script.
Notice that the statement ends with a semicolon character ( ; ). This character is used to mark the end of the statement and in fact it must be included at the end of all expression statements in all pawn scripts (one of the most common syntax errors is indeed to forget to include some semicolon after a statement).
- The return statement causes the main function to finish. return may be followed by a return code (in our example is followed by the return code with a value of one). A return code of 1 for the OnGameModeInit function is generally interpreted as the program worked as expected without any errors during its execution. This is the most usual way to end a pawn script. While it doesn't really matter which number you put in most cases, sometimes it does and because the gamemode that LS-RP is running is based on the GodFather/Pen1 script some functions (Like OnPlayerCommandText) looks for a 1 to make sure things have happened properly.
You may have noticed that not all the lines of this gamemode perform actions when the code is executed. There were lines containing only comments (those beginning by //). There were lines with directives for the compiler's preprocessor (those beginning by #). Then there were lines that began the declaration of a function (in this case, the OnGameModeInit function) and, finally lines with statements (like the insertion into print), which were all included within the block delimited by the braces ({ }) of the function.
The program has been structured in different lines in order to be more readable, but in pawn, we do not have strict rules on how to separate instructions in different lines. For example, instead of
pawn Code:
public OnGameModeInit()
{
print("Hello World!");
return 1;
}
pawn Code:
public OnGameModeInit() { print("Hello World!"); return 1; }
In pawn, the separation between statements is specified with an ending semicolon ( ; ) at the end of each one, so the separation in different lines does not matter at all for this purpose. We can write many statements per line or write a single statement that takes many script lines. The division of code in different lines serves only to make it more legible and schematic for the humans that may read it.
Let us add an additional instruction to our first gamemode:
pawn Code:
// my second gamemode
#include <a_samp>
public OnGameModeInit()
{
print("Hello World!");
print("I'm a pawn gamemode!");
return 1;
}
Quote:
...<Standard SA-MP dedicated server stuff>... Hello World! I'm a pawn gamemode! |
pawn Code:
public OnGameModeInit() { print("Hello World!"); print("I'm a pawn gamemode!"); return 1; }
pawn Code:
public OnGameModeInit()
{
("Hello World!");
("I'm a pawn gamemode!");
return
1;
}
Preprocessor directives (those that begin by #) are out of this general rule since they are not statements. They are lines read and processed by the preprocessor and do not produce any code by themselves. Preprocessor directives must be specified in their own line and do not have to end with a semicolon ( ; ).
Comments
Comments are parts of the script disregarded by the compiler. They simply do nothing. Their purpose is only to allow the scripter to insert notes or descriptions embedded within the source code.
pawn supports two ways to insert comments:
pawn Code:
// line comment
/* block comment */
We are going to add comments to our second program:
pawn Code:
/* my second gamemode
with more comments! */
#include <a_samp>
public OnGameModeInit()
{
print("Hello World!"); // Prints "Hello World!" into the server window
print("I'm a pawn gamemode!"); // Prints "I'm a pawn gamemode!" into the server window
return 1;
}
Quote:
...<Standard SA-MP dedicated server stuff>... Hello World! I'm a pawn gamemode! |
Variables and Data Types
The usefulness of the "Hello World" gamemode shown in the previous section is quite questionable. We had to write several lines of script, compile it, and then execute the resulting program just to obtain a simple sentence written on the screen as result. It certainly would have been much faster to type the output sentence by ourselves. However, programming is not limited only to printing simple texts on the screen. In order to go a little further on and to become able to write scripts that perform useful tasks that really save us work we need to introduce the concept of variable.
Let us think that I ask you to retain the number 5 in your mental memory, and then I ask you to memorize also the number 2 at the same time. You have just stored two different values in your memory. Now, if I ask you to add 1 to the first number I said, you should be retaining the numbers 6 (that is 5+1) and 2 in your memory. Values that we could now for example subtract and obtain 4 as result.
The whole process that you have just done with your mental memory is a simile of what a computer can do with two variables. The same process can be expressed in C++ with the following instruction set
pawn Code:
a = 5;
b = 2;
a = a + 1;
result = a - b;
Therefore, we can define a variable as a portion of memory to store a determined value.
Each variable needs an identifier that distinguishes it from the others. For example, in the previous piece of script the variable identifiers were a, b and result, but we could have called the variables any names we wanted to invent, as long as they were valid identifiers.
Identifiers
A valid identifier is a sequence of one or more letters, digits or underscore characters (_). Neither spaces nor punctuation marks or symbols can be part of an identifier. Only letters, digits and single underscore characters are valid. In addition, variable identifiers always have to begin with a letter. They can also begin with an underline character (_ ), but in some cases these may be reserved for compiler specific keywords or external identifiers, as well as identifiers containing two successive underscore characters anywhere. In no case they can begin with a digit.
Another rule that you have to consider when inventing your own identifiers is that they cannot match any keyword of the pawn language nor your compiler's specific ones, which are reserved keywords. some examples for reserved keywords are: bool, break, case, default, do, else, enum, false, float, for, if, new, public, return, sizeof, static, switch, true, while
Your includes may also include some additional specific reserved keywords.
Very important: The pawn language is a "case sensitive" language. That means that an identifier written in capital letters is not equivalent to another one with the same name but written in small letters. Thus, for example, the RESULT variable is not the same as the result variable or the Result variable. These are three different variable identifiers.
Variables
A variable is basically a bit of memory, it's where data is stored and can be changed and read as required. Variables are one or more cells, a cell is 32 bits (4 bytes) big and by default signed so they can store from -2147483648 to 2147483647 (although -2147483648 gives odd results if displayed). A variable made from more than one cell is called an array, strings are a special type of array where each cell holds a character of the string.
There are many things you can do with a variable, here are a few examples (Read the comments for explanations!)
pawn Code:
myVariable = myVariable + 4; // Changes the value of the variable from (For example) 7 to 7 + 4= 11
myVariable += 4; // Does the same thing, but the difference is that this means "Add 4"
pawn Code:
myVariable -= 4; // Decreases the value by 4
pawn Code:
myVariable *= 4; // Multiplies the variable by 4
pawn Code:
myVariable /= 4; // Divides the number by 4
Arrays
An array is a variable in which you can store multiple pieces of data at once and access them dynamically. An array is declared to a set size at compile time so you need to know how many pieces of data you need to store in advance, a good example of this is the very common MAX_PLAYERS array, this will have one slot for every possibly connected player, so you know data for one player will not interfere with data for another player.
pawn Code:
new myVariable[5];
To set a value in an array you need to say which part of the array you want to store the data in, this CAN be done with another variable:
pawn Code:
new myVariable[5];
myVariable[2] = 7;
pawn Code:
0, 0, 7, 0, 0
pawn Code:
0, 7, 0, 0, 0
pawn Code:
2, 4, 6, 8
pawn Code:
0 1 2 3 4
0 0 7 0 0
pawn Code:
new myVariable[5];
myVariable[5] = 7;
pawn Code:
new myVariable[5],
myIndex = 2;
myVariable[myIndex] = 7;
pawn Code:
myVariable[2] = myVariable[2] + 1;
myVariable[2] += 1;
myVariable[2]++;
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. A character is one byte big (although there are extended sets where a character is multiple bytes but these are not well defined in pawn) and by default a character takes up one cell (one normal variable or one array slot). Characters are encoded in a system called ASCII, the character "A" is represented by the number 65, telling the system to display a number will give 65, telling the system to display a character will give a capital a. Obviously is a single character takes up a single cell multiple characters (i.e. text) will take up multiple cells, collections of cells, as just explained, are called arrays.
Strings in PAWN (and other languages) are what's called "NULL terminated", this means that when 0 is reached, the string ends. This is not the same as the character "0", represented by the number 48, this is the NULL character, represented by the number 0. This means that you can have a string array 20 cells large but only have a string 3 characters long if the fourth character is the NULL character, signalling the end of the string. You can not however have a string 20 characters long as the NULL character MUST be in the string, so in a 20 cell array you can have a 19 character string and a NULL termination character.
pawn Code:
new myVariable[16] = "hello";
pawn Code:
104 101 108 108 111 0 x x x x x x x x x x
Strings can be manipulated like normal arrays, for example:
pawn Code:
new myVariable[16] = "hello";
myVariable[1] = 97;
pawn Code:
new myVariable[16] = "hello";
myVariable[1] = 'a';
pawn Code:
new myVariable[16] = "hello";
myVariable[1] = '\0';
pawn Code:
new myString[16] = "hello";
myString[1] = 0;
pawn Code:
new myString[16] = "hello";
myString[1] = '0';
Quote:
h |
Quote:
h0llo |
pawn Code:
new result[128];
new number = 42;
format(result,sizeof(result), "The number is %i.",number); //-> The number is 42.
new string[]= "simple message";
format(result,sizeof(result), "This is a %s containing the number %i.", string, number);