5.1 local multiplayer not working

I’ve just noticed that the ‘Skip Assigning Gamepad to Player 1’ bug has broken our couch co-op game for multiple players. I spent about an hour debugging it, then found this thread. I guess it serves me right for not frequently testing with 2 controllers, but in my defence, we upgraded to 5.1 to avoid the near-constant crashes of 5.0 so I’m not sure what choice we had.

To confirm, despite bug UE-173306 showing a target fix of 5.2, it has not been fixed in 5.2. It has also been marked as a duplicate bug and closed, but as others have pointed out, I’m not sure where the other bug report is. I’ve searched and I can’t find it.

So right now, local multiplayer on PC (i.e. involving a keyboard) has been broken for about 7 months and Epic have incorrectly closed the one bug that reported it.

I’ll go through the motions of reporting it again, but given that Epic have sat on my last bug report where 5.1 repeatedly corrupts blueprints with no warning or reason, I won’t hold my breath.

Edit - I’ve submitted a bug report, for what it’s worth. I also took a look at the relevant source code and how the ‘Skip Assigning…’ setting actually works - it’s basically a hack that offsets the Controller ID if the input is coming from a gamepad and there is more than 1 local player in the world. The relevant function is UGameViewportClient::RemapControllerInput, and it’s used in the UGameViewportClient::InputKey function which processes input. Between 5.1 and 5.0 there were a bunch of changes in UGameViewportClient::InputKey related to deprecating ControllerIDs (which is what the setting offsets) and introducing Platform User IDs, which appear to be a more robust way to do it.

There are two topics here and here which discuss the issue, but neither of them appear to fix the fundamental issue of the keyboard and gamepad going to the same player controller.

But I’m fairly certain it’s this work - which, of course, is not documented - that has broken it.

2 Likes


This worked for my 2 player game. Shout out to Uno and everyone else in this thread!

1 Like

I think there’s some confusion over two seperate issues here. Using the new platform user nodes fixes the issue with enhanced input, but it does not solve the issue of the keyboard + first gamepad being bound to the same player.

If you want 1 keyboard and 1 gamepad to control 2 different players, it will not work, because the new ‘Platform User’ system does not respect ‘Skip Assigning Gamepad to Player 1’. With a keyboard and 1 gamepad, you will only get 1 platform user.

You’re right I should have been more clear. Also with additional testing I am finding this only works in PIE and not in an executable.

I also want to confirm that Uno’s way worked for me! Same thing that Erasio did. Adding the mapping context for both the players is crucial with this new input system. Also creating local player using id 0 and 1 instead of -1, as according to Uno -1 doesn’t work anymore.
I was stuck on this for quite long, but it was just these things.

To summarize: Follow Uno’s couch coop tutorial in the thread for 5.1 local multiplayer.

1 Like

I must be missing something else. I purchased Uno’s project and also watched his video. I also created a separate input mapping and tried the other recommendations on this thread. Doesn’t work for using keyboard for one player and controller for another in 5.1

It won’t, see my comments above. That approach only resolves the issue some people noticed with multiple gamepads not working with enhanced input. It doesn’t resolve the broken keyboard / gamepad offset.

Hello there :wave:

We are developing a local couch-coop multiplayer game and had to stay on 5.0 because of exactly this issue (1 keyboard, 1 gamepad → no controls on second pawn)! :frowning_face:

I am just here to confirm that (been testing this in every new version coming up in a fresh TPT) even the brand new 5.3 preview version hasn’t resolved this issue. :-1:

I have a feeling Epic doesn’t like local multiplayer very much :smiling_face_with_tear:
Please Epic, fix this already! :sweat_smile:

Greetings,
Markus
Bagpack Games

2 Likes

Thanks for checking, saved me downloading it to check for myself.

I’ve not received any response to my bug report from 6 weeks ago. Not that I’m surprised, I raised an even more serious bug back in March (4 months ago!) where 5.1 was randomly corrupting Blueprints, I sent a video of it happening, and I’ve heard nothing. Not sure what other route there is to making Epic fix problems if they simply ignore bug reports.

The only thing I can think of is commenting on the commit and hoping the dev who made the change (and introduced the bug) sees it. I don’t particularly like having to do that, but this is getting a bit ridiculous now.

1 Like

I also submitted another bug report as the ones previously mentioned are mixing up stuff a little which is maybe why it is not resolved yet?

Haven’t heard anything back though, keeping our fingers crossed in the meantime! :sweat_smile: :man_facepalming:

I think I’ve discovered an odd bug that produces the original post issue, not being able to receive P2 input.
Steps:
1- Follow Uno’s tutorial to create Local multiplayer project.
2- In the Editor Go to three dots in the top menu shows in the picture below.
3- Choose “New Editor Window”.
Result: UE will no longer receive P2 input.
So I suggest to stick with SelectedViewport in the meantime.

Sorry to be late but is there a fix now , or is there a bug report I can upvote or something ?

We updated to the 5.3 release yesterday and unfortunately the bug is still present there… :smiling_face_with_tear:

I haven’t heard back anything about my submitted bug report either :neutral_face:

We can just hope that with 5.4 we have better luck? :sweat_smile: :cold_face:

Best regards,
Markus
Bagpack Games

I have not heard back from my bug report either.

A programmer that was working on our team put together a fix for us (5.1) by creating a custom GameViewport class - I mentioned he could post it here but it looks like he hasn’t got around to it, so here it is. This is not my code.

BoozeGameViewportClient.h:

#pragma once

#include "CoreMinimal.h"
#include "Engine/GameViewportClient.h"
#include "BoozeGameViewportClient.generated.h"

DECLARE_MULTICAST_DELEGATE_OneParam(FOnInputKeySignature, const FInputKeyEventArgs& /*EventArgs*/);

DECLARE_DELEGATE_RetVal_FourParams(bool, FOverrideInputAxisHandler, FInputKeyEventArgs& /*EventArgs*/, float& /*Delta*/, float& /*DeltaTime*/, int32& /*NumSamples*/);


/**
 * 
 */
UCLASS()
class BOOZEGAME_API UBoozeGameViewportClient : public UGameViewportClient
{
	GENERATED_BODY()
public:
	virtual bool InputKey(const FInputKeyEventArgs& EventArgs) override;
	virtual void RemapControllerInput(FInputKeyEventArgs& InOutKeyEvent) override;

	virtual bool InputAxis(FViewport* InViewport, FInputDeviceId InputDevice, FKey Key, float Delta, float DeltaTime, int32 NumSamples = 1, bool bGamepad = false) override;

private:
	ULocalPlayer* GetLocalPlayerFromControllerId(int32 ControllerId) const;
	/** A broadcast delegate broadcasting from UGameViewportClient::InputKey */
	FOnInputKeySignature OnInputKeyEvent;

	/** Delegate for overriding input key behavior */
	FOverrideInputKeyHandler OnOverrideInputKeyEvent;

	/** A broadcast delegate broadcasting from UGameViewportClient::InputAxis */
	FOnInputAxisSignature OnInputAxisEvent;

	/** Delegate for overriding input axis behavior */
	FOverrideInputAxisHandler OnOverrideInputAxisEvent;

#if WITH_EDITOR
	/** Delegate called when game viewport client received input key */
	FOnGameViewportInputKey GameViewportInputKeyDelegate;
#endif
};

BoozeGameViewportClient.cpp:

#include "BoozeGameViewportClient.h"

#include "GameMapsSettings.h"
#include "Engine/Console.h"
#include "Framework/Application/SlateApplication.h"
#include "GameMapsSettings.h"



bool UBoozeGameViewportClient::InputKey(const FInputKeyEventArgs& InEventArgs)
{
	FInputKeyEventArgs EventArgs = InEventArgs;
	
	if (TryToggleFullscreenOnInputKey(EventArgs.Key, EventArgs.Event))
	{
		return true;
	}

	if (EventArgs.Key == EKeys::LeftMouseButton && EventArgs.Event == EInputEvent::IE_Pressed)
	{
		GEngine->SetFlashIndicatorLatencyMarker(GFrameCounter);
	}
	
	//if turn on skip setting and using gamepad, increment controllerId
	RemapControllerInput(EventArgs);
	
	if (IgnoreInput())
	{
		return ViewportConsole ? ViewportConsole->InputKey(EventArgs.InputDevice, EventArgs.Key, EventArgs.Event, EventArgs.AmountDepressed, EventArgs.IsGamepad()) : false;
	}

	OnInputKeyEvent.Broadcast(EventArgs);

#if WITH_EDITOR
	// Give debugger commands a chance to process key binding
	if (GameViewportInputKeyDelegate.IsBound())
	{
		if ( GameViewportInputKeyDelegate.Execute(EventArgs.Key, FSlateApplication::Get().GetModifierKeys(), EventArgs.Event) )
		{
			return true;
		}
	}
#endif

	// route to subsystems that care
	bool bResult = ( ViewportConsole ? ViewportConsole->InputKey(EventArgs.InputDevice, EventArgs.Key, EventArgs.Event, EventArgs.AmountDepressed, EventArgs.IsGamepad()) : false );
	// Try the override callback, this may modify event args
	if (!bResult && OnOverrideInputKeyEvent.IsBound())
	{
		bResult = OnOverrideInputKeyEvent.Execute(EventArgs);
	}

	if (!bResult)
	{
		
		ULocalPlayer* TargetLocalPlayer = GetLocalPlayerFromControllerId(EventArgs.ControllerId);
		if(TargetLocalPlayer && TargetLocalPlayer->PlayerController)
		{
			bResult = TargetLocalPlayer->PlayerController->InputKey(FInputKeyParams(EventArgs.Key, EventArgs.Event, static_cast<double>(EventArgs.AmountDepressed), EventArgs.IsGamepad(), EventArgs.InputDevice));
		}

		// A gameviewport is always considered to have responded to a mouse buttons to avoid throttling
		if (!bResult && EventArgs.Key.IsMouseButton())
		{
			bResult = true;
		}
	}


	return bResult;
}

bool UBoozeGameViewportClient::InputAxis(FViewport* InViewport, FInputDeviceId InputDevice, FKey Key, float Delta,
                                         float DeltaTime, int32 NumSamples, bool bGamepad)
{
	

	if (IgnoreInput())
	{
		return false;
	}

	// Handle mapping controller id and key if needed
	FInputKeyEventArgs EventArgs(InViewport, InputDevice, Key, IE_Axis);

	//if turn on skip setting and using gamepad, increment controllerId
	RemapControllerInput(EventArgs);

	OnInputAxisEvent.Broadcast(InViewport, EventArgs.ControllerId, EventArgs.Key, Delta, DeltaTime, NumSamples, EventArgs.IsGamepad());
	
	bool bResult = false;

	// Don't allow mouse/joystick input axes while in PIE and the console has forced the cursor to be visible.  It's
	// just distracting when moving the mouse causes mouse look while you are trying to move the cursor over a button
	// in the editor!
	if( !( InViewport->IsSlateViewport() && InViewport->IsPlayInEditorViewport() ) || ViewportConsole == nullptr || !ViewportConsole->ConsoleActive() )
	{
		// route to subsystems that care
		if (ViewportConsole != nullptr)
		{
			bResult = ViewportConsole->InputAxis(EventArgs.InputDevice, EventArgs.Key, Delta, DeltaTime, NumSamples, EventArgs.IsGamepad());
		}
		
		// Try the override callback, this may modify event args
		if (!bResult && OnOverrideInputAxisEvent.IsBound())
		{
			bResult = OnOverrideInputAxisEvent.Execute(EventArgs, Delta, DeltaTime, NumSamples);
		}

		if (!bResult)
		{
			ULocalPlayer* const TargetLocalPlayer = GetLocalPlayerFromControllerId(EventArgs.ControllerId);
			if (TargetLocalPlayer && TargetLocalPlayer->PlayerController)
			{
				bResult = TargetLocalPlayer->PlayerController->InputKey(FInputKeyParams(EventArgs.Key, (double)Delta, DeltaTime, NumSamples, EventArgs.IsGamepad(), EventArgs.InputDevice));
			}
		}

		if( InViewport->IsSlateViewport() && InViewport->IsPlayInEditorViewport() )
		{
			// Absorb all keys so game input events are not routed to the Slate editor frame
			bResult = true;
		}
	}

	return bResult;
}

void UBoozeGameViewportClient::RemapControllerInput(FInputKeyEventArgs& InOutEventArgs)
{
	const int32 NumLocalPlayers = World ? World->GetGameInstance()->GetNumLocalPlayers() : 0;

	if (NumLocalPlayers > 1 && InOutEventArgs.Key.IsGamepadKey() && GetDefault<UGameMapsSettings>()->bOffsetPlayerGamepadIds)
	{
		InOutEventArgs.ControllerId++;
	}
	else if (InOutEventArgs.Viewport->IsPlayInEditorViewport() && InOutEventArgs.Key.IsGamepadKey())
	{
		GEngine->RemapGamepadControllerIdForPIE(this, InOutEventArgs.ControllerId);
	}
}



ULocalPlayer* UBoozeGameViewportClient::GetLocalPlayerFromControllerId(int32 ControllerId) const
{
	if (UGameInstance* BoozeGameInstance = GetWorld()->GetGameInstance())
	{
		const TArray<ULocalPlayer*>& BoozeLocalPlayerArray = BoozeGameInstance->GetLocalPlayers();
		for(ULocalPlayer* const BoozeLocalPlayer : BoozeLocalPlayerArray)
		{
			if(BoozeLocalPlayer && BoozeLocalPlayer->GetControllerId() == ControllerId)
			{
				return BoozeLocalPlayer;
			}
		}
	}
	return nullptr;
}
2 Likes

This is crazy. Except for upvoting the bug report, how could we get this to be fixed by Epic?

Unfortunately there isn’t even a bug report to vote on yet, the only bug report related to this issue (here) got closed by someone who apparantly misunderstood the issue and closed it as a duplicate without indicating what they thought it was a duplicate of.

The bug reporting process involves submitting a form and hoping for a reply, but I still haven’t received a response to a bug report from March, and at least two of us (me and @Bagpack_Games) have submitted reports for this issue months ago and not heard back.

Given what’s going on at Epic, I’d say the chances of this getting fixed anytime soon is a long shot. Unreal Engine is just outright unsuitable for local multiplayer games now.

1 Like

Thanks Dave,

Would it be possible to get the link to both bug reports (from @DaveFace and @Bagpack_Games) so that anyone reading this can upvote? I can’t find them.

I tried to implement the code you provided (thanks) but failed for now.

Thanks!

1 Like

Hello @Nimillion,
apparently there is no way to find out if handed in bug reports are actually moved to an actual ticket in Epics system and that, to me at least, is the most annoying part :smiling_face_with_tear:

I haven’t tried to implement the solution provided by @DaveFace either, but maybe will soon.
I will report in this thread if I managed to do so successfully or anything else changes regarding this annoying bug! :neutral_face:

Best regards,
Markus
Bagpack Games

1 Like

Awesome, looking forward to it. I will try again as well when I absolutely need this done… somehow hoping it will get fixed by then…

1 Like

Unfortunately, like Markus said you submit the form and it disappears into the void - all you get is a confirmation that the report was received, but no ID or ticket to track it. Epic have to review and accept it before it appears on the public tracker. Neither of my bug reports have made it that far yet.

What issue are you having with the code? I didn’t write it (and my C++ is a bit rusty, to say the least) but I can try to help.

2 Likes