Bloga Dön

UEFN'de Bir Oyuncu Ayrıldığında Verse Weak Map'ler Otomatik Olarak Temizlenir mi?

Yayınlanma tarihi 5 Haziran 2026
UEFN'de Bir Oyuncu Ayrıldığında Verse Weak Map'ler Otomatik Olarak Temizlenir mi?

Özet olarak

Bu makale, UEFN'de Verse `weak_map` yapılarının oyuncular oyundan ayrıldığında verileri otomatik olarak temizlemediğini ve bunun sessiz bellek sızıntıları ile runtime çökmelerine yol açtığını açıklamaktadır. Kalıcı veri ve geçici oturum durumları (session states) arasındaki farklar ele alınmakta, oyuncu ayrıldığında yapılması gereken manuel temizleme yöntemleri kod örnekleriyle gösterilmektedir. Son olarak, UEFN limitlerine takılmadan performansı korumak için en iyi pratikler ve bulut tabanlı backend çözümleri sunulmaktadır.

AI kodlama asistanınızın, bir oyuncu Fortnite adanızdan ayrıldığı milisaniyede Verse'ün oyuncu verilerini bir weak_map yapısından otomatik olarak temizlediğini söylemesine güveniyorsanız, kendinizi ciddi bir runtime çökmesine hazırlıyorsunuz demektir. Bu iddia kulağa mantıklı geliyor: zayıf bir referans (weak reference) olduğu için, player nesnesi Garbage Collection işlemine tabi tutulduğunda oyun motorunun anahtarı temizlemesi gerekir. Ancak Unreal Editor for Fortnite (UEFN) ortamında gerçek bundan çok daha karmaşıktır ve Verse bellek yöneticisinin (memory manager) oyuncu yaşam döngülerini (lifecycle) nasıl işlediğini yanlış anlamak, sessiz durum (state) sızıntılarına ve oyunu bozan istisnalara (exception) yol açar.

Bir oyuncu ayrıldığında, bir map içinde onun eskiyen (stale) nesnesine referans vermek korkunç ErrRuntime_WeakMapInvalidKey hatasını tetikleyebilir veya ada genelinde çökmelere neden olabilir; bu da sunucularınızı stabil tutmak için katı bir UEFN server crash fix protocol uygulamanızı gerektirir. Bunu önlemek için geliştiricilerin, Verse'ün belleği dahili olarak nasıl yönettiğini ve kesin (authoritative) temizleme rutinlerini nasıl uygulayacaklarını öğrenmeleri gerekir.

Yanılgı: Yapay Zeka Tavsiyeleri vs. Verse Gerçekleri

Pek çok geliştirici yapay zeka asistanlarına, bir oyuncu maçtan ayrıldığında onun map verilerini nasıl işleyeceklerini soruyor. Sıkça karşılaşılan bir yapay zeka tavsiyesi, motorun oyuncu anahtarını (player key) bir "weak reference" olarak ele aldığı ve ayrılma anında oyuncunun girdisini (entry) map yapısından otomatik olarak sildiği yönündedir. Bu tamamen yanlıştır.

Verse'in weak_map(player, t) yapısı, Garbage Collection işlemini engelleyecek güçlü referans döngülerini (hard reference cycles) önlemek için arka planda weak reference'ları anahtar olarak kullansa da, map girdilerinin (entry) kendisinde otomatik ve anında bir temizlik gerçekleştirmez. Hem anahtar yuvasını (key slot) hem de onunla ilişkili verileri içeren girdi, map konteynerinde ayrılmış (allocated) olarak kalmaya devam eder.

Eğer kodunuz, oyuncu ayrıldıktan sonra bu anahtara erişmeye, onu değerlendirmeye veya değiştirmeye çalışırsa, Verse runtime boş (null) veya geçersiz bir player nesnesinin referansını çözmeye (dereference) çalışacaktır. Çalışma zamanı bu durumu kontrollü bir hata ile geçiştirmek yerine çökmeyi tetikler veya yakalanamayan bir exception fırlatır. Sistem, otomatik temizliğe güvenmek yerine yaşam döngüsü (lifecycle) geçişlerini açıkça yönetmenizi bekler.

Weak Map'ler Neden Oyuncu Girdilerini Otomatik Temizlemez?

Bunun neden gerçekleştiğini anlamak için, UEFN'deki bir weak_map yapısının amacına bakmalıyız. Weak map'lerin geçici bellek önbellekleri (transient memory cache) olduğu standart programlama ortamlarının aksine, Verse, weak_map(player, t) yapısını öncelikli olarak persistent player data (kalıcı oyuncu verileri) için bir geçit bekçisi olarak kullanır.

Oyun Oturumları Boyunca Kalıcılık

Modül kapsamında (module scope) tanımlanmış bir weak_map(player, t) kullandığınızda, oyun motoru bu değerleri Epic'in kalıcı bulut veritabanına (persistent cloud database) bağlar. Bir oyuncu maçtan ayrılıp üç gün sonra geri döndüğünde, oyun motoru ilerlemesini geri yüklemek için oyuncu kimliğini (player ID) kalıcı map anahtarı ile eşleştirir.

Eğer oyun motoru, oyuncunun oyundan ayrıldığı an onun girdisini map yapısından otomatik olarak silseydi, map tüm kalıcı verilerini kaybederdi. Oyuncunun bağlantısı her koptuğunda veya bir ağ zaman aşımı (network timeout) yaşandığında seviyeler, özel para birimleri ve kilidi açılmış öğeler sıfırlanırdı. Bu nedenle veritabanı, tam da bağlantı kesintilerinden sağ çıkabilmeleri için bu girdileri sağlam tutacak şekilde tasarlanmıştır.

Oyuncu Nesnelerinin Kısıtlı Yaşam Süresi (Scoped Lifetime)

Bir oyuncu maçtan ayrıldığında, playspace içindeki aktif oturum nesnesi yok edilir. Verse kodunuz tarafından tutulan fiziksel player referansı artık geçersiz bir tutucu (dead handle) haline gelir.

Map içindeki anahtar artık geçersiz, inaktif bir nesneyi işaret ettiği için, bu geçersiz referansla map sorgulamak başarısız olacaktır. Oyun motoru, gerçek zamanlı olarak geçersiz anahtarları aktif şekilde tarayıp map'ten temizlemez. Bunun yerine onları etkisiz bırakır; bu nedenle eskiyen referansların (stale reference) birikmesini önlemek için manuel yönetim zorunludur.

Sonuçlar: Bellek Sızıntıları, Eski Veriler ve Sunucu Çökmeleri

Oyuncu girdilerini temizlememek, uzun maçlar boyunca oyun performansını ve sunucu stabilitesini düşüren üç belirgin soruna yol açar.

  • Stale Data Leakage (Eskiyen Veri Sızıntısı): Bir oyuncu ayrılıp yenisi katıldığında, oyun motoru dahili oyuncu yuvalarını (player slots) yeniden kullanırsa yeni oyuncu eski oyuncunun oturum verilerini devralabilir. Bu durum, yeni oyuncuların tam dolu envanter çantalarıyla veya yanlış maç istatistikleriyle doğması gibi durum (state) hatalarına yol açar.
  • Memory Accumulation (Bellek Birikmesi): Tek bir boolean veya integer önemsiz miktarda yer kaplasa da, yüksek kapasiteli bir lobide 50 oyuncuya kadar karmaşık yapıları depolamak bellek kullanımını artırabilir. 4 saatlik bir sunucu oturumunda bu birikme, sunucu tick rate değerlerini düşürebilir.
  • Look-up Failures (Sorgulama Hataları): Aktif olmayan bir oyuncunun durumunu sorgulamaya çalışmak veya geçersiz bir oyuncu referansı üzerinde fonksiyonlar çağırmak anında runtime çökmelerini tetikler.

Epic Cloud Save Limitlerine Takılmak

UEFN kalıcı veriler üzerinde katı sınırlar uygular. Ada başına en fazla 4 adet kalıcı weak_map ile sınırlandırılırsınız ve her bir oyuncunun bireysel kayıt boyutu 256 KB veriyi aşamaz.

Geçici oturum durumlarını (session states) saklamak için kalıcı bir weak_map kullanırsanız, bu değerli veritabanı alanını boşa harcamış olursunuz. Her güncelleme Epic'in veritabanına yazar; bu da bir yazma sınırlama (write-throttling) cezası alma veya 256 KB sınırını aşma riski taşır ve daha fazla veri yazmaya çalışırken bir runtime hatası tetikler.

Adım Adım Rehber: Oyuncu Oturum Durumlarını Güvenli Bir Şekilde Yönetmek

Bellek sızıntıları veya veritabanı şişmesi riski olmadan oyuncu durumlarını yönetmek için, geçici oturum verilerinizi kalıcı bulut verilerinizden ayırmalısınız. Geçici veriler, oyuncu bağlantısı kesildiğinde manuel olarak temizlemeniz gereken standart, kalıcı olmayan map'lerde saklanmalıdır.

Adım 1: Session State Yapınızı Tanımlayın

Oyuncunuzun tek bir raunt veya maç sırasında ihtiyaç duyduğu tüm değişkenleri içeren kalıcı olmayan (non-persistable) bir struct tanımlayarak başlayın. Bu sınıfı veya yapıyı <persistable> olarak işaretlemeyin.

# Define the transient data structure for active gameplay tracking
player_session_state := struct:
    IsMoneyBagFull : logic = false
    CurrentGold : int = 0
    SpawnTime : float = 0.0

Adım 2: Yönetici Cihazı (Manager Device) Oluşturun

Koordinatör görevi görecek bir creative device oluşturun. Bu cihaz, aktif oyuncuların değiştirilebilir (mutable), kalıcı olmayan bir map'ini tutacaktır. Verse'teki standart map'ler değiştirilemez (immutable) olduğundan, oyuncular katıldığında veya ayrıldığında üzerine yazabilmek için map değişkenini var olarak tanımlıyoruz.

using { /Fortnite.com/Devices }
using { /Fortnite.com/Playspaces }
using { /Verse.org/Simulation }

# Device handling player lifecycle events and session state mapping
state_manager_device := class(creative_device):

    # Non-persistent map for tracking active player sessions
    var SessionStates : [player]player_session_state = map{}

Adım 3: Playspace Etkinliklerine Abone Olun

OnBegin fonksiyonunda playspace'in bağlantı etkinliklerine (connection events) abone olun. Bu, bir oyuncu katıldığında başlatma (initialization) kodunun, ayrıldığında ise temizleme (cleanup) kodunun çalışmasını sağlar.

    OnBegin<override>()<suspends>:void=
        GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
        GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved)
        
        # Initialize any players already in the session (useful for UEFN hot-reloading)
        for (Player : GetPlayspace().GetPlayers()):
            OnPlayerAdded(Player)

Adım 4: Kayıt ve Temizleme Mantığını Uygulayın

Bir oyuncu katıldığında, map'i onun varsayılan oturum durumu (session state) ile doldurun. Ayrıldıklarında ise girdilerini map'ten temizlemelisiniz. Verse'te yerleşik bir Map.Remove() fonksiyonu bulunmadığı için, ayrılan oyuncuyu filtreleyerek map'i yeniden oluşturmanız gerekir. Bu, eskiyen referansların bellekte kalmasını önler.

    # Triggered when a player connects to the server
    OnPlayerAdded(Player: player):void=
        if (not SessionStates[Player]):
            InitialState := player_session_state{IsMoneyBagFull := false, CurrentGold := 0, SpawnTime := GetEngineTime()}
            if (set SessionStates[Player] = InitialState):
                Print("Initialized gameplay state for joining player.")

    # Triggered when a player disconnects or leaves the game
    OnPlayerRemoved(Player: player):void=
        Print("Player disconnected. Initiating map cleanup.")
        RemovePlayerSession(Player)

    # Purges the player's entry by reconstructing the map
    RemovePlayerSession(PlayerToRemove: player):void=
        var CleanedStates : [player]player_session_state = map{}
        for (ActivePlayer -> State : SessionStates):
            # Copy all players except the one who left
            if (ActivePlayer <> PlayerToRemove):
                if (set CleanedStates[ActivePlayer] = State):
                    # Entry successfully migrated to the cleaned map
        
        set SessionStates = CleanedStates
        Print("Successfully removed player session entry from memory.")

Oyuncu ayrıldığında map'i yeniden oluşturarak referans anahtarını tamamen silmiş olursunuz. Böylece Garbage Collector, oyun döngünüzde (game loop) eskiyen girdiler bırakmadan oyuncunun kaynaklarını geri kazanabilir.

Bu yaşam döngüsü geçişleri sırasında özel telemetri verilerini izlemek istiyorsanız, harici backend'lere oturum sürelerini veya para birimi istatistiklerini raporlarken 32-character analytics event name limit in Verse gibi sınırlara da dikkat etmelisiniz.

Verse Durum Yönetimi (State Management) İçin En İyi Pratikler

UEFN sunucularınızın stabil ve yüksek performanslı kalmasını sağlamak için oyuncu verilerini yönetirken şu kurallara uyun:

  1. Oturum Verisi ve Kalıcı Veri Ayrımı Yapın: Kısa ömürlü değişkenleri (mevcut maç canı, raunt skoru veya geçici konumlar gibi) asla kalıcı bir weak_map yapısında saklamayın. Geçici durumları (transient states), bir yönetici sınıfı içine sarmalanmış standart değiştirilebilir (mutable) bir map'te tutun.
  2. IsActive ile Oyuncu Aktivitesini Doğrulayın: Herhangi bir map'teki oyuncu verilerini almadan veya değiştirmeden önce, IsActive[] sorgusunu kullanarak oyuncunun playspace'te hâlâ mevcut olup olmadığını kontrol edin. Eğer IsActive[] false dönerse sorgulamayı iptal edin ve bir temizleme etkinliği (cleanup event) tetikleyin.
  3. FitsInPlayerMap ile Veri Boyutlarını İzleyin: Kalıcı bir weak_map'e yazma işlemi gerçekleştirirken, güncellemenin 256 KB sınırını aşmayacağını doğrulamak için FitsInPlayerMap() fonksiyonunu çağırın ve böylece runtime exception'ların önüne geçin.
  4. Map'lerinizi Birleştirin: Her değişken için ayrı map'ler oluşturmayın. Tüm oyuncu değişkenlerini içeren tek bir sınıf tanımlayın ve oyuncuyu bu sınıfla eşleyin. Bu, map sayınızı en aza indirir ve ada genelindeki dört kalıcı weak_map sınırına uymanızı sağlar.

Karmaşıklığı Güvenilir Bir Bulut Backend'ine Aktarmak

Verse'te oyuncu oturumu yaşam döngülerini, veritabanı limitlerini ve manuel temizleme mantığını yönetmek hızlıca karmaşık bir hal alabilir. Oturumlar arası ilerleme (cross-session progression), küresel olarak senkronize edilmiş envanterler veya bölgesel matchmaking oluşturmak istiyorsanız, bu durumları manuel yönetmek webhook kurulumu yapmayı, harici veritabanlarını ölçeklemeyi ve sunucular arası senkronizasyonla uğraşmayı gerektirir.

horizOn ile bu backend zorlukları otomatik olarak halledilir. horizOn SDK'sını oyun sunucunuza entegre ederek, oyuncu oturumu yönetimini özel bir bulut veritabanına aktarabilirsiniz. Bir oyuncunun bağlantısı kesildiğinde, horizOn otomatik oturum temizliğini tetikler, global veritabanlarını günceller ve Verse'in 256 KB bellek sınırlarına takılmadan veya çalışma zamanı çökmeleri (runtime crashes) riski yaşamadan envanter kayıtlarını sunucu örnekleri arasında senkronize eder.

UEFN backend yapınızı ölçeklendirmeye hazır mısınız? horizOn'u ücretsiz deneyin veya API docs sayfasına göz atın.


Kaynak: When using weak maps, does a player's entry in the map automatically get removed on them leaving the game?