Aller au contenu

Nouveau membre ?! Pense à te présenter pour accéder au contenu du forum !

New member ?! Introduce yourself to get access to the forum !

Rory

 tutoriel [C++]Création d'un nouveau module Python

Messages recommandés

Bonsoir à tous.

 

Je ne participe quasiment jamais sur le forum, mais mieux vaut tard que jamais.

 

Aujourd'hui, je vais vous apprendre (ou pas) comment créer un module Python par le biais de vos sources client. Quel intérêt me direz-vous ? Et bien à vous de le décider. Je ne suis pas très active sur Metin2. Mais je me souviens il y a de ça 3 ans avoir fait un système et crée un nouveau module pour pouvoir mieux m'y retrouver, car mes fonctions ne rentraient vraiment dans aucun des nombreux modules disponibles. Et j'aime quand les choses sont bien rangées !

 

Vous vous doutez bien que pour suivre ce tutoriel, vous aurez besoin de votre tête, un cerveau si possible(/disponible après une bonne journée de travail) et de vos sources client.

 

Explications du code et préambule : 

Révélation

 

Avant de nous lancer tête baisser dans le code, ce qui serait efficace dans la pratique, mais pas dans l'apprentissage : je vous propose de comprendre d'abord le principe du code que nous allons écrire.

Il y a plusieurs méthodes de créer un module, vous n'êtes pas obligés de respecter la hiérarchie que je vais vous donner ci-après, cependant elle respecte la hiérarchie mit en plus par les sources originelles. Elle est rapide et efficace, sauf si vous avez une idée révolutionnaire, je vous conseille de la garder, si ce n'est que pour pouvoir vous inspirer sur les autres modules afin de compléter le votre.

 

Pour ce tutoriel, je vais utiliser le préprocesseur define afin que vous puissiez désactiver les modifications à tout moment juste en commentant une ligne.

 

Le préambule étant annoncé, nous pouvons commencer la théorie.

 

Tout d'abord, il faut comprendre ce qui se passe quand vous appelez une fonction d'un module crée par C++. Pour ça, un exemple va très bien illustrer mes propos.

Prenons comme exemple la fonction

SetShadowLevel()

Qu'est ce qui se passe quand nous appelons cette fonction ?

Déjà, il faut savoir que cette fonction prend pour paramètre un int, c'est un dire un nombre entier. Quand vous allez dire à votre code  d'éxecuter la fonction SetShadowLevel du module Background :

background.SetShadowLevel(5)

En réalité, Python va dire au C++ :  "Dans ton module Background, tu exécute la fonction SetShadowLevel avec comme paramètre l'entier 5".

Le C++ va donc se dire : "Hum... Ok, attends je vais voir à quelle fonction ça correspond de mon côté, et je te fais ça ! "

 

C'est très simpliste, je l'avoue, mais vous venez avec ça de comprendre quelque chose de fondamentale :

Une fonction python de votre module = une fonction écrite en C++ qui reçoit les données envoyées par Python.

Pour les faire correspondre, nous utiliserons une liste contenant le nom de la fonction python, et à quoi elle correspond en C++.

 

Et si nous codions ?!  

Ça vient ça vient ! 

 

je vous invite premièrement à créer un nouveau fichier dans vos sources client. Il va stocker notre nouveau module. Pour cela, vous allez vous rendre tout seul dans votre fichier où se trouvent tous les autres fichiers sources tels que PythonApplicationModule.cpp. Inspirez vous de ce nom, et créez par exemple PythonRoryModule (Rory étant le nom de votre module). Essayez de ne pas faire de fautes de frappes ! Quoi que vous seriez aux normes d'ymir... ;) 

291856Screenshot-1.png

Nous allons ensuite l'ajouter à nos sources pour cela, si vous utilisez visual studio

292105Screenshot-1.png

Allez ensuite chercher votre fichier.

Ouvrez le dans votre éditeur, et nous allons pouvoir commencer !

 

Pour commencer, nous allons inclure deux fichiers :

#include "Locale_inc.h"
#include "StdAfx.h"

Pourquoi le Locale_inc.h ? Car nous allons utiliser le préprocesseur define, et nous allons le définir dans ce fichier. Je vous invite d'ailleurs à le faire :

#define ENABLE_FIRST_MODULE

Ceci étant fait, je vous propre de directement ajouter le ifdef et coder de dans :

#include "Locale_inc.h"
#include "StdAfx.h"

#ifdef ENABLE_FIRST_MODULE

  
#endif

Pour commencer "vrai" code, nous allons créer la variable d'initialisation. On peut dire que c'est elle qui va poser la base de notre module. C'est la racine de l'arbre !

 

Elle sera du type void car nous n'attendrons pas de retour de la fonction :

void initrory()
{

}

(J'aurai ajouté une majuscule pour séparer les deux mots, mais ymir ne l'a pas fait avant, je respecte leur façon d'écrire et je ne change pas).

 

Dans cette fonction, nous allons faire trois choses différentes :

  • Enregistrer toutes nos fonctions
  • Enregistrer le module
  • Enregistrer différentes constantes.

 

Nous allons commencer par enregistrer nos méthodes, pour cela, nous allons créer une liste :

void initrory()
{
	static PyMethodDef s_methods[] =
	{

		{ NULL, NULL },
	};
}

Notez que j'ai déjà ajouter quelque chose dans s_methods: Il se finira toujours par cette ligne. Ça permet de créer une case vide si vous voulez, cela évite qu'elle soit vide dans tous les cas.

Avant cette ligne, vous allez pouvoir enregistrer autant de méthodes que vous le voudrez comme ceci :

{ "pyMethod", cppMethod, METH_VARARGS },

Comprenez bien que :

  • pyMethod : Nom de la fonction Python. 
  • cppMethod : Lien direct avec la fonction c++ que nous allons créer après.
  • METH_VARARGS : Par convention Cython. Ne vous souciez pas de ça, c'est rare d'avoir à le modifier sur Metin, je ne pense pas avoir vu autre chose comme METH_KEYWORDS par exemple.

 

Pour enregistrer notre module, nous allons ajouter l'instruction

PyObject * poModule = Py_InitModule("rory", s_methods);

"rory" étant le nom de votre module.

 

Ce qui donne

#include "Locale_inc.h"
#include "StdAfx.h"

#ifdef ENABLE_FIRST_MODULE
void initrory()
{
	static PyMethodDef s_methods[] =
	{

		{ NULL, NULL },
	};

	PyObject * poModule = Py_InitModule("rory", s_methods);
}
#endif

 

Voici ! Maintenant, il ne nous reste plus que quelques méthodes à voir.

 

je vais vous donner un exemple de fonction pour achever ce tutoriel :

Ajouter dans s_methods une fonction... Qui va récupérer un nombre entier et le renvoyer dans la console par exemple.

Par défaut, notre fonction va ressembler à ça :

PyObject * roryFirstMethod(PyObject * poSelf, PyObject * poArgs)
{

	return Py_BuildNone();
}

 

Maintenant, pour vous montrer de manière complète la chose, je vais imaginer que ma fonction requiert un argument comme un string par exemple.

 

Pour stocker cette argument, nous allons créer un pointeur. Cela évitera d'allouer de la mémoire pour rien. Créons donc un pointeur nommé text par exemple :

char* text = NULL;

Bien, maintenant nous allons récupérer l'argument 0 (le premier). Voici là méthode pour le faire et l'attribuer au pointeur text, :

PyTuple_GetString(poArgs, 0, &text)

Notez que nous avons pour habitude de le mettre dans une condition pour gérer dans la foulé le manque d'argument :

	if (!PyTuple_GetString(poArgs, 0, &text))
		return Py_BuildException();

Bien. Maintenant nous allons ajouter ça dans la console. Ce qui donne

Tracenf("%s", text);

 

Voilà ! Notre fonction est donc :

PyObject * roryFirstMethod(PyObject * poSelf, PyObject * poArgs)
{
	char* text = NULL;

	if (!PyTuple_GetString(poArgs, 0, &text))
		return Py_BuildException();

	Tracenf("%s", text);
	return Py_BuildNone();
}

Nous n'avons plus qu'à ajouter dans notre s_methods :

{ "Log",		roryFirstMethod,	METH_VARARGS },

 

 

Bien, maintenant nous allons nous rendre à deux endroits afin d'y ajouter un appel à notre fonction d'initialisation, et sa déclaration et nous aurons fini :

  • Dans RunMainScript se trouvant dans UserInterface.cpp 
#ifdef ENABLE_FIRST_MODULE
initrory();
#endif
  • Avec les autres dans le StAfx.h de l'UserInterface toujours pour la déclarer.
#ifdef ENABLE_FIRST_MODULE
void initrory();
#endif

 

Et voici, votre module est désormais disponible une fois vos sources compiler :

import rory

et :

rory.Log(arg)

 

Merci d'avoir lu jusqu'au bout, j'espère avoir été assez claire... Bonne chance de votre côté !

 

Amicalement.

Modifié par Rory

Partager ce message


Lien à poster
Partager sur d’autres sites

Attention par contre, la méthode:

 

il y a 25 minutes, Rory a dit :

rory.method(arg)

 

N'est pas celle que tu as créée, il faut que tu appelles:

 

rory.Log(arg)

 

Partager ce message


Lien à poster
Partager sur d’autres sites

Ce n'est pas réellement une erreur dans le sens où je voulais donner une écriture générale à la fin. method devait prendre la forme du nom de la fonction Python comme indiqué plus haut. J'ajoute la précision, merci de ta contribution.

Désolée de m'être mal exprimée.

Modifié par Rory

Partager ce message


Lien à poster
Partager sur d’autres sites

Très bonne idée.

Excellent tutoriel, merci à toi !

Partager ce message


Lien à poster
Partager sur d’autres sites
il y a 42 minutes, Rory a dit :

Ce n'est pas réellement une erreur dans le sens où je voulais donner une écriture générale à la fin. method devait prendre la forme du nom de la fonction Python comme indiqué plus haut. J'ajoute la précision, merci de ta contribution.

Désolée de m'être mal exprimée.

 

Ouais mais du coup la personne qui va suivre le tutoriel ne comprendra pas pourquoi tu as mis method et pas Log, et inversement ^^.

Dans l'idée il aurait fallu éviter le générique pour que les personnes qui ne sont pas forcément à l'aise avec le code puissent trouver leurs marques.

 

Sinon à part ça, le tutoriel est plutôt correct dans l'ensemble, ça aidera peut-être certains développeurs débutants à s'habituer aux sources Metin2.

Partager ce message


Lien à poster
Partager sur d’autres sites

Je cherche d'autres idées de tutoriels, si vous en avez je suis intéressée.

Partager ce message


Lien à poster
Partager sur d’autres sites

Excellent tutoriel, il en faut plus de ce genre.

Partager ce message


Lien à poster
Partager sur d’autres sites

×

Information importante

By using this site, you agree to our Conditions d’utilisation.