- #1
Konu Sahibi
: serkankacan
[C++ FIX] Knight Online v19xx/v20xx Karakter Görünmeme & Region Görüntü Bugu Çözümü
UserInOutForMe & RegionUserInOutForMe Paket Offset Senkronizasyon Düzeltmesi
UserInOutForMe & RegionUserInOutForMe Paket Offset Senkronizasyon Düzeltmesi
Merhaba değerli forum sakinleri,
Knight Online v1960 ve benzeri sürümlerde (özel GameServer kaynak kodlarında) Moradon pazar alanı, Colony Zone veya savaş haritalarında sıkça karşılaşılan;
- Oyuncuların birbirini görememesi / görünmez olması,
- Gate (ışınlanma) sonrası etraftaki karakterlerin geç yüklenmesi veya hiç çizilmemesi,
- Bölge (Region) sınırlarından geçerken anlık takılmalar ve client paket düşmeleri (drop),
Teknik Teşhis & Hatanın Nedeni' Alıntı:RegionUserInOutForMefonksiyonundaresult(WIZ_REGIONCHANGE, uint8(1))tanımlandığında paketin ilk 2 baytı opcode ve sub-opcode tarafından rezerve edilir. Ardından gelen 2 baytlık placeholder sayaç, paketin **2. indeksinden** başlar.
Orijinal kodda yer alanresult.put(1, ...)komutu, sub-opcode'un üzerine veri yazarak paketin imzasını bozar. İstemci bozuk paketi okuyamadığı için çevredeki kullanıcıları render edemez.
Düzeltilmiş ve Optimize Edilmiş C++ Kodları
GameServerDlg.cpp dosyanızı açarak mevcut fonksiyonları aşağıdaki hatasız ve optimize bloklar ile değiştiriniz:
C++:
void CGameServerDlg::UserInOutForMe(CUser *pSendUser)
{
if (pSendUser == nullptr)
return;
Packet result(WIZ_REQ_USERIN);
C3DMap* pMap = pSendUser->GetMap();
ASSERT(pMap != nullptr);
uint16 user_count = 0;
result << uint16(0); // 2 bayt sayaç alanı ayrılıyor (Offset: 0)
int16 rx = pSendUser->GetRegionX();
int16 rz = pSendUser->GetRegionZ();
foreach_region(x, z)
GetRegionUserIn(pMap, rx + x, rz + z, result, user_count, pSendUser->GetEventRoom());
// Sayacı paketin başlangıç noktasına (Offset 0) yazıyoruz
result.put(0, uint16(user_count));
// Kalabalık alanlarda bant genişliğini korumak için sıkıştırma kontrolü
if (user_count < 100)
pSendUser->Send(&result);
else
pSendUser->SendCompressed(&result);
}
void CGameServerDlg::RegionUserInOutForMe(CUser *pSendUser)
{
if (pSendUser == nullptr)
return;
// 1. ADIM: Bölge temizleme / sıfırlama bildirimi (Küçük paketlerde SendCompressed KULLANMAYINIZ)
Packet Ref(WIZ_REGIONCHANGE, uint8(0));
pSendUser->Send(&Ref);
// 2. ADIM: Çevredeki kullanıcı listesini toplama
Packet result(WIZ_REGIONCHANGE, uint8(1));
C3DMap* pMap = pSendUser->GetMap();
ASSERT(pMap != nullptr);
uint16 user_count = 0;
result << uint16(0); // WIZ_REGIONCHANGE (1 byte) + SubOpcode 1 (1 byte) = Sayaç 2. bayttan başlar!
int16 rx = pSendUser->GetRegionX();
int16 rz = pSendUser->GetRegionZ();
foreach_region(x, z)
GetRegionUserList(pMap, rx + x, rz + z, result, user_count, pSendUser->GetEventRoom());
// KRİTİK FİX: Offset 1 değil, kesinlikle 2 olmalıdır!
result.put(2, uint16(user_count));
if (user_count < 100)
pSendUser->Send(&result);
else
pSendUser->SendCompressed(&result);
// 3. ADIM: Bölge güncelleme tamamlandı bildirimi
Packet IP(WIZ_REGIONCHANGE, uint8(2));
pSendUser->Send(&IP);
}
Yapılan Kritik Değişiklikler Nelerdir?
- Offset İndeks Düzeltmesi (
result.put(2, ...)):result.put(1, ...)hatası giderilerek sayaç doğru bellek adresine yazıldı; böylece istemci gelen oyuncu sayısını eksiksiz okur. - Gereksiz Sıkıştırma Kaldırıldı: Yalnızca 2 bayttan oluşan `Ref` ve `IP` paketleri doğrudan
Send()ile iletilerek CPU yükü ve LZF paket başlığı kirliliği engellendi. - Sıkıştırma Eşiği İyileştirildi: Sıkıştırma limiti 500'den 100'e çekildi. 100+ oyuncunun olduğu kalabalık bölgelerde paket boyutu küçültülerek istemciye iletim hızı artırıldı.
Nasıl Uygulanır?
- GameServer projenizi Visual Studio ile açın.
- GameServerDlg.cpp içerisindeki ilgili iki fonksiyonu yukarıdaki kod ile güncelleyin.
- GameServer projenizi Release modunda yeniden derleyin (Rebuild Solution).
- Yeni GameServer.exe ile sunucunuzu başlatıp kalabalık haritalarda test edin.
Sorularınızı ve takıldığınız yerleri bu konu altından iletebilirsiniz. Herkese iyi geliştirmeler!

