Jump to content

Abel(Tiger)

Active+ Member
  • Posts

    195
  • Joined

  • Last visited

  • Days Won

    14
  • Feedback

    0%

Everything posted by Abel(Tiger)

  1. You can install it in rescue mode like this: (this is a workaround and is not the best way) 1. Buy the vps 2. Restart in rescue mode 3. get the data from email and login using winscp 4. run command: wipefs -a /dev/sdb 5. run command: wget [Hidden Content] 6. run command: xz -dc FreeBSD-12.4-RELEASE-i386.raw.xz | dd of=/dev/sdb bs=1M 7. run command: reboot vps Please note this is an already configured freebsd image and it might not be the way you will configure it. If you want to do a clean install (didn't try this yet) I guess you can just mount the install image from here [Hidden Content]
  2. Everyone has this "problem". The problem is in uiShop.py in the function def SellAttachedItem. if item.IsAntiFlag(item.ANTIFLAG_SELL): popup = uiCommon.PopupDialog() popup.SetText(localeInfo.SHOP_CANNOT_SELL_ITEM) popup.SetAcceptEvent(self.__OnClosePopupDialog) popup.Open() self.popup = popup return Add a mouseModule.mouseController.DeattachObject() before the popup appears like this: if item.IsAntiFlag(item.ANTIFLAG_SELL): mouseModule.mouseController.DeattachObject() popup = uiCommon.PopupDialog() popup.SetText(localeInfo.SHOP_CANNOT_SELL_ITEM) popup.SetAcceptEvent(self.__OnClosePopupDialog) popup.Open() self.popup = popup return
  3. what if I just edit the pack before opening the client
  4. Hello, As the title says, there is a significant exploit in the chat link system. Given that many servers utilize this system, I believe most private servers are affected by this exploit. Essentially, someone can execute any CMD command they want on a player's computer by instructing them to click on an item. How does it work? [Hidden Content] He sends this through whisper or public chat, and the player clicks on the item. The command opens notepad.exe. How can you fix it? Firstly, STOP using os.system to open links. There are special libraries for that, such as the one I will use to implement this fix. Please note that this is a straightforward fix and may not be 100% foolproof because malicious links can still be sent to open in the browser. I recommend using a link validation technique on the server side and allowing only specific links. [Hidden Content]
  5. Hello, Today I will present you two new features for affect. Real time affects: Long time ago a client of mine wanted some of his affects to be real time. As you may know the affects in metin2 are consumed only if you are online (except premium, but that is another story). The easier way to achieve this was to create a new variable inside the affect called bIsRealTime, set it to 1 and store the duration as unix time instead of seconds. You can use this new functionality by adding True at the end of the AddAffect function call and everything will work automaticaly. Update affect: In a version some time ago the official servers added this new functionality to only update an affect. This is used for the Summer Event Roulette minigame to update the collected souls count in real time in client. I didn't create a easy way to use this because it depends on what you update for the affect. Here is an example of how I used it on this event to update lApplyValue: CAffect * pAffect = pkChar->FindAffect(AFFECT_LATE_SUMMER_EVENT_BUFF); if(pAffect) { pAffect->lApplyValue += 1; pAffect->bIsUpdate = true; // Update the client SendAffectAddPacket(pkChar->GetDesc(), pAffect); } Download link: [Hidden Content]
  6. 5 loading images made full hd with Photoshop Beta new Generative AI: [Hidden Content]
  7. This is a nasty bug where a boss can throw you inside a wall if he hits you with a skill that has SKILL_FLAG_CRUSH or SKILL_FLAG_CRUSH_LONG. // char_skill.cpp // Search: if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH | SKILL_FLAG_CRUSH_LONG) && !IS_SET(pkChrVictim->GetAIFlag(), AIFLAG_NOMOVE)) { ... } // Replace the if with: if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH | SKILL_FLAG_CRUSH_LONG) && !IS_SET(pkChrVictim->GetAIFlag(), AIFLAG_NOMOVE)) { float fCrushSlidingLength = 200; if (m_pkChr->IsNPC()) fCrushSlidingLength = 400; if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH_LONG)) fCrushSlidingLength *= 2; float fx, fy; float degree = GetDegreeFromPositionXY(m_pkChr->GetX(), m_pkChr->GetY(), pkChrVictim->GetX(), pkChrVictim->GetY()); if (m_pkSk->dwVnum == SKILL_HORSE_WILDATTACK) { degree -= m_pkChr->GetRotation(); degree = fmod(degree, 360.0f) - 180.0f; if (degree > 0) degree = m_pkChr->GetRotation() + 90.0f; else degree = m_pkChr->GetRotation() - 90.0f; } GetDeltaByDegree(degree, fCrushSlidingLength, &fx, &fy); long startX = (long)(pkChrVictim->GetX()); long startY = (long)(pkChrVictim->GetY()); long endX = (long)(pkChrVictim->GetX() + fx); long endY = (long)(pkChrVictim->GetY() + fy); // We asume all positions between the start and end point are movable bool allPositionsMovable = true; // Calculate the distance between the start and end points double distance = std::sqrt((endX - startX) * (endX - startX) + (endY - startY) * (endY - startY)); // Calculate the step size for each coordinate double stepX = (endX - startX) / distance; double stepY = (endY - startY) / distance; // Check if all points on the trajectory between startX, startY and endX, endY are movable for (double i = 0; i <= distance; ++i) { double currentX = startX + i * stepX; double currentY = startY + i * stepY; long roundedX = static_cast<long>(std::round(currentX)); long roundedY = static_cast<long>(std::round(currentY)); if (!SECTREE_MANAGER::instance().IsMovablePosition(pkChrVictim->GetMapIndex(), roundedX, roundedY)) { allPositionsMovable = false; break; } } if (allPositionsMovable) { sys_log(0, "CRUSH SUCCESS! %s -> %s (%d %d) -> (%d %d)", m_pkChr->GetName(), pkChrVictim->GetName(), pkChrVictim->GetX(), pkChrVictim->GetY(), (long)(pkChrVictim->GetX()+fx), (long)(pkChrVictim->GetY()+fy)); pkChrVictim->Sync(endX, endY); pkChrVictim->Goto(endX, endY); pkChrVictim->CalculateMoveDuration(); } else { sys_log(0, "CRUSH FAIL! %s -> %s (%d %d) -> (%d %d)", m_pkChr->GetName(), pkChrVictim->GetName(), pkChrVictim->GetX(), pkChrVictim->GetY(), (long)(pkChrVictim->GetX()+fx), (long)(pkChrVictim->GetY()+fy)); } if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() == (DWORD) pkChrVictim->GetVID()) { SkillAttackAffect(pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 4, m_pkSk->szName); } else { if (allPositionsMovable) pkChrVictim->SyncPacket(); } }
  8. There is a nasty bug where sometimes some items are not loaded in the safebox. This problem occurs because the function that interprets the data from the set packet (RecvSafeBoxSetPacket) is called too early. Fix: // PythonNetworkStream.cpp // 1. Search: Set(HEADER_GC_SAFEBOX_SET, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemSet), STATIC_SIZE_PACKET)); // 1. Replace with: Set(HEADER_GC_SAFEBOX_SET, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemSet2), STATIC_SIZE_PACKET));
  9. If you use the code from the leaked Rubinum source, it does not mean you have solved the problem. Also, if you use modern coding techniques from C++ new standards, it does not mean all the code will fix itself. Your vector is still sorted, and the pointer in the map is still pointing to the old vector position. Add this code after the std::sort, and you will be surprised: for (auto it = m_map_itemTableByVnum.begin(); it != m_map_itemTableByVnum.end(); ++it) { DWORD key = it->first; // Access the key TItemTable* value = it->second; // Access the value if(value->dwVnum != key) { sys_err("Map Key %u --- Value vnum %u", key, value->dwVnum); } }
  10. Check if the FOXFS_MAGIC of the packer is the same with the FOXFS_MAGIC of the client.
  11. 1. The fix I posted is done because you can spam the database with queries. 2. With your "fix" you can delete all coments from the guild with no permissions :)))
  12. Hello, As you may already be aware, the official source code that was leaked some time ago includes code for saving window status. The code is designed to save various window attributes such as visibility, minimized status, x and y coordinates, and height. However, I have made some adjustments to only save the x and y coordinates. Please note that the logic I have utilized is different from the original planned approach. In order to ensure the proper functioning of the save function, it is imperative to address an issue where the function is called too late and the interfacehandler is null: ## 1. Open UserInterface/PythonSystem.cpp ## 1. Replace the following function: void CPythonSystem::SaveInterfaceStatus() { ... } ## 1. With: void CPythonSystem::SaveInterfaceStatus() { if(m_poInterfaceHandler) // This will always be null when this function is called PyCallClassMemberFunc(m_poInterfaceHandler, "OnSaveInterfaceStatus", Py_BuildValue("()")); FILE* File; File = fopen("interface.cfg", "wb"); if(!File) { TraceError("Cannot open interface.cfg"); return; } fwrite(m_WindowStatus, 1, sizeof(TWindowStatus) * WINDOW_MAX_NUM, File); fclose(File); } Now, let's explore how we can save window attributes, specifically for the inventory window as an example: ## 1. Go to interfaceModule.py: ## 1. Search: if self.wndInventory: self.wndInventory.Hide() self.wndInventory.Destroy() ## 1. Change it like this: if self.wndInventory: xInventory, yInventory = self.wndInventory.GetGlobalPosition() systemSetting.SaveWindowStatus(systemSetting.WINDOW_INVENTORY, False, True, xInventory, yInventory, 0) self.wndInventory.Hide() self.wndInventory.Destroy() ## 2. Now search: self.wndInventory = uiInventory.InventoryWindow() self.wndInventory.BindInterfaceClass(self) ## 2. Change it like this: self.wndInventory = uiInventory.InventoryWindow() self.wndInventory.BindInterfaceClass(self) (_, _, invX, invY, _) = systemSetting.GetWindowStatus(systemSetting.WINDOW_INVENTORY) if invX > 0 and invY > 0 and invX + self.wndInventory.GetWidth() < wndMgr.GetScreenWidth() and invY + self.wndInventory.GetHeight() < wndMgr.GetScreenHeight(): self.wndInventory.SetPosition(invX, invY) I hope you find this information useful.
  13. Hello, If you check the algorithm in char.cpp (DetermineDropMetinStone()) you will see that in some cases that is totaly posible. (for example pick randomly number 90 and folow the algorithm with the percentages from your table line 1 (40, 13, 5, 2, 0). If you look closely the last value from the table is not even used because the loop goes from 0 to 3 only (the 0 in your table). To fix this I consider the best way is to use discrete_distribution from stl: #include <random> void CHARACTER::DetermineDropMetinStone() { const int METIN_STONE_NUM = 14; static DWORD c_adwMetin[METIN_STONE_NUM] = { 28030, 28031, 28032, 28033, 28034, 28035, 28036, 28037, 28038, 28039, 28040, 28041, 28042, 28043, }; DWORD stone_num = GetRaceNum(); int idx = std::lower_bound(aStoneDrop, aStoneDrop+STONE_INFO_MAX_NUM, stone_num) - aStoneDrop; if (idx >= STONE_INFO_MAX_NUM || aStoneDrop[idx].dwMobVnum != stone_num) { m_dwDropMetinStone = 0; } else { const SStoneDropInfo & info = aStoneDrop[idx]; m_bDropMetinStonePct = info.iDropPct; std::random_device rd; std::mt19937 gen(rd()); std::discrete_distribution<> d(info.iLevelPct, info.iLevelPct + STONE_LEVEL_MAX_NUM + 1); int randomIndex = d(gen); // This will be a random number in range (0, 4) m_dwDropMetinStone = c_adwMetin[number(0, METIN_STONE_NUM - 1)] + (randomIndex * 100); } }
  14. Hello, This is because the values for hyperlink are sent as hexadecimal values (base 16) and not decimal values (base 10). You can use this converter if you want decimal values: [Hidden Content]
  15. Like this: self.item_editline.OnIMEUpdate = ui.__mem_func__(self.OnUpdateItemEditLine) And : def OnUpdateItemEditLine(self): ui.EditLine.OnIMEUpdate(self.item_editline) editLineText = self.item_editline.GetText()
  16. What is this ? I think the following video will explain everything: [Hidden Content] Please note this is experimental and might have issues. Download: [Hidden Content]
  17. If you work with Python scripts (uiscript files) you probably were in a situation where you needed to change an element property or add a new element based on a condition/binary macro. Here are two new functions I wrote to make this job easier for you: (the code is based on official root from 2018 I posted here) ## ui.py ## 1. Find: class PythonScriptLoader(object): ## 1. Add ABOVE: def FindElementByName(window, name): if isinstance(window, dict): if "name" in window and window["name"] == name: return window for key in window: found = FindElementByName(window[key], name) if found is not None: return found elif isinstance(window, list) or isinstance(window, tuple): for item in window: found = FindElementByName(item, name) if found is not None: return found return None def ChangeElementProperty(window, name, propertyName, propertyValue): element = FindElementByName(window, name) if element: if propertyName in element: element[propertyName] = propertyValue ## 2. Search: self.ScriptDictionary["LOCALE_PATH"] = app.GetLocalePath() ## 2. Add after: self.ScriptDictionary["FindElementByName"] = FindElementByName self.ScriptDictionary["ChangeElementProperty"] = ChangeElementProperty And this is how you can use it: ## The example is based on minimap.py from the official uiscript ## 1. BEFORE: window["children"][0]["children"] = window["children"][0]["children"] + [ { "name" : "InGameEventButton", ... },] ## 1. AFTER: openWindow = FindElementByName(window, "OpenWindow") if openWindow: openWindow["children"] = openWindow["children"] + [ { "name" : "InGameEventButton", ... },] ## 2. BEFORE: window["children"][0]["children"][5]["x"] = 0 window["children"][0]["children"][5]["y"] = 57 ## 2. AFTER: ChangeElementProperty(window, "AtlasShowButton", "x", 0) ChangeElementProperty(window, "AtlasShowButton", "y", 57)
  18. Post ClientManagerPlayer.cpp from the db. In most cases, your problem stems from this file.
  19. The issue is occurring because the pointer to the event function is null, whereas the info is not null. To resolve this issue, you can simply check whether the pointer to the function is null or not. if (!the_event->func) continue; new_time = (the_event->func) (get_pointer(the_event), processing_time); Because the event functions are global functions, there are not many ways to get a null pointer. Therefore, you should check the following (although there may be other ways to get a null pointer, one of the following is likely your case): - Check your event code and ensure that you are not explicitly setting the "func" pointer to null. - Check whether you are calling the event_create function like this: event_create(nullptr, info, PASSES_PER_SEC(1));
  20. Nice work Here is a token list I use: ["\t", "\r", "\n", ",", " ", "=", "[", "]", "+", "-", "<", ">", "~", "!", ".", "(", ")", "*", "{", "}", "/"] So you can use: (and more) local drops = {DROP1, DROP2, DROP3} local count = COUNT1+COUNT2
  21. If you want to bulk crop here is a little python script I did some time ago:
  22. Try this: UserInterface -> Properties -> Manifest Tool -> Input and Output -> DPI Awareness -> High DPI Aware Recompile the binary and try again.
  23. Good job. For what I've seen in the metin2 comunity a lot of people doesn't care about these values (they leave bosses without skills on live servers and so on). The damage multiply column have also wrong values. Example: if the monster have 5.3 damage multiuply when you unpack you will get 5. I personally changed the unpack function to read those values from a file at unpack. Here is the code with all variables I found with wrong values or missing (maybe it can help you): The database file too:
  24. BYTE bMaxItemSize = 1; const CSpecialItemGroup* pGroup = ITEM_MANAGER::instance().GetSpecialItemGroup(dwBoxVnum); if (pGroup) { for (int i = 0; i < pGroup->GetGroupSize(); i++) { const TItemTable* itemTable = ITEM_MANAGER::instance().GetTable(pGroup->GetVnum(i)); if(itemTable) { if(itemTable->bSize > bMaxItemSize) { bMaxItemSize = itemTable->bSize; if(bMaxItemSize == 3) break; } } } } if(GetEmptyInventory(bMaxItemSize) == -1) { ChatPacket(CHAT_TYPE_INFO, "You need %d perpendicular slots in your inventory to open the Chest.", bMaxItemSize); return false; } Place it in char_item.cpp inside case ITEM_GIFTBOX: after dwBoxVnum. (there are other cases for other types of chests, this instruction is only for giftbox type). Also I think the max size from the chest must be a info loaded when the special group is loaded and saved in a variable. The code I posted for you increases the time complexity of the function and it's not a good ideea
×
×
  • 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.