UEFN Sunucu Performans Açığı Açıklandı: Unreal Engine Netcode Yapınızı Zırhlandırın
Her Multiplayer geliştiricisi o kabus senaryosunu bilir: Tek bir kötü niyetli oyuncu sunucunuza bağlanır, görünüşte masum bir dizi eylem gerçekleştirir ve aniden tick rate değeriniz 60Hz'den tek haneli rakamlara düşer. Tüm sunucu durma noktasına gelir ve düzinelerce masum oyuncu bu durumdan etkilenir.
Geçtiğimiz günlerde, geliştirici Vysena Woyka tarafından Unreal Engine forumlarında kritik bir UEFN sunucu performans açığı (exploit) bildirildi. Rapor, Unreal Editor for Fortnite (UEFN) haritalarında sunucu genelinde ciddi bozulmalara neden olan %100 yeniden üretilebilir bir tekniği özetliyor. Bu açık, daha fazla oyuncu katıldıkça şiddetini artırıyor, kesinlikle sıfır üçüncü taraf araç gerektiriyor ve uzun süreli uygulama ile toplam sunucu istikrarsızlığına neden olma potansiyeline sahip.
Yaygın suistimali önlemek için tam yeniden üretim adımları gizli tutulduğundan, birçok geliştirici şu soruyu soruyor: Böyle bir exploit arka planda gerçekte nasıl çalışıyor? ve daha da önemlisi, Kendi özel Unreal Engine dedicated servers yapılarımı benzer saldırılardan nasıl korurum?
Bu teknik derinlemesine incelemede, Unreal Engine'deki sunucu tarafı performans bozulmasının mimarisini analiz edeceğiz. Kötü niyetli oyuncuların dedicated servers yapılarını boğmak için kullandıkları yaygın vektörleri, C++ kullanarak katı sunucu tarafı doğrulamalarının nasıl uygulanacağını ve altyapınızı maksimum dayanıklılık için nasıl mimarileştireceğinizi keşfedeceğiz.
Bir Unreal Engine Sunucu Açığının Anatomisi
Bir oyuncunun harici hack araçları olmadan bir sunucuyu nasıl çökertebileceğini anlamak için Unreal Engine'in ana oyun döngüsünü (Main Game Loop) nasıl yönettiğini anlamanız gerekir. Unreal Engine dedicated servers, Game Logic söz konusu olduğunda ağırlıklı olarak tek iş parçacıklıdır (single-threaded). Physics Simulation (Chaos fizik motoru aracılığıyla) ve asenkron yükleme gibi görevler iş parçacıklarına (worker threads) aktarılabilse de, Actor'larınızın temel Tick işlevi, Replication Serialization ve RPC (Remote Procedure Call) yürütme işlemlerinin tümü Game Thread üzerinde gerçekleşir.
Bir sunucu saniyede 30 tick (30Hz) hızında çalışıyorsa, tüm player inputs işlemlerini işlemek, Game State güncellemek, fiziği hesaplamak ve bir sonraki kare için ağ verilerini serileştirmek için tam olarak 33,3 milisaniyesi vardır. Bir oyuncu sunucuyu işlemesi 50 milisaniye süren bir işlemi yürütmeye zorlayabilirse, sunucunun tick rate değeri anında 20Hz'e düşer.
Sunucu tick rate değeriniz bu kadar sert düştüğünde, sadece görsel gecikme (lag) yaşamazsınız; feci bir durum sapması (state divergence) yaşarsınız. Bunun sonuçlarını The Unreal Engine Multiplayer Sync Bug Ruining Your World States And How To Fix It hakkındaki teknik kılavuzumuzda kapsamlı bir şekilde ele aldık.
Bellek enjektörleri veya paket düzenleyiciler kullanmadan, oyun içi performans açıkları genellikle üç vektörden birine dayanır: RPC Flooding, Physics/Collision Overload veya Replication Saturation.
Vektör 1: RPC Flooding ve Doğrulama Hataları
Bir Unreal Engine sunucusunu çökertmenin veya yavaşlatmanın en yaygın yolu Server RPCs spam yapmaktır. Bir istemci (client), bir Server RPC'yi fare tekerleğine veya kilidi açılmış bir kare hızı girişine bağlarsa, sunucuya saniyede yüzlerce istek gönderebilir.
Server RPC'niz bir Actor spawn etmek, bir line trace (Raycast) gerçekleştirmek veya büyük diziler (arrays) üzerinde dönmek gibi karmaşık mantıklar içeriyorsa, sunucu bu maliyetli mantığı kare başına yüzlerce kez yürütmeye zorlanır.
Unreal Engine, RPC'ler için WithValidation makrosunu sağlar, ancak birçok geliştirici bunu sadece bir pointer'ın geçerli olup olmadığını kontrol etmek için kullanır ve Rate Limiting konusunu tamamen görmezden gelir.
Çözüm: C++ RPC Rate Limiter Uygulamak
Sunucunuzu korumak için, tüm istemciden sunucuya iletişimlerde katı Rate Limiting uygulamalısınız. İşte C++'da özel bir Actor Component kullanarak Server RPCs sınırlamak için savaşta test edilmiş bir yaklaşım.
Önce, header dosyamızda hız sınırlama mantığımızı tanımlıyoruz:
// RateLimiterComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "RateLimiterComponent.generated."
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class MULTIPLAYER_API URateLimiterComponent : public UActorComponent
{
GENERATED_BODY()
public:
URateLimiterComponent();
// Checks if the action is allowed. Returns false if the client is spamming.
UFUNCTION(BlueprintCallable, Category = "Security")
bool CanExecuteAction(FName ActionName, float CooldownTime);
private:
// Maps action names to the last time they were executed
TMap<FName, float> LastExecutionTimes;
// Threshold for maximum allowed actions per second before flagging the player
const int32 MaxActionsPerSecond = 20;
int32 CurrentActionCount;
float LastResetTime;
};
Ardından, CPP dosyasında doğrulama mantığını uyguluyoruz. İstemcinin cooldown süresini atlatmak için yerel saatini taklit edememesini sağlamak için sunucu saatini (GetWorld()->GetTimeSeconds()) nasıl kullandığımıza dikkat edin.
// RateLimiterComponent.cpp
#include "RateLimiterComponent.h"
URateLimiterComponent::URateLimiterComponent()
{
PrimaryComponentTick.bCanEverTick = false;
CurrentActionCount = 0;
LastResetTime = 0.0f;
}
bool URateLimiterComponent::CanExecuteAction(FName ActionName, float CooldownTime)
{
// Only run this logic on the server
if (!GetOwner()->HasAuthority())
{
return false;
}
float CurrentTime = GetWorld()->GetTimeSeconds();
// Reset the global action counter every second
if (CurrentTime - LastResetTime >= 1.0f)
{
CurrentActionCount = 0;
LastResetTime = CurrentTime;
}
// Global spam check
CurrentActionCount++;
if (CurrentActionCount > MaxActionsPerSecond)
{
UE_LOG(LogTemp, Warning, TEXT("Player %s is exceeding global RPC limits!"), *GetOwner()->GetName());
return false;
}
// Specific action cooldown check
if (LastExecutionTimes.Contains(ActionName))
{
float LastTime = LastExecutionTimes[ActionName];
if (CurrentTime - LastTime < CooldownTime)
{
// Client is spamming this specific action
return false;
}
}
// Update the execution time and allow the action
LastExecutionTimes.Add(ActionName, CurrentTime);
return true;
}
Şimdi, Server_PerformAction_Validate işlevinizi uyguladığınızda, istemci RPC'yi spam yapıyorsa isteği dinamik olarak reddedebilirsiniz:
bool AMyPlayerController::Server_PerformExpensiveAction_Validate()
{
// If the rate limiter returns false, the RPC is rejected and the client is disconnected
if (URateLimiterComponent* RateLimiter = GetComponentByClass<URateLimiterComponent>())
{
return RateLimiter->CanExecuteAction(FName("ExpensiveAction"), 0.5f);
}
return true;
}
Vektör 2: Fizik ve Çarpışma Aşırı Yüklemesi (Physics and Collision Overload)
Bir başka yaygın exploit vektörü (ve UEFN gibi sandbox ortamlarında şüphelenilen bir durum) fizik aşırı yüklemesidir. Oyuncular nesne spawn edebiliyor, öğe bırakabiliyor veya fizik gövdelerini (Physics Bodies) manipüle edebiliyorsa, kısıtlı bir alanda kasıtlı olarak yüzlerce nesneyi üst üste yığabilirler.
Fizik gövdeleri çakıştığında, Chaos fizik motoru çarpışmaları çözmeye çalışır. 500 nesne aynı koordinat alanına zorlanırsa, çarpışma çözme matematiği katlanarak büyür ve sunucuda toplam CPU kilitlenmesine neden olur.
Dahası, bu nesnelerin bGenerateOverlapEvents değeri true olarak ayarlanmışsa, sunucu kare başına yüz binlerce kez OnComponentBeginOverlap tetikleyecektir.
Çözüm: Agresif Çarpışma Ayıklama (Collision Culling)
Fizik tabanlı sunucu bozulmasını önlemek için görsel fiziği sunucu tarafı çarpışma doğrulamasından ayırmalısınız.
- Bırakılan Öğelerde Overlap'leri Devre Dışı Bırakın: Bir oyuncu bir öğe bırakırsa, öğe durduktan sonra sunucuda
bGenerateOverlapEventsözelliğini devre dışı bırakın. - Spawn Limitlerini Belirleyin: Izgara sektörü başına maksimum fizik nesnesi yoğunluğunu kodun içine sabitleyin.
- Overlap Mantığını Sınırlayın: Overlap kullanmanız gerekiyorsa, karmaşık mantığı doğrudan overlap olayı içinde yürütmeyin. Bunun yerine, bir dirty flag ayarlayın ve overlap işlemini
Tickişlevi sırasında kontrollü bir toplu işlem (batch) olarak gerçekleştirin.
Vektör 3: Replication Saturation ve Bant Genişliği Boğulması
Unreal Engine'in replikasyon sistemi güçlüdür ancak aynı zamanda yoğun şekilde CPU bağımlıdır. Sunucu, replike edilen her Actor üzerinde dönmeli, belirli bir istemciyle ilgili olup olmadığını kontrol etmeli, özelliklerini son onaylanan durumla karşılaştırmalı ve değişiklikleri serileştirmelidir.
Kötü niyetli oyuncular, replike edilen değişkenleri (karakter özelleştirme verileri veya envanter durumu gibi) hızla ileri geri değiştirerek bunu suistimal edebilirler. Bu, sunucuyu sürekli olarak büyük veri bloklarını serileştirmeye zorlayarak hem sunucunun CPU'sunu hem de bant genişliği sınırlarını doyurur.
Çözüm: NetUpdateFrequency Optimizasyonu
Kritik olmayan actor'lar için NetUpdateFrequency değerini asla varsayılan değerinde (100.0) bırakmayın. Replikasyon frekansını oyuncu yakınlığına ve eylem durumuna göre dinamik olarak ölçeklendirmelisiniz.
Ek olarak, dedicated server üzerinde katı bant genişliği sınırları uygulamak için DefaultEngine.ini dosyasını kullanmalısınız. Bu, tek bir kötü niyetli istemcinin sunucuyu devasa paket akışlarını işlemeye zorlamasını önler:
[/Script/OnlineSubsystemUtils.IpNetDriver]
MaxClientRate=15000
MaxInternetClientRate=10000
NetServerMaxTickRate=30
LanServerMaxTickRate=30
ConnectionTimeout=15.0
InitialConnectTimeout=30.0
MaxClientRate değerini sınırlayarak, sunucu ağ kanalını doldurmaya çalışan bir istemciden gelen fazla paketleri basitçe düşürecek ve CPU döngülerini meşru oyuncular için koruyacaktır.
Altyapı Dayanıklılığı: Kaçınılmaz Olanı Yönetmek
Mükemmel C++ koduyla bile, sıfırıncı gün (zero-day) açıkları olacaktır. UEFN sunucu performans hatası gibi bir exploit özel oyununuzu vurduğunda, sunucu düğümleriniz kaçınılmaz olarak %100 CPU kullanımına çıkacak ve çökecektir.
Tüm sunucu filosu mimariniz tek bir hata noktasına karşı savunmasızsa, kalıcı oyuncu kaybı riskiyle karşı karşıya kalırsınız. The Stop Killing Games Campaign Vs Live Ops Architecting Server Fallbacks mimari analizimizde tartıştığımız gibi, uygun fallback yönlendirmesine sahip dayanıklı bir altyapı oluşturmak, şiddetle savunduğumuz bir şeydir.
Bir sunucu bir exploit nedeniyle çöktüğünde, backend yapınız ölü düğümü anında tespit etmeli, yeni bir örnek (instance) başlatmalı ve etkilenen oyuncuları kalıcı verilerini kaybetmeden matchmaking kuyruğuna zarif bir şekilde geri taşımalıdır.
Bunu kendiniz oluşturmak; özel load balancers, database sharding, konteyner orkestrasyonu (Kubernetes gibi) ve SSL sertifika yönetimi kurulumunu gerektirir — bu da kolayca 4-6 aylık özel mühendislik çalışması demektir. horizOn ile bu backend hizmetleri önceden yapılandırılmış olarak gelir. Altyapımız sunucu sağlığını otomatik olarak izler, CPU yüküne göre örnekleri otomatik olarak ölçeklendirir ve oyuncu oturum yönlendirmesini yönetir; böylece altyapınızla savaşmak yerine oyun kodunu düzeltmeye odaklanmanıza olanak tanır.
Sunucu İstikrarı İçin 5 En İyi Uygulama
Unreal Engine multiplayer oyununuzu performans açıklarına karşı korumak için bu beş mimari kuralı hemen uygulayın:
- Katı RPC Kotaları Uygulayın: İstemcinin giriş hızına asla güvenmeyin. Her bir Server RPC üzerinde sert cooldown'lar uygulamak için yukarıda ayrıntıları verilen C++ rate limiter bileşenini kullanın.
- Hareket Vektörlerini Temizleyin: Speed hack'ler ve ışınlanma açıkları, sunucuya devasa vektörler göndererek çalışır. Sunucu tarafında
AddMovementInputveSetActorLocationisteklerini her zaman karakterin maksimum teorik hareket hızına göre sınırlayın (clamp). - Replication Graph Kullanın: Oyununuz 40'tan fazla oyuncuyu destekliyorsa, varsayılan replikasyon sistemi bir darboğaz haline gelecektir. Actor'ları mekansal olarak gruplandırmak ve alaka düzeyi kontrollerinin (relevance checks) CPU yükünü büyük ölçüde azaltmak için Unreal Engine Replication Graph'ı uygulayın.
- Sunucu Tarafı Görselleri Devre Dışı Bırakın: Dedicated servers asla UI, partikül sistemleri veya skeletal mesh animasyonları yüklememelidir. Bellek ve CPU döngülerini boşaltmak için proje ayarlarınızın bu varlıkları dedicated server build'inden kesinlikle çıkardığından emin olun.
- Tick Rate'i Dinamik Olarak İzleyin: Ortalama delta süresini izleyen sunucu tarafı bir alt sistem uygulayın. Sunucu, tick rate değerinin 5 saniyeden fazla bir süre 15Hz'in altına düştüğünü tespit ederse, iyileşmek için temel olmayan arka plan görevlerini (AI spawn etme veya ortam olayı oluşturma gibi) otomatik olarak duraklatmalıdır.
Sonuç
Son UEFN sunucu performans açığı, multiplayer oyun geliştirmenin doğası gereği bir siber güvenlik çalışması olduğunu sert bir şekilde hatırlatıyor. Oyuncuların oyununuzla amaçlandığı gibi etkileşime gireceğine basitçe güvenemezsiniz. Her RPC, her fizik etkileşimi ve replike edilen her değişken potansiyel bir saldırı vektörüdür.
Zihniyetinizi "Sunucu Yetkili, İstemci Güvensiz" (Server-Authoritative, Client-Distrusted) modeline kaydırarak, C++ replikasyon mantığınızı derinlemesine optimize ederek ve katı hız sınırları uygulayarak, oyununuzu bu tür feci performans çökmelerine karşı zırhlandırabilirsiniz.
Kurşun geçirmez oyun kodunu otomatik ölçeklenen, kendi kendini iyileştiren sunucu altyapısıyla birleştirdiğinizde, exploit'lerin oyun öldüren felaketler yerine küçük can sıkıcı durumlara dönüştüğü bir ortam yaratırsınız. Multiplayer backend yapınızı dev-ops sancısı çekmeden ölçeklendirmeye hazır mısınız? horizOn'u ücretsiz deneyin ve sunucu orkestrasyonunuzu biz halledelim.