Download

[Networking] How to send an event to client's UMG from Server GameMode (Ex Round over Win/Lose )

Situation:
I am making a puzzle game with online vs for Android. No dedicated server so first player will be server-client.
When the game begins store a list of connected players connected storing customs PlayerControllers on post login event.
The game is vs puzzle so panels are pawn actors but blocks are not replicated. just is send information about damage. GameMode checks damage on every player. If a players HP==0 GameMode should fire an event to everyone indicating that the round is over and who is the winner.

first problem: Delegates are not replicated so i could not use Delegate events to clients.

i tried this way



GameMode.h

.h
UFUNCTION(BlueprintCallable, Category = "Gamemanager ")
            void DamagePlayer(int32 _instigator, int32 _playerDamaged, float _damage,bool bPlayAnimation=true);
          void DamagePlayer_Implementation(int32 _instigator, int32 _playerDamaged, float _damage, bool bPlayAnimation = true);



.cpp

void APawsGameModeBase::DamagePlayer(int32 _instigator, int32 _playerDamaged, float _damage, bool bPlayAnimation)
  {
     if(Role<ROLE_Authority ||  GetGameState<APawsGameState>()->currentGameState!= EGameStates::GAMEPLAY)    return;


      PlayerControllerList[_instigator]->RecoverDamage(_damage);

      if (PlayerControllerList.Num() < 2)return;

      float _rest = PlayerControllerList[_playerDamaged]->GetDamaged(_damage, bPlayAnimation);

      if (_rest <= 0)
      {
          GetGameState<APawsGameState>()->SetGameOver(_instigator);

          PlayerControllerList[_playerDamaged]->NotifyGameStateChanged(EGameStates::ROUND_OVER, _instigator);
          PlayerControllerList[_instigator]->NotifyGameStateChanged(EGameStates::ROUND_OVER, _instigator);

      }

  }




then the player controller receives the function call (instead of events). The definitions in player controller i divided in two: a function that gets the server call and then that function should call a event that should be called on clients only. that way the UMG gets a notification of who won.

GameplayPlayerController




.h
UFUNCTION(BlueprintCallable, NetMulticast, Reliable, Category = "Gameplay Player Controller | Touch Input")
    void  NotifyGameStateChanged(EGameStates  _gamestate, int32  _value);
    void  NotifyGameStateChanged_Implementation(EGameStates  _gamestate, int32  _value);


 UFUNCTION(BlueprintNativeEvent, Category = "Gameplay Player Controller | Game States")
    void OnGameStateChange(EGameStates  _gamestate, int32  _value);
    void OnGameStateChange_Implementation(EGameStates  _gamestate, int32  _value);


.cpp


void AGameplayPlayerController::NotifyGameStateChanged_Implementation(EGameStates _gamestate, int32 _value)
{

     OnGameStateChange(_gamestate, _value);

}

void AGameplayPlayerController::OnGameStateChange_Implementation(EGameStates _gamestate, int32 _value)
{
}




the i use plugins to notify UMGs


and the UMG is set up like this

Result:
one player host, other connects . when the round is over every player should be notify (YOU LOSE, YOU WIN)
only the host is receiving the notification

The GameMode only exists on the Server, therefore only the Servers version of the Player Controllers are calling this function. Also, it looks like you’re trying to copy a lot of functionality that already exists in the gameplay framework classes, so I’d suggest studying AGameMode in more detail (it already has a system for things like MatchState etc.)

You need to create a Reliable Client RPC (Not a NetMulticast) in the player controller class, and call that from the GameMode on every individual controller. Not the greatest method though IMO, but the player Controller can then talk directly to it’s local widget.

AGameMode already has a system for dispatching ‘win’ messages, I’d suggest studying it a bit more and trying to set your system up in tandem with the existing gameplay framework.

Well in discord suggest me to use matchstate comunicate the state but as i see it i have to ways of get that match state changed
1 using a rpc client call as you mentioned
2 check on every client tick if the match state is different
all this because there is no events that can be binded from clients to match state

I cant find a way to dispatch win with gamemode.are you sure that is gamemode and not gamestate?

MatchState is already a replicated variable in AGameState, so you can (and should) access it from there. There’s also an OnRep_MatchState function.

There is a ‘ClientGameEnded’ function in APlayerController. You can call this by calling ‘OnGameEnded’ on all controllers on the server when your match is over, from the GameMode. It’s not called automatically. Just do a for loop and call ‘OnGameEnded’ on all controllers, and pass in the required vars.

Since ‘Winning’ is a gameplay concept, it’s up to you to tell each player whether they won or not.

I tried this and works half way.
in OnRep_MatchState i called gamemode to replicate to controllers about gamestate change and update UI.
The problem i Had is that the client didn’t read the MatchState update at the moment of the function call so i had to place a delay of 1 second on the playercontroller before the UMG reads the value of the Gamestate and playerstate to acknowledge who is the winner.
i Guess it’s because the simulated lag server and client.
I don’t know if it is the best solution to add a delay before reading a replicated value because i guess if there is lag the delay should be higher than 1 sec but most topics on anserhub points to this “solution”.

i believed that OnRep_MatchState would send the call when MatchState is replicated for all clients but looks like it doesn’t.
I have the code like this BTW





void APawsGameState::SetGameOver_Implementation(int32 _playerWinner, int32 _loser)
{
     if(Role==ROLE_Authority)
     {
         currentGameState = EGameStates::ROUND_OVER;

     SetMatchState(FName(TEXT("ROUND_OVER")));
     APawsPlayerState *tmpPlayerState;
     for (APlayerState* p : PlayerArray)
     {
         tmpPlayerState = Cast<APawsPlayerState>(p);

         if (tmpPlayerState->playerIndex == _playerWinner)
         {
             tmpPlayerState->bRoundWinner = true;

         }

     }
     }

}

void APawsGameState::OnRep_MatchState()
{
    Super::OnRep_MatchState();


    if (MatchState == "ROUND_OVER")
    {


        if (Role == ROLE_Authority)
        {
            Cast<APawsGameModeBase>(GetWorld()->GetAuthGameMode())->ChangeGameState(EGameStates::ROUND_OVER);

        }
     }


}


 void APawsGameModeBase::ChangeGameState(EGameStates newGameState)
  {
      switch (newGameState)
      {

      case EGameStates::ROUND_OVER:
          for (AGameplayPlayerController *playerController : PlayerControllerList)
          {

              playerController->CallUIEvent(EUIEvents::GameState_Changed);

          }
          break;

      }
  }


 void AGameplayPlayerController::CallUIEvent_Implementation(EUIEvents uiEvent)
  {
      if (IsLocalPlayerController())
      {
          switch (uiEvent)
          {

          case EUIEvents::GameState_Changed:

              OnCheckStateChange();

              break;

          }


      }

  }