- Mesaj
- 268
- Çözümler
- 4
- Beğeni
- 423
- Puan
- 784
- Ticaret Puanı
- 0
Merhaba arkadaşlar,
Özellikle Windows üzerinde geliştirme yapanların veya 64-bit altyapılara geçenlerin sıkça karşılaştığı moblardan aşırı derecede item ve pot saçılması sorununun ana çözümünü paylaşıyorum.
Sorunun Kaynağı:
1) Windows CRT rand() Sınırı: Metin2 çekirdeği drop hesaplamalarını 4.000.000 üzerinden yapar. Windows derleyicilerinde eski C rand() fonksiyonu en fazla 32.767 üretir. Atılan zar 32.767 değerini geçemediği için drop oranları tam 122 kat daha yüksek çıkar.
2) Modulo Sapması: Eski yüzde operatörü ile yapılan zar hesaplamaları matematiksel sapmaya yol açar.
3) GetDropPct Parantez Hatası: iRandRange hesaplanırken hırsız eldiveni ternary operatörü parantez içine alınmadığı için artı operatörü öncelik alarak formülü bozar.
4) CreateDropItem Döngüsünde break Eksikliği: common_drop_item.txt içerisinde 1-15 level arası 54 farklı eşya bulunur. Döngü bir eşya düşürdüğünde durmadığı için tek bir slottan 54 eşyanın tamamı için ayrı ayrı zar atılır ve yere aynı anda birden fazla eşya düşer.
Çözüm Adımları:
İşlem bu kadar. Artık hem Windows hem FreeBSD üzerinde, 32-bit ve 64-bit fark etmeksizin tüm drop oranları orijinal Metin2 TR standartlarında çalışacaktır.
Özellikle Windows üzerinde geliştirme yapanların veya 64-bit altyapılara geçenlerin sıkça karşılaştığı moblardan aşırı derecede item ve pot saçılması sorununun ana çözümünü paylaşıyorum.
Sorunun Kaynağı:
1) Windows CRT rand() Sınırı: Metin2 çekirdeği drop hesaplamalarını 4.000.000 üzerinden yapar. Windows derleyicilerinde eski C rand() fonksiyonu en fazla 32.767 üretir. Atılan zar 32.767 değerini geçemediği için drop oranları tam 122 kat daha yüksek çıkar.
2) Modulo Sapması: Eski yüzde operatörü ile yapılan zar hesaplamaları matematiksel sapmaya yol açar.
3) GetDropPct Parantez Hatası: iRandRange hesaplanırken hırsız eldiveni ternary operatörü parantez içine alınmadığı için artı operatörü öncelik alarak formülü bozar.
4) CreateDropItem Döngüsünde break Eksikliği: common_drop_item.txt içerisinde 1-15 level arası 54 farklı eşya bulunur. Döngü bir eşya düşürdüğünde durmadığı için tek bir slottan 54 eşyanın tamamı için ayrı ayrı zar atılır ve yere aynı anda birden fazla eşya düşer.
Çözüm Adımları:
Fix:
// ================================================================================
// 1. ADIM
// Açılacak Dosya: libthecore/src/utils.c (veya utils.cpp)
// ================================================================================
// [ARAT] (Dosyanın en üstündeki include satırlarında)
#include "stdafx.h"
// [ALTINA EKLE]
#include <random>
// --------------------------------------------------------------------------------
// [ARAT]
DWORD thecore_random()
{
#ifdef __WIN32__
return rand();
#else
return random();
#endif
}
int number_ex(int from, int to, const char *file, int line)
{
if (from > to)
{
int tmp = from;
sys_err("number(): first argument is bigger than second argument %d -> %d, %s %d", from, to, file, line);
from = to;
to = tmp;
}
int returnValue = 0;
if ((to - from + 1) != 0)
returnValue = ((thecore_random() % (to - from + 1)) + from);
else
sys_err("number(): devided by 0");
return returnValue;
}
float fnumber(float from, float to)
{
return (((float)thecore_random() / (float)RAND_MAX) * (to - from)) + from;
}
// [KOMPLE DEĞİŞTİR]
static thread_local std::mt19937 g_rng([]() {
std::random_device rd;
return std::mt19937(rd());
}());
DWORD thecore_random()
{
return static_cast<DWORD>(g_rng());
}
int number_ex(int from, int to, const char *file, int line)
{
if (from > to)
{
sys_err("number(): first argument is bigger than second argument %d -> %d, %s %d", from, to, file, line);
const int tmp = from;
from = to;
to = tmp;
}
std::uniform_int_distribution<int> dist(from, to);
return dist(g_rng);
}
float fnumber(float from, float to)
{
if (from > to)
{
const float tmp = from;
from = to;
to = tmp;
}
std::uniform_real_distribution<float> dist(from, to);
return dist(g_rng);
}
// ================================================================================
// 2. ADIM
// Açılacak Dosya: game/src/item_manager.cpp
// ================================================================================
// [ARAT]
iRandRange = 4000000;
iRandRange = iRandRange * 100 /
(100 +
CPrivManager::instance().GetPriv(pkKiller, PRIV_ITEM_DROP) +
pkKiller->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_ITEM)?100:0);
// [DEĞİŞTİR]
iRandRange = 4000000;
iRandRange = iRandRange * 100 /
(100 +
CPrivManager::instance().GetPriv(pkKiller, PRIV_ITEM_DROP) +
(pkKiller->IsEquipUniqueItem(UNIQUE_ITEM_DOUBLE_ITEM) ? 100 : 0));
// ================================================================================
// 3. ADIM
// Açılacak Dosya: game/src/item_manager.cpp
// ================================================================================
// [ARAT]
// Common Drop Items
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;
int iPercent = (c_rInfo.m_iPercent * iDeltaPercent) / 100;
sys_log(3, "CreateDropItem %d ~ %d %d(%d)", c_rInfo.m_iLevelStart, c_rInfo.m_iLevelEnd, c_rInfo.m_dwVnum, iPercent, c_rInfo.m_iPercent);
if (iPercent >= number(1, iRandRange))
{
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);
}
}
// [KOMPLE DEĞİŞTİR]
// Common Drop Items
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;
int iPercent = (c_rInfo.m_iPercent * iDeltaPercent) / 100;
sys_log(3, "CreateDropItem %d ~ %d %d(%d)", c_rInfo.m_iLevelStart, c_rInfo.m_iLevelEnd, c_rInfo.m_dwVnum, iPercent, c_rInfo.m_iPercent);
if (iPercent >= number(1, iRandRange))
{
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);
break;
}
}
}
İşlem bu kadar. Artık hem Windows hem FreeBSD üzerinde, 32-bit ve 64-bit fark etmeksizin tüm drop oranları orijinal Metin2 TR standartlarında çalışacaktır.
