Jump to content

Search the Community

Showing results for tags 'fix'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


  • Metin2 Dev
    • Announcements
  • Community
    • Member Representations
    • Off Topic
    • Metin2
  • Private Servers
    • Private Servers
  • Help Center
    • Questions & Answers
    • File Requests
  • Metin2 Development
    • Basic Tutorials / Beginners
    • Guides & HowTo
    • Binaries
    • Programming & Development
    • Web Development & Scripts / Systems
    • Tools & Programs
    • Maps
    • Quests
    • 3D Models
    • 2D Graphics
    • Operating Systems
    • Miscellaneous
  • Marketplace
    • Searching
  • Miscellaneous
    • Drafts
    • Trash
    • Archive
    • Temporary
    • Metin2 Download
  • Forum Bureau of Investigation's Forum

Product Groups

  • Header & Footer
  • Topic & Forum
  • Pack
  • Small Advertisement
  • Large Advertisement


Find results in...

Find results that contain...

Date Created

  • Start


Last Updated

  • Start


Filter by number of...


  • Start





Game Server

My Message








  1. Hello fine people, A little bit of context, recently we have been talking with @ msnas about doing an action upon server closure. Which is what the db/game should be doing by default when it exits MainLoop. However, I noticed that because of the way I shutdown my core, I wasn't receiving the right signal and was exiting the server before I can do any other actions. He then told me that he noticed this issue a long time ago because it was also present for him, only on Windows. Well, after a little bit of digging, I noticed that the signal was simply not handled on a Windows server. So here is a rudimentary (but working) signal management for Windows. I guess you can still rewrite the whole thing using more modern stuff like boost signal. But so far so good. Please not that you should close your db / game using CTRL+C or CTRL+BREAK if you want to gracefully shutdown. As a forced closure (closing the window, shutdown or task manager) would still not allow for the server to properly close, notably because it doesn't give enough grace time. So, here is the fix: Have a nice day!
  2. The game loop freezes when dragging/resizing the window. This is an attempt at fixing that. We'll open a thread and call "Process()" from there when we receive WM_ENTERSIZEMOVE and then shut it down when receiving WM_EXITSIZEMOVE, continuing our main game loop. There's also the option of creating a timer when entering WM_ENTERSIZEMOVE, calling "Process()" when receiving WM_TIMER and killing it when receiving WM_EXITSIZEMOVE, but there's quite a big delay till the timer starts and it's also not very reliable, so.. Note: This is HIGHLY experimental, and it might result in data races and crash the client. Even though I tested it as good as I could and tried to make sure the main thread's loop doesn't resume before ending the future, it's still a possibility for that to happen when you least expect it. Video: Code: Github or M2DL Many thanks to @limefor taking the time to test it. Good luck!
  3. Premise: I don't know whether you have this system from a free release elsewhere or you bought it and I have no idea if they are the same or not (even though the same version can be found in the first server files of Rubinum). There are two issues I was concerned with: -creating an item when you request the infos -putting the mob data dictionary in constinfo The first is if you have, in CreateDropItemVector function something like this: if (item) vec_item.push_back(item); So, we create an item and we do nothing with it, staying in memory for no purpose. Instead, we can use a vector of pairs. Open item_manager.h Replace bool CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item); with: bool CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item); Now, open item_manager.cpp Replace bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item) With bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item) and remove LPITEM item = NULL; and edit the function accordingly. EX for common_drop_item, replace: std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin(); while (it != g_vec_pkCommonDropItem[bRank].end()) { const CItemDropInfo & c_rInfo = *(it++); if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd) continue; TItemTable * table = GetTable(c_rInfo.m_dwVnum); if (!table) continue; item = NULL; if (table->bType == ITEM_POLYMORPH) { if (c_rInfo.m_dwVnum == pkChr->GetPolymorphItemVnum()) { item = CreateItem(c_rInfo.m_dwVnum, 1, 0, true); if (item) item->SetSocket(0, pkChr->GetRaceNum()); } } else item = CreateItem(c_rInfo.m_dwVnum, 1, 0, true); if (item) vec_item.push_back(item); } with std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin(); while (it != g_vec_pkCommonDropItem[bRank].end()) { const CItemDropInfo & c_rInfo = *(it++); if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd) continue; TItemTable * table = GetTable(c_rInfo.m_dwVnum); if (!table) continue; if(c_rInfo.m_dwVnum > 70103 && c_rInfo.m_dwVnum < 70108) { if (c_rInfo.m_dwVnum != pkChr->GetPolymorphItemVnum()) { continue; } } vec_item.push_back(std::make_pair(c_rInfo.m_dwVnum, 1)); } (edit 70103 and 70108 accordingly with your Polymorph Marble vnums The whole function should look like this: bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item) { if (pkChr->IsPolymorphed() || pkChr->IsPC()) { return false; } int iLevel = pkKiller->GetLevel(); BYTE bRank = pkChr->GetMobRank(); std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin(); while (it != g_vec_pkCommonDropItem[bRank].end()) { const CItemDropInfo & c_rInfo = *(it++); if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd) continue; TItemTable * table = GetTable(c_rInfo.m_dwVnum); if (!table) continue; if(c_rInfo.m_dwVnum > 70103 && c_rInfo.m_dwVnum < 70108) { if (c_rInfo.m_dwVnum != pkChr->GetPolymorphItemVnum()) { continue; } } vec_item.push_back(std::make_pair(c_rInfo.m_dwVnum, 1)); } // Drop Item Group { itertype(m_map_pkDropItemGroup) it; it = m_map_pkDropItemGroup.find(pkChr->GetRaceNum()); if (it != m_map_pkDropItemGroup.end()) { typeof(it->second->GetVector()) v = it->second->GetVector(); for (DWORD i = 0; i < v.size(); ++i) { vec_item.push_back(std::make_pair(v[i].dwVnum, v[i].iCount)); } } } // MobDropItem Group { itertype(m_map_pkMobItemGroup) it; it = m_map_pkMobItemGroup.find(pkChr->GetRaceNum()); if ( it != m_map_pkMobItemGroup.end() ) { CMobItemGroup* pGroup = it->second; if (pGroup && !pGroup->IsEmpty()) { const CMobItemGroup::SMobItemGroupInfo& info = pGroup->GetOne(); vec_item.push_back(std::make_pair(info.dwItemVnum, info.iCount)); } } } // Level Item Group { itertype(m_map_pkLevelItemGroup) it; it = m_map_pkLevelItemGroup.find(pkChr->GetRaceNum()); if ( it != m_map_pkLevelItemGroup.end() ) { if ( it->second->IsInLevelRange((DWORD)iLevel) ) { typeof(it->second->GetVector()) v = it->second->GetVector(); for ( DWORD i=0; i < v.size(); i++ ) { DWORD dwVnum = v[i].dwVNum; vec_item.push_back(std::make_pair(dwVnum, v[i].iCount)); } } } } // ETC DropItem if (pkChr->GetMobDropItemVnum()) { itertype(m_map_dwEtcItemDropProb) it = m_map_dwEtcItemDropProb.find(pkChr->GetMobDropItemVnum()); if (it != m_map_dwEtcItemDropProb.end()) { vec_item.push_back(std::make_pair(pkChr->GetMobDropItemVnum(), 1)); } } //Metin if (pkChr->IsStone()) { if (pkChr->GetDropMetinStoneVnum()) { vec_item.push_back(std::make_pair(pkChr->GetDropMetinStoneVnum(), 1)); } } return vec_item.size(); } Note: level item group and drop item group might be different because in my files i have a level_limit_max token and, for drop_item_group, I don't expect to put a Polymorph Marble. Also it's missing the buyertheifgloves item group, but you can clearly see the pattern and modify accordingly. Edit : I found a bug in the mob_drop_item infos. If you don't know, with the type "kill", you drop only one of the items you put in the list, so the info drop, well, will show only one, and then another, and another one, every time you reopen it. To fix this issue, go to item_manager.h and search for: const SMobItemGroupInfo& GetOne() const { return m_vecItems[GetOneIndex()]; } Add after: std::vector<std::pair<int,int>> GetVector() { std::vector<std::pair<int,int>> item_list; for(auto &x : m_vecItems) item_list.emplace_back(std::make_pair(x.dwItemVnum,x.iCount)); return item_list; } then, in item_manager.cpp, instead of: if (pGroup && !pGroup->IsEmpty()) { const CMobItemGroup::SMobItemGroupInfo& info = pGroup->GetOne(); vec_item.push_back(std::make_pair(info.dwItemVnum, info.iCount)); } replace it with: if (pGroup && !pGroup->IsEmpty()) { auto vec_items = pGroup->GetVector(); for(auto &x : vec_items) vec_item.push_back(std::make_pair(x.first,x.second)); } Side Note (how to add drop of the Stones from Metins server side): Before return vec_item.size(); you can also organize the vector as you want or create another one with a certain structure, like, first the common drop items, then the advance items or whatever, you can also clear it for what I care. Me, for example, I sort it to have all the vnums from the smallest to the largest. You can do that by: std::sort(vec_item.begin(), vec_item.end(), std::less<std::pair<int,int> >()); Now, let's go to input_main.cpp Remove: LPITEM pkInfoItem; We don't use an item anymore, remember? So now replace: static std::vector<LPITEM> s_vec_item; with: static std::vector<std::pair<int,int> > s_vec_item; Then replace: if (ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item) && (m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone())) with: if ((m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone()) && ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item)) This way also, if you target a player or a NPC, it will not trigger the CreateDropItemVector function. Remove everything inside the function if (s_vec_item.size() == 0); //this is useless else if (s_vec_item.size() == 1) //even more useless Just put: for(std::vector<std::pair<int,int> >::const_iterator iter = s_vec_item.begin(); iter != s_vec_item.end();++iter) { pInfo.dwVID = m_pkChrTarget->GetVID(); pInfo.race = m_pkChrTarget->GetRaceNum(); pInfo.dwVnum = iter->first; pInfo.count = iter->second; ch->GetDesc()->Packet(&pInfo, sizeof(TPacketGCTargetInfo)); } That's it. Now the constinfo issue. So, open constinfo.py and remove: import app if app.ENABLE_SEND_TARGET_INFO: MONSTER_INFO_DATA = {} Open uitarget.py and after the imports add: MONSTER_INFO_DATA = {} and replace whatever declaration with constinfo.MONSTER_INFO_DATA with just MONSTER_INFO_DATA Now open game.py. Even here, replace constinfo.MONSTER_INFO_DATA with uiTarget.MONSTER_INFO_DATA Last little fix, open intrologin.py Search for: self.stream.SetLoginInfo(id, pwd) the one in: def __OnClickLoginButton(self): add before uiTarget.MONSTER_INFO_DATA.clear() This should fix the issue when you don't close the client and, if you change the drop, you still see the older drop. Now you may ask, why all the other servers who may have this have never crashed or imploded? To be fair, I don't know and I don't care. Just know, if your system is similar to this, be aware of the issues and that this is one but definitely not the only fix possible.
  4. You cannot drop items from equipped items. Why? Why there are bugs when throwing equipped items -------------------------------BUG 1-------------------------------------- If you have sura/warrior aura and throw 1 item the aura is still there if (iWearCell == WEAR_WEAPON) { if (IsAffectFlag(AFF_GWIGUM)) { RemoveAffect(SKILL_GWIGEOM); } if (IsAffectFlag(AFF_GEOMGYEONG)) { RemoveAffect(SKILL_GEOMKYUNG); } } Fix bypass for this code You have aura ? drop items and this aura is work -------------------------------END-------------------------------------- -------------------------------BUG 2-------------------------------------- If your serverfiles have a problem with HP/SP and drop object the HP stays In some server files there are even more problems -------------------------------END-------------------------------------- Tested and works perfectly, I have player block for exploiting quite a few bugs with items To fix this solves every problem!!!! thanks you 1) OPEN input_main.cpp search: void CInputMain::ItemDrop2(LPCHARACTER ch, const char * data) Change with: #ifdef FIX_DROP_ITEMSSS void CInputMain::ItemDrop2(LPCHARACTER ch, const char * data) { TPacketCGItemDrop2 * pinfo = (TPacketCGItemDrop2 *) data; LPITEM pkItem = ch->GetItem(pinfo->Cell); if (!pkItem || !ch) return; if (pkItem->IsEquipped() == true) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("bug fixes items IsEquipped not drop.")); return; } if (pinfo->gold > 0) ch->DropGold(pinfo->gold); else ch->DropItem(pinfo->Cell); } #else void CInputMain::ItemDrop2(LPCHARACTER ch, const char * data) { TPacketCGItemDrop2 * pinfo = (TPacketCGItemDrop2 *) data; if (!ch) return; if (pinfo->gold > 0) ch->DropGold(pinfo->gold); else ch->DropItem(pinfo->Cell); } #endif 2) OPEN service.h add #define FIX_DROP_ITEMSSS ------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- OPEN CLIENT ROOT OPEN: game.py search: if player.SLOT_TYPE_INVENTORY == attachedType: dropItemIndex = player.GetItemIndex(attachedItemSlotPos) item.SelectItem(dropItemIndex) dropItemName = item.GetItemName() ## Question Text questionText = localeInfo.HOW_MANY_ITEM_DO_YOU_DROP(dropItemName, attachedItemCount) ## Dialog itemDropQuestionDialog = uiCommon.QuestionDialog() itemDropQuestionDialog.SetText(questionText) itemDropQuestionDialog.SetAcceptEvent(lambda arg=True: self.RequestDropItem(arg)) itemDropQuestionDialog.SetCancelEvent(lambda arg=False: self.RequestDropItem(arg)) itemDropQuestionDialog.Open() itemDropQuestionDialog.dropType = attachedType itemDropQuestionDialog.dropNumber = attachedItemSlotPos itemDropQuestionDialog.dropCount = attachedItemCount self.itemDropQuestionDialog = itemDropQuestionDialog constInfo.SET_ITEM_QUESTION_DIALOG_STATUS(1) change with: if player.SLOT_TYPE_INVENTORY == attachedType: if player.IsEquipmentSlot(attachedItemSlotPos): self.stream.popupWindow.Close() self.stream.popupWindow.Open(localeInfo.EXCHANGE_FAILURE_EQUIP_DROPPP, 0, localeInfo.UI_OK) else: dropItemIndex = player.GetItemIndex(attachedItemSlotPos) item.SelectItem(dropItemIndex) dropItemName = item.GetItemName() questionText = localeInfo.HOW_MANY_ITEM_DO_YOU_DROP(dropItemName, attachedItemCount) itemDropQuestionDialog = uiCommon.QuestionDialog() itemDropQuestionDialog.SetText(questionText) itemDropQuestionDialog.SetAcceptEvent(lambda arg=True: self.RequestDropItem(arg)) itemDropQuestionDialog.SetCancelEvent(lambda arg=False: self.RequestDropItem(arg)) itemDropQuestionDialog.Open() itemDropQuestionDialog.dropType = attachedType itemDropQuestionDialog.dropNumber = attachedItemSlotPos itemDropQuestionDialog.dropCount = attachedItemCount self.itemDropQuestionDialog = itemDropQuestionDialog constInfo.SET_ITEM_QUESTION_DIALOG_STATUS(1) OPEN LOCALE CLIENT: locale_game.txt add EXCHANGE_FAILURE_EQUIP_DROPPP Hello friends i am sorry this bug is closed :( . OPEN LOCALE SERVER: locale_string.txt add "bug fixes items IsEquipped not drop." "bug fixes items IsEquipped not drop." You can now take a test Put on a weapon or armor and try to throw them down.
  5. Hello, So I have shared this fix with someone a few days ago and saw this morning that martysama has published it on it's blog. I let you know before kids with "private" access to this start playing with it. (I do not have acces to martysama's blog member posts and do not know who does) The "pc_change_name" function has an exploitable item duplication bug. The fix is simple, in "pc_change_name" replace this code: db_clientdesc->DBPacketHeader(HEADER_GD_FLUSH_CACHE, 0, sizeof(DWORD)); db_clientdesc->Packet(&pid, sizeof(DWORD)); with this: if (!CHARACTER_MANAGER::instance().FlushDelayedSave(ch)) { ch->SaveReal(); } I will edit this post to add details on how and why later on to avoid kids playing with it before it's patched on majority of servers. Regards,
  6. Download Click here to check the repository ↗ I think everyone already knows this problem, this happens because the client is just rendering the text pointer of the normal chat window, when opening the chat-log window, it just changes the position of the rendering text. Before After
  7. I noticed a few days ago that the inventory class was not getting freed upon rewarp. Circular dependency was causing the garbage collector to fail. The fix was fairly simple, so I fixed it and decided to share it with you. So here it is:
  8. [Hidden Content] -Items Blood Pill- Fixed the Blood Pill item Use by right clicking the item When a Blood Pill is used you will notice a minus sign (-) to the left side of your character's screen . Clicking that takes one point from the stat you choose and you can add it to another stat later . #1 Bug If you have x94 stat and hit 4 pills and spend 4 stat at the end when you add another stat then the stat doubles from 90 to 160 #2 Bug In some serverfiles it stat can be as high as -90 -80 -70 -30 -10 1) OPEN: cmd_general.cpp 2) Search: ACMD(do_stat_minus) 3) Change with: ACMD(do_stat_minus) { char arg1[256]; one_argument(argument, arg1, sizeof(arg1)); if (!*arg1) return; if (ch->IsPolymorphed()) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("둔갑 중에는 능력을 올릴 수 없습니다.")); return; } if (ch->GetPoint(POINT_STAT_RESET_COUNT) <= 0) return; if (!strcmp(arg1, "st")) { if (ch->GetRealPoint(POINT_ST) <= JobInitialPoints[ch->GetJob()].st) return; BYTE stpoints = 0; stpoints = POINT_ST; ch->SetRealPoint(stpoints, ch->GetRealPoint(stpoints) - 1); ch->SetPoint(stpoints, ch->GetPoint(stpoints) - 1); ch->ComputePoints(); ch->PointChange(stpoints, 0); } else if (!strcmp(arg1, "dx")) { if (ch->GetRealPoint(POINT_DX) <= JobInitialPoints[ch->GetJob()].dx) return; BYTE dxpoints = 0; dxpoints = POINT_DX; ch->SetRealPoint(dxpoints, ch->GetRealPoint(dxpoints) - 1); ch->SetPoint(dxpoints, ch->GetPoint(dxpoints) - 1); ch->ComputePoints(); ch->PointChange(dxpoints, 0); } else if (!strcmp(arg1, "ht")) { if (ch->GetRealPoint(POINT_HT) <= JobInitialPoints[ch->GetJob()].ht) return; BYTE htpoints = 0; htpoints = POINT_HT; ch->SetRealPoint(htpoints, ch->GetRealPoint(htpoints) - 1); ch->SetPoint(htpoints, ch->GetPoint(htpoints) - 1); ch->ComputePoints(); ch->PointChange(htpoints, 0); ch->PointChange(POINT_MAX_SP, 0); } else if (!strcmp(arg1, "iq")) { if (ch->GetRealPoint(POINT_IQ) <= JobInitialPoints[ch->GetJob()].iq) return; BYTE iqpoints = 0; iqpoints = POINT_IQ; ch->SetRealPoint(iqpoints, ch->GetRealPoint(iqpoints) - 1); ch->SetPoint(iqpoints, ch->GetPoint(iqpoints) - 1); ch->ComputePoints(); ch->PointChange(iqpoints, 0); ch->PointChange(POINT_MAX_HP, 0); } else return; ch->PointChange(POINT_STAT, + 1); ch->PointChange(POINT_STAT_RESET_COUNT, - 1); ch->ComputePoints(); }
  9. When cancelling server timers a core will crash as it is removing a timer from the map and increasing the iterator twice by calling erase() function and afterwards increasing it once again when entering a new cycle of loop. One way of solving this issue is to first collect the timers which must be removed and clean them up at the end. You can also add a simple counter which is increased at each end of the loop's cycle and remove the timer directly from the first loop by specifying the position with the counter itself. Note that this issue seems to appear after upgrading code to C++11 or higher. Replace the following function inside questmanager.cpp: void CQuestManager::CancelServerTimers(DWORD arg) { vector<pair<string, DWORD>> ServerTimersToDelete; for (const auto& kv : m_mapServerTimer) { if (kv.first.second == arg) { LPEVENT event = kv.second; event_cancel(&event); ServerTimersToDelete.push_back(kv.first); } } // Delete all the required server timers for (const auto &timer : ServerTimersToDelete) m_mapServerTimer.erase(timer); // Clean up ServerTimersToDelete.clear(); }
  10. the sectree in metin2 is scary and not even the old korean wizards know what the fuck is happening over there so I took a deep breath and started to change some stuff around. While doing so I found something strange, after around 2.5-5k mobs the ram is exploding and its getting extremely laggy. I noticed that every `server-source/game/src/entiy.h` -> `CEntity` has its own `ENTITY_MAP m_map_view;` But what is a ENTITY_MAP? In short, it's a container of every currently-visible entity. This is useful to see things but has a small issue: every entity sees every entity in proximity. In other words: the amount of currently tracked entities is growing exponentially. 1 mob -> 0 entries 2 mobs -> 1 entry per mob / 2 entries 3 mobs -> 2 entries per mob / 6 entries 4 mobs -> 3 entries per mob / 12 entries 2500 mobs -> 2499 entries per mob / 6247500 entries This is not good, but we have to see things, we can't just remove it completely. Introducing, my pseudo fix: search for `CFuncViewInsert` in `server-source/game/src/entity_view.cpp` my change would be to avoid calling `m_me->ViewInsert(ent);` every time, so it would look something like that [Hidden Content] We check if both entities are of the "character" type and if so we cast them to `LPCHARACTERS`. If the current view is of a player, he has to see everything if the current view is of a NPC, he has to see only players This fix has some issues, with newer versions of the game there are a few mobs that heal each other - I haven't tested those neither have I tested this change throughout, but I'm fairly certain that it will work. If there is a problem you could add a type/subtype check or even hardcode the vnums since I think that there are only a handful of NPCs that have to see other NPCs (i.e. jotun) good luck Thanks to everyone from sura-head. esp. @CYN3 and @HelloBrightness ( Amas ).
  11. The client is freezing for a while(anywhere from a few ms to multiple seconds for slower PCs) when meeting a new NPC/enemy for the first time because that's when it registers, creates and loads the entity. Note: This is only a partial fix. Why is it partial? Because it only loads the static mobs and NPCs available in the map(npc, regen, boss, group and group_group), so it won't fix the problem for dynamic entities, like pets and mounts. It can be extended to load those as well, of course, but you'll have to take care of that on your own. Variant: A way of completely fixing this would be to create the instances and load their files after registering the path(in root->playerSettingModule->LoadGameNPC), BUT that would slow down the initial loading quite a bit, since it'll load ALL mobs/npcs/pets/mounts, even if most of them will never be used. There's advantages and drawbacks to both methods. Choose for yourself. Here's the link: [or] [Hidden Content] [or] [Hidden Content] Good luck! - Amun Edit: UPDATE 08/12/2022: 1. Refactored to use unordered_set from the beginning instead of creating a vector, then filtering unique entities by creating a set, and then moving them back to a vector. 2. Extended to load the full spectrum of entities(both static and dynamic), except for PC(which are loaded in loading phase), WARP, GOTO, and DOOR. They can be included by altering this block in char_manager.cpp: #ifdef ENABLE_ENTITY_PRELOADING //@Amun: or be specific, like if(ch->ispet, mount, whatever) if (!ch->IsPC() && !ch->IsGoto() && !ch->IsWarp() && !ch->IsDoor()) SECTREE_MANAGER::Instance().ExtendPreloadedEntitiesMap(lMapIndex, pkMob->m_table.dwVnum); #endif
  12. Yo! If you have an item with a limit type LIMIT_REAL_TIME_START_FIRST_USE that has been used at one point and put it inside the safe-box, it will not be removed from the game until you pull it out of the safe-box and perform a teleport. item.h // Search: protected: friend class CInputDB; // Add below: friend class CHARACTER; char.cpp // Search: void CHARACTER::LoadSafebox(int iSize, DWORD dwGold, int iItemCount, TPlayerItem * pItems) { [...] if (!m_pkSafebox->Add(pItems->pos, item)) { M2_DESTROY_ITEM(item); } else item->SetSkipSave(false); // Modify to: if (!m_pkSafebox->Add(pItems->pos, item)) { M2_DESTROY_ITEM(item); } else { item->OnAfterCreatedItem(); item->SetSkipSave(false); }
  13. Sup bois and grils I still see a lot of people not being able to select the character when using a local server for friends or VPS behind a internal router or firewall. Disclaimer: This code wasn't made by me, it was originally posted on the metin2dev's French brother Funky-Emu by @Veltor88. On service.h add this: #define ENABLE_PROXY_IP On char.cpp: After: p.lAddr = lAddr; Add: #ifdef ENABLE_PROXY_IP if (!g_stProxyIP.empty()) p.lAddr = inet_addr(g_stProxyIP.c_str()); #endif Like this: On config.cpp: After (or anywhere within the global variables definition): char g_szInternalIP[16] Add: #ifdef ENABLE_PROXY_IP std::string g_stProxyIP = ""; #endif Like this: Still on config.cpp: After: the last TOKEN Add: #ifdef ENABLE_PROXY_IP TOKEN("proxy_ip") { g_stProxyIP = value_string; } #endif Like this: On config.h: After (or anywhere in the file): extern char g_szInternalIP[16]; Add: #ifdef ENABLE_PROXY_IP extern std::string g_stProxyIP; #endif Like this: On desc.cpp: Within the function: void DESC::SendLoginSuccessPacket() Inside: for (int i = 0; i < PLAYER_PER_ACCOUNT; ++i) Add in the beginning: #ifdef ENABLE_PROXY_IP if (!g_stProxyIP.empty()) rTable.players[i].lAddr=inet_addr(g_stProxyIP.c_str()); #endif Like this: On input_db.cpp: Withing the function: bool GetServerLocation(TAccountTable & rTab, BYTE bEmpire) After: rTab.players[i].szName); Add: #ifdef ENABLE_PROXY_IP if (!g_stProxyIP.empty()) rTab.players[i].lAddr=inet_addr(g_stProxyIP.c_str()); #endif Still on input_db.cpp and within the same function: After: struct in_addr in; Add: #ifdef ENABLE_PROXY_IP if (!g_stProxyIP.empty()) rTab.players[i].lAddr=inet_addr(g_stProxyIP.c_str()); #endif Like this: Still on the same file: Within the function: void CInputDB::PlayerCreateSuccess(LPDESC d, const char * data) After: pack.player = pPacketDB->player; Add: #ifdef ENABLE_PROXY_IP if (!g_stProxyIP.empty()) pack.player.lAddr=inet_addr(g_stProxyIP.c_str()); #endif Like this: Done on the source, you can compile. On every channel config, add this: BIND_IP: <Private IPv4 IP> (it will be something within a private IP class. Check the IP of the machine using the "ifconfig" command) PROXY_IP: <Public IPv4 IP> (check out something like [Hidden Content]) Private IPv4 classes: On some sources (like the one from vanilla, BIND_IP was renamed to PUBLIC_IP, just check which TOKEN is responsible to set the value on the variable g_szPublicIP. You will need to open the ports on your router, even for yourself on localhost with the virtual machine because the game server will try to "proxy" you through the public IP you defined. This wasn't tested using a VPN, like Hamachi or ZeroTier.
  14. open : char_affect.cpp Find : char_affect.cpp Find in : if (AFFECT_REVIVE_INVISIBLE != pkAff->dwType && AFFECT_MOUNT != pkAff->dwType) Replace with this : if (AFFECT_REVIVE_INVISIBLE != pkAff->dwType && AFFECT_MOUNT != pkAff->dwType && SKILL_TANHWAN != pkAff->dwType) Proof : [Hidden Content]
  15. Hello, today I saw that people are using the old collsion rendering code from ymir and I think its disgusting and useless to be honest. The new method is using wire framed 3d meshes instead of weird circles that look like a psychosis. You enable the collision rendering by opening the console and run the "collision" command Before: After: [Hidden Content] Once again thanks to everyone from sura-head. esp @HelloBrightness ( Amas ) and @CYN3. and thanks to @ TAUMPfor testing it.
  16. Disclaimer: This fix is intended for correct server data. I really advise you not to ban any player based on data gathered by this, but just to take into consideration that one player appearing a lot in this list (in different maps) is suspicious. I strongly recommend you to inspect which mobs are problematic for false positives, then to fix them. Hello everyone, While working on some clientside thing. I noticed one big exploit that I was able to reproduce. Long story short, I was basically able to extend any collision and be able to reproduce (in a makeshift dirty way) the famous "kill aura" or whatever you call it. Basically allowing you to hit monsters from really far away without any problem. That's one of the most common thing the bots are using. Videos of the bug: Spoiler Videos of the fix: Spoiler The fix Spoiler [Hidden Content] Have a nice day!
  17. This vulnerability should affect every server. You can duplicate item rewards, and also crash the server through dangling pointers. The danger of this bug escalates to how many custom systems, and how many crafting quests (for example, the vitality ore quest, not the cube system) you have in your server. How to trigger it: Any quest that uses select & wait, and the item lua module after that is vulnerable. After the server uses select() or wait(), the player's quest state is suspended. After the player replies using the CG packet, the quest state is recovered. So what's wrong with it? It doesn't verify if the stored quest item ptr expired. You basically need to destroy the selected item ptr in order to dupe the rewards of the quest. After some tries, you may get a core crash in the game. (dangling pointers often cause crashes only after that memory sector has been rewritten) In my files, I've checked (since several years ago) if the quest state was suspended for the the default windows such as exchange, cube, shop. This bug can work very easily on offline shops or other new systems that don't check that. After the select() or wait() is called, you send the selected item to the (e.g.) offlineshop system window. It will delete the item ptr in the game. Now, you can press "Ok" on the quest, and the quest will proceed as if the item still existed. The item still exists in the offlineshop, but not the item ptr anymore. The item won't be deleted by the quest even after item.remove() is called. This is the fix: diff --git a/s3ll_server/Srcs/Server/game/src/char.cpp b/s3ll_server/Srcs/Server/game/src/char.cpp index 0ea307fa..65b1dd65 100644 --- a/s3ll_server/Srcs/Server/game/src/char.cpp +++ b/s3ll_server/Srcs/Server/game/src/char.cpp @@ -303,7 +303,10 @@ void CHARACTER::Initialize() m_dwQuestNPCVID = 0; m_dwQuestByVnum = 0; - m_pQuestItem = NULL; + m_dwQuestItemVID = 0; m_dwUnderGuildWarInfoMessageTime = get_dword_time()-60000; @@ -6123,33 +6126,37 @@ LPCHARACTER CHARACTER::GetQuestNPC() const void CHARACTER::SetQuestItemPtr(LPITEM item) { - m_pQuestItem = item; + m_dwQuestItemVID = (item) ? item->GetVID() : 0; } void CHARACTER::ClearQuestItemPtr() { - m_pQuestItem = NULL; + m_dwQuestItemVID = 0; } LPITEM CHARACTER::GetQuestItemPtr() const { - return m_pQuestItem; + if (!m_dwQuestItemVID) + return nullptr; + return ITEM_MANAGER::Instance().FindByVID(m_dwQuestItemVID); } diff --git a/s3ll_server/Srcs/Server/game/src/char.h b/s3ll_server/Srcs/Server/game/src/char.h index cc4da2bb..74b3470e 100644 --- a/s3ll_server/Srcs/Server/game/src/char.h +++ b/s3ll_server/Srcs/Server/game/src/char.h @@ -1674,9 +1674,9 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider private: DWORD m_dwQuestNPCVID; DWORD m_dwQuestByVnum; - LPITEM m_pQuestItem; + DWORD m_dwQuestItemVID{}; // @fixme304 (LPITEM -> DWORD) // Events To unlock the client in order to move your windows with a quest open, you edit this from interfacemodule.py: You can even Hide the TopBar and BottomBar from uiQuest.py for a better result. Important: after this fix, the item ptr may be nullptr after they press enter, so you need to check if the item ptr is still valid by using this function: ALUA(item_is_available) { auto item = CQuestManager::instance().GetCurrentItem(); lua_pushboolean(L, item != nullptr); return 1; } ... { "is_available", item_is_available }, // [return lua boolean] By simply doing: when 169.take begin local s1=select("Yes", "No") if s1==2 then return end if not item.is_available() then return end end If you want to tryhard, and be sure that the item ptr didn't get swapped, you can do as following via quest:
  18. I think some of you faced this bug when you had a monster with more then 21 million HP: the gauge showed 0% even when it was on full HP. It's because when the server calculates the HP percentage is like MINMAX(0, (m_pkChrTarget->GetHP() * 100) / m_pkChrTarget->GetMaxHP(), 100), so 21 million will overflow when it's multiplied by 100. The fix is really quick and easy: [Hidden Content]
  19. Hi guys, A guy reported to me a weird bug about shamans w/m which are skipping collision when they are too fast to attack. On default source files it is still an unresolved bug which appear when the shaman's attack speed is more than 145/150. here a video which show how it is not getting the damage text for each hit on the stone. here the FIX. ATTENTION: Since the problem is the InvisibleTime on Attack.msa which it is too high, we could think to reduce it without need to edit nothing in our source (and it may be more efficient), but honestly i preferred to make a function which calculating the "adjustment" of the invisible time using the speed attack to don't risk to get the reversed problem (2 damage on 1 hit when the attack speed is low) feel free to use one of the two options.
  20. Download Other Mirros Download Here (GitHub) Download Here (Mega) Hey M2Dev, I want to share something small but visually appreciated by players that are paying attention to details. What I'm sharing with you today will solve the issue of item tooltips with large strings, these strings overflow the width of the tooltip causing the text to hang out of the edge, especially when metin stones are attached to your weapon or armor. BEFORE PREVIEW AFTER PREVIEW .
  21. An annoying bug which need a fix. Gif with the problem (from ѕeмa™) : // PythonApplicationProcedure.cpp // After: if (m_isWindowFullScreenEnable) { __MinimizeFullScreenWindow(hWnd, m_dwWidth, m_dwHeight); } // Just add: OnMouseMiddleButtonUp(0, 0);
  22. Hello guys, I am once again coming with a really little fix that is related to rare attribute (6/7 bonuses). This bug is extra rare because to trigger it you must have a way to add 6 and 7 bonuses to an item and you must have an incorrect field or an error in your item_attr_rare table. So this is most likely a code sanitization but it's always good to avoid such errors as I encountered people having this bug. When you add a rare bonus, the game will fill a vector with the bonuses from the table and randomly pick one in the vector. However, if you have an error in the table, this vector will be empty and the randomization will result in doing "number(0, -1)", which is obviously incorrect and your core will crash. For searching purposes (if someone in the future is looking for a fix), the error is this one : The fix is really simple, we must check if the vector is empty and then exit at this moment. In item_attribute.cpp, in the following function: bool CItem::AddRareAttribute() Under this block of code: for (int i = 0; i < MAX_APPLY_NUM; ++i) { const TItemAttrTable& r = g_map_itemRare[i]; if (r.dwApplyIndex != 0 && r.bMaxLevelBySet[nAttrSet] > 0 && HasRareAttr(i) != true) { avail.push_back(i); } } Add the following check: [Hidden Content] And that's pretty much it, pretty straightforward, but instead of crashing, you will have a syserr and you will know what to check next!
  23. M2 Download Center Download Here ( Internal ) Hi devs, today I will release the fix I made for the skill cooldown, already fixed on official servers, this is the bug it self: And this is the fix: Regards! Fix skill cooldown ~ Shang.rar ### root/ui.py ### Search: def SetSlotCoolTimeColor(self, slotIndex, r, g, b, a): wndMgr.SetSlotCoolTimeColor(self.hWnd, slotIndex, r, g, b, a) ### Add after: def StoreSlotCoolTime(self, key, slotIndex, coolTime, elapsedTime = 0.0): wndMgr.StoreSlotCoolTime(self.hWnd, key, slotIndex, coolTime, elapsedTime) def RestoreSlotCoolTime(self, key): wndMgr.RestoreSlotCoolTime(self.hWnd, key) Thanks to @Horinna for report that bug. Here's the fix: """ Find this: elif (not self.__CanUseSkillNow()) or (skillGrade != j): skillPage.SetSlotCount(realSlotIndex, 0) skillPage.DisableCoverButton(realSlotIndex) Add this under:""" skillPage.DeactivateSlot(realSlotIndex) # After the else, paste this: if player.IsSkillActive(slotIndex) and (skillGrade == j): # fix001 skillPage.ActivateSlot(realSlotIndex) # The if should look like this: if (skillGrade == skill.SKILL_GRADE_COUNT) and j == (skill.SKILL_GRADE_COUNT-1): skillPage.SetSlotCountNew(realSlotIndex, skillGrade, skillLevel) elif (not self.__CanUseSkillNow()) or (skillGrade != j): skillPage.SetSlotCount(realSlotIndex, 0) skillPage.DisableCoverButton(realSlotIndex) skillPage.DeactivateSlot(realSlotIndex) # fix else: skillPage.SetSlotCountNew(realSlotIndex, skillGrade, skillLevel) if player.IsSkillActive(slotIndex) and (skillGrade == j): # fix skillPage.ActivateSlot(realSlotIndex)
  24. There is a problem, when you change position of horse riding skill in playersettingsmodule, you cannot use horse skills. For me, the way skills work on the client side of thing is really dumb, but they went even furher. I don't understand why, client checks if player has a horse riding level from the fixed skill slot, not from skill info itself. Without my fix, it goes like this: With fix: Actual fix:
  25. Hey guys, I just noticed that the description of leadership skill is not displaying party group bonuses values correctly. [Hidden Content] And you're done. Now you can go into the game and check, if the bonuses values in the leadership skill description are displayed correctly. Good luck!
  • 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.