How does Network Relevancy work? (MOBA game with pathfinding on authoritative server)

Hello guys, I’m having problems with Network Relevancy in my MOBA game which uses a special Pawn/PlayerController + Character/AIController setup:
the Character becomes network irrelevant on the client, allthough all involved actors (Pawns & Controllers) are synced to the current Character location (on Server & Client).

  1. How does Network Relevancy exactly work? (Is there any more logic than “Object becomes irrelevant outside of NetCullDistance”?)
  2. Based on which object/location is the Network Relevancy of other objects evaluated? (Distance to which object used for the “> NetCullDistance check”)
  3. Where can I find the Network Relevancy logic in the source code?

Some background info: in order to get a MOBA control scheme (3rd person top-down view, move to mouse click location using pathfinding) to work within the UE4 Client/Authoritative Server architecture, I created a special controller setup with ClassBlueprints:

a custom “ProxyPlayerController” (extending PlayerController) and a custom “ProxyPawn” (extending Pawn) are set as default PlayerController and Pawn classes in the GameMode.
the ProxyPlayerController calculates the target position on a mouse click (using “GetHitResultUnderCursorByChannel()”) and sends an RPC “MoveToLocation” to the Server.

the ProxyPawn spawns a “MyAIController” (extending AIController) and a “MyCharacter” (extending Character) on “BeginPlay” on the Server and possesses the MyCharacter instance by the MyAIController instance. Also the ProxyPawn saves the MyAIController and MyCharacter instance references in corresponding public variables.

all “MoveToLocation” RPCs called in ProxyPlayerController on the Server are passed to the MyAIController (via ProxyPlayerController.GetControlledPawn() -> cast to ProxyPawn -> GetMyAIController()). MyAIController finally calls the UE4 function AIController.MoveToLocation(), in order to move the controlled MyCharacter to the target location using pathfinding on the Authoritative Server. MyCharacter’s movement is network replicated to all clients (even the “own” client).

the ProxyPawn has additional logic which updates its location to that of the referenced/replicated MyCharacter instance (on Server and “own” Client) via LERP. ProxyPawn also has the actual CameraComponent attached, which allows to “observe” the replicated MyCharacter.

Everything works fine until I decrease the “NetCullDistanceSquared” of MyCharacter to a value half of the map size: now the MyCharacter “observed” by the ProxyPawn becomes network irrelevant on the client (and destroyed), as soon as he’s too far away from location 0,0 (UActorChannel::CleanUp() is called) and then the Camera pops back to 0,0. I added log output to all involved actors (ProxyPawn, ProxyPlayerController, MyCharacter, MyAIController) and all instance locations are identical on the Server and Client and MyCharacter therefor should be within the “NetworkCullDistance”!

I already tried out all the available checkboxes without luck:

PlayerController.PlayerCameraManagerClass (none or PlayerCameraManager), AutoManageActiveCameraTarget

AI/PlayerController.AttachToPawn

Pawn.AutoPossess

I don’t want to increase the “NetworkCullDistance” or enable “AlwaysRelevant” because I need to reduce network traffic in the future.

Thank you very much for any help and sorry for the wall of text. If required I can upload the corresponding blueprints and will gladly give more info!

AnswerHub link: https://answers.unrealengine.com/questions/60485/how-does-network-relevancy-work-moba-game-with-pat.html

bump this one, it’s an interesting question but I have yet dig enough to try solve this because of steam summer sale.(too many new games to buy/play, lol)
I hope more experienced guy can help solve this and we can have a wiki or something put up.

My speculation is that, this current proxy method of controller was not intended behavior, but more or less because of SimpleMoveToLocation does not work properly in a networked situation.
So the controller on the client side does not actually own the pawn on server side, if one instance became irrelevant, it might actually be getting rid of.
My question would be, what would happen if you don’t have both ProxyPawn and a MyCharacter? Instead, just a MyCharacter that are properly replicated, and let a MyAIController to control it from the server side and send the reference back to client controller?

This is not a answer by any means, but some thought I get from another answerhub question that Epic staff MieszkoZ answer the SimpleMoveToLocation problem.

Hi PenguinTD, sorry I didn’t answer earlier, I was on vacation (far away from the steam summer sale :P). Actually I know about the answer hub question you linked to, that’s why I use the same Controller setup mentioned there :).

I debugged this with PrintString() and neither ProxyPlayerController, ProxyPawn, MyAIController nor MyCharacter have an owner set, on the client or the server.
Should they have? If yes how do I configure owners from blueprints (no function SetOwner() exists)?

I implemented your idea:

  • removed the ProxyPawn
  • set MyCharacter as “Default Pawn Class” in GameMode
  • on the server the MyCharacter instance, which is default possessed by a ProxyPlayerController, is repossessed with an newly spawned MyAIController
  • I don’t know what “send the reference back to client controller” is useful for. On the client the character must be possessed by a PlayerController.

This improved the network relevancy issue a little bit: the controlled MyCharacter instance now doesn’t disappear anymore on the own client, if moving too far way from the map origin (0,0).
However any other MyCharacter instance B from another player still disappears on client A, if MyCharacter A is too far away from the map origin (and not too far away from character B). So the problem remains: some instance (ViewPoint? Owner object?) which determines the network relevancy of other actors for a pawn/controller, isn’t correctly updated and stays at the map origin position (0,0). Any idea?

I captured and uploaded a video of this remaining issue: https://www.youtube.com/watch?v=2O3qCHXPOPQ
I can also provide the migrated blueprint binaries etc. if someone wants to test it out…

Thank you!

interesting observation, I appreciate that you invested time into this, it would greatly help others that follow this path. :slight_smile:

So back to topic, here is my understand while I traverse c++ code in order to do a proper multiplayer respawn/reposses.
First question is simpler, controller have no ownership they are game object that possess other things(mostly pawn), so that’s normal.
But Pawn should really have something to control them, it may not be actual “owner”, but you can try use getcontroller to see if a pawn is currently possessed.
I think ownership is pretty vague thing to say, but for server/client, server owns pretty much everything, and client only owns the local player controller.

On server, there is always a mirrored player controller that client’s player controller sents info to. Imagine that player controller is actually the “wire” from your client to server.
However, they are not the same instance and does not have same name, this is the same for all object, except some base object like GameMode or level objects which only exists on server.
(in source code, all client side level objects are wiped during loading, so everything that can move is replicated from server)

Here is my speculation(again, sorry I can’t give a more solid answer due to my limited knowledge):
When a pawn is not controlled by a server side controller, and when it moved out side of relevancy, how do you check what info sent to client?
You basically see what’s the mirrored controller on server and check what it is currently controlling, if it’s a pawn, then send relevant info with in radius.
In our case, your mirrored controller no longer control anything, server MyAIController does, and I don’t know if multiple controller possession is possible or not.
So when your controller possess nothing(like if you kill the pawn in client), they are default to possess a default pawn(basically a empty locator with default camera).
I guess that’s why it always seems to disappear for the client in a weird way. (as your server player controller are left in place where your pawn first initialized. )

So my suggestion would be, you might guessed it already, to try move your server side player controller with your currently controlled pawn.(what ever the current one your MyAIController possessed)
It sounds simple with just one line, but it would require some careful thought of how you preserve reference to your currently possessed pawn, and still have server side player controller properly update it’s position.

Good Luck! :slight_smile:

And hopefully Epic guys can spot this thread and come in to offer more in depth views about this apect in framework and how can we approach this.
(Instead of waiting it to happen one day.)