Aide et tutoriaux pour les serveur privée
Vous souhaitez réagir à ce message ? Créez un compte en quelques clics ou connectez-vous pour continuer.


Bonjour, je vous présente mon d'aide pour crée des serveur privée et autres...
 
AccueilDernières imagesRechercherS'enregistrerConnexion
Le Deal du moment : -40%
Tefal Ingenio Emotion – Batterie de cuisine 10 ...
Voir le deal
59.99 €

 

 Tutorial , concernant le scripting

Aller en bas 
AuteurMessage





Date d'inscription : 01/01/1970

Tutorial , concernant le scripting Empty
MessageSujet: Tutorial , concernant le scripting   Tutorial , concernant le scripting EmptyMar 20 Juil - 0:16

Trucs de scripting

Table des matières
(0 - Présentation)

1 - Utilisation des directives de précompilations
1.1 - Les define
1.2 - Les if
1.3 - Les pragma

2 - Arrangement des variables
2.1 - Qu'es-ce qu'une variable?
2.2 - Déclaration correcte de variable
2.3 - Utilisation d'enum
2.4 - Local ou global?

3 - La validations de fonctions
3.1 - Vérifier l'effectivité d'une fonction
3.2 - Vérifier la vitesse d'exécution
3.3 - Vérifier les possibilités gérées

4 - Les fonctionnalités du langage
4.1 - Les state

0 - Présentation

J'ai cru remarqué que bien des gens utilisent certaines "fonctionnalités" à tord et à travers, de façon pas très pratique ou ignore carrément certaines fonctionnalités. Notez bien que ceci n'est qu'une ébauche, ce post sera modifier selon ce qui est demandé et intéresse les gens, d'ailleurs, merci de passer vos commentaires suggestions afin de rendre ce tuto plus pertinent et complet.

1 - Utilisation des directives de précompilations

Les directives de précompilation sont les expressions généralement précédées par le caractère '#'.
Il s'agit de directives s'adressant au compilateur (le programme qui traduit le code pour qu'il soit compréhensible par la machine).
Ces directives peuvent servir à donner des précisions, annuler des bouts de code, modifier les paramètre de compilation (stack alloué lors de l'exécution par exemple), etc.

1.1 - Les defines

La directive #define est probablement la directive la plus utile. Sa structure est la suivante:

Code:

#define EXPRESSION VALEUR


Le terme valeur n'est pas le meilleur terme, un terme plus approprié serait EXPRESSION_RÉSULTANTE.
Il faut bien comprendre que lorsque le compilateur rencontre cette directive, il remplacera EXPRESSION par VALEUR partout dans le code suivant la directive (à moins d'ordre contraire, la directive #undef permet de stopper la directive #define correspondante).
Simplement en sachant ceci, je suis certains que vous pouvez identifier de multiples usages de cette directive.

Normalement, un define est écrit entièrement en majuscule et utilise des _ au lieu d'espace, un peu comme ceci:

Code:

#define SALUT_CA_VA
#define FONCTION_PEU_UTILE
#define COULEUR_INUTILE 0xF1054


En voici quelques un avec un petit exemple:

Création de fonctions (macro):

Je sais que je viens de dire de toujours utiliser des majuscules, mais comme il s'agit d'une technique afin de discerner les defines, les fonctions de defines n'entrent que partiellement dans cette catégorie (d'ailleurs POINT_TO_POINT c'est laid et peu pratique).
Fonctions de calcul de distance:
Code:

#define PointToPoint(%0,%1,%2,%3,%4,%5,%6) ((%0 - %3) * (%0 - %3) + (%1 - %4) * (%1 - %4) + (%2 - %5) * (%2 - %5) <= (%6 * %6))
#define GetDistance(%0,%1,%2,%3,%4,%5) floatsqroot((%0 - %3) * (%0 - %3) + (%1 - %4) * (%1 - %4) + (%2 - %5) * (%2 - %5))
#define MsgBlanc(%0,%1) SendClientMessage(%0, 0xFFFFFFFF, %1)
#define TypoMsg(%0,%1) MsgBlanc(%0, "FORMULATION: " #%1)
#define Kill(%0) SetPlayerHealth(%0, 0.0)

Qui se traduisent comme suit (pour mettre dans un include par exemple):
Code:

forward PointToPoint(Float:X1, Float:Y1, Float:Z1, Float:X2, Float:Y2, Float:Z2, Float:Distance);
forward Float:GetDistancet(Float:X1, Float:Y1, Float:Z1, Float:X2, Float:Y2, Float:Z2);
forward MsgBlanc(playerid, text[]);
forward TypoMsg(playerid, commande[]);
forward Kill(playerid);


Pour ceux qui n'ont pas identifié les fonctions,
la première permet de savoir si deux points sont à une distance inférieure ou égale au paramètre distance.
La seconde retourne en nombre à virgule la distance entre deux points.
La troisième permet de raccourci un SendClientMessage et le mettre blanc

PS. la première est plus rapide que la seconde car elle ne comporte aucun appel à une fonction qui est nécessairement plus lente que de simples multiplications de variables, de plus une racine carrée est nécessairement complexe.

Définition de constantes

Par ailleurs, une autre fonction des defines est de rendre les valeurs redondantes plus lisible et simple à modifier:

Par exemple, disons que vous désirez utiliser la couleur rouge : 0xFF0000FF
utilisée une fois ça va, mais disons que l'on retrouve partout dans votre code une telle chose:

Code:

SendClientMessage(playerid, 0xFF0000FF, "Salut");

La couleur en question n'est pas simple à voir, l'utilisation d'un define est recommandée.

La définition de constante, bien que très simple, comporte quelques subtilités.
[!] Ne pas oublier la règle des majuscules dans ce cas-ci.

Exemples:

Code:

#define COLOR_BLUE (0x00FF00FF)
#define COLOR_GREEN (0x0000FFFF)
#define COLOR_RED (0xFF0000FF)
#define COULEUR_JAUNE 0xFFFF00AA


Notez bien que les parenthèse sont optionnelles, le define peut se faire en français ou en anglais (ou une autre langue) au choix.

1.2 - Les #if

Les #if peuvent d'avérés particulièrement utiles dans les gros scripts. Ils permettent de faire des test avant même la compilation du script.
Un exemple simple que tous connaissent est le #if defined FILTERSCRIPT au début du new.pwn
Il permettait d'activer/désactiver une section du script simplement en modifiant un #define en haut du script.
Ce genre d'application est le plus fréquent et le plus utilisé, toutefois, il est possible de les utiliser avec des valeurs (qui doivent être constantes).

Par exemple, au début du streamer Y_OBJECTS on peut voir:

Code:

#if !defined OBJECT_SECTOR_SIZE
#if OBJECT_DISTRIBUTION <= 10
#define OBJECT_SECTOR_SIZE (1)
#endif
#endif

#if !defined OBJECT_SECTOR_SIZE
#if OBJECT_DISTRIBUTION <= 100
#define OBJECT_SECTOR_SIZE (5)
#endif
#endif

#if !defined OBJECT_SECTOR_SIZE
#if OBJECT_DISTRIBUTION <= 1000
#define OBJECT_SECTOR_SIZE (10)
#endif
#endif


La taille des secteurs dépend de la valeur de OBJECT_DISTRIBUTION (qui est une "fonction" définie plus tôt).

Je n'irai pas plus en détail dans cette partie puisque peu de gens utilisent vraiment...

1.3 - Les #pragma

Pour la plupart, les gens ne comprennent pas ce que font les différentes directives des pragma, donc nous allons les aborder une à une.

#pragma dynamic:

permet d'augmenter le stack (mémoire) accessible au script. Faire bien attention avec celle si si vous allouez trop de stack à vos fs vous pourriez atteindre la limite (et oui il y en a une, il y a toujours une limite, mais peu probable que vous l'atteignez).

Vous utilisez trop de variables locales lorsque vous obtenez un avertissement de mémoire, comme ceci:

Citation de: PAWN Compiler Output
Pawn compiler 3.2.3664 Copyright © 1997-2006, ITB CompuPhase

Header size: 1592 bytes
Code size: 63816 bytes
Data size: 119508 bytes
Stack/heap size: 16384 bytes; estimated max. usage=4108 cells (16432 bytes)
Total requirements: 201300 bytes


Vous avez alors trois options (de la meilleure à la pire):

- réduire vos variable
- passer vos variables en global
- utiliser un #pragma dynamic

Réduire les variables est malheureusement une option partielle (parfois ne s'applique pas), mais surtout compliquée, les gens n'aiment pas ça....
Passer les variables en global est une technique très simple qui est toutefois relativement peu utiliser à cause des conflits de noms de variable possiblement générés.
Utiliser un pragma dynamic semble être une technique très prisé. De plus, le message d'erreur nous indique déjà combien de mémoire est nécessaire à la bonne exécution du programme (selon la mesure du compilo).
Dans ce cas-ci, un #pragma dynamic 210000 est parfait (il est recommandé de mettre un peu plus que la valeur indiqué par le compilo).

#pragma unused

unused permet d'enlever le warning disant qu'une expression est inutilisée, ceci peut arriver avec des variables, des fonctions ou des paramètres.
Cette directive ne devrait être utilisée que dans un seul cas: une fonction dont un paramètre est inutilisé.

Pour les variables et les fonctions, s'il y a possibilité de les utiliser ou non selon par exemple un define, vous devriez savoir que vous pouvez les déclaré en stock ce qui annule leur compilation dans le cas d'une inutilisation.

#pragma tabsize

Cette directive permet de modifier la taille du TAB, attention, elle ne s'adresse qu'au compilateur. Utiliser un tabsize de 0 permet d'éliminer les avertissements concernant l'indentation, ce n'est toutefois pas recommandé car les scripts mal indentés peuvent aisément devenir illisibles.

Notez qu'il existe bien d'autres #pragma, la plupart sont toutefois inutiles je ne les ai donc pas abordés

2 - Les variables

2.1 - Qu'es-ce qu'une variable?

Une variable c'est simplement une quantité de mémoire qui sert à retenir une information. En PAWN, toutes les variables sont semblables. Elles font toutes 32 bits (ou 4 octets). Bref, vous voulez réutiliser une information plus tard? Stocker la dans une variable!

Les variables se séparent en deux catégories en PAWN:

- les variables locales
- les variables globales (ou static)

Les variables Locales:

Les variables locales deumeurent généralement seulement un court instant dans la mémoire de la machine, elles sont stockées sur le stack. Il faut faire attention à la mémoire utilisée par les variables locales, si jamais elles représentent trop de mémoire, le compilateur vous averti d'un usage excessif de la mémoire par ce messages que tous pratiquement ont déjà vu.

Il faut toutefois faire attention avec les fonction récursives. Puisque la fonction s'appèle elle-même, à chaque appel elle recrée les variables ce qui risque d'entrainer un dépassement de la mémoire allouée. Or ce genre de dépassement est difficilement détectable pour le compilateur. Il ne peut vous indiquer une quantité de mémoire utilisé puisque'il ne peut savoir combien de fois la fonction sera appelée. Il est recommandé d'éviter ce genre de fonctions. Elles peuvent toutefois être utiles dans certaines conditions.

Les variables globales:

Ces variables peuvent être aussi nombreuses que désiré (jusqu'à une certaine limite déterminée par la machine bien sûr). D'ailleurs vous pourrez remarquez en compilant un script comprenant une grande variable locale qu'elle est directement présente dans le fichier .amx. Ceci permet l'initialisation de la variable, c'est à dire lui attribuer une valeur de départ. Dans le cas bien particulier de l'initialisation dans l'en-tête du script, la valeur de départ est présente dès l'allocation de la mémoire à la variable, contrairement à l'initialisation dans une fonction.

2.2 - Déclaration de variables:

Pour déclarer une nouvelle variable, un seul mot clé est nécessaire: new
Le nom de la variable (l'expression qui suit new) doit être non-utilisée par le langage lui-même, par exemple on ne peut utiliser state comme nom de variable.

2.3 - Utilisation d'enum:

les enum peuvent être utiliser de plusieurs façons, dans certains cas il peuvent remplacer des defines (comme pour des teams) de la façon suivante:

Code:

enum
{
NO_TEAM,
TEAM_1,
TEAM_2,
TEAM_3,
...
}

PS. il n'existe aucune norme de majuscule ou autre dans les enum, il faut toutefois s'arranger pour éviter les conflits de noms.

L'avantage des enum dans les variables est très simple, ils permettent de faire un tableau dont les différentes colonnes/rangées ne sont pas de même types/taille.
Par exemple, dans un même tableau il est possible de retrouver des entiers, des nombres à virgules, des mots...
Toutefois, pour pouvoir utiliser un enum dans une fonction il faut le nommer, voici un exemple de variable utilisant un enum:

Code:

enum enum_stats
{
Float:Pos_X,
Float:Pos_Y,
Float:Pos_Z,
player_name[MAX_PLAYER_NAME],
kills,
death
}
new gPlayerStats[MAX_PLAYERS][enum_stats]


Nous pouvons ensuite utiliser cette variable comme suit:
Code:

GetPlayerPos(playerid, gPlayerStats[playerid][Pos_X], gPlayerStats[playerid][Pos_Y], gPlayerStats[playerid][Pos_Z]);
GetPlayerName(playerid, gPlayerStats[playerid][player_name]);


2.4 - Local ou global?

Cette partie est sans doute la plus simple, il faut toutefois faire une nuance. Il existe des situations où il est préférable d'utiliser des variables globales, d'autres locales.
Personnellement, je tente de limiter le plus possible l'utilisation de variables globales pour des usages locaux. Un bon exemple est le ystring dans le script de course yrace. il faut éviter ce genre de choses car il serait possible que la variable soit utilisées par deux bouts de code en même temps (même si très peu probable et le tout dépend de la façon que vous l'utilisez)).
Lorsque vous n'avez pas besoin de conserver la valeur d'une variable hors de la fonction (excluant les appels à d'autres fonctions par la fonction elle-même), la variable devrait être locale.
Les variables qui peuvent être utilisés à plusieurs endroits dans le scripts n'étant pas nécessairement directement liés (par des appels entre-eux disons) devraient être globales. il faut noter que l'on marque généralement les variable globale en utilisant la lettre g devant elles (g pour global).

Comment savoir si une variable est locale ou globale?

C'est très simple, les variables déclarées hors de tout bloc d'instruction (par exemple en haut complètement du script) sont globales. Celle qui sont déclarées dans des blocs d'instructions (fonctions, callback etc..) sont généralement locales. Toutefois, les variable static sont toujours globales.

Bref,
globales : static + en-tête
locales : variables déclarées dans des blocs d'instructions (autre que static)

petit exemple:
Code:

new test;//cette variable est globale car elle est de niveau 0 (hors de toute fonction)
public OnPlayerCommandText(playerid, cmdtext[])
{
new test2 = 12;//cette variable est locale car déclarée dans la fonction, elle n'est valide que dans le bloc d'instruction (entre les {})
//la variable test est toujours valable
if(strcmp("text", cmdtext[1], true, 4) == 0)
{
//les deux variables sont valides
GameTextForAll(cmdtext[6], 4000, 5);
return 1;
}
return 0;
}


3 - La validation de fonctions

J'entend par validation la vérification d'une fonction, s'assurer qu'elle est relativement efficace et pourra gérer la plupart voir tous les cas possibles.

3.1 - Vérifier l'effectivité d'une fonction

J'entend ici de vérifier le fonctionnement d'une fonction, si tout se déroule normalement. En général ce n'est pas nécessaire, seulement pour les fonctions qui ne font pas ce qu'elles sont supposées faire.
Il n'y a pas de technique magique, il faut marquer les différentes parties des fonctions. Pour marquer, une seule fonction suffit, printf.
À chaque action, il faut pouvoir savoir si elle est réussie ou non. Donc un print avant et après. Bien sûr, certaines actions ne sont pas à risque de rater comme un simple SendClientMessage.
Si des variables sont modifiés, il est recommandé d'en faire un print avant et après le moindre changement.

Un petit exemple?
En voici un très simple:
Code:

IsMoto(vehicleid)
{
new model = GetVehicleModel(vehicleid);
printf("model vehicle: %d", model);
switch(model)
{
case 448,461,462,463,468,471/*(quad)*/,481,509,510,521,522,523,581,586:
{
model = 1;
}
default:
{
model = 0;
}
}
printf("IsMoto: %d", model);
return model;
}

Donc en regardant le log il est assez aisé de savoir si la fonction fonctionne correctement. Vous y verez le modèle du véhicule suivit de la valeur de retour.

3.2 - Vérifier le temps d'exécution d'une fonction

Toujours pas de technique magique. Toutefois, il faut faire une distinction. Le but est de déterminer la rapidité d'exécution d'une fonction et de l'améliorer, mais pour se faire il faut faire un benchmark.
Il existe tout plein de petites defines déjà faites pour le faire. Syg en avait donnée une très intéressante dans le topic des scripts utiles: Benchmark macro
Sinon vous pouvez faire votre test par vous même, il suffit d'exécuter la fonction à répétition sur un serveur
PS. il est préférable de faire les test sur un serveur vide (aucun script chargé autre que le test)

3.3 - Vérifier les valeurs prises en charge

Encore une fois, il n'y a pas de technique magique. Je vais toutefois ressortir certains terme que l'on voit en calcul différentiel/intégral. La meilleur technique afin de vérifier une telle chose est de simplement vérifier les valeurs critiques.
Par exemple, sur une fonction GetVehicleModelName(modelid) il ne sert absolument à rien de vérifier tous les véhicules. Par contre, il pourrait être intéressant de regarder les valeurs -1, 0, 1, 399, 400, 611, et 612.
Pourquoi?

-1 : une valeur négative
0 : il s,agit d'une valeur particulière que je recommande de toujours testé, elle est unique.
1 : +/- utile mais simplement au cas où
399 et 400 : vérifier le début du fonctionnement de la fonction, qu'elle commence bien à 400
611 et 612 : comme 399 et 400 mais pour la fin.
Il serait aussi possible d'ajouter une autre valeur plus grande pour être certains qu'elle le gère

Comment déterminer ses valeurs critiques?

Il s'agit simplement de déterminer qu'elles sont les valeurs pour lesquelles la fonction agit différement.
Par exemple, pour un switch chaque case représente une exécution différente, il faudrait donc vérifier chacun d'entre-eux en plus d'une valeur extérieure (sauf dans le cas de présence d'un default qui est cette valeur autre).
Si l'on revient au cas de la fonction GetVehicleModelName(modelid), il est évident qu'il faut vérifier 399, 400, 611 et612 puisque ce sont les valeurs de fin et de début de la plage de modèles.
Il faut noter aussi qu'il existe deux méthodes afin de voir ces valeurs à vérifier. Par le code de la fonction elle même, ou en se basant sur ce qu'elle fait. Pour la fonction cité ci-dessus, en se basant sur le fonctionnement et ce qu'elle fait nous obtenons toujours les valeurs 399, 400, 611 et 612, les autres sont quelque peu superflues mais peuvent être testées pour plus de sécurité.

Selon l'action de la fonction:

puisqu'elle permet de savoir le nom d'un modèle de véhicules, quels sont les endroits où la plage change? La fin et le début des modèles.

Selon le fonctionnement:

En regardant le tableau des noms de véhicules, on voit qu'il fait 212 par un nombre variable dépendant de l'initialisation (la longueur du nom du véhicule).
Puisque l'on utilise le modèle-400 pour déterminer où se trouve le nom du modèle, les points névralgiques sont lorsque l'on sort du tableau (-1 et 212 soit 399 et 612).

4 - Les fonctionnalités du langage

4.1 - Les State

Les state sont une fonction très intéressante du PAWN, ils sont toutefois très peu utilisés sur SA-MP. Ils peuvent remplacer des variables globales et permettre un code beaucoup plus lisible et facile à comprendre. Afin de les utiliser de façon optimale, il vaut mieux prévoir quels seront les statuts dans le script. Prenons un petit exemple simple:

Code:

public OnPlayerCommandtext(playerid, cmdtext[]) <test:statuton>
{
state (strcmp("toggle", cmdtext[1]) == 0) test:statutoff;
Debug("Ce message n'apparaitra pas);
}

public OnPlayerCommandtext(playerid, cmdtext[]) <test:statutoff>
{
state (strcmp("toggle", cmdtext[1]) == 0) test:statuton;
Debug("ce message sera affiché");
}

Debug(text[]) <test:statuton>
{
return print(text);
}

Debug(text[]) <test:statutoff>
{
return 0;//on ne fait rien, on pourrais retirer le return 0...
}

dans ce cas-ci, nous pourrions définir statutoff comme aucun debug, et statuton comme débug activé
Il est possible de faire plein de statuts différents. Il faut toutefois faire attention, les state (ou automata) sont les mêmes pour tous. Il n'est pas possible, par exemple, de faire un state qui soit différent pour chaque joueurs (possible mais trop complexe et sans intérêt)
Revenir en haut Aller en bas
 
Tutorial , concernant le scripting
Revenir en haut 
Page 1 sur 1

Permission de ce forum:Vous ne pouvez pas répondre aux sujets dans ce forum
Aide et tutoriaux pour les serveur privée :: Aide et Tutoriaux serveur privée :: GTA San Andreas-
Sauter vers: