Problem with HUD only being valid on clients

Some background information in case it’s relevant: My project’s current setup is a main menu level that can host/join a session, a lobby level where players in the same session can choose a character before seamless travel to the actual gameplay level. Since it’s seamless, the gameplay level’s gamemode overrides HandleStartingNewPlayer_Implementation to spawn the pawn before having the controller possess it.

The authority is handled by the session host acting as the listen server. The widget I’m trying to implement is just a text that displays the player’s health and changes whenever OnTakeAnyDamage is called by the character it’s binded to. Once everyone gets control of their characters, only the clients have that widget attached to their viewport, but the listen-server player doesn’t get anything. The clients’ health display gets updated after damage is replicated, so I know that the widget itself is functional.

Currently the HUD holds the health userwidget, and the Controller class overrides AcknowledgePossession to tell the character to GetWorld()->GetFirstPlayerController()->GetHUD(), cast, and then call a function to create the widget and attach to viewport. This is most likely where the problems originate from.

The HUD class itself also only seems to be valid on clients and not the listen-server for some reason.

Also, is there a resource for best practices for handling UI in multiplayer? My goal was originally to implement the player health and then implement a system to display teammates’ healths the same way Deep Rock Galactic does it, and the simple stuff is already stumping me.

Following LeafBranchGames’s video on multiplayer health bar, I’ve overridden the Possessed event of the test character class I’m using, which calls a Client RPC. The RPC then gets the PlayerController, creates UserWidget class of the health display, and adds it to the viewport. The health display’s Construct event then calls Get Player Character, casts it to the test character class, and then try to bind to the OnHealthChanged delegate inside that class.

I put print statements within the blueprints, and it seems the GetPlayerCharacter call inside the widget always fails when ran on the listen-server. The clients themselves have no issue.

Last update: Really hacky solution and it’ll definitely lead to issues down the line, but I put a delay before the widget is created to avoid the Construct event calling GetPlayerCharacter before Possession is finished. My only guess is that the listen server gets occupied with handling the connected clients and can’t possess its character before the widget the character creates calls Construct event. I’ll mark this as a solution for now.