x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

How can I replicate Pawns between a server and multiple clients?

Alright, I've spent the night trying to figure this one out and I just can't.

I've got a pawn, it's controlled by the default player controller. When it's either a single player, or a listen + 1 client the listen/single player moves around fine. The second client doesn't. In the case of a dedicated server neither replicate to one another.

I've also tried making my pawn inherit from DefaultPawn so that it has a movement component however using AddMovementInput ended up with the same results as using SetActorLocation or SetActorRelativeLocation.

  1. If single player, works

  2. If Listen and 1 client, Server works

  3. If dedicated server, none of the clients replicate

What am I missing? Is it as simple as telling the the server the client pawn is attempting to move and to move the pawn on the server?

Product Version: Not Selected
Tags:
more ▼

asked May 15 '14 at 05:33 AM in C++ Programming

avatar image

HailstoneRyan
709 39 25 53

avatar image AnxGotta May 15 '14 at 06:47 PM

Look at the pawn class (and other classes) of the ShooterGame example. It is a fully functional multiplayer shooter game. Compare the classes [mostly the values set in the constructor] and see what you are doing different.

It's really difficult for someone to answer a question like this without the full details of your entire setup. It could be 1 or more of many things but I would start with the ShooterGame example project.

avatar image HailstoneRyan May 16 '14 at 12:23 AM

Unfortunately the problem comparing the two is all moving/replicated Actors in that example are inheriting ACharacter. My current game is inheriting from APawn or ADefaultPawn because the Mesh doesn't have a skeleton. The problem I'm describing above even happens with the flying template if you attempt to play it multiplayer. The second ship doesn't appear to be replicated on a listen/client setup, and neither appear to be replicated on a dedicated server setup. This is all with Replicate and Replicate Movement turned on.

avatar image AnxGotta May 16 '14 at 12:48 AM

Hmm. So setting the variables inherited from AActor to true in the constructor...

 bRelicates = true;
 bReplicateMovement = true;

Isn't helping...

Is it the movement that isn't replicating or the visibility or both?

Mesh or movement or both?

avatar image HailstoneRyan May 16 '14 at 01:11 AM

Just the movement. Meshes are drawn as expected.

avatar image AnxGotta May 16 '14 at 01:20 AM

Well then, my guess would be that since you don't have the UCharacterMoveComponent that comes with the ACharacter class, none of your movements are being replicated. Look at the UCharacterMovementComponent and you can see it uses the interface INetworkPredictionInterface.

This is the only major difference I can see between APawn and ACharacter that relates to movement.

The UMovementComponent doesn't utilize this interface and maybe that's why it's not replicating. Can you try to stick a UCharacterMovementComponent on your object and move it that way? Maybe it will replicate the movement then.

I'm simply trying to work through the possibilities with you, I'm no UE4 guru =)

avatar image HailstoneRyan May 16 '14 at 01:49 AM

Giving it a go though I do notice it requires a reference to a character. I may take a look at all the network prediction related code in UCharacterMovement and see if I can't make it work in a custom movement class that doesn't depend on the character.

avatar image AnxGotta May 16 '14 at 01:53 AM

Where do you see this dependency?

avatar image HailstoneRyan May 16 '14 at 01:59 AM

https://docs.unrealengine.com/latest/INT/API/RuntimeModules/Engine/GameFramework/UCharacterMovementComponent/CharacterOwner/index.html

I tried adding it to the existing Flying template, unfortunately it does depend on this reference but you've given me a place to start possibly fixing it.

[2014.05.16-01.57.19:426][505]LogCharacterMovement:Error: MovementComponent0 owned by TestFlying2Pawn_1 must update a component owned by a Character

I should add that this only happens when a second client is introduced.

avatar image AnxGotta May 16 '14 at 01:29 PM

Well... crap.

I think you just need to implement the movement manually kind of like sixb0nes was saying.

Something like this:

 Moving Client:
 
 Get input, send request to server.
 
 Server:
 
 Handle client request, move pawn, tell all clients to move pawn
 
 All Clients:
 
 Move Pawn from method called by server.
avatar image HailstoneRyan May 16 '14 at 05:29 AM

Alright, so just to keep the loop going... I tried using the mesh as a character, ran the same test, same results. So I'm obviously not doing something right here. Taking a break, getting some rest.

avatar image HailstoneRyan May 16 '14 at 05:40 AM

Scratch that last update. I had reverted back to using SetActorLocation instead of AddMovementInput. Now that I've gone back to AddMovementInput it's working.

avatar image AnxGotta May 16 '14 at 11:26 AM

Hmm, I posted a reply 2 times but it didn't post...

I'm glad it's working. What I was trying to say was the same as the edit in my comment below to sixb0nes.

The Client connected to the Listen Server can see the replicated movement because when you are moving the pawn on the Listen Server, you are telling the server directly to move the pawn. When you move the connected Client, you are not telling the server anything about the movement. You have to implement the movement like this:

 Moving Client:
 
 Get input, send request to server.
 
 Server:
 
 Handle moving client request, move pawn on server, tell all clients to move pawn
 
 All Clients:
 
 Move Pawn from method called by server
avatar image HailstoneRyan May 16 '14 at 02:09 PM

Yep, I don't doubt that that would work.

Initially I was going to write my own NodeJS based socket server for the game I'm working on, and I'd have had to do all that with it as well. By using the UE4 server I was trying to avoid having to write things like that. More specifically by writing it for a pawn I'm concerned about losing interpolation/extrapolation that the character has.

Perhaps the examples they give in 4.2 will shed some light on how better to do this for Pawns rather than Characters.

avatar image HailstoneRyan May 16 '14 at 11:34 PM

So I've found that the character replication only works when using AddMovementInput or AddControllerYawInput and replicating the rotation code I had with AddControllerYawInput is giving me an equally painful headache.

Here's the code for doing what you've suggested above, unfortunately I'm getting the same problem. Even though the value of CurrentForwardSpeed is changing on both clients the connected client is only moving on it's own screen, not on the hosting client.

 UPROPERTY(Transient, Replicated)
 float CurrentForwardSpeed;
 
 UFUNCTION(Reliable, Server, WithValidation)
 void ServerMoveForward(float Val);
 
 ========================
 void ATestPlayerController::MoveForward(float Val)
 {
     ServerMoveForward(Val);
 }
 
 bool ATestPlayerController::ServerMoveForward_Validate(float Val)
 {
     return true;
 }
 
 void ATestPlayerController::ServerMoveForward_Implementation(float Val)
 {
     bHasForwardInput = !FMath::IsNearlyEqual(Val, 0.f);
     float CurrentAcc = bHasForwardInput ? (Val * Acceleration) : 0.0f;
 
     float NewForwardSpeed = CurrentForwardSpeed + (GetWorld()->GetDeltaSeconds() * CurrentAcc);
 
     CurrentForwardSpeed = FMath::Clamp(NewForwardSpeed, -MaxSpeed, MaxSpeed);
     CurrentForwardValue = Val;
 }
 
 void ATestPlayerController::PlayerTick(float DeltaSeconds)
 {
     const FVector LocalMove = FVector(CurrentForwardSpeed * DeltaSeconds, CurrentRightSpeed * DeltaSeconds, 0.f);
 
     GetPawn()->SetActorLocation(GetPawn()->GetActorLocation() + LocalMove, true);
 }
avatar image AnxGotta May 17 '14 at 12:13 AM

This is good to let the server know but you need to tell the clients about the new value. So for CurrentForwardSpeed, you probably want to replicated it and have it call a method.

 UPROPERTY(Transient, ReplicatedUsing=OnRep_UpdateSpeed)
 float CurrentForwardSpeed;

and then make a method

 void ATestPlayerController::OnRep_UpdateSpeed(){
 
     // do the things to move the player
 
 }

this will be replicated to all the clients causing the simulated player model to move.

So you, the moving player, tell the server, "I am moving" then the server changes the value and does the move. The changing of the value of CurrentForwardSpeed is changed and causes it to replicate to the clients and call the above method to move the player model on them as well.

Edit:

I just noticed you are using the replicated variable in the Tick function. Hmm. I will look at some more. I'm busy tonight but I'll be back tomorrow to help again.

(comments are locked)
10|2000 characters needed characters left

2 answers: sort voted first

Alright dude.

I built a test project this morning and got it working. I doubt this is exactly what you need but it does what you want. It replicates the movement of the object to all clients using the APawn class only. I have no doubt in my mind that there are better ways because this is sending a lot of data to handle movement... without a full understanding of how the engine does it with the movement components I can't say whether or not the performance with this implementation is comparable.

I tested this using multiple clients on a dedicated server.

NOTE: This does not replicate the rotation unless you are moving forward or backward.. I'll leave that to you ;)

Commence code dump...

ATestPawn.h

 #pragma once
 
 #include "GameFramework/Pawn.h"
 #include "Net/UnrealNetwork.h"
 #include "TestPawn.generated.h"
 
 /**
  * 
  */
 UCLASS()
 class ATestPawn : public APawn
 {
     GENERATED_UCLASS_BODY()
 
     virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) OVERRIDE;
 
     UFUNCTION()
     void MoveForward(float Value);
 
     UFUNCTION(Server, Reliable, WithValidation)
         void ServerMoveForward(float Value, FRotator Rotation);
 
     UPROPERTY(Transient, ReplicatedUsing=OnRep_PosChange)
         FVector CurrentPosition;
 
     UPROPERTY(Transient, ReplicatedUsing=OnRep_RotChange)
         FRotator CurrentRotation;
 
     UFUNCTION()
         void OnRep_PosChange();
 
     UFUNCTION()
         void OnRep_RotChange();
 
 };

ATestPawn.cpp

 #include "TestAActorRep.h"
 #include "TestPawn.h"
 
 
 ATestPawn::ATestPawn(const class FPostConstructInitializeProperties& PCIP)
     : Super(PCIP) {
     bUseControllerRotationPitch = true;
     bUseControllerRotationYaw = true;
     bReplicates = true;
 }
 
 void ATestPawn::OnRep_PosChange(){
     SetActorLocation(CurrentPosition);
 }
 
 void ATestPawn::OnRep_RotChange(){
     SetActorRotation(CurrentRotation);
 }
 
 
 void ATestPawn::MoveForward(float Value){    
     if (!Controller || Value == 0.0f) return;    
     if (Role != ROLE_Authority && IsLocallyControlled()){
         ServerMoveForward(Value, Controller->GetControlRotation());
     }
 }
 
 bool ATestPawn::ServerMoveForward_Validate(float Value, FRotator Rotation){
     return true;
 }
 
 void ATestPawn::ServerMoveForward_Implementation(float Value, FRotator Rotation){
     SetActorRotation(Rotation);
     CurrentRotation = Rotation;
     const FVector Direction = FRotationMatrix(CurrentRotation).GetScaledAxis(EAxis::X) * Value;
     SetActorLocation(GetActorLocation() + Direction);
     CurrentPosition = GetActorLocation();
 }
 
 
 void ATestPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent){
     check(InputComponent);
     InputComponent->BindAxis("MoveForward", this, &ATestPawn::MoveForward);
     InputComponent->BindAxis("Turn", this, &ATestPawn::AddControllerYawInput);
     InputComponent->BindAxis("LookUp", this, &ATestPawn::AddControllerPitchInput);
 }
 
 
 void ATestPawn::GetLifetimeReplicatedProps(TArray<FLifetimeProperty> &OutLifetimeProps) const{
     Super::GetLifetimeReplicatedProps(OutLifetimeProps);
     DOREPLIFETIME(ATestPawn, CurrentPosition);
     DOREPLIFETIME(ATestPawn, CurrentRotation);
 }



Key Bindings From Editor

Also note that I do nothing on the client side. This is an implementation choice that I made to simply show the replication moving the clients, including the one you are currently controlling.

Hopefully this is enough to get you going!

keybindings.png (19.4 kB)
more ▼

answered May 17 '14 at 07:01 PM

avatar image

AnxGotta
1.6k 61 45 237

avatar image pieterw72 Sep 15 '17 at 07:50 PM

You can set bReplicatesMovement to true in your pawn constructor. This automatically replicates any pawn movement happening on the server to all connected clients. To move a client pawn, and get that movement shared to all other clients/server you call an RPC on the server (like this example above). However you don't have to do the ReplicatedUsing to get the movements back to the clients; that is handled automatically by the bReplicatesMovement flag.

avatar image K6L2 Feb 27 '18 at 10:01 AM

No, it doesn't. It will work for Server->Server and Server->Client, but it wont work from Client->Server->Client. AnxGotta's answer is complete and correct.

(comments are locked)
10|2000 characters needed characters left

I responded to another thread about this yesterday - it seems like a gap that should be filled by the team - unless that was never the intention of the Pawn? I'm in the same boat FYI. For now I've just put it on the back burner and moved on.

Here is a way to get it working through Blueprints, though I don't think it is something you would want to do in production code?

There was also mention by JamesG on the forums that they would be releasing some networking pieces around the vehicle stuff in 4.2, that might help us out.

Pawn Replication - Blueprints

JamesG on vehicles

more ▼

answered May 16 '14 at 01:27 AM

avatar image

sixb0nes
16 1 2 7

avatar image AnxGotta May 16 '14 at 01:32 AM

This makes sense. You have to manually replicate your movement calls.

Client:

Get input, send request to server.

Server:

Handle client request, move pawn, tell client to move pawn

Client:

Move Pawn from method called by server.

Although, I don't think this handles any network smoothing [interpolation/extrapolation] like I believe the UCharacterMovementComponent does internally.

avatar image sixb0nes May 16 '14 at 01:39 AM

Right, but there are tick boxes to "replicate movement" when blueprinting, etc. and they seemingly have no effect? Again, maybe using the Pawn for a purpose (character) that it wasn't designed for and the assumption is that you will be handling it on your own. Seems odd.

I'm pretty green too, so I may be completely out to lunch as well :)

avatar image HailstoneRyan May 16 '14 at 01:45 AM

Thanks sixb0nes,

What I don't understand is why does the movement replication work on a pawn when one player is also acting as the server? The server/client machine has it's movement replicated onto the connected client machine no problem while the connected client machine doesn't.

I do agree that this seems like something that should be filled in by the engine.

avatar image AnxGotta May 16 '14 at 01:51 AM

When you are a Listen Server, you act as the server and a client... nothing is being replicated anywhere... technically. You are playing as the client, but the other clients talk to you rather than a dedicated server. So you are seeing your own movement locally as if you were just a normal client. When you spawn in another client that connects to the game the Listen Server is hosting that is the true replication there... the Listen Server is not replicating any movement so you don't see each other move.

avatar image HailstoneRyan May 16 '14 at 01:54 AM

I may have not been clear, or perhaps I'm not understanding what you're saying. When I setup a listen server and a second client the listen server/client movement is replicated to the second client. That's where my confusion lies. So there's clearly something going from the listen server to the second client letting it know that a pawn has moved.

avatar image AnxGotta May 16 '14 at 01:57 AM

Oh my... I completely missed that. I misunderstood your first post, I'm sorry.

So... you have a Listen Server with another Client connected to it. From the connected Client you can see the Listen Server Pawn moving around, but the Lister Server can't see the Client pawn move. This is odd indeed.

Edit:

You know what... it might be that the second client isn't sending any of it's movements to the server, kind of like sixb0nes was saying. Look at my first comment on sixb0nes' post there and that is what you need to implement. The Listen Server moves fine and replicated because it is the server... you are telling the server directly to move so the other connected Client sees the movement.

avatar image HailstoneRyan May 16 '14 at 01:59 AM

Exactly! Which is where a lot of my confusion lies :)

(comments are locked)
10|2000 characters needed characters left
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question