25.02.2014, 05:19
(
Last edited by Lordzy; 25/01/2017 at 04:26 PM.
)
25th of January, 2017 : I believe this tutorial requires a lot more to be brief over definitions and also requires addition of few more topics. I completely forgot about this thread over the last years and I totally apologise for that. I will be creating another thread regarding include development once I have time. Until then, if necessary, you can read this tutorial and please refer PAWN language's PDF file and SAMP wiki.
Before starting on, I'm asking the users who doesn't know to create include, after reading this tutorial don't just get on in creating small includes and posting them - which are mainly of small functions. If you're creating functions, feel free to post it on 'Useful Functions' topic or if small snippet, post it on 'Useful Snippets' topic.
Alright, many of the users have been seen includes being created. When I was new towards SA-MP, I used to see includes getting released. I had no idea in how they're like but then realized it's just like normal PAWN codes but just small difference from normal scripts and include scripts.
What are includes?
Includes on PAWN are those which includes codes according to what it's done on that include. Different includes got different modes of working according to what's coded. For example, an anti-cheat include might have many hooked parts where as an include which is a set of functions would be having many functions in it than hooking parts. Though, hooking may come in the set of functions too.
Tutorial
•Important topics to be viewed
PAWN Pre-Processor - By Y_Less
Improving your coding habits - By Emmet_
Tips & Tricks - By Slice
Code Optimization - By Y_Less
Oh and
SA-MP Wiki
Creating an Include
IntroductionBefore starting on, I'm asking the users who doesn't know to create include, after reading this tutorial don't just get on in creating small includes and posting them - which are mainly of small functions. If you're creating functions, feel free to post it on 'Useful Functions' topic or if small snippet, post it on 'Useful Snippets' topic.
Alright, many of the users have been seen includes being created. When I was new towards SA-MP, I used to see includes getting released. I had no idea in how they're like but then realized it's just like normal PAWN codes but just small difference from normal scripts and include scripts.
What are includes?
Includes on PAWN are those which includes codes according to what it's done on that include. Different includes got different modes of working according to what's coded. For example, an anti-cheat include might have many hooked parts where as an include which is a set of functions would be having many functions in it than hooking parts. Though, hooking may come in the set of functions too.
Tutorial
•IncludingAnd well, that's it with the tutorial for now. I may update it in case if necessary. Questions and anything relating to this tutorial can be posted here rather than spamming my inbox. Before ending of, here's some links that would be useful in creating includes.Before creating an include, it's necessary to know how #include directive works. Generally, most of the users include a_samp by just doing #include <a_samp>. Usage : #include <file_name_here>.•Starting On
This works in a way like the '#include' gets on '/pawno/include/' and finds the file called a_samp.inc . Including part can also be done in other way by using quotes instead of angular brackets which allows you to browse the include packages even outside the pawno folder. It also allows you to include files of different extensions. Here's an example in showing both the including parts.
pawn Code://Normal way:
#include <a_samp>
/* How it works? :
Goes to '/pawno/include/'
Gets 'a_samp.inc'
*/
#include <a_samp.inc> //Does the same like above.
#include <a_samp.pwn> //Does the same but fetches for the file called 'a_samp.pwn' instead of a_samp.incWhen it comes to folders, you could include it with folder path. Suppose, inside include folder, you got a folder name myLib and inside that you got your include called mylib.inc . So including it would be:pawn Code://Including using quotes
#include "a_samp" //Does the same as #include <a_samp>
#include "../include/gl_common.inc"
//Instead of going to 'pawno/include', it goes to parent directory and goes to 'include' folder. And then it gets 'gl_common.inc' included.
•#tryincludepawn Code:#include <myLib/mylib.inc>
tryinclude works just like #include works, however it doesn't give a fatal error in case if the include isn't found. For example, guess that #tryinclude <mystufffffs> is being used where 'mystufffffs.inc' isn't found at '/pawno/include' directory. Instead of giving a fatal error like #include does, it just skips the inclusion part. This isn't suggested in case if you're trying it to get included for basic functions.
For more about including, you could read the documentation of keyword either on SA-MP wiki or on pawn documentation file.
•Collision #1 : Accidental double includingThe first step in creating an include is to avoid it colliding in case if it's included twice by an accident or if it's being used on other includes or scripts. The basic step is to check if a part is defined or not, if it's defined (the defined symbol should be something like 'KEY' of your include) then it's inclusion part should be aborted assuming that it's already included. And then after closing the checking part, that symbol which has been used for checking has to be defined or else colliding would occur if included twice.• Collision #2 : Variable/Array declaration.
Oh and that's script-wise, before doing that, make sure that you've created the include on '/pawno/include' directory or the place where you want it to be.
Using this method, it's possible to break MAIN colliding parts. However, there are other ways in which there could occur colliding with other scripts. Now, if your script is using some functions from other include or generally a_samp, you could use #tryinclude to get it included than #include so that it doesn't give a fatal error.pawn Code://myfirstinclude.inc
//I'll be checking if myinclude's define has been done already
#if defined _included_myfirstincludestuff
//If, '_included_myfirstincludestuff' has been defined already. If so:
#endinput
//Using '#endinput' breaks the inclusion progress of THIS include only. It's a PAWN directive which doesn't harm other includes.
#endif
//Ending the 'If' statement which was started.
//Now the key which has been checked as if it's defined or not has to be defined before anything.
#define _included_myfirstincludestuff
//Now your include KEY define has been defined!
pawn Code:#if defined _included_myfirstincludestuff
#endinput
#endif
#define _included_myfirstincludestuff
#tryinclude <a_samp>The other way in collision occurring between the include created and scripts are due to variable/arrays. Suppose that a variable called "myvar" is being declared in such a way like new myvar; . And in case if the same variable is being used on your scripts, it would pop up errors in regarding this symbol being declared already. Secondly, arrays could also be a major problem. Suppose there's an array called "string" with size 128 declared on the incude in such a way like new string[128]; , that would give errors in case if it's declared once more with different or equal sizes.• Collision #3 : Callbacks
Avoiding that is pretty simple, either it has to be used of with tags like "myLIB_myvar;", "myLIB_string[128]" or using static variables/arrays. While using static variables or arrays, it works in a way that the variable is being used on the 'include' part. When it's being included in other scripts and if the var/array is present on other scripts too, it wouldn't give errors as it's isn't really a global var/array declared. It was static. Here's an example in regarding it:
You can also use some sort of symbols on your variables to avoid collision such as "LIBTAG_myVar_@_;", "LIBTAG_myArray_@_[128];", but make sure that it don't get you confused later while writing the whole incude.pawn Code:#if defined _included_myfirstinclude
#endinput
#endif
#define _included_myfirstinclude
static
LIB_myvar,
LIB_string[128];
//Those tag was used to just clarify that things won't collide later.
//somewhere:
if(LIB_myvar == 5) format(LIB_mystring, sizeof(LIB_mystring), "Creating an Include - Tutorial");Suppose that "OnPlayerConnect" is being used on the include and the include file is being included in a gamemode where OnPlayerConnect is being used. This would give error because a callback couldn't be used twice without getting hooked. 'Hooking' is done to ensure that the rest usage of the same callback would perform what's included internally and also allows the user to perform their own functions within the callback.•Getting the include user-friendly #1 : Indentation
Now on my gamemode:pawn Code://myinclude.inc
#if defined _included_myfirstinclude
#endinput
#endif
#define _included_myfirstinclude
static
LIB_myvar,
LIB_pName[MAX_PLAYER_NAME];
public OnPlayerConnect(playerid)
{
LIB_myvar = playerid;
GetPlayerName(playerid, LIB_pName, sizeof(LIB_pName));
return 1;
}
• Hookingpawn Code:#include <a_samp>
#include <myinclude>
main() {}
public OnGameModeInit()
{
printf("my awzum gamemode loaded");
return 1;
}
public OnPlayerConnect(playerid)
{
//This would give an error because this callback has been already delcared on 'myinclude.inc' and hasn't been hooked!
return 1;
}
As already mentioned, hooking is done to ensure that the call is being done internally from our include as well as the functions performed my the user under that callback. The current hooking method being used mostly is the Hook Method 7 which uses '_ALS_' prefix. Many users might have been thinking why _ALS_ prefix is being used. Well, it's being used to avoid collision with other libraries as they also using hooking method. _ALS_ being a global prefix ensures that things are going right and doesn't create collision between the include being created and other libraries if being used.
Hooking, using _ALS_ prefix is done in a way that it passes other callback which will later be the same callback under which we're performing this. To be clear, if OnPlayerSpawn is being taken, the other call would probably be 'myLIB_OnPlayerSpawn' which later would perform the same as OnPlayerSpawn does on the scripts being used.
Now, it's time to get 'myLIB_OnPlayerSpawn' to be also called as 'OnPlayerSpawn'. Which means, we're going to define OnPlayerSpawn as 'myLIB_OnPlayerSpawn' so as if other scripts are using 'OnPlayerSpawn', it means that they're actually using 'myLIB_OnPlayerSpawn' if the include is included.pawn Code:#if defined _included_myinclude
#endinput
#endif
#define _included_myinclude
static
myLIB_myvar,
myLIB_lastpspawned[MAX_PLAYER_NAME];
public OnPlayerSpawn(playerid)
{
myLIB_myvar = playerid;
GetPlayerName(playerid, myLIB_lastpspawned, sizeof(myLIB_lastpspawned));
//Now, the bypass of this same call should be done. It can also be done using 'CallLocalFunction',
//however comparing to it's performance, hook method 7 is alot better.
//Before hooking, we're just checking if our call has been defined already.
#if defined myLIB_OnPlayerSpawn
//If yes.
return myLIB_OnPlayerSpawn(playerid); //It returns the callback to 'myLIB_OnPlayerSpawn' callback.
#else
//If not.
return 1; //The call is being stopped here and returns 1.
#endif
//Ends the if-statement started.
}
And then, the problem with collision of callbacks are solved. In this way, the prevention of callback collision can be solved! I haven't explained much about hooking in this tutorial, so if you're new to hooking, or if the above step didn't sort things out well for you, please have a look on the Hook method 7 created by Y_Less and a small explanation regarding the normal hooking method by myself.pawn Code://Before defining, we're using the _ALS_ method to check and undefine this callback before defining.
//So it ensures that the previous calls won't fail.
#if defined _ALS_OnPlayerSpawn //If _ALS_OnPlayerSpawn is defined.
#undef OnPlayerSpawn //If yes, it undefines OnPlayerSpawn
#else //If not
#define _ALS_OnPlayerSpawn //It defines _ALS_OnPlayerSpawn so it gets functioned.
#endif //Closing the if-statement which has been created.
//Now OnPlayerSpawn is undefined.
#define OnPlayerSpawn myLIB_OnPlayerSpawn //OnPlayerSpawn now becomes myLIB_OnPlayerSpawn.
//Now forward myLIB_OnPlayerSpawn or it would give warns as it lacks forwarding.
#if defined myLIB_OnPlayerSpawn //Checking if it's defined.
//If yes.
forward myLIB_OnPlayerSpawn(playerid); //It forwards the callback.
#endif //Closing the if-statement we started.
Hood Method 7 - by Y_Less
How to hook Callbacks & Functions (Normal Hook method) - By Lordz (myself)Includes have to be always friendly, their use most commonly. Because if the include gives warns or errors, it wouldn't have a positive feedback from others. Also, the indentation of the include has to be fixed even if the indentation style is hard to read by users. What it's really meant is in avoiding the usage of "#pragma tabsize" inside the include. It would just get other's script to avoid warnings if their indentation has to be fixed.•Getting the include user-friendly #2 : Cell size
In cases if tabsize is set to 0, don't forget to get it on tabsize 4 (SA-MP default tabsize) at the end of the include. But that also wouldn't be convenient in case if the user is using any other tabsize values.Most of the users are generally declaring variables or arrays which are considered to be as a string of size 256. This isn't right in case if 256 cells aren't actually required. If your include is using #pragma dynamic , it's not a good usage in my view. Includes must be always friendly and should be easier to use. Using of dynamic directive would get the AMX's size bigger in case if you're using higher value of cells on arrays. If you're not good in determining the cell size well, I suggest you to use rBits or y_iterate.•Getting the include user-friendly #3 : Easy settings
If you're confused about what I've actually meant, here's a message which was conveyed to Misuir: (About #pragma dynamic)
Quote:
Yes, it doesn't change the cell size. It just allows you to get your script compiled with arrays of larger cells. It won't be giving any stack size warning or message in case if higher cells are used under a callback and compiled.
If the include got some sort of setting to be done, make it easier. Also in a way that the user won't forget what has to be done. You can use the #error directive to make it possible in such a way that the symbol MY_VALUE has to be declared by the user before including. However if it's not defined, it would give a user-error in reminding to define it.•Getting the include user-friendly #4 : Performance (in-game and while compiling)
And here's an example:
My gamemode:pawn Code://myinclude:
#if defined _included_myinclude
#endinput
#endif
#define _included_myinclude
#if !defined MY_VALUE
//if MY_VALUE is not defined.
#error Please make sure that MY_VALUE is defined before including myinclude.inc
#endif
//And somewhere..
for(new i; i< MY_VALUE; i++) ///....
//Assuming that 'MY_VALUE' is being used for something.
Now, this would just give a fatal error or an error done by the user in regarding the MY_VALUE message. This can be fixed by defining MY_VALUE.pawn Code:#include <a_samp>
#include <myinclude>
In this way, the settings could be done rather than editing the include each time for different uses.pawn Code:#include <a_samp>
#define MY_VALUE 1
#include <myinclude>When the size of the array becomes higher, the performance would get reduced. That's why I've suggested in keeping the cell size as the ones which are required. Useless variables/arrays should be removed as it isn't useful at all. And about performance in-game - It's in looking how complex your codes are. You might be using 5 steps for checking something where as it would be done quicker using just 1 step. This actually happens to most of them. You've to just spend some time in thinking what all steps could be done in what you're actually doing.•Getting the include user-friendly #5 : Fake natives / Support in Syntax highlighting
Secondly, in some cases, for example if you're trying create a simple textdraw message to all. Rather than creating many textdraws, you could just create 1 single textdraw and set it's string when required. I've once created 100 textdraws for messaging and then realized it could be done with just a single one easily and more quickly!Suppose if you're creating an include with some functions which got alot of parameters, the function being highlighted when a bracket is being opened would be really helpful for the users. This can be done by creating fake natives. pawno.exe looks for the part of code which begins with 'native' and adds it into the native list. By this, it also supports in highlighting the parameters when the brackets of the function are being opened. To create a fake native, it's pretty simple.•Creating functions with tags
Now these functions just got 2 parameters, however I'm going to show in creating fake natives.pawn Code://I may have some function called 'Addint' and 'Subint' which adds and subtracts.
stock Addint(num1, num2)
{
return num1+num2; //Returns the sum of first number and second number.
}
stock Subint(num1, num2)
{
return num1-num2; //Does the same but here it subtracts.
}
The compiler would consider this as a native created and will be adding to native list. It will also highlight the parameters when your code either reaches "AddInt(" or "SubInt(" - When the brackets are being opened.pawn Code:/*
native Addint(num1, num2);
native Subint(num1, num2);Creating tags for functions would just make the look better and also the handling of them would be better than normal ones. In case if there's parameter called "worldid, playerid" in a function and if the user assumes first parameter to be playerid, that would create problems then later. Tags - here what I've meant is in adding tags to the variable/array required.•Creating Callbacks
Why a "_:" has been used before id is to prevent tag-mismatch warning. Using that would get the compiler to assume that a tag is present before the parameter called "id".pawn Code:stock myTAG:CreateMyObject(modelid, Float:X, Float:Y, Float:Z)
{
new myTag:ID = CreateObject(modelid, Float:X, Float:Y, Float:Z, 0.0, 0.0, 0.0, 200.0);
return myTAG:ID; //Returns the result with tag.
}
//Now using it would be like
new myTAG:something;
myTAG:something = CreateMyObject(123, 12.0, 13.0, 14.0);
//Creating functions which requires the tagged variable/array
new myTAG:res[100];
stock GetTaggedResult(myTAG:id)
{
return myTAG:res[_:id];
}To create callback, a simple function called CallLocalFunction can be used to get them called under the circumstance you require. Using the above functions, we can try an example. Before the example, here's the parameter of CallLocalFunction.
CallLocalFunction(function[], specifiers[], {Float,_}:...);
function[] - stands for the callback which is going to get called.
specifiers[] - stands for the specifiers according to the next parameter.
%i or %d - stands for integer.
%s - for string.
%f - for float.
%x - for hex.
%c - for character.
And for more : https://sampwiki.blast.hk/wiki/format
The last parameter stands for the data to get called. Check WIKI SA-MP for further information in regarding it.
pawn Code:stock myTAG:CreateMyObject(modelid, Float:X, Float:Y, Float:Z)
{
new myTag:ID = CreateObject(modelid, Float:X, Float:Y, Float:Z, 0.0, 0.0, 0.0, 200.0);
//Let's get the call to be done.
CallLocalFunction("OnObjectGetCreated", "i", myTAG:id); //This is called when the object is created.
return myTAG:ID; //Returns the result with tag.
}
//Forward the callback, while forwarding, the parameters mentioned here are then used as the same on public.
forward OnObjectGetCreated(myTAG:objectid);
public OnObjectGetCreated(myTAG:objectid)
{
return 1;
}
//In case if you're not using tags, you could just simply remove the tags.
•Important topics to be viewed
PAWN Pre-Processor - By Y_Less
Improving your coding habits - By Emmet_
Tips & Tricks - By Slice
Code Optimization - By Y_Less
Oh and
SA-MP Wiki