Jump to content

Istny

Premium
  • Posts

    72
  • Joined

  • Last visited

  • Days Won

    1
  • Feedback

    0%

Posts posted by Istny

  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;
    			}
    		}
    	}
    }

     

    • Metin2 Dev 10
    • Good 6
    • Love 9
  2. 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 😀

    • Love 1
  3. 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

       spacer.png

     

     

    • Angry 2
    • Good 1
    • Love 2
  4. 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 ?

    GKbRPMR.png

     

  5. 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

×
×
  • 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.