[Help] Showing player info in lobby problem (recreating blueprint multiplayer tutorial in c++)

I’m trying to recreate the blueprint multiplayer tutorial by epic in c++ but I’m stuck in the lobby, the Chat works well, but I can’t get the player info and display it to all players, the server either duplicates the info of the host several time (the number of players) or send no info at all.

I pretty much tried everything I could think of, I couldn’t fix it no matter what i tried, I even asked on the answerhub and someone just posted an answer then marked the question as answered but the answer didn’t work at all.

97eab8f9f9551a9f84109882de37f2e0c8b44001.jpeg

here is my code, if you know a better way to do it, please inform me:

LobbyGameMode.h


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameFramework/GameMode.h"
#include "LobbyGameMode.generated.h"

/**
 * 
 */

USTRUCT(BlueprintType)
struct FPlayerInfo
{
	GENERATED_BODY()

		//Always make USTRUCT variables into UPROPERTY()
		//    any non-UPROPERTY() struct vars are not replicated

		// So to simplify your life for later debugging, always use UPROPERTY()
	UPROPERTY(BlueprintReadWrite, Category = SessionDisplay)
	FString PlayerName;

	UPROPERTY(BlueprintReadWrite, Category = SessionDisplay)
	uint32 bIsReady : 1 ;



	//Constructor
	FPlayerInfo()
	{
		//Always initialize your USTRUCT variables!
		//   exception is if you know the variable type has its own default constructor
		bIsReady = false;
		

	}
};

UCLASS()
class WAVE_API ALobbyGameMode : public AGameMode
{
	GENERATED_BODY()

	virtual void PostLogin(APlayerController * NewPlayer) override;

	virtual void Logout(AController * Exiting) override;
	
	UPROPERTY(Replicated)
	TArray<class ALobbyPlayerController*> ConnectedPlayers;

	UPROPERTY(Replicated)
	uint32 NumberofConnectedPlayers = 0;

	UPROPERTY(Replicated)
	TArray<FPlayerInfo> PlayerInfoArr;

	virtual void BeginPlay() override;

public:

	UFUNCTION(BlueprintCallable, Category = "Network|Test")
	FORCEINLINE TArray<class ALobbyPlayerController*> GetConnectedPlyers() { return ConnectedPlayers; }

	TArray<FPlayerInfo> GetPlayerInfoFromServer() const;

	/*Kicking Players*/
	UFUNCTION(Server, reliable, WithValidation)
	void ServerKickPlayer(const int32& PlayerIndex);
	void ServerKickPlayer_Implementation(const int32& PlayerIndex);
	FORCEINLINE bool ServerKickPlayer_Validate(const int32& PlayerIndex) { return true; }

	/*Chat Handling*/
	UFUNCTION(BlueprintCallable, Category = "Network|Test")
	void SendChatMessage(const FText& Message);


	/*Updating PlayerInfo*/
	UFUNCTION(Server, reliable, WithValidation)
	void EveryOneUpdate();
	void EveryOneUpdate_Implementation();
	FORCEINLINE bool EveryOneUpdate_Validate() { return true; }



	
};


LobbyGameMode.cpp


// Fill out your copyright notice in the Description page of Project Settings.

#include "Wave.h"
#include "LobbyGameMode.h"
#include "LobbyPlayerController.h"
#include "LobbyPlayerState.h"

void ALobbyGameMode::PostLogin(APlayerController * NewPlayer)
{
	//Super::PostLogin(NewPlayer);
	if (Role == ROLE_Authority)
	{
		ALobbyPlayerController* NewConnectedPlayer = Cast<ALobbyPlayerController>(NewPlayer);
	
		if (NewConnectedPlayer) 
		{
			ConnectedPlayers.Add(NewConnectedPlayer);
			NumberofConnectedPlayers++;
		}
		
		EveryOneUpdate();
	}
}

void ALobbyGameMode::Logout(AController * Exiting)
{
	Super::Logout(Exiting);

	if (Role == ROLE_Authority)
	{
		ALobbyPlayerController* LeavingPlayer = Cast<ALobbyPlayerController>(Exiting);

		if (LeavingPlayer)
		{
			ConnectedPlayers.Remove(LeavingPlayer);
		}
		EveryOneUpdate();
	}
}

void ALobbyGameMode::BeginPlay()
{
	Super::BeginPlay();
	EveryOneUpdate();
}

TArray<FPlayerInfo> ALobbyGameMode::GetPlayerInfoFromServer() const
{
	return PlayerInfoArr;
}


/*Kicking Players*/
void ALobbyGameMode::ServerKickPlayer_Implementation(const int32 & PlayerIndex)
{
	ConnectedPlayers[PlayerIndex]->GotKicked();
}


/*Chat Handling*/
void ALobbyGameMode::SendChatMessage(const FText& Message)
{
	for (ALobbyPlayerController* ConnectedPlayer : ConnectedPlayers)
	{
		ConnectedPlayer->GetChatMessage(Message);
	}
}

void ALobbyGameMode::EveryOneUpdate_Implementation()
{
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Updating"));
	
	PlayerInfoArr.Empty();
	
	for (ALobbyPlayerController* ConnectedPlayer : ConnectedPlayers)
	{
		FPlayerInfo tempinfo;

		tempinfo.bIsReady = ConnectedPlayer->bIsReady;
		tempinfo.PlayerName = ConnectedPlayer->Nickname;

		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Emerald, FString::Printf(TEXT("Player Name: %s, Ready State: %d"), *tempinfo.PlayerName, tempinfo.bIsReady));
		PlayerInfoArr.Add(tempinfo);
	}

	/*for (TActorIterator<ALobbyPlayerState> ActorItr(GetWorld()); ActorItr; ++ActorItr)
	{
		FPlayerInfo tempinfo;

		tempinfo.bIsReady = ActorItr->bIsReady;
		tempinfo.PlayerName = ActorItr->PlayerStateName;
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Emerald, FString::Printf(TEXT("Player Name: %s, Ready State: %d"), *tempinfo.PlayerName, tempinfo.bIsReady));
		PlayerInfoArr.Add(tempinfo);
	}*/

	for (ALobbyPlayerController* ConnectedPlayer : ConnectedPlayers)
	{
		ConnectedPlayer->UpdatePlayerList(PlayerInfoArr);
		//ConnectedPlayer->UpdatePlayerList();
	}
}


void ALobbyGameMode::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(ALobbyGameMode, ConnectedPlayers);
	DOREPLIFETIME(ALobbyGameMode, PlayerInfoArr);
	DOREPLIFETIME(ALobbyGameMode, NumberofConnectedPlayers);
	

}




LobbyPlayerController.h


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameFramework/PlayerController.h"
#include "LobbyGameMode.h"
#include "LobbyPlayerController.generated.h"

/**
 * 
 */
UCLASS()
class WAVE_API ALobbyPlayerController : public APlayerController
{
	GENERATED_BODY()


	virtual void BeginPlay() override;

	void SetSteamNickname();

public:
	UPROPERTY(Replicated)
	uint32 bIsReady : 1;

	UPROPERTY(Replicated)
	FString Nickname;

	UPROPERTY(Replicated)
	TArray<struct FPlayerInfo> PlayerInfoArr;
	
public:

		ALobbyPlayerController();

		UFUNCTION(Client, reliable)
		void UpdatePlayerList(const TArray<struct FPlayerInfo>& PlayerInfo);

		UFUNCTION(BlueprintImplementableEvent, Category = "Network|Test")
		void BPUpdatePlayerList(const TArray<struct FPlayerInfo>& PlayerInfo);

		
		UFUNCTION(BlueprintCallable, Category = "Network|Test")
		FORCEINLINE FString GetPlayerNickname() const { return Nickname; }

		FORCEINLINE bool GetPlayerReadyState() const { return bIsReady; }


		
		/*Chat*/
		UFUNCTION(BlueprintCallable, Server, unreliable, WithValidation, Category = "Network|Test")
		void SendChatMessageToServer(const FText& ChatMessageText);
		void SendChatMessageToServer_Implementation(const FText& ChatMessageText);
		FORCEINLINE bool SendChatMessageToServer_Validate(const FText& ChatMessageText) { return true; }


		UFUNCTION(Client, unreliable)
		void GetChatMessage (const FText& ChatMessageText);

		UFUNCTION(BlueprintImplementableEvent, Category = "Network|Test")
		void UMGUpdateChat(const FText& NewChatMessageText);



		/*Kicking Players*/
		UFUNCTION(BlueprintCallable, Category = "Network|Test")
		void KickPlayer(const int32 &PlayerIndex);

		UFUNCTION(Client, reliable)
		void GotKicked();

		UPROPERTY(Replicated)
		struct FPlayerInfo PlayerInfo;
	
};


LobbyPlayerController.cpp


// Fill out your copyright notice in the Description page of Project Settings.

#include "Wave.h"
#include "LobbyPlayerController.h"
#include "WaveGameInstance.h"
#include "WaveGameState.h"
#include "LobbyPlayerState.h"




void ALobbyPlayerController::BeginPlay()
{
	Super::BeginPlay();

	SetSteamNickname();
}

/*Setting Nickname*/
void ALobbyPlayerController::SetSteamNickname()
{
	IOnlineSubsystem* ion = IOnlineSubsystem::Get(FName("Steam"));
	if (ion)
	{
		ULocalPlayer * LP = Cast<ULocalPlayer>(this->Player);
		Nickname = ion->GetIdentityInterface()->GetPlayerNickname(LP->GetControllerId());
	}
	else
	{
		UWaveGameInstance* GI = Cast<UWaveGameInstance>(GetGameInstance());
		if (GI)
			Nickname = GI->GetPlayerNickname();
	}
	PlayerInfo.PlayerName = Nickname;
}



void ALobbyPlayerController::UpdatePlayerList_Implementation(const TArray<FPlayerInfo>& PlayerInfo)
{
	
	/*for (TActorIterator<ALobbyPlayerState> ActorItr(GetWorld()); ActorItr; ++ActorItr)
	{
		FPlayerInfo tempinfo;

		tempinfo.bIsReady = ActorItr->bIsReady;
		tempinfo.PlayerName = ActorItr->PlayerStateName;
		PlayerInfoArr.Add(tempinfo);
	}*/

	BPUpdatePlayerList(PlayerInfo);
}

ALobbyPlayerController::ALobbyPlayerController()
{
	bIsReady = false;
	PlayerInfo.bIsReady = bIsReady;
}


/*Chat Handling*/
void ALobbyPlayerController::GetChatMessage_Implementation(const FText& ChatMessageText)
{
	UMGUpdateChat(ChatMessageText);
}

void ALobbyPlayerController::SendChatMessageToServer_Implementation(const FText & ChatMessageText)
{
	ALobbyGameMode* LobbyGM = GetWorld()->GetAuthGameMode<ALobbyGameMode>();
	if (LobbyGM) {
		LobbyGM->SendChatMessage(ChatMessageText);
	}
}



/*Kicking Players*/

void ALobbyPlayerController::KickPlayer(const int32 & PlayerIndex)
{
	if (Role == ROLE_Authority) 
	{
		ALobbyGameMode* LobbyGM = GetWorld()->GetAuthGameMode<ALobbyGameMode>();
		if (LobbyGM) {
			LobbyGM->ServerKickPlayer(PlayerIndex);
		}
	}
}

void ALobbyPlayerController::GotKicked_Implementation()
{
	UWaveGameInstance* GI = Cast<UWaveGameInstance>(GetGameInstance());
	if (GI)
		GI->DestroySessionAndLeaveGame();
}

/*Replication*/

void ALobbyPlayerController::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(ALobbyPlayerController, Nickname);
	DOREPLIFETIME(ALobbyPlayerController, bIsReady);
	DOREPLIFETIME(ALobbyPlayerController, PlayerInfoArr);
	DOREPLIFETIME(ALobbyPlayerController, PlayerInfo);

	
	

}

I even tried using PlayerStates but the same problem, they return nothing

LobbyPlayerState.h


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameFramework/PlayerState.h"
#include "LobbyPlayerState.generated.h"

/**
 * 
 */
UCLASS()
class WAVE_API ALobbyPlayerState : public APlayerState
{
	GENERATED_BODY()

public:
	UPROPERTY(Replicated, BlueprintReadWrite)
	bool bIsReady;
	UPROPERTY(Replicated, BlueprintReadWrite)
	FString PlayerStateName;
	
	
};


LobbyPlayerState.cpp


// Fill out your copyright notice in the Description page of Project Settings.

#include "Wave.h"
#include "LobbyPlayerState.h"





void ALobbyPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(ALobbyPlayerState, PlayerStateName);
	DOREPLIFETIME(ALobbyPlayerState, bIsReady);


}