Single/Multiplayer, Touch & Vive blueprint only Template

@pixelvspixel Thanks. Believe me I tried at least an hundred packaged versions of the multiplayer template to finally surrender. Even if I want it to be very simple, the multiplayer solution will ask for a 2-lines changes in the UE4 source code (MotionControllerComponent.cpp). Without that change, clients cannot own their respective motion controllers. This ownership fix cannot be done in blueprints (for now).

Besides and with that fix, everything should work as expected. I’ll finalize it this week and write proper docs for it.

@prometeus Great to hear that, thanks! It would be interesting to hear from @marksatt-pitbull (or other Epic devel working on this part of the engine) about the issue you are having with the ownership of the motion controllers, having to rebuild the whole engine for this is kind of unfortunate.

@marksatt-pitbull @devel.bmad @muchcharles @PenguinTD OK here I call for the gods.

The problem is still ownership of the MotionController Component. Each player spawn and see the other. But the MotionController Component of the client refuses to be controlled by the client pawn.

As other as highlighted in Motion controllers and networking - VR and AR Development - Unreal Engine Forums , by default the MotionController component will receive inputs from the actor who as authority over him, which in the case of my and Epic’s template is BP_MotionController.
The corresponding and relevant C++ code in the MotionControllerComponent.cpp, is:

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

which I (and others) changed to

{
// Cache state from the game thread for use on the render thread
const AActor* MyOwner = GetOwner();
while (MyOwner && MyOwner->GetOwner() != nullptr)
{
MyOwner=MyOwner->GetOwner();
}
const APawn* MyPawn = Cast<APawn>(MyOwner);
bHasAuthority = MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner->Role == ENetRole::ROLE_Authority);
}

which, by design, should go to the owner of the 2 spawned BP_MotionControllers. Sadly, it still doesn’t work :frowning:

So I’m casting @mordentral and @getnamo (which I had the pleasure to met again at OC3) to bring some light. Even with a PhD I’m stuck at it, which is lame.

So here’s the build on github at https://github.com/ProteusVR/MultiplayerVR or onedrive at Microsoft OneDrive - Access files anywhere. Create docs with free Office Online.

How it works:

You should download UE4.14 source version from GitHub, and change / improve these lines of code (what I did). Then convert the project to your UE4 build.

You need 2 computers each with rift or vive, each with a different Steam account. connected to Steam
Package project
Player 1 is the listen-server. In the Main Menu you’re GREENPAWN. Launch Host. Now you’re in the lobby. Select a character (it’s all REDPAWN), and launch game (don’t select map 2 it lead to sadness and death).

Now Player 2 launch the game, select find a match, internet, and there it is.

There’s full of little things to arrange (like replication of animations etc) but I’ll do that when our main problem is resolved.

I checked in Epîc trello and 4.15 preview, nothing changed. Maybe I could raise a flag for this.

PS. @mordentral what you’re doing for VR multiplayer is fantastic and optimized. The reason why we want to have a no-plugin integration is because we work with many UE4 versions, and also tackle Hololens and we had white nights adapting yours, Rama’s and other plugins into different builts. :cool:

We Can make UE4 Great Again.

Whats the problem? Is it just not moving or is it picking up the servers controller movements?

Because even with ownership it still has to be sent manually from the client to the server and then from the server to all non owning remote clients. Just getting proper ownership isn’t enough as the server doesn’t know the local clients VR tracking locations.

@mordentral:

The way I set it:

2 instances of the pawn spawn, one is the listen-server and the other one client, via Steam. Player Index of MC Component is by default -1.
For each instance, inside the pawn:

  1. Spawn on server BP_MotionController, reference at Left or Right Controller as Epic template:

&stc=1

  1. Right/Left Controllers RepNotify, inside RepNotify set MotionController Component at 0:

&stc=1

3)On tick capture left/right/head transform and RepNotify:

&stc=1

4)Inside RepNotifyeverybody is getting location and rotation

&stc=1

So am I missing something?

Yeah doing it in blueprint it still replicates back down to the original owner, you would have to manually set it on client again.

In c++ you set it to not replicate to the owner to avoid this issue, other than that though it should work, though I didn’t look that closely at the ownership check.

Correct, in 4) looks like you should avoid to use the position / rotation replicated by the server when the pawn is controlled locally and use directly the local values.

Looks like I wasn’t needed. It’s a tricky one for sure for multiplayer especially if you want to grab things without suffering latency.(Actually I don’t think it should be replicated at all when you grab anything on the client side.)

Basically a simple case like this(server/client A and B), anything owned/grabbed by client A immediately lose replication status on client A(or seems to be), and then update server side object by running multicast call every frame). A cheap way to do this is by using the duplicated object method where you spawn identical thing(shape) for you to grab, and you just don’t see the replicated object(owner no see flag maybe, which lags behind anyway). Server will update things controlled by client A and then replicated to client B, client B can see since B is not owner, so B saw the replicated shape. B won’t notice anything differently, but upon release, you have to lose owner ship and then server takes over, you then see the replicated shape and hide/destroy whatever shape you are grabbing. C++ should be easier like above post mentioned. But it would be pretty hard to do anti-cheat this way if and object update is from client side. ie. a melee sword that can just fly/stab client B by sending out wrong location from client A if you didn’t put in any checks.

@PenguinTD @devel.bmad @mordentral

Thanks all for the tips. I adjusted some parameters but nada. The problem is still the MotionController Component does not receive input from the client side.
Here’s is more detail the flow:
1)Pawn #1 (listen-server via Steam) spawns BP_MotionController. Pawn #2 (client) will follow later.

&stc=1

So at this point the pawn owns BP_MotionController_0 and _1, which in return own their MotionController Component
Server set a reference variable with RepNotify for BP_MotionController exist for everybody. There, client should have a replicate of BP_MotionController and control the MotionControllerComponent inside.

Within RepNotify, Player Index of the MotionController Component is set to 0, ultimately owned by the locally controlled pawn. The, the server attach the BP_MotionController to the root (VRORigin).

&stc=1

Then at every tick, on the server and the client, the worldtransform of the controllers are captured then RepNotified from the Server to everybody.

&stc=1

On RepNotify, transforms are replicated to everybody and local pawns get local variables

&stc=1

Finally some fine-tuning stuff

&stc=1

The result the same: Pawns see each other, their face and controllers. But motioncontrollers are not associated with client (Pawn #2) hands. They are still at VROrigin location, but animation works.

I tried with/without C++ changes, the same.

Original code:
bool UMotionControllerComponent::PollControllerState(FVector& Position, FRotator& Orientation)
{
if (IsInGameThread())
{
// Cache state from the game thread for use on the render thread
const AActor* MyOwner = GetOwner();
while (MyOwner && MyOwner->GetOwner() != nullptr)
{
MyOwner = MyOwner->GetOwner();
}
const APawn* MyPawn = Cast<APawn>(MyOwner);
bHasAuthority = MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner->Role == ENetRole::ROLE_Authority);
}

if ((PlayerIndex != INDEX_NONE) && bHasAuthority)
{
	TArray&lt;IMotionController*&gt; MotionControllers = IModularFeatures::Get().GetModularFeatureImplementations&lt;IMotionController&gt;( IMotionController::GetModularFeatureName() );
	for( auto MotionController : MotionControllers )
	{
		if ((MotionController != nullptr) && MotionController-&gt;GetControllerOrientationAndPosition(PlayerIndex, Hand, Orientation, Position))
		{
			CurrentTrackingStatus = MotionController-&gt;GetControllerTrackingStatus(PlayerIndex, Hand);
			return true;
		}
	}
}
return false;

}

Modified code:
bool UMotionControllerComponent::PollControllerState(FVector& Position, FRotator& Orientation)
{
if (IsInGameThread())
{
// Cache state from the game thread for use on the render thread
const AActor* MyOwner = GetOwner();
while ( MyOwner && MyOwner->GetOwner() != nullptr)
{
MyOwner = MyOwner->GetOwner();
}
const APawn* MyPawn = Cast<APawn>(MyOwner);
bHasAuthority = MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner->Role == ENetRole::ROLE_Authority);
}

if ((PlayerIndex != INDEX_NONE) && bHasAuthority)
{
	TArray&lt;IMotionController*&gt; MotionControllers = IModularFeatures::Get().GetModularFeatureImplementations&lt;IMotionController&gt;( IMotionController::GetModularFeatureName() );
	for( auto MotionController : MotionControllers )
	{
		if ((MotionController != nullptr) && MotionController-&gt;GetControllerOrientationAndPosition(PlayerIndex, Hand, Orientation, Position))
		{
			CurrentTrackingStatus = MotionController-&gt;GetControllerTrackingStatus(PlayerIndex, Hand);
			return true;
		}
	}
}
return false;

}

I’m stuck as an old gum under a park bench on a summer day.

Hi , I can’t see the first 2 screenshots. RepNotify, isn’t the locally controlled branch redundant?
It basically applies to the controller mesh the transformation it already has. Does’t it?

And doesn’t the fine tuning part conflict with the repnotify above?
Sorry, cannot directly test the code right now, I will give it a run as soon as possible.

@devel.bmad

Success! I’ve resolved my problem of motioncontroller owning, by including the component among the main pawn. I’ll also have a closer look at my fine tuning functions.

Now everything works allright multiplayer VR. I’ve got the teleportation to redo/rebuild, do the docs and extensive tests and I should be able to release the multiplayer template soon.

Great to hear this! Looking forward to test it out :wink:

, on related news: Mobile Avatars SDK and Unreal Avatars SDK beta - Meta Community Forums - 443497

@devel.bmad Thanks I sent a PM to the Oculus guy!

Hi,

I’m getting this issue when running the template, just wondering if there is a 90 degree yaw rotation somewhere I can’t find that could be causing it? Or is it an Unreal problem.

For reference when I run the lab, forward is forward from room scale set up, but with the CV1 and launching into the template I’m rotated 90 degrees to the right.

@Zaaa You’re right it’s a problem I’ve encountered in other projects. The way I deal with it in Unreal is that I setup the “North” (monitor) side of my playground by clicking 90 degrees from the actual North I want. I do the same whatever for a Vive or an Oculus.

Good news: I’ve finally got my multiplayer VR template working. In the following days I’ll do some optimizations and I’ll release the template somewhere next week. Next iteration will be Oculus Avatar integration, to be released the day of the SDK public release mid-March.

Just assessing right now the changes with 4.15. Seems that Overrides Screenpercentage command (hmd pd xxx) doesn’t work anymore with Oculus SDK 1.10 (the one integrated within UE4.15) makes the system crashes. Will investigates further.

PS @devel.bmad Thanks for the tip working right now with Oculus Avatars in UE4 it works fine!

Great to hear this :slight_smile: Looking forward to test the next few versions of the template! Have you already started working also on Oculus voice chat? Guess it needs a bit of integration in order to have it working with 3D positional audio, but it looks like the next logical step for you :wink:

In case it may help, here is a thread I started in answerhub a few months ago on the subject: Voice Chat Spatialization - Blueprint - Epic Developer Community Forums

@devel.bmad Yes, with Touch hands, multiplayer and Oculus Avatar, VOIP is another feature I want to include

Hi there! I’ve been messing with your template for a few days now because I’m trying to implement hand animations in my game. I’m currently stuck because I can’t figure out how your blend spaces are working. For some reason I can’t preview any of your animations. If I mess with the values in the animation blueprint (like ThumbPosition) it works as expected, and everything works when in-game. But the previews on all the animations themselves as well as within the blend spaces don’t do anything, making it really hard for me to figure out how it all works. I’m also generally confused by the included FBX, because it has 8 keyframes in it even though you only seem to be using 4 frames (hand_idle, hand_frame2, hand_frame3, hand_frame4). Any help would be greatly appreciated, and thanks for making this template in the first place!

@Guidolatry

The key here is to have a look at the AnimGraph of the VR_Hand_AnimBP (now CVR_Hand_AnimBP). Here you see that each set of fingers (thumb, index and the 3 other fingers) are animated independently via "Layered blend per bone). Then look at each finger 1-d blendspace.

But you were right, something is broken with hands / Vive / Touch anims. I fixed the hands, just redownload on OneDrive or GitHub.

I’ll have the Vive and Touch anims fixed with the next version, multiplayer, which is at the optimization stage right now.

Hope that helps!