// Copyright Epic Games, Inc. All Rights Reserved. #include "LyraTeamCreationComponent.h" #include "Net/UnrealNetwork.h" #include "GameModes/LyraExperienceDefinition.h" #include "GameModes/LyraExperienceManagerComponent.h" #include "LyraTeamPublicInfo.h" #include "LyraTeamPrivateInfo.h" #include "GameFramework/PlayerState.h" #include "Player/LyraPlayerState.h" #include "GameFramework/PlayerController.h" #include "GameFramework/GameModeBase.h" #include "Engine/World.h" #include "GameModes/LyraGameMode.h" ULyraTeamCreationComponent::ULyraTeamCreationComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { PublicTeamInfoClass = ALyraTeamPublicInfo::StaticClass(); PrivateTeamInfoClass = ALyraTeamPrivateInfo::StaticClass(); } #if WITH_EDITOR EDataValidationResult ULyraTeamCreationComponent::IsDataValid(TArray& ValidationErrors) { EDataValidationResult Result = Super::IsDataValid(ValidationErrors); //@TODO: TEAMS: Validate that all display assets have the same properties set! return Result; } #endif void ULyraTeamCreationComponent::BeginPlay() { Super::BeginPlay(); // Listen for the experience load to complete AGameStateBase* GameState = GetGameStateChecked(); ULyraExperienceManagerComponent* ExperienceComponent = GameState->FindComponentByClass(); check(ExperienceComponent); ExperienceComponent->CallOrRegister_OnExperienceLoaded_HighPriority(FOnLyraExperienceLoaded::FDelegate::CreateUObject(this, &ThisClass::OnExperienceLoaded)); } void ULyraTeamCreationComponent::OnExperienceLoaded(const ULyraExperienceDefinition* Experience) { #if WITH_SERVER_CODE if (HasAuthority()) { ServerCreateTeams(); ServerAssignPlayersToTeams(); } #endif } #if WITH_SERVER_CODE void ULyraTeamCreationComponent::ServerCreateTeams() { for (const auto& KVP : TeamsToCreate) { const int32 TeamId = KVP.Key; ServerCreateTeam(TeamId, KVP.Value); } } void ULyraTeamCreationComponent::ServerAssignPlayersToTeams() { // Assign players that already exist to teams AGameStateBase* GameState = GetGameStateChecked(); for (APlayerState* PS : GameState->PlayerArray) { if (ALyraPlayerState* LyraPS = Cast(PS)) { ServerChooseTeamForPlayer(LyraPS); } } // Listen for new players logging in ALyraGameMode* GameMode = Cast(GameState->AuthorityGameMode); check(GameMode); GameMode->OnGameModeCombinedPostLogin().AddUObject(this, &ThisClass::OnPostLogin); } void ULyraTeamCreationComponent::ServerChooseTeamForPlayer(ALyraPlayerState* PS) { if (PS->IsOnlyASpectator()) { PS->SetGenericTeamId(FGenericTeamId::NoTeam); } else { const FGenericTeamId TeamID = IntegerToGenericTeamId(GetLeastPopulatedTeamID()); PS->SetGenericTeamId(TeamID); } } void ULyraTeamCreationComponent::OnPostLogin(AGameModeBase* GameMode, AController* NewPlayer) { check(NewPlayer); check(NewPlayer->PlayerState); if (ALyraPlayerState* LyraPS = Cast(NewPlayer->PlayerState)) { ServerChooseTeamForPlayer(LyraPS); } } void ULyraTeamCreationComponent::ServerCreateTeam(int32 TeamId, ULyraTeamDisplayAsset* DisplayAsset) { check(HasAuthority()); //@TODO: ensure the team doesn't already exist UWorld* World = GetWorld(); check(World); FActorSpawnParameters SpawnInfo; SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ALyraTeamPublicInfo* NewTeamPublicInfo = World->SpawnActor(PublicTeamInfoClass, SpawnInfo); checkf(NewTeamPublicInfo != nullptr, TEXT("Failed to create public team actor from class %s"), *GetPathNameSafe(*PublicTeamInfoClass)); NewTeamPublicInfo->SetTeamId(TeamId); NewTeamPublicInfo->SetTeamDisplayAsset(DisplayAsset); ALyraTeamPrivateInfo* NewTeamPrivateInfo = World->SpawnActor(PrivateTeamInfoClass, SpawnInfo); checkf(NewTeamPrivateInfo != nullptr, TEXT("Failed to create private team actor from class %s"), *GetPathNameSafe(*PrivateTeamInfoClass)); NewTeamPrivateInfo->SetTeamId(TeamId); } int32 ULyraTeamCreationComponent::GetLeastPopulatedTeamID() const { const int32 NumTeams = TeamsToCreate.Num(); if (NumTeams > 0) { TMap TeamMemberCounts; TeamMemberCounts.Reserve(NumTeams); for (const auto& KVP : TeamsToCreate) { const int32 TeamId = KVP.Key; TeamMemberCounts.Add(TeamId, 0); } AGameStateBase* GameState = GetGameStateChecked(); for (APlayerState* PS : GameState->PlayerArray) { if (ALyraPlayerState* LyraPS = Cast(PS)) { const int32 PlayerTeamID = LyraPS->GetTeamId(); if ((PlayerTeamID != INDEX_NONE) && !LyraPS->IsInactive()) // do not count unassigned or disconnected players { check(TeamMemberCounts.Contains(PlayerTeamID)) TeamMemberCounts[PlayerTeamID] += 1; } } } // sort by lowest team population, then by team ID int32 BestTeamId = INDEX_NONE; uint32 BestPlayerCount = TNumericLimits::Max(); for (const auto& KVP : TeamMemberCounts) { const int32 TestTeamId = KVP.Key; const uint32 TestTeamPlayerCount = KVP.Value; if (TestTeamPlayerCount < BestPlayerCount) { BestTeamId = TestTeamId; BestPlayerCount = TestTeamPlayerCount; } else if (TestTeamPlayerCount == BestPlayerCount) { if ((TestTeamId < BestTeamId) || (BestTeamId == INDEX_NONE)) { BestTeamId = TestTeamId; BestPlayerCount = TestTeamPlayerCount; } } } return BestTeamId; } return INDEX_NONE; } #endif // WITH_SERVER_CODE