// Fill out your copyright notice in the Description page of Project Settings. #include "DTFluxPursuitManager.h" #include #include "DTFluxCoreSubsystem.h" #include "DTFluxCoreSubsystemModule.h" UDTFluxPursuitManager::UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer) { } void UDTFluxPursuitManager::InitPursuit(const TArray InContestIds, const int MaxSimultaneousPursuit) { CoreSubsystem = Cast(GetOuter()); if (!CoreSubsystem) { UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("CoreSubsystem is not Available !!!")); return; } AllRankings.Reset(); for (const auto& ContestId : InContestIds) { FDTFluxContest Contest; if (CoreSubsystem->GetContestForId(ContestId, Contest)) { BindRankings(); FDTFluxStageKey StageKey = FDTFluxStageKey(ContestId, Contest.GetLastStageId()); FDTFluxStageRankings TempStageRankings; //Obtenir les ranking Frais. CoreSubsystem->GetStageRankingsWithKey(StageKey, TempStageRankings, false); PendingStageRanking.Add(StageKey, false); } } } void UDTFluxPursuitManager::SetPursuitInfoIsMassStart(FDTFluxPursuitGroup NextFocusGroup) { for (auto& Pursuit : NextFocusGroup.PursuitGroup) { Pursuit.bIsMassStart = Pursuit.StartTime >= MassStartTime; } } void UDTFluxPursuitManager::DebugFocusNext(const TArray& OutPursuitFocusNext) { FString FocusBibs; for (const auto& Pursuit : OutPursuitFocusNext) { FocusBibs += FString::Printf(TEXT("%d "), Pursuit.Bib); } UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus Bibs: %s"), *FocusBibs); } void UDTFluxPursuitManager::DebugOutPoursuitNext(const TArray& OutPursuitNext) { FString NextBibs; for (int32 i = 0; i < OutPursuitNext.Num(); i++) { NextBibs += FString::Printf(TEXT("%d "), OutPursuitNext[i].Bib); } UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next Bibs: %s"), *NextBibs); } void UDTFluxPursuitManager::GetPursuit(TArray& OutPursuitFocusNext, TArray& OutPursuitNext, bool& BIsFocusTruncate, const int MaxSimultaneousPursuit) { UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== GetPursuit CALLED ===")); UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("MaxSimultaneousPursuit: %d"), MaxSimultaneousPursuit); UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Available groups: %d"), GroupedPursuit.Num()); // Validation if (MaxSimultaneousPursuit <= 0) { UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Invalid MaxSimultaneousPursuit: %d"), MaxSimultaneousPursuit); OutPursuitFocusNext.Reset(); OutPursuitNext.Reset(); BIsFocusTruncate = false; return; } if (bIsSequenceDone || GroupedPursuit.IsEmpty()) { UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("No groups available or sequence completed")); OutPursuitFocusNext.Reset(); OutPursuitNext.Reset(); BIsFocusTruncate = false; return; } OutPursuitFocusNext.Reset(); OutPursuitNext.Reset(); // === ÉTAPE 1: FOCUS = PREMIER GROUPE (et le supprimer) === FDTFluxPursuitGroup FocusGroup = GroupedPursuit[0]; // Copie du premier groupe GroupedPursuit.RemoveAt(0); // ✅ SUPPRIMER le premier groupe SetPursuitInfoIsMassStart(FocusGroup); OutPursuitFocusNext = FocusGroup.PursuitGroup; BIsFocusTruncate = FocusGroup.PursuitGroup.Num() > 1; UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus Group: StartTime=%s, Participants=%d"), *FocusGroup.StartTimeGlobal.ToString(), FocusGroup.PursuitGroup.Num()); // === ÉTAPE 2: NEXT = GROUPES SUIVANTS (SANS les supprimer) === int32 TargetNextCount = MaxSimultaneousPursuit - 1; // -1 pour le focus int32 AddedNextCount = 0; UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Target Next Count: %d"), TargetNextCount); // ✅ PARCOURIR les groupes restants SANS les modifier for (int32 GroupIndex = 0; GroupIndex < GroupedPursuit.Num() && AddedNextCount < TargetNextCount; GroupIndex++) { FDTFluxPursuitGroup& NextGroup = GroupedPursuit[GroupIndex]; // Référence (pour SetPursuitInfoIsMassStart) if (NextGroup.PursuitGroup.Num() == 0) { continue; // Groupe vide } int32 AvailableInGroup = NextGroup.PursuitGroup.Num(); int32 NeededFromGroup = FMath::Min(TargetNextCount - AddedNextCount, AvailableInGroup); UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Processing Next Group %d: StartTime=%s, Available=%d, Taking=%d"), GroupIndex, *NextGroup.StartTimeGlobal.ToString(), AvailableInGroup, NeededFromGroup); // ✅ COPIER les participants nécessaires (SANS les supprimer du groupe) for (int32 ParticipantIndex = 0; ParticipantIndex < NeededFromGroup; ParticipantIndex++) { FDTFluxPursuitInfo NextParticipant = NextGroup.PursuitGroup[ParticipantIndex]; // Copie // Appliquer MassStart NextParticipant.bIsMassStart = NextParticipant.StartTime >= MassStartTime; OutPursuitNext.Add(NextParticipant); AddedNextCount++; UE_LOG(logDTFluxCoreSubsystem, VeryVerbose, TEXT("Added to Next: Bib %d from Group %d"), NextParticipant.Bib, GroupIndex); } } // === LOGS DE RÉSUMÉ === UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== PURSUIT RESULTS ===")); UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus: %d participants"), OutPursuitFocusNext.Num()); UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next: %d participants"), OutPursuitNext.Num()); UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Remaining groups for future: %d"), GroupedPursuit.Num()); if (OutPursuitFocusNext.Num() > 0) { DebugFocusNext(OutPursuitFocusNext); } // Log détaillé des Next (limité pour éviter spam) if (OutPursuitNext.Num() > 0) { DebugOutPoursuitNext(OutPursuitNext); } // Vérifier si la séquence est terminée if (GroupedPursuit.IsEmpty()) { bIsSequenceDone = true; UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Pursuit sequence will be completed after this round")); } } bool UDTFluxPursuitManager::InitSubSystems() { if (NetworkSubsystem) { return true; } NetworkSubsystem = GEngine->GetEngineSubsystem(); return NetworkSubsystem != nullptr; } bool UDTFluxPursuitManager::BindRankings() { if (CoreSubsystem) { if (!bIsRankingBounded) { CoreSubsystem->OnStageRankings.AddDynamic(this, &UDTFluxPursuitManager::OnRankingsReceived); bIsRankingBounded = true; } return bIsRankingBounded; } UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("CoreSubsystem is not Available !!!")); return bIsRankingBounded = false; } void UDTFluxPursuitManager::UnbindRankings() { if (CoreSubsystem) { if (bIsRankingBounded) { CoreSubsystem->OnStageRankings.RemoveDynamic(this, &UDTFluxPursuitManager::OnRankingsReceived); bIsRankingBounded = false; return; } } bIsRankingBounded = false; UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("CoreSubsystem is not Available !!!")); } void UDTFluxPursuitManager::OnRankingsReceived(const FDTFluxStageKey NewStageKey, const FDTFluxStageRankings NewStageRankings) { if (PendingStageRanking.Contains(NewStageKey)) { PendingStageRanking.Remove(NewStageKey); AllRankings.Add(NewStageRankings); if (PendingStageRanking.IsEmpty()) { //everything is ready to go compute and start UnbindRankings(); LaunchPursuitSequence(); } } } bool UDTFluxPursuitManager::LaunchPursuitSequence() { GroupedPursuit.Empty(); TArray AllPursuits; TMap TempGroups; bIsSequenceDone = false; // Full the Array Of Rankings for (auto& Ranking : AllRankings) { for (auto StageRanking : Ranking.Rankings) { int ContestId = Ranking.ContestId; FDTFluxPursuitInfo PursuitInfo; PursuitInfo.StartTime = StageRanking.StartTime; PursuitInfo.Bib = StageRanking.Bib; PursuitInfo.ContestId = ContestId; AllPursuits.Add(PursuitInfo); } } UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("AllPursuits.Num() = %i"), AllPursuits.Num()); for (auto& Pursuit : AllPursuits) { if (TempGroups.Contains(Pursuit.StartTime)) { FDTFluxPursuitGroup& Group = TempGroups[Pursuit.StartTime]; Group.PursuitGroup.Add(Pursuit); UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Adding [%i] To PursuitGroup starting At %s, PursuitGroup.Num() %i"), Pursuit.Bib, *Pursuit.StartTime.ToString(), Group.PursuitGroup.Num()); } else { FDTFluxPursuitGroup NewGroup; UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("New Group starting At %s, Adding Bib [%i]"), *Pursuit.StartTime.ToString(), Pursuit.Bib); NewGroup.StartTimeGlobal = Pursuit.StartTime; NewGroup.PursuitGroup.Add(Pursuit); TempGroups.Add(Pursuit.StartTime, NewGroup); for (const auto& Group : TempGroups) { UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Group.StartTime = %s, Group.PursuitGroup.Num() = %i"), *Group.Key.ToString(), Group.Value.PursuitGroup.Num()); } } } TempGroups.KeySort([](const FDateTime& A, const FDateTime& B) { return A < B; }); TMap StartTimeFrequency; int32 MaxFrequency = 0; GroupedPursuit.Reserve(TempGroups.Num()); // parcours du TMap for (const auto& Pair : TempGroups) { if (Pair.Value.StartTimeGlobal != FDateTime::MinValue() && Pair.Value.StartTimeGlobal != FDateTime::MaxValue()) { // récuperation de la ref de la valeur actuel de la fréquence dans la TMap Freq int& CurrentFreq = StartTimeFrequency.FindOrAdd(Pair.Value.StartTimeGlobal, 0); CurrentFreq = Pair.Value.PursuitGroup.Num(); if (CurrentFreq > MaxFrequency) { MaxFrequency = CurrentFreq; MassStartTime = Pair.Value.StartTimeGlobal; } } GroupedPursuit.Add(Pair.Value); } GroupedPursuit.Sort([](const FDTFluxPursuitGroup& A, const FDTFluxPursuitGroup& B) { return A.StartTimeGlobal < B.StartTimeGlobal; }); TArray FocusPursuits; TArray NextPursuits; bool bIsFocusTruncate = false; GetPursuit(FocusPursuits, NextPursuits, bIsFocusTruncate); FPursuitStarterData PursuitData = FPursuitStarterData(FocusPursuits, NextPursuits, MassStartTime, bIsFocusTruncate); OnPursuitSequenceReady.Broadcast(PursuitData); return true; }