Aller au contenu
Concours du mois de Juin 2018 : Carte Amazon 15 €

Rechercher dans la communauté

Affichage des résultats pour les étiquettes 'tutoriel'.



Plus d’options de recherche

  • Rechercher par étiquettes

    Saisir les étiquettes en les séparant par une virgule.
  • Rechercher par auteur

Type du contenu


Forums

  • Inscription & Connexion
    • Inscription
    • Connexion
  • Espace communautaire
    • Funky-Emulation
    • Discussions Générales
    • Commerce
    • Espace Gaming
    • Réseaux Sociaux
    • International
  • Emulation de jeux
    • Roblox
    • RaiderZ
    • Aura Kingdom
    • Metin 2
    • Dofus
    • GTA V Online
    • Minecraft
    • World of Warcraft
    • Aion
    • Habbo
    • Wakfu
    • GTA San Andreas
    • Jeux .IO
    • Autres jeux
  • Espace divers
    • Programmation
    • Arts
    • Discord
    • Mobile
    • Divers
  • International
    • Community
    • Games Emulation
    • Others
  • Archives

Blogs

Il n’y a aucun résultat à afficher.

Il n’y a aucun résultat à afficher.


Nationalité


Sexe

831 résultats trouvés

  1. tutoriel Implanter un PNJ/Mob

    Salut à tous, je viens vous montrer comment implanter un NPC sur raiderZ. L'implantation se déroule en deux parties très rapides, mais avant de commencer je vous partage également un tool permettant d'avoir des XML un peu plus lisibles que sur notepad++. Il s'agit de Ice Raiderz Database Manager, vous pouvez vous en passer mais durant le tutoriel je l'utiliserai. Contenu Masqué On commence par ouvrir le tool et on importe le fichier npc.xml en cliquant sur l'onglet File->Import to->NPC's Database. Sélectionnez ensuite le chemin vers votre npc.xml puis cliquez sur l'onglet NPC's Manager, après ça ajoutons une ligne. Un nombre incalculable de paramètres sont disponible sur chaque ligne, du simple ID du mob jusqu'à la possibilité de lancer un son/musique à l'engagement d'un fight contre le mob. Les paramètre qui vont nous intéresser sont : id, type, Meshpath, MeshName, Attackable, Interact. Pour les autre paramètres c'est a vous de voir ce que vous voulez faire avec, d’ailleurs beaucoup de paramètre sont encore inconnu pour moi. Explication : ID : l'ID du monstre (prenez un ID qui n'est pas déjà utilisé) type : npc, monster ou vehicle (jamais utilisé le vehicle si quelqu'un a une idée de son utilité n’hésitez pas) MeshPath : Le dossier où se trouve le model / Le model. Exemple : NPC/le_model MeshName : Le nom du model, donc pour suivre l'exemple précédent ici c'est le_model Attackable : Si le PNJ attaque ou non : NONE il n'attaque pas, ALWAYS il attaque ou FACTION il attaque seulement les factions adverses. Interact : Ne pas oublier ce paramètre pour les dialogues, True le PNJ interagit, False le PNJ n'interagit pas. Si vous avez un doute sur l'un des paramètres, aidez vous de ceux des NPC déjà existant. Ceci étant fait nous pouvons exporter le XML dans le client et le serveur, il suffit simplement de cliquer sur l'onglet File ->Export to ->NPC's Database et comme pour l'importation vous sélectionnez votre npc.xml et vous le remplacez Attention le logiciel de fait pas de sauvegarde de vos xml il est préférable de faire un dossier de sauvegarde de vos xml avant de remplacer quoi que ce soit. La partie implantation du PNJ est fini on va s'occuper maintenant de l'affichage in Game de son Nom, pour ça, rendez vous dans votre client Data/system/lang/En_US puis ouvrez le name_npc.xml. Vous avez 2 lignes à ajouter (En Rouge ce que vous devez changer) : <STR key="NPC_NAME_12" string="FunkyNPC" /> Ici je met NPC_NAME_12 (12 l'ID de mon PNJ) et FunkyNPC (Le nom de mon PNJ) Cette ligne représente le Nom IG de votre NPC <STR key="NPC_CLAN_12" string="Le NPC Tutoriel !" /> Comme précédemment on met l'ID du NPC et maintenant un Mot/Phrase qui apparaîtra juste en dessous de son Nom. Inutile de modifier le xml coté serveur étant donné que ce n'est que de l'affichage. Ceci était la dernière étape on ferme le masterServ on relance tout et c'est prêt ! Résultat : Merci d'avoir lu le tutoriel en espérant que celui-ci vous soit utile.
  2. Version des fichiers/ Type d'émulateur :funky-ému Domaine (Base de données, Client, Core...) :base de données Votre niveau (Débutant, Intermédiaire, Avancé..) :débutant Description du problème Implanter un magasin et le remplir voila le dernier lien pour continuer le tutoriel est mort Comment reproduire le problème ? :aller dans tutoriel implanter un magasin et le remplir cliquer sur le dernier lien pour faire la suite du tuto Recherches et tests effectués : Screenshot du problème concerné : En postant ma demande d'aide, j'affirme avoir lu et accepté le Règlement de Funky-Emulation.
  3. Bonjour / Bonsoir ! Je me suis rendu compte que beaucoup de débutants ne savaient pas vraiment comment marchait un jeu en ligne, rendant la modification dudit jeu encore plus compliquée pour eux. Je vais donc tenter d'expliquer le plus simplement possible comment marche un jeu en ligne Pour commencer, je vais vous parler dans une première partie des différentes parties du jeu, et après dans une deuxième partie on verra comment les modifier, pour finir sur le cas un peu particulier des sources du jeu. Pour commencer, un MMO est composée de deux parties: un client et un serveur. Les joueurs ont un client qui se connecte au serveur du jeu et leur permet de jouer en ligne: Le client c'est ce que vous téléchargez quand vous voulez jouer à un serveur ou à l'officiel, et ça ressemble à ça: Le serveur, c'est ce qui permet de relier tous les joueurs entre eux Il est composé de deux grosses parties: les fichiers serveur et la base de donnée. Les fichiers serveurs (server files, ou files, ou files server) sont l'ensemble des fichiers qui permettent au serveur de fonctionner: Trois programmes sont utilisés par Metin2 pour ça: le "game", qui est le programme principal du serveur, le "db", qui gère la connexion à la base de donnée, et le "qc", qui permet de compiler les quêtes. La base de donnée, c'est un ensemble de tables qui contiennent les informations variables du jeu: informations sur les comptes, sur les joueurs, sur les guildes, sur les quêtes, etc. Maintenant qu'on a vu quelles sont les différentes parties du jeu, on va voir comment les modifier ! Pour savoir quoi modifier, de nombreux tutoriels sont à votre disposition, ici je vous explique juste comment modifier ces fichiers ! Pour modifier les fichiers du client, il faut extraire les fichiers .eix / .epk qui sont dans le dossier "pack": Le logiciel le plus utilisé pour extraire ces fichiers est l'EterNexus. Pour modifier les fichiers du serveur, il faut passer par un logiciel d'accès FTP/SFTP à distance, comme WinSCP ou FileZilla (voir tutoriel sur Créer un serveur privé Metin2, partie VII). Pour modifier la base de donnée, il faut un logiciel comme Navicat (Lien de téléchargement). Du coup maintenant vous savez quels logiciels utiliser pour modifier les fichiers du client, du serveur, et la base de donnée ! Le cas particulier des sources: Les sources, c'est quoi? Les sources, c'est ce qu'il y a avant le lanceur client et les programmes du serveur. Avant d'arriver à un beau metin2client.exe que vous pouvez lancer, il y a un code source, c'est à dire des fonctions, variables, structures qui permettent au programme de marcher: Vous vous en doutez, Metin2 ne se résume pas à un "Hello world!", et il a donc besoin de dizaines de milliers de lignes de code source pour fonctionner. Mais pourquoi modifier le code source, si c'est aussi compliqué? La raison est simple: si vous voulez, par exemple, modifier la résolution des ombres du jeu pour qu'elles soient plus jolies, vous préférez le faire comment? Comme ça, avec un éditeur hexadécimal? Ou comme ça, avec le code source? Je vous laisse répondre tous seuls Pour compiler le code source client, vous aurez besoin de Visual Studio (version 2013). Pour compiler le code source serveur, vous aurez besoin de FreeBSD. Tout est décrit ici: Comment compiler le client Comment préparer sa machine virtuelle pour compiler le serveur Comment compiler le game Comment compiler le db Ce petit tutoriel est maintenant fini, merci de l'avoir suivi, si vous avez des suggestions n'hésitez pas
  4. tutoriel [Tutoriel]Créer un nouveau monde

    Bonjour à tous ! Tutoriel court et rapide pour créer un nouveau monde sur votre serveur RaiderZ. /!\ Vous êtes obligés d'avoir au moins un monde pour pouvoir jouer /!\ Pour commencer, ouvrez simplement Navicat, connectez vous à la BDD de votre serveur, et allez dans la base de donnée account, table "world". Pour rentrer un monde, appuyez sur "Inser", ou cliquez sur le + en bas de la fenêtre: Puis remplissez comme sur ce screen: server_id: ID des serveurs de Login ou Game (par défaut sur le DevKit: 101 et 1) world_id: ID du monde (1,2,3,etc..) name: Nom du monde host_adress: Adresse des serveurs du monde (si tout est sur la même machine, c'est 127.0.0.1) port: Port du serveur de Login ou Game type: LoginServer ou GameServer curr_player: Laisser vide max_player: Nombre maximum de joueurs sur le monde last_update_time: Laisser vide servable: mettre t (pour true) alive_timeout: Laisser vide state: FINE order_num: Numéro du monde dans la liste des mondes côté client (commencez à 1) Voilà, vous avez un monde sur votre serveur ! Vous pouvez lancer votre serveur et vous connecter
  5. tutoriel [Tutoriel]Créer un compte

    Bonjour à tous, Petit tutoriel sur comment créer un compte sur RaiderZ. Pour commencer, allez dans Navicat, puis ouvrez la base de donnée RZ_ACCOUNT. Ensuite, suivez le screen: cliquez sur Queries, puis New Query: Dans le champ principal, entrez la fonction suivante: SELECT rz_account_insert('Username', 'blue', '[email protected]'); Remplacez Username par le nom d'utilisateur, et l'adresse mail par l'adresse mail du joueur. /!\Attention: peu importe le mot de passe que vous mettrez, la fonction mettra automatiquement le mot de passe "blue" au compte! /!\ Une fois la fonction tapée, cliquez sur "Run", et vous devriez avoir une fenêtre qui ressemble à peu près à ça: Voilà, votre compte est créé !
  6. Salut ! Avant de suivre cette partie, je vous invite à lire la précédente : Je vais ici essayer de vous faire découvrir HTML, CSS, JS et PHP. Voici les différents objectives de cette partie : Découvrir les éléments basiques de l'HTML tels que : La doctype Différents balises : Les principales balises telles que le p,h1,ul,td... Les principales balises orphelines telles que le hr, br... Les formulaires qui seront très utiles pour la suite, Les tableaux, Les liaisons : CSS, JS. PHP : Découverte de PDO : Créer sa requête : Préparation, Insertion de données, Exécution, Traiter les données : Nombre de ligne affectée, Colonne affectée, Ligne des colonnes affectées. Divers. Nous avons un emploi du temps assez chargé, nous ferions mieux de nous y mettre maintenant ! HTML: HTML est un langage de balisage. Il dispose de balise. Je vous propose une petite liste, qui résume les principales balises. @ASIKOO Il faudrait un système pour faire des cartes mentales ! ! (Je sais pas si c'est toi que je devais taguer, mais tu m'as l'air d'être le seul actif...). Sauf si je précise que les balises sont orphelines, elles devront être ouvertes puis fermées. Utilisez pour les fermer la même balise que pour les ouvrir, mais ajoutez un "/" exemple : <p>Message</p> Des balises assez spéciales : <!DOCTYPE html> Elle au dessus de tout votre code HTML. Elle est obligatoire, même si le code marche sans, pas de question, c'est obligatoire. Balise orpheline. <head>.. </head> Se place après la balise de doctype. Elle contient les informations complémentaires de la pages telles que : Le titre, Les mots clés, Les importations de : CSS, JS. Toutes les méta-informations. <body> .. </body> Se place après la balise de fermeture </head> Contient littéralement le corps de la page. C'est tout ce qui sera affiché au client. Les balises principales : Texte brut : <p> : Permet de rédiger un paragraphe. <h1>,<h2>... <h6> Permet d'écrire un titre plus ou moins important. L'évolution de l'importance est décroissante par rapport à la valeur associée à h. Les tableaux, un tableau ce compose de ces balises : <table> : Désigne le début et la fin du tableau, <tr> : Désigne une ligne du tableau, <td> : Désigne une cellule du tableau. On va donc créer un tableau comme ceci : <table> --> On crée le tableau <tr> --> On crée une nouvelle ligne <td>.. </td> --> On crée les colonnes en désignant les cellules </tr> --> On marque la fin de la ligne <tr> --> On crée une nouvelle ligne <td>.. </td> --> On crée les colonnes en désignant les cellules </tr> --> On marque la fin de la ligne </table> --> On marque la fin du tableau Il existe aussi (mais nous en reparlerons si nécessaire) : <thead> <tfoot> <tbody> Liste dite ordonnée : <ol> <li> élément </li> </ol> Liste dite non ordonnée : <ul> <li> élément </li> </ul> Je n'ai pas d'autre balise en tête dans l'instant présent. Si une balise que l'on va utiliser n'est pas présente dans ce petit regroupement, je vous l'expliquerais le moment venu. Je vais maintenant vous parlez des liaisons. Si vous ne le savez pas encore l'html, le css, et le javascript ne se mettent pas dans le même fichier pour des raisons évidentes de : Propretés, Efficacités Facilités. Il y a deux types de lignes différents qui se mettent tous les deux dans l'entête (head) du fichier html : <link href="assets/css/style.css" rel="stylesheet"> Ici, nous importons, créons un lien entre les deux fichiers. Notez que la seule valeur à changer est celle du href="" (Qui désigne une destination) vers la destination du fichier voulu. <script src="assets/js/modernizr.js"></script> Cette balise est assez bizarre je sais. Vous n'avez qu'à changer la destination nécessaire dans la valeur de l'attribue src. Je vous propose d'entamer maintenant PHP, PDO : PHP --> PDO: Bien, je vous rappelle que nous avons déjà crée notre connexion PDO stockée dans la variable db. Je vous propose pour commencer de déjà vous apprendre à créer une fonction en php. Une fonction vous permet d'effectuer un protocole plus ou moins long sans devoir le retaper entièrement. C'est le même principe que les fonctions en mathématiques, sauf qu'ici, nous ne faisons pas qu'ajouter des nombres, multiplier, etc... La structure d'une fonction est assez simple : <?php function GetUserIP(){ } ?> Ici, nous avons crée notre fonction nommée GetuserIP. Si votre fonction prends en compte des paramètres, (comme un x en mathématiques par exemple), vous les ajouterez entre les parenthèses, sous forme de variable, et séparés par des virgules. Ici, je vous montre une fonction assez technique, ça va peut-être, être même la fonction la plus compliquée que nous allons faire avec ce CMS. Car clairement... Il n'y a rien de compliqué sur un CMS Metin2. Bien, avant de commencer à écrire notre code, il sera préférable de savoir exactement ce que va faire la fonction. Pour des raisons de sécurités, nous allons vérifier que l'utilisateur dispose bien d'une IP valide, et que le serveur peut "capter". On ne sera pas à l'abri des VPNs, mais c'est déjà mieux que rien. Nous allons définir pour commencer trois variables qui contiendront : client L'ip de la personne enregistrée si possible forward L'ip malgré un proxy remote L'ip du client qui demande la page courante Pour cela nous allons utiliser une super variable. Si vous avez lu l'autre tutoriel, vous savez que les supers variables commencent toutes pas : $_ ici, nous allons utiliser la super variable server, donc : $_SERVEUR. Pour client et forward, nous allons mettre un arobase devant le dollars. Pour un soucis de type, je m'attarde pas là dessus, nous ne l'utiliserons plus de toute façon... Vous ne pouvez pas deviner le code, sauf si vous chercher des heures sur la doc, cela donne : <?php $client = @$_SERVER['HTTP_CLIENT_IP']; $forward = @$_SERVER['HTTP_X_FORWARDED_FOR']; $remote = $_SERVER['REMOTE_ADDR']; ?> Bien, nous allons maintenant vérifier si au moins client ou forward à le format d'une IP, sinon, nous utiliserons remote. Nous allons pour ça faire un bloc de condition facilement représentable grâce à ce schéma : On définie la variable client On définie la variable forward On définie la variable remote Si client ressemble à une IP valide, si oui : On assimile sa valeur à une autre variable nommée IP Si client n'a pas l'air d'être une IP valide : On vérifie si forwad là, si oui : On assimile sa valeur à une autre variable nommée IP Sinon : On assimile la valeur de remote à la variable nommée IP. Pour vérifier son format, on va utiliser une filtre, pas d'expression régulière c'est démodée. Vous ne pouvez pas le deviner, cela donne : <?php $client = @$_SERVER['HTTP_CLIENT_IP']; $forward = @$_SERVER['HTTP_X_FORWARDED_FOR']; $remote = $_SERVER['REMOTE_ADDR']; if(filter_var($client, FILTER_VALIDATE_IP)){ $ip = $client; }elseif(filter_var($forward, FILTER_VALIDATE_IP)){ $ip = $forward; }else{ $ip = $remote; } ?> On utilise donc les outils if --> Il veut dire "Si", soit : Si ce que je te dis dans la parenthèse est vrai (=true) alors : Il fait ce qu'il y a entre les accolades. Si la condition est respectée, on sort de la boucle. elseif marche comme le if à une différence près : Il ne peut être placé qu'après un if Ne s'exécutera que si le if n'est pas respecté Si la condition est respectée, on sort de la boucle. else --> Si aucune des conditions n'est respectées, le code fait ce qui se trouve dans le else. Sachez que additionner des if à la suite, et mettre des elseif n'a pas le même effet. Si vous enchainez des if, le code va tous les essayer un par un. Si vous mettez des elseif, il va tous les vérifier dans l'ordre, mais dès qu'il aura trouvé une solution, il ne va plus suivre les autres elseif de ce bloc. Il ne nous reste plus qu'une chose à faire : Vérifier si la variable IP est définie Si non : On sort de la condition en affichant une erreur Si oui : On retourne l'IP en sortit de variable Je vous montre une autre façon de faire un if (pas de panique, il n'en n'existe que deux) je vous explique après, notre fonction donne : <?php function GetUserIp(){ $client = @$_SERVER['HTTP_CLIENT_IP']; $forward = @$_SERVER['HTTP_X_FORWARDED_FOR']; $remote = $_SERVER['REMOTE_ADDR']; if(filter_var($client, FILTER_VALIDATE_IP)){ $ip = $client; }elseif(filter_var($forward, FILTER_VALIDATE_IP)){ $ip = $forward; }else{ $ip = $remote; } if(!isset($ip)) exit("ERREUR"); return $ip; } ?> Quelques petites précisions : La méthode exit permet de retourner une erreur et d'arrêter le script actuel, il va bloquer l'utilisateur. La méthode return permet de sortir de la fonction en retournant une valeur, ici l'ip. La méthode isset() permet de vérifier si une valeur est définie ou non. Ajouter un ! devant des méthodes telles que isset inverse leur fonctionnement. En quelque sorte nous faisons ici : Si l'ip n'est pas définie : Tu dégages le mec avec un bon coup de pied au cul Sinon, tu le laisse passer ! Et voilà vous avez fait votre première fonction ! Et pas des plus facile en plus. S'il y a du code que vous ne comprenez pas. La divine bible est là pour vous. Bien, allons enfin au vif du sujet : PDO. Pour envoyer une query au serveur, nous allons : Stocker notre requête dans une variable Lui attribuer des paramètres si nécessaires Exécuter la requête. Pour cela nous allons stocker notre requête dans une variable en utilisant un facteur d'affectation. Nous allons pour ça nous servir de la variable DB de la dernière fois. Si vous utilisez la variable $db dans une fonction, il faudrait que vous refassiez un include dans cette même fonction. Bien, on va préparer une query, on va utiliser : db->prepare(query); On va assigner ça à la variable $Akihira par exemple, tapons : <?php $Akihira = $db->prepare(); ?> Bien, nous allons taper une query au hasard, par exemple une assez simple : <?php $Akihira = $db->prepare("SELECT * FROM account.account"); ?> Bien, ici pas besoin d'attribuer des paramètres, vous pouvez exécuter votre fonction comme ceci : <?php $Akihira = $db->prepare("SELECT * FROM account.accoun"); $Akihira->execute(); ?> Maintenant, nous avons trois façon de récupérer des informations sur ce que nous avons fait : Savoir combien de ligne ont été affectée, ici cela nous renvoie le nombre de compte crée en réalité? rowCount(); Sélectionner les lignes affectées avec les colonnes indiqués fetch(); Sélectionner toutes les les colonnes de toutes les lignes affectées. fetchAll(); Bien, pour le rowCount(); c'est le plus simple, tapez : <?php $Akihira = $db->prepare("SELECT * FROM account.accoun"); $Akihira->execute(); $Akihira = $Akihira->rowCount(); ?> Ici, comme nous avons tout sélectionner ("*"), au niveau où vous êtes pour l'instant, il n'y a pas de différence, mais ne vous habituez pas à ça, il y en aura bientôt ! Pour le fetch, il va vous créer un tableau, il va falloir utiliser une méthode spécial pour obtenir les résultats. Mais on va voir ça dans un prochaine tutoriel où je vous parlerais plus en détails des tableaux, et on verra aussi la boucle foreach je pense pour pouvoir parler plus en détail du fetchAll. En attendant je vous dis merci d'avoir lu, et référé vous à la doc en attendant Salut !
  7. Salut ! Comme les tutoriels de @Takuma on été (si j'ai compris) totalement détruit par le changement de moteur forum, je vais essayer de reprendre ses tutoriels avec plus de détails, et en publiant du contenue régulièrement. J'espère faire au moins aussi bien qu'il l'a fait. Voilà pourquoi j'ai repris son nom de poste, et que je vais partir sur la même branche que lui. Introduction : Vous voulez apprendre à créer votre CMS metin2 ? Mais vous ne vous pensez pas capable d'apprendre tant de langage de programmation ? Pas d'inquiétude. Je vais vous guider pas à pas (dans l'élimination des pages de publicité... Ah non désolé ça c'est une pub !) pour vous familiariser avec l'HTML et le CSS. Je vous donnerais également quelques astuces en JavaScript, chose que il me semble @Takuma n'avait pas fait. Puis dans le même temps, nous allons parler de PHP, et même l'utiliser. HTML : L'HTML est un langage de balisage. Il permet de donner une structure à votre code. Tout seul, il ne rend rien de beau. la mise en page s'effectue avec un autre langage. CSS : Nous parlions de mise en page ? La voici. Le CSS est un lagage de style, il permet de mettre en page votre site web. Vous pouvez déjà commencer rien qu'avec ses connaissances basiques à créer un site web. Cependant, il sera nommé site statique. Pour la simple raison que aucune interaction ne sera possible avec votre site. Ces deux langages permettent une création de page de présentation par exemple. Impossible de faire un espace membre digne de ce nom ou autre. JavaScript/JS : C'est un langage de script. Il permet de faire des pages webs interactives. Malheureusement, nous ne pouvons nous contenter de celui-ci. Il est accessible au client, c'est à dire qu'il peut le modifier, et faire ce qu'il veut de son côté. PHP : Le PHP ! Mon petit chouchou. Le PHP lui, par rapport aux trois aux langages ci-dessus, est exclusivement interprété par le serveur, le client ne recevra jamais aucune ligne de PHP. Votre PHP est traduit dans les trois langages ci-dessus avant d'être envoyer au client. Cela vous permet des opérations sécurisées et efficaces. Ne négligez cependant pas la sécurité de votre site web, elle est primordiale. PHP est très pratique, mais s'il n'est pas maîtrisé, en mettre sur votre site peut-être comme vous tirez une balle dans le pied. Notre site web, se devra d'être complet. Comme je n'ai pas le temps de créer à la main un design, je ne l'évoquerai pas. Vous pouvez cependant utiliser vous des designs déjà partagés. Ou même créer le votre, vous trouvez tout ce dont vous avez besoin sur internet en vous renseignant sur l'HTML et le CSS. Avant de vous dire ce que contiendra notre site web, je dois vous parler de la canon, la belle, la jolie, la sacrée, la légendaire, la divine et céleste documentation PHP ! Elle est complète, claire, disponible en français, courte et efficace. Ce sera pour mes tutoriels votre bible. Oubliez vos religions elle, vous prierez elle, et seulement elle. Bien ! Que va contenir notre futur site web alors ? Un système de news, vous pourrez les gérer entièrement sans aucune manipulation technique grâce à son panneau d'administration qui vous permettra : Ajouter une news, Supprimer une news, Modifier une news, Épingler des news afin qu'elles soient visibles en priorité par rapport aux autres. Un espace membre complet : Inscriptions : Nom de compte, Mot de passe crypté Mot de passe clair si l'utilisateur le désire en cas de perte de mot de passe pour ne pas avoir à remettre à 0 son mot de passe : Panneau de prévention en quoi cette option est déconseillée. Mail valide. Acceptation des règles. Connexion, Gestion du compte : Suppression du compte, Modification du mot de passe, Gestion du mot de passe entrepôt si celui-ci est ouvert IG (activé), Changer son adresse mail. Inscription avec validation par mail. Un système de vote : Vérification du vote : Si le joueur a bien voté Si le temps entre les vote est respecté Accréditation des crédits si le vote est validé. Classements : Classement joueurs complet : Top 10 en page d'accueil, Classement habituel avec les pages. Classement de guilde complet : Top 10 en page d'accueil en fonction du niveau et des victoires. Classement habituel avec les pages. Option permettant de trier le classement par ordre décroissant/croissant. Option permettant de chosire le facteur pris en compte pour la classement : Niveau Nom (A->Z ou inversement) Nom du chef (A->Z ou inversement) Energie etc... Un ItemShop complet et opérationnel : Gestion des catégories : Ajout, Suppression, Modifications (ordre, nom, etc...). Gestion des items : Ajout, Suppression, Modifications (ordre, prix, nom, etc..). Les icons seront placés automatiquement grâce au pack icon qui sera uplodad dans une destination bien spéciale. Système de langue : Un fichier spécifique par langue, Modifiable sur le site, Choix pour chaque personne la langue désirée qui sera stockée grâce à un système de cookies s'ils sont acceptés par le client. Système de support : Comptes : Super-Administrateur (compte d'ID 1) : Ajout de Super Administrateur, Suppression, Tous les droits. Administrateurs (Implementor) : Toute la modérations et l'administration nécessaire. Modérateur (GM) : Toute la modération permettant de répondre au sujet sans avoir tous les droits d'un administrateur. Système d'installation : Configuration générale du site : Nom, Date, Copyrtight, etc... Configuration des base de données : Base de données du site : Nom de la base de données. Base de données du jeu : Nom des bases de données : Player, Account, Common. Adresse IP, Utilisateur, Mot de passe, Port, Administration : Configuration générale du site, Gestion des GM, Configuration des langues Gestion du jeu. J'oublie beaucoup de paramètre. Mais quand nous aurons déjà fait tout ça, nous aurons déjà travailler un petit bout de temps ! Vous êtes prêts ? Démarrage du tutoriel... Les pré-requis: Pour suivre ce tutoriel, vous n'aurez pas besoin de beaucoup de chose, mais elles seront toutes obligatoires. Un éditeur de texte adapté. Je ne compte pas vous faire un cours sur l'édition du code avec un bloc note, foncez prendre un éditeur adapté tel que : Sublime texte NotePad++ Atome DreamWeaver Un serveur WEB : Distant : Hebergement mutualisé VPS, dédié avec Apache Locale : EasyPHP, WAMP, XAMP. Des bases de données : Player Common Account Website Si vous avez besoin d'aide pour ceci, je vous aiderais en pv. Où non demandez à @Takuma ça l'occupera, d'après ce qu'il m'a dit il n'a rien à faire de ces vacances d'étudiant. Fainéant. L'arborescence : Mon arborescence est beaucoup utilisée, mais est des fois assez bizarre. Je vais m'adapter à une arborescence plus connu, cela donne : assets css js img pages db.php functions.php admin capatcha index.php Nous ajouterons plus tard des dossiers comme celui de l'installateur par exemple, mais celui-ci se fera à la fin. En attendant pour conclure cette première partie au même point que @Takuma l'avait fait, je vais vous faire créer une connexion PDO. La connexion à la base de données principale : Bien, pour ceci, nous allons nous rendre dans notre fichier index.php : Nous allons tout d'abord dire au site : "Je veux écrire du PHP", pour cela il n'existe qu'un moyen (enfin deux... mais utilisez celui là) : <?php //J'écris ici mon code PHP ?> Notez que tout le contenue précédé de // dans sa ligne est nommé commentaire. C'est à dire qu'il ne sera pas interprété par PHP, vous pouvez mette ce que vous voulez. Ce code ne sera pas visible pas le client non plus. Ici, à la différence de @Takuma Je vais directement inclure mon fichier nommé functions.php dans mon index. Ce fichier va contenir toutes les fonctions PHP, mais aussi la connexion à la base de données, vous verrez. <?php require_once('assets/functions.php'); ?> Il existe trois façon d'inclure du code d'un fichier dans un autre : require_once include require Ces méthodes ont toutes les trois le même but, mais elles ont des subtilités : include : Vous pouvez importez autant de fois que vous voulez le fichier. Si le fichier n'est pas trouvé le code ne s'arrête pas, et continue quand même. require : Vous pouvez inclure autant de fois que nécessaire le fichier dans un autre. Cependant, si une seule fois le fichier n'est pas trouvé, le code s'arrête net, puis affiche une erreur. require_once : Vous pouvez inclure seulement une seule fois un fichier dans un autre, et celui-ci doit obligatoirement être là, sinon erreur et arrêt immédiat du code. Bien, une fois ceci tapez, ouvrons notre fichier functions.php (assets/functions.php), puis tapez : <?php require('db.php'); ?> Ici, nous donnons cette instruction au fichier : Tu importes le fichier db.php qui se trouve dans la même destination que toi, si tu ne le trouves pas, tu coupes tout Franky ! Pourquoi ne pas avoir faire un require_once ? Vous comprendrez plus tard, nous allons importer souvent ce fichier. Passons pour finir à l'édition de notre fichier db.php : Nous allons créer quatre variables : IP : Elle contiendra l'adresse d'accès à nos base de données USER : Elle contiendra l'utilisateur PASS : Elle contiendra le mot de passe WEBDBNAME : Elle contiendra le nom de la base de données du site. Nous allons comme @Takuma créer une variable (un peu spécial) qui va contenir notre connexion PDO. Créons déjà nos variables, en PHP pour créer une variable nous utilisons : $ puis le nom de la variable (ne doit pas commencer par _ car c'est le prefix des super variables, nous verrons ça plus tard). Cela donne : <?php $IP = "192.168.1.29"; $USER = "root"; $PASS = "pF5tfiUH3f7KDyT49rQu52Hu88"; $WEBDBNAME = "website" ; ?> Adaptez selon vos identifiants. Nous allons par contre rajouter un petit code qui vous nous permettre d'afficher absolument tous les warnings/errors possibles et imaginables dès qu'il y en aura en ajoutant ces deux lignes avant la déclaration de notre variable IP : <?php ini_set('display-errors', true); error_reporting(E_ALL); ?> (Je n'ajoute les balises PHP que pour que vous puissiez profiter de la coloration syntaxique). Bien créons maintenant notre connexion dans un bloc try cela donne en théorie : Essaye de : Te connecter Si cela echoue : Tu coupes le code Tu affiches l'erreur Bien, je vous donne le code, vous ne pouvez pas le devinez de toute façon, en tout cela donne : <?php ini_set('display-errors', true); error_reporting(E_ALL); $IP = "192.168.1.29"; $USER = "root"; $PASS = "pF5tfiUH3f7KDyT49rQu52Hu88"; $WEBDBNAME = "website" ; try { $db = new PDO('mysql:host='.$IP.';dbname='.$WEBDBNAME, $USER, $PASS,array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8')); } catch (PDOException $e) { print "DATABASE ERROR : " . $e->getMessage() . "<br/>"; die; } ?> Et voilà, vous avez crée votre variable $db qui contient les informations nécessaires à l'utilisation complète de PDO. J'espère avoir été claire, désolé j'ai pas vraiment l'habitude de faire des tutoriels. D'habitude je reste un peu inactive, en ninja sur les autres forums. Bonne lecture !
  8. tutoriel [Tutoriel]La BDD de RaiderZ

    Bonjour à tous ! Je vais vous expliquer comment fonctionnent les bases de données de RaiderZ, comment s'y repérer, et à quoi servent les différentes tables. Pour commencer, le moteur de la base de donnée est PostGreSQL. C'est un choix fait parce que les tables utilisent des types de données personnalisés, qui ont nécessité un moteur les supportant. Le jeu comporte trois bases de données, Account, Game et Log. Account La base de donnée Account contient les tables des comptes, des serveurs, et des mondes. Pour les tables: rz_account : Contient les informations sur les comptes des joueurs; rz_server: Contient les informations sur les serveurs du jeu; rz_server_status: Permet de récupérer les statuts des serveurs; rz_world: Contient les informations sur les mondes lancés. Game: rz_buff: Contient les informations sur les buffs rz_character: Contient les informations sur les personnages rz_cutscene: Contient les informations sur les cinématiques rz_emblem: ?? rz_equipment: Contient les informations sur les items équipés par les joueurs rz_faction: Contient les informations sur les relations entre les joueurs et les factions rz_gem_enchant: Contient les informations sur les enchantements de gemmes rz_guidebook: Contient les ID des items qui servent d'aide à la première connexion rz_inventory: Contient les données des inventaires des joueurs rz_palette: ?? rz_quest: Contient les informations sur les quêtes en cours rz_quest_history: Contient les informations sur les quêtes terminées rz_recipe: Contient les recettes de craft connues par les joueurs rz_storage: Contient les items du dépôt des joueurs rz_talent: Contient les informations sur les talents appris par les joueurs rz_talent_cooltime: Contient les informations sur les talents actuellement utilisés rz_user_item: Contient les informations sur les items en circulation dans le jeu Log: La BDD Log est vide pour le moment, elle doit contenir les informations sur les logs du jeu, pour les connexions, les items utilisés, les talents appris, etc... Je continue à chercher l'utilité des tables restantes, si vous le savez n'hésitez pas à le dire !
  9. Bonjour à tous ! Aujourd’hui je vais vous apprendre à mettre en place un serveur privé RaiderZ ! /!\ Ce tutoriel ne demande aucune connaissance particulière en informatique, et est accessible à tous /!\ Pré-requis : Un ordinateur ou un VPS utilisant Windows 7 ou plus PostGreSQL Driver ODBC PostgreSQL Navicat 12.0, DataGrip, ou n’importe quel programme de gestion de BDD fonctionnant avec PostgreSQL Le DevKit de RaiderZ Notepad++ /!\ Ce tutoriel est réalisé avec PostgreSQL 10.1 et Navicat 12.0.13 /!\ I) Installation de PostgreSQL Lancez le fichier d’installation, et suivez les étapes jusqu’à la création du mot de passe de l’utilisateur par défaut. Entrez le mot de passe que vous voulez (c’est celui qui sera utilisé plus tard pour configurer le serveur). Une fois l'installation finie, décochez la case Stack Builder et cliquez sur "Terminer" II) Installation de Navicat → Voir[Partage]Navicat 12.0.13 III) Configuration de PostgreSQL A) Création des BDD Pour créer les tables, allez dans votre menu démarrer, et cherchez: Lancez le programme, et validez par "Entrée" jusqu'au mot de passe: Vous vous souvenez du mot de passe que vous avez mis à l'installation? Tapez le /!\ Le mot de passe ne s'affiche pas quand vous le tapez, c'est normal ! /!\ Une fois que vous êtes connecté, vous pouvez taper la commande suivante: Puis validez par "Entrée". Faites la même chose pour les deux bases de données restantes, en validant bien par "Entrée", et en oubliant pas le ; à la fin: Vous pouvez fermer, vos tables sont créées! B) Configuration de Navicat La configuration de Navicat est assez simple, commencez par ajouter une nouvelle connexion PostgreSQL: Puis configurez le serveur comme sur le screen: Dans le champ "password", mettez votre mot de passe PostgreSQL. C) Restauration des tables C'est sans doute la partie la plus compliquée du tutoriel, accrochez vous ! Cherchez le dossier d'installation de PosgreSQL, et allez dans le dossier "bin": /!\ Si vous ne l'avez pas modifié, c'est C:\Program Files (x86)\PostgreSQL\10\bin /!\ Une fois que vous avez trouvé le dossier, ouvrez une fenêtre de commande et faites un cd pour arriver jusqu'au dossier: Vous êtes prêt à lancer les commandes ! Pour créer les tables, vous devez utiliser la commande suivante: psql : Nom du programme principal de PostgreSQL -U postgres : Nom d'utilisateur pour se connecter à la BDD -d rz_account : Nom de la base de donnée -f C:\Users\...\rz_accountdb.sql : Fichier de sauvegarde à restaurer Une fois la commande tapée, validez avec "Entrée", vous devriez avoir ceci qui s'affiche dans la fenêtre de commande: Vous connaissez la chanson: tapez votre mot de passe PostgreSQL Une fois le mot de passe validé, la restauration se fera automatiquement, et vous devriez voir ceci: Faites la même chose, mais pour la base de donnée rz_gamedb avec le fichier rz_gamedb.sql la commande devrait mettre quelques dizaines de secondes à se finir. Après ça, vous pouvez fermer la console, on en a fini avec elle ! Pour vérifier que vos tables sont bien mises, vous pouvez allez vérifier dans Navicat: IV) Configuration des files Dans chaque dossier du serveur se situe un fichier server.ini. C’est lui qui contient la configuration De chaque programme du serveur. Vous devez ouvrir chaque fichier server.ini, et remplacer à l’intérieur de cette ligne : PASSWORD = "password" password par votre mot de passe PostgreSQL. Exemple: V) Installer le driver ODBC Ouvrez le fichier d’installation des drivers ODBC, et installez le. Allez dans Panneau de Configuration → Outils d’administration → Sources de données ODBC (32 bit): /!\SI VOUS N'AVEZ PAS L’ICÔNE ODBC/!\ (Merci Saya pour l'astuce ! ) Une fois dans le menu des sources de données, rendez vous dans l’onglet Utilisateur, cliquez sur « Ajouter », et sélectionnez « PostgreSQL Unicode » dans la liste, puis cliquez à nouveau sur « Ajouter »: Configurez le driver comme suit : Cliquez ensuite sur "Tester", si ce message: s'affiche, c'est que tout est bon ! V) Relier le client au serveur Pour relier le client au serveur, ouvrez le fichier « RaiderZ Run.bat », et changez l’adresse IP par celle de votre serveur. Exemple : ./START Raiderz.exe localhost Exemple : ./START Raiderz.exe 142.89.32.16 Voilà, vous avez un serveur complet fonctionnel ! Il ne vous reste plus qu'à le lancer ! VI) Lancement du serveur Pour lancer le serveur, vous devez simplement lancer, dans n'importe quel ordre, les quatre programmes: LoginServer AppServer GameServer MasterServer VII) Quitter le serveur Pour quitter le serveur, vous avez juste à fermer le programme "MasterServer", et le reste suivra ! Pour continuer: Si le tutoriel vous a été utile, laissez un point de réputation ou un commentaire, ça fait toujours plaisir !
  10. Bonjour à tous ! Petit tutoriel assez court pour vous apprendre à utiliser le DevKit de RaiderZ ! Pour commencer, le matériel nécessaire: Visual Studio 2017 Community Edition ou plus Commencez par décompresser les sources dans un dossier: Le dossier "Develop" doit être dans le même dossier que "SDK". Ensuite, pour ouvrir les sources, ouvrez la solution All.sln Vous devriez voir l'ensemble des projets: Les sources sont séparées en grandes catégories: Client contient les projets relatifs au lanceur du jeu; Common contient les projets communs au client et au serveur; mdk contient les sources du moteur de jeu; Serveur contient les sources des serveurs. Pour compiler, vous devez sélectionner une des configurations suivantes dans le menu des configurations: Client: RELEASE_CLIENT Serveur: RELEASE_SERVER Une fois la bonne configuration de solution sélectionnée, vous n'aurez plus qu'à lancer la compilation ! Les fichiers sortiront dans le dossier Develop/Out/Nom_Du_Projet/Nom_De_La_Configuration. Voilà pour les explications basiques ! Si vous rencontrez un problème sur l'utilisation des sources, passez en A/Q/S !
  11. Hello Funky Emu ! Pour la sortie tant attendue de la V6 de notre beau forum, j'avais bien envie de reprendre un peu de service en vous partageant quelques notions et nouveaux concepts d'un univers que j'apprécie tout particulièrement depuis 2 ans : le Web. Plus précisément, je vais essayer de vous dresser le tableau d'une toile qui est sujette à de fortes mutations depuis quelques années, tant au niveau de son utilisation par ses grands acteurs que pour ses fondements techniques, très différents de la norme d'il y a dix ans. Ayant démarré le développement informatique sur ce même forum il y a maintenant 6 ans sur du web en PHP (pour des CMS Dofus), vous retracer mon exploration de ce domaine et ses technologies me fait vraiment plaisir, et j'espère que ça vous intéressera tout autant que moi. Bien que maintenant assez éloigné de l'émulation, il m'a semblé que quelque soit le jeux émulé, son écosystème (CMS, émulateurs, autres outils) n'évoluait que très peu au cours des années et qu'il faisait souvent appel aux mêmes architectures, librairies et autres composants : bref, que l'émulation ne bénéficie pas toujours des progrès de l'aire du numérique. C'est donc dans cette idée que je tente d'amener un petit vent frais sur la section programmation, en espérant que certains soient suffisamment curieux pour me faire des retours et me dire si approfondir le sujet plus loin les intéresserait, ou même mettre en application eux-même ces outils à travers des projets persos. Sommaire Petit historique NodeJS, du Javascript côté serveur NPM Les Single Page Application ReactJS Angular Le packaging Le Backend Les SPA isomorphiques Conclusion 1/ Petit historique Commençons par rappeler rapidement la traditionnelle stack (pile de technologies) LAMP encore très utilisée aujourd'hui pour des sites webs : Linux + Apache + Mysql + PHP. Ce n'est bien sûr pas la seule solution qu'il y avait avant les nouvelles technologies, nous pouvons par exemple citer Ruby on Rails ou J2EE qui ont aussi eu leur lot de popularité, mais LAMP reste certainement l'architecture la plus connue et répandue. Dans ce schéma, le navigateur de l'utilisateur (client) envoie une requête HTTP au serveur Web Apache (serveur), Apache exécute le fichier php demandé qui lui renvoie la page HTML après avoir requêté si besoin MySQL. Il ne reste donc à Apache plus qu'à renvoyer cette page au navigateur alors prêt à l'afficher, et exécuter le code javascript y étant contenu. A noter que chaque changement de page entraîne alors une nouvelle requête consommant des ressources serveur pour un traitement souvent en bonne partie identique à celui de la requête précédente. Jusqu'à il y a quelques années, le javascript exécuté chez notre client était majoritairement utilisé pour des animations esthétiques ou pour dynamiser certaines pages via des requêtes AJAX. Entre les mauvaises performances et incompatibilités des différents moteurs Javascript (= composant d'un navigateur chargé de l'exécution du javascript), ce langage a longtemps été moqué et c'est pourquoi il n'était alors utilisé que pour des tâches visuelles ou peu critiques. V8, la résurrection du Javascript V8, c'est un moteur Javascript open-source développé par Google pour son navigateur Chrome, la première release datant de 2008. Ce qu'il a de particulier, ce sont ses performances : utilisant une compilation JIT (Just In Time: a la volée) du Javascript en bytecode (langage machine), V8 s'est avéré être assez performant pour, au cours de ces dernières années, remettre complètement en question la place du Javascript. C'est aussi une course à la performance dans laquelle se sont ainsi lancés tous les géants du web : Mozilla sur son moteur Javascript SpiderMonkey (le tout premier moteur javascript initialement développé par/pour Netscape), Apple sur son JavascriptCore (Safari), Microsoft sur Chakra (Edge/IE), ... L'enjeux de cette course, c'est ni plus ni moins que de savoir qui pourra le plus bénéficier des nouvelles possibilités ouvertes par ce langage propulsé à une échelle bien supérieure à tout ce qu'il a pu connaître. Mais Jamie, à quoi ressemble donc ce nouveau champs des possibles !? 2/ NodeJS, du Javascript côté serveur Pour commencer, non, NodeJS n'est bien sûr pas la première solution utilisant du JS côté serveur. L'expérience avait bien déjà été réalisée par Netscape avec la sortie de Rhino en 1997, un autre moteur Javascript prévu pour du server-side. Mais si l'utilisation de NodeJS a tout simplement explosé quelques années seulement après sa sortie en 2009, c'est sans aucun doute parce que son auteur Ryan Dahl a su les réunir les bons éléments au bon moment : Un moteur javascript assez performant pour rendre le JS côté serveur viable L'implémentation d'une gestion de modules au format CommonJS (Nativement et avant le standard ES2015, le Javascript ne permet pas d'inclure d'autres fichiers) Des modules permettant l'interaction avec des composants de base comme le système d'exploitation, le système de fichier, les sockets Un gestionnaire de module (npm) permettant l'organisation d'une communauté open source publiant des modules répondant à tout type de besoin L'asynchrone du Javascript utilisé avec un modèle concurrentiel (synchronisation des tâches asynchrones) fiable et efficace. Lorsqu'il reçoit une requête, un serveur Java standard crée un nouveau thread pour la gérer : ce modèle peut causer des problèmes d'accès concurrentiel à la mémoire, de nombreux threads bloqués en attente du retour d'une opération, un trop grand nombre de threads pour les capacités de la machine ... A son inverse, NodeJS fonctionne avec un unique thread disposant d'une queue listant toutes les tâches devant être exécutées. Lorsque vient le tour du traitement d'une nouvelle requête, les instructions sont donc exécutées linéairement jusqu'au prochain appel asynchrone dont le traitement du retour sera ajouté à la file d'attente. Mais tant qu'une instruction asynchrone n'est pas terminée, Node continue de traiter les autres requêtes sans être bloqué. De cette façon, la productivité de votre thread est maximisée, à l'opposé d'un langage comme le Java qui encapsulera ses appels bloquants dans de multiples threads régulièrement rendus 'inactifs'. Bien sûr, cela ne veut pas dire qu'un environnement node sera toujours plus performant que du Java, loin de là, mais c'est sûrement vrai pour de nombreux services webs ne nécessitant pas de lourds calculs CPU. Si vous êtes intéressé par le sujet, cet article décrit le fonctionnement de Node un peu plus en détail et dans quelles situations il prend tout son intérêt. Ce sont je pense les éléments clés de cette plateforme de développement JS. Pour les curieux, voilà les quelques lignes suffisant à ouvrir un serveur en écoute avec NodeJS (extrait de la doc nodejs) : Comme vous pouvez le voir, ce serveur est minimaliste. A vous d'indiquer le code retour de chaque réponse (200 pour un succès), de renvoyer votre contenu avec une autre méthode (end), et même le dispatch vers la bonne unité d'exécution en fonction de l'URL devra être fait en regardant celle-ci vous même via le paramètre req. Certains seront ennuyés par tout ce superflu, d'autres apprécieront le grand contrôle qu'ils ont sur le cycle de vie d'une requête. Quoi qu'il en soit, n'oubliez pas que de nombreux modules vous prémâchant le travail existent à tous les niveaux. C'est ce que fait très bien ExpressJS pour la partie HTTP : Je pourrai vous parler des nombreux autres modules et middlewares venant se plugger à express (comme pour de la gestion de session), mais ce n'est pas l'objectif de ce sujet. Retenez qu'une solution web à base de node + express serait très bien pour écrire une API (afin d'interfacer votre utilisation d'une base de donnée ou tout autre service), ou simplement servir des fichiers, mais que pour un site web classique (comme un CMS), Node sera largement insuffisant. On pourrait bien sûr entièrement réaliser un site avec uniquement NodeJS, mais on se retrouverait à générer puis renvoyer bêtement de l'HTML au navigateur en perdant tout l'intérêt du langage par rapport à du PHP . Nous allons rapidement parler du package manager de Node sur la prochaine partie, juste avant d'entamer la partie la plus intéressante, au sujet de ces technologies JS révolutionnaires qui déferlent sur notre web. 3/ NPM Comme mentionné au début de l'introduction de Node, celui-ci dispose de son propre package manager : Node Package Manager. Il a aussi son propre site, sur lequel vous pouvez chercher et souvent facilement trouver réponse à votre besoin. Une telle communauté s'est aggloméré autour de npm que son utilisation s'est étendue à tout type de projet JS, utilisant ou non Node. Par exemple, si vous devez faire une application entièrement front ne nécessitant même pas l'utilisation de node, vous commencerez quand même sûrement par créer un projet npm, télécharger les modules de vos technos, puis de setup votre bundler (c'est la 7ème partie) de sorte à pouvoir produire une solution finale contenant chacune des dépendances installées par npm. Et pour le bonheur de tous, l'utilisation de npm est des plus simples : npm init : cette commande permet de créer un nouveau projet npm en initialisant tout simplement le fichier package.json, la configuration de npm pour ce projet npm install : lorsque vous téléchargez / clonez un projet npm (qu'on repère donc à la présence d'un package.json), cette commande téléchargera chacune des dépendances listées dans le package.json npm install --save package_name : Si vous ajoutez un nom devant npm install, cela installera simplement le package en question. L'option --save permet d'ajouter aussi la nouvelle dépendance au fichier package.json, afin qu'il soit installé lors d'un simple npm install. npm uninstall --save package_name : vous l'aurez compris, c'est l'opération inverse de celle-ci dessus. --save retire donc la ligne du package.json. npm run script_name : au début de votre package.json, vous pouvez voir un objet scripts. Il contient toutes les commandes shell que vous pouvez exécuter avec cette commande, en lui passant en paramètre le nom du script désiré. Très utile pour minifier l'appel de longues commandes que vous devez régulièrement utiliser. Nous verrons un cas d'utilisation très pratique partie 7. npm start : si votre objet scripts contient bien une entrée 'start', exécute celle-ci. C'est une forme raccourcie de npm run start qui ne fonctionne que pour start, une commande réservée au lancement de votre application. C'est donc généralement la deuxième et dernière commande à exécuter après avoir téléchargé une application npm simple, juste après le npm install. Bon, on parle beaucoup d'application js, mais quand est-ce qu'on y vient vraiment ?! Et bien, l'heure est venue ... 4/ Les Single Page Application Lors du petit historique de ce début de sujet, nous disions qu'avec une stack LAMP classique, chaque changement de page / envoi de formulaire entraînait une nouvelle requête entre le client et le serveur. Et si je vous disais qu'aujourd'hui, le serveur pourrait ne plus recevoir la moindre requête d'un utilisateur une fois que celui-ci a eu sa première page demandée ? Que les changements de page se feraient donc de manière quasi instantanée sans que le client doive attendre le rafraîchissement de sa page ? C'est ce qu'est une SPA, une application dont l'intégralité du code se trouve dans une seule page. Une fois celle-ci téléchargée, le client peut naviguer de manière très fluide à travers les différents écrans de l'application, n'envoyant de nouvelles requêtes que pour valider un formulaire ou récupérer de nouvelles données à afficher dans une portion bien précise de la page. En fait, c'est un type d'application qu'on a tous déjà l'habitude de côtoyer : n'importe quel réseau social moderne (Facebook, Twitter, Linkedin, Youtube, ...) est une SPA. Qu'est-ce qui nous empêche donc de nous tourner vers cette solution moderne ? Les pré-requis sont assez limités : connaissance du Javascript, curiosité des nouvelles technologies. Pour le premier point, la curiosité peut aussi faire l'affaire. Pour le deuxième, c'est précisément ce sur quoi j'espère vous ouvrir. De nombreux frameworks web front-end défilent depuis quelques années, chacun avec une approche du problème différente, mais de manière générale plus travaillée au cours des années. Certains diront peut-être que je vulgarise un peu trop le 'problème', mais je pense que cette problématique cruciale et commune à tous les frameworks est ni plus ni moins que la synchronisation entre l'état d'une application et son DOM (Document Object Model). Par cet état, j'entends l'ensemble des variables définies statiquement ou dynamiquement (depuis une requête au backend par exemple), pour être ensuite affichées au milieu du code HTML. Le DOM, c'est l'arborescence de chaque bloc html (en partant de la racine <html>) construite en mémoire du navigateur après l'interprétation du code HTML, ou de toute manipulation faite en Javascript. Par exemple, pour les quelques courageux addicts au VanillaJS, faire un elem.appendChild(document.createElement('span')) revient à ajouter dynamiquement un <span> dans elem, via ce fameux DOM virtuel. Même chose pour les déjà-plus-raisonnables pro-jQuery : $(".inner").append("<p>Test</p>"); Cependant, ces fonctions sont pratiques quand il s'agit de changer quelques parties précises de notre page, mais deviennent très vite laborieuses si on veut construire et mettre à jour l'ensemble de notre page html en Javascript. C'est donc notamment à ce problème que répondent les célèbres frameworks front-end Ember, Backbone, React, Angular et de nombreux autres. Je me contenterai de ne vous présenter succinctement que les deux derniers, étant les plus modernes / populaires et aussi les deux seuls que je maîtrise. Ces librairies vous permettront de construire des applications web entières comme de plus petits modules que vous pourrez tout aussi aisément intégrer dans un site déjà existant, comme par exemple un chat. 5/ ReactJS ReactJS, c'est le petit bébé développé par et pour Facebook (entre autres) depuis 2013. Si le mobile intéresse certains, il se trouve que Facebook a aussi sorti en 2015 React-Native, un framework JS mobile permettant de développer des applications compatibles Android et iOS, utilisant la même organisation et suivant les mêmes principes que React. En moins de 3 ans de développement, des milliers de contributeurs ont déjà rejoint le projet et les releases se suivent à intervalles très courtes, de sorte que React-Native soit déjà tout à fait viable en production . Je tiens aussi à arrêter tout de suite les médisants : non, react-native n'est pas un framework hybride (Xamarin, Ionic ... : applications mobiles embarquant une webview), ou alors à moitié. React-Native utilise JavascriptCore (Moteur JS de Safari) pour exécuter votre code JS qui va faire appel à des composants codés nativement pour les deux plateformes : votre application communiquera donc bien avec le système hôte et non un moteur de rendu. Mais revenons à nos moutons, React. Et quoi de mieux pour introduire une technologie que son implémentation de l'indétrônable Hello World: Contenu Masqué Sa particularité, c'est son organisation centrée autour de ses components. Un component, c'est comme un nouveau bloc html qui reçoit des paramètres et se formate correctement tout seul. Dans l'exemple ci-dessus, le component HelloWorld déclaré au début du fichier index.jsx ne reçoit qu'un seul paramètre name pour afficher une simple div avec. <HelloWorld name="Funky Emulation" /> Cette syntaxe HTML, qui peut paraître déroutante au beau milieu du code Javascript, est en réalité transformée en une instruction Javascript valide par le module JSX à l'exécution : React.createElement(HelloWorld, {name: 'Funky Emulation'}) On se doute qu'une application React serait tout simplement immonde à écrire sans ce petit JSX incontournable, dont vous pouvez d'ailleurs voir la balise d'inclusion au début du fichier html d'exemple. Bien, je vous ai montré l'éternel et simple exemple d'Hello World, mais qu'en est-il si on veut dynamiser un peu tout ça ? Une TODOLIST par exemple ? > Plunkr < Dans ce bout de code, plusieurs choses nouvelles. Déjà, nos components sont codés en classes Javascript (feature disponible depuis ES2015), une façon de faire que je préfère nettement aux simples fonctions qui ne permettent pas d'organiser les différentes fonctions de traitement du component aussi clairement qu'avec une classe. Remarquons par ailleurs que chacune de ces classes hérite de React.Component, la classe mère de tous les Components. Ensuite, de nouveaux attributs HTML sont apparus : onSubmit={this.onSubmit.bind(this)} dans le form du component Input permet d'appeler la méthode onSubmit lors de l'évènement onsubmit (envoi du formulaire). De même, onChange={this.onChange.bind(this)} situé juste en dessous permet d'appeler la méthode onChange à chaque nouvelle valeur de l'input. A noter la présence d'accolade lors de l'assignation de ces attributs, qui permet d'injecter du code JS dans ce 'pseudo html' interprété par JSX. Nous pouvons aussi voir une nouvelle assignation this.state = ... dans chaque constructeur : c'est une variable propre à React et contenant l'état interne d'un component. Avec le HelloWorld, nous avons vu la notion de props permettant d'injecter des variables au sein de component, afin de faire descendre une information depuis un component container jusqu'à un component contenu (par la suite, j'appellerai le container 'parent', et le contenu 'enfant'). La différence du state, c'est qu'il ne sera pas rempli par son component parent mais par son propre traitement (le curValue du component Input), ou par l'action d'un component enfant (le state.todolist dans le component App). Le point commun et l'intérêt de ces deux variables, c'est que le moindre changement de leur valeur forcera l'actualisation du component. Pour le state, l'affichage ne sera cependant rafraîchi que en le modifiant via la fonction setState (héritée de React.Component), afin d'en informer React. Il y a un deuxième point capital à savoir pour le bon déroulé du refresh et très spécifique au fonctionnement de React : lorsqu'on modifie une valeur contenue dans state ou transmise en props à un component enfant, et si c'est un objet, il doit être un clone modifié de l'ancienne valeur. Ce qui explique l'étrange syntaxe que j'ai utilisé pour la fonction d'ajout d'un TODO dans mon component App : newTodo(txt) { this.setState({ todolist: [txt, ...this.state.todolist] }) } La valeur assignée à todolist est en fait un nouveau tableau composé en premier de la nouvelle valeur, suivie de toutes les valeurs de l'ancien tableau (ce que permet de faire les '...', encore une nouveauté du JS). C'est grâce à la référence de ce nouvel objet que React pourra détecter les changements entre l'ancien state (ou props) et le nouveau : il parcourt récursivement chaque attribut du nouveau state (ou props), compare sa référence à celle correspondante de l'ancien state, et met à jour la partie de l'interface associée si elles sont différentes. Voilà un petit schéma (pardonnez mes tristes talents artistiques ) résumant les différentes interactions entre les 3 components (App, Input, Todolist) : App injecte : Sa méthode à appeler lors de l'ajout d'une entrée en props de Input Sa TODOLIST contenu dans le state en props de Todolist Input met à jour state.curValue à chaque changement du champs texte. Input appelle props.onAddTodo lors de l'envoi du formulaire en lui fournissant state.curValue onAddTodo() met à jour le state de App en reconstruisant le tableau todolist avec la nouvelle valeur Automatiquement, voyant que le props.todolist transmis à Todolist est un nouveau tableau, React déclenche la mise à jour du component C'est souvent de cette manière que sera construite une application React : les composants enfants interagissant avec l'utilisateur traiteront les données remplies par celui-ci en interne (state), pour ensuite les faire remonter au parent via une fonction transmise dans ses props, permettant au parent de stocker l'information et de la redispatcher à d'autres components. De manière générale, ce découpage en composants bien distincts est une excellente habitude à prendre et applicable dans n'importe quel contexte de développement, que ce soit ou non du Javascript, et même si ce n'est pas pour du web. Vous êtes ainsi forcé d'organiser clairement votre application en différentes unités chacune assignée à une tâche bien précise, dans un ensemble cohérent mais non couplé (lié trop étroitement) que vous interconnecterez par de l'injection de dépendance, à l'opposée d'une classe depuis laquelle vous instancierez ses dépendances. L'intérêt est multiple: chaque unité est facilement remplaçable, testable, et réutilisable. De plus, le travail collectif sur un tel projet en est aussi grandement simplifié. Bref, c'était juste une petite parenthèse pour vous sensibiliser à cette notion de conception utile dans tout contexte de dév, et que les attentifs pourront remarquer dans la prochaine partie ... 6/ AngularJS AngularJS, quant à lui, a été publié par Google pour la première fois en Octobre 2010, avant la sortie d'Angular en Mai 2016. Angular est officiellement la v2 d'AngularJS, mais en fait pas vraiment, leur plus gros point commun reste sûrement leur nom. En effet, après 6 ans de travail sur AngularJS et les retours de nombreux contributeurs, Google a certes gardé les grands axes de sa philosophie mais a construit très différemment Angular en fonction des points noirs relevés durant cette longue période : le système de détection de changement (synchronisation datas / views) a été bien plus réfléchi et ainsi largement optimisé, plusieurs notions ont été purement et simplement supprimées ou remplacées par soucis de simplification (AngularJS utilise de nombreux types de composants différents, ce qui est un frein à sa prise en main / bonne utilisation). Ces changements drastiques entre les deux versions a d'ailleurs été source de controverse auprès d'un grand nombre de développeurs, qui n'avaient juste plus affaire à leur outil habituel. Si la question vous intéresse, cet article et de nombreux autres en parlent en long et en large. Si je ne vous parle ici que d'AngularJS, ce n'est absolument pas parce que je suis un vieil aigri nostalgique rebutant Angular, mais uniquement parce je n'ai pas encore eu l'occasion d'utiliser ce dernier, bien qu'il m'intéresse tout à fait. Et en raison de mon idéal de l'évolution des choses où AngularJS disparaîtrait inévitablement dans l'ombre de son successeur, je vais vous dresser une présentation plus succincte d'AngularJS que pour ReactJS, vous laissant l'opportunité de vous y intéresser mais avec tout de même ma suggestion de vous tourner plus vers Angular. Exception faite des cas où on peut avoir tout intérêt à rester sur AngularJS, l'article cité au dessus en parle en fin de page. Quoi qu'il en soit, les grandes lignes directrices de la v1 restent selon moi intéressantes et instructives à voir. Comme pour React, commençons ce petit aperçu par un classique Hello World : Contenu Masqué Une première chose devrait vous sauter aux yeux, c'est la légèreté de la syntaxe par rapport à React. angular.module('app', []) : cette ligne renvoie un nouvel objet module lié au block html ayant l'attribut "ng-app='app'". Notez la présence du deuxième paramètre permettant de lier d'autres modules dépendants au nôtre dès sa création. Pour accéder au module 'app' après sa création, omettez simplement ce deuxième paramètre. Sinon, il sera réécrit et toutes les ressources liées perdues. .controller('MyCtrl', ... : cette instruction chaînée au module lui associe un nouveau contrôleur, nommé 'MyCtrl' et implémenté par la fonction en deuxième paramètre function ($scope) { $scope.name = 'Funky Emulation }) : La fonction en question, ainsi responsable de son scope Nous avons donc 3 notions centrales d'AngularJS : Le module, est un objet lié à un block html (ayant le bon ng-app) et contenant toutes les ressources nécessaires à la gestion du block en question. Parmi celles-ci, il y a cette liste de modules dépendants renseignée en deuxième paramètre du premier appel d'angular.module, et pouvant servir tout type de besoin : un module router pour amener le client sur la bonne vue en fonction de l'url, un module facilitant l'accès aux cookies, ... Bien souvent, ce sont des modules que vous pourrez télécharger via npm. Le contrôleur, est une simple fonction responsable de la logique métier de son block html lié et ses enfants. J'appellerai par la suite ce block la vue du contrôleur. Donc, la liaison entre le contrôleur et sa vue s'établit simplement avec l'attribut ng-controller sur cette dernière, que vous pouvez voir juste après l'ouverture du body. Le contrôleur accède à toutes ses dépendances via ses paramètres, et dans notre cas il n'en a qu'une : le $scope. Le $scope : cette variable est comparable au state d'un component dans React. C'est un objet contenant toutes les valeurs accessibles depuis la vue (et ses enfants) et uniquement depuis celle-ci. Donc quand je crée un $scope.name depuis mon contrôleur, j'injecte la même variable name au sein de ma vue. La documentation d'AngularJS à ce sujet est très complète et très instructive. Côté html, il suffit simplement d'utiliser une double paire d'accolade ({{variable}}) pour afficher une variable du scope. Cette syntaxe, repéré par AngularJS lors de son initialisation, crée un one-way-data-binding. En d'autres mots, la paire d'accolade devient un simple miroir de la valeur initialisée / modifiée depuis le contrôleur. Si la manière avec laquelle AngularJS maintient cette synchronisation (nommée dirty checking) vous intéresse, je vous invite à lire cet article qui discute et compare les façons de procéder de plusieurs frameworks dont React et AngularJS. En quelques mots et comme l'indique son nom, le dirty checking est aussi simple que bête, en testant chaque variable de scope affichée (toutes ne le sont pas forcément) après chaque action (un évènement onclick, un formulaire changeant de valeur, une requête ajax ...), à la recherche des différences. Si je vous parle du one-way-data-binding, c'est qu'il existe un two-way-data-binding : c'est ainsi qu'on crée un lien bidirectionnel entre une variable du scope et le champs d'un formulaire HTML. A l'inverse de React, plus besoin donc de dispatch la nouvelle valeur lors d'un évènement onChange, la synchronisation est maintenue "toute seule". Sans plus attendre, voici donc la simplissime implémentation de la TODOLIST cette fois avec AngularJS : Contenu Masqué Au niveau de l'index, on peut voir que notre vue est maintenant une simple balise fermante avec un attribut ng-include. C'était uniquement pour vous montrer qu'on peut bien sûr découper son template en plusieurs fichiers pour les inclure dans un deuxième temps. A noter que ce ng-include a toutefois un coût : le fichier en question est chargé depuis le client via une requête AJAX. Jetons maintenant un oeil au todolist.html, on peut y voir trois nouveaux attributs : ng-submit : équivalent du onSubmit chez React, permet d'appeler une méthode lors de l'envoi du formulaire ng-repeat : très utile, permet de répéter le block en question autant de fois que le tableau indiqué en paramètre a de valeurs, chaque nouveau block accédant à sa valeur via le nom placé avant le in ng-model : un autre trésor d'AngularJS aussi utile que ng-repeat, c'est le fameux attribut permettant la synchronisation entre le formulaire et la variable renseignée en paramètre. À noter aussi que cette variable est attribut d'un objet, c'est très important et permet d'éviter tout problème de synchronisation dans ce cadre-là. Une petite recherche google vous renseignera sûrement sur les raisons derrière ce petit 'tricks'. En fait, l'essentiel est déjà là, comme vous le comprendrez en voyant le contrôleur. Celui-ci se contente d'initialiser les variables todolist / form.todo, puis de déclarer son callback permettant l'ajout d'une entrée avec la réinitialisation du champs texte. Magique, n'est-ce pas ? Il y aurait de nombreuses (presque trop) autres notions à vous présenter au sujet d'AngularJS mais je ne m'y avancerai pas pour les raisons évoquées au début de cette partie. Si vous voulez aller plus loin avec AngularJS (ce que je peux complètement comprendre, c'est quand même très sexy ), je vous recommande de commencer par voir ce que sont les services, une des forces principales qui m'ont le plus plu au début (et c'est toujours le cas) ; une démonstration de l'injection de dépendances dans toute sa splendeur. Le tutoriel disponible sur le SDZ est par ailleurs et comme souvent un très bon début. 7/ Le packaging À travers les deux dernières parties sur des frameworks front-end, nous avons pu constater une chose : c'est un univers composé de multiples dépendances. Au final, tout comme le développement d'un software quelque soit le langage utilisé, sauf si votre application est une calculatrice en ligne de commande. Et bien souvent, un software peut s'installer voir s'utiliser directement via un unique exécutable. Qu'en est-il de notre application ? Nous n'allons tout de même pas inclure des dizaines de fichiers JS différents dans notre fichier html ? Ça marcherait, mais ce n'est sûrement pas la meilleure solution, ne serait-ce que pour le maintien de ces dépendances. Non, a la place, plusieurs outils permettent une gestion poussée de la compilation de tous vos fichiers en un bundle, ce fichier final et unique qu'il suffira de joindre à votre .html pour lancer l'application. Parmi les outils (taskrunners, packagers ou encore bundlers) les plus complets et avancés, on peut citer webpack ou gulp : tous deux permettent la concaténation de tous vos fichiers, leur minification, optimisation, lint-age (lint est une opération de débuggage de base) ... Il est aussi possible d'ajouter des traitements personnalisés, comme par exemple la précompilation de certaines extensions précises, comme pour des feuilles de style .sass. Personnellement, je n'ai jamais eu envie de m'intéresser à ce genre d'usine à gaz (malgré tout très performante, utile et utilisée) et leur configuration syntaxiquement bien trop verbeuse à mon goût. A la place, je vous propose d'écrire vos propres tâches npm de bundling. Les seuls pré-requis, c'est donc d'être sur un projet npm et d'installer les quelques modules nécessaires. Sans perdre de temps, voilà quelques tâches opérant un premier bundling de base : "bundle": "browserify ./entrypoint.js -o ./public/bundle.js", "bundle:watch": "npm run bundle && watchify ./entrypoint.js -o ./public/bundle.js -v", "bundle:prod": "browserify ./entrypoint.js | uglifyjs -mc > ./public/bundle.min.js" Ces lignes sont à placer avant la fin de l'objet "scripts", dans votre package.json. Chaque script s'utilise en ligne de commande dans le répertoire du package.json ; npm run bundle/bundle:watch/... Nous utilisons ici : browserify : cet utilitaire parcourt chaque fichier de votre application en commençant par le point d'entrée indiqué en premier paramètre, puis en suivant récursivement toutes ses inclusions de fichiers (instructions require) watchify : celui-là, il surveille tous vos fichiers et appelle browserify à chacune de leur modification. Très pratique pour la période de développement uglifyjs : Un incontournable une fois la phase de production arrivée, uglifyjs minifie votre code et renomme notamment tous vos symboles (variables / classes ...) Ne pouvant coder JS sans utiliser de feature Ecmascript récent, j'utilise aussi systématiquement babel, un transpileur Javascript . Son travail est tout simplement de réécrire chaque utilisation de syntaxe récente par un équivalent forcément portable. Pour lier babel au parcourt récursif de browserify, il nous faut aussi installer babelify. Nous pouvons alors dire à browserify de systématiquement appliquer babelify avec ces quelques lignes à ajouter dans votre package.json : "browserify": { "transform": ["babelify"] } Enfin, il faut indiquer à babel quels plugins nous voulons appliquer aux fichiers. En effet, beaucoup de nouvelles syntaxes sont apparues avec les dernières versions JS et chaque plugin babel n'en implémente que quelques unes. Chacun de ces plugins est un module node à installer, pour finalement les déclarer dans un fichier .babelrc à placer aux côtés du fichier package.json : { "presets": ["es2017", "es2015"], "plugins": ["transform-object-rest-spread"] } Ici, nous ajoutons la destructuration d'objet, ainsi que tous les plugins contenus dans les preset es2017 et es2015. Regardez sur ces pages babel pour installer les modules correspondant si vous les désirez. A noter que suivant votre environnement, toutes ces features JS peuvent ou non être disponibles. Sur un navigateur et à l'heure actuelle, babel est indispensable. Côté backend, avec node, ce site indique la portabilité de chaque feature sur les différentes versions de node. Une fois votre bundle généré, il suffit alors de l'inclure dans votre petite page .html servie par votre backend. 8/ Le Backend Bon, nous avons commencé ce topic par parler un peu de l'intérêt de NodeJS comme base pour développer une API web, avant de rapidement virer vers le front-end et toutes ses composantes. Je trouvais important de donner la priorité à la question NodeJS par soucis historique, et je ne suis certainement pas la bonne personne pour vous en apprendre plus sur le sujet. Cependant, il est quand même temps que nous revenions un peu dans les coulisses, s'assurer qu'on puisse bien répondre aux besoins de la scène Au sein d'une stack LAMP, nous disions que l'accès aux bases de données se faisait depuis le code PHP. Ce qui est logique, puisqu'il est responsable de la bonne génération des pages webs. Mais dans notre cas, le site web est entièrement construit en Javascript ... côté client. Vous voyez le problème ? Fournir au monde entier les identifiants de votre BDD n'est sûrement pas la meilleure des idées, puisque le code JS en aurait obligatoirement besoin pour requêter le SGBD. L'idée est donc la suivante ; si on ne peut parler à notre SGBD directement depuis le front-end, on va devoir ajouter un interlocuteur : cette fameuse API pour laquelle NodeJS semble être parfaitement approprié. Ainsi, les accès à votre SGBD resteront stockés côté back-end, lequel sera interrogé par le front-end. A partir de là, vous avez deux options : Codez vous même chaque requête nécessaire avec le SGBD, chacune ayant son url d'accès définie depuis Node (n'oublions pas Express !). C'est l'option la plus rapide si vous n'avez que quelques requêtes à gérer Tournez vous vers des API de stockage déjà existantes et reconnues, il n'en manque pas. Il vous faudra quelques jours pour bien la prendre en main, mais vous ne regretterez pas votre choix en voyant tous les bienfaits que ça vous aura apporté. Effectivement, cette API vous fera oublier le SQL et ses malheurs, proposant généralement quelque chose comme un ORM pour opérer intuitivement et directement sur vos entités, les liant entre elles selon la configuration que vous aurez au préalable défini dans des fichiers, laissant à l'API le soucis de créer et formater la base de donnée, écrire correctement les requêtes de jointure ... Vous pourrez alors complètement séparer la logique propre au SGBD de celle propre aux différentes opérations à faire sur vos données, permettant encore et toujours de pouvoir plus facilement remplacer votre SGBD, tester vos opérations, etc. Enfin, tout ce code implémentant vos différentes actions sur votre BDD ne sera pas non plus à faire si vous devez développer un autre front comme une application mobile, celle-ci n'aura qu'à requêter votre API comme le fait votre premier front. N'étant pas vraiment mon rayon, je me contenterai de vous conseiller ces api assez connues : Sails, Parse. Si le NoSQL vous intéresse, vous pouvez aussi setup votre propre API pour MongoDB 9/ Une SPA isomorphique Vous avez maintenant eu l'aperçu d'une bonne partie d'une stack JS moderne telle qu'on peut facilement la voir aujourd'hui (quoi que, pas sûr que soit toujours vrai dans une institution vieillissante comme la SNCF ) : Un backend constitué au moins d'un serveur NodeJS pouvant servir d'API mais servant surtout nos fichiers statiques (images/css/js), souvent un SGBD accompagné de son API, ainsi qu'une application front développée avec un framework comme Angular ou React, magnifiquement packagée puis servie au navigateur depuis notre NodeJS. Pour la petite culture, on vient presque de citer une stack assez connue : MEAN, pour MongoDB + Express + Angular + NodeJS. Express, je vous en parlais comme d'un module d'interface aux requêtes HTTP dans la partie sur Node, comme quoi c'est bien un module incontournable avec ce dernier. Avec tout ça, vous avez quelque chose de tout à fait pro, scalable, et tutti cuenti. Je tenais simplement à vous apporter une dernière petite notion: ce sont les applications isomorphiques. Au début de la partie sur les SPA, j'ai introduit les framework front-end comme des librairies permettant la conception d'application fonctionnant uniquement chez le client. En fait, ce n'est pas tout à fait vrai. Ce qui est sûr, c'est qu'une fois l'application téléchargée et lancée par le navigateur client, le backend n'a plus rien à faire avec ce front-end, sauf pour les requêtes avec le SGBD. Mais au moment où le serveur reçoit la première requête du client, qu'en est-il ? Dans un cas simple, il se contente de retourner la SPA, avant que le navigateur initialise l'application et commence ses premiers traitements. Mais ne pourrait-on pas prémâcher le travail du navigateur ? Puisqu'on peut maintenant exécuter du code JS autant côté client que serveur, on pourrait effectivement réaliser le premier rendu du front avant même de retourner celui-ci, de sorte que le navigateur n'ai plus qu'à afficher les vues déjà insérées avec leurs données pré-injectées par le serveur. L'intérêt, c'est de gagner de précieuses ms au chargement du site, des ms qui se sentiront notamment sur les mobiles, ainsi que pour votre SEO. Voilà donc ce qu'est qu'une application isomorphique. Ce n'est bien sûr pas un impératif à mettre obligatoirement en place, mais c'est un plus que certains gros services ne peuvent même pas se permettre d'ignorer, et que d'autres plus petits sont bien heureux d'adopter. Dans les grandes lignes, l'implémentation d'une telle app passe en premier lieu par s'assurer que toutes les dépendances du front-end (modules comme les appels d'API propres au navigateur) sont disponibles aussi et de la même manière sur le back-end. Par exemple, tous les appels fetch (= requête ajax), une API propre aux navigateurs, nécessiteront l'utilisation d'un polyfill (implémentation d'une feature indisponible en raison d'un mauvais environnement / version) côté serveur. Si ces requêtes font appel à des services tournant sur le même serveur que celui délivrant l'application, il serait bien plus optimisé de carrément remplacer ces fetch par l'appel direct aux bonnes fonctions. Ce problème de dépendance peut aussi s'appliquer à certains modules clés comme un module de routage, d'où l'intérêt d'autres modules prévus à cet effet, comme par exemple Universal-Router chez React. Suivant comment votre framework stocke / gère ses données, il faudra aussi réfléchir à comment ré-injecter celles-ci dans l'application une fois qu'elle arrive chez le client. Une étape qui commencera inévitablement par la sérialisation des données dans une variable JS dynamiquement ajoutée à la page par le serveur. Bref, de nombreux articles existent à ce sujet pour les grands frameworks comme React ou Angular, je laisse donc les curieux se documenter eux-même. 10/ Conclusion Et voilà, c'est sur cette dixième et dernière partie que je peux enfin conclure cet article (plus un article qu'un tutoriel proprement dit), après quelques soirées puis une dernière journée complète de rédaction. Me remettre derrière mon clavier pour vous apporter mon savoir comme j'en avais l'habitude il y a quelques années m'a fait vraiment plaisir, d'autant que c'est quelque chose que je voulais faire depuis quelques temps, peinant juste à trouver un point de ralliement entre l'activité du forum et mes centres d'intérêts, qui ont bien évolué depuis mes débuts ici. C'est finalement la sortie de la V6 qui m'a fait me décider, pour essayer de survoler avec vous le maximum de composantes d'un web en pleine évolution : on a vu le radical changement de rôle du Javascript, passant du langage boiteux servant à faire des animations à celui implémentant toute la logique d'une application de manière dynamique et directement chez le client, avec à son opposé un backend maintenant centré sur le stockage et le traitement de l'information en un service réutilisable depuis tout front-end. J'aurais pu faire un vrai tutoriel plus poussé sur l'un des sujets abordés ici mais je pense que ce n'était pas le plus intéressant. Quelque soit le jeux ou le forum, beaucoup de gens bossant dans l'émulation n'ont sûrement jamais eu connaissance de plusieurs des notions / technos abordées ici et c'est "dommage", parce qu'il suffit de savoir qu'une chose existe pour pouvoir exploiter tout son intérêt le bon moment venu, aussi obscure paraisse la chose jusqu'à ce moment. C'était pour moi aussi une bonne occasion de passer en revues et compléter mes connaissances, en malmenant mon pauvre chromium avec les je-ne-sais-combien d'onglet wikipédia et autres qui ont dû être ouverts. Je remercie les quelques courageux qui ont eu la patience de lire le sujet jusqu'ici, et n'hésitez pas à me faire des retours : si une partie n'est pas claire, mérite plus d'illustration, ou que l'une d'elle vous intéresse assez pour vouloir un tutoriel plus approfondi à ce sujet (ou sur tout autre sujet en JS). Bonne soirée la Funky Family !
  12. tutoriel SOMMAIRE DES TUTORIAUX

    SOMMAIRE DES TUTORIAUX Création de serveur Créer un serveur privé Metin2 public avec No-IP Créer un serveur privé Metin2 avec Hamachi [Serveur] Créer un serveur Metin2 [Serveur] Installer un serveur dédié Metin2 Créer un environnement complet pour Serveur Metin2 [File] Installer les Files 2014 [Ports] Ouvrir ses ports sur différents opérateurs Gestion de serveur Implanter une map Metin2 [GM] Créer Compte GM Implanter arme/armure/costume/objet... Comment changer de File Metin2 [2013] Revenir à l'ancien système item_proto & mob_proto 2012 : Changer les mobs des metins [Refine_proto & Mob_proto] [Refine_proto & Mob_proto] Personnalisation des monstres et probabilité des up Implanter un magasin et le remplir Créer le server_attr serveur Créer et configurer le Network FreeBSD 10+ [File] Installer les Files 2012 [Tutoriel]Corriger le bug du motlist.txt [FreeBSD] Erreur libs ld-elf.so.1 - Télécharger le dossier lib32 au complet. [Logiciel] Utilisation de Virtual Box [Fix] Jouer à plusieurs en files 2014 + (No-IP et Windows) [2014] VDI FreeBSD 10.1 FreeBSD - Installation d'un Serveur Web Serveur - Héberger deux serveurs metins sur le même dédié FreeBSD - MultiShell via Screen FreeBSD - Sauvegarder efficacement et durablement votre serveur Sécurité - Protection serveur [DB] Gestion des protos txt FreeBSD - Mise à jour ports-mgmt/pkg FreeBSD - Firewall, Packet Filter FreeBSD - Transférer ses fichiers d'un serveur freebsd à un autre FreeBSD - Auto fsck -y Virtual PC - Virtual PC sur Windows 8 FreeBSD - Archiver ses files en tar.gz Modifier la taille des magasins Serveur - Channel Restarter Virtual PC - Faire apparaître la carte réseau VPC FreeBSD - Mot de passe oublié FreeBSD - Installer les libs sur son dédier FreeBSD - Packet Filter securiter [Mode PASS IN ALL] Serveur - Orientation d'un PNJ. FreeBSD - Changer le mot de passe SSH FreeBSD - Un peu de couleur dans le Shell Unix FreeBSD - Faire une Sauvegarde Base de données [MySQL] Sécurité MySQL de base [MySQL] Changer le mot de passe de navicat. Créer un user spécifique dans navicat. MySQL - Création d'un évènement en base de données Item_Attr - Changer les valeurs des bonus MySQL - Vider les logs MySQL MySQL - Sécurité MySQL par restriction d'IP Debug - Réparer l'erreur mysql sock(2) MySQL - Installer un serveur MySQL MySQL - Démarrer MySQL sans mot de passe root localhost Mobs - Recréer un mob et changer sa valeur Gérer les portails de téléportation Skill_Proto - Le facteur K SQL - Requêtes SQL avec boucles Debug - Régler le could not load threewaywars Debug - Réparer l'erreur de la table money_log Terrain de Guilde ! Augmenter la taille des terrains MySQL - Réparer ses tables Client [Encyclopédie] Configurer tous ses objets [Mapping] Créer une map Modifier la brillance d'une texture. Comment Depack / Repack avec EterNexus [Système] Afficher les statistiques à l'aide d'une GUI Protéger son client contre certains hacks. Grande Liste de textures. [root]Envoyer un mp avec le chat normal [Item] Créer des objets personnalisés Voir les textures sur GrannyViewer Changez l'extension de ses packs [ROOT] Comment recréer les msm a la main Ajouter un bouton à la taskbar Les modules CPython Débug le bouton pour annuler la connexion Quelques commandes de la console client [Python]Corriger le "Python int too large to convert to C long" Afficher le nom du gm lors des annonces Ajouter un bouton dans le target [Partie 2] Développons un anti-cheat en Python [Partie 4] Développons un anti-cheat en Python [Partie 3] Développons un anti-cheat en Python Mettre le pourcentage de vie d'un mob dans le target. Cacher ses MP comme ses quêtes [Partie 1] Développons notre anti-cheat en Python Retirer le clavier à la connexion Ne plus avoir à consulter 2 fois le même magasin. /n avec nom de GM, /nn Sans le nom du GM [Client TeamFE] Réparer quelques erreurs syserr Comprendre comment marche/Faire un systeme Python Créer ses gui d'une manière simplifié ! [Mapping] Convertir sa map en image [Système] Impossible de mentir sur ces Pvs après un duel Hight_light effect Dragon_soul [Tutoriel]Corriger le bug "cannot find accumulation data in file [...]" [etc] Changer la couleurs des bulles d'exp [Python] Comprendre le fonctionnement des boutons [etc] Changer la texture des boutons [root/locale_X] Ajouter un logo au démarrage du client [locale_X] Modifier le nom des grades d'alignement [root] Changer le dossier d'enregistrement des screenshots [root] Ajouter un bouton "S'inscrire" [locale_X] Changer la description des races [locale_X] Changer la description des royaumes [Patcher] Triad patcher (Site no-ip) [etc] Changer l'icône des skills en jeu [root] Changer le nom du client dans la fenêtre Windows [Interface] Ajouter l'heure sous la minicarte [locale_X] Changer les phrases en jeu non émise par le serveur [locale_X]Changer les mots interdits [root] Changer les musiques d'interface. [locale_X] Changer la description des skills [root] Changer NORM ect de l'interface et avec de la couleur Selection Empire Patcheur officiel Faire courir son personnage avec le Smoking Créer un système d'installation professionnel Assembler certains objets [Mapping] Implanter des bâtiments. Protéger ses pack avec Enigma Désactiver le mot debug du client après une compilation Séparateurs de milliers fenêtre d'échange [root] Changer les couleurs du texte [Système] Info Board In Game ! [ROOT] Changer l'IP de son client Inscription Et Forum Au Login Comment changer la taille de la police dans le client [Affichage]Coordonnées sous la minimap. [Tutoriel]Afficher le taux de réussite de Up un item [Tutoriel]Faire ses propres effect damage ou miss [Tutoriel][Texture]Laisser la peau sur une texture. [Mapping] Mettre en place la grande carte [Switch Bot] Fix pour le Bonus Switcher de Padmak Modifier la description de vos items. [Affichage] Ajouter un bouton à l'interface [Debug]Mettre un effet sur Berserk [Console] Activer la console & Comment s'en servir [Mapping] Changer la texture et l’environnement d'une map [Sécurité] Mettre son exporton à son nom [Tutoriel] Corrigez vos GR2 [VB] Création ² Metin AutoPatcher VisualBasic 2008 © [Protection] Protégez vos gr2 [VB] Créer son launcher en VB.Net [Sécurité] Anti PadMak [Affichage] Petite modification graphique [Debug] Correctif du temps restant dans les magasins Ajouter le niveau dans les MP's [2012-] Désactiver les 6ème skills [Mob] Comment Implanter un mob [Skill] Temps d'attente des skills [Effect] Modifier les drapeaux des empires [ROOT] Activé téléportation de groupe [Modification] Agrandir Logo GM [ROOT] Augmenter le max de yangs échangeables Protéger son client contre Mt2Repacker [Root] Modifier la distance max de la caméra FTP [Debug] cannot find accumulation data in file 'data/monster/' [CONFIG] mysql_query error: Table 'xxx.xxx' doesn't exist [fishing.txt] Config de la pêche [boss/npc/stone/regen.txt] Gérer tous les spawns de vos maps [cube.txt] Rajouter un nouvel Item au craft [locale.lua] Changer les phrases d'un PNJ [oxquiz.lua] Question d'OX-Contest Désactiver le Strict-mode pour Mysql 5.6 [RELEASE] Bloquer la faille DB [sysctl.conf] Bloquer les IP coréennes et failles game [Debug] Réparer l'erreur " mob_drop_item " (Channel/Game99) [CMD] Modifier les permissions des commandes GM [Serveur] Extension d'un core [CONFIG] Modifier le level maximum Quêtes [Liste] Les fonctions Lua [Création] Quêtes, Les bases. Implanter une quête [Debug] Déboguer sa quête Couleurs dans les quêtes [Fonction] Gérer un donjon [Débug files 2016] give_basic_weapon.quest [Création] Les boucles [Création] Les tableaux en Lua Liaison Client ---> Quête (+ debug) [Fonction] pc.give_item3 [pc.warp] Calculer des coordonnées universelles [Modification] Changer les quêtes du biologiste Sources Compiler le Game 40250 Compiler le Client Binary Compiler le db Préparer sa VM pour compiler [C++]Bloquer drapeau empire sur PNJ [C++] Potion Rouge & Bleu Illimité [C++]Résoudre le problème pet disparait quand tu ouvres magasin [C++] Supprimer la limite d'exp reçue par mob [Recensement] Modif source 40k [C++] Accélérer la vitesse du texte des quêtes [C++] Réduire le temps de résurrection [C++]Buff de groupe. Augmenter le nombre de place disponible dans l'interface d'échange Anti MobPuller // Waithack [FIX]Remettre le niveau en temps réel. Ajouter [Team] pour les membres du staff. [Fix]Bug avec l'aura activé Réapparaitre avec 100% de vos points de vie [C++] Empêcher le buff entre joueur/staffien [C++] Changer la couleur des dropps selon le propriétaire. [C++] Changer la chance de cape des mobs et la distance de cape [C++] Changer le nombre de personnes maximum dans un groupe [C++] Flèches illimitées [C++] Changer les clés de sécurité Eter LZO [C++] Changer le premier fichier python chargé. [SOURCES]Remplacer granny 2.4 par Granny 2.9 [C++] Informations sur les entitées Bloquer les drapeaux des empires sur les PNJ [SOURCE][DEBOG] 0 Dégat sur le Dragor [FILES 2016] [C++] Format d'image [C++] Yang illimité [C++] Fix des crash core dungeon Afficher les metins sur la minimap [SOURCE][CLIENT] Enlever l'italique écriture GM dans les messages privés [FE 2016] Correctif accent dans le chat général [DEBOG][SOURCE][FILES 2016] Suppression personnage [SOURCE][GAME][DEBOG] CHAT FE 2016 Slot personnage supplémentaire Modifier nombre de manuel pour passer G Colorer les niveaux Implanter une nouvelle monture [enhanceMT] Compiler le serveur sous Windows Repasser les proto en sql (au lieu des txt) Probabilité des drops dans les palourdes Ban ingame + Raison [Tutoriel]Installer un serveur sous Windows Activer le specular sur les mobs et les Pnj's 4 pages d'inventaire [Debug] Ceintures Orbe de béné/Cuivre magique visible et suppression Taxe x3 Inter Empire [C++]Supprimer temps d'attente lecture Game 40k Upgrader son Python de 2.2 en 2.7 [enhanceMT]Compiler sous Freebsd 10 [Fix-Source]Switch bonus équipement équiper Accès à l'entrepôt via l'inventaire (c++/py) Bloquer ou modifier les droits des commandes GM [Game] Bloquer l'API Tool sur le Game 40K [Source] Compiler: Pourquoi? comment? [Game] Debug de l'échange des 4 pages d'inventaires. [Event] Activer le drop des Fragments de pierre dragon Site web [WEB] Installer MT2-CMS Développons notre CMS Metin2 #3 Installer un serveur web Apache sous FreeBSD Développons notre CMS Metin2 #1 [Hamachi] Se connecter à un site web avec Wampserver Sécurité avec les différents CMS [CMS JigSaw] Implanter reCaptcha [Tutoriel]Bloquer une ip sur son site Informations générales sur votre serveur [Ajout] Vote rémunéré pour MT2-CMS Hasher un Mot de Passe MySQL en PHP La fonction password() [MT2-CMS] Ajouter un système de parrainage [MT2-CMS] Un système de news complet [MT2-CMS] Réparer la faille classement [MT2-CMS] Faire un top classement [MT2-CMS] Ajouter un système de récupération de mot de passe [Sécurité] Protection, cryptage, securité de vos sites [MT2-CMS] Ajouter code d'effacement et acceptation des règles à l'inscription [Debug] Pierres sur les armes/armures MT2-CMS [Wamp] Problème avec le port 80 [Script] On/Off où que vous voulez Inclassable Commandes GM 2014+ Implantation mob Avoir une miniature des TGA dans l'explorateur Windows Mettre VirtualBox en Azerty Un peu de couleur dans le MakeFile ! Liaison Python->LUA // LUA->Python [Logiciel] Utilisation de WorldEditor Aura de l'épée [ON/OFF] [Liste] Bonus Les différents fichiers du client et du serveur Les différents types de coordonnées [r40250] Recensement diff Comprendre les variables en Python Comprendre les conditions en Python [Game] Appliquer une Diff [Guide]Les différents Event-flags et comment les utiliser ! Les droits InGame! Importation modèles divers jeux [Adaptation] Adapter une coiffure pour metin2 via 3DS max 7 [34083] Recensement diff [Tools] Les différents outils [Outils] Regen Maker Bien calculer vos rates Anti "Kick Hack" [Debug] Système de costume: Bugs courants [Debug] Erreur Navicat: Got a packet bigger than 'max_allowed_packet' bytes [2012-] Protection client [FreeBSD] Accélérer la vitesse de sa base de donnée et WAMP Créer ses propres icônes tga
  13. tutoriel [C++]Détruire un Item

    Bonjour, J'ai décidé de refaire ce tutoriel Aujourd'hui je vais vous expliquer comment ajouté l'option "détruire" lorsque vous voulez jeter un item. Ca va être assez long, mais facile si vous suivez bien le tutoriel. Commençons ______________________________________________________________________ PRÉREQUIS ______________________________________________________________________ 1.Des source Serveur 2.Des sources Client 3.Un client ______________________________________________________________________ 1.SOURCE SERVEUR ______________________________________________________________________ Ouvrez votre "packet.h" et recherchez: HEADER_CG_ITEM_DROP2 = 20, Ajoutez ceci juste en dessous: HEADER_CG_ITEM_DESTROY = 21, Recherchez maintenant: typedef struct command_item_drop2 { BYTE header; TItemPos Cell; DWORD gold; BYTE count; } TPacketCGItemDrop2; Ajoutez ceci juste en dessous: typedef struct command_item_destroy { BYTE header; TItemPos Cell; } TPacketCGItemDestroy; Ouvrez maintenant le fichier packet_info.cpp et recherchez: Set(HEADER_CG_ITEM_DROP2, sizeof(TPacketCGItemDrop2), "ItemDrop2", true); Ajoutez ceci juste en dessous: Set(HEADER_CG_ITEM_DESTROY, sizeof(TPacketCGItemDestroy), "ItemDestroy", true); Ouvrez maintenant le fichier input_main.cpp et recherchez la fonction: void CInputMain::ItemDrop2(LPCHARACTER ch, const char * data) Ajoutez cette fonction juste après: void CInputMain::ItemDestroy(LPCHARACTER ch, const char * data) { struct command_item_destroy * pinfo = (struct command_item_destroy *) data; if (ch) ch->DestroyItem(pinfo->Cell); } Cherchez maintenant ceci: case HEADER_CG_ITEM_DROP2: if (!ch->IsObserverMode()) ItemDrop2(ch, c_pData); break; Et ajoutez: case HEADER_CG_ITEM_DESTROY: if (!ch->IsObserverMode()) ItemDestroy(ch, c_pData); break; Ouvrez maintenant le fichier char_item.cpp et recherchez la fonction: bool CHARACTER::DropItem(TItemPos Cell, BYTE bCount) Ajoutez cette fonction juste au-dessus: bool CHARACTER::DestroyItem(TItemPos Cell) { LPITEM item = NULL; if (!CanHandleItem()) { if (NULL != DragonSoul_RefineWindow_GetOpener()) ChatPacket(CHAT_TYPE_INFO, LC_TEXT("°*È*âÀ» ¿¬ »óÅ¿¡¼*´Â ¾ÆÀÌÅÛÀ» ¿Å±æ ¼ö ¾ø½À´Ï´Ù.")); return false; } if (IsDead()) return false; if (!IsValidItemPosition(Cell) || !(item = GetItem(Cell))) return false; if (item->IsExchanging()) return false; if (true == item->isLocked()) return false; if (quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning() == true) return false; if (item->GetCount() <= 0) return false; SyncQuickslot(QUICKSLOT_TYPE_ITEM, Cell.cell, 255); ITEM_MANAGER::instance().RemoveItem(item); ChatPacket(CHAT_TYPE_INFO, LC_TEXT("L'item %s est desormais detruit."), item->GetName()); return true; } Ouvrez maintenant le fichier char.h et recherchez: bool DropItem(TItemPos Cell, BYTE bCount=0); Ajoutez ceci juste au-dessus: bool DestroyItem(TItemPos Cell); Ouvrez maintenant le fichier input.h et recherchez: void ItemDrop2(LPCHARACTER ch, const char * data); Ajoutez ceci juste en dessous: void ItemDestroy(LPCHARACTER ch, const char * data); ______________________________________________________________________ 2. SOURCE CLIENT ______________________________________________________________________ Ouvrez le fichier packet.h et recherchez: HEADER_CG_ITEM_DROP2 = 20, Ajoutez ceci juste en dessous: HEADER_CG_ITEM_DESTROY = 21, Recherchez maintenant: typedef struct command_item_drop2 { BYTE header; TItemPos pos; DWORD gold; BYTE count; } TPacketCGItemDrop2; Ajoutez ceci juste en dessous: typedef struct command_item_destroy { BYTE header; TItemPos pos; }TPacketCGItemDestroy; Ouvrez maintenant le fichier PythonNetworkStreamPhaseGameItem.cpp et recherchez la fonction: bool CPythonNetworkStream::SendItemDropPacketNew(TItemPos pos, DWORD elk, DWORD count) Ajoutez cette fonction juste en dessous: bool CPythonNetworkStream::SendItemDestroyPacket(TItemPos pos) { if (!__CanActMainInstance()) return true; TPacketCGItemDestroy itemDestroyPacket; itemDestroyPacket.header = HEADER_CG_ITEM_DESTROY; itemDestroyPacket.pos = pos; if (!Send(sizeof(itemDestroyPacket), &itemDestroyPacket)) { Tracen("SendItemDestroyPacket Error"); return false; } return SendSequence(); } Ouvrez maintenant le fichier PythonNetworkStreamModule.cpp et recherchez la fonction: PyObject* netSendItemDropPacket(PyObject* poSelf, PyObject* poArgs) Ajoutez cette fonction juste en dessous: PyObject* netSendItemDestroyPacket(PyObject* poSelf, PyObject* poArgs) { TItemPos Cell; if (!PyTuple_GetInteger(poArgs, 0, &Cell.cell)) return Py_BuildException(); CPythonNetworkStream& rkNetStream = CPythonNetworkStream::Instance(); rkNetStream.SendItemDestroyPacket(Cell); return Py_BuildNone(); } Recherchez maintenant: { "SendItemDropPacketNew", netSendItemDropPacketNew, METH_VARARGS }, Ajoutez ceci juste après: { "SendItemDestroyPacket", netSendItemDestroyPacket, METH_VARARGS }, Ouvrez maintenant le fichier PythonNetworkStream.h et recherchez la fonction: bool SendItemDropPacketNew(TItemPos pos, DWORD elk, DWORD count); Ajoutez ceci juste après: bool SendItemDestroyPacket(TItemPos pos); ______________________________________________________________________ 3. PYTHON CLIENT ______________________________________________________________________ Ouvrez le fichier uicommon.py du pack root et recherchez la class: class QuestionDialog(ui.ScriptWindow): Juste après celle class, ajoutez la class suivante: class QuestionDialogItem(ui.ScriptWindow): def __init__(self): ui.ScriptWindow.__init__(self) self.__CreateDialog() def __del__(self): ui.ScriptWindow.__del__(self) def __CreateDialog(self): pyScrLoader = ui.PythonScriptLoader() pyScrLoader.LoadScriptFile(self, "uiscript/questiondialogitem.py") self.board = self.GetChild("board") self.textLine = self.GetChild("message") self.acceptButton = self.GetChild("accept") self.destroyButton = self.GetChild("destroy") self.cancelButton = self.GetChild("cancel") def Open(self): self.SetCenterPosition() self.SetTop() self.Show() def Close(self): self.Hide() def SetWidth(self, width): height = self.GetHeight() self.SetSize(width, height) self.board.SetSize(width, height) self.SetCenterPosition() self.UpdateRect() def SAFE_SetAcceptEvent(self, event): self.acceptButton.SAFE_SetEvent(event) def SAFE_SetCancelEvent(self, event): self.cancelButton.SAFE_SetEvent(event) def SetAcceptEvent(self, event): self.acceptButton.SetEvent(event) def SetDestroyEvent(self, event): self.destroyButton.SetEvent(event) def SetCancelEvent(self, event): self.cancelButton.SetEvent(event) def SetText(self, text): self.textLine.SetText(text) def SetAcceptText(self, text): self.acceptButton.SetText(text) def SetCancelText(self, text): self.cancelButton.SetText(text) def OnPressEscapeKey(self): self.Close() return True Ouvrez maintenant le fichier game.py et cherchez la def suivante: def __DropItem(self, attachedType, attachedItemIndex, attachedItemSlotPos, attachedItemCount): Recherchez ce bout de code dans la fonction (2x): itemDropQuestionDialog = uiCommon.QuestionDialog() Remplacez par: itemDropQuestionDialog = uiCommon.QuestionDialogItem() Toujours dans la même fonction, recherchez: itemDropQuestionDialog.SetAcceptEvent(lambda arg=True: self.RequestDropItem(arg)) Ajoutez ceci juste après: itemDropQuestionDialog.SetDestroyEvent(lambda arg=True: self.RequestDestroyItem(arg)) Recherchez maintenant la fonction: def RequestDropItem(self, answer): Ajoutez cette fonction juste après: def RequestDestroyItem(self, answer): if not self.itemDropQuestionDialog: return if answer: dropType = self.itemDropQuestionDialog.dropType dropNumber = self.itemDropQuestionDialog.dropNumber if player.SLOT_TYPE_INVENTORY == dropType: if dropNumber == player.ITEM_MONEY: return else: self.__SendDestroyItemPacket(dropNumber) self.itemDropQuestionDialog.Close() self.itemDropQuestionDialog = None constInfo.SET_ITEM_DROP_QUESTION_DIALOG_STATUS(0) Recherchez ensuite la fonction: def __SendDropItemPacket(self, itemVNum, itemCount, itemInvenType = player.INVENTORY): Et ajoutez celle-ci juste après: def __SendDestroyItemPacket(self, itemVNum, itemInvenType = player.INVENTORY): if uiPrivateShopBuilder.IsBuildingPrivateShop(): chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.DROP_ITEM_FAILURE_PRIVATE_SHOP) return net.SendItemDestroyPacket(itemVNum) Ouvrez maintenant le fichier locale_interface.txt et ajoutez ceci: DESTROY Destroy Placez le fichier questiondialogitem.py dans votre pack uiscript ! questiondialogitem.py Screen du système: Voilà c'est fini ! Source du totoriel : .Avenue™ de Metin2Dev Traduction: moi Cordialement, #Saw.
  14. Bonjour et bonsoir à tous, Au vu de l'activité dans l'A/Q/S à ce sujet ainsi qu'à un problème de @Sufhal sur Discord, j'ai remarqué que les tutoriaux d'installation du World Editor tendaient à être flous ou pas assez explicatifs sur la manière dont il fallait l'installer, de ce fait, j'ai donc décidé de créer un tutoriel vidéo sur l'installation du World Editor ! La procédure est complète à l'exception près de la création de la partition "D:\" du disque dur. Ce tutoriel est très rapide, je l'ai réalisé d'une traite dans la vidéo, je m'excuse pour la petite erreur de langue qui s'est glissée au début ainsi que pour la petite erreur où j'ai dit "côté client" alors que c'était bel et bien "côté serveur" (j'ai fait une correction dans la vidéo). Les packs que vous devrez depacker : Effect ETC PC PC2 Property Terrain textureset Tree Zone Voici donc la vidéo !
  15. Bonjour, Ce tutoriel nécessite Photoshop équipé du plugin de lecture des fichiers en .DDS. Comment supprimer une brillance ? Comment ajouter ou modifier une brillance ? N'oubliez pas d'enregistrer sous le format suivant : DXT5 ARGB 8bpp | interpolated alpha Source : Moi-même. Au plaisir,
  16. Salut à toi ! Aujourd'hui je vais t'apprendre comment changer les files Metin2 que tu ais une machine virtuelle ou un serveur dédié. Vous trouverez la plupart des files dans la section Serveur de Téléchargement: Contenu Masqué Mais vous pouvez très bien les avoir trouvé un autre forum: le processus ne change pas. Des files Metin2 se découpent en trois parties: Les fichiers serveurs. Souvent archivés dans une archive au format .tar.gz Les tables SQL. C'est la base de données de votre serveur Le client. I. Installer les fichiers serveur 1. Vous avez normalement une archive au format .tar.gz (ou autre) qui doit contenir les fichiers serveur avec plusieurs dossiers tel que channel1, share, auth, db, etc .. Placez l'archive dans le dossier /usr sur WinSCP 2. Dans votre machine virtuelle (ou putty), tapez la commande: cd /usr/metin2 && tar xvf metin2.tar.gz J'exécute deux commandes. Déjà je me rends dans le dossier metin2 et ensuite j'extrais les fichiers de l'archive. Une fois l'extraction terminé, vous aurez un dossier metin2 dans le dossier usr (voir screen ci-dessus) II. Installation de la base de données SQL 1. Vous avez normalement une archive avec des fichiers .sql (account, common, etc) .. Ce sont des fichiers SQL que l'on va devoir installer sur notre base de données. 2. Connectez vous à votre base de données sur Navicat. 3. Faites un clique droit sur votre connection puis cliquez sur New Database 4. Dans la fenêtre qui s'ouvre, mettez account dans Enter Database Name puis cliquez sur OK 5. Une fois la base de données créé, double cliquez dessus pour l'ouvrir puis faites un clic droit dessus et sélectionnez Execute SQL File 6. Sélectionnez le fichier account.sql. Cochez la deuxième case comme sur le screen ci-dessus et cliquez sur Start 7. Une fois l'exécution terminé, vous en avez fini avec la table account. Répétez les étapes 3, 4, 5 et 6 plusieurs fois en créant toutes les bases de données nécessaires: common, log, metin2_runup, hotbackup et player Par exemple pour la base de données player, je vais appelé ma database player au lieu de account. Et au lieu de choisir le fichier account.sql, je vais choisir le fichier player.sql III. Connecter le serveur et la base de données 1. Il va falloir maintenant configurer les fichiers serveur pour qu'ils se connecter à la base de données SQL. 2. Vous avez des dossiers de channel. Le dossier channel1, channel2, etc .. Chaque channel contient 5 dossiers core (core1, core2, etc ..). Chaque dossier core contient un fichier appelé CONFIG. Il faut configurer le fichier CONFIG pour chaque core ! Ouvrez ce fichier avec un éditeur de texte. Attention ! Sur les files <= 2012, il n'y a pas de dossiers core, juste un fichier CONFIG par channel ! (contrairement au 2012+ où il y a 5 fichiers CONFIG par channel, un par core). 3. Dans ce fichier CONFIG, vous devez apercevoir cette partie là: 4. La configuration est organisé de cette sorte HOST user password table 5. Sur le screen ci-dessus, la configuration dit: Connecte toi à MySQL avec l'utilisateur metin2 et le mot de passe epv4minq. Changez ces informations en fonction de votre user MySQL. Ne touchez pas au mot player, common et log, juste les informations de connexion ! Une fois terminé, enregistrez le fichier et faites la même chose pour tous les channels (et tous les cores) Si vous avez la machine virtuelle du tutoriel Créer un serveur Metin2, les informations de connexion sont. Host: localhost User: metin2 Mot de passe: serveur 6. Faites la même chose pour le fichier CONFIG du dossier auth et le fichier CONFIG du dossier game99 7. Faites la même chose avec le fichier conf.txt du dossier db. La forme change un petit peu mais ça reste la même chose. Changez juste localhost metin2 et epv4minq IV. Fin du tutoriel Vos files sont maintenant changés ! Il ne vous reste plus qu'à configurer l'IP sur votre nouveau client: Contenu Masqué Si tu as besoin d'aide, n'hésite pas à aller créer un topic dans la catégorie Aide / Question / Support de la section Metin2. La communauté sera là pour vous aider.A bientôt !
  17. Salut à toi ! Je vais vous expliquer comment repack et depack vos fichiers eix / epk Metin2. Qu'est ce que ces fichiers ? Ce sont tout le contenu de votre client tel que les maps, les modèles 3D, les armes, les interfaces, etc .. Ce sont tous les fichiers qui se trouvent dans le dossier pack. I. Téléchargement EterNexus: Contenu Masqué II. Depack un fichier 1. Dans le menu Plus, cliquez sur Extraire 2. Choisissez le fichier que vous voulez extraire (dans le dossier pack de votre client). Je vais ici depack le fichier root. Ensuite, cliquez sur Ouvrir. 3. Le fichier a été depack. Vous verrez un dossier appelé root dans le dossier pack de votre client. Tout le contenu du pack se trouve dans le dossier root. Si vous depackez le fichier BGM, le dossier s'appellera BGM, et etc .. II. Repack un fichier 1. Il est important de repack un fichier pour que la modification effectué dans les fichiers du pack soient prise en compte. Dans le menu Plus de EterNexus, cliquez sur Archiver. 2. Il faut sélectionner le dossier que vous voulez repack (dossier qui se trouve dans le dossier pack de votre client). Dans notre cas, nous voulons repack le dossier root, donc je sélectionne le dossier root. 3. Une fois le repack terminé, vous trouverez les fichiers repack dans le dossier où se trouve votre logiciel EterNexus. 5. Il suffit de déplacer ces deux fichiers dans le dossier pack de votre client et de remplacer les anciens. III. Fin du tutoriel Vous savez maintenant depack et repack un fichier. Si tu as besoin d'aide, n'hésite pas à aller créer un topic dans la catégorie Aide / Question / Support de la section Metin2. La communauté sera là pour vous aider. A bientôt !
  18. Salut à toi ! Tu n'as jamais fait de serveur Metin2 ? Ce tutoriel est fait pour toi ! Je vais t'expliquer comment créer un serveur de A à Z ! Tu pourras ensuite le modifier à ta guise et le modifier à ton image. Lors de ce tutoriel, vous allez apprendre à créer un serveur public avec No-IP ! Vous pourrez ainsi vous connecter avec vos amis sur le serveur ! Prêt ? Alors commençons ! Attention !! Ce tutoriel ne fonctionnera seulement avec des files Metin2 <= 2012 (ce sont les files 2014 installés sur cette VM). Il faut donc installer les filles 2012 une fois le serveur configuré. Aujourd'hui, aucune solution n'a été trouvé pour faire fonctionner un serveur public (NoIP) avec des files 2014+. Problème rencontré: Quand une personne externe choisi un personnage lors de la sélection, il se fait instantanément déconnecté par le serveur. I. Téléchargement VirtualBox - Contenu Masqué Navicat - Contenu Masqué WinSCP - Contenu Masqué Le serveur (le VDI) - Contenu Masqué Le client - Contenu Masqué NoIP - Contenu Masqué II. La machine virtuelle 1. Installez VirtualBox. Il nous permettra de virtualiser le système d'exploitation FreeBSD. 2. Lancez le logiciel VirtualBox 3. Cliquez sur le bouton Nouvelle, en haut à gauche 4. Choisissez un nom pour votre machine virtuelle. Dans Type, choisissez BSD et dans Version, FreeBSD (32bit ou 64bit en fonction de votre ordinateur). Et cliquez sur Suivant 5. Allouez la mémoire vive à votre machine virtuelle en fonction de votre ordinateur. Le minimum conseillé est 1go (1024Mo). N'hésitez pas à en donner plus le temps que le curseur est dans la barre verte. Cliquez sur Suivant. 6. Cochez la case " Utiliser un fichier de disque dur virtuel existant " et choisissez le fichier VDI (.vdi) que vous avez téléchargé. Cliquez ensuite sur Créer. 7. Votre machine virtuelle est crée. Configurons la carte réseau. Clique droit sur votre VM (virtual machine) puis Configuration 8. Dans le menu Réseau, mettez " Accès par pont " dans mode d'accès réseau et dans nom, mettez votre carte réseau (carte Wifi si vous êtes en WIF ou Ethernet si vous êtes en Ethernet). Cliquez ensuite sur OK. 9. Votre machine virtuelle est maintenant créée ! III. Configuration de la machine virtuelle 1. Pour lancer votre machine virtuelle, il suffit de cliquer deux fois sur votre machine. Une fenêtre s'ouvre et votre système d'exploitation démarre. Patientez. On vous demande ensuite de vous connecter à un utilisateur. 2. Dans login, entrez: root pour login et dans Password, mettez mcncc.com. Vous êtes maintenant connecté et vous avez accès à tout le système d'exploitation. Tapez la commande sysinstall dans le terminal. Vous tomberez sur: 3. Vous pouvez naviguer dans ce menu avec les flèches du haut et du bas puis la touche Entrée pour sélectionner le choix. 4. Dans l'ordre, allez sur Configure, puis Networking puis Interfaces. Vous tombez sur: 5. Choisissez em0. Une petite fenêtre s'ouvre. Répondez No. Une autre s'ouvre, répondez No aussi. Vous tombez sur la configuration de l'interface réseau. 6. Configurez comme sur le screen ci-dessous. Remplacez IP_DU_ROUTEUR par l'adresse IP locale de votre routeur. Voici un petit tutoriel pour la connaitre: Contenu Masqué Chez Free par exemple, l'adresse IP du routeur est: 192.168.0.254 Remplacez ADRESSE_IP par 192.168.0.100 (Vous pouvez remplacer le .100 par ce que vous voulez du temps que l'adresse ne soit pas utilisé ailleurs) 7. Une fois la configuration terminé, validez avec le bouton OK. Vous tomberez sur le screen ci-dessous. Répondez Yes à la question. 8. Appuyez sur la touche Echap (ou le bouton Cancel) jusqu'à quitter cette interface. 9. Tapez la commande /etc/rc.d/netif restart pour prendre en compte le changement. Les informations de votre interface réseau s'affichent: 10. L'adresse IP que vous avez configuré devrait s'afficher. Je l'ai entouré en rouge. Dans mon cas, c'est celle qui se termine par le .100. Pensez à bien noter votre adresse IP (celle qui s'affiche sur votre machine et non celle du screen). Nous en aurons besoin. IV. Configuration de No-IP Cette étape n'est pas obligatoire ! Elle suffit juste d'avoir un nom de domaine sur votre IP même si celle-ci est dynamique. Si vous avez une IP fixe, cette étape est inutile. 1. Commencez par installer le logiciel No-IP et inscrivez vous. On vous demandera de choisir un hostname lors de l'inscription. Mettez ce que vous voulez avec le thème que vous voulez. 2. Une fois connecté, rendez-vous dans l'onglet Hosts / Redirects ( Contenu Masqué ). Vous voyez donc déjà le domaine que vous avez créer à l'inscription. Vous pouvez toujours le modifier. Si vous voulez ajouter une nouvelle redirection sur votre IP, cliquez sur le bouton Create Host et procédez comme le screen ci-dessous 3. Lancez le logiciel No-IP et connectez vous à votre compte.. Vous devriez avoir le domaine que vous avez créé. Cochez sa case et cliquez sur Save. 4. Votre redirection est maintenant active. La configuration de No-IP est terminée. IV. Configuration des ports sur sa box 1. Il faut maintenant ouvrir les ports sur votre box internet pour permettre l'accès aux joueurs à votre serveur. Voici un tutoriel qui vous explique comment ouvrir les ports sur les différents fournisseurs: Contenu Masqué 2. Si votre fournisseur n’apparaît pas, faites une recherche sur Internet. Vous trouverez sans problème un article qui vous expliquez comment les ouvrir avec le panel de votre fournisseur internet. 3. Voici la liste des ports à ouvrir: 13000 (TCP/UDP) --> avec l'ip de votre serveur ! (L'ip que /etc/rc.d/netif restart nous a envoyé tout à l'heure) 13001 (TCP/UDP) --> avec l'ip de votre serveur ! (L'ip que /etc/rc.d/netif restart nous a envoyé tout à l'heure) 13002 (TCP/UDP) --> avec l'ip de votre serveur ! (L'ip que /etc/rc.d/netif restart nous a envoyé tout à l'heure) 13003 (TCP/UDP) --> avec l'ip de votre serveur ! (L'ip que /etc/rc.d/netif restart nous a envoyé tout à l'heure) 13004 (TCP/UDP) --> avec l'ip de votre serveur ! (L'ip que /etc/rc.d/netif restart nous a envoyé tout à l'heure) 13061 (TCP/UDP) --> avec l'ip de votre serveur ! (L'ip que /etc/rc.d/netif restart nous a envoyé tout à l'heure) 13099 (TCP/UDP) --> avec l'ip de votre serveur ! (L'ip que /etc/rc.d/netif restart nous a envoyé tout à l'heure) 23000 (TCP) --> avec l'ip de votre serveur ! (L'ip que /etc/rc.d/netif restart nous a envoyé tout à l'heure) 11002 (TCP/UDP) --> avec l'ip de votre serveur ! (L'ip que /etc/rc.d/netif restart nous a envoyé tout à l'heure) 50000 (TCP/UDP) --> avec l'ip de votre serveur ! (L'ip que /etc/rc.d/netif restart nous a envoyé tout à l'heure) Vous pouvez ouvrir le port 80 si vous voulez un site internet sur votre ordinateur et que vous souhaitez que les joueurs puissent y accéder. 80 (TCP) --> avec l'ip locale ! (l'ip de la box/routeur) V. Lancement du serveur Metin2 1. Pour lancer votre machine virtuelle, il suffit de cliquer deux fois sur votre machine. Une fenêtre s'ouvre et votre système d'exploitation démarre. Patientez. On vous demande ensuite de vous connecter à un utilisateur. 2. Dans login, entrez: root pour login et dans Password, mettez mcncc.com. Vous êtes maintenant connecté et vous avez accès à tout le système d'exploitation. Dans le terminal, tapez cd /usr/metin2 4. Vous voila dans le dossier de votre serveur metin2. Pour démarrer le serveur il suffit de taper: sh start.sh (et sh stop.sh pour l'arrêter). Entrez ensuite le nombre de channel que vous voulez ouvrir. Pour commencer, un seul suffit. 5. Votre serveur Metin2 est lancé ! Il ne vous reste plus qu'à configurer votre client ! Bien entendu, laissez votre fenêtre Virtual Box ouverte pour que votre serveur reste ouvert. VI. Se connecter In Game ! 1. Commencez par extraire le client de Client 2014.rar sur votre bureau. 2. Il faut configurer le client de sorte à ce qu'il se connecte sur l'adresse IP de votre serveur. Il existe un tutoriel pour vous apprendre à le faire: Contenu Masqué 3. Dans le tutoriel, on vous demande de mettre une adresse IP. Pour votre client Metin2, il faut configurer votre client sur l'adresse IP que la comment /etc/rc.d/netif restart vous a renvoyé tout à l'heure.. Par contre, pour ceux qui veulent se connecter sur votre serveur, il faut configurer le client sur le domaine No-IP que vous avez créé ou alors votre adresse IP public. Vous la trouvez ici: Contenu Masqué Vous aurez donc deux client. Un pour vous avez l'adresse IP de la machine virtuelle et un pour les joueurs configurer sur votre domaine No-IP ou votre adresse IP publique. 4. Dans mon cas, pour mon client, j'aurais l'adresse IP 192.168.0.100 . 5. Une fois la configuration terminée, lancez metin2client.exe qui se trouve à la racine de votre client. 6. Connectez vous avec ces identifiants. User: admin Mot de passe: test 7. Choisissez votre personnage et ça y est, vous voila sur votre premier serveur privé Metin2 ! VII. Accéder aux fichiers du serveur 1. Pour accéder aux fichiers du serveur, il faut installer le logiciel WinSCP. Une fois installé, lancez le. 2. Cliquez sur nouveau site et remplissez comme l'image ci dessous et cliquez sur Connexion. 3. Vous voici connecté à WinSCP. Vous avez tout simplement un accès à votre système d'exploitation FreeBSD mais cette fois avec un explorateur de fichier, et non en ligne de commande. 4. Déplacez-vous dans l’arborescence. Cliquez sur .. pour revenir à la racine du serveur, cliquez sur le dossier usr puis metin2. Vous tombez sur: 5. Ceci sont les fichiers serveur de votre serveur Metin2 ! VIII. Accéder à la base de données du serveur 1. La base de données contient toutes les données de votre serveur (comptes, joueurs, etc ..). Pour y accéder, installez Navicat et lancez-le. 2. Cliquez sur le bouton Connection puis MySQL 3. Remplissez comme l'image ci-dessous: 4. La connexion s'est créé dans le volet de gauche. Il suffit juste de cliquer 2 fois dessus pour s'y connecter. 5. Ceci est la base de donnée de votre serveur Metin2 ! IX. Fin du tutoriel C'est tout bon, vous avez tous les outils pour créer un serveur Metin2 à votre image FAQ des débutants à voir absolument: Contenu Masqué Si tu as besoin d'aide, n'hésite pas à aller créer un topic dans la catégorie Aide / Question / Support de la section Metin2. La communauté sera là pour vous aider. Bon courage, jeune apprenti !
  19. Salut à toi ! Tu n'as jamais fait de serveur Metin2 ? Ce tutoriel est fait pour toi ! Je vais t'expliquer comment créer un serveur de A à Z ! Tu pourras ensuite le modifier à ta guise et créer un serveur à ton image ! Lors de ce tutoriel, vous allez apprendre à créer un serveur avec HAMACHI ! Vous pourrez ainsi vous connecter avec vos amis via le réseau Hamachi ! Prêt ? Alors commençons ! Attention !! Ce tutoriel ne fonctionnera seulement avec des files Metin2 <= 2012 (ce sont les files 2014 installés sur cette VM). Il faut donc installer les filles 2012 une fois le serveur configuré. Aujourd'hui, aucune solution n'a été trouvé pour faire fonctionner un serveur Hamachi avec des files 2014+. Problème rencontré: Quand une personne externe choisi un personnage lors de la sélection, il se fait instantanément déconnecté par le serveur. I. Téléchargement VirtualBox - Contenu Masqué Navicat - Contenu Masqué WinSCP - Contenu Masqué Le serveur (le VDI) - Contenu Masqué Le client - Contenu Masqué Hamachi - Contenu Masqué Port (Le fichier #138) - Contenu Masqué II. La machine virtuelle 1. Avant de se lancer dans la création de la machine, installez Hamachi et lancez le. Vérifiez que Hamachi est bien actif. Vous devriez avoir cela (avec votre propre IP Hamachi). Dans mon cas, mon IP Hamachi est: 25.73.148.152 (Notez bien votre IP Hamachi, nous en aurons besoin ! 2. Installez VirtualBox. Il nous permettra de virtualiser le système d'exploitation FreeBSD. 3. Lancez le logiciel VirtualBox 4. Cliquez sur le bouton Nouvelle, en haut à gauche 5. Choisissez un nom pour votre machine virtuelle. Dans Type, choisissez BSD et dans Version, FreeBSD (32bit ou 64bit en fonction de votre ordinateur). Et cliquez sur Suivant 6. Allouez la mémoire vive à votre machine virtuelle en fonction de votre ordinateur. Le minimum conseillé est 1go (1024Mo). N'hésitez pas à en donner plus le temps que le curseur est dans la barre verte. Cliquez sur Suivant. 7. Cochez la case " Utiliser un fichier de disque dur virtuel existant " et choisissez le fichier VDI (.vdi) que vous avez téléchargé. Cliquez ensuite sur Créer. 8. Votre machine virtuelle est crée. Configurons la carte réseau. Clique droit sur votre VM (virtual machine) puis Configuration 9. Dans le menu Réseau, mettez " Accès par pont " dans mode d'accès réseau et dans nom, mettez la carte réseau Hamachi !). Cliquez ensuite sur OK. 10. Votre machine virtuelle est maintenant créée ! III. Configuration de la machine virtuelle 1. Pour lancer votre machine virtuelle, il suffit de cliquer deux fois sur votre machine. Une fenêtre s'ouvre et votre système d'exploitation démarre. Patientez. On vous demande ensuite de vous connecter à un utilisateur. 2. Dans login, entrez: root pour login et dans Password, mettez mcncc.com. Vous êtes maintenant connecté et vous avez accès à tout le système d'exploitation. Tapez la commande sysinstall dans le terminal. Vous tomberez sur: 3. Vous pouvez naviguer dans ce menu avec les flèches du haut et du bas puis la touche Entrée pour sélectionner le choix. 4. Dans l'ordre, allez sur Configure, puis Networking puis Interfaces. Vous tombez sur: 5. Choisissez em0. Une petite fenêtre s'ouvre. Répondez No. Une autre s'ouvre, répondez No aussi. Vous tombez sur la configuration de l'interface réseau. 6. Configurez comme sur le screen ci-dessous. Remplacez VOTRE_IP_HAMACHI par l'IP que vous avons vu tout à l'heure sur le logiciel Hamachi, dans mon cas 25.73.148.152. (le petit 1 u I)). Et remplacez IP_HAMACHI_100 par votre IP Hamachi .100. L'IP .100 est tout simplement votre IP Hamachi mais avec le nombre 100 à la fin. Par exemple dans mon cas, l'IP Hamachi est: 25.73.148.152, alors mon IP .100 sera: 25.73.148.100. 7. Une fois la configuration terminé, validez avec le bouton OK. Vous tomberez sur le screen ci-dessous. Répondez Yes à la question. 8. Appuyez sur la touche Echap (ou le bouton Cancel) jusqu'à quitter cette interface. 9. Tapez la commande /etc/rc.d/netif restart pour prendre en compte le changement. Les informations de votre interface réseau s'affichent: 10. L'adresse IP devrait s'afficher. Je l'ai entouré en rouge. C'est celle qui se termine par .100. Pensez à bien noter votre adresse IP (celle qui s'affiche sur votre machine et non celle du screen). Nous en aurons besoin. IV. Configuration de portmap 1. Commencez par ouvrir le logiciel portmap.exe. Vous avez une liste de port, il va falloir tous les configurer sur votre IP .100 2. Pour chaque ports (un port = une ligne), répétez les étapes 2 et 3. Sélectionnez la ligne souhaité puis cliquez sur l'icône en haut à gauche. 3. Une petite fenêtre s'ouvre. Mettez votre IP .100 dans le champ indiqué (voir screen ci-dessous) et valider en cliquant sur le bouton (entouré en rouge): 4.Une fois tous les ports configuré, vous devriez voir votre adresse IP .100 configuré sur tous les ports dans la colonne indiqué ci-dessous. (Dans le cas du screen, c'est mon IP .100) 5. Pour chaque ligne (les sélectionner une par une), cliquez sur le bouton vert qui se trouve en haut pour les activer (le cercle vert avec l'icône blanche dedans). Vous devriez avoir toutes les pastilles de couleur verte ! 6. La configuration de portmap est terminé ! Laissez la fenêtre ouverte, sinon cela ne fonctionnera plus ! V. Lancement du serveur Metin2 1. Retournons sur la machine virtuelle (VirtualBox). Tapez la commande: cd /usr/metin2 (voir screen ci-dessous). 2. Vous voila dans le dossier de votre serveur metin2. Pour démarrer le serveur il suffit de taper: sh start.sh (et sh stop.sh pour l'arrêter). Entrez ensuite le nombre de channel que vous voulez ouvrir. Pour commencer, un seul suffit. 3. Votre serveur Metin2 est lancé ! Il ne vous reste plus qu'à configurer votre client ! Bien entendu, laissez votre fenêtre Virtual Box ouverte pour que votre serveur reste ouvert. VI. Créer son réseau Hamachi ! 1. Afin que vous puissiez vous connecter tous ensemble sur le serveur, il faut créer un réseau Hamachi. Pour cela, sur Hamachi, cliquez sur Réseau puis Créer un réseau. 2. Dans ID Réseau, mettez le nom du réseau (ce que vous souhaitez). Et dans Mot de passe, le mot de passe d'accès à votre réseau. Cliquez ensuite sur Créer. Votre réseau Hamachi est créé. 3. Attention ! Pour que les joueurs puissent se connecter à votre serveur, il faut qu'ils rejoignent votre réseau. Pour cela, au lieu de cliquer sur Créer un réseau (dans le menu Réseau), cliquez sur Rejoindre un réseau. Ensuite il suffit juste de rentrer le nom du réseau puis de cliquer sur Rejoindre. 4. Par exemple sur mon réseau asikoojtm, j'ai un joueur qui la rejoint. (je l'ai caché pour ne pas voir son nom mais c'est Asikoo). VI. Se connecter In Game ! 1. Commencez par extraire le client de Client 2014.rar sur votre bureau. 2. Il faut configurer le client de sorte à ce qu'il se connecte sur l'adresse IP de votre serveur. Il existe un tutoriel pour vous apprendre à le faire: Contenu Masqué 3. Dans le tutoriel, on vous demande de mettre une adresse IP. Pour votre client Metin2, il faut configurer votre client sur l'adresse IP .100. Par contre, pour ceux qui veulent se connecter sur votre serveur Hamachi, il faut configurer le client sur l'IP Hamachi normale (Celle qui ne termine pas par 100). Donc un client pour vous et un client pour vos joueurs. 4. Dans mon cas, 25.73.148.100 pour mon client à moi et 25.73.148.152 pour mes joueurs. 5. Une fois la configuration terminée, lancez metin2client.exe qui se trouve à la racine de votre client. 6. Connectez vous avec ces identifiants. User: admin Mot de passe: test 7. Choisissez votre personnage et ça y est, vous voila sur votre premier serveur privé Metin2 ! VII. Accéder aux fichiers du serveur 1. Pour accéder aux fichiers du serveur, il faut installer le logiciel WinSCP. Une fois installé, lancez le. 2. Cliquez sur nouveau site et remplissez comme l'image ci dessous 3. Vous voici connecté à WinSCP. Vous avez tout simplement un accès à votre système d'exploitation FreeBSD mais cette fois avec un explorateur de fichier, et non en ligne de commande. 4. Déplacez-vous dans l’arborescence. Cliquez sur .. pour revenir à la racine du serveur, cliquez sur le dossier usr puis metin2. Vous tombez sur: 5. Ceci sont les fichiers serveur de votre serveur Metin2 ! VIII. Accéder à la base de données du serveur 1. La base de données contient toutes les données de votre serveur (comptes, joueurs, etc ..). Pour y accéder, installez Navicat et lancez-le. 2. Cliquez sur le bouton Connection puis MySQL 3. Remplissez comme l'image ci-dessous puis cliquez sur Ok: 4. La connexion s'est créé dans le volet de gauche. Il suffit juste de cliquer 2 fois dessus pour s'y connecter. 5. Ceci est la base de donnée de votre serveur Metin2 ! IX. Fin du tutoriel C'est tout bon, vous avez tous les outils pour créer un serveur Metin2 à votre image FAQ des débutants à voir absolument: Contenu Masqué Si tu as besoin d'aide, n'hésite pas à aller créer un topic dans la catégorie Aide / Question / Support de la section Metin2. La communauté sera là pour vous aider. Bon courage, jeune apprenti !
  20. Bonjour à toutes et à tous ! J'ai fais quelques recherches sur ce forum mais je n'ai rien trouvé à ce sujet donc je vais vous expliquer comment mettre en place une GUI (Graphical User Interface) montrant les statistiques sans passer par une quête. Bien sûr, il y a possibilité de désactiver ou réactiver l'affichage de cette interface à tout moment à l'aide d'une petite quête. Bref, au travail ! Pour commencer vous aurez besoin de dépack le fichier "root" afin d'avoir accès au fichier "game.py" puis ouvrez le. Petite précision: le copier-coller c'est pas terrible vu que le python n'aime pas les espaces. Donc si vous avez des problèmes à la fin du tuto, remplacez les espaces par des tabulations. /!\La quête est nécessaire pour afficher l'interface/!\ Cherchez la ligne: self.SetSize(wndMgr.GetScreenWidth(), wndMgr.GetScreenHeight()) Puis faites un copier/coller de ceci: #START_KILLGUI KillGuiBg = ui.AniImageBox() KillGuiBg.AppendImage("d:/ymir work/ui/blue_killgui_interface.tga") self.KillGuiBg = KillGuiBg self.KillGuiBg.SetPosition(wndMgr.GetScreenWidth()-235,185) self.KillBlauReich = ui.TextLine() self.KillBlauReich.SetDefaultFontName() self.KillBlauReich.SetPosition((wndMgr.GetScreenWidth()-285)+120, 412) self.KillBlauReich.SetText("Royaume bleu: NaN") self.KillBlauReich.SetOutline() self.KillGelbReich = ui.TextLine() self.KillGelbReich.SetDefaultFontName() self.KillGelbReich.SetPosition((wndMgr.GetScreenWidth()-289)+120, 332) self.KillGelbReich.SetText("Royaume jaune: NaN") self.KillGelbReich.SetOutline() self.KillRotReich = ui.TextLine() self.KillRotReich.SetDefaultFontName() self.KillRotReich.SetPosition((wndMgr.GetScreenWidth()-289)+120, 255) self.KillRotReich.SetText("Royaume Rouge: NaN") self.KillRotReich.SetOutline() self.KillMob = ui.TextLine() self.KillMob.SetDefaultFontName() self.KillMob.SetPosition((wndMgr.GetScreenWidth()-268)+120, 490) self.KillMob.SetText("Monstre: NaN") self.KillMob.SetOutline() ##END_KILLGUI Cherchez maintenant la ligne: serverCommandList={ Puis faites un copier/coller de ceci: ##KILLGUI "ShowKillGui" : self.__showkillgui, "HideKillGui" : self.__hidekillgui, "KillBlauReich" : self.__KillBlauReich, "KillGelbReich" : self.__KillGelbReich, "KillRotReich" : self.__KillRotReich, "KillMob" : self.__KillMob, ##END_KILLGUI Maintenant, allez à la fin de votre fichier game.py, personnellement je l'ai mis au dessus de cette ligne: def __ProcessPreservedServerCommand(self): Lorsque vous avez repéré cette ligne, faites un copier/coller de ceci: def __hidekillgui(self): self.KillGuiBg.Hide() self.KillBlauReich.Hide() self.KillGelbReich.Hide() self.KillRotReich.Hide() self.KillMob.Hide() def __showkillgui(self): self.KillGuiBg.Show() self.KillBlauReich.Show() self.KillGelbReich.Show() self.KillRotReich.Show() self.KillMob.Show() def __KillBlauReich(self, KillBlauReich): self.KillBlauReich.SetText("Royaume bleu " + KillBlauReich) def __KillGelbReich(self, KillGelbReich): self.KillGelbReich.SetText("Royaume jaune " + KillGelbReich) def __KillRotReich(self, KillRotReich): self.KillRotReich.SetText("Royaume rouge " + KillRotReich) def __KillMob(self, KillMob): self.KillMob.SetText("Monstre " + KillMob) Repackez votre fichier root. Vous devez maintenant extraire "etc" pour avoir accès à votre dossier "ui" Téléchargez ce fichier: Contenu Masqué Placez le dans le dossier "ui" d:/ymir work/ui/blue_killgui_interface.tga Ouvrez le fichier "etc_repack.xml" et ajoutez cette ligne: depack\ymir work\ui\blue_killgui_interface.tga Sauvegardez et repackez "etc" Le côté client est terminé ! maintenant il faut implanter la quête permettant de cacher ou afficher l'interface. Allez dans le dossier "quest" via winscp ou filezilla. Créez un fichier nommé: kill_gui.quest Puis ajoutez la quête au fichier: quest killgui begin state start begin when kill begin if npc.is_pc() then local new_point = pc.getqf("empire"..npc.get_empire())+1 pc.setqf("empire"..npc.get_empire(), new_point) cmdchat("KillRotReich "..pc.getqf("empire1")) cmdchat("KillGelbReich "..pc.getqf("empire2")) cmdchat("KillBlauReich "..pc.getqf("empire3")) else local new_point = pc.getqf("mob")+1 pc.setqf("mob", new_point) cmdchat("KillMob "..pc.getqf("mob")) end end when login begin if pc.getqf("showkillgui") == 1 then cmdchat("ShowKillGui") cmdchat("KillRotReich "..pc.getqf("empire1")) cmdchat("KillGelbReich "..pc.getqf("empire2")) cmdchat("KillBlauReich "..pc.getqf("empire3")) cmdchat("KillMob "..pc.getqf("mob")) else cmdchat("HideKillGui") end end when letter begin send_letter("Statistiques") end when info or button begin say_title("Statistiques") say("Voulez-vous désactiver l'affichage de vos") say("statistiques ?") say("Les personnes ou mobs tués seront tout de même") say("comptabilisés. Vous pourrez réactiver à tout moment") say("l'affichage des statistiques.") local janein = select("Afficher", "Cacher") if janein == 2 then pc.setqf("showkillgui", 0) cmdchat("HideKillGui") else pc.setqf("showkillgui", 1) cmdchat("ShowKillGui") cmdchat("KillRotReich "..pc.getqf("empire1")) cmdchat("KillGelbReich "..pc.getqf("empire2")) cmdchat("KillBlauReich "..pc.getqf("empire3")) cmdchat("KillMob "..pc.getqf("mob")) end end end end Ajoutez le fichier à filezilla ou winscp puis ouvrez le fichier: locale_list et ajoutez cette ligne: kill_gui.quest (N'oubliez pas de laisser une ligne vide en dessous de la dernière ligne ajoutée) Sauvegardez puis recompilez la quête. Dans la liste des quêtes vous verrez la quête "Statistiques" cliquez dessus puis activez l'affichage de la GUI, le tour est joué ! Petit plus ! Il y a en tout 5 interfaces (en comptant celle-ci) j'ai choisis de vous partager cette interface car je trouve que c'est la plus belle, mais après chacun ses goûts. Téléchargement: Contenu Masqué Téléchargement: Contenu Masqué Pour changer d'interface, renommez le fichier téléchargé en "blue_killgui_interface.tga" puis déplacez le ici en remplaçant l'ancienne interface si besoin est: d:/ymir work/ui/blue_killgui_interface.tga Changer l'emplacement de l'écriture Si vous changez d'interface, vous aurez sans doute besoin de régler la position du texte. Ouvrez le fichier "game.py" puis cherchez la ligne: self.SetSize(wndMgr.GetScreenWidth(), wndMgr.GetScreenHeight()) Vous verrez donc les coordonnées de chaque ligne: Si vous souhaitez changer le texte, c'est simple. Cherchez la ligne: def __hidekillgui(self): Laisser la GUI affichée après chaque téléportation: Merci à Spark pour la modification du python et de la quête ! Pour commencer, remplacez la quête implantée précédemment par celle-ci: quest killgui begin state start begin when kill begin if npc.is_pc() then local new_point = pc.getqf("empire"..npc.get_empire())+1 local tab = {"KillRotReich", "KillGelbReich", "KillBlauReich"} pc.setqf("empire"..npc.get_empire(), new_point) cmdchat(tab[npc.get_empire()].." "..new_point) else local new_point = pc.getqf("mob")+1 pc.setqf("mob", new_point) cmdchat("KillMob "..new_point) end end when login begin cmdchat("ShowKillGui") cmdchat("KillRotReich "..pc.getqf("empire1")) cmdchat("KillGelbReich "..pc.getqf("empire2")) cmdchat("KillBlauReich "..pc.getqf("empire3")) cmdchat("KillMob "..pc.getqf("mob")) end end end Allez dans vitre game.py puis remplacez: "ShowKillGui" : self.__showkillgui, Par: "ShowKillGui" : self.__showkillgui2, Remplacez ensuite: def __hidekillgui(self): self.KillGuiBg.Hide() self.KillBlauReich.Hide() self.KillGelbReich.Hide() self.KillRotReich.Hide() self.KillMob.Hide() Par: def __showkillgui2(self): constInfo.killgui = 1 self.KillGuiBg.Show() self.KillBlauReich.Show() self.KillGelbReich.Show() self.KillRotReich.Show() self.KillMob.Show() Repackez votre root puis testez ! Merci à Spark ! Afficher/cacher l'interface à l'aide d'un raccourcis (F5)Passer par une quête à chaque fois pour afficher ou cacher l'interface peut être assez chiant, voilà la solution: un raccourcis sur la touche F5 ! Ouvrez le fichier game.py de votre root puis allez à la fin de votre fichier pour trouver ces lignes: Remplacez donc: def __hidekillgui(self): self.KillGuiBg.Hide() self.KillBlauReich.Hide() self.KillGelbReich.Hide() self.KillRotReich.Hide() self.KillMob.Hide() def __showkillgui(self): self.KillGuiBg.Show() self.KillBlauReich.Show() self.KillGelbReich.Show() self.KillRotReich.Show() self.KillMob.Show() Par ceci: def __hidekillgui(self): self.KillGuiBg.Hide() self.KillBlauReich.Hide() self.KillGelbReich.Hide() self.KillRotReich.Hide() self.KillMob.Hide() def __showkillgui(self): if constInfo.killgui == 0: constInfo.killgui = 1 self.KillGuiBg.Show() self.KillBlauReich.Show() self.KillGelbReich.Show() self.KillRotReich.Show() self.KillMob.Show() elif constInfo.killgui == 1: constInfo.killgui = 0 self.KillGuiBg.Hide() self.KillBlauReich.Hide() self.KillGelbReich.Hide() self.KillRotReich.Hide() self.KillMob.Hide() Cherchez la ligne: onPressKeyDict[app.DIK_F4] = lambda : self.__PressQuickSlot(7) Puis ajoutez en dessous: onPressKeyDict[app.DIK_F5] = lambda : self.__showkillgui() (ne rien ajouter dans la parenthèse en fin de ligne) Sauvegardez puis quittez le fichier game.py. Toujours dans root ouvre le fichier "constinfo.py" Cherchez la ligne: PVPMODE_PROTECTED_LEVEL = 30 (la valeur à la fin de cette ligne peut changer) Puis en dessous ajoutez cette ligne: killgui = 0 Sauvegardez, repackez et en jeu, appuyez sur F5 ! Après ça on a toujours besoin de la quête, donc ne la supprimez pas ! Optimisé pour l'interface V4 (avec raccourcis) Je me suis intéressé à la version 4 de l'interface, voilà donc les emplacements que je trouve optimaux pour cet interface: /!\Vous devez avoir installé le raccourcis de la partie juste au dessus/!\ En dessous de la ligne: self.SetSize(wndMgr.GetScreenWidth(), wndMgr.GetScreenHeight()) Mettre ceci: ##START_KILLGUI KillGuiBg = ui.AniImageBox() KillGuiBg.AppendImage("d:/ymir work/ui/blue_killgui_interface.tga") self.KillGuiBg = KillGuiBg self.KillGuiBg.SetPosition(wndMgr.GetScreenWidth()-245,171) self.KillBlauReich = ui.TextLine() self.KillBlauReich.SetDefaultFontName() self.KillBlauReich.SetPosition((wndMgr.GetScreenWidth()-285)+120, 445) self.KillBlauReich.SetText("Royaume bleu: NaN") self.KillBlauReich.SetOutline() self.KillGelbReich = ui.TextLine() self.KillGelbReich.SetDefaultFontName() self.KillGelbReich.SetPosition((wndMgr.GetScreenWidth()-289)+120, 340) self.KillGelbReich.SetText("Royaume jaune: NaN") self.KillGelbReich.SetOutline() self.KillRotReich = ui.TextLine() self.KillRotReich.SetDefaultFontName() self.KillRotReich.SetPosition((wndMgr.GetScreenWidth()-289)+120, 231) self.KillRotReich.SetText("Royaume Rouge: NaN") self.KillRotReich.SetOutline() self.KillMob = ui.TextLine() self.KillMob.SetDefaultFontName() self.KillMob.SetPosition((wndMgr.GetScreenWidth()-268)+120, 557) self.KillMob.SetText("Monstre: NaN") self.KillMob.SetOutline() ##END_KILLGUI Puis au dessus de la ligne: def __ProcessPreservedServerCommand(self): Ceci: def __hidekillgui(self): self.KillGuiBg.Hide() self.KillBlauReich.Hide() self.KillGelbReich.Hide() self.KillRotReich.Hide() self.KillMob.Hide() def __showkillgui(self): if constInfo.killgui == 0: constInfo.killgui = 1 self.KillGuiBg.Show() self.KillBlauReich.Show() self.KillGelbReich.Show() self.KillRotReich.Show() self.KillMob.Show() elif constInfo.killgui == 1: constInfo.killgui = 0 self.KillGuiBg.Hide() self.KillBlauReich.Hide() self.KillGelbReich.Hide() self.KillRotReich.Hide() self.KillMob.Hide() def __KillBlauReich(self, KillBlauReich): self.KillBlauReich.SetText(" " + KillBlauReich) def __KillGelbReich(self, KillGelbReich): self.KillGelbReich.SetText(" " + KillGelbReich) def __KillRotReich(self, KillRotReich): self.KillRotReich.SetText(" " + KillRotReich) def __KillMob(self, KillMob): self.KillMob.SetText(" " + KillMob) Repackez votre fichier root et c'est bon. Optimisé pour l'interface V5 (avec raccourcis) Par LordGune Nul besoin de faire tout un pitch pour expliquer comment mettre ce changement: #START_KILLGUI KillGuiBg = ui.AniImageBox() KillGuiBg.AppendImage("d:/ymir work/ui/blue_killgui_interface.tga") self.KillGuiBg = KillGuiBg self.KillGuiBg.SetPosition(wndMgr.GetScreenWidth()-235,185) self.KillBlauReich = ui.TextLine() self.KillBlauReich.SetDefaultFontName() self.KillBlauReich.SetPosition((wndMgr.GetScreenWidth()-207)+120, 458) self.KillBlauReich.SetText("Bleue Tuer: NaN") self.KillBlauReich.SetOutline() self.KillGelbReich = ui.TextLine() self.KillGelbReich.SetDefaultFontName() self.KillGelbReich.SetPosition((wndMgr.GetScreenWidth()-209)+120, 354) self.KillGelbReich.SetText("Jaune Tuer: NaN") self.KillGelbReich.SetOutline() self.KillRotReich = ui.TextLine() self.KillRotReich.SetDefaultFontName() self.KillRotReich.SetPosition((wndMgr.GetScreenWidth()-209)+120, 246) self.KillRotReich.SetText("Rouge Tuer: NaN") self.KillRotReich.SetOutline() self.KillMob = ui.TextLine() self.KillMob.SetDefaultFontName() self.KillMob.SetPosition((wndMgr.GetScreenWidth()-215)+120, 572) self.KillMob.SetText("Monstre: NaN") self.KillMob.SetOutline() ##END_KILLGUI Merci à LordGune pour avoir partagé cette version optimisée ! Sources: Tuto, screens, adaptation de l'interface/écriture, (petite) traduction de la quête entièrement par moi. Le reste est de ©ChaoSS sur epvp. L'option "raccourcis" provient de "ToBii™" sur epvp. GUI v5 par .ωєιя∂ sur epvp. J'espère ne rien avoir oublié. Bon jeu à tous et bonne chance ! Source : Craven
  21. Bonjour, pour protéger son client, il va falloir pour contrer le plus de cheat/hack utiliser plusieurs méthodes, seule ou toutes à la fois comme: gnorer certains fichier Protéger ses packs Fermer le client en cas de fichier d'un certain type etc... Je vais donc vous apprendre ça durant ce tutoriel. Depacker votre root : Si vous ne savez pas comment faire je vous renvoie ici : Contenu Masqué Ouvrer le fichier intrologin.py : Vous cherchez ensuite : self.__LoadLoginInfo(" Il est possible que vous ayez par exemple : self.__LoadLoginInfo("logininfo.xml") Peut importe, suivez le tutoriel : Remplacez, ou enlevez le texte entre les (), mais ne supprimez pas toute la ligne, sinon, va falloir faire d'autres modifications : self.__LoadLoginInfo("RandomFile.py") Ou bien : self.__LoadLoginInfo() 3.Repackez le pack, et déplacez le dans votre dossier pack client. 2)La protection des packs. Alors, pour la protection des packs, je vous propose d'enlever les fichiers eix (fichier index) de votre client, ce qui fait que les dépackeurs classiques ne pourront plus les dépacks. Je ne vous pas vous citez 7000 méthodes, je n'en connais qu'une... !!!!!!!!!!!Avant toute chose, faites une copie obligatoire de votre client !!!!!!!!!!! Pour ça, télécharger : Contenu Masqué Ouvez le .exe puis : Faites : "Next >" Cochez la case "I accept the agreement" puis, faites : "Next >" Ce qui est sélectionné, c'est le dossier d'installation, remplir ou vous voulez le mettre, puis faites "Next >". Ce qui est sélectionné, c'est le nom dans votre menu démarrer de Enigma, sinon faites : "Next >". Cochez la case si vous voulez créer un icon du logiciel sur votre programme sinon, faites "Next >". Faites : "Install" Laissez la case "Launch Enigma Virtual Box" si vous voulez que le programme se lance juste après avoir cliquez sur "Finish", sinon décochez et appuyez sur "Finish". Pour une plus grosse facilité, je vous invite à trier vos .EIX dans un autre fichier que vos epk. Allez ensuite dans enigma : Cliquez sur le bouton "Brownse..." et sélectionner votre client : ça donne : J'insiste encore une fois sur la "COPIE". Le champs "Enter Output File Name:" va se remplir automatique tout seul. Faites ensuite un clique droit sur : "Virtual box Files" : Faites : New Folder : Cliquez sur "OK", puis : Nommez le "pack" et appuyez sur entrée. Faites ensuite un clique droit sur le dossier "pack" que vous venez de créer, puis faites : "Add File(s)". Allez maintenant selectionner tous vos fichiers EIX de votre client (1 par 1 en maintenant le touche ctrl, ou tous les fichiers entres vos deux cliques en maintenant la touche MAJ) Vous obtiendrez quelque chose qui ressemble à ça : Appuyez ensuite sur : "Process" : Ceci va apparaitre : Cliquez sur "Run", si vous voulez démarrer votre nouveau client. Ou close pour fermer cette fenetre. Vous pouvez constater que dans votre client, un nouveau .exe est apparue : Vous pouvez remarquer que le boxed est plus gros ! Pourquoi ? Car il contient en lui les EIX !! Et voilà, vous pouvez maintenant utiliser votre client avec le metin2client_boxed.exe sans vos eix ! 3)Bloquer certains types de fichier. Le but ici est de bloquer les fichiers : asi ; m3d ; flt ; mix ; py qui pourrait se trouver en dehors de vos packs . 1.Depacker votre root : Allez dans votre fichier prototype.py Cherchez : import stringCommander Ajoutez : import antihack Téléchargez : Contenu Masqué Déplacez directement ce fichier dans votre pack root, puis repackez. Maintenant pour un seul type de fichier... Télécharger toujours cette archive : Contenu Masqué Vous aurez pour deux types de fichier : def RunCheckUp(): Ordner = os.listdir('.') Ordner.sort() for Datei in Ordner: if Datei.find('.asi') != -1: dbg.LogBox("Une erreur est survenue: " + str(Datei) + "") try: os.remove(Datei) except: dbg.LogBox("Erreur !") dbg.LogBox("Fichier: " + str(Datei)) app.Abort() else: pass def RunCheckUp1(): Ordner = os.listdir('.') Ordner.sort() for Datei in Ordner: if Datei.find('.py') != -1: dbg.LogBox("Une erreur est survenue: " + str(Datei) + "") try: os.remove(Datei) except: dbg.LogBox("Erreur !") dbg.LogBox("Fichier: " + str(Datei)) app.Abort() else: pass Par exemple, je vérifie ici les fichier .asi dans mon : RunCheckUp(): Puis, dans mon RunCkeckUp1() je vérifie les fichier .py Il vous suffit de supprimer toute une paragraphe, par exemple si je veux que les fichiers .asi et .m3d (qui sont à l'opposer du fichier, j'aurais : def RunCheckUp(): Ordner = os.listdir('.') Ordner.sort() for Datei in Ordner: if Datei.find('.asi') != -1: dbg.LogBox("Une erreur est survenue: " + str(Datei) + "") try: os.remove(Datei) except: dbg.LogBox("Erreur !") dbg.LogBox("Fichier: " + str(Datei)) app.Abort() else: pass def RunCheckUp4(): Ordner = os.listdir('.') Ordner.sort() for Datei in Ordner: if Datei.find('.m3d') != -1: dbg.LogBox("Une erreur est survenue: " + str(Datei) + "") try: os.remove(Datei) except: dbg.LogBox("Erreur !") dbg.LogBox("Fichier: " + str(Datei)) app.Abort() else: pass Il faut après modifier à la fin du fichier ce bout de code : RunCheckUp() RunCheckUp1() RunCheckUp2() RunCheckUp3() RunCheckUp4() De façon à n’appeler que vos defs et non des defs inexistante par exemple pour l'exemple du haut, j'aurais ici : RunCheckUp() RunCheckUp4() Contenu Masqué Contenu Masqué Contenu Masqué Source : Takuma
  22. tutoriel Grande Liste de textures.

    Bonjour/Bonsoir à tous. Voilà , je tiens à faire une très grande liste de textures. C'est à dire ? Pour les mobs j'ai mis à quoi correspondaient chaque fichier. Et je dis oú on peut trouver les textures principales utiles pour son serveur. J'ai pris un temps fou à faire ça x.x bref peu importe commençons ! Alors j'ai fais cette liste qu'à partir du client 2012 /!\. Rendez vous directement dans le fichier Ymir Work. Commençons par le fichier Item : Le fichier ETC : Vous trouverez des objets dont des monstres peuvent faire tomber ou des objets qui appartiennent à la décoration du jeu. Basket1 => Un panier d'oeufs de pâques vide Basket2 => Un panier d'oeufs de pâques pleins Boss_box => C'est la boîte de boss (Quand on tue un boss logique ^^) Digger => Des pioches Easter_egg1 => Oeuf de pâques fishing_rod => Une canne à pêche football1 => Un ballon de foot (étrange sur Metin2) god_herb => Une simple fleur gold_nugget => Un lingot d'or item_bag => C'est le petit sac qu'on fait tomber quand on drop un item quelconque item_box1 => X si quelqu'un sait ^^ jewellery_box => Boite rayon de lune medicine1 et medicine2 => Les textures des potions. (rouges , bleues , vertes ect ect) money => Yangs q_box => Boite puzzle silver_nugget => Lingot d'argent socks => La chausette de Noël Le fichier Quest comporte pas des textures mais plutôt des icons de coiffures comme ceci : Le fichier Weapon comporte les textures des armes. buke => Le bouquet de fleur qui sert au mariage weapon_choegogeup01_01 => Différentes armes 65 weapon_choegogeup01_02 => Armes allant du niveau 70 à 90 (épée sura , épée , éventail) weapon_choegogeup01_03 => Armes allant du niveau 70 à 90 (Lances , dagues , éventails) weapon_choegogeup01_04 => Armes allant du niveau 70 à 90 ( Gongs , arcs ) weapon_choegogeup01_05 => Armes 80 ( Arc diabolique géant , gong hibiscus , Eventail démoniaque) weapon_chogeup_01 => Armes allant du niveau 0 à 20 ( Lances , dagues , épées , gongs) weapon_chogeup_02 => Armes allant du niveau 0 à 20 (Eventails , arcs) weapon_gogeup_01 => Armes allant du niveau 45 à 60 (Epees , dagues , eventails , gongs) weapon_gogeup_02 => Armes allant du niveau 45 à 60 (Lances) weapon_junggeup_01 => Armes allant du niveau 25 à 40 (Arc , eventails , épées , gongs) weapon_junggeup_02 => Armes allant du niveau 25 à 40 ( Lances , éventails , dagues) weapon_junggeup_03 => Armes lvl 30 (LFR , LP , DFN) Courage ce n'était que le début ! Passons au plus long ! Les fichiers des monstres. barbarian_boss => Capitaine Bestial barbarian_bow => Archer Barbare / Archer Bestial. barbarian_infantry => Soldat Barbare / Soldat Bestial barbarian_knight => Général Barbare / Spécialiste Bestial barbarian_soldier => Minion Barbare / Maniac Bestial bear => Ours / Grizzly / Ours brun / Ours noir bkarcher => Archer du serment blanc bkboss => Jin Hee bkfourth => Se-Rang bkknight => Général du serment blanc / Cdt du serment Blanc … bksecond => Mi-Jung bksoldier => Soldat du serment Blanc bkthird => Eun-Jung bou => Bo chaos_ghost => Esprit du Chaos chuhen => Chuong fox_ninetail => Neuf Queues goblin_leafhead => Croque-mitaine greenfrog_general => Chef Grenouille des arbres. greenfrog_soldier => Soldat Grenouille des arbres gupae => Goo-Pae ice_snow_dog => Lion de glace ice_snow_giant_man => Yéti ice_snow_golem => Golem de glace ice_snow_insect=> Insecte de glace ice_snow_man => Homme de glace ice_snow_monster => Glace enchantée ice_snow_whale => Baleine de glace ice_snow_witch => Sorcière de glace immotal_ghost => Esprit éternel maenghwan => Mahon metinstone => Metin du niveau 5 à 45 metinstone_2 => Metin du niveau 50 à 90 metinstone_egg => Metin pascale (Oeuf) metinstonebig => Metin de la revanche milgyo_executor => Executeur ésotérique milgyo_founder => Chef esotérique milgyo_monster1 => Bourreau Sombre milgyo_monster2 => Invocateur esotérique milgyo_nahan_general => Chef arahan esotérique milgyo_nahan1 => Arahan esotérique milgyo_nahan2 => Combatant esotérique milgyo_religionist => Fanatique esotérique misterious_diseased_boss => Chef péstiféré misterious_diseased_bosshost => Port Géant Pestiféré misterious_diseased_bow => Archer pestiféré misterious_diseased_dog => Chien pestiféré misterious_diseased_egg => Oeuf infecté misterious_diseased_host => Porteur pestiféré misterious_diseased_infector => Pestiféré misterious_diseased_kid => Ame d'un corps sans vie misterious_diseased_spear => Lancier pestiféré misterious_diseased_sword => Soldat pestiféré mountain_dog_god => Lykos orc_bigblack => Orc noir géant orc_black => Orc noir orc_general => Général Orc Élite orc_knight => Combattant Orc Élite orc_lord => Chef Orc orc_magican =>Sorcier Orc Élite orc_scouter=> Eclaireur Orc élite orc_soldier => Orc élite outlaw => Hors la loi du Désert recycle_monster => Bête démoniaque skeleton_bigboss => Maître à épée squelette skeleton_general => Vil Chef Démoniaque skeleton_god => La faucheuse skeleton_king => Roi démon skeleton_magician => Chamane démoniaque skeleton_soldier_bow =>Archer démon skeleton_soldier_scythe => Soldat démoniaque skeleton_soldier_spear=> Lancier démoniaque skeleton_wizard =>Vile Tête de mort volante spite_ghost => Esprit de rancune stray_dog => Chien errant sugu_general =>Gal Grenouille-taureau thief1 => Soldat du vent noir thief2 =>Maniac du vent noir thief3=> Archer du vent noir thiefboss1 => Jak-to du vent Noir thiefboss2=>To-Su du Vent Noir thiefboss3=> Gu-Ryung Du vent Noir tiger => Tigre / Tigre blanc wild_boar => Sanglier sauvage / Sanglier rouge sauvage wild_boar_god =>Scrofa wolf => Loup /Loup bleu / Loup Gris yellow_tigerman => Esprit du Tigre Jaune blue_dragon => Beran Setaou ch_bowman => Archer Setaou ch_footman => Soldat Setaou ch_general => Général Yonghan ch_magician=> Maîtresse Setaou ch_officer => Chef Setaou ch_water_dragon => Beran Setaou ent_black =>Arbre maléfique ent_elder => Seigneur Esprit d'arbre ent_guru =>Esprit de la souche ent_hu => Dryade ent_huge =>Esprit D'arbre Géant ent_red =>Saule pleureur rouge end_trent =>Arbre Fantôme evil_eye =>Oeil Volant fennec_fox => Renard fire_dragon => Dragon Rouge fire_ghost => Esprit des flammes fire_king => Roi flamme fire_knight => Guerrier Flamme fire_man => Flamme fire_tiger => Tigre minion Combattant fire_tiger_boss => Fort Combattant Tigre / Hae-Tae giant_desert_turtle => Tortue des sables Géante giant_scorpion => Roi scorpion gnoll_commander => gnoll_commander gnoll_hellhound => gnoll_hellhound gnoll_mage => gnoll_mage gnoll_minotaur => gnoll_minotaur gnoll_warrior => gnoll_warrior god_monkey => Seigneur Singe gold_monkey => Singe qui marche (Oui c'est écrit ça dans ma BDD o_o) golem_1 => Golem de pierre golem_2 => Golem de pierre golem_3 => Golem géant de pierre japanese_pirate => Pirate Tanaka monkey => Singe mutant_1 => Destructeur mutant_2 => Ogre Berseker mutant_3 => Millier Combattant naga_commander => nage_commander naga_mage => nage_mage naga_soldier => naga_soldier naga_warrior => naga_warrior nersluck_1 => Ogre Guerrier nersluck_2 => Ogre Boucher nersluck_3 => Ogre Berseker outlaw => Hors la loi scorpionman_bow => Archer Scorpion scorpionman_sword => Jeune Homme scorpion seal_stone => Pilier ? snakeman_bow =>Archer Serpent snakeman_sword =>Epéiste serpent spider_king => Roi araignée spider_nipper => Araignée griffue spider_poison =>Araignée venimeuse spider_queen => Reine araignée spider_redpoison => Araignée rouge venimeuse spider_soldier => Soldat araignée mortelle spider_young => Jeune araignée stone_monkey => Singe de pierre troll_argus => troll_argus troll_commander => troll_commander troll_mage => troll_mage troll_warrior => troll_warrior zombie_bigboss => Major Infernal zombie_bigboss2 => Azrael zombie_diseased_boss => Griffeur Infernal zombie_diseased_bow => Archer Infernal zombie_diseased_dog => Chien Infernal zombie_diseased_infector =>Zombie zombie_diseased_kid => Méduse infernal zombie_diseased_spear => Garde Infernal zombie_diseased_sword => Officier infernal zombie_general => Général Infernal zombie_ghost => Boucher Infernal zombie_god =>Charon zombie_king => Tartare zombie_magician => Prêtre Infernal zombie_soldier_bow =>Archer Infernal zombie_soldier_scythe => Guerrier Infernal zombie_soldier_spear =>Lancier infernal Courage ! C'est parti pour les armures ! Rendez vous dans les fichiers PC et PC2 Assassin = Aux ninjas : Armure lvl 0 : assassin_tanma Armure lvl 9 : assassin_geukseom Armure lvl 18 : assassin_dahong Armure lvl 26 : assassin_biyeong Armure lvl 34 assassin_yeongrin Armure lvl 42 : assassin_jeoksal Armure lvl 48 : assassin_yonga Armure lvl 54 : assassin_salpung Armure lvl 61 : assassin_bihyeon Armure lvl 66 : assassin_4-1 assassin_novice_green : Tenue du début 2 assassin_novice_red : Tenue du début 1 Sura Armure lvl 0 : sura_jinhon Armure lvl 9 : sura_mageuk Armure lvl 18 : sura_biun Armure lvl 26 : sura_saryeong Armure lvl 34 :sura_eumyang Armure lvl 42 : sura_heukbi Armure lvl 48 :sura_unra Armure lvl 54 : sura_gwimyeon Armure lvl 61 : sura_maryeong Armure lvl 66 : sura_4-1 sura_novice_black : Tenue du début 1 sura_novice_blue:Tenue du début 2 sura_unra_cloak : Cape de l'armure 48 , 54 , 61 sura_saryeong_cloak : Cape de l'armure 26 , 34 , 42 Shaman : Armure lvl 0 : shaman_cheongnang Armure lvl 9 : shaman_nabong Armure lvl 18 : shaman_bihong Armure lvl 26 shaman_miyeom Armure lvl 34 : shaman_seocheon Armure lvl 42 shaman_ilseon Armure lvl 48 : shaman_cheonryun Armure lvl 54 : shaman_amyo Armure lvl 61 : shaman_bongsin Armure lvl 66 : shaman_4-1 Warrior = Guerrier Armure lvl 0 warrior_nahan Armure lvl 9 warrior_giryung Armure lvl 18 warrior_jaho Armure lvl 26 warrior_saja Armure lvl 34 warrior_jain Armure lvl 42 warrior_moryong Armure lvl 48 warrior_cheongrin Armure lvl 54 warrior_geumrin Armure lvl 61 warrior_yongsin Armure lvl 66 : warrior_4-1 Ne vous inquiétez pas c'est le même nom de texture pour les hommes et pour les femmes. Il nous reste 2 gros fichiers après je ferai très court et vous serez tranquille. Les PNJs : alchemist => Alchimiste arms => Marchand d'armes auntie => Aranyo baby_and_mom =>Ah-Yu bank => Animateur beggar => Passant ivre blacksmith => Forgeron boar => Monture Sanglier boar_2 => Monture Sanglier Pugnace bookworm => Soon ceramist => Yonah defence => Marchand d'armures doctor => Baek-Go dog_god => Monture Loup dog_god2 => Loup Bagarreur eojiryu fire_tiger => Monture Tigre fire_tiger_2 => Tigre tempéteux gangyo_patrol_bow => Maitres d'armes Shinsoo gangyo_patrol_spear => Gardien du village Shinsoo girl_lost_elder_brother => Mirine goldstatue => Grenouille dorée goods => Marchande / Marchande de coiffures guard_leader =>Capitaine horse => Fichier de chevaux de combat horse_event1 => Cheval lvl 30 horse2 => Chevaux militaires hotel_grandfa => Magasinier hotel_grandma => Vieille dame hunter => Yang_Shin jinno_patrol_bow => Maitre d'armes Chunjo jinno_patrol_spear >Gardien du village Chunjo lion => Monture lion lion_2 => Lion carnassier lion_white => Lion blanc mineral =>Les veines mirinae_brother => Cultivateur de Ginseng mr_reastaurant => Octavio musician =>Yu-Hwan obj_bag003 =>Sac d'herbe old_pirate => Balso le traitre oldster => Téléporteur peddler => Marchand ambulant plant_researcher => Uriel pony => Poney et chevaux normal privateshop => Magasin de vente reindeer_female => Monture reine (femelle) reindeer_male => Monture reine (male) rice_cake_seller =>Yu-Rang sailor =>Le pêcheur santa => Père Noel seal_stone => Pilier de la tour sinseon => X sinsu_patrol_bow => Maitre d'armes Jinno sinsu_patrol_spear => Gardien du village Jinno sura_skeleton => Squelettes de sura suraghost =>Fantome du Sura timid_boy => Taurean tombstone => Monument ? Warriorghost => Fantome du Guerrier widow => Ariyoung woodcutter => DeokBae worship_dragon => bâtiment young_merchant =>Huahn-So yu_hwa_rang =>Harang halloween1 => PNJ Halloween historian => Historien obj_bag003 => Sac d'herbe phoenix1 => Pet Phoenix Rouge phoenix2 => Pet phoenix Bleu privateshop => Magasin rabbit => Lapin de Pâques reiinder_young => Pet reine scheletro => Un squelette ? Starveling => Gens du désert sura_skeleton => Squelette de sura suraghost => Fantôme du Sura warriorghost=> Fantôme du Guerrier worship_dragon=> Bâtiment zombie_ghost_door =>Porte de la Damnation zombie_god_stone => Monument Catacombe zombie_key_stone =>Statue Catacombe zombie_security_stone => Statue de Tortue Catacombe zombie_warp_stone =>Monument Catacombe Comme j'ai dis je ferai court pas la suite. En espérant que ça puisse vous aidez ! Source : Mei
  23. Bonjour à tous, Avec ce système vous tapez dans votre chat : @[Pseudo] [Message] Et ça envoie tout seul le mp. Dépackez votre root : Ouvrez uichat.py : Remplacez la fonction : def __SendChatPacket(self, text, type): Par celle ci : def __SendChatPacket(self, text, type): if text.find("@") ==0: text = text.split(" ") user = text[0] user = text[0].split("@") user = user[1] del text[0] realtext = "" for i in xrange(len(text)): if i > 0: realtext = realtext + " " + text[i] else: realtext = realtext + text[i] if len(realtext) > 0: net.SendWhisperPacket(user, realtext) chat.AppendChat(chat.CHAT_TYPE_INFO, " " + user + " a vu votre message.") else: chat.AppendChat(chat.CHAT_TYPE_INFO, "Vous devez taper un message.") elif net.IsChatInsultIn(text): chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.CHAT_INSULT_STRING) else: net.SendChatPacket(text, type) PS : N'oubliez pas de replacer les tabulations, remplacez tous les 4 espaces par une tabulation. def __SendChatPacket(self, text, type):#On défenie une fonction qui utilise 3 arguments. if text.find("@") ==0:#Si dans le texte(un des arguments), on trouve "@" et qu'il est à la première place : text = text.split(" ") user = text[0] user = text[0].split("@") user = user[1] del text[0] realtext = "" for i in xrange(len(text)): if i > 0: realtext = realtext + " " + text[i] else: realtext = realtext + text[i]#Ici et au dessus c'est la procédure on va dire pour récupérer le texte, et l'utilisateur. if len(realtext) > 0:#Si le message contient des caractères : net.SendWhisperPacket(user, realtext)#On envoie le message chat.AppendChat(chat.CHAT_TYPE_INFO, " " + user + " a vu votre message.")#On envoie la confirmation à l'utilisateur else: chat.AppendChat(chat.CHAT_TYPE_INFO, "Vous devez taper un message.")#Si il n'y a pas de caractère, il est vide donc on envoie un message d'erreur elif net.IsChatInsultIn(text): chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.CHAT_INSULT_STRING)#On enlève les insultes. else: net.SendChatPacket(text, type)#Sinon on envoie le message normalement Source :EPVP
  24. Bonsoir, je fais un post pour expliquer comment utiliser la lib proposée par iMer, permettant de supprimer le système avec les fichiers item_proto.txt et mob_proto.txt présent sur les nouveaux files 2013 et de retrouver l'ancien système où on n'avait qu'à se soucier des tables item_proto et mob_proto. A l'issu du petit tutoriel, vous n'aurez plus besoin des fichiers .txt . Ce post est à destination des personnes ne voulant pas utiliser le système des fichiers item_proto.txt, ... présent dans le répertoire /db pour la version du core db 33820 mais revenir à l'ancien système utilisant les tables item_proto côté base de donnée. Certaines personnes préfèrent le nouveau système, d'autres l'ancien système. La philosophie du post est donc juste de montrer aux personnes le souhaitant, comment revenir à l'ancien système. Tout d'abord, téléchargez les lib nécessaires : libstdc++.so.6 Téléchargement Vous avez déjà cette lib sur votre ftp dans usr/lib ( FreeBSD 32 bits) ou usr/lib32 (FreeBSD 64 bits) selon la version de FreeBSD que vous utilisez. Il vous suffit donc la remplacer par la version présente ici en téléchargement. libdb_notxt.so Téléchargement C'est la lib qui permet d'interpréter vos tables SQL à la place des fichiers txt. Vous pouvez la placer où vous voulez, dans le répertoire /share par exemple ça sera très bien. Je vous ai mis en upload la dernière version disponible, normalement exempte des bugs des premières versions. Bien maintenant vous avez remplacé une lib existante et placé une seconde sur votre ftp. Rendez-vous maintenant dans votre script de démarrage du serveur (nommé start.sh si vous ne l'avez pas modifié). On va modifier l'exécution du core db pour qu'il prenne en compte libdb_notxt.so. Pour cela il suffit de remplacer la ligne concernée : cd ./db/ ./db & Par cd ./db && env LD_PRELOAD=/usr/home/metin2/share/libdb_notxt.so ./db & À noter que selon la version de FreeBSD que vous possédez, cela peut être LD_PRELOAD (FreeBSD 32 bits) ou LD_32_PRELOAD (FreeBSD 64 bits) que vous devez utilisez dans la ligne ci-dessus. À noter aussi que cette ligne est présente plusieurs fois dans votre script (car plusieurs options possibles lors du lancement du serveur) donc pensez bien à remplacer les différentes occurrences. Et à noter enfin que le chemin que j'ai mis est celui où est placé la lib libdb_notxt.so, peut-être que sur votre serveur ce n'est pas le même chemin selon où vous avez voulu la placer donc modifiez cela en conséquence ! Si vous rencontrez des soucis, veuillez utiliser le script que je propose à la fin du tuto, en vérifiant bien le chemin de la lib Il reste maintenant à régler le problème de la compatibilité des tables item_proto et mob_proto car on a enlevé certaines colonnes de ces tables dans nos files, mais la lib de iMer fonctionne pour des tables item_proto et mob_proto non retouchées. Voici donc les tables item_proto et mob_proto adaptées aux files EB 2013 (j'ai ajouté que les colonnes nécessaires pour que la lib de iMer fonctionne et les tables contiennent déjà tous les objets et mobs des files EB 2013) : item_proto mob_proto Si jamais pour une raison X ou Y vous voulez la structure originale des tables files 2013 (par exemple parce que vous utilisez un tool ou convertisseur particulier, ...), les voilà : item_proto 2013 original SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for item_proto -- ---------------------------- CREATE TABLE `item_proto` ( `vnum` int(11) unsigned NOT NULL DEFAULT '0', `name` varbinary(24) NOT NULL DEFAULT 'Noname', `locale_name` varbinary(24) NOT NULL DEFAULT 'Noname', `type` tinyint(2) NOT NULL DEFAULT '0', `subtype` tinyint(2) NOT NULL DEFAULT '0', `weight` tinyint(3) DEFAULT '0', `size` tinyint(3) DEFAULT '0', `antiflag` int(11) DEFAULT '0', `flag` int(11) DEFAULT '0', `wearflag` int(11) DEFAULT '0', `immuneflag` set('PARA','CURSE','STUN','SLEEP','SLOW','POISON','TERROR') NOT NULL DEFAULT '', `gold` int(11) DEFAULT '0', `shop_buy_price` int(10) unsigned NOT NULL DEFAULT '0', `refined_vnum` int(10) unsigned NOT NULL DEFAULT '0', `refine_set` smallint(11) unsigned NOT NULL DEFAULT '0', `refine_set2` smallint(5) unsigned NOT NULL DEFAULT '0', `magic_pct` tinyint(4) NOT NULL DEFAULT '0', `limittype0` tinyint(4) DEFAULT '0', `limitvalue0` int(11) DEFAULT '0', `limittype1` tinyint(4) DEFAULT '0', `limitvalue1` int(11) DEFAULT '0', `applytype0` tinyint(4) DEFAULT '0', `applyvalue0` int(11) DEFAULT '0', `applytype1` tinyint(4) DEFAULT '0', `applyvalue1` int(11) DEFAULT '0', `applytype2` tinyint(4) DEFAULT '0', `applyvalue2` int(11) DEFAULT '0', `value0` int(11) DEFAULT '0', `value1` int(11) DEFAULT '0', `value2` int(11) DEFAULT '0', `value3` int(11) DEFAULT '0', `value4` int(11) DEFAULT '0', `value5` int(11) DEFAULT '0', `socket0` tinyint(4) DEFAULT '-1', `socket1` tinyint(4) DEFAULT '-1', `socket2` tinyint(4) DEFAULT '-1', `socket3` tinyint(4) DEFAULT '-1', `socket4` tinyint(4) DEFAULT '-1', `socket5` tinyint(4) DEFAULT '-1', `specular` tinyint(4) NOT NULL DEFAULT '0', `socket_pct` tinyint(4) NOT NULL DEFAULT '0', `addon_type` smallint(6) NOT NULL DEFAULT '0', PRIMARY KEY (`vnum`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; mob_proto 2013 original SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for mob_proto -- ---------------------------- CREATE TABLE `mob_proto` ( `vnum` int(11) NOT NULL DEFAULT '0', `name` varchar(24) NOT NULL DEFAULT 'Noname', `locale_name` varbinary(24) NOT NULL DEFAULT 'Noname', `rank` tinyint(2) NOT NULL DEFAULT '0', `type` tinyint(2) NOT NULL DEFAULT '0', `battle_type` tinyint(1) NOT NULL DEFAULT '0', `level` smallint(3) NOT NULL DEFAULT '1', `size` enum('SMALL','MEDIUM','BIG') NOT NULL DEFAULT 'SMALL', `ai_flag` set('AGGR','NOMOVE','COWARD','NOATTSHINSU','NOATTCHUNJO','NOATTJINNO','ATTMOB','BERSERK','STONESKIN','GODSPEED','DEATHBLOW','REVIVE') DEFAULT NULL, `mount_capacity` tinyint(2) NOT NULL DEFAULT '0', `setRaceFlag` set('ANIMAL','UNDEAD','DEVIL','HUMAN','ORC','MILGYO','INSECT','FIRE','ICE','DESERT') NOT NULL DEFAULT '', `setImmuneFlag` set('STUN','SLOW','FALL','CURSE','POISON','TERROR') NOT NULL DEFAULT '', `empire` tinyint(4) NOT NULL DEFAULT '0', `folder` varchar(100) NOT NULL DEFAULT '', `on_click` tinyint(4) NOT NULL DEFAULT '0', `st` smallint(5) unsigned NOT NULL DEFAULT '0', `dx` smallint(5) unsigned NOT NULL DEFAULT '0', `ht` smallint(5) unsigned NOT NULL DEFAULT '0', `iq` smallint(5) unsigned NOT NULL DEFAULT '0', `damage_min` smallint(5) unsigned NOT NULL DEFAULT '0', `damage_max` smallint(5) unsigned NOT NULL DEFAULT '0', `max_hp` int(10) unsigned NOT NULL DEFAULT '0', `regen_cycle` tinyint(3) unsigned NOT NULL DEFAULT '0', `regen_percent` tinyint(3) unsigned NOT NULL DEFAULT '0', `gold_min` int(11) NOT NULL DEFAULT '0', `gold_max` int(11) NOT NULL DEFAULT '0', `exp` int(10) unsigned NOT NULL DEFAULT '0', `def` smallint(5) unsigned NOT NULL DEFAULT '0', `attack_speed` smallint(6) unsigned NOT NULL DEFAULT '100', `move_speed` smallint(6) unsigned NOT NULL DEFAULT '100', `aggressive_hp_pct` tinyint(3) unsigned NOT NULL DEFAULT '0', `aggressive_sight` smallint(10) unsigned NOT NULL DEFAULT '0', `attack_range` smallint(5) unsigned NOT NULL DEFAULT '0', `drop_item` int(10) unsigned NOT NULL DEFAULT '0', `resurrection_vnum` int(10) unsigned NOT NULL DEFAULT '0', `enchant_curse` tinyint(4) unsigned NOT NULL DEFAULT '0', `enchant_slow` tinyint(4) unsigned NOT NULL DEFAULT '0', `enchant_poison` tinyint(4) unsigned NOT NULL DEFAULT '0', `enchant_stun` tinyint(3) unsigned NOT NULL DEFAULT '0', `enchant_critical` tinyint(3) unsigned NOT NULL DEFAULT '0', `enchant_penetrate` tinyint(3) unsigned NOT NULL DEFAULT '0', `resist_sword` tinyint(4) NOT NULL DEFAULT '0', `resist_twohand` tinyint(4) NOT NULL DEFAULT '0', `resist_dagger` tinyint(4) NOT NULL DEFAULT '0', `resist_bell` tinyint(4) NOT NULL DEFAULT '0', `resist_fan` tinyint(4) NOT NULL DEFAULT '0', `resist_bow` tinyint(4) NOT NULL DEFAULT '0', `resist_fire` tinyint(4) NOT NULL DEFAULT '0', `resist_elect` tinyint(4) NOT NULL DEFAULT '0', `resist_magic` tinyint(4) NOT NULL DEFAULT '0', `resist_wind` tinyint(4) NOT NULL DEFAULT '0', `resist_poison` tinyint(4) NOT NULL DEFAULT '0', `dam_multiply` float DEFAULT NULL, `summon` int(11) DEFAULT NULL, `drain_sp` int(11) DEFAULT NULL, `mob_color` int(10) unsigned DEFAULT NULL, `polymorph_item` int(10) unsigned NOT NULL DEFAULT '0', `skill_level0` tinyint(3) unsigned DEFAULT NULL, `skill_vnum0` int(10) unsigned DEFAULT NULL, `skill_level1` tinyint(3) unsigned DEFAULT NULL, `skill_vnum1` int(10) unsigned DEFAULT NULL, `skill_level2` tinyint(3) unsigned DEFAULT NULL, `skill_vnum2` int(10) unsigned DEFAULT NULL, `skill_level3` tinyint(3) unsigned DEFAULT NULL, `skill_vnum3` int(10) unsigned DEFAULT NULL, `skill_level4` tinyint(3) unsigned DEFAULT NULL, `skill_vnum4` int(10) unsigned DEFAULT NULL, `sp_berserk` tinyint(4) NOT NULL DEFAULT '0', `sp_stoneskin` tinyint(4) NOT NULL DEFAULT '0', `sp_godspeed` tinyint(4) NOT NULL DEFAULT '0', `sp_deathblow` tinyint(4) NOT NULL DEFAULT '0', `sp_revive` tinyint(4) NOT NULL DEFAULT '0', PRIMARY KEY (`vnum`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; Remarque: N'oubliez pas de mettre des priorités suffisantes sur les libs ! Je re-précise que je ne suis pas à l'origine de la lib, l'auteur est iMer donc merci à lui pour ce partage fort utile si vous êtes réfractaire au nouveau système. J'espère n'avoir rien oublié, si c'est le cas dites le moi. Après avoir appliqué ces modifications, les fichiers .txt dans le répertoire /db sont maintenant inutiles, vous pouvez les supprimer (à condition que vous ne lanciez jamais le core db sans précharger la lib avant, cf le script start.sh !). Source: EB & EPVP Pour toutes questions, rendez-vous dans la catégorie Aide/Questions/Support
  25. Bonjour, Tous les jours, un administrateur de serveur privé a besoin de s'occuper de sa base de données. Pour cela, il a besoin d'un outil à la fois précis et puissant : le langage SQL. 1. Le SQL, c'est quoi ? SQL (sigle de Structured Query Language, en français langage de requête structurée) est un langage informatique normalisé servant à effectuer des opérations sur des bases de données relationnelles. La partie langage de manipulation de données de SQL permet de rechercher, d'ajouter, de modifier ou de supprimer des données dans les bases de données relationnelles. En plus du langage de manipulation de données, la partie langage de définition de données permet de créer, et de modifier l'organisation des données dans la base de données, la partie langage de contrôle de transaction permet de commencer et de terminer des transactions, et la partie langage de contrôle de données permet d'autoriser ou d'interdire l'accès à certaines données à certaines personnes. Créé en 1974, normalisé depuis 1986, le langage est reconnu par la grande majorité des systèmes de gestion de bases de données relationnelle (abrégé SGBDR) du marché. SQL fait partie de la même famille que les langages SEQUEL (dont il est le descendant), QUEL ou QBE (Zloof). 2. Un peu de vocabulaire SQL est un langage qui repose sur des requêtes, des ordres qu'on va demander d'effectuer à la base de données. On appelle ces requêtes des queries (query au singulier) (requête en français). En SQL, on travaille sur une base de données. Cette dernière est composée de plusieurs entités embriquées les unes dans les autres. Premièrement, une base de données est en fait composée d'une ou plusieurs bases, qui contiennent une ou plusieurs tables. Chaque table est composée de plusieurs champs (ou attributs) qui peuvent être de plusieurs types (nombres, textes, dates, ...). Une table est composée d'une ou plusieurs entrées, qui sont en faite les lignes. On peut également désigner la table table de la base base en base.table. 3. Le SQL, ça sert à quoi ? Le langage SQL permet d’interragir avec les bases de données. Cela veut dire que c'est grâce à lui que votre serveur fonctionne. À chaque fois qu'un joueur fait une action, le programme client envoie indirectement une requête SQL vers la base de données. Le programme client sélectionne sans arrêt les informations contenues dans la base de données et les exploitent pour en faire ce que vous voyez à l'écran. Prenons un exemple simple : Un objet est dans votre inventaire. Vous cliquez dessus pour l'équiper. Lors de votre clic, de nombreuses requêtes SQL vont s'effectuer. Ainsi, dans la table player.item, l'objet qui était votre armure va être placé dans votre inventaire, celui que vous voulez mettre dans votre équipement. Dans la table player, on met l'ID de l'objet dans le champs part_main. Le programme client va alors sélectionner à nouveau l'équipement, l'inventaire et la partie centrale du personnage pour les afficher à l'utilisateur graphiquement. Attention : le processus est ici simplifié à l'extrême, d'autres facteurs rentrent en jeu mais je pense que cela peut vous aider à comprendre. La grosse différence avec la réalité est dans le fait que pour des raisons de sécurités évidentes, seul le serveur peut émettre des requêtes SQL. Ainsi c'est le client qui demande des données au serveur, le serveur qui récupère ces informations depuis la base de données et qui les renvoie au client. 4. Les différents types de requêtes En SQL, on a quatre grands types de requêtes : les requêtes d'ajouts, de modifications, suppressions et de sélections. Le langage SQL étant un langage de haut niveau (proche du langage humain), on utilise pour ces requêtes des mots clés, en anglais. Chaque type de requête obéit à une syntaxe et une structure précise. La requête d'ajout : INSERT INTO table (champs1, champs2, champs3) VALUES (valeur1, valeur2, valeur3) Cette requête va ajouter dans la table table une nouvelle entrée qui dans le champs1 aura comme valeur valeur1, champs2 valeur2, champs3 valeur3. La requête de modification : UPDATE table SET champs1 = valeur1, champs2 = valeur2 Cette requête va modifier le champs1 en valeur1 et le champs2 en valeur2 de toutes les entrées de la table table. La requête de suppression : DELETE FROM table Cette requête va supprimer tout le contenu de la table table. La requête de sélection : SELECT champs1, champs2, champs3 FROM table Cette requête va sélectionner (et afficher) le contenu des champs1, champs2 et champs3 de chaque entrée de la table table. Il est également possible de sélectionner d'un coup tous les champs comme ceci : SELECT * FROM table Cette requête va sélectionner le contenu de tous les champs de chaque entrée de la table table. 5. Les critères de sélection Heureusement, il est possible d'affiner nos requêtes avec des critères de sélection. Le mot clé WHERE : Reprenons une requête de sélection basique : SELECT * FROM table WHERE champs1 = valeur1 Cette requête va sélectionner le contenu de tous les champs de chaque entrée de la table pour laquelle champs1 est égal à valeur1[/php]. On peut comprendre le mot WHERE par sa traduction littérale : où. Il est possible d'ajouter des conditions en utilisant les opérateurs logiques AND (et) et OR (ou). SELECT * FROM table WHERE champs1 = valeur1 AND champs2 = valeur2 Cette requête va sélectionner le contenu de tous les champs de chaque entrée de la table pour laquelle champs1 est égal à valeur1 et champs2 est égal à valeur2. Le mot clé ORDER BY SELECT * FROM table ORDER BY champs1 Cette requête va sélectionner le contenu de tous les champs de chaque entrée de la table table ordonné dans l'ordre croissant en fonction de champs1. Il est également possible de trier dans l'ordre décroissant comme ceci : SELECT * FROM table ORDER BY champs1 DESC Cette requête va sélectionner le contenu de tous les champs de chaque entrée de la table table ordonné dans l'ordre décroissant en fonction de champs1. On peut même trier selon différents critères, le premier étant le plus important. SELECT * FROM table ORDER BY champs1, champs2 Cette requête va sélectionner le contenu de tous les champs de chaque entrée de la table table ordonné dans l'ordre croissant en fonction de champs1 dans la table table. Pour les valeurs égales dans le champs champs1, elles sont triées en fonction du champs champs2 dans l'ordre croissant. Le mot clé LIMIT SELECT * FROM table LIMIT valeur1, valeur2 Cette requête va sélectionner le contenu de tous les champs de l'entrée valeur1 pour valeur2 entrées de la table table. Tout ça est un peu flou ? Une application concrète : SELECT * FROM table LIMIT 0, 10 Cette requête va sélectionner le contenu de tous les champs à partir de l'entrée 0 pour 10 champs. La requête affichera donc 10 entrées. Attention à ne pas confondre le nombre d'entrées avec une entrée maximum ! Par exemple : SELECT * FROM table LIMIT 5, 5 Cette requête va sélectionner le contenu de tous les champs à partir de l'entrée 5 pour 5 champs. On aura donc 5 entrées, et la première sera la 5e entrée trouvée par SQL. Un assemblage démoniaque Tous ces critères de sélection peuvent s'assembler pour former des requêtes diablement précises. Cependant, ils ne peuvent être utilisés que dans ce sens : SELECT * FROM table WHERE champs1 = valeur1 ORDER BY champs2 LIMIT valeur2, valeur3 Cette requête va sélectionner le contenu de tous les champs pour lesquels champs1 = valeur1 ordonnés dans l'ordre croissant en fonction de champs2 à partir de l'entrée valeur2 pour valeur3 entrées de la table table. Un petit exemple numérique pour mieux comprendre : SELECT champs1 FROM table WHERE champs1 = 1 ORDER BY champs2 DESC LIMIT 2, 100 Cette requête va sélectionner le contenu du champs champs1 pour lequel champs1 est égal à 1 ordonné dans l'ordre décroissant en fonction du champs champs2 à partir de la 2e entrée pour 100 entrées de la table table. C'est plus clair maintenant ? Voyons comment appliquer toutes ces connaissances à notre base de données : 6. Application à la base de données d'un serveur metin2 La base de données d'un serveur metin2 contient environ 8 à 9 bases, pour une moyenne d'un peu plus de 150 tables (cela varie en fonction des files utilisées). Dans ces tables, toutes ne sont pas exploitables aussi facilement que les autres. Nous nous intéresserront dans ces applications aux tables player, item et account. Création d'un classement des joueurs Un classement se résume à une sélection des joueurs en tenant compte de leur niveau et de leur expérience. SELECT * FROM player ORDER BY level DESC, exp DESC Cette requête va sélectionner le contenu de tous les champs de chaque personnage ordonnées par ordre décroissant en fonction du niveau du personnage. Lorsque le niveau est le même, on trie par rapport aux points d'expériences dans l'ordre décroissant. On peut ajouter aussi une limite pour ne sélectionner que les 50 premiers : SELECT * FROM player ORDER BY level DESC, exp DESC LIMIT 0, 50 Et pour finir, pourquoi ne pas prendre que les joueurs qui ont plus de 1 000 000 yangs sur eux ? (les pauvres on s'en fout ! *.*) SELECT * FROM player WHERE gold >= 1 000 000 ORDER BY level DESC, exp DESC LIMIT 0, 50 Et voici notre requête finale ! Modification d'un item Imaginons que nous voulons cheater l'objet d'un joueur en particulier (hé oui, vive la triche ). Nous pouvons faire ça facilement grâce à une requête SQL. UPDATE item SET attrtype0 = 5, attrvalue0 = 999999, attrtype1 = 3, attrvalue1 = 999999, attrtype2 = 4, attrvalue2 = 999999 WHERE vnum = 10 AND owner_id = 145 ette requête va modifier les bonus de l'objet de vnum 10 (épée lv.0) du personnage d'id 145. Suppression d'un compte Nous voulons supprimer un compte (juste le compte, pas ses persos...). DELETE FROM account WHERE id = 12 Cette requête va supprimer le compte d'id 12. 7. Conclusion Merci d'avoir lu ce tutoriel sur le langage SQL adapté aux serveurs metin2. Ce tutoriel a entièrement été rédigé par Hystos pour Funky Emulation. Merci de ne pas le copier sans citer la source et sans mon accord écrit. Source : Hystos Réécriture : FrancH
×

Information importante

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