Search the Community

Showing results for tags 'tutoriel / partage'.



More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Register & Login
    • Register
    • Login
  • Espace Communautaire
    • Funky Emulation
    • Présentations
    • Services
    • Discussions Générales
    • Bureau de la Communauté
    • Espace Premium
  • Emulation & Co
    • Suggest a Release / Tutorial
    • The Ideas Box
  • Emulation de jeux
    • RaiderZ
    • Aura Kingdom
    • Metin2
    • Dofus
    • World of Warcraft
    • Minecraft
    • Aion
    • Habbo
    • Voir plus...
  • Espace Divers
  • PassionDev's Forum
  • M2 Project's M2P
  • M2 Project's Aide / Questions / Support
  • M2 Project's Tutoriels & Partages
  • M2 Project's Suggestions
  • M2 Project's Report de Bugs

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Nationality


Sexe


Discord


Skype


Biographie


Site


Nombre

 
or  

Found 762 results

  1. Niveau requis : Intermédiaire Temps estimé : Entre 25 et 30 minutes Bonjour j'ai vue ce partage sur d'autre forum mais pas ici, donc je vous en fais une petite traduction ici pour les moins bilingue d'entre nous Dans le tutoriel il y est utilisé la variable "int" car elle propose une limite de 2147483647 niveau pour cette utilisation ce qui semble bien plus que suffisant c'est pourquoi l'auteur du post recommande cette variable. Bref j'imagine que je n'ai pas vraiment de vous expliquer en quoi consiste ce tuto, voici un petit screen du résultat : Pré-requis: - Les sources de votre serveur - Les sources de votre client - Un accès ftp via FileZilla ou WinSCP - Un accès BDD via Navicat I. Partie /Srcs/Server/game/ II. Partie /Srcs/Server/common/ III. Partie /Srcs/Server/db/ IV. Parti /SrcsClient/UserInterface/ Pour finir dirigez vous vers Navicat, ouvrez votre bdd 'player' faites clique droit sur la table 'player', selectionnez "Design Table", déscendez a la ligne 'level', dans la colonne 'Type' modifiez en 'int' et dans la colonne 'Length' modifiez le chiffre et mettez '5' pour etre tranquille. Le fichier 'constants.cpp' : ICI Credit, Source : ~masodikbela Pour continuer: Vous n'avez plus qu'a compiler et modifier le niveau max de vos files : Modifier le level maximum
  2. Bonjour, Bonsoir Je vous partage un système de magasin Hors Ligne qui est debug par rapport à celui qui est déjà partagé sur FE pars History/Liberty. Niveau requis : Intermédiaire Temps estimé : 30 minutes Pré-requis: - Sources Serveur - Sources Client - Client Download Un aperçu du système ( Je ne suis pas la personne qui à fait la vidéo ) : PS : Je n'ai pas testé le Partage de History/Liberty mais on ma dit qu'il ne fonctionnait pas si je me trompe merci de faire signe. Sources : Turkmmo Pour continuer: Vous trouverez de nombreux partages et tutoriels sur Funky-Émulation dans la section Metin2. La FAQ pourra répondre à la plupart de vos questions. Une catégorie support est disponible. Si vous avez un problème ou une question, n'hésitez pas à poster une demande !
  3. Bonjour, Bonsoir Les Funkiest, je vous partage un système qui va vous permettre de stocker vos items Up, Pierre, Manuel dans différents inventaires. Niveau requis : Intermédiaire Temps estimé : 30 minutes Pré-requis: - Sources Client - Sources Serveur - Client Tout se trouve dans le dossier, tout est expliquer la dedans. Download Aperçu : [Hidden Content] Sources : Turkmmo Pour continuer: Vous trouverez de nombreux partages et tutoriels sur Funky-Émulation dans la section Metin2. La FAQ pourra répondre à la plupart de vos questions. Une catégorie support est disponible. Si vous avez un problème ou une question, n'hésitez pas à poster une demande !
  4. Niveau requis : Intermédiaire Temps estimé : 30 minutes Bonjour, Bonsoir, Aujourd'hui je vous partage un Système qui vous permettra de changer les yang de l'inventaire ainsi que dans les Shop si je ne dit pas de bêtise. Tout est expliquer dans le Fichier RAR. Pré-requis : Client Sources Client Sources Serveur Téléchargement En cas de problème veuillez ouvrir un Sujet dans A/Q/S, je serait là pour vous donner un coup de main. PS : J'ai utilisé les Files de Saejin pour tester le Système, sa devrait marcher avec n'importe quel Files Sources : Metin2Downloads
  5. Gestion des yangs : Aujourd'hui je vais vous partager une quête qui permet d'échanger ces yangs contre un lingot n'importe ou dans la map. quest yangs begin state start begin when 80001.use begin--Ajuster en fonction de l'item a utiliser say_title("Gestion des yangs") say("") say("Bonjour.") say("Ici, vous pouvez transformer vos yangs en lingots.") say("Combien de yangs voulez vous transformer ?") say("Vous avez un total de "..pc.gold.." yang(s).") local sy = select("50M","100M","250M","500M","1MM","Aucun")--Ajuster en fonction des yangs du lingots if sy==6 then return elseif sy==1 then if pc.gold > 50000000 then --Ajuster en fonction des yangs du lingots pc.change_gold(-50000000)--Ajuster en fonction des yangs du lingots pc.give_item2(80003, 1) else syschat("Vous n'avez pas assez de yangs !") end elseif sy==2 then if pc.gold > 100000000 then--Ajuster en fonction des yangs du lingots pc.change_gold(-100000000)--Ajuster en fonction des yangs du lingots pc.give_item2(80004, 1) else syschat("Vous n'avez pas assez de yangs !") end elseif sy==3 then if pc.gold > 250000000 then--Ajuster en fonction des yangs du lingots pc.change_gold(-2500000000)--Ajuster en fonction des yangs du lingots pc.give_item2(80005, 1) else syschat("Vous n'avez pas assez de yangs !") end elseif sy==4 then if pc.gold > 500000000 then--Ajuster en fonction des yangs du lingots pc.change_gold(-500000000)--Ajuster en fonction des yangs du lingots pc.give_item2(80006, 1) else syschat("Vous n'avez pas assez de yangs !") end elseif sy==5 then if pc.gold > 1000000000 then--Ajuster en fonction des yangs du lingots pc.change_gold(-1000000000)--Ajuster en fonction des yangs du lingots pc.give_item2(80006, 1) else syschat("Vous n'avez pas assez de yangs !") end end end end end Si vous ne savez pas implanter une quête, suivez ce tutoriel : ICI
  6. Ouvrir Userinterface/instancebase.cpp : if (12010 <= vnum && vnum <= 12049) Ajouté : if (vnum >= 20760 && vnum <= 20959) { __AttachEffect(EFFECT_REFINED+YENI_YILDIRIM_EFEKTI _BLACK); } Ouvrir instancebase.h : EFFECT_BODYARMOR_SPECIAL2, Ajouté : YENI_YILDIRIM_EFEKTI_BLACK, Ouvrir root / playersettingsmodule.py : chrmgr.RegisterEffect(chrmgr.EFFECT_REFINED+20, "Bip01", "D:/ymir work/pc/common/effect/armor/armor-4-2-2.mse") Ajouté : chrmgr.RegisterEffect(chrmgr.EFFECT_REFINED+22, "Bip01", "D:/ymir work/pc/common/effect/armor/armor-5-1.mse") Lien téléchargement : TELECHARGEMENT Ajouter à l' index : *black_new_armor_effects * Source : Turkmmo
  7. Salut a tous, voici le Système d'information groupe [Hidden Content]
  8. Salut à tous, voici le Système de recherche de groupe officiel [Hidden Content]
  9. Salut à tous, voici le système de blocage officiel toujours de blackDragonx61 [Hidden Content]
  10. Niveau requis : Débutant Temps estimé d'installation : Entre 1 et 2 minutes Je vous partage une petite correction des balises codes & citations, alors elle n'est pas obligatoire mais par défaut je trouve que ces balises sont imposantes, si l'on cite une personne ou que l'on a un fichier de c++ à copier et bien cela prendra plus d'une page et cela gâche fortement l'esthétique d'un sujet. Voici donc à quoi ressemble les balises par défaut : Et après correction : Rendez-vous sur votre panel Admin Allez dans la Rubrique Personnalisation (Icon Pinceau) puis dans Thèmes Cliquez sur Editer l'HTML/CSS (Icon Crayon) de votre thème Cliquez sur l'onglet CSS et ouvrez le fichier custom.css Copiez ces codes à l’intérieur : .ipsCode{ overflow-y: auto; max-height: 250px; } .ipsQuote_contents{ overflow-y: auto; max-height: 200px; margin: 0px -12px !important; } Cliquez sur Enregistrer et rafraîchissez votre page. Information: A savoir que si certaines modifications ne sont pas prises en compte c'est qu'un code peut passer avant un autre dans ce cas ajouter le code !important avant chaque points virgules. Xayah,
  11. Niveau requis : Débutant Temps estimé : Entre 5 et 10 minutes ( Hors téléchargements ) Pré-requis: Une bonne connexion internet, un petit cerveau en fonction, Une bière,. surtout une bière ! I. Partie I - Les Téléchargements II. Configurer le client III. Configurer la partie serveur IV. BONUS une vidéo, que j'ai réalisé. Les sources: DiliS Discord (...) Pour les fichiers serveurs c'est la dernière release actuelle disponible pour les personnes ' patreon ' 30.01.2019. Bisou PS : Il m'est possible de partager les sources de l'émulateur ( en C# ) si des gens sont réellement interessés d'y travailler dessus et d'y partager les modifications avec la communauté DiliS. Pourquoi ne pas le mettre en meme temps ? Car des gens veulent < vendre > ces files qui ne sont pas à eux. Download
  12. Niveau requis : Débutant Temps estimé : Entre 5 et 10 minutes Je vous propose un partage d'un séparateur de sujets (différencier les topics épinglés & non épinglés) grâce à un plugin. Ce plugin est compatible sous IPS 4.0 , 4.1 ,4.2 & 4.3. (Testé) Voici à quoi ressemble la bête : Pré-requis: Le plugin en question : Lien médiafire Tutoriel & Modification Pour continuer: Si vous avez un problème, n'hésitez pas à poser votre demande d'aide dans la section AQS Source du plugin : Invision Virus Xayah, Download
  13. Bonjour à tous ! Un petit message d'introduction et on commence ! Tout d'abord merci à @Nicky31 pour son tutoriel qui représente une très grande partie de ce poste ! Je me permets de le reprendre pour corriger/apporter des éléments qui bloquaient pas mal de monde. Durée approximative : 10 à 30 en fonction de votre qualification. L'avantage d'Ancestra Remake 54 est le multi-serveurs (Il peut gérer plusieurs serveurs). Commençons ! 1 - Pré-Requis Disposer d'Ancestra Remake 54 Disposer d'une IP de connexion (Hamachi, No-Ip, dédié ...), à vous de voir ce que vous préférez. Hamachi : Simule un réseau local. No-Ip : Redirige sur votre IP accessible ici quand votre opérateur ne vous permet pas de la garder fixe. Dédié : Sur un autre PC constamment allumé fait maison ou loué en ligne. Hébergeur conseillé : Kimsufi / soyoustart. Disposer de WampServer (gratuit) et de Navicat (crack ou payé) sauf si vous savez vous débrouiller avec PhpMyAdmin Disposer de Dofus 1.29 (Certaines personnes ont des problèmes lors du lancement du client Dofus lorsqu'ils n'installent pas Dofus 1.29 dans Program Files (x86) ). 2 - Installer les base de données En premier lieu, l'émulateur a besoin, pour fonctionner, de deux bases de données. Je vais expliquer la procédure via Navicat, si vous utilisez PhpMyAdmin je pense que vous saurez vous débrouiller tout seul. Si ce n'est pas déjà fait, commencez par installer Wamp & Navicat. Lancez Wamp puis Navicat en attendant que Wamp passe au vert dans votre barre des tâches. Si Wamp ne passe pas vert (vous pouvez passer cette étape si c'est le cas), le problème vient probablement d'une interférence avec skype. Dans skype: Outils -> Options -> Avancées -> Connexion -> Décocher "Utiliser les ports 80 et 443 comme alternative". Redémarrez Skype & Wamp [*]Ouvrez Navicat, cliquez sur Connection -> MySQL [*]Mettez seulement localhost à Connection Name et un mot de passe de votre choix à Password (non obligatoire mais bon). Ne touchez pas à ce qui est déjà rempli, faites Ok [*]Double cliquez sur localhost normalement apparu dans la colonne de gauche de Navicat. (Afin d'ouvrir la connexion) [*]Click droit sur localhost (votre connexion), puis clickez sur New Database [*]Ecrivez ancestra_realm dans Enter database name puis clickez sur OK. [*]Répétez la même opération pour une bdd du nom de ancestra_game [*]Comme pour la connexion, double clickez sur vos deux bases de données afin de les "ouvrir" (cela s'affichera en vert) [*]On va maintenant importer des données dans nos deux bdd à partir de fichiers .sql: Click droit sur ancestra_realm, puis Execute Batch File et ciblez AncestraR_Realm.sql contenu dans le dossier AR54/BDD, puis faites Start [*]Répétez la même opération pour remplir ancestra_game à partir du fichier AncestraR_Game.sql, toujours situé dans le même dossier. Vous voilà maintenant avec vos deux bases de données crées, remplies, prêtes mais ... non à jours J'vous jure, j'vous ai pas fait faire de la *****, juste que les développeurs d'AR ont dû rectifier des choses sur les bases de données, et au lieu de mettre à jour les deux gros patchs qu'on vient d'exécuter, ont mis chaque rectification dans un fichier .sql à part. Tous ces fichiers .sql, vous pouvez les trouver dans le sous dossier BDD/Patchs. Attention ! Vous ne devez pas les exécuter n'importe comment. Vous pouvez observer que le nom de chaque fichier suit cette logique: AncestraR_Realm/Game - Patch revN°Patch - Infos sur contenu.sql Realm/Game correspond à la bdd sur laquelle doit être appliqué le patch. N°Patch correspond au numéro du patch (ils sont tous numérotés) Infos sur contenu correspond à quelques infos sur le contenu du patch. Vous devez exécuter tous ces patchs en faisant attention à le faire sur la bonne base de données, et dans l'ordre croissant des numéros. Exemple pour la db game: AncestraR_Game - Patch rev2 - Pets.sql en premier AncestraR_Game - Patch rev3 - Defenders of resources.sql en deuxième ... PS : Ne vous inquiétez pas "Patch rev5" affiche un message d'erreur dans les logs mais ce n'est pas grave. Au boulot ! Une fois ceci fait, vous aurez vos deux bases de données enfin prêtes et complètes. Nous pourrons alors passer à la configuration de l'émulateur en lui même 2(bis) - Survol du fonctionnement d'un Emulateur La particularité d'AR54 qui m'a poussé à faire un tuto sur son installation (alors qu'il y a déjà des tutos pour d'autres versions d'AR), c'est que la différence de fonctionnement entre cette version et celles antérieures complique sa configuration. Voilà comment fonctionne normalement un émulateur, dissocié en deux parties realm et game : Le client se connecte sur la partie realm. Il choisit son serveur, et le realm lui renvoi l'ip et le port du game gérant le serveur demandé. Le client coupe alors la connexion avec le realm pour se connecter au game Le game gère tout le reste du jeux. Il doit néanmoins communiquer avec le realm pour avoir certaine données. Dans les versions précédentes, le multi-serveur n'était pas géré: on ne pouvait alors avoir qu'un serveur, et il n'était plus utile d'avoir cette organisation avec une application realm et une autre pour chaque serveur. Les développeurs ont donc fusionné ces deux applications en une seule. De cette façon, le client restait tout le temps connecté sur la même application (pas tout à fait en réalité, mais on entrerait dans les détails techniques). Au niveau de la configuration; on n'avait qu'une config pour une application, avec une ip & un port à renseigner pour l'unique application. Avec le multi-serveur, les développeurs ont du dissocier le realm et le game : Un seul realm (serveur de connexion), et un game par serveur. Il faut donc à présent une config par appli' (deux pour un seul serveur), et des ips & ports en plus. En résumé, il nous faut: L'ip du realm et son port de connexion (celui sur lequel le client se connecte) L'ip de chaque serveur & leur port de connexion (donnés par le realm au client). Pour ne pas compliquer les choses, nous allons gérer qu'un serveur Le port de communication utilisé entre le realm et les games. L'identifiant de chaque game ; c'est avec celui-ci que le realm s'assure qu'il à affaire aux games attendus. (Sinon n'importe quel personne tierce pourrait connecter son propre game modifié) 3 - Configurer Ancestra Remake 54 La partie précédente n'était pas vraiment prévue, mais j'ai trouvé intéressant de parler du fonctionnement de l'émulateur, ça peut aider certains à comprendre certains éléments. Bon, la première étape est de faire connaître au realm notre game. Et une table de notre db realm est prévue pour cela. Une table ??? Kézako !? Voilà une nouvelle notion pour nous, jeunes padawans que nous sommes. Une table n'est rien d'autre qu'une partie d'une base de données, regroupant donc un certains nombre d'informations à propos d'une chose précise, informations organisées comme un tableau. D'où le nom table, qui en anglais signifie tableau. 1 - Base de Données Rendez vous dans ancestra_realm Double clickez sur la table gameservers Une fenêtre s'ouvre alors. Voilà que s'affiche le contenu de notre table. L'unique ligne affichée devrait être celle-ci : Colonnes en italiques id | ServerIP | ServerPort | State | ServerBDD | ServerDBName | ServerUser | ServerPassword | key 1 | 127.0.0.1 | 5555 | 0 | 127.0.0.1 | ancestra_game | root | | server1 L'id correspond à l'id du serveur. Vous pouvez trouver tous les fichiers d’emblème des serveurs dans Dofus/clips/artworks/servers. Leur nom correspond à leur id. Pour ma part je m'en fou un peu donc je laisse 1. IP de la machine qui aura le game. Donc votre ip hamachi/no-ip/dédié ou 127.0.0.1 si vous voulez tester en localhost. On laisse aussi 5555 au ServerPort, c'est ce port ainsi que la serverIP que le realm enverra au client pour qu'il se connecte au game. State, c'est l'état du serveur: 0 pour hors ligne, 1 en ligne, 2 sauvegarde. On laisse 0, le realm s'occupe de le mettre à 1 quand il reçoit la connexion du game. ServerBDD ; l'ip de notre db game. S'il est sur la même machine que le game vous pouvez laisser 127.0.0.1, sinon mettez son ip. ServerDbName ; le nom de la db game correspondante, on laisse ancestra_game (sauf si vous l'avez appelé autrement) ServerUser ; le nom d'utilisateur de la connexion à la db. Si vous ne l'avez pas changé depuis l'installation, c'est toujours root ServerPassword ; le mot de passe de la connexion à la db. Si vous n'en avez pas mit, il y en a pas. key ; c'est la clé qui identifie le game & qu'il devra donner au realm. Je décide de la laisser telle quelle, gardez la en mémoire si vous la changez. 2 - Configuration du realm A présent, direction AR54/Realm/Realm_Config.txt : REALM_PORT : Port de connexion à mettre dans la config.xml, on va prendre 444 REALM_COM_PORT : Port de communication entre le realm & les game. On prend 489 Je vous laisse configurer les identifiants de votre db realm. Dans le cas ou vous avez fait le tutoriel à la lettre ; vous êtes censé mettre : REALM_DB_HOST = 127.0.0.1 REALM_DB_USER = root REALM_DB_PASSWORD = REALM_DB_NAME = ancestra_realm Warning Si vous n'avez pas mis de mot de passe (password) ; n'oubliez pas de laisser un espace après le "=" sinon le serveur ne se lancera pas. 3 - Configuration du game Direction AR54/Game/Game_Config.txt: REALM_IP : l'ip hamachi/no-ip/dédié de la machine hébergeant le realm. Configurez l'accès à la db realm. Comme pour la partie db, mettez 127.0.0.1 si la db est sur la même machine que le game. HOST_IP : Ip hamachi/no-ip/dédié de la machine hébergeant le game. AUTH_KEY : La clé que vous avez indiqué dans la colonne key de la db GAME_PORT: Le port de connexion du game, que vous avez indiqué dans la colonne ServerPort (5555 dans ce tutoriel) COM_PORT : Port de communication entre le game & le realm. Il doit être identique à celui que nous avons mis dans la config realm, soit 489 PLAYER_LMIT : Ne pas laisser -1, il provoque un bug à la connexion au serveur (Merci à @Défense pour l'infos). Mettez donc une autre limite de joueurs, comme 100. Configurez l'accès à la db game de la même façon que pour la realm. Dans le cas ou vous avez fait le tutoriel à la lettre ; vous êtes censé mettre : #Configuration de la base de donnée du serveur de connexion REALM_DB_HOST = 127.0.0.1 REALM_DB_USER = root REALM_DB_PASS = REALM_DB_NAME = ancestra_realm # Configuration de la base de donnée DB_HOST = 127.0.0.1 DB_USER = root DB_PASS = DB_NAME = ancestra_game 4 - Configuration des .bat pour démarrer le realm et le game Ouvrez avec un éditeur de .txt les fichiers "startRealm.bat" du dossier "realm" et "game". Vous devez à présent renseigner la localisation de votre "java.exe". Dans mon cas : @echo off title Ancestra Remake rev 54 :loop "C:\Program Files\Java\jre1.8.0_191\bin\java.exe" -jar -Xmx1024m -Xms1024m Ancestra.jar goto loop PAUSE Petite astuce pour avoir votre chemin rapidement : - Cherchez votre java.exe qui devrait se trouver dans "Programmes". - Créez un raccourci sur le bureau - Clique droit sur le raccourci puis "propriété" - Copier le contenu de "Cible :" La partie "-Xmx1024m -Xms1024m" correspond à la ram que vous allouez à votre serveur. Ici 1024m donc 1G de ram. Normalement, vous avez à présent Ancestra Remake bien installé et configuré. Il ne reste plus qu'à indiquer au client Dofus l'ip et le port de connexion du realm. 5 - Configuration du client DOFUS Ne vous inquiétez pas, c'est beaucoup plus simple que pour l'émulateur. Direction Dofus/config.xml, remplacez tout le contenu par ceci : <config> <delay value="500"/> <rdelay value="3000"/> <rcount value="10"/> <conf name="En ligne"> <connserver ip="Adresse ip du serveur" name="Nom de votre serveur" port="Port du realm"/> <dataserver url="data/" type="local" priority="3" /> <dataserver url="[Hidden Content]" priority="1" /> <dataserver url="[Hidden Content]" priority="0" /> </conf> <conf name="En ligne (TEST)" type="test"> <dataserver url="data/" type="local" priority="3" /> <dataserver url="[Hidden Content]" priority="1" /> <dataserver url="[Hidden Content]" priority="0" /> </conf> <cacheasbitmap> <cache element="ExternalContainer/InteractionCell" value="false" /> <cache element="ExternalContainer/Ground" value="false" /> <cache element="ExternalContainer/Object1" value="false" /> <cache element="ExternalContainer/Object2" value="false" /> <cache element="ExternalContainer/Zone" value="false" /> <cache element="ExternalContainer/Select" value="false" /> <cache element="ExternalContainer/Grid" value="false" /> <cache element="ExternalContainer/Pointer" value="false" /> <cache element="GAPI/UI" value="false" /> <cache element="GAPI/UITop" value="false" /> <cache element="GAPI/Popup" value="false" /> <cache element="GAPI/UIUltimate" value="false" /> <cache element="GAPI/Cursor" value="false" /> <cache element="mapHandler/BACKGROUND" value="false" /> <cache element="mapHandler/Cell/Ground" value="false" /> <cache element="mapHandler/Cell/Object1" value="false" /> <cache element="mapHandler/Cell/Object2" value="false" /> <cache element="mapHandler/Cell/ObjectExternal" value="false" /> <cache element="Zone/Zone" value="true" /> <cache element="Zone/Pointers" value="true" /> </cacheasbitmap> </config> Remplacez de la ligne suivante par ce qui est demandé. <connserver ip="Adresse ip du serveur" name="Nom de votre serveur" port="Port du realm"> Exemple : <connserver ip="127.0.0.1" name="Serveur tuto" port="444"/> Si vous avez bien suivi ce tutoriel, vous devriez normalement mettre 444 pour le port du realm. Le nom du serveur ne dépend de rien de ce qu'on à fait avant. A présent que tout est en place, il ne reste qu'à lancer l'émulateur : En premier le Realm en lançant AR54/Realm/startRealm.bat puis le Game en lançant AR54/Game/startRealm.bat. (Vous pouvez renommer celui la en startGame.bat) Et voilà, distribuez la config.xml à vos joueurs, et votre serveur est en place ! Si vous avez des difficultés malgré le tutoriel je vous laisse les 3 configs que nous avons vu réglé pour faire tourner votre serveur en réseau local. Je conseille tout de même d'essayer un minimum pour ne pas être décourager pour la suite ! Game_Config.txt Realm_Config.txt config.xml Je me ferai une joie de répondre à vos questions et d'avoir des suggestions pour améliorer le contenu. Encore une fois, le tutoriel n'est pas de moi, j'ai seulement corrigé quelques parties causants des problèmes à beaucoup et apporter des précisions en accord avec @Calypso. Bonne chance à tous ! --- Download
  14. Bonjour à tous, Je vous propose un tutoriel pour faire ses propres effect damage et miss. Qu'est ce que c'est ? Aperçu I°/Les fichiers. Rendez vous dans le fichier effect => affect => damagevalue. Les damage (0 à 9) + le miss correspondent aux chiffres Rouges quand un mob ou quelqu'un nous tape. Les target (0 à 9) + le miss correspondent aux chiffres Jaunes quand on tape quelqu'un ou un mob. II°/Sur photoshop. Pour mon exemple j'ai pris le N°1 comme ci dessous : exemple Ensuite je le recouvre de noir : Exemple Ensuite je mets mon chiffre 1 (Avec une police au hasard) et je lui fais quelques style de calque. (Je ne suis pas graphiste donc j'ai fais simple) Comme ceci : Exemple Maintenant rendez vous sur le canal alpha : Canal alpha On le recouvre de noir /!\ Canal Alpha On retourne sur le RVB (en gros sur l'image normal) On sélectionne tout le calque. Exemple Ensuite faites Ctrl+c et retournez sur le canal alpha puis CTRL+V vous verrez que la forme de votre nombre ou lettre apparaîtra. Exemple Pour moi j’aplatis l'image et je sauvegarde en format DDS. En remplaçant l'ancien chiffre et voici le résultat après repack : Résultat Voilà en espérant que cela puisse être utile. Si il y a un soucis n'hésitez pas à me le faire parvenir par MP ou via la section A/Q/S. Source : Mei Cordialement,
  15. Bonsoir à tous, Après avoir fait des recherches sur le forum j'ai vu qu'aucun partage pour le global_chat a été fait, je décide donc de vous le partager pour les files 2014 vierges car sur les 2016 il est déjà implanté et fonctionnel mais ce serait un petit plus pour les personnes utilisant les 2014 pures. Pour ce partage vous aurez besoin que de vos sources serveur et ces 4 fichiers: config.cpp config.h input_main.cpp input_p2p.cpp 1. config.cpp et config.h 2. input_main.cpp 3. input_p2p.cpp Vous pouvez recompiler vos sources et votre global_chat sera implanté et fonctionnel sans oublier de mettre global_chat: 1 dans les config de vos files. Link
  16. Salut à tous, Je viens vous partager les résultats de mes test après quelques mois. Moins de déconnexion inexpliqué lié au game. Plus aucune erreur lié au séquence dans les syserrs Sources serveur & client allégés Comme vous pouvez le constater, la suppression de ce système est entièrement bénéfique puisque c'est un système incomplet provoquant des déconnexion en jeu sans aucune raison. Le système générait également cette erreur côté serveur: Par contre, si vous avez désactiver l'encryption des packets sur vos sources, ça nécessitera beaucoup d'autres modifications assez importantes. Si vous voulez faire une série de test vous avez la possibilité d'agir de 3 façons différentes : #ifndef SEQUENCE_SYSTEM_DISABLED avant chaque code lié aux séquences. Vous commentez le code Vous supprimez le code pour alléger votre sources (j'ai opté pour celle-ci) Commençons, Ouvrez input.cpp et cherchez : if (bHeader == HEADER_CG_PONG) sys_log(0, "PONG! %u %u", m_pPacketInfo->IsSequence(bHeader), *(BYTE *) (c_pData + iPacketLen - sizeof(BYTE))); Ici, le syslog lié au Header de la clé pong fait appel à la fonction IsSequence, on va modifier ça par : if (bHeader == HEADER_CG_PONG) sys_log(0, "PONG! %u", *(BYTE *) (c_pData + iPacketLen - sizeof(BYTE))); Juste en bas vous avez : if (m_pPacketInfo->IsSequence(bHeader)) { BYTE bSeq = lpDesc->GetSequence(); BYTE bSeqReceived = *(BYTE *) (c_pData + iPacketLen - sizeof(BYTE)); if (bSeq != bSeqReceived) { sys_err("SEQUENCE %x mismatch 0x%x != 0x%x header %u", get_pointer(lpDesc), bSeq, bSeqReceived, bHeader); LPCHARACTER ch = lpDesc->GetCharacter(); char buf[1024]; int offset, len; offset = snprintf(buf, sizeof(buf), "SEQUENCE_LOG [%s]-------------\n", ch ? ch->GetName() : "UNKNOWN"); if (offset < 0 || offset >= (int) sizeof(buf)) offset = sizeof(buf) - 1; for (size_t i = 0; i < lpDesc->m_seq_vector.size(); ++i) { len = snprintf(buf + offset, sizeof(buf) - offset, "\t[%03d : 0x%x]\n", lpDesc->m_seq_vector[i].hdr, lpDesc->m_seq_vector[i].seq); if (len < 0 || len >= (int) sizeof(buf) - offset) offset += (sizeof(buf) - offset) - 1; else offset += len; } snprintf(buf + offset, sizeof(buf) - offset, "\t[%03d : 0x%x]\n", bHeader, bSeq); sys_err("%s", buf); lpDesc->SetPhase(PHASE_CLOSE); return true; } else { lpDesc->push_seq(bHeader, bSeq); lpDesc->SetNextSequence(); //sys_err("SEQUENCE %x match %u next %u header %u", lpDesc, bSeq, lpDesc->GetSequence(), bHeader); } } Ajoutez le ifdef, commentez ou supprimez l'intégralité du code. Cherchez : CInputHandshake::CInputHandshake() { CPacketInfoCG * pkPacketInfo = M2_NEW CPacketInfoCG; pkPacketInfo->SetSequence(HEADER_CG_PONG, false); m_pMainPacketInfo = m_pPacketInfo; BindPacketInfo(pkPacketInfo); } On va supprimer l’envoi de la séquence de la clé pong ici comme ceci : CInputHandshake::CInputHandshake() { CPacketInfoCG * pkPacketInfo = M2_NEW CPacketInfoCG; // pkPacketInfo->SetSequence(HEADER_CG_PONG, false); m_pMainPacketInfo = m_pPacketInfo; BindPacketInfo(pkPacketInfo); } C'est tout pour l'input.cpp, ouvrons le fichier desc.cpp : #include "sequence.h" Ajoutez un commentaire à l'include. Ensuite cherchez et commentez le code : m_iCurrentSequence Cherchez et commentez le code: m_seq_vector.clear(); Cherchez à nouveau & commentez le code : m_seq_vector.clear(); Cherchez : BYTE DESC::GetSequence() { return gc_abSequence[m_iCurrentSequence]; } void DESC::SetNextSequence() { if (++m_iCurrentSequence == SEQUENCE_MAX_NUM) m_iCurrentSequence = 0; } Commentez l'intégralité. Pour finir, cherchez : void DESC::push_seq(BYTE hdr, BYTE seq) { if (m_seq_vector.size()>=20) { m_seq_vector.erase(m_seq_vector.begin()); } seq_t info = { hdr, seq }; m_seq_vector.push_back(info); } Commentez également l'intégralité de la fonction. Ouvrons le desc.h pour les déclarations. Cherchez : // sequence 버그 찾기용 데이타 struct seq_t { BYTE hdr; BYTE seq; }; typedef std::vector<seq_t> seq_vector_t; // sequence 버그 찾기용 데이타 Commentez le tout. Cherchez : BYTE GetSequence(); void SetNextSequence(); Commentez les 2 fonctions. Cherchez : int m_iCurrentSequence; Commentez la fonction. Cherchez : public: seq_vector_t m_seq_vector; void push_seq (BYTE hdr, BYTE seq); Commentez tout. Ouvrons le packet_info.cpp et cherchons : void CPacketInfo::Set(int header, int iSize, const char * c_pszName, bool bSeq) { if (m_pPacketMap.find(header) != m_pPacketMap.end()) return; TPacketElement * element = M2_NEW TPacketElement; element->iSize = iSize; element->stName.assign(c_pszName); element->iCalled = 0; element->dwLoad = 0; element->bSequencePacket = bSeq; if (element->bSequencePacket) element->iSize += sizeof(BYTE); m_pPacketMap.insert(std::map<int, TPacketElement *>::value_type(header, element)); } Modifiez par: void CPacketInfo::Set(int header, int iSize, const char * c_pszName) { if (m_pPacketMap.find(header) != m_pPacketMap.end()) return; TPacketElement * element = M2_NEW TPacketElement; element->iSize = iSize; element->stName.assign(c_pszName); element->iCalled = 0; element->dwLoad = 0; /* element->bSequencePacket = bSeq; if (element->bSequencePacket) element->iSize += sizeof(BYTE); */ m_pPacketMap.insert(std::map<int, TPacketElement *>::value_type(header, element)); } Cherchez : bool CPacketInfo::IsSequence(int header) { TPacketElement * pkElement = GetElement(header); return pkElement ? pkElement->bSequencePacket : false; } void CPacketInfo::SetSequence(int header, bool bSeq) { TPacketElement * pkElem = GetElement(header); if (pkElem) { if (bSeq) { if (!pkElem->bSequencePacket) pkElem->iSize++; } else { if (pkElem->bSequencePacket) pkElem->iSize--; } pkElem->bSequencePacket = bSeq; } } Commentez la totalité. Maintenant, on va modifier tout les packets de la fonction CPacketInfoCG::CPacketInfoCG(). L’envoi de séquence n'étant plus nécessaire, remplacez ceci : Set(HEADER_CG_GUILD_SYMBOL_UPLOAD, sizeof(TPacketCGGuildSymbolUpload), "SymbolUpload", false); Par : Set(HEADER_CG_GUILD_SYMBOL_UPLOAD, sizeof(TPacketCGGuildSymbolUpload), "SymbolUpload"); Faites ceci pour tous les header avec un true ou un false. Vous devriez avoir quelque comme comme ça: Ouvrons le packet_info.h et cherchons : typedef struct SPacketElement { int iSize; std::string stName; int iCalled; DWORD dwLoad; bool bSequencePacket; } TPacketElement; Modifiez par: typedef struct SPacketElement { int iSize; std::string stName; int iCalled; DWORD dwLoad; //bool bSequencePacket; } TPacketElement; Cherchez : void Set(int header, int size, const char * c_pszName, bool bSeq=false); Modifiez par : void Set(int header, int size, const char * c_pszName); Cherchez et commentez le code : bool IsSequence(int header); void SetSequence(int header, bool bSeq); Pour conclure le tutoriel, ouvrons input_udp.cpp et cherchons : Set(1, sizeof(ServerStateChecker_RequestPacket), "ServerStateRequest", false); Modifiez par : Set(1, sizeof(ServerStateChecker_RequestPacket), "ServerStateRequest"); Enfin, supprimez les 2 fichiers séquence de vos sources et n'oubliez pas de les retirer du makefile. Pour la partie client je ne vais pas faire de tutoriel étant donné que ce fichier n'est jamais modifié donc vous pouvez prendre le miens (j'en ai refait un pour vous vu que j'ai désactivé l’encryption des packets de mon côté). Lien de téléchargement : Cliquez-ici ! FE Source : Madara, merci de citer la source si vous souhaitez partager ce tutoriel ailleurs.
  17. Bonsoir à tous, Je vous propose d'ajouter l’événement Dead à vos quêtes, il pourrait vous être utile ! Pour cela vous devez disposer d'une machine (virtuelle de préférence) pour compiler et des sources du game (il s'agit pour ma part du 40250). Ouvrez quest.h, trouvez la ligne : QUEST_ITEM_INFORMER_EVENT, Et ajoutez ceci juste après : QUEST_DEAD_EVENT, Ouvrez questmanager.cpp, trouvez la ligne : m_mapEventName.insert(TEventNameMap::value_type("item_informer", QUEST_ITEM_INFORMER_EVENT)); Et ajoutez ceci juste après : m_mapEventName.insert(TEventNameMap::value_type("dead", QUEST_DEAD_EVENT)); Ajoutez la fonction ci-dessous dans ce même fichier : void CQuestManager::Dead(unsigned int pc) { PC * pPC; if ((pPC = GetPC(pc))) { if (!CheckQuestLoaded(pPC)) return; m_mapNPC[QUEST_NO_NPC].OnDead(*pPC); } else sys_err("QUEST no such pc id : %d", pc); } Ouvrez questmanager.h, trouvez la ligne : void Kill(unsigned int pc, unsigned int npc); Et ajoutez ceci juste après : void Dead(unsigned int pc); Ouvrez questnpc.cpp et ajoutez la fonction : bool NPC::OnDead(PC& pc) { return HandleReceiveAllEvent(pc, QUEST_DEAD_EVENT); } Ouvrez questnpc.h, trouvez la ligne : bool OnKill(PC& pc); Et ajoutez ceci juste après : bool OnDead(PC& pc); Enfin, ouvrez char_battle.cpp et trouvez la ligne : void CHARACTER::Dead(LPCHARACTER pkKiller, bool bImmediateDead) Juste après l'ouverture de cette fonction ajoutez : quest::CQuestManager::instance().Dead(GetPlayerID()); Sauvez le tout, compilez et c'est terminé ! Vous pourrez maintenant utiliser l'événement dans vos scripts de quêtes : when dead begin syschat("You are dead.") end Pensez à bien refaire les indentations/tabulations sans quoi la compilation pourrait échouer. Source : metin2dev.org PS : Merci à @ASIKOO, notre IPS converter
  18. Bonsoir. La dernière mise à jour des fichiers clients de l'officiel a apporté quelques nouveautés. Notamment un système de localisation plus poussée du jeu. Désormais tous les packs de langues sont présents dans un seul et même client, ce qui va probablement nous amener à posséder un client unique dans le futur et ce qui crédibilise la théorie d'un serveur international complétement multilingue, voire à une fusion complète des serveurs. Je ne vais pas vous partager tout le dernier client (nouveaux protos + correctifs du Zodiaque), car l'utilité y est réduite, mais je vais vous partager le texte des quêtes. Qui désormais se situe dans locale_fr/locale_quest.txt. Le formatage ressemble à ça : ID de la page [TAB] Texte de la ligne 1[ENTER]Texte de la ligne 2. Je n'ai pas trop remarqué de soucis à part peut-être la ligne 1 qui possède l'ID et Vice-versa. Peut-être que ce partage peut vous sembler spécifique ou inutile, mais de mon côté il m'aurait été très utile lorsque je recodais les quêtes de l'officiel avec des textes qui ne le sont pas, ou qui sont traduits des brouillons coréens. Ça peut donc être utile à ceux souhaitent recoder ces quêtes où y mettre le texte approprié. J'invite par ailleurs n'importe qui de motivé à recréer les quêtes ou bien à coder le système qui fait lire le texte des quêtes depuis ce fichier. Le fichier (en Français) : MEGA Bonne soirée ! --- Download
  19. Bonjour, Aujourd'hui je vous propose les files 2016 de la Team-FE, aussi, repris par Saw, que j'ai modifié de mon côté. Changelog V3.0 (Team-FE) : -Système d'étole (Avec les 4 types d'étoles) -Système de costume d'armes -Système de costume de monture -Système de liaison d'âme -Système des 7è & 8è compétences -Système du pet officiel (Meley, petit singe, Bébé némère, Bébé razador, petite araignée) -Pierres anti-magie disponibles jusque +4 -Interface de création et de sélection de personnages officielle -4 inventaires disponibles -Ajout de nombreux mobs et items (Utilisation des item_proto et mob_proto officiels avec quelques modifications) -Level 255 débloqué mais fixé à 115 dans les CONFIG -Système des targets de couleur -Ajout de nombreux costumes et coiffures -Possibilité d'acheter des items à 0 yangs -Ajout de l'effet officiel quand on drop un item. -Ajout des armures 115. -Certaines interfaces ont été traduites. -FIX du lame enchanté -Mise en place de sécurité afin d'éviter certaines injections SQL -Les games et DB sont optimisés et ont la possibilité d'être stripper pour obtenir des poids de 5.000 Ko et 7.180Ko Liste des modifications apportées sur les files (Saw) : Serveur : drop des yang's sur les moobs réglé Réglage des fichiers CONFIG Amélioration du start.sh Ajout des cshybridcrypt dans packages Correction des data find count etc.... (0 syserr à l'ouverture) Débug de la give_basic_weapon + rangement du locale_list dans l'ordre A-Z Installation MySQL_query Client : Suppression de fichiers inutiles Optimisation des lib's (beaucoup moins lourdes) On obtient un client de 1go5 au lieu de 2go6 Débug des effets d'étoles Possibilité d'appuyer sur "ENTRER" pour choisir son personnage Fix de la texture du Theowahdan Ajout de la texture du Azräel qui était invisible Ajout de la liste des GM dans la liste d'amis Fix de la texture du Seon-Pyeong Réorganisation des Inventaires Débug effets/sons des 6èmes skill's Source serveur : achats d'item à 0 yang les monstres ne contenant pas de race le sysser DB de la monarch election l'erreur de la table des pet's lors de l'ouverture du serveur la suppression des personnages. Réglage de quelques warning de compilations. (Makefile dans l'ordre A-Z) Réglage du chat global et activations sur tous les channels. Réglage du shutdown: tics did not updated (checkpoint). Fix injection sql Fix ban depuis le messenger Fix ouverture db (merci à @Darknessmt2) Fix création de guilde/injection guilde Fix d'accumulation de bonus Fix core dungeon Activation fonction "pc.change_name" fix motion pet (sysser) fix header 100 (syserr) activation 6/7 fix accumulation des bonus des pets via l'échange. Fix du Bug Aura. Fix event kill qui est déclenché deux fois (merci à @A n t 0 k) Fix d'un problème qui empêcher de changer d'item lorsque l'inventaire est plains Fix du BeltInventory Fix bug PV négative lors d'un tp/connexion Source client : Amélioration de la structure des fichiers (plus besoin de spécifié les includes/libs dans les paramètres/beaucoup plus compréhensible) Accélération du défilement des quêtes (0 secondes) Modification des "[Niv. %d" en "Lv %d" Suppression italique sur MP avec un staffien. Passage en Python 2.7 Et maintenant, ce que j'apporte de mon côté aux files : Files : Armures / Armes Kyanites et Zodiaque Biologiste universelle Multi-sélection de shop (à remplir) Bonus 6 et 7 dans l'item-proto Client : Correctif du temps restant dans les magasins Effet Berserk Prix à 0 yangs = Gratuit 999.999.999 yangs échangeables. Lisibilité du nombre de yang en échange (Séparation par ".") Ne plus avoir à consulter 2 fois le même magasin (Blanc = non ouvert / Vert = Ouvert) Détruire un Item (Inventaire) Armures / Armes Kyanites et Zodiaque Afficher le niveau des équipements dans l'inventaire (+0 à +9) Mode graphique HD Source client : Informations sur les entités Anti MobPuller // Waithack Changer la couleur des drops selon le propriétaire (rouge quand l'item ne nous revient pas) Remettre le niveau en temps réel. Détruire un Item (Inventaire) Afficher le niveau des équipements dans l'inventaire (+0 à +9) Mode graphique HD Stop collision Source serveur : Chance de lier les pierres sur les armes ou armures de 30% Chance d'insérer un diamant de 50% bonus 6-7 sur les costumes désactiver Enlevez les potions à chaque up level Cape de bravoure (100% de réussite) Les compétences passent à M1 directement dès 17 points Informations sur les entités Empêcher le buff entre joueur/staffien Remettre le niveau en temps réel Détruire un Item (Inventaire) Protection contre le kick et le Sync Hack Log MP surveillance (MySQL) Log d'échange surveillance (MYSQL) Drop de groupe en instance Bloquer n'importe quel objet, sur n'importe quelle Map sans reboot ! Anti Wall Hack Montrer un item dans le chat sans loupe magique Crash lorsqu'un objet est acheté en même temps en magasin J'espère que ce sera utile pour certains, je vous mets de suite les liens de téléchargement. Quelques images Client : Cliquez ici pour télécharger Files : Cliquez ici pour télécharger MySQL : Cliquez ici pour télécharger Source serveur : Cliquez ici pour télécharger Source client : Cliquez ici pour télécharger VDI (Utilisateur : root / Mot de passe : admin) : Cliquez ici pour télécharger Mise à jour le 04.08.2018 à 13h Cordialement, Saejin --- Download
  20. Bonjour/soir à toutes et à tous. Beaucoup se sont certainement demandés à quoi sert exactement la colonne `skill_level` et même plus encore: comment est-ce qu'elle fonctionne ? Je crée ce sujet pour vous l'expliquer. C'est parti ! Un BLOB ? ¿Ma qué? Commençons par la base: Un BLOB qu'est-ce que c'est ? Ça signifie tout bêtement Binary Large OBject, c'est un type de donnée très maniable qui permet de stocker des données en brut (hexadécimal, pour être précis), et ce, en permettant de changer de taille de manière totalement libre. Il existe 4 types de BLOBs: tinyblob blob mediumblob longblob Contrairement aux données de texte (varchar, text, etc) , les BLOBs sont lus en tant que données binaires, et non de Texte, bien que l'éditeur hexadécimal permette d'afficher le dump (on verra ça plus tard) ASCII de ces données. Ok magique, mais comment on voit ces données ? Vous avez deux solutions: Exporter la donnée sous forme de fichier binaire pour la visualiser avec un éditeur Hexadécimal comme HexEdit Lire la donnée directement depuis l'outil de Navicat (ou autre logiciel de gestion s'il le permet) Dans notre cas, on va utiliser Navicat, c'est plus rapide, et plus pratique que d'exporter les données à chaque fois. Surtout qu'en exportant la donnée, on ne pourra pas Copier-Coller les modifications. Alors comment se fait-ce ? (il a dit fesse lol) Dans votre table `player`.`player`, il vous suffit de cliquer sur votre BLOB, puis sur le menu contextuel (la flèche ▼) à côté du bouton "Mémo" comme suit: Puis sélectionner "Hex" comme sur l'image. Une fois fait, une petite fenêtre va apparaître avec tout un tas de zéros incompréhensibles (alors que vous êtes plein de skills...). Et forcément, vous l'aurez compris, le but du tutoriel c'est que vous sortiez d'ici en sachant expliquer comment ça marche ! Bon maintenant qu'on a notre beau panneau Hex et nos données affichées (enfin, avec plein de zéros, "données" hein...), on va pouvoir comprendre comment ça marche; et pour ça je vous ai facilité la tâche, je me suis mis tous les skills en P. J'ai bien dit TOUS les skills, même ceux qui ne sont pas de ma classe ! (je vous préviens, on pourra pas utiliser aura sur une shaman) Du coup, voici à quoi ça ressemble maintenant: Ooook mais j'ai toujours rien compris. Par contre y'a pas que des zéros là ! Bien vu l'aveugle (dit-il en se parlant à lui-même) ! Bon du coup, si vous ne comprenez rien à l'Hexadécimal, je ne vais pas pouvoir vous aider plus que ça, je ne peux pas vous faire un cours d'Hexa donc si vous ne savez pas compter en Hexa... Vous pouvez prendre la porte, sauf si vous estimez comprendre les choses assez vite, vous pourrez continuer le tutoriel. Bon, du coup c'est bien beau tout ça mais ça ne nous fait pas plus comprendre comment ça marche... Eh bien à moi de vous expliquer ça. Un octet correspond à deux zéros: 00. Car en Hexa, on travaille sur 4 bits (base 16, donc forcément). Avoir deux zéros, ça nous donne un octet car 4 bits collés à 4 bits nous donnent 8 bits (quel génie fais-je), et donc 8 bits équivalent à un octet. Ce qu'il faut savoir, c'est que les 6 premiers octets (6 x 2 zéros = 12 premiers zéros) ne sont pas utilisés. D'ailleurs je n'ai pour l'instant pas encore trouvé à quoi ils peuvent bien servir. Si quelqu'un sait, je le rajouterai dans le tutoriel. Voilà, enfin un peu de lourd, on s'faisait chier avec ta théorie là ! Du calme, du calme, j'y viens ! Maintenant qu'on sait tout ça, on tombe sur notre premier code hexadécimal: 0328 0000 Pourquoi j'ai rajouté les 4 zéros ? Car ils concernent le premier skill. Comme c'est un condensé de données, il faut pouvoir différencier les autres skills donc on les sépare avec 2 octets de données chacun. Et à votre avis, on voit combien de fois ce code hexadécimal en tout sur les premières lignes ? 6 fois, comme pour 6 skills. Assez dingue non ? Je trouve aussi, la nature est bien faite, vous ne trouvez pas ? Du coup, voilà pour la séparation des données: En rouge, les 4 octets de séparation: Maintenant qu'on sait comment les skills sont séparés, il faut savoir les différencier ! Eh bien là encore une fois c'est pas plus compliqué, ils sont inscrits dans un ordre précis que vous connaissez peut-être déjà grâce à la SkillTable.txt: Ils sont dans l'ordre ! Les 03 28 que vous voyez correspondent tous à un skill en P (je viendrai plus tard sur pourquoi 03 et 28). Du coup maintenant on sait où sont nos skills, et là on a 3 lignes de skills, mais à quoi correspondent-ils ? En fait, ils sont comme je le disais, dans l'ordre décimal croissant de votre SkillTable, mais aussi dans l'ordre des classes. On aura donc à chaque ligne les sorts d'une classe différente, dans l'ordre qui suit: Guerrier Corps-à-Corps Guerrier Mental Ninja Assassin Ninja Archer Sura Armes Magiques Sura Magie Noire Shaman Dragon Shaman Soin Là on connaît l'ordre, donc sur notre screenshot suivant, on voit les séparations en rouge, ainsi que la ligne du Guerrier Corps-à-Corps en vert: La deuxième ligne commence à partir de l'offset 0x060 et se termine à l'offset 0x07F, la troisième ligne commence à partir de l'offset 0x0BA et se termine à l'offset 0x0D9. /!\ ATTENTION ! Si vous n'avez que 5 skills en G Mental, l'offset ne se terminera pas à 0x07F mais 0x079 /!\ Vient ensuite la séparation des classes de skills. Elles sont toutes séparées par 58 octets ! Pourquoi ? Je vous avoue que j'en ai aucune idée. Je planche encore dessus, je précise que je fais ce tutoriel à partir d'aucune source, seulement des données hexadécimales ! Notre séparation de "classes de skills" ressemble donc à ça: Du coup bingo, on connaît l'ordre des classes, on sait par combien d'octets ils sont séparés, on peut maintenant passer à la suite ! Yes ! J'attends toujours que tu m'expliques pourquoi 03 28, c'est le nouveau numéro de la sécu ? Qu'est-ce qu'on se marre sur ce topic, j'vous jure ! En fait c'est très simple vous allez vite comprendre. 03 et 28 sont séparés pour une bonne raison en fait. Les deux premiers octets correspondent au rang du skill, c'est à dire son grade. Il y a 4 possibilités: 00 = Niveau 1 à 19 01 = M1 à M10 02 = G1 à G10 03 = P "Ah ouais malynx le lynx ! C'est pour ça que tous tes sorts P possèdent 03 en premier !", me direz-vous. Eh bien oui, c'est tout con. Pour ce qui est du deuxième octet, ça correspond en fait au niveau réel du skill. Le niveau qu'on a quand on tape la fameuse commande /setsk 1 40 pour avoir Triple Lacération en P. Bah en fait, si vous regardez bien, convertissez 40 en Hexadécimal et on obtient... 28 ! D'où le 28, et pas niveau 28 ! Car si je le veux en M8, je convertis 27 en Hexadécimal... 1D ! /!\ ATTENTION ENCORE ! Si vous mettez 03 pour P et niveau 27 (1D en Hexa), le serveur ne comprendra pas les données que vous avez, et vous répondra que vous n'avez pas appris le skill /!\ Du coup, si je veux un skill en M8, je vais devoir faire 01 pour rang M, et 1D pour le niveau 27 (M8). On n'aura donc pas 03 28 Mais 01 1D à la place :). É-NO-RME ! Attends attends... Et pour les autres skills ? Commandement, Combo, etc... ?? Je savais que vous alliez poser la question (évidemment c'est moi qui les écris) ! Eh bien en fait ils se situent tout en bas ! Comme dit plus haut, on a Shaman Soin en dernier. On ajoute notre séparateur de 58 octets et on a notre premier skill à 0x2D7: Le commandement ! Pourquoi commandement ? Parce que c'est le prochain skill après le dernier skill du Shaman Soin ! 111 SHAMAN Attaque renforcée 121 SUPPORT Commandement Faites attention par contre, certains skills comme la pêche ne possèdent pas de niveau, de ce fait il y aura des zéro à la place ! 121 SUPPORT Commandement Aptitude à mener vos partenaires. 122 SUPPORT Combo Augmenter une attaque combo. 123 SUPPORT Pêche Capacité à pêcher du poisson. 124 SUPPORT Extraction Capacité à extraire du minerai. 125 SUPPORT Création d objet Créer un objet. 126 SUPPORT Langue Shinsoo Vous comprenez le Shinsoo. 127 SUPPORT Langue Chunjo Vous comprenez le Chunjo. 128 SUPPORT Langue Jinno Vous comprenez le Jinno. Les skills comme la Pêche ne peuvent pas avoir de niveau. Si on a tout en P, on aura donc: Commandement Combo (ici en P, même si c'est inutile d'avoir plus de 2) (Pêche, pas utilisable donc 0) Extraction Création d'Objet Langue Shinsoo Langue Chunjo Langue Jinno Polymorphie Équitation Appel Cheval (spacer) (spacer) (spacer) (spacer) (spacer) Combat Équestre Charge à Cheval Vague de Pouvoir Grêle de Flèches Et là vous avez tout... Ou presque ! On en fait quoi des skills 7 et 8 ? Eh bien je vais vous laisser mettre ça en pratique, qu'est-ce que vous en dites ? Comme ça si vous avez pigé le tutoriel, vous pouvez modifier vos skills depuis la BDD. Ça peut servir selon certains cas, tout comme ça peut ne pas servir. Mais au moins, vous savez comment ça fonctionne et surtout à quoi ça correspond ! Si vous avez tenu jusqu'ici, c'est déjà un bel effort (je vous en parle pas à quel point c'était long d'écrire tout ça), alors ne gâchez pas cet élan et profitez-en pour commencer le "mini TP". Pour la petite partie TP, je vais juste vous donner l'adresse de départ des skills 7 et 8, et à vous d'essayer ! Les premiers skills 7 et 8 se trouvent à l'offset 0x52F et se terminent à l'offset 0x5B9. Trouvez les skills correspondants et rajoutez vos propres skills 7 et 8 ! Voilà, c'est la fin de ce (très) long tutoriel, j'espère que vous aurez compris tout ça, parce que ça fait beaucoup à avaler, comme dans un Bukkake. Mais l'important, c'est de piger des trucs que les gens ne comprennent pas ! Sur ce... À vous de jouer ! Cordialement, Kameyu
  21. Plop, Je vous partage l'item shop in game de shang dans sa dernière version v1.3.1 fonctionne parfaitement avec quelques petites retouches sur les files FE 2016 Dl : ICI FE Pour les problèmes et les bugs c'est dans l'A/Q/S Source : Shang
  22. Plop, J'ai vue que certains avait des problèmes avec l'ajout d'effets sur leurs costumes, je vous ai fait un petit tuto rapido :p, bien-sur ce n'est pas la méthode la plus optimisé etc etc mais elle marche. Alors on a besoin des fichiers suivant : InstanceBase.h InstanceBase.cpp playersettingmodule.py Ouvrez votre instanceBase.h (qui est dans vos sources client) Cherchez: EFFECT_BODYARMOR_SPECIAL2, En dessous mettez: EFFECT_BODYCOSTUME, C'est le nom de variable qui va nous servir a attribué un effet a nos costumes !! une variable = un effet !! !! Maintenant mettez votre souris sur la variable EFFECT_BODYCOSTUME vous allez voir un petit message du style: enum CInstanceBase::<unnamed>::EFFECT_BODYCOSTUME =25 retenez bien ce nombre on en aura besoin plus tard !! Enregistrez instanceBase.h on en a plus besoin Ouvrez votre instanceBase.cpp(toujours dans vos sources client) Cherchez: if (pItem->GetSubType() == CItemData::ARMOR_BODY) { m_armorRefineEffect = EFFECT_REFINED+EFFECT_BODYARMOR_REFINED7+refine-7; __AttachEffect(m_armorRefineEffect); } break; Apres le break; revenez a la ligne et collez ce code: case CItemData::ITEM_TYPE_COSTUME: if (pItem->GetSubType() == CItemData::COSTUME_BODY) { DWORD vnum = pItem->GetIndex(); if (41517 == vnum) { __AttachEffect(EFFECT_REFINED + EFFECT_BODYCOSTUME); } } break; De sorte a ce que sa ressemble à ça: if (pItem->GetSubType() == CItemData::ARMOR_BODY) { m_armorRefineEffect = EFFECT_REFINED+EFFECT_BODYARMOR_REFINED7+refine-7; __AttachEffect(m_armorRefineEffect); } break; case CItemData::ITEM_TYPE_COSTUME: if (pItem->GetSubType() == CItemData::COSTUME_BODY) { DWORD vnum = pItem->GetIndex(); if (41517 == vnum) { __AttachEffect(EFFECT_REFINED + EFFECT_BODYCOSTUME); } } break; if (41517 == vnum) veut dire si l'id est égal a 41517(Costume à crocs+) alors on lui attribue un effet ici sa sera effect_refined + notre effet Enregistrez instanceBase.cpp et vous pouvez compilé vos sources client Ensuite on va ouvrir playersettingmodule.py (qui est dans votre dossier root) Cherchez: chrmgr.RegisterEffect(chrmgr.EFFECT_REFINED+20, "Bip01", "D:/ymir work/pc/common/effect/armor/armor-4-2-2.mse") En dessous on va coller le code suivant en changeant ""votre nombre"" et ""le chemin de votre effet"" par votre nombre et votre chemin bien-sur : chrmgr.RegisterEffect(chrmgr.EFFECT_REFINED + votre nombre, "Bip01", "d:/ymir work/le chemin de votre effet") votre nombre = le nombre qui c'est affiche au passage de la souris dans notre instanceBase.h le chemin de votre effet = le chemin de votre effet ^^^^^^^^ Ce code renseigne le chemin de l'effet a notre variable Enregistrer, repacker et c'est parti ! Noubliez pas de mettre votre nouveau lanceur ^^ Petite bonus, Pour rajouter plusieurs effet on devra crée plusieurs variable EXEMPLE : EFFECT_BODYCOSTUME, EFFECT_BODYCOSTUME_1, EFFECT_BODYCOSTUME_2, EFFECT_BODYCOSTUME_N, Une variable = un effet Pour rajouter un effet a plusieurs costume EXEMPLE: if (41517 == vnum || 41518 == vnum || 41519 == vnum) { __AttachEffect(EFFECT_REFINED + EFFECT_BODYCOSTUME); } Ou si vous voulez ajouter l'effet a 10 costumes qui se suivent (id parlant) on pourra faire EXEMPLE: if (41517 <= vnum && vnum <= 41530) { __AttachEffect(EFFECT_REFINED + EFFECT_BODYCOSTUME); } Ici tout les costumes de l'id 41517 à 41530 auront l'effet (EFFECT_REFINED + EFFECT_BODYCOSTUME) Pour rajouter plusieurs effet sur un costume EXEMPLE: if (41517 == vnum) { __AttachEffect(EFFECT_REFINED + EFFECT_BODYCOSTUME + EFFECT_BODYCOSTUME_1 + EFFECT_BODYCOSTUME_N); } Et enfin pour finir ajouté plusieurs effet different sur plusieurs costumes different on va répéter notre condition if EXEMPLE: if (41517 == vnum) { __AttachEffect(EFFECT_REFINED + EFFECT_BODYCOSTUME); } if (41555 == vnum) { __AttachEffect(EFFECT_REFINED + EFFECT_BODYCOSTUME_N); } if (41590 == vnum) { __AttachEffect(EFFECT_REFINED + EFFECT_BODYCOSTUME_N); } Voilà maintenant vous pouvez ajouter vos effets sur vos costumes ps : c'est mon premier tuto si y'a des choses mal expliquer ou a modifier dites le moi ps2 : je suis loin d'être expert c++ donc si on peu optimiser ce code dites le moi aussi
  23. Bonsoir à tous, Suite à une récente demande d'@Abass. J'ai décidé de vous partager ce petit tweak, qui franchement ne casse pas trois pattes à un canard mais qui mine de rien est assez sympathique. Et puis, qui dit inutile dit indispensable. Qu'est-ce que ça fait au juste ? Tout simplement, dès lors qu'un objet dans un magasin vaudra 0 Yang, le jeu vous dira "Prix : Gratuit" à la place de "Prix : 0 Yangs". C'est tout ! Sans plus attendre, commençons le tutoriel, ça va être très rapide. Ouvrez le fichier "uitooltip.py" qui se situe dans root puis changez la définition "AppendPrice(self, price)" avec la mienne, qui contient la condition en plus. def AppendPrice(self, price): self.AppendSpace(5) if price == 0: self.AppendTextLine(localeInfo.TOOLTIP_BUYPRICE_FREE) else: self.AppendTextLine(localeInfo.TOOLTIP_BUYPRICE % (localeInfo.NumberToMoneyString(price)), self.GetPriceColor(price)) Enregistrez, fermez, repackez root puis rendez vous dans votre fichier locale_fr, ouvrez "locale_game.txt" et rajoutez cette ligne : TOOLTIP_BUYPRICE_FREE Prix : Gratuit En dessous de celle-ci (pour la propreté) : TOOLTIP_BUYPRICE Prix : %s Enregistrez, fermez, repackez locale_fr puis rendez-vous en jeu. Voici le résultat : Voilà, c'est tout pour ce mini-guide ! Passez une bonne journée
  24. Bonjour ! Je vous propose un tutoriel pour installer le système de ticket partagé par Shang, que j'ai corrigé car il contenait d'assez gros problèmes côté python, permettant des injections SQL. I - Source client : Dans le fichier Packet.h : Chercher : QUEST_INPUT_STRING_MAX_NUM = 64, Ajouter juste après : #ifdef ENABLE_TICKET_SYSTEM QUEST_INPUT_STRING_LONG_MAX_NUM = 512, #endif Chercher ensuite : typedef struct command_quest_input_string { BYTE bHeader; char szString[QUEST_INPUT_STRING_MAX_NUM+1]; } TPacketCGQuestInputString; Ajouter : #ifdef ENABLE_TICKET_SYSTEM typedef struct command_quest_input_long_string { BYTE bHeader; char szString[QUEST_INPUT_STRING_LONG_MAX_NUM]; } TPacketCGQuestInputLongString; #endif Pour en finir avec ce fichier, chercher : #ifdef __AUCTION__ HEADER_CG_AUCTION_CMD = 205, #endif Puis ajouter : #ifdef ENABLE_TICKET_SYSTEM HEADER_CG_QUEST_INPUT_LONG_STRING = 214, #endif Vérifiez qu'aucun de vos packets n'utilise le 214, sinon changez en conséquence, et vous ferez de même dans les sources serveur. Dans le fichier PythonNetworkStream.h : Chercher : bool SendQuestInputStringPacket(const char * c_szString); Ajouter : #ifdef ENABLE_TICKET_SYSTEM bool SendQuestInputStringLongPacket(const char * c_pszString); #endif Dans le fichier PythonNetworkStreamModule.cpp : Chercher : PyObject* netSendQuestInputStringPacket(PyObject* poSelf, PyObject* poArgs) { char * szString; if (!PyTuple_GetString(poArgs, 0, &szString)) return Py_BuildException(); CPythonNetworkStream& rns=CPythonNetworkStream::Instance(); rns.SendQuestInputStringPacket(szString); return Py_BuildNone(); } Ajouter : #ifdef ENABLE_TICKET_SYSTEM PyObject * netSendQuestInputLongStringPacket(PyObject * poSelf, PyObject * poArgs) { char * szString; if (!PyTuple_GetString(poArgs, 0, &szString)) return Py_BuildException(); CPythonNetworkStream & rns = CPythonNetworkStream::Instance(); rns.SendQuestInputStringLongPacket(szString); return Py_BuildNone(); } #endif Chercher ensuite : { "SendQuestInputStringPacket", netSendQuestInputStringPacket, METH_VARARGS }, Ajouter : #ifdef ENABLE_TICKET_SYSTEM { "SendQuestInputLongStringPacket", netSendQuestInputLongStringPacket, METH_VARARGS }, #endif Dans le fichier PythonNetworkSteamPhaseGame.cpp : Chercher : bool CPythonNetworkStream::SendQuestInputStringPacket(const char * c_szString) { TPacketCGQuestInputString Packet; Packet.bHeader = HEADER_CG_QUEST_INPUT_STRING; strncpy(Packet.szString, c_szString, QUEST_INPUT_STRING_MAX_NUM); if (!Send(sizeof(Packet), &Packet)) { Tracen("SendQuestInputStringPacket Error"); return false; } return SendSequence(); } Ajouter : #ifdef ENABLE_TICKET_SYSTEM bool CPythonNetworkStream::SendQuestInputStringLongPacket(const char * c_pszString) { TPacketCGQuestInputLongString Packet; Packet.bHeader = HEADER_CG_QUEST_INPUT_LONG_STRING; strncpy(Packet.szString, c_pszString, QUEST_INPUT_STRING_LONG_MAX_NUM); if (!Send(sizeof(Packet), &Packet)) { Tracen("SendQuestInputStringLongPacket Error"); return false; } return SendSequence(); } #endif Dans le fichier PythonApplicationModule.cpp : Chercher : #ifdef ENABLE_COSTUME_SYSTEM PyModule_AddIntConstant(poModule, "ENABLE_COSTUME_SYSTEM", 1); #else PyModule_AddIntConstant(poModule, "ENABLE_COSTUME_SYSTEM", 0); #endif Ajouter : #ifdef ENABLE_TICKET_SYSTEM PyModule_AddIntConstant(poModule, "ENABLE_TICKET_SYSTEM", 1); #else PyModule_AddIntConstant(poModule, "ENABLE_TICKET_SYSTEM", 0); #endif Dans le fichier Locale_inc.h : Ajouter : #define ENABLE_TICKET_SYSTEM Vous pouvez maintenant lancer la compilation de vos sources client le temps qu'on s'occupe des sources serveur ! II - Source serveur : Dans le fichier service.h : Ajouter : #define ENABLE_TICKET_SYSTEM Dans le fichier input.h : Chercher : void QuestInputString(LPCHARACTER ch, const void * pvData); Ajouter : #ifdef ENABLE_TICKET_SYSTEM void QuestInputLongString(LPCHARACTER ch, const void * c_pvData); #endif Dans le fichier input_main.cpp : Chercher : void CInputMain::QuestInputString(LPCHARACTER ch, const void* c_pData) { TPacketCGQuestInputString * p = (TPacketCGQuestInputString*) c_pData; char msg[65]; strlcpy(msg, p->msg, sizeof(msg)); sys_log(0, "QUEST InputString pid %u msg %s", ch->GetPlayerID(), msg); quest::CQuestManager::Instance().Input(ch->GetPlayerID(), msg); } Ajouter : #ifdef ENABLE_TICKET_SYSTEM void CInputMain::QuestInputLongString(LPCHARACTER ch, const void * c_pvData) { const TPacketCGQuestInputLongString * p = reinterpret_cast<const TPacketCGQuestInputLongString*>(c_pvData); sys_log(0, "QUEST InputLongString pid %u msg %s", ch->GetPlayerID(), p->szMsg); quest::CQuestManager::Instance().Input(ch->GetPlayerID(), p->szMsg); } #endif Chercher : case HEADER_CG_QUEST_INPUT_STRING: QuestInputString(ch, c_pData); break; Ajouter : #ifdef ENABLE_TICKET_SYSTEM case HEADER_CG_QUEST_INPUT_LONG_STRING: QuestInputLongString(ch, c_pData); break; #endif Dans le fichier packet.h : Chercher : typedef struct command_quest_input_string { BYTE header; char msg[64+1]; } TPacketCGQuestInputString; Ajouter : #ifdef ENABLE_TICKET_SYSTEM typedef struct command_quest_input_long_string { BYTE header; char szMsg[512]; } TPacketCGQuestInputLongString; #endif Chercher : HEADER_CG_XTRAP_ACK = 204, Ajouter : #ifdef ENABLE_TICKET_SYSTEM HEADER_CG_QUEST_INPUT_LONG_STRING = 214, #endif -->Penser à modifier le packet si vous l'avez fait dans les sources client. Dans le fichier packet_info.cpp : Chercher : Set(HEADER_CG_STATE_CHECKER, sizeof(BYTE), "ServerStateCheck", false); Ajouter : #ifdef ENABLE_TICKET_SYSTEM Set(HEADER_CG_QUEST_INPUT_LONG_STRING, sizeof(TPacketCGQuestInputLongString), "QuestInputLongString", true); #endif Dans le fichier questlua_global.cpp : Si, et seulement si vous n'avez pas mysql_direct_query, vous suivez cette étape, sinon, vous pouvez dores et déjà compiler vos sources. Chercher : void RegisterGlobalFunctionTable(lua_State* L) Ajouter au dessus : #ifdef _MSC_VER #define INFINITY (DBL_MAX+DBL_MAX) #define NAN (INFINITY-INFINITY) #endif int _mysql_direct_query(lua_State* L) { // char szQuery[1024]; if (!lua_isstring(L, 1)) return 0; // strncpy(szQuery, lua_tostring(L, 1), sizeof(szQuery)); int i=0, m=1; MYSQL_ROW row; MYSQL_FIELD * field; MYSQL_RES * result; // SQLMsg * pMsg = DBManager::instance().DirectQuery(szQuery); std::auto_ptr<SQLMsg> pMsg(DBManager::instance().DirectQuery(lua_tostring(L, 1))); if (pMsg.get()) { // ret1 (number of affected rows) lua_pushnumber(L, pMsg->Get()->uiAffectedRows); //-1 if error such as duplicate occurs (-2147483648 via lua) // if wrong syntax error occurs (4294967295 via lua) // ret2 (table of affected rows) lua_newtable(L); if ((result = pMsg->Get()->pSQLResult) && !(pMsg->Get()->uiAffectedRows == 0 || pMsg->Get()->uiAffectedRows == (uint32_t)-1)) { // LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr(); // ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Retrieved %u fields\n", __FUNCTION__, mysql_num_fields(result)); // ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Retrieved %u rows\n", __FUNCTION__, mysql_num_rows(result)); // ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Affected %u rows\n", __FUNCTION__, pMsg->Get()->uiAffectedRows); // ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Num %u rows\n", __FUNCTION__, pMsg->Get()->uiNumRows); while((row = mysql_fetch_row(result))) { lua_pushnumber(L, m); lua_newtable(L); while((field = mysql_fetch_field(result))) { lua_pushstring(L, field->name); if (!(field->flags & NOT_NULL_FLAG) && (row[i]==NULL)) { // lua_pushstring(L, "NULL"); lua_pushnil(L); } else if (IS_NUM(field->type)) { double val = NAN; lua_pushnumber(L, (sscanf(row[i],"%lf",&val)==1)?val:NAN); } else if (field->type == MYSQL_TYPE_BLOB) { lua_newtable(L); for (DWORD iBlob=0; iBlob < field->max_length; iBlob++) { lua_pushnumber(L, row[i][iBlob]); lua_rawseti(L, -2, iBlob+1); } } else lua_pushstring(L, row[i]); // LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr(); // ch->ChatPacket(CHAT_TYPE_INFO, "<%s> Retrieved %d flag %s for %s\n", __FUNCTION__, field->type, field->name, row[i]?row[i]:"NULL"); lua_rawset(L, -3); i++; } mysql_field_seek(result, 0); i=0; lua_rawset(L, -3); m++; } } } else {lua_pushnumber(L, 0); lua_newtable(L);} // delete pMsg; return 2; } Chercher ensuite : { NULL, NULL } Ajouter au dessus : { "mysql_direct_query", _mysql_direct_query }, Vous pouvez maintenant lancer la compilation de vos sources serveur, le temps qu'on s'occupe des fichiers serveur ! III - Serveur : Vous allez dans cette partie, ajouter ceci dans votre quest_functions : ticket.answer_ticket ticket.create_ticket ticket.load_answers ticket.load_permisions ticket.load_tickets ticket.add_member ticket.update_permisions ticket.delete_member ticket.has_permision ticket.add_viewer ticket.delete_viewer Ajouter dans questlib : dofile(get_locale_base_path().."/quest/ticket_lib.so") Ajouter ensuite la lib disponible en pièce jointe, la quête et le dossiers "tickets" (Pièce jointe : QUEST.rar). IV - MySQL : je vous invite pour cette partie à lancer ces queries : SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for tickets -- ---------------------------- DROP TABLE IF EXISTS `tickets`; CREATE TABLE `tickets` ( `order_id` int(11) NOT NULL AUTO_INCREMENT, `id` varchar(8) NOT NULL, `title` varchar(60) NOT NULL, `priority` varchar(150) NOT NULL, `date` datetime NOT NULL, `status` tinyint(1) NOT NULL, `creator` varchar(16) NOT NULL, `msg` varchar(500) NOT NULL, `category` int(2) NOT NULL, PRIMARY KEY (`order_id`) ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for tickets_answers -- ---------------------------- DROP TABLE IF EXISTS `tickets_answers`; CREATE TABLE `tickets_answers` ( `order_id` int(11) NOT NULL AUTO_INCREMENT, `id` varchar(8) NOT NULL, `creator` varchar(16) NOT NULL, `date` datetime NOT NULL, `msg` varchar(500) NOT NULL, PRIMARY KEY (`order_id`) ) ENGINE=MyISAM AUTO_INCREMENT=14 DEFAULT CHARSET=latin1; SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for tickets_permisions -- ---------------------------- DROP TABLE IF EXISTS `tickets_permisions`; CREATE TABLE `tickets_permisions` ( `pid` int(50) NOT NULL, `answer_permision` tinyint(1) NOT NULL, `delete_permision` tinyint(1) NOT NULL, `add_permision` tinyint(1) NOT NULL, PRIMARY KEY (`pid`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; V - Client : Dans votre locale_game.txt, ajouter : TICKET_PRIORITY_HIGH Élevée TICKET_PRIORITY_MEDIUM Normale TICKET_PRIORITY_LOW Faible TICKET_STATE_ON_HOLD En cours TICKET_STATE_CLOSED Fermé TICKET_STATE_SOLVED Résolu TICKET_ADD_NAME Nom: TICKET_ADD_TITLE Ajouter un membre TICKET_ADD_ACCEPT Accepter TICKET_SEND Envoyer TICKET_TITLE Asylum : Tickets TICKET_MY_TICKETS Mes Tickets TICKET_ADMINISTRATION Administration TICKET_SORT_BY Ordre : TICKET_ID ID TICKET_TITLE_T Titre TICKET_PRIORITY Priorité TICKET_DATE Date TICKET_STATE Etat TICKET_CREATE_NEW Créer un nouveau ticket TICKET_SEARCH Rechercher par ID: TICKET_SEARCH_BUTTON Rechercher TICKET_NAME Nom TICKET_ANSWER Répondre TICKET_DELETE Supprimer TICKET_CHANGE_PERMISIONS Modifier les permissions TICKET_ADD_MEMBER Ajouter un membre TICKET_REFRESH Actualiser TICKET_CATEGORY Categorie TICKET_CATEGORY_0 Tous Dans votre uiquest.py : Chercher : def Destroy(self): self.ClearDictionary() if self.OnCloseEvent: self.OnCloseEvent() self.OnCloseEvent = None # QUEST_INPUT if self.needInputString: if self.editLine: text = self.editLine.GetText() net.SendQuestInputStringPacket(text) # END_OF_QUEST_INPUT self.imgTitle = None self.images = None self.eventCurtain = None self.board = None Remplacer par : def Destroy(self): self.ClearDictionary() if self.OnCloseEvent: self.OnCloseEvent() self.OnCloseEvent = None # QUEST_INPUT if self.needInputString: if self.editLine: text = self.editLine.GetText() if app.ENABLE_TICKET_SYSTEM: if (len(text) > 64): net.SendQuestInputLongStringPacket(text) else: net.SendQuestInputStringPacket(text) else: net.SendQuestInputStringPacket(text) # END_OF_QUEST_INPUT self.imgTitle = None self.images = None self.eventCurtain = None self.board = None Dans le fichier interfaceModule.py : Ajouter : if app.ENABLE_TICKET_SYSTEM: import uiTicket Chercher : self.dlgShop = uiShop.ShopDialog() self.dlgShop.LoadDialog() self.dlgShop.Hide() Ajouter : if app.ENABLE_TICKET_SYSTEM: self.wndTicket = uiTicket.TicketWindow() self.wndTicket.Hide() Chercher : if self.dlgShop: self.dlgShop.Destroy() Ajouter : if app.ENABLE_TICKET_SYSTEM: if self.wndTicket: self.wndTicket.Destroy() Chercher : del self.wndItemSelect Ajouter : if app.ENABLE_TICKET_SYSTEM: del self.wndTicket Dans le fichier costinfo.py : Ajouter : if app.ENABLE_TICKET_SYSTEM: Tickets = { 'QID' : 0, 'QCMD' : '', 'MY_TICKETS' : [], 'GLOBAL_TICKETS' : [], 'ANSWERS' : {}, 'PERMISIONS' : [] } CApiSetHide = 0 Dans le fichier game.py : Chercher : "mall" : self.__InGameShop_Show, Ajouter : if app.ENABLE_TICKET_SYSTEM: "TICKETS" : self.ManagerTickets, Chercher : def OpenQuestWindow(self, skin, idx): Ajouter : if app.ENABLE_TICKET_SYSTEM: if constInfo.CApiSetHide == 1: net.SendQuestInputStringPacket(str(constInfo.SendString)) constInfo.CApiSetHide = 0 return Chercher : def __InGameShop_Show(self, url): if constInfo.IN_GAME_SHOP_ENABLE: self.interface.OpenWebWindow(url) Ajouter : if app.ENABLE_TICKET_SYSTEM: def ManagerTickets(self, cmd): cmd = cmd.split('#') if cmd[0] == 'QID': constInfo.Tickets['QID'] = int(cmd[1]) elif cmd[0] == 'INPUT': constInfo.INPUT_IGNORE = int(cmd[1]) elif cmd[0] == 'SEND': net.SendQuestInputLongStringPacket(str(constInfo.Tickets['QCMD'])) constInfo.Tickets['QCMD'] = '' elif cmd[0] == 'CLEAR_CONTENT': constInfo.Tickets['MY_TICKETS'] = [] constInfo.Tickets['GLOBAL_TICKETS'] = [] elif cmd[0] == 'CLEAR_PERMISIONS': constInfo.Tickets['PERMISIONS'] = [] elif cmd[0] == 'SET_TICKET': date = cmd[4].split('[_]') constInfo.Tickets['GLOBAL_TICKETS'].append([cmd[1], cmd[2].replace('[_]', ' '), int(cmd[3]), date[0], date[1], int(cmd[5]), cmd[6], cmd[7].replace('[_]', ' '), int(cmd[8])]) if cmd[6] == player.GetName(): constInfo.Tickets['MY_TICKETS'].append([cmd[1], cmd[2].replace('[_]', ' '), int(cmd[3]), date[0], date[1], int(cmd[5]), cmd[6], cmd[7].replace('[_]', ' '), int(cmd[8])]) elif cmd[0] == 'CREATE_ANSWER': constInfo.Tickets['ANSWERS'][cmd[1]] = [] elif cmd[0] == 'SET_ANSWER': date = cmd[3].split('[_]') constInfo.Tickets['ANSWERS'][cmd[1]].append([cmd[2], date[0], date[1], cmd[4].replace('[_]', ' ')]) elif cmd[0] == 'SET_PERMISION': constInfo.Tickets['PERMISIONS'].append([cmd[1], int(cmd[2]), int(cmd[3]), int(cmd[4])]) elif cmd[0] == 'OPEN': self.interface.wndTicket.Open(int(cmd[1])) elif cmd[0] == 'REFRESH_CONTENT': self.interface.wndTicket.RefreshPage() Dans le fichier ui.py : Ajouter la class : if app.ENABLE_TICKET_SYSTEM: class CoolButton(Window): BACKGROUND_COLOR = grp.GenerateColor(0.0, 0.0, 0.0, 1.0) DARK_COLOR = grp.GenerateColor(0.4, 0.4, 0.4, 1.0) WHITE_COLOR = grp.GenerateColor(1.0, 1.0, 1.0, 0.3) HALF_WHITE_COLOR = grp.GenerateColor(1.0, 1.0, 1.0, 0.2) def __init__(self, layer = "UI"): Window.__init__(self, layer) self.eventFunc = None self.eventArgs = None self.ButtonText = None self.ToolTipText = None self.EdgeColor = None self.isOver = FALSE self.isSelected = FALSE self.width = 0 self.height = 0 def __del__(self): Window.__del__(self) self.eventFunc = None self.eventArgs = None def SetSize(self, width, height): Window.SetSize(self, width, height) self.width = width self.height = height def SetEvent(self, func, *args): self.eventFunc = func self.eventArgs = args def SetTextColor(self, color): if not self.ButtonText: return self.ButtonText.SetPackedFontColor(color) def SetEdgeColor(self, color): self.EdgeColor = color def SetText(self, text): if not self.ButtonText: textLine = TextLine() textLine.SetParent(self) textLine.SetPosition(self.GetWidth()/2, self.GetHeight()/2) textLine.SetVerticalAlignCenter() textLine.SetHorizontalAlignCenter() textLine.SetOutline() textLine.Show() self.ButtonText = textLine self.ButtonText.SetText(text) def SetToolTipText(self, text, x=0, y = -19): if not self.ToolTipText: toolTip=createToolTipWindowDict["TEXT"]() toolTip.SetParent(self) toolTip.SetSize(0, 0) toolTip.SetHorizontalAlignCenter() toolTip.SetOutline() toolTip.Hide() toolTip.SetPosition(x + self.GetWidth()/2, y) self.ToolTipText=toolTip self.ToolTipText.SetText(text) def ShowToolTip(self): if self.ToolTipText: self.ToolTipText.Show() def HideToolTip(self): if self.ToolTipText: self.ToolTipText.Hide() def SetTextPosition(self, width): self.ButtonText.SetPosition(width, self.GetHeight()/2) self.ButtonText.SetHorizontalAlignLeft() def Enable(self): wndMgr.Enable(self.hWnd) def Disable(self): wndMgr.Disable(self.hWnd) def OnMouseLeftButtonDown(self): self.isSelected = TRUE def OnMouseLeftButtonUp(self): self.isSelected = FALSE if self.eventFunc: apply(self.eventFunc, self.eventArgs) def OnUpdate(self): if self.IsIn(): self.isOver = TRUE self.ShowToolTip() else: self.isOver = FALSE self.HideToolTip() def OnRender(self): xRender, yRender = self.GetGlobalPosition() widthRender = self.width heightRender = self.height grp.SetColor(self.BACKGROUND_COLOR) grp.RenderBar(xRender, yRender, widthRender, heightRender) if self.EdgeColor: grp.SetColor(self.EdgeColor) else: grp.SetColor(self.DARK_COLOR) grp.RenderLine(xRender, yRender, widthRender, 0) grp.RenderLine(xRender, yRender, 0, heightRender) grp.RenderLine(xRender, yRender+heightRender, widthRender, 0) grp.RenderLine(xRender+widthRender, yRender, 0, heightRender) if self.isOver: grp.SetColor(self.HALF_WHITE_COLOR) grp.RenderBar(xRender + 2, yRender + 2, self.width - 3, heightRender - 3) if self.isSelected: grp.SetColor(self.WHITE_COLOR) grp.RenderBar(xRender + 2, yRender + 2, self.width - 3, heightRender - 3) Chercher (EditLine): self.eventKillFocus = None Ajouter : if app.ENABLE_TICKET_SYSTEM: self.CanClick = None Chercher : def SetTabEvent(self, event): self.eventTab = event Ajouter : if app.ENABLE_TICKET_SYSTEM: def CanEdit(self, flag): self.CanClick = flag Chercher : def OnMouseLeftButtonDown(self): En dessous de : if FALSE == self.IsIn(): return FALSE Ajouter : if app.ENABLE_TICKET_SYSTEM: if FALSE == self.CanClick: return Déplacer ensuite les fichiers fournis dans l'archive PACK.rar où il faut, et voilà ! C'est terminé. Vous pouvez maintenant ouvrir la fenêtre en utilisant la fonction : if app.ENABLE_TICKET_SYSTEM: def OpenTicketWindow(self): import event constInfo.Tickets['QCMD'] = 'OPEN#' event.QuestButtonClick(constInfo.Tickets['QID']) return Rendu (je vous renvoie sur la chaine de l'auteur) : A ploush ! TÉLÉCHARGE MOI EN CLIQUANT ICI
  25. Bonjour à tous. Dans ce tutoriel, je vais vous expliquer comment avoir 6 inventaires dans l'entrepôt. Pour ce faire, vous aurez besoin de : -Source client -Source serveur -Votre client SOURCE CLIENT SOURCE SERVEUR CLIENT Voici un aperçu IG: Le tutoriel est à présent terminé, j'espère qu'il vous sera utile. Cordialement. Source: Freakgamers