I’ve been working on a project for quite a while now to familiarize myself with networking in UE4, trying to avoid C++ as much as possible - just to see how viable doing projects solely with blueprints actually is. Thus, I’m keeping to a relatively simple project and it’s all been going relatively nicely apart from the multiplayer testing.
I’m currently using the Advanced Sessions Blueprints and I’ve made a lobby system which works with Steam. I can host a session and another person can join it via the server list, the players are then put in a lobby and the host can choose to start playing. So far, everything is muy bueno.
However, I’m having a ton of issues trying to find a setting within UE4 where I can reliably create things*(such as basic multiplayer logic)* that both work as intended in the editor but also in a packaged project. For example most recently I’ve been trying to add basic functionality for death and spectating other players until the match has ended*(last man standing)*.
The problem is, when it works in the editor - it does not work in a packaged project. When I manage to get it to work for the packaged project, it doesn’t work in the editor. This makes it an absolute nightmare to try and work out gameplay logic, when there are so many discrepancies.
I’ve tried all kinds of settings such as both using single processes, and using separate ones. Running as dedicated or listen server. All of which produce complete and utterly different results.
Has anyone else battled with this and found a solution that is at least somewhat reliable? Currently, to do multiplayer testing that is actually reliable I’ve had to package the project after every change - I then use a virtual machine to try it over Steam.
I have read other not-positive comments about Steam’s online subsystem and UE4 and have avoided it due to them. Your experience seems to reinforce my impression.
While I have no help to offer in this regard, I do want to thank you for sharing your experience.
The sad part is that I have found that I have to weight unsuccessful reports much more heavily than successful ones when it comes to consideration of UE4 networking and (limited) multiplayer.
I’m not sure if it’s to do with Steam subsystem, the session management seems to work fairly well, the issue is that Steam’s test app id is very restrictive and I’d need to get my game greenlit to get my own to do more thorough testing.
Mainly the issue is that trying to set up an environment within UE4 where you can reliably test game logic and achieve the same result with a packaged project. I have yet to find such an environment, the closest seem to be to use several processes on a dedicated server with all clients playing as a client. Couldn’t tell you why, but this comes with the drawback of other clients running utterly poorly.
I don’t really wanna say it’s bad because I don’t know if or how it could be done better, but I hope it’ll be improved.
It sounds like you may not fundamentally understand the server-client relationship and how replication works. I haven’t had issues like you describe once I had a better understanding of it all.
While I’ve got a lot to learn still, server-client communication is not something I’m new to. Out of curiosity which method do you mainly use to test multiplayer stuff with?
I assumed you meant… For example, in one way, client1 can pick up an object, but server can’t… And in another, it’s the opposite. Is that the sort of thing you meant?
If so, since I’m building a game meant for dedicated server use, I just launch 2+ clients and select “dedicated server”. If I were to be programming for listen, I’d just not have that option checked… I see no reason why anything wouldn’t work in a packaged product if it works fine in the editor. Do you have a very specific example?
I think a big issue as to why I find it so confusing when trying stuff in the editor is the way GetPlayerController works, like it assumes that you’re playing it in splitscreen. I’ve been having to fiddle around with indexes as of lately in order to try stuff in the editor - previously I didn’t as I was just packaging it and testing it with a virtual machine.
I’m not a fan of running on separate processes as it always seem to mess up my sessions in some way, or just outright crash if I try and create a session. Only workaround I know of is to run a dedicated server with 3 clients*(one will just be black, presumably for the dedicated server)* but this way, the 2nd client will always be running at 1 FPS, making it impossible to try stuff.
Edit:
Well, I just ran into the same issue again. It works in the editor, but not in a packaged product. So I’ll post what I’ve got here in hopes that someone might be able to shed some light as to what I’m doing wrong;
When a player dies, it triggers an event inside the GameMode. This event receives the player controller of the player that just died and removes that PC from the AlivePlayers array. I also add this PC to the SpectatorPlayers array for future usage. Once this is done, I cast the PC to my custom PC so I can access an event that I created.
I pass along the first available item inside the AlivePlayers array so I have a target to spectate.
I then use this event to get the pawn of that PC so I can attach my spectator pawn to that.
This all works in the editor if I change the Player Controller index to 1 during SomeEvent. It does not work in any way, shape or form in a packaged project.
The most likely suspect is probably latency. You’d be surprised how much stuff will appear to work on the instant-connection speed of two instances on the same machine, but cracks in the logic will show when everything is simulated with latency. I recommend getting everything working in the editor version, then also making sure it all works with the ping and packet loss simulation settings: Finding Network-based Exploits - Unreal Engine
As for ‘GetPlayerController()’ - the index is local only. On the Server and Client, index 0 will only ever return the local controller. The Listen Server spawns a controller for itself and every client, but clients only spawn one for themselves. It’s pretty rare that you ever need to use anything other than index zero, unless you’re doing split-screen - which makes it more complicated.
Dedicated Servers are different in that they don’t have a Controller for themselves, but they still have the client ones.
Thanks for the detailed reply! I’ve run into some issues where delays have been necessary, both when working locally and over network. But I’m not quite sure if that’s the issue here, but I’ll give it a go - and thanks for that link, if only I had known of it earlier
I think I understand the concept of GetPlayerController fairly well, and as I said, I’ve never used an index other than 0 since I used to package it after every change in order to test it, but that was quite a bothersome way of testing things.
I see no reason to ever use “GetPlayerController” in multiplayer. I can see why you have issues…
Why not pass in “Player Controller” from OnPlayerDeath, or grab that PC from SpectatorArray? You’re storing it, but not using it when you should be in place of “GetPlayerController”
I’ll give it a go, SpectatorArray is not yet used - it will be in the future. The reason I’m trying to use GetPlayerController like that is cause it goes CharacterThatDied -> GameMode -> CharacterThatDied, so the PC would be the same either way? I only need a target controller, so I can get a pawn that I want to try and spectate*(hence I’m using the AlivePlayers array)*.
I really only communicate with the GameMode to fetch another client’s PC.
No, “GetPlayerController” in GameMode will return nothing in a dedicated server setting. Hence, you’re trying to get “nothing” to possess the spectator pawn. Whereas if you passed in the actual PC reference, that PC will possess the pawn.
Try this: Do a simple print on “GetPlayerController” in both a dedicated and listen setting. TheJamsh explained it well.
I wrote a Spectate-On-Death thing a couple days ago conveniently enough. I’ll post the source code here once I get home tonight, it’s C++ though so you’ll have to transfer whatever over to Blueprint