-
Compteur de contenus
3211 -
Inscription
-
Dernière visite
jamais -
Jours gagnés
9
Type de contenu
Profils
Forums
Boutique
Calendrier
Tout ce qui a été posté par Metin2 Dev
-
Python - Prix : Gratuit quand un Objet est Gratuit
Metin2 Dev a répondu à un(e) sujet de Gurgarath dans Programmation & Développement / Codes
Good jobs! sa éviteras de voir des 0 partout -
Merci
-
C++ - Support / Ticket in Game
Metin2 Dev a posté un sujet dans Programmation & Développement / Codes
Centre de Téléchargement Télécharger ( Interne ) 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à /uploads/emoticons/biggrin.png ! 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- 3 réponses
-
- 22
-
-
-
Merci pour ce partage
-
Salut ! Je vais voir pour le télécharger, voir les potentiels bugs, les corriger si j'en suis capable (je suis pas non plus une experte), et vous publiez un .vdi, etc. Ça à l'air d'être à la mode sur ce forum...
-
C++ - 6 Pages d'Inventaires dans l'Entrepôt
Metin2 Dev a répondu à un(e) sujet de Theboost dans Programmation & Développement / Codes
C'est vrai, mais à ta place, j'aurais mis le bit et le nibble à côté, ils ne se traitent pas de la même façon et n'ont pas de "type de variable" prédéfini comme les autres. Pour en revenir au sujet, je l'avais déjà installé, et effectivement, il y a quelques modifications à faire pour le rendre entièrement fonctionnel. Si tu veux mettre cinq pages, il faudra aussi changer des choses au niveau des sources comme les tailles dans l'input_db etc, par exemple. Il faudra en plus adapter la partie Python. Et ne pas faire seulement la partie Python. -
Créer un serveur privé Metin2 avec Hamachi
Metin2 Dev a répondu à un(e) sujet de Calypso dans Création & Gestion de Serveur
-
Merci pour le partage
-
Plop, merci du partage en revanche il y a quelques points à voir : Certains ne sont pas tous pro dans l'émulation (car c'est souvent rabâché, le mieux serait d'avoir un tutoriel d'installation de tes files ainsi qu'une explication sur leur fonctionnement) Me semble t-il il manque les tables SQL, si tes files possèdent de nouveaux systèmes, les tables nécessitent des modifications. Aucun serveur ne peut-être full debug car même le FR n'est pas full débug Étant donné que ce sont une majo des files Nirvana, qu'as-tu apporté comme modification dessus ? En dehors des traductions. Il faut qu'on sache quels codes ont été modifiés..
-
Si ça s'arrête à success HotBackup c'est que ton game est mort et dans ce cas il faut passer par les sources pour enlever ou modifier le timebomb
-
Bonsoir, Sur ta VM tapes : cd /usr chmod -R 755 metin2 Ensuite modifies tes fichiers CONFIG
-
Tu aurais des news de Metroid Prime 4 ? ça été vite fait discuté lors de l'e3 2k17 , j'aimerais vraiment savoir le potentiel du jeu ! Doom Eternal ? C'est un Doom fait comme le Doom 3 et du coup un gros flop ou à la méthode des dooms N64 et compagnie ? (la réadaptation des dooms en doom 3 c'était une horreur, le film y compris). Il n'y a que moi qui pense que Fallout c'est du vu et revu ? C'est pratiquement la même histoire de manière générale J'ai hâte de voir le côté Nintendo, voir ce qu'ils peuvent nous sortir d’intéressant D'ailleurs ils sont présents cette année à l'e3 ?
-
Bonjour ! Je vous avez dis dans ce tutoriel que j'allai vous parler des radios boutons, cela sera chose faite ! Avant de lire ce tutoriel sachez que je vais utiliser la structure que j'avais partagé avant, car c'est à mon avis la plus propre. Bien, commençons avec le côté interface : La définition d'un élément radio button ressemble à ceci : { "name" : "takuma_button", "type" : "radio_button", "x" : 1, "y" : 1, "text" : "test", "default_image" : ROOT_PATH + "Middle_Button_01.sub", "over_image" : ROOT_PATH + "Middle_Button_02.sub", "down_image" : ROOT_PATH + "Middle_Button_03.sub", }, On utilise les mêmes propriétés que pour un bouton normal à l'exception du type, au lieu de mettre button, vous mettez : radio_button. Si vous voulez des exemples, je vous invite à aller par exemple voir dans le systemoptiodialog.py. Bien, pour vous expliquer exactement comment marche les boutons, je vais vous proposer une démonstration, je pense qu'on apprend mieux quand on fait quelque chose de concret. je vous propose donc de créer trois boutons qui vont vous permettre de gérer les ombres dans le jeu, voici le rendu final : Visible en bas de votre page "Options systèmes". Vous aurez besoin de deux notions en C++ que je vais vous expliquer brièvement car ce n'est pas le but du tutoriel : systemSetting.getShadowLevel() : Fonction qui permet de récupérer le niveau de qualité des ombres (varie entre 0 et 5 inclus); systemSetting.setShadowlevel() : Fonction qui permet de définir le niveau de qualité des ombres (varie entre 0 et 5 inclus). Bien, on va donc ajouter trois éléments à notre interface : Du texte qui va contenir "Ombres"; Un bouton "Faibles"; Un bouton "Moyennes"; Un bouton "Hautes". Ajoutons les ! Je vous invite à ouvrir le fichier systemoptiodialog.py qui se trouve dans votre pack uiscript. On va se rendre à la fin du fichier, vous devriez avoir quelque chose comme : { "name" : "tiling_apply", "type" : "button", "x" : 110+100, "y" : 185, "text" : uiScriptLocale.OPTION_TILING_APPLY, "default_image" : ROOT_PATH + "middle_Button_01.sub", "over_image" : ROOT_PATH + "middle_Button_02.sub", "down_image" : ROOT_PATH + "middle_Button_03.sub", }, Si vous n'avez pas trop toucher à votre interface. Commençons par ajouter le texte, il va avoir cinq propriétés : name : le nom de notre texte, même s'il n'est pas important ici type : qui sera "text" x : position abscisse y : positon ordonnée text : Le texte qu'il va contenir. Bien, nous allons définir son nom sur shadow et je vous explique pour les coordonnées juste après : { "name" : "shadow", "type" : "text", "x" : 40 + TEXT_TEMPORARY_X, "y" : 165+75, "text" : uiScriptLocale.OPTION_SHADOW, }, Le x, vous ne le touchez pas, c'est pour que cela soit aligné avec tous les autres textes. Il va cependant devoir changer le y qui va désigner la position verticale du bouton. Pour cela, ce n'est pas au hasard, vous allez prendre la somme de 25 et de l'ordonnée de l'élément situer le plus en bas actuellement sur votre fenêtre. Pour ma part c'est 165+50 dont j'ajoute 20 soit 165+75 (ou 240). Retenez l'ordonnée, il ne va pas changer pour nos boutons ! Comme vu précédement, le radio button à défaut de son type se définie de la même façon que le bouton, je passe donc cette étape ajoutez : { "name" : "shadow_low", "type" : "radio_button", "x" : 110, "y" : 165+75, "text" : uiScriptLocale.OPTION_SHADOW_LOW, "default_image" : ROOT_PATH + "small_Button_01.sub", "over_image" : ROOT_PATH + "small_Button_02.sub", "down_image" : ROOT_PATH + "small_Button_03.sub", }, { "name" : "shadow_medium", "type" : "radio_button", "x" : 110+50, "y" : 165+75, "text" : uiScriptLocale.OPTION_SHADOW_MEDIUM, "default_image" : ROOT_PATH + "small_Button_01.sub", "over_image" : ROOT_PATH + "small_Button_02.sub", "down_image" : ROOT_PATH + "small_Button_03.sub", }, { "name" : "shadow_high", "type" : "radio_button", "x" : 110+100, "y" : 165+75, "text" : uiScriptLocale.OPTION_SHADOW_HIGH, "default_image" : ROOT_PATH + "small_Button_01.sub", "over_image" : ROOT_PATH + "small_Button_02.sub", "down_image" : ROOT_PATH + "small_Button_03.sub", }, On leur a donné respectivement les noms : shadow_low shadow_medium shadow_high Bien, je vous laisse régler l'ordonnée des éléments si nécessaires. Pour en finir une bonne fois pour toute avec l'interface, vous allez ajouter dans votre locale_interface : OPTION_SHADOW Ombres OPTION_SHADOW_LOW Faibles OPTION_SHADOW_MEDIUM Moyennes OPTION_SHADOW_HIGH Hautes Et vous êtes parés ! Il ne nous reste plus que le système en lui même à faire, et vous allez voir que les radio_buttons ne sont pas très complexes, mais toujours plus que le bouton habituel. Rendez-vous ensuite dans le fichier uisystemoption.py dans votre pack root. Avant toute chose vous allez vérifier que dans vos importations au début du fichier, vous aillez bien ce module : import systemSetting Si non, je vous laisse l'ajouter. Nous allons maintenant procéder par étape. Souvenez vous de nos fonctions principales. Nous avions l'__Initialize pour initialiser les listes, les boutons etc... Bingo ! Nous allons devoir créer une liste qui va contenir nos boutons afin de les faire fonctionner ensemble, je vous invite donc à ajouter de dans : self.ShadowButtonList = [] Ensuite, si vous scrollez un peu le fichier vers le bas, vous verrez notre fonction __Load_BindObject. Comme nous avons besoin d'importer trois nouveaux boutons, nous en aurons besoin ! -"Mais Takuma, et le texte alors, on l'importe pas ?!" Non ! Il va s'ajouter tout seul quand le système va charger l'interface ! Mais, attention, nous n'allons pas effectuer la même opération que pour des boutons normal. ici, nous allons ajouter l'élément à notre liste, pour cela nous allons utiliser la fonction append(), comme ceci : self.ShadowButtonList.append(GetObject("shadow_low")) self.ShadowButtonList.append(GetObject("shadow_medium")) self.ShadowButtonList.append(GetObject("shadow_high")) Ici nous disons bien à Python "Je veux que tu ajoute à ma liste ShadowButtonList ce que te renvoie la fonction GetObject avec le paramètre que je lui donne". Bien, continuons ! Allez un peu plus en bas, nous trouverons la fonction __Load. Souvenez vous, je vous ai dis que c'était ici que nous définissons les événements ! Et bien nous allons le faire. Mais cette fois-ci de manière plus subtile, tout simplement car nos trois boutons sont stockés dans la même liste. Nous allons donc faire appel à leur pointeur, c'est à dire le numéro qui leur est attribué dans le liste. Ce numéro n'est pas donné au hasard, il commence à 0 et va jusqu'à l'infini (ou presque). Nous allons donc taper ceci : self.ShadowButtonList[0].SAFE_SetEvent(self.__OnClickShadowLow) self.ShadowButtonList[1].SAFE_SetEvent(self.__OnClickShadowMedium) self.ShadowButtonList[2].SAFE_SetEvent(self.__OnClickShadowHigh) Vous retrouvez donc bien la liste, avec le bouton 1(0), 2(1) et 3(2). Je vous donne les trois fonctions car cela ne fait pas parti de notre objectif aujourd'hui : def __OnClickShadowLow(self): systemSetting.SetShadowLevel(1) self.__SetShadow(0) def __OnClickShadowMedium(self): systemSetting.SetShadowLevel(3) self.__SetShadow(1) def __OnClickShadowHigh(self): systemSetting.SetShadowLevel(5) self.__SetShadow(2) Cependant, vous soyez que dans chacune des fonctions, j'appelle la fonction SetShadow avec l'index du bouton en paramètre, pourquoi me direz-vous ? Je vous montre la fonction, et vous essayer de comprendre? def __SetShadow(self, index): self.__ClickRadioButton(self.ShadowButtonList, index) La voici. Il existe une fonction __ClickRadioButton bouton qui permet avec le self, et l'index de gérer nos radio_buttons. Bien, si vous essayez maintenant vous boutons vont marcher correctement, mais un détail est encore gênant : Aucun bouton n'est activé dès le début ! C'est en réalité très simple, pour cela nous allons utiliser la fonction ClickRadioButton qui va sélectionner le bouton qu'il faut avec l'index. Pour récupérer ce même index nous allons le niveau des ombres. Cependant problème : On a que 3 boutons, et il existe 6 niveau différents d'ombres... 6/2 = 3 ! Bingo ! On va donc diviser le niveau de l'ombre et prendre ensuite sa partie entière afin d'avoir un index ! Tout cela va se passer dans la fonction __Load, généralement après les événements, on définie si nécessaire les index. Vous allons donc ajouter : self.__ClickRadioButton(self.ShadowButtonList, int(systemSetting.GetShadowLevel()/2)) On envoie donc à ClickRadioButton, notre liste contenant les boutons, et on lui donne en index la valeur nécessaire : On récupère la valeur de la qualité de l'ombre avec GetShadowLevel(), on la divise ensuite pas deux avec le /2 et on fini par prendre sa valeur entière grâce à la fonction int(). Vous savez maintenant comment faire vos radio_buttons ! Félicitation !
-
Python - Créer ses propres Systèmes
Metin2 Dev a posté un sujet dans Programmation & Développement / Codes
Salut! Après avoir regardé mes anciens tutoriels, et voulant en refaire d'autre, j'ai conclu que certains manquaient vraiment d'informations, donc une petite rénovation ne lui fera pas de mal à celui la non plus. Le python est facilement compréhensible pour certain, pour d'autre non (cc @Kijaru, me demande pas pourquoi j'ai pensé à toi). Quelques explications ne seraient donc pas de refus. Objectifs : Comprendre comment marche un système, Connaître les principales fonctions essentielles à n'importes quelles interfaces. Réalisez vous même votre interface. Partie 1 : Comprendre comment marche un système : Pour bien comprendre comment marche un système, il faut déjà comprendre ce que c'est, et combien de partie le compose. Il faut savoir qu'un système, si nous suivons la manière de coder de ymir, est composé de deux parties : Les éléments et leurs positions : Dans ce fichier, on va répertorier dans un dictionnaire tous les éléments qui vont composer notre interface, comme des boutons, etc. Mais plus en général, c'est ici que nous allons définir la taille de la fenêtre, des boutons, et le texte. Le système : C'est la partie qui va gérer le tout. Il va afficher l'interface, et dire "si il clique là, tu fais ça" etc... Quand je vous dis que c'est "si nous respectons le format de ymir" c'est qu'il existe une autre façon. Effectivement, il vous est possible de tout regrouper en un fichier, et de définir "sur le tas" tous les éléments nécessaire. Je pourrais si vous le voulez vous faire un autre tutoriel pour vous présenter l'autre méthode. C'est notre partie système qui va être chargé en première, c'est elle même qui ira chercher la partie interface afin de s'en servir. On dispose généralement les parties comme ceci dans nos packs : Système : ui[nom système].py ---> root Interface: [nom système].py -----> uiscript Partie 2 : L'interface : Bien, pour créer une interface, nous allons créer un nouveau fichier dans le uiscript je vais l'appeler takuma.py de mon côté. Je vous invite à ouvrir le fichier que vous venez de créer. Nous pouvons commencer ! Avant, comme je compte vous apprendre à coder un minimum proprement, nous allons utiliser le uiScriptLocale, c'est un module qui permet de récupérer les phrases qui seront déclarées dans notre locale_interface, nous verrons ça plus tard. Pour l'ajouter à notre projet, tapez : import uiScriptLocale Bien. Maintenant, nous allons dès maintenant définir une variable qui va contenir le chemin d'accès aux différents éléments, je vous expliquerais ceci au moment venu, pour l'instant, tapez à la ligne : ROOT_PATH = "d:/ymir work/ui/public/" Bien ! Nous pouvons enfin commencer notre interface. Nous allons maintenant créer un dictionnaire que nous allons nommé "window" (fenêtre) : window = {} C'est ce dictionnaire qui va contenir toute notre interface, je vous invite à l'écrire comme ceci pour une meilleur lisibilité : window = { } Maintenant, tous les éléments se trouvant de dans seront devancés d'au moins une tabulation. Nous devons donc maintenant donner des propriétés à notre window, sinon, comment pourrait-on connaitre comment il est fait etc... ? Pour cela nous allons définir 6 propriétés : name : Tout simplement le nom de notre fenêtre, style : Le modèle utilisé pour notre fenêtre, x : L'apparition de la fenêtre sur l'axe des abscisses sachant qu'il démarre en haut à gauche de l'écran. y : L'apparition de la fenêtre sur l'axe des ordonnées. width : La largeur de la fenêtre height : la haute de la fenêtre. Bien, commençons par le nom : window = { "name" : "TakumaDialog", } Retenez la syntaxe elle est sensiblement la même pour tous les éléments. Maintenant pour le style, nous allons utiliser un tuple (liste immuable) qui va contenir nos deux propriétés : movable et float : "style" : ("movable", "float",), Pourquoi mettre une virgule à la fin me direz-vous ? C'est les tuples, ils doivent être défini comme ceci. Sinon python va enlever les parenthèses et considéré ça comme une variable lambda. Ensuite, pour x et y nous allons mettre 0 : window = { "name" : "TakumaDialog", "style" : ("movable", "float",), "x" : 0, "y" : 0, } Ajoutons maintenant les deux derniers paramètres : "width" : 305, "height" : 255+25, Compléter avec la taille que vous voulez généralement, on adapte au fur à mesure ! Nous allons ajouter "des enfants" à notre fenêtre sinon, on doit s'arrêter là... Autant vous dire que ça ne ressemble à rien ! Pour ça, nous allons ajouter une nouvelle balise : "chlidren" : window = { "name" : "TakumaDialog", "style" : ("movable", "float",), "x" : 0, "y" : 0, "width" : 305, "height" : 255+25, "children" : } Pour celle-ci, nous allons devoir mettre les parenthèses pour le tupples, puis ajouter des guillemets pour recréer un dictionnaire, les éléments qui seront de dans. Cela donne : window = { "name" : "TakumaDialog", "style" : ("movable", "float",), "x" : 0, "y" : 0, "width" : 305, "height" : 255+25, "children" : ( { }, ), } Bien ! Nous allons maintenant dans les childs définir le tableau en lui même "board", il aura les mêmes balises si ce n'est "style" qui va passer en "type", je le fais d'un coup pour abréger un peu : Maintenant dans notre board, nous allons ajouter tous les éléments que va contenir notre fenêtre. Cependant, n'oubliez pas un élément important : La barre de titre et le titre ! Vous ne pouvez pas deviner le code, je vous le donne : Oui, ça largeur doit être de 4 plus grande que le board. Maintenant, j'attire votre attention sur deux choses : J'ai définie les propriétés en ligne pour les chlidrens de titlebar, comme ça vous voyez comment cela se passe, La balise text à la fin, va nous renvoyer une valeur ("texte" ici). Cependant, nous allons faire de façon qu'elle nous renvoie une valeur de notre locale_interface. Pour cela, nous allons taper : "text": uiScriptLocale.TAKUMA_TITLE, Vous devrez ensuite ajouter une ligne dans votre locale_interface (locale/xx/) et taper : TAKUMA_TITLE valeur Ceci, bien séparer par une tabulation. Et votre fichier récupérera tout seul la variable comme un grand afin de la faire correspondre à la propriété texte. Et bien voilà, vous avez votre base. Il ne vous manque plus que un élément, les types qui existent et vous pourrez finir la partie interface tout seul. les éléments suivant vont se placer sur le board, il faut donc sortir de titlebar, et repartir dans board, pour cela, j'ai déjà comme vous pouvez le voir sur le screen du haut remis des autres balises au même niveau que titlebar. Et bien c'est elles qui vont contenir un nouvel élément, par exemple, un bouton : Quant au trois nouvelles propriétés apparues ici : defaut_image: Chemin d'accès de l'image du bouton par défaut, over_image : Chemin d'accès de l'image du bouton quand la souris passe dessus, down_image : Chemin d'accès de l'image du bouton quand on clique dessus. Notez qu'on utilise la variable ROOT_PATH afin de ne pas avoir à taper à chaque fois "d:/ymir work/ui/public/" Si vous voulez ajouter d'autres éléments, il vous suffit de procéder au même niveau que le bouton, et de répéter cette opération. Nous découvririons d'autres types plus tard. Vous avez donc à la fin de cette partie : Partie 3 : Le système : Enfin la troisième partie ! La plus sympa ! Pour celle-ci vous allez créer un fichier ui[nomsystème].py, pour moi, cela sera uitakuma.py. Nous repartons d'un fichier vide ! Comme l'autre, nous avons des importations à effectuer ! Elles ajoutent selon ce que vous voulez faire, mais vous aurez en général besoin de celles-ci : import ui import app Nous allons maintenant créer une class, nommée TakumaDialog qui prendra en paramètre ui.ScriptWindow : Nous allons ensuite créer la fonction init, c'est une fonction assez spéciale. C'est une fonction qui s'exécute automatiquement quand on fait appel à la class. Pour faire comme ymir, et respecter son caractère privé, elle va en réalité s'appeler __init__, tapez : Pourquoi utiliser self dans init me direz vous ? Et bien, outre que ce soit une norme, il va nous permettre si vous allez plus loin dans le Python de créer des instances. Bref, nous allons ensuite créer deux fonctions importantes : __Initialize, __Load, Je ne l'ai pas précisé jusque là, les deux tirets du huit devant marque le caractère privée de la fonction. Chaque fonction va avoir un but très précis : La fonction Initialize va créer toutes les variables, les listes, etc... nécessaires au bon fonctionnement du programme. Vous verrez plus tard que les listes seront très importantes quand vous utiliserez des radio_buttons (bouton donc un seul est actionnable). Je ne vous en ai pas parlé tout de suite car nous devons évoquer une histoire d'index avec eux. je vous en parlerai plus tard. Dans la fonction Initialize vous allez mettre par exemple pour l'interface que j'ai faite : self.takumaButton = 0 Cette fonction ne servira que à ça. Profitons en pour finir notre fonction __init__ : Maintenant avant de commencer notre fonction __Load(), nous allons en créer deux autres (oui ça fait beaucoup, je sais, mais plus tard ça sera automatique chez vous. Si vous voulez, je vous fournirai un .py avec toutes les fonctions essentielles pour coder proprement) : __Load_LoadScript() __Load_BindObject() La fonction LoadScript est toujours la même, la voici : def __Load_LoadScript(self, fileName): try: pyScriptLoader = ui.PythonScriptLoader() pyScriptLoader.LoadScriptFile(self, fileName) except: import exception exception.Abort("System.TakumaDialog.__Load_LoadScript") Je vous explique ne tombez pas de votre chaise ! try signifie "essayer", ici on essaye de faire ce qu'il y a après le try, et s'il n'y arrive pas, il envoie le except. Ici, nous utilisons en réalité cette fonction crée dans notre ui.py : def LoadScriptFile(self, window, FileName): import exception import exceptions import os import errno self.Clear() print "===== Load Script File : %s" % (FileName) try: # chr, player µîÀº sandbox ³»¿¡¼ import°¡ Çã¿ëµÇÁö ¾Ê±â ¶§¹®¿¡,(º¿ÀÌ ¾Ç¿ëÇÒ ¿©Áö°¡ ¸Å¿ì Å.) # ¹Ì¸® script dictionary¿¡ ÇÊ¿äÇÑ »ó¼ö¸¦ ³Ö¾î³õ´Â´Ù. import chr import player import app self.ScriptDictionary["PLAYER_NAME_MAX_LEN"] = chr.PLAYER_NAME_MAX_LEN self.ScriptDictionary["DRAGON_SOUL_EQUIPMENT_SLOT_START"] = player.DRAGON_SOUL_EQUIPMENT_SLOT_START self.ScriptDictionary["LOCALE_PATH"] = app.GetLocalePath() execfile(FileName, self.ScriptDictionary) except IOError, err: import sys import dbg dbg.TraceError("Failed to load script file : %s" % (FileName)) dbg.TraceError("error : %s" % (err)) exception.Abort("LoadScriptFile1") except RuntimeError,err: import sys import dbg dbg.TraceError("Failed to load script file : %s" % (FileName)) dbg.TraceError("error : %s" % (err)) exception.Abort("LoadScriptFile2") except: import sys import dbg dbg.TraceError("Failed to load script file : %s" % (FileName)) exception.Abort("LoadScriptFile!!!!!!!!!!!!!!") ##### Body = self.ScriptDictionary["window"] self.CheckKeyList("window", Body, self.BODY_KEY_LIST) window.ClearDictionary() self.InsertFunction = window.InsertChild window.SetPosition(int(Body["x"]), int(Body["y"])) if localeInfo.IsARABIC(): w = wndMgr.GetScreenWidth() h = wndMgr.GetScreenHeight() if Body.has_key("width"): w = int(Body["width"]) if Body.has_key("height"): h = int(Body["height"]) window.SetSize(w, h) else: window.SetSize(int(Body["width"]), int(Body["height"])) if True == Body.has_key("style"): for StyleList in Body["style"]: window.AddFlag(StyleList) self.LoadChildren(window, Body) Assez complexe, je vous l'accorde. Je vais donc vous simplifiez la chose : Elle permet de charger un fichier d'interface et ses éléments. Du côté de la fonction __Load_BindObject, elle va de son côté charger tous les éléments de notre interface et les stocker dans des variables. Bien, si on résume pour nous, nous avons : Retenez la manière de charger des éléments, avec le GetObject = self.GetChild c'est la manière la plus propre à mon goût. Bien, maintenant complétons la fonction __Load : Dans Load nous allons maintenant attribuer tous les événements(ce qu'il se passe quand on clique sur quelque chose) et la position de la fenêtre car si vous vous souvenez nous avions mis x = y = 0 pour coordonnées d'apparition de la fenêtre. Pour cela, ajoutons : self.SetCenterPosition() Tapez aussi cette ligne, qui ne change quelque soit la gui, sauf si elle n'a pas de titlebar ou que vous avez changé le nom des éléments : self.titleBar.SetCloseEvent(ui.__mem_func__(self.Close)) Ce qui nous donne en tout : Définissons maintenant un événement à notre bouton. Pour cela nous allons utiliser la fonction SAFE_SetEvent(), renvoyant sur cette fonction : def SAFE_SetEvent(self, func, *args): self.eventFunc = __mem_func__(func) self.eventArgs = args Enfin, bref, je vous donne la syntaxe, cela donne : self.takumaButton.SAFE_SetEvent() Nous entrons ensuite entre les parenthèses la fonction qui va s'exécuter quand l'événement sera lancé, par exemple : self.takumaButton.SAFE_SetEvent(self.maFonction) Par exemple, nous allons faire que cette fonction envoie "Bonjour !" dans le chat à la place de celui qui déclenche l'événement. Créons la fonction : def maFonction(self): import net net.SendChatPacket("Bonjour ! ") Il ne vous manque plus qu'une seule fonction pour que votre système soit prêt, la fonction pour l'afficher ! Et la voici : def Show(self): ui.ScriptWindow.Show(self) Félicitation, vous avez maintenant les bases pour créer votre système ! Je vous ferais d'autre tutoriel, pour par exemple évoquer les radio_buttons, les images, etc. Je vous invite à essayer de découvrir par vous même en regardant dans votre fichier ui.py qui contient tout ce qu'il faut ! Vous obtenez donc à la fin : Comme promis, un fichier neutre contenant les fonctions minimums : ICI ! Bon développement !- 8 réponses
-
- 40
-
-
-
-
Hi xSoul998 No ! This costume and hair.. work 100%, i have tested on my pserver
-
Implanter un magasin et le remplir Probléme
Metin2 Dev a répondu à un(e) question de Hotopoche dans Questions & Réponses
Hey pour faire ça tu peux signaler en dessous du post que le lien est mort au lieu de créer un topic non ? -
Merci pour ton partage !
-
Centre de Téléchargement Télécharger ( Interne ) Bonjour, Aujourd'hui je vous partage un set de costume Avalon ! Dans le fichiers Téléchargement : ICI Cordialement
- 5 réponses
-
- 25
-
-
-
Voila ses ajouter dans la section ''Téléchargements''
-
J'ai ajouter le lien de téléchargement, je voulais le bloquer comme les autres lien sur le forum, mais sa fonctionne pas.
-
Centre de Téléchargement Télécharger ( Interne ) Bonjour, Aujourd'hui je vous partage les 2 nouveaux Set Samouraï 2018... Dans le fichiers Téléchargement : ICI Cordialement
- 17 réponses
-
- 36
-
-
-
-
-
Merci. On voit un gros partage, il reste des problèmes, très mineurs et facilement corrigés en cherchant un peu sur internet. Bonne initiative, merci !
-
Merci du partage !
-
C++ - Choix du Skin Magasin
Metin2 Dev a répondu à un(e) sujet de iryzz dans Programmation & Développement / Codes
Pas mal pas mal !
