[C++] Windows Drop Bugu ve Random Fixi

  • Konuyu açan Konuyu açan Sys
  • Açılış Tarihi Açılış Tarihi
  • Yanıt Yanıt 5
  • Gösterim Gösterim 152

Sys

MT Üye
MT Üye
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ı:

Fix:
Genişlet Daralt Kopyala
// ================================================================================
// 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.
 
lan hani yakalanıyordun
 
Olum iş yerinde felan boş oldukça bakıyorum evde 1-2 saat pc başında vakit geçirmeme izin veriyor 10+ saat geçirince izinli günde papaz oluyoruz :D
ara sıra çıkın gezin sizde 10 saat ne kardeşimmm
 
windowsta drop ayarlayıp oyuna verince şok yaşayıp farkedip fixlediğim konuydu eline sağlık
 
Geri
Üst