Istny
-
Posts
72 -
Joined
-
Last visited
-
Days Won
1 -
Feedback
0%
Content Type
Forums
Store
Third Party - Providers Directory
Feature Plan
Release Notes
Docs
Events
Posts posted by Istny
-
-
carray: 30ms map: 55ms unordered_map :39ms
tested with array size of 1250 elements 1mln random access
- 1
-
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; } } } }
- 10
- 6
- 9
-
9 minutes ago, Sonitex said:
BYTE material_count = 0; for (int i = 0; i < REFINE_MATERIAL_MAX_NUM; i++) { str_to_number(prt->materials[i].vnum, data[col++]); str_to_number(prt->materials[i].count, data[col++]); if (prt->materials[i].vnum) material_count++; } prt->material_count = material_count;
Issue is how they approached counting in the first place. When the loop hits a vnum equal to zero, material count was assigned based on current loop index which means if you had 5 slots occupied you would never hit an empty slot which is why material count would be always zero. By extending the loop you would also need to extend mysql columns which does nothing in this case.
Cleaner, only downside is, if someone for unknow reason will have for example socket3 set to 0 and socket4 to 11 loop in CHARACTER::DoRefine will use value frome socket3 as material vnum which is 0. Summarazing don't do a mess and you are good to go
- 1
-
Fix allow you to use vnum4 in refine_proto
Explanation:
By default refine materials are beign loaded until vnumx == 0, but if you assign item to the last socket, material_count will never be assigned. As a result material_count will remain 0, despite 5 material items being set.
Adding simple additional check solves this problem
Open db/ClientManagerBoot.cpp
replace
for (int i = 0; i < REFINE_MATERIAL_MAX_NUM; i++) { str_to_number(prt->materials[i].vnum, data[col++]); str_to_number(prt->materials[i].count, data[col++]); if (prt->materials[i].vnum == 0) { prt->material_count = i; break; } }
with
for (int i = 0; i < REFINE_MATERIAL_MAX_NUM; i++) { str_to_number(prt->materials[i].vnum, data[col++]); str_to_number(prt->materials[i].count, data[col++]); if (prt->materials[i].vnum == 0) { prt->material_count = i; break; } else if (i+1 == REFINE_MATERIAL_MAX_NUM) { prt->material_count = REFINE_MATERIAL_MAX_NUM; break; } }
final result
- 2
- 1
- 2
-
1 hour ago, Fr3zy94 said:
Wrong. They have like 10 games, and Metin2 is the only game where they don't get direct revenue, they get only share revenue.
Graph shows revenue ONLY from metin2, you can go through webzen financial reports ans see how much money they make from games like MU
Gameforge does not disclose how much money they make from each game, so we will never know how much money in total metin2 generates
-
below graph shows how much webzen makes money of this "dead game" called metin2
- 1
-
CCI * pkCCI = P2P_MANAGER::instance().Find(nickname);
you can find how to use that in source by searching code above
-
3 hours ago, Denny2399 said:
Using mainline? Can you tell me how did you open it etc.?
if you open solution in newer visual studio it will be converted to newer version, however there will be some errors during compilation, but they are easy to fix if you have knowlage of c++ xd
- 1
-
i'm using v141 toolset without any problems
-
There is no simple answer, on my server we are using packer written from scratch with new encryption method and other compression algorithms like lizard, lz4hc
-
2 hours ago, Elmy said:
I dont`t think is from attr, is from the last 2 errors.
mayby you should learn first how to read gdb output
-
broken server_attr
-
probably you are running some queries without index
you have to enable log_queries_not_using_indexes in my.cnf to see where is the problem
-
From what i remember this issue is related to broken server_attr, core go down when player walk into broken part of the map
- 1
-
add
log-queries-not-using-indexes
to my.cnf
and run mysqltuner on server -
Is this the reason why some servers are now offline ? ^.^
-
SECTREE_MANAGER::instance().PurgeMonstersInMap(ch->GetMapIndex());
-
Looks like only germany, but until someone don't tell what happend we can only wondering
We don't know what gameforge done, maybe the only takedown websites, and owners in panic disabled game servers also.
i have hope owners of these server are not coward and tell something more ;x
shiro3 statment
translated via google
Dear Community,
as you know, it came yesterday at 23:00 to the decision to move the server start of the project due to legal reasons indefinitely. I want to mention again that you should please stay calm here. We had this serious decision taken quickly, to the situation in which we find ourselves is clear.
The project Shiro3 is not canceled. It could be that we start this weekend. It may also be that it is shifted by 7 days. To you to name any exact period, we have access to the generalized term "indefinitely".
Thus, of course, should also be clear that your investments, whether money or anticipation going, never lost. You need us as a community some understanding bring to, because we're not talking about a DDoS or hacker attack, but of something far worse.
Therefore, I ask now officially again: Widespread no rumors and tolerated you today. We will not later than tonight the correct information and give everything more known.
Best regards,
The Shiro3 team -
i can give you some data from me
900 players on one channel (one core), about 40 maps. 50% cpu usage of one core e3-1240v2.
there was no core crashes, lags etc
everything depends on optimization ;x
right now i'm using 2 cores per channel, but it's not caused by stabilitiy or something simmilar, I assure- 1
-
1 hour ago, bumxd said:
anybody can say more information?
char.cpp
inside CHARACTER::SetPlayerProto
if (GetGMLevel() > GM_LOW_WIZARD)
-
39 minutes ago, Legor said:
Hello,
it's possible to add delay of 3-10 seconds like logout, with countdown?
Thanks
you can do this with TimedEventInfo
-
nothing new, from couple years is known that closing p servers is too costly for them. if they can't shutdown sg which lost license because of them, especially they won't take down p servers, only couple youtubers movies ;x
-
1 hour ago, Juppsy said:
I was trying with older Crypto++ and the newer boost few minutes ago, and the error are the same. Well, I tink the problem is from boost.
have you added -lcrypto in makefile ?
-
i'm using cryptop 5.6.3 on 64 bit, no problems
[HELP] After starting more than 1 ch, other ch crash after 10-15sec - only ch1 running
in Community Support - Questions & Answers
Posted
Maybe your server don't have enough RAM to run 2 channels at once ?