Files
UnrealEngine/Engine/Plugins/Online/OnlineFramework/Source/Lobby/Private/LobbyBeaconHost.cpp
2025-05-18 13:04:45 +08:00

355 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LobbyBeaconHost.h"
#include "GameFramework/GameModeBase.h"
#include "Engine/NetConnection.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/GameSession.h"
#include "OnlineBeaconHost.h"
#include "LobbyBeaconClient.h"
#include "LobbyBeaconState.h"
#include "LobbyBeaconPlayerState.h"
#include "OnlineSessionSettings.h"
#include "OnlineSubsystemUtils.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(LobbyBeaconHost)
DEFINE_LOG_CATEGORY(LogLobbyBeacon);
ALobbyBeaconHost::ALobbyBeaconHost(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer),
SessionName(NAME_None),
LobbyState(nullptr)
{
ClientBeaconActorClass = ALobbyBeaconClient::StaticClass();
LobbyStateClass = ALobbyBeaconState::StaticClass();
if (!HasAnyFlags(RF_ClassDefaultObject))
{
}
}
bool ALobbyBeaconHost::Init(FName InSessionName)
{
bool bSuccess = false;
SessionName = InSessionName;
if (ensure(ClientBeaconActorClass))
{
BeaconTypeName = ClientBeaconActorClass->GetName();
bSuccess = true;
}
return bSuccess;
}
void ALobbyBeaconHost::SetupLobbyState(int32 InMaxPlayers)
{
if (!LobbyState)
{
if (ensure(LobbyStateClass))
{
FActorSpawnParameters SpawnInfo;
SpawnInfo.Owner = this;
LobbyState = GetWorld()->SpawnActor<ALobbyBeaconState>(LobbyStateClass.Get(), FVector::ZeroVector, FRotator::ZeroRotator, SpawnInfo);
// Associate with this objects net driver for proper replication
LobbyState->SetNetDriverName(GetNetDriverName());
}
}
if (LobbyState)
{
LobbyState->MaxPlayers = InMaxPlayers;
}
}
void ALobbyBeaconHost::UpdatePartyLeader(const FUniqueNetIdRepl& PartyMemberId, const FUniqueNetIdRepl& NewPartyLeaderId)
{
if (LobbyState)
{
LobbyState->UpdatePartyLeader(PartyMemberId, NewPartyLeaderId);
}
}
bool ALobbyBeaconHost::DoesSessionMatch(const FString& InSessionId) const
{
UWorld* World = GetWorld();
IOnlineSessionPtr SessionInt = Online::GetSessionInterface(World);
FNamedOnlineSession* Session = SessionInt.IsValid() ? SessionInt->GetNamedSession(SessionName) : NULL;
if (Session && Session->SessionInfo.IsValid() && !InSessionId.IsEmpty() && Session->SessionInfo->GetSessionId().ToString() == InSessionId)
{
return true;
}
return false;
}
ALobbyBeaconPlayerState* ALobbyBeaconHost::HandlePlayerLogin(ALobbyBeaconClient* ClientActor, const FUniqueNetIdRepl& InUniqueId, const FString& Options)
{
if (LobbyState != nullptr)
{
UWorld* World = GetWorld();
check(World);
FString NewPlayerName = UGameplayStatics::ParseOption(Options, TEXT("Name")).Left(20);
if (NewPlayerName.IsEmpty())
{
NewPlayerName = TEXT("UnknownUser");
}
FString InAuthTicket = UGameplayStatics::ParseOption(Options, TEXT("AuthTicket"));
UE_LOG(LogLobbyBeacon, Log, TEXT("Lobby beacon received AuthTicket from client for player: UniqueId:[%s]"), *InUniqueId.ToDebugString());
if (GetNetMode() != NM_Standalone)
{
IOnlineSessionPtr SessionInt = Online::GetSessionInterface(World);
if (SessionInt.IsValid() && InUniqueId.IsValid())
{
// Register the player as part of the session
bool bWasFromInvite = UGameplayStatics::HasOption(Options, TEXT("bIsFromInvite"));
SessionInt->RegisterPlayer(NAME_GameSession, *InUniqueId, bWasFromInvite);
}
}
FText DisplayName = FText::FromString(NewPlayerName);
ALobbyBeaconPlayerState* NewLobbyPlayer = LobbyState->AddPlayer(DisplayName, InUniqueId);
return NewLobbyPlayer;
}
else
{
UE_LOG(LogLobbyBeacon, Warning, TEXT("No lobby state to handle user logging in!"));
return nullptr;
}
}
void ALobbyBeaconHost::ProcessLogin(ALobbyBeaconClient* ClientActor, const FString& InSessionId, const FUniqueNetIdRepl& InUniqueId, const FString& UrlString)
{
UE_LOG(LogLobbyBeacon, Verbose, TEXT("ProcessLogin %s SessionId %s %s %s from (%s)"),
ClientActor ? *ClientActor->GetName() : TEXT("NULL"),
*InSessionId,
*InUniqueId.ToString(),
*UrlString,
ClientActor ? (ClientActor->GetNetConnection() ? *ClientActor->GetNetConnection()->LowLevelDescribe() : TEXT("NULL")) : TEXT("NULL"));
if (ClientActor)
{
bool bSuccess = false;
if (DoesSessionMatch(InSessionId) && InUniqueId.IsValid())
{
FURL InURL(NULL, *UrlString, TRAVEL_Absolute);
if (InURL.Valid)
{
// Make the option string.
FString Options;
for (int32 i = 0; i < InURL.Op.Num(); i++)
{
Options += TEXT('?');
Options += InURL.Op[i];
}
if (PreLogin(InUniqueId, Options))
{
ALobbyBeaconPlayerState* NewPlayer = HandlePlayerLogin(ClientActor, InUniqueId, Options);
if (NewPlayer && NewPlayer->IsValid())
{
bSuccess = true;
NewPlayer->SetOwner(ClientActor);
ClientActor->PlayerState = NewPlayer;
NewPlayer->ClientActor = ClientActor;
ClientActor->SetLobbyState(LobbyState);
for (AOnlineBeaconClient* ExistingClient : ClientActors)
{
if (ExistingClient != ClientActor)
{
ALobbyBeaconClient* LBC = Cast<ALobbyBeaconClient>(ExistingClient);
LBC->ClientPlayerJoined(NewPlayer->DisplayName, NewPlayer->UniqueId);
}
}
}
}
}
if (LobbyState && !LobbyState->HasLobbyStarted())
{
int32 NumLobbyPlayers = LobbyState->GetNumPlayers();
if (NumLobbyPlayers == LobbyState->GetMaxPlayers())
{
// Session is full, load into game
LobbyState->StartLobby();
}
else if (NumLobbyPlayers == 1)
{
// First player has joined, start waiting for others
LobbyState->StartWaiting();
}
}
}
ClientActor->ClientLoginComplete(InUniqueId, bSuccess);
ClientActor->bLoggedIn = bSuccess;
if (bSuccess)
{
PostLogin(ClientActor);
}
else
{
DisconnectClient(ClientActor);
}
}
}
bool ALobbyBeaconHost::PreLogin(const FUniqueNetIdRepl& InUniqueId, const FString& Options)
{
return true;
}
void ALobbyBeaconHost::PostLogin(ALobbyBeaconClient* ClientActor)
{
// Handle beacon connection termination code (code the GameMode would normal handle)
}
void ALobbyBeaconHost::KickPlayer(ALobbyBeaconClient* ClientActor, const FText& KickReason)
{
UE_LOG(LogLobbyBeacon, Log, TEXT("KickPlayer for %s. IsValid %d UNetConnection %s UNetDriver %s State %d"), *GetNameSafe(ClientActor), IsValid(ClientActor), *GetNameSafe(ClientActor->BeaconConnection), ClientActor->BeaconConnection ? *GetNameSafe(ClientActor->BeaconConnection->Driver) : TEXT("null"), ClientActor->BeaconConnection ? ClientActor->BeaconConnection->GetConnectionState() : -1);
ClientActor->ClientWasKicked(KickReason);
DisconnectClient(ClientActor);
}
bool ALobbyBeaconHost::ProcessJoinServer(ALobbyBeaconClient* ClientActor)
{
bool bSuccess = false;
if (LobbyState != nullptr)
{
ALobbyBeaconPlayerState* Player = LobbyState->GetPlayer(ClientActor);
if (Player && Player->bInLobby)
{
Player->bInLobby = false;
ClientActor->AckJoiningServer();
bSuccess = true;
}
else
{
FString PlayerName = ClientActor ? (ClientActor->PlayerState ? *ClientActor->PlayerState->UniqueId.ToString() : TEXT("Unknown")) : TEXT("Unknown Client Actor");
UE_LOG(LogLobbyBeacon, Warning, TEXT("Player attempting to join server while not logged in %s Id: %s"), *GetName(), *PlayerName);
}
}
else
{
UE_LOG(LogLobbyBeacon, Warning, TEXT("No lobby state to handle user joining the game!"));
}
return bSuccess;
}
void ALobbyBeaconHost::ProcessDisconnect(ALobbyBeaconClient* ClientActor)
{
if (ClientActor)
{
AOnlineBeaconHost* BeaconHost = Cast<AOnlineBeaconHost>(GetOwner());
if (BeaconHost)
{
DisconnectClient(ClientActor);
}
}
}
bool ALobbyBeaconHost::ProcessKickPlayer(ALobbyBeaconClient* InInstigator, const FUniqueNetIdRepl& PlayerToKick, const FText& Reason)
{
bool bWasKicked = false;
if (InInstigator && PlayerToKick.IsValid())
{
for (AOnlineBeaconClient* ExistingClient : ClientActors)
{
if (ExistingClient != InInstigator)
{
ALobbyBeaconClient* LBC = Cast<ALobbyBeaconClient>(ExistingClient);
if (LBC && LBC->PlayerState && LBC->PlayerState->UniqueId == PlayerToKick)
{
// Right now the only eligible lobby kick is a party leader telling the server it kicked a party member
bool bPartyLeaderKick = (InInstigator->PlayerState->UniqueId == LBC->PlayerState->PartyOwnerUniqueId);
if (bPartyLeaderKick)
{
FText KickReason = NSLOCTEXT("NetworkErrors", "KickedPlayerFromParty", "Kicked from party.");
KickPlayer(LBC, KickReason);
bWasKicked = true;
}
break;
}
}
}
}
return bWasKicked;
}
void ALobbyBeaconHost::HandlePlayerLogout(const FUniqueNetIdRepl& InUniqueId)
{
if (InUniqueId.IsValid())
{
for (AOnlineBeaconClient* ExistingClient : ClientActors)
{
ALobbyBeaconClient* LBC = CastChecked<ALobbyBeaconClient>(ExistingClient);
if (LBC && LBC->PlayerState && LBC->PlayerState->UniqueId.IsValid() && LBC->PlayerState->UniqueId != InUniqueId)
{
LBC->ClientPlayerLeft(InUniqueId);
}
}
if (LobbyState)
{
LobbyState->RemovePlayer(InUniqueId);
}
}
}
void ALobbyBeaconHost::NotifyClientDisconnected(AOnlineBeaconClient* LeavingClientActor)
{
if (LobbyState)
{
ALobbyBeaconPlayerState* Player = LobbyState->GetPlayer(LeavingClientActor);
if (Player && Player->bInLobby)
{
// Handle beacon connection termination code (code the GameMode would normal handle)
UWorld* World = GetWorld();
check(World);
AGameModeBase* GameMode = World->GetAuthGameMode();
check(GameMode);
check(GameMode->GameSession);
// Notify the session (updates reservation beacon, unregisters the player, etc)
GameMode->GameSession->NotifyLogout(NAME_GameSession, Player->UniqueId);
HandlePlayerLogout(Player->UniqueId);
}
}
else
{
UE_LOG(LogLobbyBeacon, Warning, TEXT("No lobby beacon state to handle disconnection!"));
}
Super::NotifyClientDisconnected(LeavingClientActor);
}
void ALobbyBeaconHost::AdvertiseSessionJoinability(const FJoinabilitySettings& Settings)
{
}
void ALobbyBeaconHost::DumpState() const
{
UE_LOG(LogLobbyBeacon, Display, TEXT("Lobby Beacon: %s"), *GetBeaconType());
if (LobbyState)
{
LobbyState->DumpState();
}
}