{"id":47,"date":"2025-10-17T10:28:36","date_gmt":"2025-10-17T10:28:36","guid":{"rendered":"https:\/\/www.geekadvice.au\/?p=47"},"modified":"2025-10-19T11:01:32","modified_gmt":"2025-10-19T11:01:32","slug":"steamapi-in-unreal-engine","status":"publish","type":"post","link":"https:\/\/www.geekadvice.au\/index.php\/2025\/10\/17\/steamapi-in-unreal-engine\/","title":{"rendered":"SteamAPI in Unreal Engine"},"content":{"rendered":"\n<p>Okay so I have had some troubles getting Steam multiplayer working in Unreal Engine 5.6<br><br>I am going to share my current configuration and what is working for me, for reference to anyone else trying to get this working.<\/p>\n\n\n\n<p>There is still a lingering issue when trying to join sessions hosted on the MacOS from windows, at this stage I am guessing something like firewall blocking the client inbound connections. I don&#8217;t have two Mac&#8217;s to test Mac -&gt; Mac Connection, however the current state of play is that Windows -&gt; Windows lobbies work fine, MacOS -&gt; Windows lobbies work fine, but Windows -&gt; MacOS lobbies fail to join and timeout.<\/p>\n\n\n\n<p>DefaultEngine.ini Relevant settings:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>[OnlineSubsystem]<br>DefaultPlatformService=Steam<br>PollingIntervalInMs=20<br>bUseBuildIdOverride=True<br>BuildIdOverride=1<\/p>\n\n\n\n<p>[OnlineSubsystemSteam]<br>bEnabled=true<br>SteamDevAppId=3488710<br>bInitServerOnClient=true<br>bAllowP2PPacketRelay=true<br>bVACEnabled=false<br>bRelaunchInSteam=false<br>GameServerQueryPort=7777<br>bUseSteamNetworking=true ; enables SteamSockets path<\/p>\n<\/blockquote>\n\n\n\n<p>SteamSockets must be enabled in Project Plugins.<\/p>\n\n\n\n<p>I am also using AdvancedSession\/AdvancedSteamSessions, I have a derivative AdvancedFriendsGameInstance class that is then also Derived in Blueprints.<\/p>\n\n\n\n<p>The reason for a derived class was to add a new Join Session function, I was having some trouble with joining that may have been unrelated, but nevertheless, here is where I have ended up. For science I could go back and test the built in Join Session (maybe later), for now find my full class overwrite below:<\/p>\n\n\n\n<p>RW_AdvancedGameInstanceCPP.h<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"AdvancedFriendsGameInstance.h\"                \/\/ AdvancedSessions base GI\n#include \"Interfaces\/OnlineSessionInterface.h\"\n#include \"RW_AdvancedGameInstanceCPP.generated.h\"\n\nUCLASS()\nclass RACEWARS_GREEN_API URW_AdvancedGameInstanceCPP : public UAdvancedFriendsGameInstance\n{\n    GENERATED_BODY()\n\npublic:\n    \/** Call this from BP with the selected FBlueprintSessionResult *\/\n    UFUNCTION(BlueprintCallable, Category = \"Online|Sessions\")\n    void JoinSessionFromResult(const FBlueprintSessionResult&amp; SessionResult);\n\nprivate:\n    FOnJoinSessionCompleteDelegate JoinSessionCompleteDelegate;\n    FDelegateHandle JoinSessionCompleteHandle;\n\n    void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);\n\n    IOnlineSessionPtr GetSessions() const;\n};<\/code><\/pre>\n\n\n\n<p>And RW_AdvancedGameInstanceCPP.cpp<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Fill out your copyright notice in the Description page of Project Settings.\n\n\n#include \"RW_AdvancedGameInstanceCPP.h\"\n\n#include \"OnlineSubsystem.h\"\n#include \"Interfaces\/OnlineSessionInterface.h\"\n#include \"Kismet\/GameplayStatics.h\"\n#include \"Engine\/Engine.h\"\n\nIOnlineSessionPtr URW_AdvancedGameInstanceCPP::GetSessions() const\n{\n    if (IOnlineSubsystem* OSS = IOnlineSubsystem::Get())\n    {\n        return OSS-&gt;GetSessionInterface();\n    }\n    return nullptr;\n}\n\nvoid URW_AdvancedGameInstanceCPP::JoinSessionFromResult(const FBlueprintSessionResult&amp; SessionResult)\n{\n    IOnlineSessionPtr Sessions = GetSessions();\n    if (!Sessions.IsValid())\n    {\n        UE_LOG(LogTemp, Error, TEXT(\"&#91;JoinSessionFromResult] No SessionInterface.\"));\n        return;\n    }\n\n    \/\/ Bind join complete\n    JoinSessionCompleteDelegate = FOnJoinSessionCompleteDelegate::CreateUObject(\n        this, &amp;URW_AdvancedGameInstanceCPP::OnJoinSessionComplete\n    );\n    JoinSessionCompleteHandle = Sessions-&gt;AddOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegate);\n\n    const FName SessionName = NAME_GameSession; \/\/ Must match your CreateSession name\n    const int32 LocalUserNum = 0;\n\n    const bool bJoinStarted = Sessions-&gt;JoinSession(LocalUserNum, SessionName, SessionResult.OnlineResult);\n    UE_LOG(LogTemp, Log, TEXT(\"&#91;JoinSessionFromResult] JoinSession started: %s\"), bJoinStarted ? TEXT(\"true\") : TEXT(\"false\"));\n\n    if (!bJoinStarted)\n    {\n        Sessions-&gt;ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteHandle);\n    }\n}\n\nvoid URW_AdvancedGameInstanceCPP::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)\n{\n    IOnlineSessionPtr Sessions = GetSessions();\n    if (Sessions.IsValid())\n    {\n        Sessions-&gt;ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteHandle);\n    }\n\n    UE_LOG(LogTemp, Log, TEXT(\"&#91;OnJoinSessionComplete] Result: %d (0=Success)\"), (int32)Result);\n\n    if (Result != EOnJoinSessionCompleteResult::Success)\n    {\n        UE_LOG(LogTemp, Warning, TEXT(\"&#91;OnJoinSessionComplete] Join failed.\"));\n        return;\n    }\n\n    \/\/ Resolve Steam connect string and force ABSOLUTE travel\n    FString ConnectString;\n#if ENGINE_MAJOR_VERSION &gt;= 5\n    const bool bHasConnect = Sessions-&gt;GetResolvedConnectString(SessionName, ConnectString, NAME_GamePort);\n#else\n    const bool bHasConnect = Sessions-&gt;GetResolvedConnectString(SessionName, ConnectString);\n#endif\n\n    if (!bHasConnect || ConnectString.IsEmpty())\n    {\n        UE_LOG(LogTemp, Error, TEXT(\"&#91;OnJoinSessionComplete] No ConnectString resolved.\"));\n        return;\n    }\n\n    UE_LOG(LogTemp, Log, TEXT(\"&#91;OnJoinSessionComplete] Resolved ConnectString: %s\"), *ConnectString);\n\n    if (APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))\n    {\n        \/\/ Critical: ABSOLUTE travel prevents appending your current MainMenu map path\n        PC-&gt;ClientTravel(ConnectString, TRAVEL_Absolute);\n    }\n    else\n    {\n        UE_LOG(LogTemp, Error, TEXT(\"&#91;OnJoinSessionComplete] No PlayerController.\"));\n    }\n}<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>Beyond this, I am using the standard CreateAdvancedSession blueprint node, along with the FindSessionsAdvanced and my custom JoinSessionfromResult function. I also call the custom join function for &#8220;Event OnSessionInviteAccepted&#8221;<\/p>\n\n\n\n<p>One crucial point to make is that session destruction is not being handled well by default, particularly as a client in a session, if the host quits\/ends session the client does not seem to destroy the session, so if the client was then to try host or join a session they may fail due to not being able to do so while already in a game session.<\/p>\n\n\n\n<p>I have added a &#8220;Destroy Session&#8221; infront of all the create\/join sessions but suspect there is a cleaner method that can destroy the sessions when leaving. Will continue looking into that.<\/p>\n\n\n\n<p>Other notes would be to download the correct version of AdvancedSession&#8217;s that you need from <a href=\"https:\/\/vreue4.com\/advanced-sessions-binaries\">Advanced Sessions Binaries \u2013 VR Expansion Plugin<\/a><\/p>\n\n\n\n<p>My DefaultEngine.ini is referencing a custom BuildID but this may be unnecessary, it was added for testing, but if it can be used as a way to prevent different build&#8217;s from accessing each other&#8217;s sessions it would remove one of my session filters requirements.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>That&#8217;s all for now. Race Wars v0.0.961 released, tested working Windows -> Windows for all multiplayer sessions.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Okay so I have had some troubles getting Steam multiplayer working in Unreal Engine 5.6 I am going to share my current configuration and what is working for me, for reference to anyone else trying to get this working. There is still a lingering issue when trying to join sessions hosted on the MacOS from [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":62,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[8,6],"class_list":["post-47","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-racewars","tag-advancedsessions","tag-race-wars"],"_links":{"self":[{"href":"https:\/\/www.geekadvice.au\/index.php\/wp-json\/wp\/v2\/posts\/47","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.geekadvice.au\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.geekadvice.au\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.geekadvice.au\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.geekadvice.au\/index.php\/wp-json\/wp\/v2\/comments?post=47"}],"version-history":[{"count":3,"href":"https:\/\/www.geekadvice.au\/index.php\/wp-json\/wp\/v2\/posts\/47\/revisions"}],"predecessor-version":[{"id":52,"href":"https:\/\/www.geekadvice.au\/index.php\/wp-json\/wp\/v2\/posts\/47\/revisions\/52"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.geekadvice.au\/index.php\/wp-json\/wp\/v2\/media\/62"}],"wp:attachment":[{"href":"https:\/\/www.geekadvice.au\/index.php\/wp-json\/wp\/v2\/media?parent=47"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.geekadvice.au\/index.php\/wp-json\/wp\/v2\/categories?post=47"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.geekadvice.au\/index.php\/wp-json\/wp\/v2\/tags?post=47"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}