OYUN GÜNDEMİ

Metin2 CHARACTER::Sync Cannot Find Tree Fix’i

Metin2 source tarafında uzun süredir karşılaşılan hatalardan biri de CHARACTER::Sync fonksiyonunda görülen Cannot Find Tree problemidir. Bu hata genelde karakter, mob ya da metin taşı harita üzerinde geçerli bir sectree bulamadığında ortaya çıkar. Basit gibi duruyor ama yanlış fixlenirse drop, dead event, dungeon ve hatta potansiyel abuse tarafında can sıkıcı durumlar çıkarabilir.

image-438 Metin2 CHARACTER::Sync Cannot Find Tree Fix'i

Bu paylaşımda klasik “Dead(); bas geç” mantığından biraz daha temiz bir çözüm uygulanıyor. Amaç şu: Eğer sorun bir mob veya metin taşı üzerinden oluşuyorsa, bu varlık normal ölüm sistemiyle değil, ödülsüz şekilde temizlensin. Böylece oyuncuya gereksiz drop, yang veya quest tetikleme gibi ekstra şeyler verilmesin.

Problem Nedir?

CHARACTER::Sync fonksiyonu karakterin veya oyun içi varlığın yeni koordinata senkronlanmasını sağlar. Fakat bazı durumlarda yeni konum için sectree bulunamaz. Bu durumda kaynak kodda genelde No Tree veya Cannot Find Tree tarzı hata alınır.

image-439 Metin2 CHARACTER::Sync Cannot Find Tree Fix'i

Özellikle moblar ve metin taşları için bu durum oluştuğunda direkt Dead(); çağırmak her zaman sağlıklı değildir. Çünkü normal ölüm fonksiyonu drop, ödül, quest veya dungeon tetikleri gibi şeyleri devreye sokabilir.

Bu yüzden daha temiz bir yol olarak RewardlessDead() adında yeni bir fonksiyon oluşturuluyor.

Yapılacak İşlem

Bu düzenlemede 3 dosya üzerinde işlem yapılır:

char_battle.cpp
char.cpp
char.h

İlk olarak char_battle.cpp içinde yeni bir ölüm fonksiyonu eklenir. Bu fonksiyon sadece mob ve metin taşları için kullanılacak şekilde düşünülür.

char_battle.cpp Düzenlemesi

Dosyada şu satır bulunur:

struct FuncSetLastAttacked

Bu satırın üstüne aşağıdaki fonksiyon eklenir:

void CHARACTER::RewardlessDead()
{
if (IsPC())
{
sys_log(0, "<CHARACTER::RewardlessDead> Wrong Usage! It was a PC, redirect it to regular Dead()");
Dead();
return;
} if (IsDead())
{
return;
} if (IsMonster() || IsStone())
{
m_dwKillerPID = 0;
SET_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_NO_REWARD);
SetPosition(POS_DEAD);
ClearAffect(true);
ClearSync(); if (m_pkStunEvent)
{
event_cancel(&m_pkStunEvent);
} TPacketGCDead pack;
pack.header = HEADER_GC_DEAD;
pack.vid = m_vid;
PacketAround(&pack, sizeof(pack)); REMOVE_BIT(m_pointsInstant.instant_flag, INSTANT_FLAG_STUN); sys_log(0, "Rewardless_DEAD: %s %p", GetName(), this); if (m_pkDeadEvent)
{
event_cancel(&m_pkDeadEvent);
} if (IsStone())
{
ClearStone();
} if (GetDungeon())
{
GetDungeon()->DeadCharacter(this);
} SCharDeadEventInfo* pEventInfo = AllocEventInfo<SCharDeadEventInfo>();
pEventInfo->isPC = false;
pEventInfo->dwID = this->GetVID(); m_pkDeadEvent = event_create(dead_event, pEventInfo, PASSES_PER_SEC(0)); sys_log(0, "Rewardless_DEAD_EVENT_CREATE: %s %p %p", GetName(), this, get_pointer(m_pkDeadEvent));
}
}

Burada yapılan şey aslında basit. Mob veya metin taşı problemli bir konuma düştüğünde normal Dead() ile ödül dağıtarak öldürülmüyor. Onun yerine INSTANT_FLAG_NO_REWARD verilerek sistem daha güvenli şekilde temizleniyor.

PC yani oyuncu karakteri için bu fonksiyon kullanılmamalı. Zaten içeride kontrol var; yanlışlıkla oyuncu üzerinde tetiklenirse normal Dead() fonksiyonuna yönlendiriyor.

char.cpp Düzenlemesi

Şimdi char.cpp dosyasına geçiyoruz.

Şu fonksiyon bulunur:

bool CHARACTER::Sync(long x, long y)

Bu fonksiyon komple aşağıdaki düzenlenmiş haliyle değiştirilir:

bool CHARACTER::Sync(long x, long y)
{
LPSECTREE current_tree = GetSectree(); if (!current_tree)
{
sys_err("<CHARACTER::Sync> Sectree is NULL!");
return false;
} if (IsPC() && IsDead())
{
return false;
} LPSECTREE new_tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), x, y); if (!new_tree)
{
if (GetDesc())
{
sys_err("No Tree: (x: %ld) (y: %ld) (Name: %s) (Map: %d)", x, y, GetName(), GetMapIndex()); x = GetX();
y = GetY();
new_tree = current_tree; if (!new_tree)
{
sys_err("[CRITICAL!!] - No Tree: (x: %ld) (y: %ld) (Name: %s) (Map: %d)", x, y, GetName(), GetMapIndex());
GetDesc()->SetPhase(PHASE_CLOSE);
return false;
}
}
else
{
if (IsMonster() || IsStone())
{
sys_err("[MOB or STONE]No Tree: (x: %ld) (y: %ld) (Map: %d)", x, y, GetMapIndex());
RewardlessDead();
return false;
}
else
{
sys_err("[POTENTIAL HACK!]No Tree: (x: %ld) (y: %ld) (Map: %d)", x, y, GetMapIndex());
return false;
}
}
} SetRotationToXY(x, y);
SetXYZ(x, y, 0); if (GetDungeon())
{
if (new_tree)
{
int iLastEventAttr = m_iEventAttr;
m_iEventAttr = new_tree->GetEventAttribute(x, y); if (m_iEventAttr != iLastEventAttr)
{
if (GetParty())
{
quest::CQuestManager::instance().AttrOut(GetParty()->GetLeaderPID(), this, iLastEventAttr);
quest::CQuestManager::instance().AttrIn(GetParty()->GetLeaderPID(), this, m_iEventAttr);
}
else
{
quest::CQuestManager::instance().AttrOut(GetPlayerID(), this, iLastEventAttr);
quest::CQuestManager::instance().AttrIn(GetPlayerID(), this, m_iEventAttr);
}
}
}
else
{
sys_err("[POTENTIAL HACK!] Dungeon - There is no new tree!");
return false;
}
} if (current_tree != new_tree)
{
if (!IsNPC())
{
if (current_tree && new_tree)
{
SECTREEID id = new_tree->GetID();
SECTREEID old_id = current_tree->GetID(); const float fDist = DISTANCE_SQRT(id.coord.x - old_id.coord.x, id.coord.y - old_id.coord.y); sys_log(0, "SECTREE DIFFER: %s %dx%d was %dx%d dist %.1fm", GetName(), id.coord.x, id.coord.y, old_id.coord.x, old_id.coord.y, fDist);
}
else
{
sys_err("[POTENTIAL HACK!] There is no current tree or new tree!");
return false;
}
} if (new_tree)
{
new_tree->InsertEntity(this);
}
else
{
sys_err("[POTENTIAL HACK!] InsertEntity NOT possible because there is no new tree!");
return false;
}
} return true;
}

Bu yeni Sync düzenlemesinde birkaç önemli nokta var.

İlk olarak mevcut sectree bir kere alınıyor ve kontrol ediliyor. Eğer mevcut tree yoksa fonksiyon direkt false dönüyor. Sonra oyuncu ölü durumdaysa gereksiz sync işlemi yapılmıyor.

Asıl önemli kısım new_tree bulunamadığında başlıyor. Eğer bu bir oyuncuysa ve bağlantısı varsa, oyuncu mevcut konumuna geri çekiliyor. Ama mob veya metin taşı gibi bir varlıkta tree bulunamıyorsa, bu sefer RewardlessDead() çalışıyor. Yani problemli mob/metin temizleniyor ama oyuncuya ödül vermiyor.

Bu daha temiz bir fix, çünkü hem hata loglanıyor hem de sistem rastgele drop veya quest tetiklemeden devam ediyor.

char.h Düzenlemesi

Son olarak char.h dosyasında şu satır bulunur:

void Dead(LPCHARACTER pkKiller = NULL, bool bImmediateDead = false);

Altına şu satır eklenir:

void RewardlessDead();

Bu satır eklenmezse derleme sırasında fonksiyon tanımsız kalır. O yüzden char.h kısmı unutulmamalı.

Neden Normal Dead Yerine RewardlessDead?

Normal Dead() fonksiyonu Metin2 source içinde birçok şeyi tetikleyebilir. Drop, quest, dungeon, killer bilgisi ve benzeri işlemler devreye girebilir. Ama burada amaç mobu gerçekten öldürüp ödül vermek değil; hatalı konuma düşen veya sectree bulamayan varlığı güvenli şekilde temizlemek.

Bu yüzden m_dwKillerPID = 0 yapılıyor ve INSTANT_FLAG_NO_REWARD setleniyor. Böylece sistem “bu mob öldü ama ödül dağıtma” mantığında çalışıyor.

Kısa Not

Bu fix özellikle source tarafında çalışan kişiler için daha sağlıklı bir çözüm. Sadece hata mesajını susturmak ya da problemli yerde direkt Dead(); çağırmak uzun vadede başka sorunlar doğurabilir.

Düzenleme sonrası game source yeniden derlenmeli ve test edilmelidir. Özellikle dungeon, metin taşı, mob spawn ve farklı maplerde log kontrolü yapmak iyi olur.

Sonuç

CHARACTER::Sync içinde yaşanan Cannot Find Tree problemini daha güvenli şekilde çözmek istiyorsanız, mob ve metin taşları için ödülsüz ölüm sistemi kullanmak mantıklı bir yaklaşım. Bu yöntem hem gereksiz drop oluşmasını engeller hem de problemli entity’lerin sistemde takılı kalmasının önüne geçer.

Bu düzenleme özellikle stabilite isteyen PvP serverlar için işe yarar. Küçük gibi durur ama source tarafında böyle fixler biriktikçe server daha temiz çalışır.

Kaynak: Server source CHARACTER::Sync ve RewardlessDead düzenlemesi. Kullanıcının paylaştığı orijinal notta çözümün Cannot Find Tree problemi, PC olmayan varlıklarda Dead() kullanımının doğurabileceği hatalar ve RewardlessDead() fonksiyonu üzerinden ele alındığı belirtiliyor.

Paylaşım: MMOOYUN.COM

Not:

Daha fazla Metin2 source düzenlemesi, hazır sistem, files satın alma ve teknik destek için geliştirici tarafındaki adresimizden bize ulaşabilirsiniz:

dev.mmooyun.com

Paylaş :

Bu Kategoriden

Metin2 Cinsiyet Değiştirme Quest Sistemi: Sorunsuz Çalışan Güncel Kod 09.05.2026 Metin2 sistem ekleme aracı ile source dosyalarına otomatik kod uygulama ve patch sürecini gösteren arayüz görseli. Metin2 Sistem Ekleme Aracı: Kodları Otomatik Uygulayan Yardımcı Program 09.05.2026 Metin2 ruh taşı scripti ile Grand Master beceri geliştirme sistemini anlatan Seungryong Vadisi görseli. Metin2 Ruh Taşı Scripti: Tek Seferde Çoklu Grand Master Eğitimi 06.05.2026 Metin2 Shader Sistemi: Grafik Kodlarında Tam Temizlik ve Stabilizasyon Süreci 06.05.2026
Okuma Önerisi

Benzer rehberlere ve güncel oyun içeriklerine göz atabilirsin.

Okumaya Devam Et

Subnautica 2'de derin deniz keşiflerini mümkün kılan Modification Station ve araç modüllerinin üretim paneli. Subnautica 2 Modification Station Rehberi: Araç Yükseltmeleri ve Celestine Tarımı 19.05.2026 Subnautica 2 Biomod yetenek listesindeki en güçlü siber-biyolojik gen mutasyonları ve hücre paneli gösterimi. Subnautica 2 En İyi Yetenekler Tier List: En Güçlü Biomod Seçimleri 19.05.2026 Subnautica 2 Feedback Resonator yükseltmesini açmak için Root Canyon biyomunda taranması gereken antik laboratuvar kalıntıları. Subnautica 2 Feedback Resonator Nasıl Alınır? Root Canyon ve Tarama Rehberi 19.05.2026 Subnautica 2 Power Center ana salonunda yer alan ve dördüncü Angel Comb ile kilitleri açılan devasa uzaylı türbini. Subnautica 2 Power Center Türbini Nasıl Çalıştırılır? Alien Ruins Bulmaca Rehberi 19.05.2026

Hızlı Geçiş

Rehber Haber Önizleme Mobil Oyun