Pourquoi votre Steam Online Subsystem ne peut pas rejoindre un Lobby dans Unreal Engine 5
En bref
Ce guide technique résout les problèmes de timeout réseau lors de la connexion aux lobbies Steam dans Unreal Engine 5. Il explique comment configurer correctement les NetDriverDefinitions dans le fichier DefaultEngine.ini pour éviter les conflits entre SteamNetDriver et SteamSockets. Enfin, il aborde la gestion des filtres de distance sur l'AppID de test 480, la résolution des conflits de ports en local et l'alternative des solutions backend comme horizOn.
Vous packagez votre projet Unreal Engine 5, lancez deux comptes Steam distincts, démarrez le client, et voyez votre lobby hébergé apparaître dans les résultats de recherche de session. Vous cliquez sur "Join", attendez des secondes, et... rien ne se passe. L'écran se fige, la console affiche un avertissement de socket générique, et vous êtes renvoyé au menu principal.
Si votre Steam Online Subsystem ne parvient pas à établir les connexions pour rejoindre un lobby (join lobby), vous êtes confronté à l'un des problèmes de configuration réseau les plus frustrants d'Unreal Engine 5. Il s'agit d'un échec silencieux où l'API de matchmaking de Steam enregistre correctement votre lobby, mais le driver réseau de l'engine ne parvient pas à finaliser le low-level connection handshake. Dans ce guide, nous allons décortiquer l'architecture sous-jacente du driver réseau, identifier les incompatibilités courantes de DefaultEngine.ini, résoudre les limitations du sandbox de l'AppID 480, et vous montrer comment configurer un pipeline de connexion Steam robuste dans Unreal Engine 5.
Comprendre l'architecture Netcode Epic Games vs. Valve
Pour comprendre pourquoi la connexion échoue, il faut comprendre comment la couche Netcode d'Unreal Engine traduit un résultat de recherche de lobby Steam en une connexion réseau. Lorsque vous appelez JoinSession depuis le code de votre jeu, l'interface de session de l' OnlineSubsystemSteam résout le lobby en une chaîne de connexion (connection string). Pour Steam, cette chaîne de connexion est formatée sous la forme steam.STEAM_ID (par exemple, steam.76561198000000000), représentant le Steam ID unique de l'hôte.
La factory de driver réseau d'Unreal Engine (GameNetDriver) reçoit cette chaîne de connexion et analyse le préfixe du schéma (steam.). Elle vérifie ensuite vos configurations dans DefaultEngine.ini sous [/Script/Engine.GameEngine] pour trouver la classe configurée pour gérer les connexions Steam. Si ce mapping est manquant, incorrect, ou si la bonne classe de connexion réseau n'est pas chargée, l'engine se rabat sur l' IpNetDriver.
L' IpNetDriver ne peut pas résoudre un Steam ID. Il va tenter de traiter steam.76561198000000000 comme un nom d'hôte DNS standard ou une adresse IP, échouer à le résoudre, et déclencher un timeout réseau. Si vos drivers réseau sont mal configurés ou si les handshakes de connexion échouent, vous rencontrerez les mêmes timeouts de driver réseau qui perturbent les développeurs essayant de résoudre les Unreal Engine network driver timeouts. Comprendre comment les définitions de NetDriver correspondent au schéma de connexion est la première étape pour corriger cette incompatibilité.
Le conflit entre SteamSockets et SteamNetDriver
La cause la plus fréquente de cet échec dans Unreal Engine 5 est un conflit entre l'ancien SteamNetDriver hérité et le plugin moderne SteamSockets. Historiquement, Unreal Engine utilisait le SteamNetDriver obsolète (basé sur l'ancienne API P2P de Valve). Les projets UE5 modernes utilisent le plugin SteamSockets, qui s'appuie sur l'API Steam Networking Sockets de Valve (prenant en charge le Steam Datagram Relay, ou SDR, pour la protection DDoS et l'optimisation du routage).
De nombreux développeurs ajoutent "SteamSockets" aux modules de dépendance de leur projet en C++ mais oublient de mettre à jour les définitions de classe du driver réseau dans leurs fichiers de configuration. Ou inversement, ils spécifient les classes du driver hérité alors que l'engine tente d'initialiser Steam Sockets. Voyons la configuration correcte pour chaque approche.
La configuration de l'ancien SteamNetDriver
Si vous utilisez le driver réseau hérité OnlineSubsystemSteam, votre fichier DefaultEngine.ini doit associer le GameNetDriver à la classe héritée :
[/Script/Engine.GameEngine]
!NetDriverDefinitions=ClearArray
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")
[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"
De plus, assurez-vous que bUseSteamNetworking=true est configuré sous votre catégorie [OnlineSubsystemSteam] dans DefaultEngine.ini.
La configuration moderne de SteamSockets
Si vous avez activé le plugin SteamSockets dans votre fichier .uproject, vous devez modifier les noms des classes de driver et de connexion. Notez le changement des noms de classe et des en-têtes de section :
[/Script/Engine.GameEngine]
!NetDriverDefinitions=ClearArray
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="SteamSockets.SteamSocketsNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")
[/Script/SteamSockets.SteamSocketsNetDriver]
NetConnectionClassName="SteamSockets.SteamSocketsNetConnection"
Si vous configurez votre Build.cs pour inclure SteamSockets mais utilisez les configurations de l'ancien OnlineSubsystemSteam.SteamNetDriver dans votre fichier INI, l'engine initialisera la mauvaise classe de connexion réseau. Cela empêche le handshake réseau du client de résoudre le Steam ID de l'hôte, ce qui provoque le blocage et le timeout de la tentative de connexion.
Vérifier les dépendances de Build
Assurez-vous que le fichier .Build.cs de votre projet correspond à la configuration choisie. Par exemple, si vous ciblez l'implémentation moderne de Steam Sockets, le fichier Build.cs de votre module de jeu principal doit déclarer explicitement à la fois OnlineSubsystemSteam et 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"
});
}
}
Le piège du sandbox de l'AppID 480 de Steam
Si vos configurations sont correctes mais que vous ne parvenez toujours pas à vous connecter, vous êtes probablement victime des limites du sandbox de l'AppID de développement Steam 480. Par défaut, les développeurs utilisent l'AppID 480 (Spacewar) pour tester l'intégration de Steam sans enregistrer d'application commerciale auprès de Valve. Cependant, l'AppID 480 est partagé par des milliers de développeurs dans le monde entier.
Cela crée deux problèmes distincts :
- Lobby Pollution : Une recherche de session sur l'AppID 480 retournera des lobbies hébergés par les jeux d'autres développeurs. Lorsque votre client tentera de rejoindre un lobby aléatoire, la connexion échouera en raison de versions de jeu ou d'IDs de build incompatibles.
- Region Isolation : Les lobbies Steam ont par défaut une visibilité régionale pour maintenir des pings bas. Si vous testez avec un coéquipier à distance (par exemple, un à New York et un autre à Londres), les requêtes de session standard ne se trouveront pas et ne se connecteront pas entre elles, à moins de modifier vos filtres de distance de recherche.
Pour contourner ces limitations, vous devez configurer explicitement les paramètres de recherche de session en C++ pour utiliser un filtre de distance mondial (worldwide) et des paramètres de requête personnalisés.
Voici comment écrire une requête de recherche de session personnalisée en C++ pour cibler les lobbies Steam mondiaux tout en filtrant le trafic non lié à l'AppID 480 :
#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());
}
}
Lors de la création de la session sur le client hôte, assurez-vous d'ajouter la même clé personnalisée (GAME_VERSION_KEY avec la valeur MyTPSGame_v1.0.4) à la map FOnlineSessionSettings::Settings. Cela garantit que votre requête de recherche ne renvoie que les lobbies de votre jeu, éliminant complètement la pollution de l'AppID 480. Une fois la connexion initiale au lobby établie, vous devez également vous assurer que vos RPC se répliquent de manière fiable sans causer de Unreal Engine RPC replication desyncs, ce qui pourrait briser vos états de gameplay immédiatement après la connexion.
Conflits de liaisons de ports et de tests sur une seule machine
Si vous testez le multiplayer localement sur un seul PC en utilisant deux comptes Steam (par exemple, via Sandboxie ou une seconde instance compilée lancée depuis un script en ligne de commande), vous rencontrerez probablement des conflits de liaisons de ports. Lors de l'hébergement, le sous-système Steam d'Unreal Engine tente d'enregistrer un serveur de jeu local. Le paramètre bInitServerOnClient=true dans DefaultEngine.ini indique au client d'initialiser l'API game server de Steamworks.
Si deux instances sur la même machine tentent toutes deux de se lier aux ports par défaut Steam Query Port (27015) et Game Port (7777), la seconde instance ne parviendra pas à ouvrir ses sockets. Par conséquent, le second client cherchera et trouvera le lobby, mais sera incapable d'initialiser le socket de connexion entrante.
Pour résoudre ce problème :
- Modifier le port de la seconde instance : Lors du lancement de la deuxième instance du jeu à partir du terminal ou d'un script batch, forcez-la à se lier à un port différent en ajoutant
-port=7778aux paramètres de ligne de commande. - Corriger les décalages de port de requête : Sous
[OnlineSubsystemSteam], vérifiez la configuration de vos ports de requête (query) et de jeu :
Si vous effectuez des tests sur le même réseau local, assurez-vous que le pare-feu de votre routeur ne bloque pas le trafic UDP sur ces ports.[OnlineSubsystemSteam] bEnabled=true SteamDevAppId=480 bInitServerOnClient=true bUseSteamNetworking=true GameServerQueryPort=27015
Éliminer les casse-têtes du matchmaking grâce à un backend moderne
Se battre manuellement avec les configurations ini de Steam, les filtres de distance et les limitations de l'AppID est une perte de temps considérable pour les studios indépendants. Mettre en place un système de lobby prêt pour la production avec des replis régionaux, des règles de matchmaking et des handshakes de driver réseau fiables peut facilement coûter 4 à 6 semaines de développement backend dédié.
C'est là qu'un backend de jeu dédié intervient. horizOn est un Backend-as-a-Service (BaaS) conçu spécifiquement pour les développeurs de jeux. Au lieu de vous contraindre à déboguer des bibliothèques de sockets spécifiques à Steam ou à écrire des modules wrapper complexes en C++, horizOn propose un SDK unifié pour gérer les lobbies, le matchmaking et les sessions de joueurs.
En migrant votre logique de matchmaking vers horizOn, vous n'avez plus à vous soucier des mappings de drivers réseau dans vos fichiers engine. Les sessions des joueurs sont intermédiées via une infrastructure de serveurs distribuée mondialement, offrant un matchmaking instantané et des relais protégés contre les attaques DDoS dès le départ.
Bonnes pratiques pour le multiplayer Steam dans Unreal Engine
Pour concevoir un pipeline multiplayer fiable capable de dépasser le stade des tests de prototypes, suivez ces bonnes pratiques :
- Passez tôt à Steam Sockets : Évitez les configurations obsolètes de driver réseau P2P. Liez-vous au driver réseau moderne
SteamSocketspour tirer parti du routage Steam Datagram Relay (SDR) et éviter les échecs de traversée de NAT (NAT punch-through). - Appliquez des clés de filtrage personnalisées : Lors de vos tests sur l'AppID 480, ajoutez un identifiant de projet très spécifique dans vos paramètres de session. Cela évitera que votre client ne tente de se connecter à d'autres jeux partageant ce sandbox.
- Gérez proprement le repli vers le sous-système NULL : Assurez-vous que votre netcode peut se rabattre sur le sous-système
NULLlorsque Steam n'est pas lancé. Cela permet des tests en LAN hors ligne sans casser la logique de connexion. - Optimisez les timeouts de connexion aux sessions : Définissez
ConnectionTimeoutsous[ActiveNetDriver]à au moins 15.0 secondes. Le handshake P2P de Steam peut prendre plusieurs secondes, en particulier s'il transite par des relais SDR globaux.
Conclusion
Résoudre les échecs de connexion aux lobbies Steam dans Unreal Engine 5 consiste avant tout à aligner les configurations de votre engine avec les plugins réseau que vous compilez. En configurant correctement vos NetDriverDefinitions et en surchargeant les filtres de recherche Spacewar, vous pouvez établir des connexions stables pour vos playtests.
Si vous souhaitez éviter les maux de tête liés à l'infrastructure backend pour vous concentrer uniquement sur le gameplay, envisagez une intégration avec un backend dédié. Prêt à faire monter en charge votre backend multiplayer ? Essayez horizOn gratuitement ou consultez la documentation de l'API.