All of my stuff is done in C++, but I’ll give you the list of everything I did.
- Set the motion controllers (MC) to be replicated. For Blueprint, there’s a checkbox in the default options.
- The the motion control component to have the PlayerIndex set to -1
- Add two RepNotify values of a Vector and Rotation and set them to COND_SkipOwner (not sure what the Blueprint equivalent is)
- In the notify functions, call SetWorldLocation and SetRotation on the MC component
- In the Tick, set these values to the current World Location and Rotation (Do this on both server and client)
- Also in the Tick (and this part might be done differently in Blueprint), check if the Owner is ROLE_AutonomousProxy. If so, call a server RPC that takes the location and rotation as parameters and calls SetWorldLocationAndRotation on the MC component
- In the player character, only spawn the controllers on the server (HasAuthority box)
- Make the references to the controllers RepNotify
- In the notify function, call GetPlayerPawn and check if it’s equal to this. If so, set the PlayerIndex of the MC to be 0 and the Hand to be whatever the correct hand is meant to be
I think that should be it.
Oh, right, you’ll also need to make the code change I posted here. Disclaimer on this one that I’m not entirely confident that it’s the right fix, but it works for me.
To break it down a little, we set the PlayerIndex to -1 so that they’re not tracking by default. Once we know which pawn is ours (by comparing the local pawn in the RepNotify function), we set the index back to 0 on just those controllers so that they start tracking. (In a non-local multiplayer game in Unreal, the local player is always index 0.)
We have to have the server take care of spawning the controllers so that they are properly connected in the backend. We make the controller references in the player character replicated so that the client knows which controllers belong to whom.
Finally, we sync the transforms of the MCs in two ways. From the client to the server, we use an RPC because that’s the only method available to us in Unreal. We’re checking for ROLE_AutonomousProxy because that indicates that we’re the client “owner” of that pawn (conversely, ROLE_SimulatedProxy would mean we’re a client but this pawn belongs to another player).
For the server to the clients, we just use a RepNotify variable. We set it to COND_SkipOwner because we don’t want the server to send the transform back to the owning client, stomping over his local position.
There’s a lot to absorb there, but it’s fairly straightforward. The biggest gotchas are controlling the spawning from the server and that code fix from the other thread. The rest is pretty standard replication code. That said, let me know if you have any questions. I’m certainly not an expert, but I’m understanding more and more of these systems every day.