Jump to content

Search the Community

Showing results for tags 'python'.

More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


  • Community
    • M2Dev
    • Offtopic
    • Games Talk
    • Music / Videos / Art
    • Member Representations
    • Services & Sales
  • Metin2
    • General
    • Questions and Answers
    • Frequently Asked Questions
    • Private Servers
    • Videos
  • Suggest a Tutorial / Release
    • Suggest a Tutorial / Release
    • Temporary forum
  • Releases
    • General
    • Guides & HowTo
    • Tools
    • Programming & Scripts / Systems
    • Maps
    • Quests
    • Binaries & Clients / ServerFiles
    • 3D Models
    • 2D Graphics
    • Operating Systems


There are no results to display.

There are no results to display.

Find results in...

Find results that contain...

Date Created

  • Start


Last Updated

  • Start


Filter by number of...


  • Start







Steam ID













Found 197 results

  1. Yo' folks! Here is a small gui extension from the official v20.4.0 binary, which is kinda trash, but can be useful for some guis to point out some stuffs. Webzen finally started to use their old codes, this is one of they have never used before. Preview: Animation is not binary sided, it's via python, the testScript.py is attached, the injection is on you, there are many examples how to test a script, there are also many loaders to it. Have fun, Download
  2. https://i.gyazo.com/b72a19cfa9cbb9ad7b52342662f94e41.mp4 ### 0.1 Root / uiMessenger.py: # 1. Search: def OnLogin(self, groupIndex ... # 1. After: member.Online() self.OnRefreshList() # 1. Add: if not name in constInfo.ALREADY_NOTIFY_LIST: self.onlinePopup = uiCommon.OnlinePopup() self.onlinePopup.SetUserName(name) self.onlinePopup.SetEvent(ui.__mem_func__(self.OpenWhisper), "MOUSE_LEFT_BUTTON_UP", name) self.onlinePopup.SlideIn() constInfo.ALREADY_NOTIFY_LIST.append(name) # 1.1 After: def OnLogin(... Add: def OpenWhisper(self, eventType, userName): self.whisperButtonEvent(userName) ### 0.2 Root / constInfo.py: # 2. Add: ALREADY_NOTIFY_LIST = [] ### 0.3 Root / ui.py # 3 Search: class Board(Window): (....) # 3 REPLACE this class with: class Board(Window): CORNER_WIDTH = 32 CORNER_HEIGHT = 32 LINE_WIDTH = 128 LINE_HEIGHT = 128 LT = 0 LB = 1 RT = 2 RB = 3 L = 0 R = 1 T = 2 B = 3 BASE_PATH = "d:/ymir work/ui/pattern" IMAGES = { 'CORNER' : { 0 : "Board_Corner_LeftTop", 1 : "Board_Corner_LeftBottom", 2 : "Board_Corner_RightTop", 3 : "Board_Corner_RightBottom" }, 'BAR' : { 0 : "Board_Line_Left", 1 : "Board_Line_Right", 2 : "Board_Line_Top", 3 : "Board_Line_Bottom" }, 'FILL' : "Board_Base" } def __init__(self, layer = "UI"): Window.__init__(self, layer) self.skipMaxCheck = False self.MakeBoard() def MakeBoard(self): CornerFileNames = [ ] LineFileNames = [ ] for imageDictKey in (['CORNER', 'BAR']): for x in xrange(len(self.IMAGES[imageDictKey])): if imageDictKey == "CORNER": CornerFileNames.append("%s/%s.tga" % (self.BASE_PATH, self.IMAGES[imageDictKey][x])) elif imageDictKey == "BAR": LineFileNames.append("%s/%s.tga" % (self.BASE_PATH, self.IMAGES[imageDictKey][x])) self.Corners = [] for fileName in CornerFileNames: Corner = ExpandedImageBox() Corner.AddFlag("not_pick") Corner.LoadImage(fileName) Corner.SetParent(self) Corner.SetPosition(0, 0) Corner.Show() self.Corners.append(Corner) self.Lines = [] for fileName in LineFileNames: Line = ExpandedImageBox() Line.AddFlag("not_pick") Line.LoadImage(fileName) Line.SetParent(self) Line.SetPosition(0, 0) Line.Show() self.Lines.append(Line) self.Lines[self.L].SetPosition(0, self.CORNER_HEIGHT) self.Lines[self.T].SetPosition(self.CORNER_WIDTH, 0) self.Base = ExpandedImageBox() self.Base.AddFlag("not_pick") self.Base.LoadImage("%s/%s.tga" % (self.BASE_PATH, self.IMAGES['FILL'])) self.Base.SetParent(self) self.Base.SetPosition(self.CORNER_WIDTH, self.CORNER_HEIGHT) self.Base.Show() def __del__(self): Window.__del__(self) def SetSize(self, width, height): if not self.skipMaxCheck: width = max(self.CORNER_WIDTH*2, width) height = max(self.CORNER_HEIGHT*2, height) Window.SetSize(self, width, height) self.Corners[self.LB].SetPosition(0, height - self.CORNER_HEIGHT) self.Corners[self.RT].SetPosition(width - self.CORNER_WIDTH, 0) self.Corners[self.RB].SetPosition(width - self.CORNER_WIDTH, height - self.CORNER_HEIGHT) self.Lines[self.R].SetPosition(width - self.CORNER_WIDTH, self.CORNER_HEIGHT) self.Lines[self.B].SetPosition(self.CORNER_HEIGHT, height - self.CORNER_HEIGHT) verticalShowingPercentage = float((height - self.CORNER_HEIGHT*2) - self.LINE_HEIGHT) / self.LINE_HEIGHT horizontalShowingPercentage = float((width - self.CORNER_WIDTH*2) - self.LINE_WIDTH) / self.LINE_WIDTH self.Lines[self.L].SetRenderingRect(0, 0, 0, verticalShowingPercentage) self.Lines[self.R].SetRenderingRect(0, 0, 0, verticalShowingPercentage) self.Lines[self.T].SetRenderingRect(0, 0, horizontalShowingPercentage, 0) self.Lines[self.B].SetRenderingRect(0, 0, horizontalShowingPercentage, 0) if self.Base: self.Base.SetRenderingRect(0, 0, horizontalShowingPercentage, verticalShowingPercentage) # 3 AFTER CLASS BOARD ADD THIS CLASS: class BorderB(Board): CORNER_WIDTH = 16 CORNER_HEIGHT = 16 LINE_WIDTH = 16 LINE_HEIGHT = 16 BASE_PATH = "d:/ymir work/ui/pattern" IMAGES = { 'CORNER' : { 0 : "border_b_left_top", 1 : "border_b_left_bottom", 2 : "border_b_right_top", 3 : "border_b_right_bottom" }, 'BAR' : { 0 : "border_b_left", 1 : "border_b_right", 2 : "border_b_top", 3 : "border_b_bottom" }, 'FILL' : "border_b_center" } def __init__(self): Board.__init__(self) self.eventFunc = { "MOUSE_LEFT_BUTTON_UP" : None, } self.eventArgs = { "MOUSE_LEFT_BUTTON_UP" : None, } def __del__(self): Board.__del__(self) self.eventFunc = None self.eventArgs = None def SetSize(self, width, height): Board.SetSize(self, width, height) def SetEvent(self, func, *args) : result = self.eventFunc.has_key(args[0]) if result : self.eventFunc[args[0]] = func self.eventArgs[args[0]] = args else : print "[ERROR] ui.py SetEvent, Can`t Find has_key : %s" % args[0] def OnMouseLeftButtonUp(self): if self.eventFunc["MOUSE_LEFT_BUTTON_UP"] : apply(self.eventFunc["MOUSE_LEFT_BUTTON_UP"], self.eventArgs["MOUSE_LEFT_BUTTON_UP"]) ### 0.4 Root / uiCommon.py # 4 Add this to the end of file: ### (Check if you have in this file import app ) class OnlinePopup(ui.BorderB): def __init__(self): ui.BorderB.__init__(self) self.isActiveSlide = False self.isActiveSlideOut = False self.endTime = 0 self.wndWidth = 0 self.textLine = ui.TextLine() self.textLine.SetParent(self) self.textLine.SetWindowHorizontalAlignCenter() self.textLine.SetWindowVerticalAlignCenter() self.textLine.SetHorizontalAlignCenter() self.textLine.SetVerticalAlignCenter() self.textLine.SetPosition(13, 0) self.textLine.Show() self.onlineImage = ui.ImageBox() self.onlineImage.SetParent(self) self.onlineImage.SetPosition(8, 8) self.onlineImage.LoadImage("d:/ymir work/ui/game/windows/messenger_list_online.sub") self.onlineImage.Show() def __del__(self): ui.BorderB.__del__(self) def SlideIn(self): self.SetTop() self.Show() self.isActiveSlide = True self.endTime = app.GetGlobalTimeStamp() + 5 def Close(self): self.Hide() def Destroy(self): self.Close() def SetUserName(self, name): self.textLine.SetText("Player %s is online." % str(name)) self.wndWidth = self.textLine.GetTextSize()[0] + 40 self.SetSize(self.wndWidth, 25) self.SetPosition(-self.wndWidth, wndMgr.GetScreenHeight() - 200) def OnUpdate(self): if self.isActiveSlide and self.isActiveSlide == True: x, y = self.GetLocalPosition() if x < 0: self.SetPosition(x + 4, y) if self.endTime - app.GetGlobalTimeStamp() <= 0 and self.isActiveSlideOut == False and self.isActiveSlide == True: self.isActiveSlide = False self.isActiveSlideOut = True if self.isActiveSlideOut and self.isActiveSlideOut == True: x, y = self.GetLocalPosition() if x > -(self.wndWidth): self.SetPosition(x - 4, y) if x <= -(self.wndWidth): self.isActiveSlideOut = False self.Close() ######## Please write in topic If i forgot something. ######## Images for board: https://mega.nz/#!O1ZjmQxY!tKJdWNXRP6Wgb3DGxzdFRNV9U-bfRIStwnq3ETd6gKo
  3. Hello M2Dev, Battle Pass (9.06.2019) Description: (Click) Lucky Box - My version inspired from official servers 19.1 Update. (9.06.2019) Description: (Click) CONTACT: ● DISCORD: ZeNu#9145 ● SKYPE: devil.devil997 ● Private Message
  4. Version of Files : XXX Hey yo, I'm injecting python modules via c++ api and after executing it my games crashes (client shuts down). My VS uses python 2.7 libs int GetInstanceType(long vid) { PyObject* args = PyTuple_New(1); PyTuple_SetItem(args, 0, PyInt_FromLong(vid)); PyObject* ret = PyObject_Call(PyObject_GetAttrString(PyImport_ImportModule("chr"), "GetInstanceType"), args, NULL); int result = PyInt_AsLong(ret); Py_DECREF(ret); Py_XDECREF(args); return result; }
  5. Hi there everyone, I'd like to share a small function which will give you a random name whilst creating a new character. This will only include the function and a list of names, so you will have to set the event on your own. Preview: The function which will most likely be put in introCreate.py import os import app #import the above lines if you don't have them already def __randomizeName(self): dir = os.path.dirname(os.path.abspath("introCreate.py")) list = open("%s\\names.list" % dir, "r").read().split("\n") rng = app.GetRandom(0, len(list)-1) randomName = list[rng] self.heroName.SetText("%s" % randomName) ## change variable 'heroName' with your editline's name Also place the attached file to this thread in the main directory of the client. (i.e: where the launcher is) names.list contains 150 names, if you ever need more names let me know so I can up the number or you can add more yourself, but do note you need to put each name as a new line so you won't mess up the function. That's all for now, hope you all have a great day! Cheers. names.list
  6. Hello community, this is my first tool shared on the forum, hope it helps a lot as it helped me. I know @Mali61 has already shared the tool on the forum but mine is open source so you can change what ever you want. The code is not the best but it does what it's supposed to, good uses. Credits: @WLsj24 for setting up the base locale_string.txt for translations. Git: https://github.com/Owsap/LocaleString-LocaleQuest-Builder
  7. Hi there devs, Its been a while since my last release and probably this will not change, but I had this guide in my head for like a year now. In my last ~2 years while I was working in the AE team various devs came and go, and this kind of guideline would have been a huge help and could prevent lots of errors. I have been in the dev scene for more than 10 years now and (as some of you may already noticed) one of my main interest has always been making more and more user friendly and complex UI objects/interfaces and along the road gathered tons of experience. You won't end up with another shiny system that you can use in your server this time by reading this article, but instead maybe I can prevent you from making windows that stays in the memory forever, opens infinite times, prevents other window's destruction, and many more. Who knows, maybe you will leave with some brand new never ever seen tricks that you can use in your future UIs. But first of all lets talk about some good2know stuff. UI layers Layers are root "windows", their purpose to hold all kind of interface windows while managing the z order of the windows (z order = depth level of the windows, which one is "above" the other). By default there are 5 layers (from bottom to top): "GAME": this is only used for the game window (game.py), and when you click on it the game will translate the click position to the map "UI_BOTTOM": this is used for shop titles "UI": this is the default layer, we would like to put most of our windows into this layer, like inventory, character window, messenger, pms, etc, so they can overlap each other "TOP_MOST": every window, that should always be in front of the player, so other windows would not overlap them from the lower layers, so for example the taskbar is placed in this layer "CURTAIN": this is for the curtain window, which is actually a black as a pit window, that is used to smooth the change from different introXX windows (like between login and charselect) This is the outest layer and goes before the other 4. So when the render happens, the 1st layer and its childs will be rendered first, then the 2nd layer, then the 3rd, etc, so by this we will get our comfy usual client layout. In the other hand, when we click, everything goes in reverse order, first the client will try to "pick" from the curtain layer's child windows (also in reverse order), then the top_most layer, etc. Ofc there is no layers beyond the game layer, so usually the "pick" will succeed there, and we will end up clicking on the map. UI windows Now lets talk a little bit about the parts of an UI window. It has a python object part and a c++ object part. When you create a basic Window (from the ui.py) basically 2 things happen: a python Window object is created first, then through the wndMgr.Register call a c++ CWindow object is created. These 2 objects are linked, in python the handle for the CWindow is saved in the self.hWnd variable, while in the CWindow the python object handle would be stored in the m_poHandler. What are these handles for? If you want to call a method on a window from python, you have to specify which window you want to perform that action on. Remember, the python part is just a "frontend" for the UI, the actual magic happens in the cpp part. Without python, its possible to create windows, but without the cpp part there is no windows at all. On the cpp part, we need the python object handle to perform callbacks, like notifying the python class when we press a key, or pressing our left mouse button. By default the newly created window will take a seat in one of the layers (if provided through the register method, otherwise it goes to the UI layer). In a healthy client we only put useful windows directly into a layer. For example you want to put your brand new won exchange window into the UI layer, but you don't want to put all parts of a window into the UI layer (for example putting the base window into the root layer then putting the buttons on it to the root layer too, then somehow using global coordinates to make it work). Instead, you want to "group" your objects, using the SetParent function. You may say that "yeah yeah who doesn't know this?", but do you know what actually happens in the background? The current window will be removed from its current parent's child list (which is almost never null, cus when you create it its in the UI layer, so the parent is the UI layer) and will be put into the end of the new parent window's child list. Why is it important, that it will be put into the end? Because it will determine the Z order of the childs of the window. For example if I create 2 ImageBox with the same size of image on the same position and then I set ImageBox1's parent before ImageBox2's parent, then I will only see ImageBox2, because that will be rendered after ImageBox1, because its position in the childList is lower than ImageBox2's. For normal window elements (like buttons) its very important, because after you set the parent of a window, you can't alter the z order (or rather the position in the childList) unless you use the SetParent again. No, you can't use SetTop, because its only for windows with "float" flags on it, which you only want to use on the base of your window (the root window that you put your stuff on it and use it as parent for most of the time). Window picking "Picking" is performed when we move the cursor. This is an important method, because this will determine the result of various events, for example the OnMouseOverIn, OnMouseOverOut, OnMouseRightButtonDown, etc. To fully understand it, you must keep in mind that every window is a square. Do you have a perfect circle image for a button? No you don't, its a square. Do you have the most abstract future window board design with full star wars going on the background? No, you DON'T. ITS A SQUARE. By default, a window is picked if: the mouse is over the window AND the window is visible (Shown) AND the window doesn't have "not_pick" flag set AND the window is "inside its parent's square" on the current mouse position, which means if the parent's position is 0,0 and it has a size of 10x10 and the window's position is 11, 0, the window is outside of its parent's square. This is really important to understand, lots of UI has fully bugged part because of ignoring this fact. I think every one of you already experienced it on some bad illumina design implementation, when you click on a picture, and your character will start to run around like a madman, because the game says that A-a-aaaaa! There is NO WINDOW ON THAT POSITION It is useful to use the not_pick flag whenever you can, for example on pure design elements, like lines and flowers and ofc the spaceships on the background. Lets say you have a size of 10x10 image that has a tooltip, and you have a window on it that has a size of 5x5. When the mouse is over the image, the tooltip will be shown, but if its over the 5x5 window, the tooltip won't appear, unless you set it to the 5x5 window too. But if you use the not_pick flag on the 5x5 window, the 5x5 window won't be picked and the tooltip would be shown even if the mouse is over the 5x5 window. Window deletion, reference counting, garbage collector, proxying The window deletion procedure starts on the python side, first the destructor of the python object will be called, then it will call the wndMgr.Destroy that deletes the c++ object. By default, we have to store our python object, or this time our python window, to make sour it doesn't vanish. Usually we do this via the interface module, like "self.wndRandomThing = myModule.RandomWindow()". But what is this? What is in the background? Python objects has reference count. Let me present it with the following example: a = ui.Window() # a new python object is created, and its reference count is 1 b = a # no new python object is created, but now b is refering to the same object as 'a', so that object has a refence count of 2 del b # b is no longer exists, so its no longer referencing to the newly created window object, so its reference count will be 1 del a # the newly created window object's reference count now 0, so it will be deleted, calling the __del__ method To be more accurate, del-ing something doesn't mean that it will be deleted immediately. If the reference count hits 0 the garbage collector (btw there is garbage collector in python if you didn't know) will delete it, and that moment the __del__ will be called. It sounds very handy isn't it? Yeeeeah its easyyyy the coder don't have to manage object deletion its sooo simple.... Yeah... But lets do the following: class stuff(object): def __del__(self): print "del" def doStuff(self): self.something = self.__del__ # here we could just simply do self.something = self too, doesnt matter a = stuff() a.doStuff() # and now you just cut your leg del a #you are expecting the "del" print, but that will never happen You may say " oh please who tf does something stupid like this? It SO OBVIOUS that its wrong whaaaaaaat????" But in reality, I could count on only one of my hand how many devs don't make this mistake. Even my codes from the past decade are bad according to this aspect, since I only realized this problem about a year ago, when I was working on the AE wiki. Even the yimir codes contain tons of this kind of errors, however there was definitely a smart man, who implemented the __mem_func__. Okay, I see that you still don't understand how is this possible to make this kind of mistake, so let me show you a real example: class myBoard(ui.Board): def __init__(self): super(myBoard, self).__init__() self.makeItRain() def __del__(self): super(myBoard, self).__del__() print "I want to break free. I want to breeaaaaaak freeeeeeeeeeee" def doNothing(self): pass def makeItRain(self): self.btn = ui.Button() self.btn.SetParent(self) self.btn.SetEvent(self.doNothing) #boom a = myBoard() del a # but where is the print? Thats not that obvious now right? What happens here? We create a myBoard object, which in the __init__ calls to the makeItRain func, which stores an ui.Button object, and sets the button to call for the myBoard class's doNothing function with the self object pointer in the OnLeftMouseButtonDown function, which means that the reference count will never be zero, because the btn referenced in the myBoard but myBoard is referenced in the btn but the btn is referenced in the.... so you know, its a spiral of death, that our best friend garbage collector can't resolve. Okay, but how to do it correctly? Lets talk about proxying. In python, proxies are weak references, which means that they don't increase reference count of an object, which is exactly what we need. #let me show this time the console output too class stuff(object): def __del__(self): print "del" >>> from weakref import proxy >>> a = stuff() #newly created object >>> b = proxy(a) #create weak reference to the new object (note that the weak reference object is also an object that stores the weak reference) >>> b #what is b? <weakproxy at 02DB0F60 to stuff at 02DBFB50> # b is a weakproxy object that refers to a which is a "stuff object" >>> del b # what if we delete b? # no "del" message, the stuff object is not deleted, because its reference count is still 1, because its stored in a >>> b = proxy(a) # lets recreate b >>> del a # now we delete the only one reference of the stuff object del # and here we go, we got the del message from __del__ >>> b # okay but whats up with b? <weakproxy at 02DB0F60 to NoneType at 7C2DFB7C> # because b is a weakproxy object, it won't be deleted out of nowhere, but it refers to a NoneType right now, because the stuff object is deleted (also note here that NoneType is also a python object :D) >>> if b: #what if I want to use b? ... print "a" ... Traceback (most recent call last): File "<stdin>", line 1, in <module> ReferenceError: weakly-referenced object no longer exists # in normal cases no need to trycatch this exception, because if we do everything right, we will never run into a deleted weakly-referenced object But how do we proxy something like self.doNothing? Actually, what is self.doNothing? self.doNothing has 3 parts: The first part is not that obvious, it has a base class pointer, which is points to myBoard. It has a class object pointer, that is basically "self". It refers to the current instance of myBoard. It has a function object pointer, called doNothing, which is located inside the myBoard class. And now we can understand ymir's ui.__mem_func__ class, which is exactly meant to be used for proxying class member functions: # allow me to reverse the order of the definitions inside the __mem_func__ so it will be more understandable class __mem_func__: def __init__(self, mfunc): #this happens when we write ui.__mem_func__(self.doSomething) if mfunc.im_func.func_code.co_argcount>1: # if the doSomething takes more than one argument (which is not the case right now, because it only needs the class obj pointer (which is the 'self' in the 'self.doSomething')) self.call=__mem_func__.__arg_call__(mfunc.im_class, mfunc.im_self, mfunc.im_func) #lets unfold the python object to the mentioned 3 parts else: self.call=__mem_func__.__noarg_call__(mfunc.im_class, mfunc.im_self, mfunc.im_func) #this will be called for the 'self.doSomething' def __call__(self, *arg): # this line runs whenever we call apply(storedfunc, args) or storedfunc() return self.call(*arg) class __noarg_call__: # this class is used whever the python object we want to proxy only takes one argument def __init__(self, cls, obj, func): self.cls=cls # this one I don't really get, we won't need the class object later also its not proxied, so its probably here to prevent delete the base class in rare cases self.obj=proxy(obj) # here we proxy the object pointer which is what really matters self.func=proxy(func) # and then we proxy the class func pointer def __call__(self, *arg): return self.func(self.obj) # here we just simply call the class function with the object pointer class __arg_call__: def __init__(self, cls, obj, func): self.cls=cls self.obj=proxy(obj) self.func=proxy(func) def __call__(self, *arg): return self.func(self.obj, *arg) # here we just simply call the class function with the object pointer and additional arguments Pros, cons, when to, when not to, why? Now you may ask that "Okay okay that sounds good, but why? What can this cause, how big the impact could be, is it worth to even bother with it?" First of all, you must understand that this is kinda equal to a memory leak. I would actually call these non-deleted windows as "leaking windows". Every time you warp, new and new instances will be generated from those objects, and ofc, one instance of a window means like about +50/100 instances, depending on the window complexity, how much childs it has. Usually these windows are at least remain in a Hide state, and hidden windows don't affect update and render time, but they will remain in the root layer, and may stack up depending on how much time the player warp. Still, the number of leaking root windows / warp is around 50-100, which is really not much for a linked list (the type of the childList). However, the memory consumption is much greater. One year ago AE had 10k leaking windows growth / warp. One base class (CWindow)'s size is 140 bytes, which means the memory growth is at least 1,3MB/warp and it does not contain the size of the python objects size for those windows, the linked_list headers and other necessary stuffs. After some hour of playing this can easily reach 100+MB of leaking memory. On worse cases the old windows are not even hidden, and on rewarp players may see multiple instances of the mentioned windows, like double inventory, double messenger window, etc. In this cases those windows can affect the render and update times too. Pros: your code will now work correctly regarding to this topic you may gain some performance boost you may find stuff in your client that even you don't know about you may find enough kebab for a whole week you can save kittens by removing leaking windows and proxying objects you can build my respect towards you and your code and you can even calculate the actual number using the following formula: totalRespect = proxysUsed * 2 + ui.__mem_func__sUsed - proxysMisused * 3.511769 - ui.__mem_func__sMisused * pi - 666 * ui.__mem_func__sNotUsed Cons: depending on how bad the situation is and how skilled you are, the time needed to find and fix everything could be vary, from few hours to several days if you are satisfied with your client as it is right now there is not that huge benefit regarding how much time it could take to fix all the windows Detecting leaking windows DON'T BLOCK THE BACKEND!! At first glance python code looks like you are invincible, you can do whatever you want, you are not responsible for the performance because python has a bottomless bucket full of update/render time and if the game freeze sometimes its definitely not your fault, the game is just BAD. Lets talk about OnUpdate and OnRender, whats the difference, when they are called, what to and what not to put in there. So as their name implies, they are called from the binary every time it performs an Update or Render action. In Update, the binary performs non-rendering actions, like updating the positions of walking characters, updating window positions, and every kind of calculation that is necessary for a render action, to make every kind of data up to date, reducing the cpu operations required for render as much as possible while keeping track of the time. In Render, the binary performs non-updating actions, calling only directx device rendering methods that builds up a picture of the current world, including the UI, using the current, up to date positions. If you try to count the number of stars in our galaxy to accurately simulate your star wars going on in the background of your inventory window, no matter where you do it, (render or update) you will start hurting the game. For example if you have an ui with tons of elements, generating all the elements under one tick can cause HUGE client lag. In my AE wiki, I only load one or two entity for the current page under an update tick, so it will still load quickly, but won't block the game. Notice that doing something like this is an UPDATE operation. Lets be nice and don't interrupt our rendering whit this kind of stuff. You should only use calls to render functions inside the OnRender, for example like in the ui.ListBox class, where we actually ask the binary to render a bar into our screen. Around 20% of time spent in an Update tick was consumed by the UI update (calling to a python object through the api is kinda slow by default) in the AE client one year ago. Removing the rendering and updating of the gui for testing purposes actually gave a huge boost to the client, so who knows, maybe one day someone will make a great client that runs smooth on low end pcs too. Answer the api calls if expected! Some calls from the binary like OnPressEscapeKey expects a return value. Of course if no return value is provided the binary won't crash, but can lead to weird problems and malfunctions. For example, the OnPressEscapeKey expects non-false (non-null) if the call was successful on the window, so it will stop the iteration from calling to lower level windows. So if you expect from the game to close only one window per esc push, you have to return True (or at least not False or nothing). There was a crash problem related to this in one of my friend's client recently. In his ui.EditLine class the OnPressEscapeKey looked something like this: def OnPressEscapeKey(self): if self.eventEscape(): return True return False In the original version it just return True unrelated to the eventEscape's return value. This looks fine at first glance, but if for some reason the self.eventEscape doesn't return anything and if (like the garbage collector decides to run or its disabled) the layer's or the parent's child lists changes in the binary because one or more windows are destroyed under the OnPressEscapeKey procedure and the iteration trough the child list is not interrupted, the binary will start to call invalid addresses thinking that they are existing windows, calling OnPressEscapeKey on them, resulting hardly backtraceable crashes. Closing words I can't highlight out enough times how much I like and enjoy creating new kind of UI stuff, (like animating windows that you may saw on my profile recently (click for the gif)) and because of this if you want to discuss a theory about new UI stuffs or mechanics of already existing UI stuffs feel free to do it in the comment section instead of writing me a pm to help others. Also this time (unlike for my other topics) I would like to keep this guideline up to date and maybe adding new paragraphs if I find out another common mistake/misunderstanding. DEATH TO ALL LEAKING WINDOW!!!!!4444four
  8. Hello, I am getting problems adding a new client interface, because i have the python files, but i cant add them to my client, always give me diferent errors. please contact me discord: Xaroca#8334 Best regards,
  9. Hi devs, I'd like to share a script I developed the other night whilst working on a project, this script is meant to improve the quality of life of Metin2 as often players find it hard to accurately tell the cooldown of skills and end up spamming their keyboards like many of us have. Preview: Without any further ado, let's dig right into coding this bad boy. We start with locale: Now we shall move onto root part: version 1.0 version 2.0 If you encounter any bugs or you have suggestions please let me know! Changes:
  10. Hi, It shows in the description of your item that it's possible to add a bonus to it. It works up to 7 bonuses. Tutorial Sincerly, ASIKOO
  11. https://gyazo.com/c10376e8fec6f5c40536eec0e880f980 https://mega.nz/file/MYdy1YQD#sk6RPL2yPJVVCSiN-eQmaJ0_U6QUlB8GhGWarjBJvVE VT: https://bit.ly/3fdvL3Q
  12. Hello Today I want to share with all of you a little thing. All times when you are trying to open your safebox, it's requesting the password, isn't it? But for what? I modified to limit these requests to one input per login. Of course if you warp or change character, you need to retype your password again. But with extra modifications you can do it to store the password until you close the client, but I don't recommend this. So, first go to the uiSafeBox.py file and paste this line into the __init__ method of the PasswordDialog class, with taking care of the tabulators: self.lastPassword = "" This variable will store your last entered password. Then replace the Accept method of the same class with this: (take a look for the syntax, modulename, and true/false!) def OnAccept(self): self.lastPassword = str(self.passwordValue.GetText()) m2net.SendChatPacket(self.sendMessage + self.lastPassword) self.CloseDialog() return True When you accept the password input dialog, the entered password will be saved, if it's correct if it isn't as well. Don't worry about it, it will be reset if you entered wrong password. The next step is that to paste these new functions into the same class. def InitSafeboxPassword(self): self.lastPassword = "" def GetSafeboxPwd(self): return self.lastPassword As you can see, here is a function which will reset the entered password . With this, the file is done. Open interfaceModule.py and serach this line: ## Safebox then paste this function below: def InitSafeboxPassword(self): self.dlgPassword.InitSafeboxPassword() Then replace the AskSafeboxPassword and AskMallPassword functions with these: def AskSafeboxPassword(self): if self.wndSafebox.IsShow(): return if self.dlgPassword.IsShow(): self.dlgPassword.CloseDialog() return self.dlgPassword.SetSendMessage("/safebox_password ") if self.dlgPassword.GetSafeboxPwd() != "": self.dlgPassword.OnAccept() return self.dlgPassword.SetTitle(localeInfo.PASSWORD_TITLE) self.dlgPassword.ShowDialog() def AskMallPassword(self): if self.wndMall.IsShow(): return if self.dlgPassword.IsShow(): self.dlgPassword.CloseDialog() return self.dlgPassword.SetSendMessage("/mall_password ") if self.dlgPassword.GetSafeboxPwd() != "": self.dlgPassword.OnAccept() return self.dlgPassword.SetTitle(localeInfo.MALL_PASSWORD_TITLE) self.dlgPassword.ShowDialog() With these functions, if the last entered password is correct, the storages will open automatically with the last entered password. The file is done. Next, open your game.py and search this function: OnSafeBoxError and extend it with this line: self.interface.InitSafeboxPassword() So, as you can see this is the answer from the server to entered wrong password and this function will reset the last entered password as well . That's all, I hope you like it, and have fun.
  13. Version of Files : XXX Hello there, I try to write some codes to make Auto storage items to warehouse but I dont know how to input password ? 1. Description of the problem / Question : There is 2 problem 1 how can I add password ? How Can I send the spesific id items to storage ? import player, event, net vid = player.GetTargetVID() net.SendOnClickPacket(vid) event.SelectAnswer(0,2) event.SelectAnswer(event.BUTTON_TYPE_NEXT, 255) event.SelectText("asd") // I tried this but it was bad try I think this part https://prnt.sc/sr072z Thanks, Sincerly, 3r01n34
  14. Introduction Hey, since I'm often asked if I can make an example for this or that in Python, I just post some of these examples in this thread from time to time. Usually they just end up in my trash (that's why the collection is small for now) but maybe it will help some of you to learn something. You are also welcome to post examples here and I will add them to the startpost. You have a wish for a certain example? Then write it in here. PS: These examples are designed very simple, so that they can be understood. Examples ui.ComoBox() ui.ScrollBar() with text ui.AniImageBox() as loading bar ui.ListBox() ui.ToggleButton() ui.RadioButtonGroup() ui.DragButton() ui.Bar(), ui.Box(), ui.Line() ui.Gauge(), ui.SliderBar() ui.TextLine(), ui.EditLine() Pagination Tabs DropDown Tree Category Navigation ListBox Search TextLineScrollable Class Collapsible window
  15. Versión de archivos : XXX Hola Metin2Dev, tengo un pequeño problema con el libro de idiomas. Yo uso 40k Puedo leer libros con el imperio Chunjo y Jinno, y trabajo muy bien, pero cuando trato de usar un Shinsoo, no puedo leer el libro de idiomas, solo fallo y nunca leo 1 ... No sé porque solo este imperio no puede leer, Obviamente utilicé el exorcismo para leer, y verifico char_item.cpp pero no puedo ver nada malo, este es el código: y como puedo poner mas%? ¡¡¡Gracias!!!
  16. So, someone asked me to do this some time ago, I know some servers have already thought of this idea and have been present for some time but anyway I will share it since I haven’t seen nothing similar shared elsewhere and this has been sitting around my hard drive. Although it's something very simple, it’s a good feeling to see from the inventory the status of the dragon soul. Instead of adding the tutorial here I will link a repository to the guidelines. Demonstration Git Repository https://github.com/Owsap/DSS_ACTIVE_EFFECT_BUTTON
  17. Hello, came by to share something simple yet helpful for players, someone requested me this feature that allows you to input money with k format on the pick money dialog window, this will enable you to input “1kk” instead of “1000000” Hope it comes in handy for who is planning to use it. GitHub Repository: https://github.com/Owsap/USE_MONEY_K_FORMAT
  18. Version of Files : XXX Hello everybody, i'm so sorry for my bad english , i try to di my best. 1. Description of the problem / Question : I try to use Guild Stats sytem, everything ok in Game/Client Source but i receive one error from Client Py, it's seems like i don't have one file and i hope you can help me to find out how to fix it. 2. SysErr ( Client ) 0511 19:31:20798 :: File "networkModule.py", line 246, in SetGamePhase0511 19:31:20798 :: File "system.py", line 131, in __pack_import0511 19:31:20798 :: File "system.py", line 111, in _process_result0511 19:31:20798 :: File "game.py", line 50, in <module>0511 19:31:20798 :: File "system.py", line 131, in __pack_import0511 19:31:20798 :: File "system.py", line 111, in _process_result0511 19:31:20798 :: File "uiWarBoard.py", line 13, in <module>0511 19:31:20798 :: File "system.py", line 138, in __pack_import0511 19:31:20799 :: ImportError0511 19:31:20799 :: :0511 19:31:20799 :: No module named component0511 19:31:20799 :: I think the solution can be to insert the component.py file in root, but i can't find this file anywhere. I hardly ever open discussions, nor am I happy to thank all Metin2 dev users, really guys, you saved me many of those times that you don't even imagine.
  19. Someone asked me for cache chat messages. Here is the release constInfo.py Add: lastSentenceStack = [] lastSentencePos = 0 game.py In def Open(self): add: if len(constInfo.lastSentenceStack) > 0: constInfo.lastSentencePos = 0 uiChat.py remove: self.lastSentenceStack = [] self.lastSentencePos = 0 All: self.lastSentenceStack Replace with: constInfo.lastSentenceStack All: self.lastSentencePos Replace with: constInfo.lastSentencePos That's all, enjoy
  20. Version of Files : XXX 1. Description of the problem / Question : It's related with the value of the bonuses. 2. SysErr ( Client / Server ) / SysLog ( Server ) 3. How to reproduce it ? This error is appears in syserr only when I'm moving my mouse over items. 4. Screenshots ? def SA(text): def f(x): return text % x return f If I'm leaving it in this way " return text % x", it's alright, no problems with the bonuses, but the error appears in syserr, but if I edit it in this way "return text + "%d" % x" for example, the error disappears, but: game.py : https://pastebin.com/w9ZFLwz3 uiaffectshower.py : https://pastebin.com/xe9H9n5h localeinfo.py: https://pastebin.com/VRjvn5Zh Thanks, Sincerly, Cristian
  21. Hi. This small release save our sent and received private messages till we change the character. If you close by mistake PM with somebody and after you'll open the converastion with that player the old messages will be loaded from dictionary. All changes are in root. Let's begin. constInfo.py Add: WHISPER_MESSAGES = {} game.py Replace these methods: def OnRecvWhisper(self, mode, name, line): if mode == chat.WHISPER_TYPE_GM: self.interface.RegisterGameMasterName(name) line.replace(" : ", ": ") if not self.interface.FindWhisperButton(name) and constInfo.WHISPER_MESSAGES.has_key(name) and not self.interface.whisperDialogDict.has_key(name): self.interface.RecvWhisper(mode, name, line, True) else: self.interface.RecvWhisper(mode, name, line, False) if not constInfo.WHISPER_MESSAGES.has_key(name): constInfo.WHISPER_MESSAGES.update({name : [(mode, line)]}) else: constInfo.WHISPER_MESSAGES[name].append((mode, line)) chat.AppendWhisper(mode, name, line) def OnRecvWhisperSystemMessage(self, mode, name, line): chat.AppendWhisper(chat.WHISPER_TYPE_SYSTEM, name, line) self.interface.RecvWhisper(mode, name, line, False) def OnRecvWhisperError(self, mode, name, line): if localeInfo.WHISPER_ERROR.has_key(mode): chat.AppendWhisper(chat.WHISPER_TYPE_SYSTEM, name, localeInfo.WHISPER_ERROR[mode](name)) else: chat.AppendWhisper(chat.WHISPER_TYPE_SYSTEM, name, "Whisper Unknown Error(mode=%d, name=%s)" % (mode, name)) self.interface.RecvWhisper(mode, name, line, False) interfaceModule.py Replace this method: def RecvWhisper(self, mode, name, line, loadMsg): if loadMsg: for text in constInfo.WHISPER_MESSAGES[name]: chat.AppendWhisper(text[0], name, text[1]) if not self.whisperDialogDict.has_key(name): btn = self.FindWhisperButton(name) if 0 == btn: btn = self.__MakeWhisperButton(name) btn.Flash() chat.AppendChat(chat.CHAT_TYPE_NOTICE, localeInfo.RECEIVE_MESSAGE % (name)) else: btn.Flash() elif self.IsGameMasterName(name): dlg = self.whisperDialogDict[name] dlg.SetGameMasterLook() introSelect.py In def Open(self): add: WHISPER_MESSAGES = {} uiWhisper.py Add: import constInfo Replace this method: def OpenWithTarget(self, targetName="", loadMessages=False): chat.CreateWhisper(targetName) chat.SetWhisperBoxSize(targetName, self.GetWidth() - 60, self.GetHeight() - 90) self.chatLine.SetFocus() self.titleName.SetText(targetName) self.targetName = targetName self.textRenderer.SetTargetName(targetName) self.titleNameEdit.Hide() self.ignoreButton.Hide() if app.IsDevStage(): self.reportViolentWhisperButton.Show() else: self.reportViolentWhisperButton.Hide() self.acceptButton.Hide() self.gamemasterMark.Hide() self.minimizeButton.Show() if loadMessages: if constInfo.WHISPER_MESSAGES.has_key(targetName): for text in constInfo.WHISPER_MESSAGES[targetName]: chat.AppendWhisper(text[0], targetName, text[1]) in def AcceptTarget(self): at botton add: if constInfo.WHISPER_MESSAGES.has_key(name): for text in constInfo.WHISPER_MESSAGES[name]: chat.AppendWhisper(text[0], name, text[1]) And replace this method: def SendWhisper(self): text = self.chatLine.GetText() textLength = len(text) if textLength > 0: if net.IsInsultIn(text): chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.CHAT_INSULT_STRING) return net.SendWhisperPacket(self.targetName, text) self.chatLine.SetText("") if not constInfo.WHISPER_MESSAGES.has_key(self.targetName): constInfo.WHISPER_MESSAGES.update({self.targetName : [(1, ("{}: {}".format(player.GetName(), text)))]}) else: constInfo.WHISPER_MESSAGES[self.targetName].append([1, "{}: {}".format(player.GetName(), text)]) chat.AppendWhisper(chat.WHISPER_TYPE_CHAT, self.targetName, player.GetName() + ": " + text) That's all If you will find any bugs just give me some feedback.
  22. Hi guy's i tried to add VegaS Title System i did the source part it was so easy but when i open the client and select the character the game stay in loading window like this and the system is empty can someone help me with that thanx in adv
  23. https://mega.nz/file/004iwajQ#8EStyJTkwr0gwqE1HI0ZJBPExVi6mIdTOlltNPE2PoQ ]
  24. Hi there Devs, I would like to share my "little" system. If you aren't interested in the introduction/preview etc. and you just want to download it and put in to your server, just scroll down until the "[How-To] Set up" subtitle. The story Firstly let me tell this system's story. I've got an idea one year before, that it would be good if the players would be able to put their items into a "global" system where they could see the other player's items, and they could buy it for DC or gold (that time I worked with the latest vanilla core (not with the source)). Then in the following 8 days I made it (it took about 80-90 working hours). Originally the system was created for one of my friend's server. but this server has never started, and nobody used this system. After some mounts I've decided to publish it on the Hungarian forum, because it won't worth to work on it for long hours if nobody uses it and its just collecting dust on my computer. Then I've published it on the 2nd of December, 2014. After some time I've decided to translate it into English and I've got a new idea for a new feature. This feature was: the trade system (I will explain its working later). This idea inspired by one of the players (from a server where this system was able to use). He told me that it would be better if they could set the gold price via an item (what's value is very high). Then with more than 180 working hours (totally) behind my back I'm here. Overview [How-To] Set up Customizing the tradehouse Questions and Answers Notes changelog: 19th of August, 2015: I publicated the tradehouse here. my toDo list: add logging for the system (the released version don't log the actions in the tradehouse) Thanks for reading the topic, if you have any problem/remark feel free to ask it here or write me a PM. Have a good day!
  25. Hey y'all. Question has been given, let me know.
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.