Premium Istny 79 Posted October 22, 2021 Premium Share Posted October 22, 2021 (edited) The problem: Mobs are using CHARACTER_POINT_INSTANT struct, which contains fixed size arrays for dragon soul, items etc. For every mob these arrays are allocating memory, but mobs obviously don't use items... The solution: Replace fixed size arrays with std::map or other data type From my own testing i saw over 70% memory usage reduction with special inventory system and optimized variables listed below: bItemGrid pItems pDSItems wDSItemGrid Below i am sharing example implementation in clean source for bItemGrid variable Spoiler //in char.cpp at the end of void CHARACTER::Initialize() function add #ifdef ENABLE_ITEM_GRID_MAP m_pointsInstant.pItemsGridMap.clear(); #endif //in char.h after function LPITEM GetInventoryItem add #ifdef ENABLE_ITEM_GRID_MAP BYTE ItemGridMapGetConst(BYTE cell_index) const; #endif //in char.h replace BYTE bItemGrid[INVENTORY_AND_EQUIP_SLOT_MAX]; with #ifdef ENABLE_ITEM_GRID_MAP std::map<BYTE, BYTE> pItemsGridMap; #else BYTE bItemGrid[INVENTORY_AND_EQUIP_SLOT_MAX]; #endif //in char_item.cpp replace function CHARACTER::SetItem with void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem) { WORD wCell = Cell.cell; BYTE window_type = Cell.window_type; if ((unsigned long)((CItem*)pItem) == 0xff || (unsigned long)((CItem*)pItem) == 0xffffffff) { sys_err("!!! FATAL ERROR !!! item == 0xff (char: %s cell: %u)", GetName(), wCell); core_dump(); return; } if (pItem && pItem->GetOwner()) { assert(!"GetOwner exist"); return; } switch(window_type) { case INVENTORY: case EQUIPMENT: { if (wCell >= INVENTORY_AND_EQUIP_SLOT_MAX) { sys_err("CHARACTER::SetItem: invalid item cell %d", wCell); return; } LPITEM pOld = m_pointsInstant.pItems[wCell]; if (pOld) { if (wCell < INVENTORY_MAX_NUM) { for (int i = 0; i < pOld->GetSize(); ++i) { int p = wCell + (i * 5); if (p >= INVENTORY_MAX_NUM) continue; if (m_pointsInstant.pItems[p] && m_pointsInstant.pItems[p] != pOld) continue; #ifdef ENABLE_ITEM_GRID_MAP m_pointsInstant.pItemsGridMap[p] = 0; #else m_pointsInstant.bItemGrid[p] = 0; #endif } } else #ifdef ENABLE_ITEM_GRID_MAP m_pointsInstant.pItemsGridMap[wCell] = 0; #else m_pointsInstant.bItemGrid[wCell] = 0; #endif } if (pItem) { if (wCell < INVENTORY_MAX_NUM) { for (int i = 0; i < pItem->GetSize(); ++i) { int p = wCell + (i * 5); if (p >= INVENTORY_MAX_NUM) continue; #ifdef ENABLE_ITEM_GRID_MAP m_pointsInstant.pItemsGridMap[p] = wCell + 1; #else m_pointsInstant.bItemGrid[p] = wCell + 1; #endif } } else #ifdef ENABLE_ITEM_GRID_MAP m_pointsInstant.pItemsGridMap[wCell] = wCell + 1; #else m_pointsInstant.bItemGrid[wCell] = wCell + 1; #endif } m_pointsInstant.pItems[wCell] = pItem; } break; case DRAGON_SOUL_INVENTORY: { LPITEM pOld = m_pointsInstant.pDSItems[wCell]; if (pOld) { if (wCell < DRAGON_SOUL_INVENTORY_MAX_NUM) { for (int i = 0; i < pOld->GetSize(); ++i) { int p = wCell + (i * DRAGON_SOUL_BOX_COLUMN_NUM); if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) continue; if (m_pointsInstant.pDSItems[p] && m_pointsInstant.pDSItems[p] != pOld) continue; m_pointsInstant.wDSItemGrid[p] = 0; } } else m_pointsInstant.wDSItemGrid[wCell] = 0; } if (pItem) { if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM) { sys_err("CHARACTER::SetItem: invalid DS item cell %d", wCell); return; } if (wCell < DRAGON_SOUL_INVENTORY_MAX_NUM) { for (int i = 0; i < pItem->GetSize(); ++i) { int p = wCell + (i * DRAGON_SOUL_BOX_COLUMN_NUM); if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) continue; m_pointsInstant.wDSItemGrid[p] = wCell + 1; } } else m_pointsInstant.wDSItemGrid[wCell] = wCell + 1; } m_pointsInstant.pDSItems[wCell] = pItem; } break; default: sys_err ("Invalid Inventory type %d", window_type); return; } if (GetDesc()) { if (pItem) { TPacketGCItemSet pack; pack.header = HEADER_GC_ITEM_SET; pack.Cell = Cell; pack.count = pItem->GetCount(); pack.vnum = pItem->GetVnum(); pack.flags = pItem->GetFlag(); pack.anti_flags = pItem->GetAntiFlag(); pack.highlight = (Cell.window_type == DRAGON_SOUL_INVENTORY); thecore_memcpy(pack.alSockets, pItem->GetSockets(), sizeof(pack.alSockets)); thecore_memcpy(pack.aAttr, pItem->GetAttributes(), sizeof(pack.aAttr)); GetDesc()->Packet(&pack, sizeof(TPacketGCItemSet)); } else { TPacketGCItemDelDeprecated pack; pack.header = HEADER_GC_ITEM_DEL; pack.Cell = Cell; pack.count = 0; pack.vnum = 0; memset(pack.alSockets, 0, sizeof(pack.alSockets)); memset(pack.aAttr, 0, sizeof(pack.aAttr)); GetDesc()->Packet(&pack, sizeof(TPacketGCItemDelDeprecated)); } } if (pItem) { pItem->SetCell(this, wCell); switch (window_type) { case INVENTORY: case EQUIPMENT: if ((wCell < INVENTORY_MAX_NUM) || (BELT_INVENTORY_SLOT_START <= wCell && BELT_INVENTORY_SLOT_END > wCell)) pItem->SetWindow(INVENTORY); else pItem->SetWindow(EQUIPMENT); break; case DRAGON_SOUL_INVENTORY: pItem->SetWindow(DRAGON_SOUL_INVENTORY); break; } } } #ifdef ENABLE_ITEM_GRID_MAP BYTE CHARACTER::ItemGridMapGetConst(BYTE cell_index) const { auto it = m_pointsInstant.pItemsGridMap.find(cell_index); if (it != m_pointsInstant.pItemsGridMap.end()) { return it->second; } else { return 0; } } #endif //in char_item.cpp replace function CHARACTER::IsEmptyItemGrid with bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, BYTE bSize, int iExceptionCell) const { switch (Cell.window_type) { case INVENTORY: { BYTE bCell = Cell.cell; ++iExceptionCell; if (Cell.IsBeltInventoryPosition()) { LPITEM beltItem = GetWear(WEAR_BELT); if (NULL == beltItem) return false; if (false == CBeltInventoryHelper::IsAvailableCell(bCell - BELT_INVENTORY_SLOT_START, beltItem->GetValue(0))) return false; #ifdef ENABLE_ITEM_GRID_MAP if (ItemGridMapGetConst(bCell)) { if (ItemGridMapGetConst(bCell) == iExceptionCell) return true; return false; } #else if (m_pointsInstant.bItemGrid[bCell]) { if (m_pointsInstant.bItemGrid[bCell] == iExceptionCell) return true; return false; } #endif if (bSize == 1) return true; } else if (bCell >= INVENTORY_MAX_NUM) return false; #ifdef ENABLE_ITEM_GRID_MAP if (ItemGridMapGetConst(bCell)) { if (ItemGridMapGetConst(bCell) == iExceptionCell) #else if (m_pointsInstant.bItemGrid[bCell]) { if (m_pointsInstant.bItemGrid[bCell] == iExceptionCell) #endif { if (bSize == 1) return true; int j = 1; BYTE bPage = bCell / (INVENTORY_MAX_NUM / 2); do { BYTE p = bCell + (5 * j); if (p >= INVENTORY_MAX_NUM) return false; if (p / (INVENTORY_MAX_NUM / 2) != bPage) return false; #ifdef ENABLE_ITEM_GRID_MAP if (ItemGridMapGetConst(p)) if (ItemGridMapGetConst(p) != iExceptionCell) return false; #else if (m_pointsInstant.bItemGrid[p]) if (m_pointsInstant.bItemGrid[p] != iExceptionCell) return false; #endif } while (++j < bSize); return true; } else return false; } if (1 == bSize) return true; else { int j = 1; BYTE bPage = bCell / (INVENTORY_MAX_NUM / 2); do { BYTE p = bCell + (5 * j); if (p >= INVENTORY_MAX_NUM) return false; if (p / (INVENTORY_MAX_NUM / 2) != bPage) return false; #ifdef ENABLE_ITEM_GRID_MAP if (ItemGridMapGetConst(p)) if (ItemGridMapGetConst(p) != iExceptionCell) return false; #else if (m_pointsInstant.bItemGrid[p]) if (m_pointsInstant.bItemGrid[p] != iExceptionCell) return false; #endif } while (++j < bSize); return true; } } break; case DRAGON_SOUL_INVENTORY: { WORD wCell = Cell.cell; if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM) return false; iExceptionCell++; if (m_pointsInstant.wDSItemGrid[wCell]) { if (m_pointsInstant.wDSItemGrid[wCell] == iExceptionCell) { if (bSize == 1) return true; int j = 1; do { BYTE p = wCell + (DRAGON_SOUL_BOX_COLUMN_NUM * j); if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) return false; if (m_pointsInstant.wDSItemGrid[p]) if (m_pointsInstant.wDSItemGrid[p] != iExceptionCell) return false; } while (++j < bSize); return true; } else return false; } if (1 == bSize) return true; else { int j = 1; do { BYTE p = wCell + (DRAGON_SOUL_BOX_COLUMN_NUM * j); if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) return false; #ifdef ENABLE_ITEM_GRID_MAP if (ItemGridMapGetConst(p)) #else if (m_pointsInstant.bItemGrid[p]) #endif if (m_pointsInstant.wDSItemGrid[p] != iExceptionCell) return false; } while (++j < bSize); return true; } } } } Edited October 24, 2021 by VegaS™ added spoiler 10 7 9 Link to comment Share on other sites More sharing options...
Siverbot 6 Posted October 23, 2021 Share Posted October 23, 2021 Very interesting, i will try this soon Link to comment Share on other sites More sharing options...
Hik 108 Posted October 23, 2021 Share Posted October 23, 2021 Thanks! I have already entered the code, soon I will try it !! Link to comment Share on other sites More sharing options...
Lead0b110010100 15 Posted October 23, 2021 Share Posted October 23, 2021 I tested it and got like 44% RAM usage reduction. But I had to do some changes in order to not get exceptions when running the server on windows. (Due to unintialized usage of map) + I changed way more lines of code to do that, but the idea is absolutely correct. 1 1 Link to comment Share on other sites More sharing options...
Forum Moderator Gurgarath 2511 Posted October 23, 2021 Forum Moderator Share Posted October 23, 2021 50 minutes ago, Lead0b110010100 said: I tested it and got like 44% RAM usage reduction. But I had to do some changes in order to not get exceptions when running the server on windows. (Due to unintialized usage of map) + I changed way more lines of code to do that, but the idea is absolutely correct. 4 1 Gurgarath coming soon Link to comment Share on other sites More sharing options...
Hik 108 Posted October 23, 2021 Share Posted October 23, 2021 1 hour ago, Lead0b110010100 said: L'ho testato e ho ottenuto una riduzione dell'utilizzo della RAM del 44%. Ma ho dovuto apportare alcune modifiche per non ottenere eccezioni durante l'esecuzione del server su Windows. (A causa dell'uso non inizializzato della mappa) + Ho cambiato molte più righe di codice per farlo, ma l'idea è assolutamente corretta. Can you post the changes you made? Link to comment Share on other sites More sharing options...
Lead0b110010100 15 Posted October 23, 2021 Share Posted October 23, 2021 (edited) 20 hours ago, Hik said: Can you post the changes you made? Okay? No problem: Spoiler From 00c973263a61583dceede12f790312434ac69fed Mon Sep 17 00:00:00 2001 From: Lead0b110010100 <Lead0b110010100@outlook.com> Date: Sat, 23 Oct 2021 14:21:15 +0200 Subject: [PATCH] Squashed commit of the following: commit ba3633233cf2e6bd08664c5cfab93155bc1ce05d Author: Lead0b110010100 <Lead0b110010100@outlook.com> Date: Sat Oct 23 14:18:01 2021 +0200 Refactoring. commit 2328c9c4e7336eb2bf6428206ab2aedc2a50f343 Author: Lead0b110010100 <Lead0b110010100@outlook.com> Date: Sat Oct 23 14:00:06 2021 +0200 Improve performants by changing char array with map. commit cb1c00629cd21f4c0bfb9cc79ca2a322013fce7e Author: Lead0b110010100 <Lead0b110010100@outlook.com> Date: Sat Oct 23 13:28:54 2021 +0200 Improve performants by changing char array with map. commit af1e5bee7838815de735e9e280ece47aceb54f8c Author: Lead0b110010100 <Lead0b110010100@outlook.com> Date: Sat Oct 23 13:14:57 2021 +0200 Improve performants by changing char array with map. commit 125bc19585cf904e8f40fcb687dbd0b3730ad13a Author: Lead0b110010100 <Lead0b110010100@outlook.com> Date: Sat Oct 23 12:45:01 2021 +0200 Improve performants by changing char array with map. --- Server/game/src/char.cpp | 6 +- Server/game/src/char.h | 35 ++++++--- Server/game/src/char_item.cpp | 134 +++++++++++++++++++++++++--------- 3 files changed, 125 insertions(+), 50 deletions(-) diff --git a/Server/game/src/char.cpp b/Server/game/src/char.cpp index 246ea710..475888e5 100644 --- a/Server/game/src/char.cpp +++ b/Server/game/src/char.cpp @@ -322,7 +322,7 @@ void CHARACTER::Initialize() m_pkChrSyncOwner = NULL; memset(&m_points, 0, sizeof(m_points)); - memset(&m_pointsInstant, 0, sizeof(m_pointsInstant)); + m_pointsInstant = {}; memset(&m_quickslot, 0, sizeof(m_quickslot)); m_bCharType = CHAR_TYPE_MONSTER; @@ -9075,12 +9075,12 @@ bool CHARACTER::HasGroupMemberInParty() const LPITEM CHARACTER::GetSwitchbotItem(uint8_t slot) { - return m_pointsInstant.pSwitchbotItems[slot]; + return GetSwitchbotItemFromGrid(slot); } LPITEM CHARACTER::GetSoulshieldItem(uint8_t slot) { - return m_pointsInstant.pItems[SOULSHIELD_EQUIP_SLOT_START + slot]; + return GetItemFromGrid(SOULSHIELD_EQUIP_SLOT_START + slot); } LPITEM CHARACTER::GetSafeboxItem(uint16_t slot) diff --git a/Server/game/src/char.h b/Server/game/src/char.h index 5a6e0f6a..5c185a2c 100644 --- a/Server/game/src/char.h +++ b/Server/game/src/char.h @@ -225,6 +225,15 @@ typedef struct character_point /* 저장되지 않는 캐릭터 데이터 */ typedef struct character_point_instant { + character_point_instant() : points(), fRot(0.0f), iMaxHP(0), iMaxSP(0), position(0), + instant_flag(0), dwAIFlag(0), dwImmuneFlag(0), dwLastShoutPulse(0), parts(), + pItems(), pItemGrid(), pExtraItems(), pExtraItemGrid(), pSwitchbotItems(), + pCubeNpc(nullptr), battle_victim(nullptr), gm_level(GM_PLAYER), + bBasePart(PART_MAIN), iMaxStamina(0), pWardrobeItems(), wardrobeIsLoaded(false), + isWardrobeItemUnequiping(0), wardrobeCount(0) + { + }; + GoldType points[POINT_MAX_NUM]; float fRot; @@ -245,20 +254,17 @@ typedef struct character_point_instant // char는 인벤을 uint8_t array로 grid를 관리하고, exchange나 cube는 CGrid로 grid를 관리하고 뭐냐 이거... // grid를 만들어 놨으면 grid를 쓰란 말이야!!! // ㅅㅂ 용혼석 인벤을 똑같이 따라서 만든 나도 잘못했다 ㅠㅠ - LPITEM pItems[INVENTORY_AND_EQUIP_SLOT_MAX]; - uint8_t bItemGrid[INVENTORY_AND_EQUIP_SLOT_MAX]; + std::map<uint16_t, LPITEM> pItems; + std::map<uint8_t, uint8_t> pItemGrid; - LPITEM pExtraItems[EXTRA_INVENTORY_MAX_NUM]; - uint16_t wExtraItemGrid[EXTRA_INVENTORY_MAX_NUM]; + std::map<uint16_t, LPITEM> pExtraItems; + std::map<uint16_t, uint16_t> pExtraItemGrid; #ifdef ENABLE_SWITCHBOT - LPITEM pSwitchbotItems[SWITCHBOT_SLOT_COUNT]; + std::map<uint16_t, LPITEM> pSwitchbotItems; #endif - // by mhh - LPITEM pCubeItems[CUBE_MAX_NUM]; LPCHARACTER pCubeNpc; - LPCHARACTER battle_victim; uint8_t gm_level; @@ -268,7 +274,7 @@ typedef struct character_point_instant int32_t iMaxStamina; #ifdef ENABLE_WARDROBE - LPITEM pWardrobeItems[WARDROBE_SLOT_COUNT]; + std::map<uint16_t, LPITEM> pWardrobeItems; bool wardrobeIsLoaded; int32_t isWardrobeItemUnequiping; int32_t wardrobeCount; @@ -1022,6 +1028,15 @@ public: void SetItem(TItemPos Cell, LPITEM item); LPITEM GetItem(TItemPos Cell) const; LPITEM GetInventoryItem(ItemCellType wCell) const; + + ItemCellType GetFromItemGrid(ItemCellType cell) const; + ItemCellType GetFromExtraItemGrid(ItemCellType cell) const; + + LPITEM GetItemFromGrid(ItemStackType cell) const; + LPITEM GetExtraItemFromGrid(ItemStackType cell) const; + LPITEM GetSwitchbotItemFromGrid(ItemStackType cell) const; + LPITEM GetWardrobeItemFromGrid(ItemStackType cell) const; + bool IsEmptyItemGrid(TItemPos Cell, uint8_t size, int32_t iExceptionCell = -1) const; LPITEM GetExtraInventoryItem(ItemCellType wCell) const; @@ -1889,8 +1904,6 @@ public: public: bool ItemProcess_Polymorph(LPITEM item); - // by mhh - LPITEM *GetCubeItem() { return m_pointsInstant.pCubeItems; } bool IsCubeOpen() const { return (m_pointsInstant.pCubeNpc ? true : false); } void SetCubeNpc(LPCHARACTER npc) { m_pointsInstant.pCubeNpc = npc; } LPCHARACTER GetCubeNpc() { return m_pointsInstant.pCubeNpc; } diff --git a/Server/game/src/char_item.cpp b/Server/game/src/char_item.cpp index 9d92abd5..8a0e5cfe 100644 --- a/Server/game/src/char_item.cpp +++ b/Server/game/src/char_item.cpp @@ -248,14 +248,14 @@ LPITEM CHARACTER::GetItem(TItemPos Cell) const sys_err("CHARACTER::GetInventoryItem: invalid item cell %d", wCell); return NULL; } - return m_pointsInstant.pItems[wCell]; + return GetItemFromGrid(wCell); case EXTRA_INVENTORY: if (wCell >= EXTRA_INVENTORY_MAX_NUM) { sys_err("CHARACTER::GetInventoryItem: invalid EXTRA item cell %d", wCell); return NULL; } - return m_pointsInstant.pExtraItems[wCell]; + return GetExtraItemFromGrid(wCell); #ifdef ENABLE_SWITCHBOT case SWITCHBOT: if (wCell >= SWITCHBOT_SLOT_COUNT) @@ -263,7 +263,7 @@ LPITEM CHARACTER::GetItem(TItemPos Cell) const sys_err("CHARACTER::GetInventoryItem: invalid switchbot item cell %d", wCell); return NULL; } - return m_pointsInstant.pSwitchbotItems[wCell]; + return GetSwitchbotItemFromGrid(wCell); #endif #ifdef ENABLE_WARDROBE case WARDROBE: @@ -272,7 +272,7 @@ LPITEM CHARACTER::GetItem(TItemPos Cell) const sys_err("CHARACTER::GetInventoryItem: invalid wardrobe item cell %d", wCell); return NULL; } - return m_pointsInstant.pWardrobeItems[wCell]; + return GetWardrobeItemFromGrid(wCell); #endif default: @@ -309,7 +309,7 @@ void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem) return; } - LPITEM pOld = m_pointsInstant.pItems[wCell]; + LPITEM pOld = GetItemFromGrid(wCell); if (pOld) { @@ -322,14 +322,14 @@ void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem) if (p >= INVENTORY_MAX_NUM) continue; - if (m_pointsInstant.pItems[p] && m_pointsInstant.pItems[p] != pOld) + if (GetItemFromGrid(p) && GetItemFromGrid(p) != pOld) continue; - m_pointsInstant.bItemGrid[p] = 0; + m_pointsInstant.pItemGrid[p] = 0; } } else - m_pointsInstant.bItemGrid[wCell] = 0; + m_pointsInstant.pItemGrid[wCell] = 0; } if (pItem) @@ -345,11 +345,11 @@ void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem) // wCell + 1 로 하는 것은 빈곳을 체크할 때 같은 // 아이템은 예외처리하기 위함 - m_pointsInstant.bItemGrid[p] = wCell + 1; + m_pointsInstant.pItemGrid[p] = wCell + 1; } } else - m_pointsInstant.bItemGrid[wCell] = wCell + 1; + m_pointsInstant.pItemGrid[wCell] = wCell + 1; } m_pointsInstant.pItems[wCell] = pItem; @@ -364,7 +364,7 @@ void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem) return; } - LPITEM pOld = m_pointsInstant.pExtraItems[wCell]; + LPITEM pOld = GetExtraItemFromGrid(wCell); if (pOld) { @@ -375,10 +375,10 @@ void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem) if (p >= EXTRA_INVENTORY_MAX_NUM) continue; - if (m_pointsInstant.pExtraItems[p] && m_pointsInstant.pExtraItems[p] != pOld) + if (GetExtraItemFromGrid(p) && GetExtraItemFromGrid(p) != pOld) continue; - m_pointsInstant.wExtraItemGrid[p] = 0; + m_pointsInstant.pExtraItemGrid[p] = 0; } } @@ -391,7 +391,7 @@ void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem) if (p >= EXTRA_INVENTORY_MAX_NUM) continue; - m_pointsInstant.wExtraItemGrid[p] = wCell + 1; + m_pointsInstant.pExtraItemGrid[p] = wCell + 1; } } @@ -402,7 +402,8 @@ void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem) #ifdef ENABLE_SWITCHBOT case SWITCHBOT: { - LPITEM pOld = m_pointsInstant.pSwitchbotItems[wCell]; + LPITEM pOld = GetSwitchbotItemFromGrid(wCell); + if (pItem && pOld) { return; @@ -528,7 +529,7 @@ void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem) if (m_pointsInstant.isWardrobeItemUnequiping > 0) { int32_t pos = m_pointsInstant.isWardrobeItemUnequiping - 1; - LPITEM item = m_pointsInstant.pWardrobeItems[pos]; + LPITEM item = GetWardrobeItemFromGrid(pos); m_pointsInstant.isWardrobeItemUnequiping = -1; pItem->RemoveFromCharacter(); @@ -548,7 +549,7 @@ LPITEM CHARACTER::GetWear(ItemCellType bCell) const return NULL; } - return m_pointsInstant.pItems[INVENTORY_MAX_NUM + bCell]; + return GetItemFromGrid(INVENTORY_MAX_NUM + bCell); } void CHARACTER::SetWear(ItemCellType bCell, LPITEM item) @@ -606,7 +607,7 @@ void CHARACTER::ClearItem() #endif #ifdef ENABLE_WARDROBE for (i = 0; i < WARDROBE_SLOT_COUNT; i++) { - item = m_pointsInstant.pWardrobeItems[i]; + item = GetWardrobeItemFromGrid(i); if (item != nullptr) { item->SetSkipSave(true); @@ -635,9 +636,9 @@ bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, uint8_t bSize, int32_t iException if (bCell >= INVENTORY_MAX_NUM) return false; - if (m_pointsInstant.bItemGrid[bCell]) + if (GetFromItemGrid(bCell)) { - if (m_pointsInstant.bItemGrid[bCell] == iExceptionCell) + if (GetFromItemGrid(bCell) == iExceptionCell) { if (bSize == 1) return true; @@ -655,8 +656,8 @@ bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, uint8_t bSize, int32_t iException if (p / (INVENTORY_MAX_NUM / INVENTORY_PAGE_COUNT) != bPage) return false; - if (m_pointsInstant.bItemGrid[p]) - if (m_pointsInstant.bItemGrid[p] != iExceptionCell) + if (GetFromItemGrid(p)) + if (GetFromItemGrid(p) != iExceptionCell) return false; } while (++j < bSize); @@ -684,8 +685,8 @@ bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, uint8_t bSize, int32_t iException if (p / (INVENTORY_MAX_NUM / INVENTORY_PAGE_COUNT) != bPage) return false; - if (m_pointsInstant.bItemGrid[p]) - if (m_pointsInstant.bItemGrid[p] != iExceptionCell) + if (GetFromItemGrid(p)) + if (GetFromItemGrid(p) != iExceptionCell) return false; } while (++j < bSize); @@ -703,9 +704,9 @@ bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, uint8_t bSize, int32_t iException ++iExceptionCell; - if (m_pointsInstant.wExtraItemGrid[bCell]) + if (GetFromExtraItemGrid(bCell)) { - if (m_pointsInstant.wExtraItemGrid[bCell] == iExceptionCell) + if (GetFromExtraItemGrid(bCell) == iExceptionCell) { if (bSize == 1) return true; @@ -723,8 +724,8 @@ bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, uint8_t bSize, int32_t iException if (p / (EXTRA_INVENTORY_MAX_NUM / EXTRA_INVENTORY_PAGE_COUNT) != bPage) return false; - if (m_pointsInstant.wExtraItemGrid[p]) - if (m_pointsInstant.wExtraItemGrid[p] != iExceptionCell) + if (GetFromExtraItemGrid(p)) + if (GetFromExtraItemGrid(p) != iExceptionCell) return false; } while (++j < bSize); @@ -751,8 +752,8 @@ bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, uint8_t bSize, int32_t iException if (p / (EXTRA_INVENTORY_MAX_NUM / EXTRA_INVENTORY_PAGE_COUNT) != bPage) return false; - if (m_pointsInstant.wExtraItemGrid[p]) - if (m_pointsInstant.wExtraItemGrid[p] != iExceptionCell) + if (GetFromExtraItemGrid(p)) + if (GetFromExtraItemGrid(p) != iExceptionCell) return false; } while (++j < bSize); @@ -765,12 +766,13 @@ bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, uint8_t bSize, int32_t iException case SWITCHBOT: { ItemCellType wCell = Cell.cell; + if (wCell >= SWITCHBOT_SLOT_COUNT) { return false; } - if (m_pointsInstant.pSwitchbotItems[wCell]) + if (GetSwitchbotItemFromGrid(wCell)) { return false; } @@ -782,12 +784,12 @@ bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, uint8_t bSize, int32_t iException #ifdef ENABLE_WARDROBE case WARDROBE: { - uint16_t wCell = Cell.cell; + ItemStackType wCell = Cell.cell; if (wCell >= WARDROBE_SLOT_COUNT) return false; - return m_pointsInstant.pWardrobeItems[wCell] == nullptr; + return !GetWardrobeItemFromGrid(wCell); } #endif } @@ -8718,7 +8720,7 @@ void CHARACTER::CombineShards(LPITEM shard1, LPITEM shard2) int32_t CHARACTER::GetEmptyWardrobeSlot() const { for (int32_t i = 0; i < WARDROBE_HIDDEN_SLOT_COUNT; i++) - if (!m_pointsInstant.pWardrobeItems[i]) + if (!GetWardrobeItemFromGrid(i)) return i; return -1; @@ -8728,7 +8730,7 @@ int32_t CHARACTER::GetWardrobeRewardSlot(uint32_t dwItemVnum) const { for (int32_t i = 0; i < WARDROBE_HIDDEN_SLOT_COUNT; i++) { - LPITEM item = m_pointsInstant.pWardrobeItems[i]; + LPITEM item = GetWardrobeItemFromGrid(i); if (item && item->GetVnum() == dwItemVnum) return i; @@ -8743,7 +8745,7 @@ int32_t CHARACTER::GetWardrobeSlot(uint32_t dwItemVnum) const for (int32_t i = WARDROBE_HIDDEN_SLOT_COUNT; i < WARDROBE_SLOT_COUNT; i++) { - LPITEM item = m_pointsInstant.pWardrobeItems[i]; + LPITEM item = GetWardrobeItemFromGrid(i); if (item == nullptr) { @@ -8930,3 +8932,63 @@ void CHARACTER::AbsorbAutopotionsInClipping(const TItemPos& pos) ChatPacketTrans(CHAT_TYPE_INFO, "Es wurden %d Autopots absorbiert!", count); } + +ItemCellType CHARACTER::GetFromItemGrid(ItemCellType cell) const +{ + const auto& it = m_pointsInstant.pItemGrid.find(cell); + + if (it == m_pointsInstant.pItemGrid.end()) + return 0; + + return it->second; +} + +ItemCellType CHARACTER::GetFromExtraItemGrid(ItemCellType cell) const +{ + const auto& it = m_pointsInstant.pExtraItemGrid.find(cell); + + if (it == m_pointsInstant.pExtraItemGrid.end()) + return 0; + + return it->second; +} + +LPITEM CHARACTER::GetItemFromGrid(ItemStackType cell) const +{ + const auto& it = m_pointsInstant.pItems.find(cell); + + if (it == m_pointsInstant.pItems.end()) + return nullptr; + + return it->second; +} + +LPITEM CHARACTER::GetExtraItemFromGrid(ItemStackType cell) const +{ + const auto& it = m_pointsInstant.pExtraItems.find(cell); + + if (it == m_pointsInstant.pExtraItems.end()) + return nullptr; + + return it->second; +} + +LPITEM CHARACTER::GetSwitchbotItemFromGrid(ItemStackType cell) const +{ + const auto& it = m_pointsInstant.pSwitchbotItems.find(cell); + + if (it == m_pointsInstant.pSwitchbotItems.end()) + return nullptr; + + return it->second; +} + +LPITEM CHARACTER::GetWardrobeItemFromGrid(ItemStackType cell) const +{ + const auto& it = m_pointsInstant.pWardrobeItems.find(cell); + + if (it == m_pointsInstant.pWardrobeItems.end()) + return nullptr; + + return it->second; +} -- 2.33.1.windows.1 Edited October 24, 2021 by VegaS™ added spoiler 1 2 1 Link to comment Share on other sites More sharing options...
Hik 108 Posted October 24, 2021 Share Posted October 24, 2021 Work, thanks Link to comment Share on other sites More sharing options...
DrTurk 133 Posted October 24, 2021 Share Posted October 24, 2021 tested on windows with only map1 blue -> with changed functions: 100,6MB without changed functions: 114MB nice release, thanks Link to comment Share on other sites More sharing options...
Cysgod 106 Posted October 24, 2021 Share Posted October 24, 2021 Did anyone test the impact on cpu usage or the time needed for the execution of the functions? The usage of a ordered std::map should be (in theory) way slower. ATM I moved the fixed sized arrays into a new struct and added a shared_ptr as member variable to the CHARACTER class. When "SetPlayerProto" gets called I'll create a shared_ptr of the new struct. - Which ofc means I wont save any memory for players which don't use all slots. In idle (without players) this results in a drop from 1480mb to 540mb ram per channel -> ~64% less ramusage. Link to comment Share on other sites More sharing options...
Premium Istny 79 Posted October 24, 2021 Author Premium Share Posted October 24, 2021 carray: 30ms map: 55ms unordered_map :39ms tested with array size of 1250 elements 1mln random access 1 Link to comment Share on other sites More sharing options...
Siverbot 6 Posted October 24, 2021 Share Posted October 24, 2021 I tested it now with just the Tutorial at Top, i know its just the "base" it changed nothing, befor my core need 330MB, after it also need 330MB Link to comment Share on other sites More sharing options...
Lead0b110010100 15 Posted October 24, 2021 Share Posted October 24, 2021 1 hour ago, Cysgod said: Did anyone test the impact on cpu usage or the time needed for the execution of the functions? The usage of a ordered std::map should be (in theory) way slower. ATM I moved the fixed sized arrays into a new struct and added a shared_ptr as member variable to the CHARACTER class. When "SetPlayerProto" gets called I'll create a shared_ptr of the new struct. - Which ofc means I wont save any memory for players which don't use all slots. In idle (without players) this results in a drop from 1480mb to 540mb ram per channel -> ~64% less ramusage. You ware totally right, I replaced std::map with std::unordered_map here. Link to comment Share on other sites More sharing options...
EAkar 8 Posted October 25, 2021 Share Posted October 25, 2021 These operations give game.core. Game gives core on loading screen. No wrong additions. Everything is correct. There was no kernel crash prior to doing this action. char.h #ifdef ENABLE_ITEM_GRID_MAP std::unordered_map<BYTE, BYTE> pItemsGridMap; #else BYTE bItemGrid[INVENTORY_AND_EQUIP_SLOT_MAX]; #endif and LPITEM GetInventoryItem(WORD wCell) const; #ifdef ENABLE_ITEM_GRID_MAP BYTE ItemGridMapGetConst(BYTE cell_index) const; #endif char_item.cpp bool CHARACTER::IsEmptyItemGrid(TItemPos Cell, BYTE bSize, int iExceptionCell) const { switch (Cell.window_type) { case INVENTORY: { WORD bCell = Cell.cell; ++iExceptionCell; if (Cell.IsBeltInventoryPosition()) { LPITEM beltItem = GetWear(WEAR_BELT); if (NULL == beltItem) return false; if (false == CBeltInventoryHelper::IsAvailableCell(bCell - BELT_INVENTORY_SLOT_START, beltItem->GetValue(0))) return false; #ifdef ENABLE_ITEM_GRID_MAP if (ItemGridMapGetConst(bCell)) { if (ItemGridMapGetConst(bCell) == iExceptionCell) return true; return false; } #else if (m_pointsInstant.bItemGrid[bCell]) { if (m_pointsInstant.bItemGrid[bCell] == iExceptionCell) return true; return false; } #endif if (bSize == 1) return true; } else if (bCell >= INVENTORY_MAX_NUM) return false; #ifdef ENABLE_ITEM_GRID_MAP if (ItemGridMapGetConst(bCell)) { if (ItemGridMapGetConst(bCell) == iExceptionCell) #else if (m_pointsInstant.bItemGrid[bCell]) { if (m_pointsInstant.bItemGrid[bCell] == iExceptionCell) #endif { if (bSize == 1) return true; int j = 1; BYTE bPage = bCell / (INVENTORY_MAX_NUM / INVENTORY_PAGE_COUNT); do { BYTE p = bCell + (5 * j); if (p >= INVENTORY_MAX_NUM) return false; if (p / (INVENTORY_MAX_NUM / INVENTORY_PAGE_COUNT) != bPage) return false; #ifdef ENABLE_ITEM_GRID_MAP if (ItemGridMapGetConst(p)) if (ItemGridMapGetConst(p) != iExceptionCell) return false; #else if (m_pointsInstant.bItemGrid[p]) if (m_pointsInstant.bItemGrid[p] != iExceptionCell) return false; #endif } while (++j < bSize); return true; } else return false; } if (1 == bSize) return true; else { int j = 1; BYTE bPage = bCell / (INVENTORY_MAX_NUM / INVENTORY_PAGE_COUNT); do { BYTE p = bCell + (5 * j); if (p >= INVENTORY_MAX_NUM) return false; if (p / (INVENTORY_MAX_NUM / INVENTORY_PAGE_COUNT) != bPage) return false; #ifdef ENABLE_ITEM_GRID_MAP if (ItemGridMapGetConst(p)) if (ItemGridMapGetConst(p) != iExceptionCell) return false; #else if (m_pointsInstant.bItemGrid[p]) if (m_pointsInstant.bItemGrid[p] != iExceptionCell) return false; #endif } while (++j < bSize); return true; } } break; case DRAGON_SOUL_INVENTORY: { WORD wCell = Cell.cell; if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM) return false; iExceptionCell++; if (m_pointsInstant.wDSItemGrid[wCell]) { if (m_pointsInstant.wDSItemGrid[wCell] == iExceptionCell) { if (bSize == 1) return true; int j = 1; do { int p = wCell + (DRAGON_SOUL_BOX_COLUMN_NUM * j); if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) return false; #ifdef ENABLE_ITEM_GRID_MAP if (ItemGridMapGetConst(p)) #else if (m_pointsInstant.bItemGrid[p]) #endif if (m_pointsInstant.wDSItemGrid[p] != iExceptionCell) return false; } while (++j < bSize); return true; } else return false; } if (1 == bSize) return true; else { int j = 1; do { int p = wCell + (DRAGON_SOUL_BOX_COLUMN_NUM * j); if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) return false; #ifdef ENABLE_ITEM_GRID_MAP if (ItemGridMapGetConst(p)) #else if (m_pointsInstant.bItemGrid[p]) #endif if (m_pointsInstant.wDSItemGrid[p] != iExceptionCell) return false; } while (++j < bSize); return true; } } break; void CHARACTER::SetItem(TItemPos Cell, LPITEM pItem) #endif { WORD wCell = Cell.cell; BYTE window_type = Cell.window_type; if ((unsigned long)((CItem*)pItem) == 0xff || (unsigned long)((CItem*)pItem) == 0xffffffff) { sys_err("!!! FATAL ERROR !!! item == 0xff (char: %s cell: %u)", GetName(), wCell); core_dump(); return; } if (pItem && pItem->GetOwner()) { assert(!"GetOwner exist"); return; } switch (window_type) { case INVENTORY: case EQUIPMENT: { if (wCell >= INVENTORY_AND_EQUIP_SLOT_MAX) { sys_err("CHARACTER::SetItem: invalid item cell %d", wCell); return; } LPITEM pOld = m_pointsInstant.pItems[wCell]; if (pOld) { if (wCell < INVENTORY_MAX_NUM) { for (int i = 0; i < pOld->GetSize(); ++i) { int p = wCell + (i * 5); if (p >= INVENTORY_MAX_NUM) continue; if (m_pointsInstant.pItems[p] && m_pointsInstant.pItems[p] != pOld) continue; #ifdef ENABLE_ITEM_GRID_MAP m_pointsInstant.pItemsGridMap[p] = 0; #else m_pointsInstant.bItemGrid[p] = 0; #endif } } else #ifdef ENABLE_ITEM_GRID_MAP m_pointsInstant.pItemsGridMap[wCell] = 0; #else m_pointsInstant.bItemGrid[wCell] = 0; #endif } if (pItem) { if (wCell < INVENTORY_MAX_NUM) { for (int i = 0; i < pItem->GetSize(); ++i) { int p = wCell + (i * 5); if (p >= INVENTORY_MAX_NUM) continue; #ifdef ENABLE_ITEM_GRID_MAP m_pointsInstant.pItemsGridMap[p] = wCell + 1; #else m_pointsInstant.bItemGrid[p] = wCell + 1; #endif } } else #ifdef ENABLE_ITEM_GRID_MAP m_pointsInstant.pItemsGridMap[wCell] = wCell + 1; #else m_pointsInstant.bItemGrid[wCell] = wCell + 1; #endif } m_pointsInstant.pItems[wCell] = pItem; } break; case DRAGON_SOUL_INVENTORY: { LPITEM pOld = m_pointsInstant.pDSItems[wCell]; if (pOld) { if (wCell < DRAGON_SOUL_INVENTORY_MAX_NUM) { for (int i = 0; i < pOld->GetSize(); ++i) { int p = wCell + (i * DRAGON_SOUL_BOX_COLUMN_NUM); if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) continue; if (m_pointsInstant.pDSItems[p] && m_pointsInstant.pDSItems[p] != pOld) continue; m_pointsInstant.wDSItemGrid[p] = 0; } } else m_pointsInstant.wDSItemGrid[wCell] = 0; } if (pItem) { if (wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM) { sys_err("CHARACTER::SetItem: invalid DS item cell %d", wCell); return; } if (wCell < DRAGON_SOUL_INVENTORY_MAX_NUM) { for (int i = 0; i < pItem->GetSize(); ++i) { int p = wCell + (i * DRAGON_SOUL_BOX_COLUMN_NUM); if (p >= DRAGON_SOUL_INVENTORY_MAX_NUM) continue; m_pointsInstant.wDSItemGrid[p] = wCell + 1; } } else m_pointsInstant.wDSItemGrid[wCell] = wCell + 1; } m_pointsInstant.pDSItems[wCell] = pItem; } break; #ifdef ENABLE_ITEM_GRID_MAP BYTE CHARACTER::ItemGridMapGetConst(BYTE cell_index) const { auto it = m_pointsInstant.pItemsGridMap.find(cell_index); if (it != m_pointsInstant.pItemsGridMap.end()) { return it->second; } else { return 0; } } #endif #ifdef ENABLE_ITEM_GRID_MAP m_pointsInstant.pItemsGridMap.clear(); #endif #ifdef ENABLE_ACCE_SYSTEM m_bAcceCombination = false; m_bAcceAbsorption = false; #endif I cleared the codes and the game opened Link to comment Share on other sites More sharing options...
Forum Moderator Gurgarath 2511 Posted October 27, 2021 Forum Moderator Share Posted October 27, 2021 (edited) That is a very brilliant fix! I just finished doing all the changes I could and ended up with pretty good results! I haven't fully finished yet (have to find a way for CopyDragonSoulItemGrid function), but the results so far are good. I also noticed a bug, it can be from my side but I prefer to report it for anyone, I am sometimes unable to switch an equipment part, on my warrior it was the weapon and the helmet, on my shaman it was only the armor. It doesn't happen when I revert my changes. (Edit : Do not bother trying, it was me. I was tired != != ==) Spoiler Before: After : Edited August 25, 2022 by Metin2 Dev Core X - External 2 Internal Gurgarath coming soon Link to comment Share on other sites More sharing options...
Active Member Exygo 1090 Posted November 3, 2021 Active Member Share Posted November 3, 2021 You can't move an item after this https://www.youtube.com/channel/UCQ8mAeda9TWq6SsTzB53emw/videos Link to comment Share on other sites More sharing options...
Forum Moderator Gurgarath 2511 Posted November 3, 2021 Forum Moderator Share Posted November 3, 2021 1 hour ago, Exygo said: You can't move an item after this Make sure that all the != are respected, I got this issue at first then after a double check it worked. However it made my special inventory completely messed up, I disabled it for now. 2 Gurgarath coming soon Link to comment Share on other sites More sharing options...
Honorable Member martysama0134 7174 Posted November 5, 2021 Honorable Member Share Posted November 5, 2021 (edited) There are far better solutions than using std::map or std::unordered_map (which still takes a lot of ram for no reason for each mob): (I included the most important parts) You also forgot the CubeItems, and we could probably fit the quickslot too. Spoiler Edited August 29, 2022 by Metin2 Dev Core X - External 2 Internal 2 7 7 Check out my GitHub Link to comment Share on other sites More sharing options...
Cysgod 106 Posted November 5, 2021 Share Posted November 5, 2021 (edited) 25 minutes ago, martysama0134 said: There are far better solutions than using std::map or std::unordered_map (which still takes a lot of ram for no reason for each mob): (I included the most important parts) You also forgot the CubeItems, and we could probably fit the quickslot too. That's exactly what I did. Running since 24. Oct without any problems. Spoiler Edited September 4, 2022 by Metin2 Dev Core X - External 2 Internal Link to comment Share on other sites More sharing options...
DrTurk 133 Posted November 5, 2021 Share Posted November 5, 2021 (edited) 6 hours ago, martysama0134 said: There are far better solutions than using std::map or std::unordered_map (which still takes a lot of ram for no reason for each mob): (I included the most important parts) You also forgot the CubeItems, and we could probably fit the quickslot too. With your changes -> 430MB Without your changes -> 775MB thats impressiv Spoiler Edited September 4, 2022 by Metin2 Dev Core X - External 2 Internal Link to comment Share on other sites More sharing options...
sxvoyz 61 Posted November 5, 2021 Share Posted November 5, 2021 void CHARACTER::Initialize() { memset(&m_pointsInstant, 0, sizeof(m_pointsInstant)); } That's why somebody got crash. Try define map/unordered_map outside this structure then everything will be alright. Link to comment Share on other sites More sharing options...
Honorable Member martysama0134 7174 Posted November 5, 2021 Honorable Member Share Posted November 5, 2021 1 hour ago, sxvoyz said: void CHARACTER::Initialize() { memset(&m_pointsInstant, 0, sizeof(m_pointsInstant)); } That's why somebody got crash. Try define map/unordered_map outside this structure then everything will be alright. You probably got a warning in that line, but the solution is even simpler: m_pointsInstant = {}; 1 1 Check out my GitHub Link to comment Share on other sites More sharing options...
Alpha 482 Posted November 5, 2021 Share Posted November 5, 2021 Mobs should be their own entity you can reduce memory usage by partly by just allocating specific stuff just for players but thats an extra allocation for every entity potentially several more allocations for example if you are using maps and not reserving or resizing you will end up with multiple allocations for a single map 2 Link to comment Share on other sites More sharing options...
Hik 108 Posted November 6, 2021 Share Posted November 6, 2021 (edited) On 11/5/2021 at 12:20 PM, martysama0134 said: There are far better solutions than using std::map or std::unordered_map (which still takes a lot of ram for no reason for each mob): (I included the most important parts) You also forgot the CubeItems, and we could probably fit the quickslot too. Thanks, I followed the code structure you posted I get some sort of warning at this line: do you know how to solve? m_pointsInstant.playerSlots-> wDSItemGrid [wCell] = 0; Buffer Overrun: When accessing m_pointsInstant.playerSlots-> wDSItemGrid, the non-write protected size is 1920 bytes, but it is possible to write to wcell. Spoiler Edited September 4, 2022 by Metin2 Dev Core X - External 2 Internal Link to comment Share on other sites More sharing options...
Premium xTryhard 12 Posted June 28, 2022 Premium Share Posted June 28, 2022 use a std::vector instead of a std::map you can resize the vector in the characters constructor if the Instance is a PlayerCharacter otherwise you keep the size from the vector at 0 Link to comment Share on other sites More sharing options...
Recommended Posts