I have a main menu in which I can set a player name. From this menu, I can join an online game. I want my name set in the offline menu to persist to the online session. I am using this to travel to the server:
But I haven’t been able to find a way to accomplish this yet
Here’s what I tried:
Storing it in the PlayerState’s “PlayerName”: This seemed like it would work, because the PlayerName seems to be persisted in the PlayerState’s “CopyProperties” method. However, this doesn’t work. Seems like it is only persisted when you’re already in a server, but not when you go from an offline session to a server.
Storing it in the PlayerController. Since my PlayerController is the one traveling to the server, I was under the impression that it would be fully persisted. I added a replicated “playerName” variable in it and set it to some name. But it didn’t work. Once the player has joined the server, its name goes back to an empty string.
Storing my entire PlayerState in my GameInstance just before joining the server, and then getting it back from the GameInstance once we’ve joined. This didn’t work. I found out the GameInstance we get once we’ve joined is actually the server’s, and not the individual clients’.
I don’t really know what to try next. I can think of some disgusting hacky solutions, but I’d much rather understand how to do things properly. I gotta say I think it’s pretty weird that my PlayerController doesn’t persist when doing ClientTravel, because here it says it should persist by default (Travelling in Multiplayer | Unreal Engine Documentation ). Am I maybe missing some options? Or am I missing an implementation of a function to copy my custom PlayerController variables to the next?
This is easy, just add a new key=value pair to the url when joining the host. Then in your Login/Post Login methods of your hosts GameMode just set the playername that comes from the URL into the fresh PRI (there are several places you should have to look at GenericPlayerInitialization and the PostLogin method).
One thing to take into account is that the player name can not hold any character that could break the URL itself, or you sanitize it or you just encode it into to Base64.
Adding a player initialization handshake
This is the option I really prefer, because you will have more than must a playername at the end. The idea is to request the playername in PostLogin to the client. You can start calling RPC methods on the PlayerController from PostLogin so just send a client RPC asking for any initialization data the host needs. Then the client will send it back to the host with a Server RPC wich in turn puts all the data where it should be (the PlayerState so it get’s to all others for example).
As a side note, once you are already in the server you should persist the data using the “Copy” props methods you already know to persist them between travels, inactive players will use them too.
Yet another node, the PC does not persist because they are spawned in the level, they are actors and their lifetime is the same as the World they where creating in, a travel will cleanup that world and so having those lingering would result in memory leaks and unexpected behavior.
@Moss
Thanks. I’m interested in the second option, because I started work on something quite like this and I will indeed most likely need to store an entire datastructure and not just the name, but for now I don’t know **where **to store the PlayerName
When I join the server, seems like PlayerController, PlayerState, GameState, GameInstance ALL get reset. I was almost certain storing it in GameInstance would have done the job, but it actually becomes the server’s gameInstance when I get to the other side. As a result, when I got the name back from the GameInstance after joining, it always gave me the host’s name (it’s not a dedicated server)…
So on PostLogin(), when I ask the PLayerController what his name is, where will it go get that information? Is there a proper way of doing this without having to serialize that data to a xml/json file (or something of the sort) and re-read it once we’ve logged in?
Look into Seamless Travel, as outlined in the wiki link you posted. It already persists some actors as you want, namely the PlayerController. You can also customize your player controller to persist other actors as needed.
OK, the idea is to get the PlayerName from the OnlineSubsytem (for example steam), it has method to request that on the client machine, or just simply store it somewhere for the client (a savegame, a config, whatever). So in the PostLogin call you just call a client RPC on the PlayerControler, once on the client you just ping back the desired PlayerName with a Server RPC back to the host. The host will then ultimatly just set the PlayerName of the Players PlayerState so it will get replicated to the full host of connected players (you could skip the owner for that var and set it on the client to save it to replicate).
I feel stupid. I just noticed I left the “bSeamless” parameter of ClientTravel() to false… Will do more tests tonight
If it still doesn’t work, I’ll go with Moss’ idea and save the name to a config, read it on the Client OnPostLogin(), send it to the server, and make the server set it in the PlayerState
You’re right. I’m tempted to actually try both solutions regardless of which one works, just for learning purposes and to clear things up. But since I’ll probably need to save that stuff to a file at some point anyway, I might as well start doing it now
I ended up using UE4’s handy SaveGame class to store the data and restore it back after login. Works really well
I first tried enabling seamless travel for when the offline client joins a server, but it doesn’t seem to work. It was unable to join the server. I’m under the impression that seamless travel is only meant to be used when you’re ALREADY connected to a server, but not when you’re joining one. Or maybe I made a mistake somewhere?
I know this is an old topic , but i pass the same situation recently and was frustating reach the same conclusion of Phillipe_St-Amand:
When you are on a Client screen (Offline Client) and Try a Seamless transition, it will simply NOT WORK!
The answer is simple too:
There are three ways in which a NON-SEAMLESS travel MUST occur:
When loading a map for the first time
When connecting to a server for the first time as a client
When you want to end a multiplayer game, and start a new one
So just to help other people to not waste the time i spent , if you are in the situations listed , find out a way to store the data in a way that your data can be read on the other side when you already Login your User(Local Files or the PixelStreaming WebApp in the case of streamed games are really good choices).