YARDIM OpenKo Piana ve Breth haritası ekleme

KY Üye
KAYIT
18 Tem 2025
Mesajlar
36
Tepki puanı
4
Kullanılan Source
OpenKO
Kullanılan Database
OpenKO
Kullanılan Client
OpenKO
OpenKO daPiana ve Breth haritası olan paylasabilir mi. Birde yaratık öldürünce kutu görünmüyor bu sorun nasıl çözülür.
 
Moderatör
KAYIT
21 Ağu 2025
Mesajlar
227
Çözümler
2
Tepki puanı
861
Kutu yok openko'da asset ve biraz işçilik istiyor. Harita konusunda ben altyapının güncellenmesi gerektiğini biliyorum ntf, dxt eksik formatlar vs. Fakat ben breth piana haritasını hiç denemedim gidip tak çalıştır çalıştırırsınız demeyin bana çalışmaz demiştin diye eski versiyon diye uyumlu çıkma ihtimali de mevcut.

Harita için de breth ile myko tanıtımı yapan bir sw'nin clientinden alın 2 dklik iş :) beceremezseniz yardımcı olurum..

İlk olarak istediğiniz kutu güncellemesi; (rehberi yazmakla uğraşamayacağım için kendi sourcemden yapay zeka aracına rehber hazırlattım)

Mob ölüm kutusu (drop box) — eksiksiz rehber​

Bu sistem Knight Online’ın orijinal protokolünde zaten var. İstemci WIZ_ITEM_DROP (0x23), WIZ_BUNDLE_OPEN_REQ (0x24), WIZ_ITEM_GET (0x26) paketlerini ve UIDroppedItemDlg penceresini hazır barındırır. Yani istemciye dokunmadan, sadece sunucu doğru paketi yollarsa kutu çalışır. Çoğu kişinin takıldığı yer burası: istemcide arıyor, oysa eksik AIServer/GameServer hattında.

1) Sistem nasıl akıyor​


[AIServer] mob ölür → CNpc::GiveNpcHaveItem()
│ AG_NPC_GIVE_ITEM (opcode 8): killerID, npcID, zone, rx, rz, x, z, y, count, (item,count)*
▼
[GameServer] CAISocket::RecvNpcGiveItem()
│ _LOOT_BUNDLE oluştur → bölgeye ekle, nBundleID = map->m_wBundle++
│ kesene (partideyse partiye) WIZ_ITEM_DROP(0x23): npcID, bundleID
▼
[Client] MsgRecv_ItemBundleDrop()
│ corpse->m_iDroppedItemID = bundleID → CPlayerNPC::Tick() kutuyu çizer
│ oyuncu kutuya tıklar → WIZ_BUNDLE_OPEN_REQ(0x24) + bundleID
▼
[GameServer] CUser::BundleOpenReq() → WIZ_BUNDLE_OPEN_REQ geri: 6 slotluk (itemID,count)*
▼
[Client] MsgRecv_ItemBundleOpen() → UIDroppedItemDlg listesi
│ iteme tıkla → WIZ_ITEM_GET(0x26) + bundleID + itemID
▼
[GameServer] CUser::ItemGet() → envanter/altın ver, bundle'dan düş
son item ise → bölgeye WIZ_ITEM_DROP(npcID, 0) = "kutu gitti, fade ol"

2) Paket sözleşmesi (birebir)​

YönOpcodeİçerik
AI → GSAG_NPC_GIVE_ITEM = 8int16 killerID, int16 npcID, uint8 zone, int16 rx, int16 rz, float x, float z, float y, uint8 count, (int32 itemID, int16 count) * n
GS → ClientWIZ_ITEM_DROP = 0x23int16 npcID, uint32 bundleID
Client → GSWIZ_BUNDLE_OPEN_REQ = 0x24uint32 bundleID
GS → ClientWIZ_BUNDLE_OPEN_REQ = 0x24(uint32 itemID, int16 count) * 6 — eksik slotlar (0,0) ile doldurulmak ZORUNDA
Client → GSWIZ_ITEM_GET = 0x26uint32 bundleID, uint32 itemID
GS → ClientWIZ_ITEM_GET = 0x26uint8 result + result’a göre ek alanlar
WIZ_ITEM_GET sonuç kodları (istemci bunları bekliyor): 0=LootError, 1=LootSolo, 2=PartyCoinDistribution, 3=PartyNotification, 4=PartyItemGivenAway, 5=PartyItemGivenToUs, 6=NoRoom

3) Sunucu tarafı — yapılacaklar​

3a) Veri yapısı (GameDefine.h)​


#define LOOT_ITEMS 6

struct _LOOT_ITEM {
uint32_t nItemID;
uint16_t sCount;
};

struct _LOOT_BUNDLE {
uint32_t nBundleID;
int16_t sNpcID = 0; // kutunun sahibi ceset; kutu gidince istemci cesedi fade eder
std::vector<_LOOT_ITEM> Items;
float x, z, y;
time_t tDropTime;
};

constexpr time_t TIME_LOOT_BUNDLE_REMAIN = 15; // loot'suz kutu ömrü (sn)

3b) Haritaya kutu ekleme (Map.h/.cpp)​

Her harita kendi artan sayacını tutar; ID 1’den başlar (0 = “kutu yok” sinyali, sakın kullanma):


uint32_t m_wBundle = 1; // C3DMap içinde

bool C3DMap::RegionItemAdd(uint16_t rx, uint16_t rz, _LOOT_BUNDLE* pBundle)
{
if (pBundle == nullptr) return false;
Guard lock(m_lock);
CRegion* pRegion = GetRegion(rx, rz);
if (pRegion == nullptr) return false;

pBundle->nBundleID = m_wBundle++;
pRegion->m_RegionItemArray.PutData(pBundle->nBundleID, pBundle);
if (m_wBundle > ZONEITEM_MAX) m_wBundle = 1;
return true;
}
RegionItemRemove: bundle’dan item’i siler, boş kalınca bundle’ı bölgeden siler. Silme işlemi m_RegionItemArray.m_lock altında yapılmalı — yoksa çok thread’li erişimde heap corruption (0xC0000374) yersin.

3c) AIServer — mob ölünce listeyi yolla (Npc.cpp)​


if (bSendDeathPacket) {
SendDeathAnimation(pKiller);
SendExpToUserList();
if (isShowBox()) // anıt / guard tower / scarecrow / hapishane vb. HARİÇ
GiveNpcHaveItem();
}
GiveNpcHaveItem() içinde kesen oyuncunun ID’si (m_sMaxDamageUserid) ve mobun bölge koordinatı (GetRegionX/Z) pakete konur. K_MONSTER_ITEM tablosundan 5 slota drop şansı uygulanır; altın TYPE_MONEY_SID ile gider.

⚠️ Burada iki klasik tuzak var:

  • K_MONSTER_ITEM slot0/slot1 iki ayrı formatta olabiliyor: biri MAKE_ITEM_GROUP indeksi, diğeri doğrudan item kodu. Grup bulunamazsa kodu olduğu gibi kullan (fallback yoksa o slot sessizce düşer, drop gelmez).
  • Çalışan AIServer.exe eski derlenmişse drop hiç üretilmez. Drop kodu değiştiyse AIServer’ı yeniden derleyip yeniden başlat.

3d) GameServer — bundle oluştur (AISocket.cpp)​


void CAISocket::RecvNpcGiveItem(Packet& pkt)
{
Packet result(WIZ_ITEM_DROP);
int16_t sUid, sNid, regionx, regionz;
float fX, fZ, fY;
uint8_t byCount, bZone;
int nItemNumber[NPC_HAVE_ITEM_LIST];
int16_t sCount[NPC_HAVE_ITEM_LIST];

pkt >> sUid >> sNid >> bZone >> regionx >> regionz >> fX >> fZ >> fY >> byCount;
for (int i = 0; i < byCount; i++) pkt >> nItemNumber >> sCount;

C3DMap* pMap = g_pMain->GetZoneByID(bZone);
if (pMap == nullptr || sUid < 0 || sUid >= MAX_USER) return;

auto* pBundle = new _LOOT_BUNDLE;
pBundle->sNpcID = sNid; // ceset eşleşmesi BUNUNLA olur
pBundle->tDropTime = UNIXTIME;
pBundle->x = fX; pBundle->z = fZ; pBundle->y = fY;

for (int i = 0; i < byCount; i++) {
if (g_pMain->GetItemPtr(nItemNumber) == nullptr) continue;
_LOOT_ITEM item(nItemNumber, sCount);
if (nItemNumber == ITEM_GOLD) { /* coin event çarpanı, USHRT_MAX clamp */ }
pBundle->Items.push_back(item);
}

if (!pMap->RegionItemAdd(regionx, regionz, pBundle)) { delete pBundle; return; }

CUser* pUser = g_pMain->GetUserPtr(sUid);
if (pUser == nullptr) return;

result << sNid << pBundle->nBundleID; // int16 npcID + uint32 bundleID
if (!pUser->isInParty()) pUser->Send(&result);
else g_pMain->Send_PartyMember(pUser->GetPartyID(), &result);
}

3e) GameServer — kutu açma ve item alma (User.cpp)​


// HandlePacket içinde:
case WIZ_BUNDLE_OPEN_REQ: BundleOpenReq(pkt); return true;
case WIZ_ITEM_GET: ItemGet(pkt); return true;

void CUser::BundleOpenReq(Packet& pkt)
{
Packet result(WIZ_BUNDLE_OPEN_REQ);
uint32_t bundle_index = pkt.read<uint32_t>();
C3DMap* pMap = GetMap(); CRegion* pRegion = GetRegion();
if (pMap == nullptr || pRegion == nullptr || bundle_index < 1 || isDead()) return;

Guard lock(pRegion->m_RegionItemArray.m_lock);
_LOOT_BUNDLE* pBundle = pRegion->m_RegionItemArray.GetData(bundle_index);
if (pBundle == nullptr || !isInRange(pBundle->x, pBundle->z, MAX_LOOT_RANGE)) return;

foreach (itr, pBundle->Items) result << itr->nItemID << itr->sCount;
for (uint32_t i = pBundle->Items.size(); i < LOOT_ITEMS; i++)
result << uint32_t(0) << uint16_t(0); // dolgu ŞART
Send(&result);
}
ItemGet() içinde:

  1. Oyuncunun bölgesi + 8 komşu bölgede bundle’ı ara (oyuncu bölge sınırının öbür tarafında olabilir!).
  2. bundleID + itemID doğrula, menzil kontrolü (bAutoLoot ? RANGE_50M : MAX_LOOT_RANGE).
  3. Altın ise: partide değilse hepsi sana, partideyse level oranıyla dağıtılır.
  4. Item ise: FindSlotForItem + CheckWeight → yer yoksa LootNoRoom dön, item’i silme.
  5. Item’i envantere yaz, sonra RegionItemRemove.
  6. Bundle boşaldıysa bölgeye haber ver:

if (bBundleNowEmpty && sNpcID != 0) {
Packet notify(WIZ_ITEM_DROP);
notify << sNpcID << uint32_t(0); // 0 = kutu gitti
SendToRegion(&notify);
}

3f) Zaman aşımı süpürgesi (GameServerDlg.cpp)​

Timer_CheckGameEvents() içinden saniyede bir çağır:


void CGameServerDlg::LootBundleSweep()
{
// her zon, her bölge:
if (pBundle && (UNIXTIME - pBundle->tDropTime) >= TIME_LOOT_BUNDLE_REMAIN) {
int16_t sNpcID = pBundle->sNpcID; float fX = pBundle->x, fZ = pBundle->z;
delete pBundle;
it = regionItems.m_UserTypeMap.erase(it);
if (sNpcID != 0) {
Packet notify(WIZ_ITEM_DROP);
notify << sNpcID << uint32_t(0);
Send_NearRegion(&notify, pMap, rx, rz, fX, fZ);
}
}
}
Süpürge yoksa: hem bellek sızıntısı, hem loot’lanmayan kutuların sonsuza kadar ortada kalması.

4) İstemci tarafı​

Değiştirilecek bir şey yok. Sadece kontrol et:

  • CPlayerNPC kutuyu Misc\itembox.n3shape’ten yüklüyor — asset mevcut olmalı.
  • MsgRecv_ItemBundleDrop cesedi NPCGetByID / CorpseGetByID ile bulabilmeli. Mob ölür ölmez sunucudan siliniyorsa kutu hiç görünmez; ceset kutu boşalana kadar durmalı.
  • GameDef.h: MAX_ITEM_BUNDLE_DROP_PACKET = 6 (GS’nin doldurduğu slot sayısıyla aynı olmalı).
Eğimli arazide kutu yarı gömülüyorsa: kutunun XZ’sinde GetHeightWithTerrain(pos.x, pos.z) ile zemin Y’sini al, üstüne küçük bir pay ekle (+0.15 gibi).

5) “Çalışmıyor” diyorsan sırayla bak​

#BelirtiSebep
1Kutu hiç çıkmıyorAIServer drop üretmiyor (eski exe / sPercent=0 / grup fallback yok)
2Kutu çıkıp hemen kayboluyorCeset erken siliniyor veya bundleID == 0 yollanıyor
3Kutu var, tıklayınca boşGS→client (0,0) dolgusu eksik
4Tıklayınca hiç açılmıyorisInRange(MAX_LOOT_RANGE) çok dar (11 birim)
5Item alınmıyor, hata yokItemGet bölge araması sadece kendi bölgesinde
6Nadir crash 0xC0000374m_RegionItemArray.m_lock kilidi yok
7Kutu sonsuz duruyorLootBundleSweep() çağrılmıyor

6) Test listesi​

  1. Tek başına mob kes → kutu çıkıyor mu, 15 sn sonra kayboluyor mu?
  2. Kutuya bas → item listesi geliyor mu, item envantere giriyor mu?
  3. Son item alınınca ceset fade oluyor mu?
  4. Partide: drop kesene mi gidiyor, altın bölünüyor mu?
  5. Envanter dolu → “yer yok” hatası veriyor, item yerde kalıyor mu?
  6. Bölge sınırında loot → komşu bölgeden alınabiliyor mu?
  7. Anıt / guard tower / scarecrow ölünce kutu çıkmamalı (isShowBox()).
 
Üst Alt