Solving character moving sync in Top-Down template multiplayer implementation

So, I am not posting this as a question, but more as a c++ developer, in order to solve some things I saw done regarding pawn movement matching (replication) in Top-Down projects networking.
I would like to say that:
I started with the idea that, in a network environment, replication actually has to mean we mimic what happens in the client movement on the server, not backwards.
This is because I saw a lot of people seem to forget the very basics of what should happen: client asks, server validates and sends back.
It is always the client that calls the action, and always the server that should validate it and say it’s fine or not. If it is, the server is not supposed to manage the whole thing and send it to the client, but rather let the client do it’s thing and replicate what happens in the client server-side. Why? Because the client will never lag and we simply remove the whole time and processing between the client request and the server part this way. The server does it’s thing on it’s part, after receiving the request, which is also done with no time delays. The only possible time delay is that in which the server receives and validates the client request.
So, keeping that in mind, I really wanted to solve the whole problem with having to create a proxy character with an AI class because some people say using the default “UAIBlueprintHelperLibrary::SimpleMoveToLocation” from the APlayerController base isn’t working in networking to allow proper pawn movement visualization (location + orientation change).

I ended up with this:

  1. We make a function called RequestSetNewMoveDestination, which is called from both the client and the server.
  2. Two new functions are needed, one that is called from the client and one from the server. We need to split them to ensure they are called specifically from these places, because, as I said, the client asks to move first, and the server validates and replicates the client movement. I called these ClientSetNewMoveDestination / ServerSetNewMoveDestination.
  3. A common function is needed, to be called from each of the two above, and handle the actual movement part we do in the client and server. This is actually the old SetNewMoveDestination, as it does what the original did.
  4. We swap calling SetNewMoveDestination to calling RequestSetNewMoveDestination in MoveToMouseCursor.

Here is the result in PlayerController, as code:

.h:

/** Common request to nagivate player to the given world location. */
void RequestSetNewMoveDestination(const FVector DestLocation);

/** Call navigate player to the given world location (Client Version). */
UFUNCTION(Reliable, Client)
void ClientSetNewMoveDestination(const FVector DestLocation);

/** Call navigate player to the given world location (Server Version). */
UFUNCTION(Reliable, Server)
void ServerSetNewMoveDestination(const FVector DestLocation);

/** Navigate player to the given world location. */
void SetNewMoveDestination(const FVector DestLocation);

.cpp:

// Requests a destination set for the client and server.
void ATopDownPlayerController::RequestSetNewMoveDestination(const FVector DestLocation)
{
    ClientSetNewMoveDestination(DestLocation);
    ServerSetNewMoveDestination(DestLocation);
}

// Requests a destination set for the client (Comes first, since client calls it by clicking).
void ATopDownPlayerController::ClientSetNewMoveDestination_Implementation(const FVector DestLocation)
{
	SetNewMoveDestination(DestLocation);
}

// Requests a destination set for the server (Comes after, to replicate client movement server-side).
void ATopDownPlayerController::ServerSetNewMoveDestination_Implementation(const FVector DestLocation)
{
	SetNewMoveDestination(DestLocation);
}

// Common destination setting and movement implementation.
void ATopDownPlayerController::SetNewMoveDestination(const FVector DestLocation)
{
	if (APawn* const MyPawn = GetPawn())
	{
		float const Distance = FVector::Dist(DestLocation, MyPawn->GetActorLocation());

		// We need to issue move command only if far enough in order for walk animation to play correctly.
	if (Distance > 120.0f)
	    UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, DestLocation);
	}
}

Very important: don’t forget to set forget to set “Allow Client Side Navigation” to true in the Project Settings, else it will not work properly. The client must be allowed to do it’s movement.

Nothing else is needed, and the client and the server will now both move properly and do their part (both move and orient to movement). Please take it for a test :).

As you see, the client calls for moving to a spot, we process the request (here we can validate it if we want), we send the client moving and the server to replicate that movement.

I am happy to share this with the community in the hope it will bring to light a better, easier and stutter-free way of pawn movement sync in this project type when implementing multiplayer, without crazy additional proxy classes, AI controllers, heavy updates per tick for solving orientation in the client or whatsoever.

Awaiting thoughts, issues debates and constructive criticism.

Have a great day and happy coding!

Just wanted to thank you for posting this. It’s one of the best solutions to multiplayer top down I’ve seen. I never liked the idea of managing the proxy player. the only issue I’ve seen with this solution is that once and a while when a client player stops moving, it jerks back slightly.

There might be a way to fix that. I was just testing it and haven’t messed with it yet.

Hey This is a possible solution. But I believe the CharacterMovementCpmponent alreadyhad function to handle client prediction of movement. That means you move client position and it will automatically sync with server and further sync other clients from server

There is built in function to handle client prediction of movement and will automatically sync with server that will further sync with other clients in side player’s view of sight. Use that because it handles a lots of dirty worksfor you in tems of movement prediction and sync in clien.

The implementation posted by @Cocolino is pretty much what I ended up doing. However, the stutter at the end of a movement annoys the hell out of me. The only thing I can think of is that when the character is nearing the destination, you can issue a StopMovement command to both the client and the server and that should resolve that janky issue. I’m about to try it, but this really shouldn’t be as buggy as it currently is.

@TimB it worked. There is still a correction that gets sent to the client, but at least there’s no more jank.

In this tutorial I show two reliable setups for replicating the Topdown template for multiplayer.

[How to reliably replicate the topdown template | Tutorial | AI | Multiplayer - Replication | UE 4.26 - YouTube][1]

Both setups are lag independent and work great for reliable multiplayer.

I use both a click-to-move setup and a simple hold-mouse to move setup.

  • To see setup 1: ‘Simple Reliable Topdown Replication’ go to 2:07​
  • To see setup 2: ‘Reliable AI Pathfinding Replication’ go to 4:53

i spent three days trying to figure out what the heck is going on till i found thsi thank you, your a god!!!