Dear Friends at Epic,
I am using William Gaul’s independent server listing service to list games for my game.
So I can upload whatever data I want about my hosted game, and then tell other instances of my game that information.
In UE3 I did the following
- determined the unique steam game Id for a newly hosted game
- uploaded that unique game ID to independent server listing / Gemini
- had a client read that unique game name from Gemini in separate game instance
- join the hosted game using ConsoleCommand(" open Steam.# ")
where # is the unique game id
the core of the UE3 code revolved around this
//display this server's net ID, must launch game from commandline with ?steamsockets
OnlineSub.AuthInterface.GetServerUniqueId(outID); //OnlineAuthInterface
//cast onlineSub to steamworks version to access string conversion function
theID = OnlineSubsystemSteamworks(OnlineSub).UniqueNetIdToInt64(outID);
final function joinGame(string steamUID) {
clientmessage("joining"@steamUID);
consolecommand("open"@ "steam."$steamUID);
}
How to do something similar in UE4?
I dont see any references to a unique game Id in the UE4 steam code
I only see references to the unique id of the hosting player
can you run open Steam.# where # is the hosting player’s id?
What is the most direct route to hosting and joining a steam game and bypassing having to advertise the game on steam’s servers, since I have my own to use?
Thanks!
RAma
PlayerState->PlayerId
I’ve confirmed that steam is working and that the onlinesubsystem pointer is not null,
when I check the PlayerState->PlayerId it does not seem to be the steam user ID, it is showing up as numbers like 256 (typical of IP / non-steam player ids)
Is this expected behavior?
How do I calculate the steam unique ID or game session unique ID?
Thanks!
Rama
#My UE3 Way
Essentially I am asking for what is comparable in UE4 to this UE3 method I was using
**the core of the UE3 code revolved around this**
//display this server's net ID, must launch game from
//commandline with ?steamsockets
OnlineSub.AuthInterface.GetServerUniqueId(outID); //OnlineAuthInterface
//cast onlineSub to steamworks version to access string conversion function
theID = OnlineSubsystemSteamworks(OnlineSub).UniqueNetIdToInt64(outID);
final function joinGame(string steamUID) {
clientmessage("joining"@steamUID);
consolecommand("open"@ "steam."$steamUID);
}
Yeah, you should be able to “open steam.” from the console.
my remaining question tho is, it is clearly
open steam.#
but what is the number in UE4?
#Cant get the steam ID for the player
I can’t obtain the player’s steam ID.
As I wrote above
when I check the PlayerState->PlayerId it does not seem to be the steam user ID, it is showing up as numbers like 256 (typical of IP / non-steam player ids)
#Steam ID for unique server game instance?
and I cant find a property or function in the steam .h files that is equivalent to GetServerUniqueId
#Steam.Number
so please do let me know what the # should be in steam.# and where to get this # from
Thanks!
once I have that, I have complete Steam integration in my UE4 game!!
Rama
#The Server Game Port is Protected
I found, in OnlineSubsystemSteam.h
protected:
/** Game port - the port that clients will connect to for gameplay */
int32 GameServerGamePort;
that sounds like what I want for open steam.#
buuuut
it’s protected!
and I dont know how else to access this property
any ideas?
or is steam.#, is the # something else like the steam player id?
Rama
nvm
/**
* @return the port the game has registered for play
*/
inline int32 GetGameServerGamePort() const
{
return GameServerGamePort;
}
this thread is being continued over here
I cant actually access GetGameServerGamePort to see if that is the right # in open steam.#
https://rocket.unrealengine.com/questions/11335/what-include-or-build-target-addition-do-i-need-fo.html
Rama
You shouldn’t be casting anything to Steam, it defeats the purpose of the interface. Everything that you need should be accessible from the core API, and if not, we can work to make it that way where possible.
That being said, this is what you are looking…
/**
* Returns the platform specific connection information for joining the match.
* Call this function from the delegate of join completion
*
* @param SessionName the name of the session to resolve
* @param ConnectInfo the string containing the platform specific connection information
*
* @return true if the call was successful, false otherwise
*/
virtual bool GetResolvedConnectString(FName SessionName, FString& ConnectInfo) = 0;
It’s on the session interface and will return the proper connection string to create an FURL to pass into ServerTravel(). For Steam, it will look like steam.steamid:port.
woohoo thanks Josh!
I will try this out and see how far I get!
The only functions I am having trouble accessing, for which I thought I needed to cast to Steam
where these below, which are not part of the IOnlineSubsystemInterface and therefore inaccessible to me
/**
* @return the steam app id for this app
*/
inline uint32 GetSteamAppId() const
{
return SteamAppID;
}
/**
* @return the port the game has registered for play
*/
inline int32 GetGameServerGamePort() const
{
return GameServerGamePort;
}
/**
* @return the port the game has registered for talking to Steam
*/
inline int32 GetGameServerSteamPort() const
{
return GameServerSteamPort;
}
/**
* @return the port the game has registered for incoming server queries
*/
inline int32 GetGameServerQueryPort() const
{
return GameServerQueryPort;
}
#Player’s Steam ID ?
Another thing, while I have your attention
I cannot figure out how to retrieve the Player’s Steam ID
When I use
PlayerState->PlayerId
it is always returning the IP version that is like 255 or 256 257 etc, not the player’s steam ID, even when I am absolutely sure Steam is running and I get th e in-game pop up, and I also start a new ?listen game
Thanks!
All of those values are in the ini and you can get them yourself with
GConfig->GetInt(file,section,key)
However, I don’t know if you’ll really need them once the game is running. The 3 port values are set at startup/init time and 2 of them aren’t needed again (QueryPort/SteamPort IIRC) because they just set up how the Steam API talks to the backend. The game server port is returned to you in the function I mentioned above if you are using gameserver API.
Both the lobby and gameserver APIs use the Steam P2P API to communicate between instances of the game.
I tried what you suggested, and it is just returning “Game” as the resolved connection string, after I’ve started hosting a game using the shootergame hostgame code, that I’ve used to create real multiplayer games using open IP
I 've confirmed steam is working, the ingame steam pop up shows up
but I simply cant get the connection string
//Gameinfo
AVictoryGameGameInfo* VictoryGameInfo = Cast(GetWorld()->GetAuthGameMode());
if (!VictoryGameInfo) return;
if (JoyHUD) JoyHUD->ClientMessage("Gameinfo found!");
//Game Session
//AVictoryGameSession
AVictoryGameSession* VictoryGameSession = Cast(VictoryGameInfo->GameSession);
if (!VictoryGameSession) return;
//Set PC
VictoryGameSession->VictoryPC = this;
if (JoyHUD) JoyHUD->ClientMessage("Game session exists");
ClientMessage(VictoryGameSession->SessionName.ToString());
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
IOnlineSessionPtr Sessions = NULL;
if (OnlineSub)
{
Sessions = OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
ClientMessage("onlinesubsys was found!!!! yay!!");
}
else ClientMessage(" IOnlineSessionPtr not found, did you change config to enable steam?");
}
else ClientMessage("onlinesubsys not found, did you change config to enable steam?");
//~~~ Steam? ~~~~
//FOnlineSubsystemSteam * VictoryOnline = FOnlineSubsystemSteam(OnlineSub);
//FOnlineSubsystemSteam * VictoryOnline = InterfaceCast(OnlineSub);
//FOnlineSubsystemSteam * VictoryOnline = Cast(OnlineSub);
//if (VictoryOnline) ClientMessage("Steam Online Subsystem Found!");
//else return;
//OptInt("Steam Game Port for joining",OnlineSub->GetGameServerGamePort());
//~~~~~
ClientMessage(VictoryGameSession->SessionName.ToString());
FString ThisIsIt;
Sessions->GetResolvedConnectString(VictoryGameSession->SessionName, ThisIsIt);
ClientMessage(ThisIsIt);
the output of the above is
Game
after a new ?listen server game is started using the shootergame hostgame code, that I"ve verified works in beta5, as I"ve had packaged games working with open IP in beta 5 !
#Video of Packaged Game Open IP Real Multiplayer
I am using the same code that worked in this real multiplayer game, to host the match
http://forums.epicgames.com/threads/977279-Video-Footage-of-Multiplayer-Packaged-Game-In-UE4-Beta5
#GameServer API ?
“The game server port is returned to you in the function I mentioned above if you are using gameserver API.”
I’m not sure what you mean by Gameserver API
here’s my relevant config file portions
[/Script/Engine.GameEngine]
!NetDriverDefinitions=ClearArray
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystem.IpNetDriver")
+NetDriverDefinitions=(DefName="NoSteamNetDriver",DriverClassName="OnlineSubsystem.IpNetDriver",DriverClassNameFallback="OnlineSubsystem.IpNetDriver")
[OnlineSubsystem]
;DefaultPlatformService=Null
DefaultPlatformService=Steam
PollingIntervalInMs=20
[OnlineSubsystemSteam]
bEnabled=true
SteamDevAppId=212960
GameServerQueryPort=27015
bRelaunchInSteam=false
GameVersion=1.0.0.0
bVACEnabled=1
bAllowP2PPacketRelay=true
P2PConnectionTimeout=90
[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"
AllowDownloads=false
The GameServer API is one of the methods Steam uses to do matchmaking on Valve servers. It is the older method, more common with server browsers and dedicated servers. In contrast, the Lobby API is their newer API and is more like automated matchmaking (find me a game like X and auto join it)
The values in the config you are looking for are right there under the [OnlineSubsystemSteam] section, (SteamDevAppId and GameServerQueryPort).
Can you access any of Valve’s documentation? I’m curious to know what you can access on their site. It would be helpful to get you up to speed on what you can do.
#Actually the Connection String is NULL
Actually I just realized the connection string not returning as Game
it is returning as nothing at all
Sessions->GetResolvedConnectString(VictoryGameSession->SessionName, ThisIsIt);
ClientMessage(ThisIsIt);
yields no output, not even a newline
#my GameSession code
bool AVictoryGameSession::HostSession(int32 ControllerId, FName SessionName, const FString & GameType, bool bIsLAN, bool bIsPresence, int32 MaxNumPlayers)
{
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
if (OnlineSub)
{
CurrentSessionParams.SessionName = SessionName;
CurrentSessionParams.bIsLAN = bIsLAN;
CurrentSessionParams.bIsPresence = bIsPresence;
CurrentSessionParams.ControllerId = ControllerId;
MaxPlayers = MaxNumPlayers;
IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
if(VictoryPC)
VictoryPC->Optimize("VictoryGameSession>>> IOnlineSessionPtr Session is valid!");
HostSettings = MakeShareable(new FVictoryOnlineSessionSettings(bIsLAN, bIsPresence, MaxPlayers));
HostSettings->Set(SETTING_GAMEMODE, GameType, EOnlineDataAdvertisementType::ViaOnlineService);
Sessions->AddOnCreateSessionCompleteDelegate(OnCreateSessionCompleteDelegate);
Sessions->CreateSession(CurrentSessionParams.ControllerId, CurrentSessionParams.SessionName, *HostSettings);
return true;
}
}
#if !UE_BUILD_SHIPPING
else
{
if(VictoryPC)
VictoryPC->Optimize("VictoryGameSession>>> No Subsystem Because not Shipping Build??!");
//still hacking workflow, but calling gameinfo directly without delegate since
//setting that delegate causes crash in gameinfo
//VictoryGameInfo->OnCreatePresenceSessionComplete(GameSessionName, true);
// Hack workflow in development
OnCreatePresenceSessionComplete().Broadcast(GameSessionName, true);
// - causes crash when trying to set this delegate some reason
// crash is in the game info
return true;
}
#endif
return false;
}
I extracted this from most recent shootergame code
and actually, this neveer completes unless I comment out the first part and only use the non-shipping build code
so that would explain why the connectionstring is not valid
but I"m not sure why the normal code is not wokring
I do get the msg back that
VictoryGameSession>>> IOnlineSessionPtr Session is valid!
but the server ?listen travel restart never happens so something is amiss
What code are you calling as part of the OnCreateSessionCompleteDelegate delegate? You assigned that delegate to call some function with the signature
void MyClass::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful)
You put logging in there and never see it? What about your logs, you should see some lines that contain this substring “FOnlineAsyncTaskSteam” and see some timings and success/failure messages.
This is because you aren’t creating any sessions. Steam sockets were meant to work in conjunction with the sessions API. Given that right now you are using bits and pieces to create your own matchmaking, accessibility to the things you want isn’t as straight forward.
Creating a session via (ISession::CreateSession), waiting for its delegate to finish (ISession::OnCreateSessionComplete), and then calling GetResolvedConnectString would work on the host. Assuming Steam allows you to create sessions with our appId (you could try 480 instead of 212960).
That being said, if you have successfully created a netdriver with ?listen, you can go back to that original thread where you tried to get the net driver, ip and port, and it should be there on the host.