Powrót do Bloga

Dlaczego tryby kreatywne cierpią na dwukrotnie wyższy ping: architektoniczne rozwiązania w optymalizacji pingu serwerów gier multiplayer

Opublikowano 5 lipca 2026
Dlaczego tryby kreatywne cierpią na dwukrotnie wyższy ping: architektoniczne rozwiązania w optymalizacji pingu serwerów gier multiplayer

W skrócie

Artykuł analizuje przyczyny dwukrotnie wyższego pingu w kreatywnych trybach multiplayer (tzw. creative mode latency penalty), wynikającego z dynamicznej orkiestracji serwerów oraz spadków tick rate wywołanych przez UGC. Przedstawiono w nim rozwiązania tego problemu, w tym oddzielenie fizycznego pingu sieciowego od opóźnień pętli gry przy użyciu przykładowej klasy w C++ dla Unreal Engine. Omówiono również wdrożenie zaawansowanego routingu brzegowego Anycast oraz dynamicznych orkiestratorów w celu zapewnienia stabilnej rozgrywki o niskich opóźnieniach.

Twoja główna kolejka Matchmaking dla trybu battle royale działa z doskonałym pingiem poniżej 30 ms, ale w momencie, gdy gracze ładują się do wygenerowanej przez użytkownika sesji kreatywnej, ich opóźnienie podwaja się do 60 ms lub więcej. Ta „kara za opóźnienie w trybie kreatywnym” (creative mode latency penalty) to powszechnie znany problem w studiach wydających gry typu sandbox, a mimo to wciąż jest słabo rozumiany. Jest to bezpośrednia konsekwencja dynamicznej orkiestracji serwerów, dynamicznego ładowania assetów i suboptymalnego routingu regionalnego. Aby go rozwiązać, deweloperzy muszą przejść od statycznych architektur Matchmaking do nowoczesnych strategii optymalizacji pingu serwerów gier multiplayer.

Dlaczego tryby kreatywne mierzą się z karą za opóźnienie

W standardowych meczach multiplayer serwery gier są wstępnie rozgrzane (pre-warmed) i zgrupowane w głównych centrach danych wyższego poziomu w danym regionie. Serwery te uruchamiają zoptymalizowane, przeznaczone tylko do odczytu mapy gry, które wymagają minimalnej dynamicznej inicjalizacji aktorów lub replikacji assetów. Algorytmy Matchmaking czekają na pogrupowanie graczy z podobnych regionów, zapewniając, że wybrany serwer znajduje się fizycznie blisko każdego w lobby.

Tryby kreatywne i sandbox całkowicie przełamują ten paradygmat. Zamiast korzystać z pre-warmed serwerów, tryby te uruchamiają dedykowane instancje kontenerów na żądanie, gdy lider grupy rozpoczyna sesję. Ponieważ te instancje uruchamiają się dynamicznie, orkiestrator jest zmuszony przedłożyć dostępność serwera nad opóźnienia sieciowe.

Jeśli najbliższe główne centrum danych działa na granicy wydajności, warstwa orkiestracji przekieruje sesję do zapasowej strefy dostępności (availability zone) lub tańszego, odległego regionu. Ta dynamiczna zmiana natychmiast dodaje od 20 ms do 40 ms czystego czasu transmisji światłowodowej do połączenia gracza. Co więcej, środowiska typu sandbox pozwalają graczom na budowanie własnych poziomów z tysiącami dynamicznych obiektów, niestandardowych skryptów i interaktywnych urządzeń. Obiekty te powodują ogromny narzut replikacji (replication overhead), spowalniając główny wątek serwera i obniżając jego tick rate.

Wpływ degradacji tick rate na odczuwalne opóźnienie

Gdy spada liczba klatek na sekundę (frame rate) serwera, pętla replikacji sieciowej również spowalnia. Jeśli serwer celuje w tick rate na poziomie 30 Hz, oczekiwany czas ramki wynosi 33,3 ms. Jeśli klient wyśle pakiet, który dotrze tuż po tym, jak serwer rozpocznie wykonywanie swojego ticku, pakiet ten musi czekać w buforze sieciowym do rozpoczęcia kolejnego ticku.

Jeśli niezoptymalizowane skrypty sandbox obniżą tick rate serwera z 30 Hz do 15 Hz, czas ramki wzrasta do 66,6 ms. To opóźnienie przetwarzania automatycznie dodaje 33,3 ms do czasu RTT (round-trip time) klienta. Wbudowany w grę interfejs sieciowy klienta rejestruje to lokalne opóźnienie przetwarzania jako ping sieciowy, mimo że opóźnienie na fizycznym światłowodzie pozostaje bez zmian.

Dodatkowo, dynamiczny streaming treści generowanych przez użytkowników (UGC) zmusza serwer do serializacji i wysyłania ogromnych pakietów danych (payloads) do dołączających graczy. Ten nagły wzrost ruchu sieciowego powoduje zjawisko buffer bloat na domowych routerach i interfejsach sieciowych, prowadząc do kolejkowania pakietów. Gdy pakiety czekają w kolejce, opóźnienia gwałtownie rosną, a utrata pakietów (packet loss) wzrasta.

Gdy niezoptymalizowane skrypty UGC przeciążają procesor, spadki tick rate mogą eskalować do całkowitego zawieszenia serwera. Jeśli doświadczasz ogromnych skoków opóźnień pod obciążeniem, zapoznaj się z naszym protokołem naprawy awarii serwerów (server crash fix protocol), aby ustabilizować swój netcode.

Rola MTU i fragmentacji pakietów podczas ładowania UGC

Gdy gracze ładują się na niestandardową mapę sandbox, serwer musi zreplikować stan setek własnych aktorów. Ta synchronizacja stanu często przekracza standardowy rozmiar jednostki MTU (Maximum Transmission Unit), wynoszący 1500 bajtów. Gdy pakiety UDP przekraczają ten limit, warstwa sieciowa musi podzielić je (sfragmentować) na wiele mniejszych pakietów IP.

Jeśli chociażby jeden fragment zostanie zgubiony po drodze, cały pakiet UDP jest odrzucany przez stos sieciowy klienta. Uruchamia to retransmisję pakietów, powodując poważny jitter i skoki odczuwalnego pingu gracza. Ponieważ mapy kreatywne zawierają wysoce dynamiczne konfiguracje, fragmentacja ta występuje znacznie częściej niż w statycznych sesjach battle royale.

Aby temu zaradzić, deweloperzy powinni wdrożyć niestandardowe techniki serializacji, aby mocniej upakować dane aktorów. Utrzymując payloady replikacji poniżej progu 1200 bajtów, można całkowicie uniknąć fragmentacji IP. Ta prosta zmiana stabilizuje ścieżki tranzytu sieciowego i znacznie poprawia jakość połączenia.

Mechanika dynamicznego routingu serwerów i cold startów

Standardowy routing IP opiera się na statycznych konfiguracjach ścieżek, które sprawdzają się w przypadku stabilnych lokalizacji serwerów. Kiedy jednak instancje serwerów są tworzone dynamicznie w rozproszonej chmurze, standardowy routing unicast IP nie jest w stanie wybrać ścieżki o najniższym opóźnieniu. Gracz uruchamiający sesję kreatywną otrzymuje dynamiczny adres unicast IP przypisany do nowo utworzonego węzła kontenera.

Ponieważ ten adres IP jest tymczasowy, nie może on korzystać z globalnego routingu Anycast. Zamiast tego pakiety gracza muszą podróżować przez publiczny internet, przechodząc przez wielu niezoptymalizowanych dostawców tranzytowych i lokalne węzły routingu ISP. Stoi to w jaskrawej sprzeczności z głównymi kolejkami Matchmaking, które kierują graczy przez proxy brzegowe (edge proxy) z obsługą Anycast.

Te serwery proxy kończą połączenia klienckie w najbliższym punkcie obecności PoP (Point of Presence) i przesyłają dane tunelem przez prywatne sieci szkieletowe (backbones) bezpośrednio do kontenera serwera gry. Dynamiczne przydzielanie zasobów wprowadza również tzw. cold starty. Jeśli kontener serwera uruchamia się zbyt długo, gracze mogą napotkać błędy połączenia. Aby to naprawić, musisz zrozumieć, jak diagnozować problemy z driverami i limitami czasu połączenia (timeouts), jak szczegółowo opisano w naszym przewodniku dotyczącym rozwiązywania problemów z timeoutami przy uruchamianiu sesji (resolving session launch timeouts).

Techniczna analiza: Pomiar opóźnienia sieciowego ICMP vs. RTT pętli gry

Aby dokładnie wdrożyć optymalizację pingu serwerów gier multiplayer, należy rozróżnić fizyczne opóźnienie tranzytu sieciowego (ping ICMP/UDP) od czasu RTT na poziomie aplikacji. Ten pierwszy mierzy czas, jaki surowy pakiet sieciowy potrzebuje na dotarcie do serwera i z powrotem. Ten drugi obejmuje opóźnienie przetwarzania ramki przez serwer, czas serializacji sieciowej oraz opóźnienie interpolacji po stronie klienta.

Głównym problemem w przypadku wyświetlania pingu po stronie klienta jest to, że mierzy on całkowity czas, jaki upłynął między wysłaniem pakietu a otrzymaniem jego potwierdzenia (ACK). Jeśli serwer doświadcza wąskiego gardła procesora (CPU bottleneck) z powodu Garbage Collection lub skomplikowanych obliczeń fizycznych, opóźnia wysłanie potwierdzenia ACK. Klient nie potrafi odróżnić tego lokalnego opóźnienia serwera od opóźnień routingu, co prowadzi do błędnego raportowania wysokiego pingu sieciowego.

Wykonując test pingu na poziomie drivera sieciowego i porównując go z tick rate głównego wątku gry, deweloperzy mogą wyizolować te wąskie gardła. Pozwala to zespołowi ds. orkiestracji Backend określić, czy muszą zoptymalizować kod, czy zmienić ścieżki routingu sieciowego. Zobaczmy, jak możemy wdrożyć takiego agenta monitorującego.

Poniższa, kompatybilna z Unreal Engine klasa C++ pokazuje, jak śledzić surowy ping sieciowy i oddzielić go od narzutu przetwarzania pętli gry. Obliczając tę różnicę, możesz ustalić, czy wysoki ping gracza jest spowodowany złym routingiem sieciowym, czy też zatykającym się serwerem (spadkiem frame rate serwera).

// PingMonitor.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "PingMonitor.generated.h"

USTRUCT(BlueprintType)
struct FNetworkQualityStats
{
    GENERATED_BODY()

    UPROPERTY(BlueprintReadOnly, Category = "Network Quality")
    float NetworkPingMS;

    UPROPERTY(BlueprintReadOnly, Category = "Network Quality")
    float FrameProcessingDelayMS;

    UPROPERTY(BlueprintReadOnly, Category = "Network Quality")
    float TotalEffectiveRTT;

    FNetworkQualityStats()
        : NetworkPingMS(0.0f)
        , FrameProcessingDelayMS(0.0f)
        , TotalEffectiveRTT(0.0f)
    {}
};

UCLASS()
class MULTIPLAYERGAME_API APingMonitor : public AActor
{
    GENERATED_BODY()

public:
    APingMonitor();

protected:
    virtual void BeginPlay() override;

public:
    virtual void Tick(float DeltaTime) override;

    UFUNCTION(BlueprintCallable, Category = "Network Quality")
    FNetworkQualityStats GetCurrentNetworkStats() const;

private:
    float LastPingTime;
    float HeartbeatInterval;
    float ServerTickRate;
    
    TMap<int32, double> SentHeartbeats;
    int32 HeartbeatSequenceId;
    FNetworkQualityStats CachedStats;

    void SendHeartbeat();
    void HandleHeartbeatAck(int32 SequenceId);
};
// PingMonitor.cpp
#include "PingMonitor.h"
#include "GameFramework/PlayerController.h"
#include "Engine/World.h"

APingMonitor::APingMonitor()
    : HeartbeatInterval(1.0f)
    , ServerTickRate(30.0f)
    , HeartbeatSequenceId(0)
{
    PrimaryActorTick.bCanEverTick = true;
    PrimaryActorTick.TickInterval = 0.1f;
}

void APingMonitor::BeginPlay()
{
    Super::BeginPlay();
    LastPingTime = GetWorld()->GetTimeSeconds();
}

void APingMonitor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    float CurrentTime = GetWorld()->GetTimeSeconds();
    if (CurrentTime - LastPingTime >= HeartbeatInterval)
    {
        SendHeartbeat();
        LastPingTime = CurrentTime;
    }
}

void APingMonitor::SendHeartbeat()
{
    HeartbeatSequenceId++;
    double SendTimeStamp = FPlatformTime::Seconds();
    SentHeartbeats.Add(HeartbeatSequenceId, SendTimeStamp);
}

void APingMonitor::HandleHeartbeatAck(int32 SequenceId)
{
    if (SentHeartbeats.Contains(SequenceId))
    {
        double SendTime = SentHeartbeats[SequenceId];
        double ReceiveTime = FPlatformTime::Seconds();
        float RTT = static_cast<float>((ReceiveTime - SendTime) * 1000.0);

        float FrameTimeMS = GetWorld()->GetDeltaSeconds() * 1000.0f;
        float ExpectedTickTimeMS = 1000.0f / ServerTickRate;
        float ProcessingDelay = FMath::Max(0.0f, FrameTimeMS - ExpectedTickTimeMS);

        CachedStats.NetworkPingMS = RTT - ProcessingDelay;
        CachedStats.FrameProcessingDelayMS = ProcessingDelay;
        CachedStats.TotalEffectiveRTT = RTT;

        SentHeartbeats.Remove(SequenceId);

        UE_LOG(LogNet, Log, TEXT("RTT: %.2fms | NetPing: %.2fms | FrameDelay: %.2fms"),
            CachedStats.TotalEffectiveRTT, CachedStats.NetworkPingMS, CachedStats.FrameProcessingDelayMS);
    }
}

FNetworkQualityStats APingMonitor::GetCurrentNetworkStats() const
{
    return CachedStats;
}

Klasa ta oblicza opóźnienie FrameProcessingDelayMS poprzez porównanie rzeczywistego delta time ticku serwera z docelowym tick rate. Jeśli opóźnienie przetwarzania jest wysokie, deweloper wie, że musi zoptymalizować wykonywanie skryptów po stronie serwera, zamiast obwiniać dostawcę sieci. Uruchamiając ten system na serwerach testowych, możesz śledzić metryki jakości sieci w czasie rzeczywistym.

Architektura dynamicznego orkiestratora o niskim opóźnieniu

Aby ręcznie rozwiązać problem kary za opóźnienia w trybie kreatywnym, musisz przeprojektować warstwę orkiestracji serwerów. Typowa architektura opiera się na trzech filarach: geograficznie rozproszonym pre-warmingu, routingu brzegowym ułatwionym przez Anycast oraz agresywnym odchudzaniu assetów (asset stripping).

Po pierwsze, wdróż orkiestrator z funkcją pre-warmingu przy użyciu narzędzi takich jak Agones na Kubernetes. Zamiast uruchamiać kontenery od zera, utrzymuj małą pulę bezczynnych, rozgrzanych (warm) instancji serwerów w 8 do 12 globalnych regionach. System Matchmaking powinien stale monitorować zagęszczenie graczy i dynamicznie skalować te pule, aby zapobiec kierowaniu graczy do odległych regionów w godzinach szczytu.

Po drugie, umieść sieć proxy brzegowych (edge proxies) przed swoimi serwerami gier. Te serwery proxy powinny korzystać z routingu Anycast IP, aby akceptować połączenia UDP klientów na najbliższej krawędzi sieci. Proxy następnie tuneluje ruch z gry przez dedykowaną, prywatną sieć szkieletową (backbone) o niskim opóźnieniu (taką jak AWS Global Accelerator) bezpośrednio do kontenera serwera docelowego. Omija to niezoptymalizowane, publiczne ścieżki routingu, które są problemem dla połączeń unicast na żądanie.

Po trzecie, zoptymalizuj buildy swoich serwerów. Usuń wszystkie niepotrzebne assety po stronie klienta — takie jak tekstury o wysokiej rozdzielczości, pliki audio i siatki szkieletowe (skeletal meshes) — z buildu przeznaczonego dla Dedicated Server. Zmniejszy to rozmiary obrazów kontenerów z kilku gigabajtów do poniżej 200 MB, skracając czas pobierania kontenera. Pozwala to na ukończenie cold bootów w czasie poniżej 500 ms w sytuacjach, gdy wyczerpie się pojemność maszyn typu pre-warmed.

Usprawnienie routingu brzegowego i hostingu z horizOn

Budowanie i utrzymywanie rozproszonego geograficznie orkiestratora z routingiem brzegowym Anycast wymaga dedykowanego zespołu operacyjnego i tysięcy dolarów narzutu na infrastrukturę chmurową. Jest to skomplikowane zadanie inżynieryjne, które odciąga cenne zasoby czasowe od rozwoju samej rozgrywki. W tym miejscu platforma horizOn zapewnia gotowe rozwiązanie (turnkey solution) dla studiów niezależnych (indie) i średniej wielkości.

Zamiast pisać własne mechanizmy Load Balancing czy zarządzać klastrami Kubernetes w wielu chmurach, możesz wdrożyć buildy swoich serwerów na tej platformie. Wykorzystując czas rozruchu kontenera poniżej sekundy oferowany przez horizOn oraz wbudowane brzegowe bazy danych, eliminujesz timeouty zimnego startu (cold-start) i nieefektywny routing. Dzięki temu gracze mogą cieszyć się tak samo niskim opóźnieniem w kreatywnych sesjach sandboxowych, jak w strukturyzowanych lobby Matchmaking.

4 najlepsze praktyki w optymalizacji pingu serwerów gier multiplayer

Jeśli chcesz utrzymać niskie opóźnienia i stabilny tick rate w swoich kreatywnych trybach gry, wdróż te cztery sprawdzone w bojach strategie:

  1. Wdróż agresywny przeplot replikacji (Replication Interleaving): Grupuj niekrytyczne aktualizacje aktorów (takie jak przedmioty kosmetyczne czy odległe dekoracje w piaskownicy) i replikuj je co trzecią lub czwartą klatkę, zamiast w każdej klatce. Zmniejsza to rozmiar payloadu sieciowego i zapobiega zjawisku buffer bloat na routerach klienckich.
  2. Ogranicz budżet czasu procesora dla UGC (UGC Tick Budgets): Wprowadź rygorystyczne limity czasu wykonywania skryptów tworzonych przez użytkowników. Jeśli niestandardowa wyspa gracza przekracza 5 ms czasu wykonywania skryptu na ramkę, dław (throttle) lub wyłączaj skrypty o niskim priorytecie, aby chronić tick rate serwera przed spadkiem poniżej 30 Hz.
  3. Stosuj mechanizm handshake na sieci brzegowej: Weryfikuj autoryzację gracza i tokeny sesji w najbliższym brzegowym PoP przed skierowaniem go do serwera gry. Zapobiega to zużywaniu cykli CPU serwera przez złośliwe żądania uwierzytelniania, co mogłoby powodować opóźnienia w kolejkach pakietów dla aktywnych graczy.
  4. Wdróż dynamiczne skalowanie tick rate (Dynamic Tick-Rate Scaling): Gdy serwer trybu kreatywnego jest bezczynny lub znajduje się na nim tylko jeden gracz, zmniejsz jego tick rate do 10 Hz, aby oszczędzać zasoby CPU. Dynamicznie zwiększaj tick rate z powrotem do 30 Hz lub 60 Hz, gdy tylko inni gracze dołączą do sesji, zapewniając płynność rozgrywki podczas aktywnej gry multiplayer.

Podsumowanie i kolejne kroki

Rozwiązanie problemu kary za opóźnienia w trybie kreatywnym wymaga dwutorowego podejścia: optymalizacji czasów ramek serwera w celu wyeliminowania opóźnień przetwarzania oraz kierowania ruchu przez dedykowane sieci szkieletowe zamiast publicznego internetu. Monitorując opóźnienia przetwarzania pętli gry i wdrażając routing brzegowy, możesz zagwarantować swoim graczom rozgrywkę z niskim pingiem.

Chcesz zoptymalizować swoją infrastrukturę multiplayer? Wdróż swój kolejny build na platformie horizOn, aby zyskać automatyczny routing brzegowy oraz rozproszone geograficznie serwery gier o niskim opóźnieniu. Możesz też zapoznać się z dokumentacją API, aby dowiedzieć się więcej o integracji brzegowych baz danych (edge-network databases) z Twoją architekturą Backend.


Źródło: Higher ping especially in creative