Listen server host can't bind to a delegate defined in PlayerState.

I’ve been trying to figure this out for a few days now with no luck. Basically I’m trying to bind to a delegate defined in the PlayerState, from a UUserWidget. I’m running a listen server setup with 1 host and 1 other player. The other player works fine but the host fails to bind to the delegate. When I add multiple other players, it works fine for them as well, just the host fails.

UUserWidget.cpp:

bool UBLPUWGameMenu::Initialize()
{
// Call parent version of function and store result in variable
const bool Success = Super::Initialize();
if (!Success) return false;

// Bind button callbacks to OnClicked delegate
if (!PropertyMenuBtn) return false;
PropertyMenuBtn->OnClicked.AddDynamic(this, &UBLPUWGameMenu::PropertyMenuBtnClicked);
if (!BuyBtn) return false;
BuyBtn->OnClicked.AddDynamic(this, &UBLPUWGameMenu::BuyBtnClicked);
if (!RollBtn) return false;
RollBtn->OnClicked.AddDynamic(this, &UBLPUWGameMenu::TakeTurnBtnClicked);
if (!FinishTurnBtn) return false;
FinishTurnBtn->OnClicked.AddDynamic(this, &UBLPUWGameMenu::FinishTurnBtnClicked);

// Bind functions to delegate
ABLPPlayerState* PlayerStatePtr = GetOwningPlayerState();
if (!PlayerStatePtr) { UE_LOG(LogTemp, Warning, TEXT(“BLPUWGameMenu: PlayerStatePtr is null”)); return false; }

PlayerStatePtr->ItsMyTurnDelegate.BindUObject(this, &UBLPUWGameMenu::ItsMyTurn);
return true;
}

void UBLPUWGameMenu::ItsMyTurn()
{
YourTurnText->SetVisibility(ESlateVisibility::Visible);
YourTurnText->SetText(FText::FromString(“It’s your turn”));
RollBtn->SetVisibility(ESlateVisibility::Visible);
}

PlayerState.h

DECLARE_DELEGATE(FItsMyTurnSignature);

FItsMyTurnSignature ItsMyTurnDelegate;

PlayerState.cpp

// Notifies UI if its this players turn, so the turn UI buttons appear (roll, finish turn, etc.)
void ABLPPlayerState::OnRep_IsItMyTurn() const
{
if (IsItMyTurn)
{
UE_LOG(LogTemp, Warning, TEXT(“%s: It’s my turn!”), *GetPlayerName());
ItsMyTurnDelegate.ExecuteIfBound();
}
else
{
UE_LOG(LogTemp, Warning, TEXT(“%s I finished my turn”), *GetPlayerName());
ItsNotMyTurnDelegate.ExecuteIfBound();
}
}

Things to note:

  • The rep notify function in PlayerState.cpp is called because the log message appears.
  • I debugged PlayerState.cpp and the delegate is not bound for the host (I also get a crash when I use Execute instead of ExecuteIfBound).
  • The button delegates in the UUserWidget bind successfully because the UI works on the host and all clients.
  • I tried making the delegate multicast, didn’t work.
  • I’m thinking this has something to with the PlayerState since the buttons work fine.

Has anyone else ever encountered a similar issue? Sorry if the formatting is weird, this is my first time posting to this.

When do you create the widget ?

Does this log appear ?

No the PlayerStatePtr is not null. I’m wondering if it’s the correct PlayerState though. I know that every client on the listen server has a local PlayerState and another one on the server. But does GetOwningPlayerState always return a local PlayerState? Since widgets only exist locally, if the PlayerState was the one from the server, then you would be binding a local function to a server delegate. Which might be the problem. I’m unsure how to even test that.

The widget is created in the PlayerController in the BeginPlayingState() function. Is this ok?

Yes this is why we need to know where/how you are creating the widget.

On client there is only one player controller so most likely you are only creating one widget for the local player, but on server there is a player controller (and associated player state) for every player. On listen server you need to make sure to only create a widget for the host player and not the other ones.

void ABLPPlayerController::BeginPlayingState()
{
Super::BeginPlayingState();

if (IsLocalPlayerController())
{
// Construct the a WBP_GameMenu
if (!GameMenuClass) return;
GameMenu = CreateWidget(this, GameMenuClass);
if (!GameMenu) return;
GameMenu->Setup();
}
}

Heres the function. I might be misunderstanding, but your saying we need to make sure the only widget created on the server is for the host. Does IsLocalPlayerController take care of that?

Yes IsLocalPlayerController should take care of that.
Everything seems to be in order then.

How many times is the message logged ?
If it’s printed only once then it’s misleading you into thinking that it works, but it’s only replicated and repnotified to the other client. When changing RepNotify variables in C++ you have to call the RepNotify function yourself if you want it to be called on server/host.
You probably want a setter like this in your playerstate :

void ABLPPlayerState::SetMyTurn(bool bMyTurn)
{
    IsItMyTurn = bMyTurn;
    OnRep_IsItMyTurn();
    ForceNetUpdate();
}
1 Like

Thank you so much…I didn’t know that about RepNotifies. The message was only logging once.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.