Motion controllers and networking

Fixed in 4.12 so far?
This is hotting up for me as I need to get a good stable solution soon.


While this does work as a workaround, it also means you lose late-updates.

I’d like to see a proper fix :confused:

Agreed on proper fix, but you could ensure you get late updates by doing this workaround in C++ as an input module that fetches MC data. Input modules get late update tick as send controller event. I want to take a look into multiplayer support and I’ll let you know if I make a solid work around.

No, he is talking about late positional updates for rendering the controller location (anything attached to it).

If you’re using the late update, you can make sure your visuals also get update in the late tick. It works the same, it probably just doesn’t automatically propagate down to the attached children, which is a fair point.

The late update is in the rendering thread, it isn’t ticked in the game thread.

Same problem here. Seems to me that it is a major issue! I can’t believe there has been no fix for this since two months. Will post this on AnswerHub if no one already did it (?).

I never did as I assumed the attention here would be enough but it seems like that would be a good idea.

Sorry for asking this here, but I’ve tried using the “is locally controlled” approach and having some issues with this workaround.

I use “Is Locally Controlled” to update the transforms based on the hand positions

Then use the replicated transforms to update the position of the static mesh controller components:

I run both of these functions on tick in my pawn.

From the client point of view, everything looks like it works properly (controllers updating properly for both client and server)

However, from the server point of view, the client controllers are staying at 0,0,0 (the client’s transform isn’t getting updated on the server)
Could you go into a bit more detail as to how you approached this and got it to work?

What we use here is: Client hand transform is sent to Server through RPC. Then the server set these transforms which are set to “RepNotify” so all the clients receive the updates, and on the “Notify” function, we set the relative transform of the meshes used to represent the hands.
I don’t know if I’m very clear, but the pictures below may help:


Thank you so much for the images! This was VERY helpful! Significantly less laggy/better than the solution I originally ended up with.
Still some issues with throwing projectiles from the client side (attaching/detaching) due to lag from the server and not getting correct velocity, but I have some ideas there…going to experiment with physics handlers first…

I’ve heard that for networking thrown objects, detecting the throw and then sending a direction and velocity instead of trying to sync the transform itself seems to work. Haven’t tried it myself, though.

Are you guys not using Motion Controller Component in this solution?

Exactly, we don’t use the MotionController component. :slight_smile:

That’s pretty much what I do, except I don’t use RepNotify, I just have the clients lerp towards the latest transform from the server every tick, to smooth things out a bit.

Thanks for this, very helpful.
However, update frequency seems to be limited to ~30 fps. I set min net update frequency to 100 and update freq to 200 and priority to 10 but still seems choppy. Is there something I can do about this?

Make sure you increase your bandwidth limits. It is really really low by default. There are like 7 variables in the inis you have to change and each answer hub post will tell you a different subset. Here’s what I set, but it is too high for internet play (was only using it for mixed reality spectator over local network):




Hey guys, I think this is the problem:

bool UMotionControllerComponent::PollControllerState(FVector& Position, FRotator& Orientation)
	if (IsInGameThread())
		// Cache state from the game thread for use on the render thread
		const APlayerController* Actor = Cast<APlayerController>(GetOwner());
		bHasAuthority = !Actor || Actor->IsLocalPlayerController();

	if ((PlayerIndex != INDEX_NONE) && bHasAuthority)

If this code is running on a listen server, then APlayerController * Actor will not be null and you will control components on every pawn. As a workaround you can just set a -1 player index and only set it appropriately once you possess your actor.

I think the real fix should be should be changing the line:

		bHasAuthority = !Actor || Actor->IsLocalPlayerController();


		bHasAuthority = Actor && Actor->IsLocalPlayerController();


I just checked, it is fixed in 4.13; on July 20th the code was changed to:

const AActor* MyOwner = GetOwner();
const APawn* MyPawn = Cast<APawn>(MyOwner);
bHasAuthority = MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner->Role == ENetRole::ROLE_Authority);

So, it’s late and I might be mixing up stuff, but doesn’t this line mean, that as soon as the Owner of the MotionControllerComponents isn’t a “APawn”, the whole thing only
is true when the Owner is the Server?

bHasAuthority = MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner->Role == ENetRole::ROLE_Authority);


is true for Clients and ListenServer, while

(MyOwner->Role == ENetRole::ROLE_Authority);

is true for Server only, or?

I’m just trying to understand Epic’s code here. :smiley:

Problem still persists for me in ue4.13

With VR template from ue4.13 i have the same problem that the motion controller actions are mirrored.
i cant find anything about it for the current version (4.13) on the net

Im trying to find a workaround but for now i cant get a proper solution

thanks in advance