Kembali ke Blog

Mengapa Steam Online Subsystem Anda Tidak Bisa Join Lobby di Unreal Engine 5

Diterbitkan pada 15 Juni 2026
Mengapa Steam Online Subsystem Anda Tidak Bisa Join Lobby di Unreal Engine 5

Ringkasnya

Artikel ini menjelaskan solusi untuk mengatasi masalah gagalnya koneksi saat mencoba join lobby menggunakan Steam Online Subsystem di Unreal Engine 5. Penulis membedah ketidakcocokan konfigurasi network driver antara SteamNetDriver legacy dan plugin SteamSockets modern pada DefaultEngine.ini. Selain itu, artikel ini memberikan panduan mengatasi batasan sandbox AppID 480 dengan filter pencarian C++ kustom serta tips menghindari konflik port binding saat pengujian lokal.

Anda mem-package proyek Unreal Engine 5, menjalankan dua akun Steam yang berbeda, meluncurkan client, dan melihat lobby yang di-host muncul di hasil pencarian session. Anda mengeklik "Join," menunggu lima detik, dan... tidak terjadi apa-apa. Layar membeku, log konsol mencetak peringatan socket generik, dan Anda dikembalikan ke menu utama.

Jika koneksi Steam Online Subsystem Anda tidak bisa join lobby, Anda sedang berhadapan dengan salah satu masalah konfigurasi jaringan yang paling membuat frustrasi di Unreal Engine 5. Ini adalah kegagalan senyap (silent failure) di mana Steam matchmaking API berhasil mendaftarkan lobby Anda dengan benar, tetapi network driver engine tidak dapat menyelesaikan low-level connection handshake. Dalam panduan ini, kami akan membedah arsitektur network driver yang mendasarinya, mengidentifikasi ketidakcocokan DefaultEngine.ini yang umum terjadi, mengatasi batasan sandbox AppID 480, dan menunjukkan cara mengonfigurasi pipeline koneksi Steam yang tangguh di Unreal Engine 5.

Memahami Arsitektur Netcode Epic Games vs. Valve

Untuk memahami mengapa proses join gagal, Anda harus memahami bagaimana layer netcode Unreal Engine menerjemahkan hasil pencarian Steam lobby menjadi koneksi jaringan. Saat Anda memanggil JoinSession dari kode game Anda, session interface OnlineSubsystemSteam akan menerjemahkan lobby tersebut menjadi connection string. Untuk Steam, connection string ini diformat sebagai steam.STEAM_ID (misalnya, steam.76561198000000000), yang mewakili Steam ID unik milik host.

Network driver factory bawaan Unreal Engine (GameNetDriver) menerima connection string ini dan mem-parsing prefix skema (steam.). Engine kemudian memeriksa konfigurasi DefaultEngine.ini di bawah [/Script/Engine.GameEngine] untuk menemukan class yang dikonfigurasi untuk menangani koneksi Steam. Jika pemetaan (mapping) ini hilang, tidak cocok, atau class net connection yang tepat tidak dimuat, engine akan beralih ke IpNetDriver sebagai fallback.

IpNetDriver tidak dapat menyelesaikan (resolve) Steam ID. Driver ini akan mencoba memperlakukan steam.76561198000000000 sebagai hostname DNS standar atau IP address, gagal menyelesaikannya, dan memicu network timeout. Jika network driver Anda salah dikonfigurasi atau connection handshake gagal, Anda akan mengalami network driver timeout yang sama seperti yang sering dikeluhkan para developer saat mencoba mengatasi Unreal Engine network driver timeouts. Memahami bagaimana definisi NetDriver cocok dengan skema koneksi adalah langkah pertama untuk menyelesaikan ketidakcocokan ini.

Konflik Antara SteamSockets vs. SteamNetDriver

Penyebab paling umum dari kegagalan ini di Unreal Engine 5 adalah konflik antara SteamNetDriver warisan (legacy) dan plugin SteamSockets modern. Secara historis, Unreal Engine menggunakan SteamNetDriver legacy (berdasarkan P2P API Valve yang lebih lama). Proyek UE5 modern menggunakan plugin SteamSockets, yang memanfaatkan Steam Networking Sockets API milik Valve (mendukung Steam Datagram Relay, atau SDR, untuk perlindungan DDoS dan optimasi routing).

Banyak developer menambahkan "SteamSockets" ke modul dependensi proyek mereka di C++ tetapi lupa memperbarui definisi class network driver di file konfigurasi mereka. Atau sebaliknya, mereka menentukan class driver legacy saat engine mencoba menginisialisasi Steam Sockets. Mari kita lihat konfigurasi yang benar untuk masing-masing pendekatan.

Konfigurasi Legacy SteamNetDriver

Jika Anda menggunakan network driver legacy OnlineSubsystemSteam, file DefaultEngine.ini Anda harus memetakan GameNetDriver ke class legacy tersebut:

[/Script/Engine.GameEngine]
!NetDriverDefinitions=ClearArray
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")

[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"

Selain itu, pastikan bUseSteamNetworking=true diatur di bawah kategori [OnlineSubsystemSteam] Anda di file DefaultEngine.ini.

Konfigurasi Modern SteamSockets

Jika Anda telah mengaktifkan plugin SteamSockets di file .uproject Anda, Anda harus mengubah nama class driver dan koneksinya. Perhatikan perubahan nama class dan header bagian:

[/Script/Engine.GameEngine]
!NetDriverDefinitions=ClearArray
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="SteamSockets.SteamSocketsNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")

[/Script/SteamSockets.SteamSocketsNetDriver]
NetConnectionClassName="SteamSockets.SteamSocketsNetConnection"

Jika Anda mengonfigurasi Build.cs Anda untuk menyertakan SteamSockets tetapi menggunakan konfigurasi OnlineSubsystemSteam.SteamNetDriver legacy di file INI Anda, engine akan menginisialisasi class net connection yang salah. Hal ini mencegah network handshake client untuk menyelesaikan Steam ID host, yang menyebabkan upaya join menjadi hang dan mengalami timeout.

Memverifikasi Build Dependencies

Pastikan file .Build.cs proyek Anda cocok dengan setup yang Anda pilih. Sebagai contoh, jika Anda menargetkan implementasi Steam Sockets modern, file Build.cs modul game utama Anda harus secara eksplisit mendeklarasikan OnlineSubsystemSteam dan SteamSockets:

using UnrealBuildTool;

public class MyTPSGame : ModuleRules
{
    public MyTPSGame(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] {
            "Core",
            "CoreUObject",
            "Engine",
            "InputCore",
            "EnhancedInput",
            "OnlineSubsystem",
            "OnlineSubsystemUtils",
            "OnlineSubsystemSteam",
            "SteamSockets",
            "UMG",
            "Slate",
            "SlateCore"
        });
    }
}

Jebakan Sandbox Steam AppID 480

Jika konfigurasi Anda sudah benar tetapi Anda masih tidak bisa join, Anda kemungkinan menjadi korban dari batasan sandbox Steam Dev AppID 480. Secara default, developer menggunakan AppID 480 (Spacewar) untuk menguji integrasi Steam tanpa mendaftarkan aplikasi komersial ke Valve. Namun, AppID 480 ini digunakan bersama oleh ribuan developer di seluruh dunia.

Hal ini menimbulkan dua masalah utama:

  1. Lobby Pollution (Polusi Lobby): Pencarian session pada AppID 480 akan mengembalikan lobby yang di-host oleh game buatan developer lain. Ketika client Anda mencoba join ke lobby acak tersebut, prosesnya akan gagal karena versi game atau build ID yang tidak cocok.
  2. Region Isolation (Isolasi Wilayah): Secara default, Steam lobbies memiliki visibilitas regional untuk menjaga ping tetap rendah. Jika Anda sedang melakukan pengujian dengan rekan tim di luar lokasi (misalnya, satu di New York dan satu di London), query session standar tidak akan menemukan atau terhubung satu sama lain kecuali filter jarak pencarian Anda diubah.

Untuk melewati batasan-batasan ini, Anda harus mengonfigurasi pengaturan pencarian session secara eksplisit di C++ untuk menggunakan filter jarak worldwide dan parameter query kustom.

Berikut cara menulis query pencarian session C++ kustom untuk menargetkan Steam lobbies global sekaligus memfilter traffic AppID 480 yang tidak relevan:

#include "OnlineSubsystem.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "OnlineSessionSettings.h"

void UMultiplayerSessionSubsystem::FindSteamLobbies(int32 MaxResults)
{
    IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get();
    if (!Subsystem)
    {
        UE_LOG(LogTemp, Warning, TEXT("Failed to get OnlineSubsystem."));
        return;
    }

    IOnlineSessionPtr SessionInterface = Subsystem->GetSessionInterface();
    if (!SessionInterface.IsValid())
    {
        UE_LOG(LogTemp, Warning, TEXT("Session interface is invalid."));
        return;
    }

    // Allocate a new session search configuration
    SessionSearch = MakeShareable(new FOnlineSessionSearch());
    SessionSearch->MaxSearchResults = MaxResults;
    SessionSearch->bIsLanQuery = false;

    // Use presence to ensure we look for Steam lobbies rather than dedicated servers
    SessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
    SessionSearch->QuerySettings.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);

    // CRITICAL: Set Steam Lobby Search Distance Filter
    // 0 = Close, 1 = Default, 2 = Far, 3 = Worldwide
    // Worldwide is necessary if testing across different regions on AppID 480
    SessionSearch->QuerySettings.Set(SEARCH_LOBBY_SEARCH_DISTANCE_FILTER, 3, EOnlineComparisonOp::Equals);

    // Apply a unique game identifier key to filter out other developers' Spacewar lobbies
    // Replace "MY_UNIQUE_GAME_ID_KEY" with a unique string specific to your prototype
    SessionSearch->QuerySettings.Set(TEXT("GAME_VERSION_KEY"), FString("MyTPSGame_v1.0.4"), EOnlineComparisonOp::Equals);

    // Bind callback to handle search completion
    OnFindSessionsCompleteDelegateHandle = SessionInterface->AddOnFindSessionsCompleteDelegate_Handle(
        OnFindSessionsCompleteDelegate,
        FOnFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnFindSessionsComplete)
    );

    ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController();
    if (LocalPlayer)
    {
        SessionInterface->FindSessions(*LocalPlayer->GetPreferredUniqueNetId(), SessionSearch.ToSharedRef());
    }
}

Saat membuat session pada client yang bertindak sebagai host, pastikan untuk menambahkan key kustom yang sama (GAME_VERSION_KEY dengan value MyTPSGame_v1.0.4) ke map FOnlineSessionSettings::Settings. Ini memastikan query pencarian Anda hanya mengembalikan lobby game Anda sendiri, sehingga benar-benar terhindar dari polusi AppID 480. Setelah berhasil melewati koneksi awal lobby, Anda juga harus memastikan RPC Anda direplikasi secara andal tanpa menyebabkan Unreal Engine RPC replication desyncs yang dapat langsung merusak gameplay state Anda setelah berhasil join.

Konflik Port Binding dan Pengujian Lokal Satu Mesin

Jika Anda menguji multiplayer secara lokal pada satu PC menggunakan dua akun Steam (misalnya, menggunakan Sandboxie atau instance kompilasi kedua yang diluncurkan melalui command-line script), Anda kemungkinan besar akan mengalami konflik port binding. Saat menjadi host, Steam Subsystem milik Unreal Engine mencoba mendaftarkan game server lokal. Pengaturan bInitServerOnClient=true di file DefaultEngine.ini menginstruksikan client untuk menginisialisasi game server API milik Steamworks.

Jika dua instance di mesin yang sama sama-sama mencoba melakukan bind ke default Steam Query Port (27015) and Game Port (7777), instance kedua akan gagal membuka socket-nya. Akibatnya, client kedua akan mencari dan menemukan lobby tersebut, tetapi tidak akan dapat menginisialisasi incoming connection socket.

Untuk mengatasinya:

  1. Ubah Port untuk Instance Kedua: Saat meluncurkan instance game kedua dari terminal atau batch script, paksa untuk melakukan bind ke port yang berbeda dengan menambahkan parameter -port=7778 pada command line.
  2. Perbaiki Offset Query Port: Di bawah bagian [OnlineSubsystemSteam], verifikasi konfigurasi query port dan game port Anda:
    [OnlineSubsystemSteam]
    bEnabled=true
    SteamDevAppId=480
    bInitServerOnClient=true
    bUseSteamNetworking=true
    GameServerQueryPort=27015
    
    Jika melakukan pengujian di jaringan lokal yang sama, pastikan firewall router Anda tidak memblokir traffic UDP pada port-port tersebut.

Mengeliminasi Masalah Matchmaking dengan Game Backend Modern

Mengutak-atik konfigurasi file ini milik Steam, distance filter, dan batasan AppID secara manual adalah pemborosan waktu yang besar bagi tim indie. Menyiapkan sistem lobby siap produksi dengan fallback regional, aturan matchmaking, dan network driver handshake yang andal dapat dengan mudah memakan waktu 4 hingga 6 minggu kerja backend engineering khusus.

Di sinilah game backend khusus berperan. horizOn adalah Backend-as-a-Service (BaaS) yang dirancang khusus untuk game developer. Alih-alih memaksa Anda melakukan debugging pada library socket khusus Steam atau menulis modul wrapper C++ yang rumit, horizOn menyediakan SDK terpadu untuk mengelola lobby, matchmaking, dan player session.

Dengan memigrasikan logika matchmaking Anda ke horizOn, Anda tidak perlu lagi mengkhawatirkan pemetaan (mapping) network driver di file engine Anda. Player session dijembatani menggunakan infrastruktur server yang terdistribusi secara global, menawarkan instant matchmaking dan relay yang terlindungi dari DDoS secara langsung (out of the box).

Best Practices untuk Unreal Engine Steam Multiplayer

Untuk membangun pipeline multiplayer yang andal dan dapat diskalakan melampaui pengujian prototipe, ikuti beberapa best practices berikut:

  1. Transisi ke Steam Sockets Lebih Awal: Hindari konfigurasi networking driver P2P legacy. Gunakan bind ke net driver SteamSockets yang modern untuk memanfaatkan routing Steam Datagram Relay (SDR) dan mencegah kegagalan NAT punch-through.
  2. Terapkan Custom Filtering Key: Saat menguji di AppID 480, tambahkan pengenal proyek yang sangat spesifik ke session settings Anda. Ini mencegah client Anda mencoba terhubung ke game lain yang berbagi sandbox yang sama.
  3. Tangani NULL Subsystem Fallback dengan Baik: Pastikan netcode Anda dapat melakukan fallback ke NULL subsystem ketika Steam tidak berjalan. Ini memungkinkan pengujian LAN offline tanpa merusak logika koneksi.
  4. Optimalkan Session Join Timeout: Atur ConnectionTimeout di bawah [ActiveNetDriver] minimal 15.0 detik. Handshake P2P milik Steam memerlukan waktu beberapa detik untuk selesai, terutama jika dirutekan melalui relay SDR global.

Kesimpulan

Mengatasi kegagalan koneksi Steam lobby di Unreal Engine 5 adalah tentang menyelaraskan konfigurasi engine Anda dengan plugin jaringan yang Anda gunakan untuk melakukan build. Dengan memetakan NetDriverDefinitions secara benar dan meng-override filter pencarian Spacewar, Anda dapat membangun koneksi yang stabil untuk playtest Anda.

Jika Anda ingin melewati kerumitan infrastruktur backend dan fokus sepenuhnya pada gameplay, pertimbangkan untuk mengintegrasikannya dengan backend khusus. Siap meningkatkan skala backend multiplayer Anda? Coba horizOn secara gratis atau pelajari API docs.


Sumber: Steam Online Subsystem can't join lobby