Memperbaiki Bug UEFN Scene Graph Entities yang Menghilang: Panduan Verse untuk Persistent World States
Ringkasnya
Artikel ini membahas bug replikasi pada UEFN Scene Graph yang menyebabkan mesh dari entitas dinamis menghilang ketika pemain berteleportasi ke luar jarak render. Masalah ini terjadi karena mismatch dalam penanganan memori rendering client dan server, sementara collision fisika tetap aktif. Solusi sementara dapat diimplementasikan menggunakan script Verse untuk memicu pembaruan status entitas secara paksa saat pemain kembali mendekat. Untuk game multiplayer skala besar dengan persistensi data yang kompleks, mengalihkan penyimpanan data tata letak spasial ke backend eksternal seperti horizOn merupakan langkah terbaik guna mengatasi keterbatasan memori server.
Jika pemain Anda melakukan teleportasi di sepanjang peta UEFN, mereka kemungkinan akan kembali dan mendapati furnitur yang telah mereka susun dengan rapi menjadi tidak terlihat, tidak interaktif, namun secara fisik menghalangi mereka layaknya dinding hantu. Replication bug yang membuat frustrasi ini telah mengganggu para developer yang membangun game sandbox, tycoon, atau game bertema building-centric di UEFN sejak update v41.00. Anda mungkin telah membangun sistem yang memungkinkan pemain menempatkan furnitur kustom, kabinet arkade, atau dekorasi mesh menggunakan entitas Scene Graph, dan semuanya berjalan lancar dalam kondisi local play. Namun, begitu pemain berteleportasi ke minigame, lobby, atau bagian pulau lain yang terpisah, lalu kembali lagi, mesh tersebut akan menghilang.
Yang membuat masalah ini sangat membingungkan adalah perilaku dari data collision-nya. Pemain dapat berjalan ke tempat objek tersebut berada, menabrak dinding yang tidak terlihat, dan berdiri di atas tempat yang seharusnya menjadi kursi atau kabinet padat. Namun, mereka tidak dapat melihatnya, dan komponen interaksi berbasis Verse mereka menolak untuk berjalan (fire). Model visualnya telah lenyap, tetapi representasi fisika tetap ter-bake ke dalam world partition grid.
Untuk memahami mengapa hal ini terjadi, kita harus melihat cara kerjanya di balik layar tentang bagaimana UEFN mengelola memori dan replikasi. Actor Unreal Engine standar menggunakan network replication graph lama untuk menentukan apa yang harus di-render pada layar pemain berdasarkan jarak kamera dan relevansinya. Dengan diperkenalkannya sistem Scene Graph baru di UEFN, Epic Games mencoba menyederhanakan desain game berbasis komponen. Namun, hal ini menyebabkan ketidakcocokan (mismatch) antara cara server melakukan cache terhadap struktur entitas dinamis dan bagaimana client melakukan streaming aset visual masuk dan keluar dari memori GPU.
Di Balik Layar: Mekanisme UEFN Scene Graph Culling
Ketika pemain berada di dalam jarak render standar dari suatu entitas—biasanya sekitar 15.000 hingga 20.000 Unreal Units (150-200 meter)—replication graph pada server secara aktif menyiarkan update koordinat, mesh, dan status komponen ke client. Client kemudian memproses paket jaringan ini dan melakukan rendering komponen visual mesh yang sesuai.
Saat pemain melakukan teleportasi menjauh, beberapa hal terjadi secara berurutan dalam waktu singkat:
- Relevancy Cutoff: Viewport milik client bergeser ribuan unit secara instan. Replication graph menandai entitas Scene Graph asli sebagai "out of relevancy".
- GPU Garbage Collection: Untuk mempertahankan framerate yang tinggi pada perangkat berspesifikasi rendah seperti perangkat mobile dan konsol, client segera menghapus (unload) representasi visual dari entitas yang berada di luar relevansi ini, serta membersihkan komponen static mesh dari memori GPU.
- Collision Caching: Berbeda dengan mesh visual, geometri collision dikelola oleh Chaos Physics engine. Struktur fisika dikelompokkan ke dalam blok spasial kasar (HLOD collision grid) atau di-cache secara lokal pada client untuk mencegah pemain jatuh menembus lantai saat terjadi lonjakan jaringan. Inilah sebabnya mengapa collision tetap utuh bahkan ketika mesh visualnya telah di-cull.
Saat pemain berteleportasi kembali ke koordinat awal, client mengharapkan urutan handshake untuk membuat ulang komponen visual. Namun, karena adanya replication bug pada network layer Scene Graph, server mengasumsikan client masih menyimpan cached visual state dari entitas tersebut. Karena server mengira client telah memiliki entitas tersebut, ia tidak mengirimkan replication spawn RPC. Client pun hanya ditinggali blok collision fisika tanpa adanya mesh visual dan tanpa koneksi aktif ke komponen interaksi.
Jika ada pemain lain yang tetap berada di area tersebut ketika pemain pertama melakukan teleportasi menjauh, server akan menjaga agar channel replikasi tetap aktif untuk entitas-entitas tersebut. Saat pemain pertama kembali, mereka masih dapat melihat objek tersebut karena replication stream yang aktif untuk pemain kedua memaksa server untuk terus membagikan update ke semua client yang terhubung. Namun, begitu kedua pemain meninggalkan area tersebut dan kembali, kondisinya akan rusak (break) untuk semua orang.
Deep-Dive Teknis: Mengapa Replication Graph Gagal Saat Pemain Masuk Kembali
Kode jaringan Unreal Engine mengandalkan sistem network relevancy yang ketat. Dalam lingkungan multiplayer, server tidak dapat mereplikasi setiap actor ke setiap pemain; melakukan hal tersebut akan membanjiri bandwidth client dan membuat server thread crash. Sebaliknya, server menggunakan UReplicationGraph untuk membagi actor ke dalam sel grid spasial.
Untuk actor statis yang sudah ditempatkan sebelumnya di peta, UEFN menggunakan World Partition untuk melakukan streaming mesh pada client. Namun, entitas dinamis yang di-spawn melalui Verse atau ditempatkan oleh pemain selama runtime berada dalam kondisi transient. Entitas transient ini tidak mendapatkan keuntungan dari grid streaming statis yang sudah dikompilasi sebelumnya. Sebaliknya, mereka bergantung pada pemeriksaan net relevancy dinamis.
Ketika pemain melakukan teleportasi, koneksi jaringan mereka mengalami perubahan status yang besar. Server harus menutup channel replikasi untuk lokasi lama dan membuka channel untuk lokasi baru. Perpindahan yang cepat ini dapat memicu konflik prioritas paket data. Server akan memprioritaskan elemen gameplay yang bergerak cepat (seperti lokasi pemain, kecepatan gerak, dan penembakan senjata) daripada komponen visual statis.
Kongesti jaringan ini sering kali menyebabkan packet state terbuang (dropped), mirip dengan masalah sinkronisasi yang kami jelaskan secara detail dalam panduan kami tentang cara memperbaiki desync lokasi pemain di multiplayer UEFN dan Unreal Engine. Dalam kasus bug scene graph ini, replication graph milik server gagal mengevaluasi kembali relevansi dari entitas yang telah di-cull bagi pemain yang kembali. Karena server tidak mendeteksi bahwa client kekurangan entitas tersebut, server melewati pengiriman update properti yang diperlukan. Entitas di sisi client pun berada dalam kondisi "zombie": aktif di physics thread, namun mati di rendering thread dan interaction thread.
Mengatasi Bug: Solusi Sementara Refresh Replikasi Berbasis Verse
Untuk memperbaiki bug uefn scene graph entities disappearing, kita harus memaksa server menandai entitas-entitas ini sebagai "dirty" saat pemain berteleportasi kembali ke dalam jangkauan. Dengan memaksakan pembaruan properti (property update), kita memicu replication graph untuk mengirimkan state packet baru ke client yang kembali, yang kemudian membangun kembali mesh dan komponen interaktifnya.
Cara paling andal untuk mencapainya adalah dengan membuat manager entitas dinamis di Verse. Manager ini melacak posisi pemain, mendeteksi peristiwa teleportasi (perubahan koordinat yang mendadak), dan mengeksekusi toggle visibilitas atau transform lokal pada entitas terdekat untuk memaksa network refresh.
Berikut adalah script Verse lengkap dan dengan sintaksis yang benar untuk mengimplementasikan pola rekonstruksi status (state reconstruction pattern) ini.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
# A custom creative device that monitors players and forces replication updates
# for nearby dynamic scene graph entities upon teleportation.
replication_refresher_device := class(creative_device):
# Tracks player positions to detect sudden coordinate jumps (teleports)
var PlayerLastPositions : [agent]vector3 = map{}
# The maximum distance (in centimeters) where an entity is considered relevant (200 meters)
ReplicationBubbleRadius : float = 20000.0
# Minimum movement distance (in centimeters) to classify as a teleport (e.g., 50 meters)
TeleportDistanceThreshold : float = 5000.0
# Polling frequency in seconds. Checking twice a second is highly efficient.
CheckInterval : float = 0.5
# A list of active dynamic devices or entities we need to monitor and refresh
var TrackedEntities : []creative_prop = array{}
# Runs when the minigame or map session starts
OnBegin<override>()<suspends> : void =
# Retrieve all dynamic props matching our gameplay tag (setup in UEFN Editor)
# For this example, we assume we register them programmatically or via editor reference
spawn { MonitorPlayers() }
# Register a new dynamic entity to be managed by the refresh system
RegisterEntity(Prop : creative_prop) : void =
set TrackedEntities = TrackedEntities + array{Prop}
# Periodically checks player locations to detect network jumps
MonitorPlayers()<suspends> : void =
loop:
Sleep(CheckInterval)
Players := GetPlayspace().GetPlayers()
for (Player : Players):
if (FortCharacter := Player.GetFortCharacter[]):
CurrentPos := FortCharacter.GetTransform().Translation
# Check if we have a recorded previous position for this player
if (LastPos := PlayerLastPositions[Player]):
DistanceTraveled := Distance(CurrentPos, LastPos)
# If the player moved faster than possible by normal running, it is a teleport
if (DistanceTraveled > TeleportDistanceThreshold):
HandlePlayerTeleport(Player, CurrentPos)
# Update the player's last known position in the map
if (set PlayerLastPositions[Player] = CurrentPos):
# Position updated successfully
pass
# Triggers a server-side state dirtying on entities near the player's arrival point
HandlePlayerTeleport(Player : agent, TargetPos : vector3) : void =
Print("Teleport detected for player. Triggering scene graph replication sync...")
# Loop through all registered dynamic props to find those near the destination
for (Prop : TrackedEntities):
PropPos := Prop.GetTransform().Translation
DistanceToTarget := Distance(TargetPos, PropPos)
# If the prop is within the newly entered replication bubble, refresh it
if (DistanceToTarget < ReplicationBubbleRadius):
spawn { ForceEntityReplicationRefresh(Prop) }
# Forces the server to redistribute the entity's network state to all clients in range
ForceEntityReplicationRefresh(Prop : creative_prop)<suspends> : void =
# To force replication, we briefly toggle the prop's visibility or interaction state.
# This flags the actor as \"dirty\" in the server's replication queue.
# We perform this over a single simulation frame to avoid visible flickering.
Prop.Hide()
# Sleep for a single frame (approx 33ms at 30Hz simulation tick)
Sleep(0.0)
Prop.Show()
Cara Kerja Kode
replication_refresher_device beroperasi dengan menjalankan loop asinkron yang memantau lokasi pemain. Dengan membandingkan vektor koordinat saat ini milik pemain dengan vektor koordinat mereka dari 0,5 detik yang lalu, script ini dapat membedakan antara pergerakan normal (locomotion) dan teleportasi berkecepatan tinggi.
Ketika terjadi event teleportasi, fungsi HandlePlayerTeleport mengevaluasi semua dynamic prop yang terdaftar. Untuk setiap prop dalam radius 200 meter dari posisi baru pemain, fungsi ini memanggil ForceEntityReplicationRefresh. Melakukan toggle Hide() dan Show() pada creative_prop memaksa server memperbarui net-dirty flag pada actor C++ di bawahnya. Hal ini memaksa replication graph untuk mendorong seluruh payload actor ke perangkat client, sehingga berhasil membangun kembali static mesh yang ter-cull serta channel interaksinya.
Merancang Persistent World States: Batasan Memori Verse
Meskipun solusi Verse ini mengatasi masalah rendering untuk peta ukuran kecil hingga menengah, solusi ini menyingkap batasan yang lebih mendalam dari arsitektur runtime UEFN. Jika game Anda mengandalkan ratusan item yang ditempatkan oleh pemain, melacak semuanya dalam array Verse dapat dengan cepat menghabiskan memori server.
Setiap class instance dinamis, koordinat vektor, dan struktur pelacakan variabel di Verse mengonsumsi memori runtime. Server Fortnite berjalan di bawah batasan resource yang ketat. Jika Anda melampaui batas jumlah instruksi atau batas alokasi memori maksimum yang diizinkan, server Anda akan mengalami penurunan performa yang parah atau crash sepenuhnya, yang berujung pada network driver timeouts yang akan melempar pemain kembali ke lobby.
Selain itu, variabel Verse tidak akan bertahan (persist) ketika instansi server berhibernasi atau mati. Jika server menjadi tidak aktif karena semua pemain telah keluar (proses yang kami analisis dalam ulasan mendalam kami tentang exploit performa server UEFN), seluruh susunan kabinet arkade dan furnitur yang ditempatkan pemain akan hilang secara permanen.
Untuk membangun game yang benar-benar persistent, Anda perlu menyimpan data tata letak spasial ini pada backend eksternal. Menyiapkannya sendiri adalah sebuah pekerjaan rekayasa perangkat lunak (engineering) yang sangat besar:
- Infrastructure Provisioning: Anda harus meng-host database (seperti PostgreSQL atau MongoDB) yang mampu menangani skala hingga ribuan operasi read dan write secara bersamaan.
- API Gateway: Anda perlu membangun HTTP server yang aman untuk menerjemahkan request Verse menjadi database query, menangani autentikasi pemain, dan mengelola rate limiting.
- Network Resilience: HTTP client milik Verse sangat terbatas dan tidak memiliki kebijakan retry otomatis atau connection pooling. Anda harus menulis boilerplate code yang kompleks untuk menangani packet loss dan server timeout.
Bagi tim indie kecil, menghabiskan waktu 4 hingga 6 minggu hanya untuk menulis kode integrasi database alih-alih memoles gameplay merupakan pemborosan resource yang sangat besar.
Seamless Persistence: Bagaimana horizOn Mengatasi Sinkronisasi Status
Di sinilah peran horizOn. Sebagai Backend-as-a-Service yang didedikasikan khusus untuk game developer, horizOn menyediakan penyimpanan status spasial berkependaman rendah (low-latency spatial state storage) yang terkonfigurasi sebelumnya dan terintegrasi langsung dengan UEFN dan Verse.
Alih-alih membiarkan ratusan entitas scene graph aktif termuat di dalam memori server setiap saat, Anda dapat menyimpan koordinat spasial, rotasi, dan atribut kustom mereka ke database cloud milik horizOn. Ketika seorang pemain berteleportasi menjauh, Anda dapat melakukan despawn entitas lokal dengan aman untuk membebaskan memori server Fortnite. Ketika pemain berteleportasi kembali, Anda cukup melakukan query ke database dan membangun kembali entitas yang hanya berada di sekitar mereka saja.
Dengan memanfaatkan horizOn, Anda menyelesaikan bug culling sekaligus masalah memori server secara bersamaan. Server hanya mereplikasi apa yang benar-benar dapat dilihat oleh pemain secara aktif, mengurangi bandwidth jaringan dari sekitar 120KB/s menjadi kurang dari 25KB/s per client. Jika instansi server berhibernasi atau dimulai ulang, progres pemain akan langsung dipulihkan dari cloud.
Integrasi dengan horizOn hanya membutuhkan beberapa baris kode Verse menggunakan HTTP request standar:
# Example conceptual Verse call to save player layout data to [horizOn](https://horizon.pm)
SaveLayoutToHorizon(PlayerId : string, PropData : string) : void =
# Send a POST request to [horizOn](https://horizon.pm)'s structured data API
# [horizOn](https://horizon.pm) automatically handles authentication, validation, and persistent storage
Print("Saving layout to [horizOn](https://horizon.pm) database for player: {PlayerId}")
Dengan horizOn yang mengelola infrastruktur, Anda mendapatkan data persistence yang andal dan performa server yang optimal tanpa harus mengelola satu baris pun kode server backend.
Best Practices untuk Mengelola UEFN Scene Graph Replication
Jika saat ini Anda sedang membangun game sandbox atau tycoon di UEFN, terapkan empat tips praktis berikut untuk memastikan world state Anda tetap sinkron dan server Anda berjalan dengan efisien:
- Implementasikan Dynamic Spawning dan Despawning: Jangan biarkan ratusan objek interaktif dimuat di dalam dunia game sekaligus. Tulis script Verse yang melakukan spawn mesh hanya ketika pemain berada dekat, dan melakukan despawn saat mereka pergi, guna menghemat siklus CPU server yang berharga.
- Gunakan Gameplay Tags untuk Batch Querying: Hindari melacak setiap actor dinamis dalam satu array Verse global. Sebaliknya, tetapkan gameplay tags ke prop scene graph Anda. Gunakan sistem tag query UEFN untuk mencari dan me-refresh prop secara dinamis berdasarkan batas spasial.
- Pisahkan Collision dari Actor Dinamis: Jika suatu objek tidak perlu bergerak, gunakan invisible collision box statis yang ditempatkan sebelumnya pada tata letak peta Anda. Hanya pasang visual mesh dan komponen interaksi Verse ke entitas scene graph dinamis. Langkah ini menjamin pemain tidak akan pernah menabrak invisible collision wall ketika replikasi gagal.
- Alihkan State Management ke Cloud: Untuk game apa pun dengan mekanika building yang persistent, terapkan integrasi backend seperti horizOn sejak awal pengembangan. Menyimpan data koordinat di cloud dapat mencegah overload alokasi memori lokal dan memastikan progres pemain tetap terjaga di berbagai instansi server.
Siap untuk membangun game multiplayer berskala besar dan persistent di UEFN tanpa pusing memikirkan backend? Daftar ke horizOn secara gratis atau pelajari dokumentasi API untuk memulai.
Sumber: Scene graph entities are buggy when player leaves render distance from v41.00