From 03eb1132ef98666611bf676e62866da0d1c9f2d0 Mon Sep 17 00:00:00 2001 From: Ange-Marie MAURIN Date: Wed, 9 Jul 2025 03:27:23 +0200 Subject: [PATCH] Added Pursuit functionality (Untested and not fully implemented) + Global TrackedRequestSending check --- DTFluxAPI.uplugin | 5 - .../DTFluxModelAssetDetailCustomization.cpp | 5 + .../DTFluxModelAssetDetailCustomization.h | 1 + .../Types/Struct/DTFluxRankingStructs.cpp | 22 +-- .../Types/Struct/DTFluxTeamListStruct.cpp | 121 +++++++------- .../Types/Struct/DTFluxRankingStructs.h | 7 +- .../Public/Types/Struct/FDTFluxPursuitInfo.h | 3 + .../Private/DTFluxCoreSubsystem.cpp | 21 +++ .../Private/DTFluxPursuitManager.cpp | 152 ++++++++++++++++++ .../Public/DTFluxCoreSubsystem.h | 11 +- .../Public/DTFluxPursuitManager.h | 133 +++++++++++++++ .../Private/DTFluxQueuedManager.cpp | 40 ++++- .../Struct/DTFluxServerResponseStruct.cpp | 3 +- .../Subsystems/DTFluxNetworkSubsystem.cpp | 92 ++++++++--- .../Public/DTFluxQueuedManager.h | 106 +----------- .../Public/Struct/DTFluxRequestStructs.h | 102 ++++++++++++ .../Struct/DTFluxServerResponseStruct.h | 23 +++ .../Subsystems/DTFluxNetworkSubsystem.h | 20 ++- .../DTFluxPursuitSystem.Build.cs | 28 ---- .../Private/DTFluxPursuitSystemModule.cpp | 19 --- .../Public/DTFluxPursuitSystemModule.h | 13 -- .../DTFluxUtilities/DTFluxUtilities.Build.cs | 3 +- 22 files changed, 636 insertions(+), 294 deletions(-) create mode 100644 Source/DTFluxCoreSubsystem/Private/DTFluxPursuitManager.cpp create mode 100644 Source/DTFluxCoreSubsystem/Public/DTFluxPursuitManager.h delete mode 100644 Source/DTFluxPursuitSystem/DTFluxPursuitSystem.Build.cs delete mode 100644 Source/DTFluxPursuitSystem/Private/DTFluxPursuitSystemModule.cpp delete mode 100644 Source/DTFluxPursuitSystem/Public/DTFluxPursuitSystemModule.h diff --git a/DTFluxAPI.uplugin b/DTFluxAPI.uplugin index a295606..f999a51 100644 --- a/DTFluxAPI.uplugin +++ b/DTFluxAPI.uplugin @@ -49,11 +49,6 @@ "Name": "DTFluxAPIStatus", "Type": "Editor", "LoadingPhase": "Default" - }, - { - "Name": "DTFluxPursuitSystem", - "Type": "Runtime", - "LoadingPhase": "Default" } ], "Plugins": [ diff --git a/Source/DTFluxAssetsEditor/Private/DTFluxModelAssetDetailCustomization.cpp b/Source/DTFluxAssetsEditor/Private/DTFluxModelAssetDetailCustomization.cpp index 66f42ca..f86d614 100644 --- a/Source/DTFluxAssetsEditor/Private/DTFluxModelAssetDetailCustomization.cpp +++ b/Source/DTFluxAssetsEditor/Private/DTFluxModelAssetDetailCustomization.cpp @@ -36,6 +36,11 @@ EActiveTimerReturnType SDTFluxAssetModelDetailsWidget::ForceInitialLayout(double } void FDTFluxModelAssetCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) +{ + CustomizeDetailsWithRawDataAccess(DetailBuilder); +} + +void FDTFluxModelAssetCustomization::CustomizeDetailsWithoutRawDataAsset(IDetailLayoutBuilder& DetailBuilder) { // Edit object TArray> ObjectsBeingCustomized; diff --git a/Source/DTFluxAssetsEditor/Public/DTFluxModelAssetDetailCustomization.h b/Source/DTFluxAssetsEditor/Public/DTFluxModelAssetDetailCustomization.h index 28aebce..6c2f9c6 100644 --- a/Source/DTFluxAssetsEditor/Public/DTFluxModelAssetDetailCustomization.h +++ b/Source/DTFluxAssetsEditor/Public/DTFluxModelAssetDetailCustomization.h @@ -11,6 +11,7 @@ public: // IDetailCustomization interface virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override; + void CustomizeDetailsWithoutRawDataAsset(IDetailLayoutBuilder& DetailBuilder); void CustomizeDetailsWithRawDataAccess(IDetailLayoutBuilder& DetailBuilder); // Crée une instance de cette customization diff --git a/Source/DTFluxCore/Private/Types/Struct/DTFluxRankingStructs.cpp b/Source/DTFluxCore/Private/Types/Struct/DTFluxRankingStructs.cpp index 96e305d..bfe1d5d 100644 --- a/Source/DTFluxCore/Private/Types/Struct/DTFluxRankingStructs.cpp +++ b/Source/DTFluxCore/Private/Types/Struct/DTFluxRankingStructs.cpp @@ -6,24 +6,6 @@ void FDTFluxContestRanking::Dump() const { UE_LOG(logDTFluxCore, Log, - TEXT("FDTFluxContestRanking ->> \n \"rank\" : %d, Participant with Bib %d \"Gap\" : %s, \"Time\" : %s "), - Rank, Bib, *Gap, *Time ); + TEXT("FDTFluxContestRanking ->> \n \"rank\" : %d, Participant with Bib %d \"Gap\" : %s, \"Time\" : %s "), + Rank, Bib, *Gap, *Time); }; - -// void FDTFluxStageRanking::Dump() const -// { -// UE_LOG(logDTFluxCore, Log, TEXT("RANKING : %02d. Participant bib %d %s %s %s %s %s"), -// Rank, Bib, *Gap, *TimeSwim, -// *TimeTransition, *TimeRun, *StartTime.ToString()); -// } -// - - - -// void FDTFluxSplitRanking::Dump() const -// { -// UE_LOG(logDTFluxCore, Log, TEXT("SplitGapItem")) -// // Participant.Dump(); -// UE_LOG(logDTFluxCore, Log, TEXT("Bib %02d Rank %02d Gap %s Time %s"), Bib, Rank, *Gap, *Time); -// } - diff --git a/Source/DTFluxCore/Private/Types/Struct/DTFluxTeamListStruct.cpp b/Source/DTFluxCore/Private/Types/Struct/DTFluxTeamListStruct.cpp index 49f6cdf..b44f04d 100644 --- a/Source/DTFluxCore/Private/Types/Struct/DTFluxTeamListStruct.cpp +++ b/Source/DTFluxCore/Private/Types/Struct/DTFluxTeamListStruct.cpp @@ -3,6 +3,8 @@ #include "Types/Struct/DTFluxTeamListStruct.h" +#include "DTFluxCoreModule.h" + void FDTFluxParticipant::AddTeammate(const FDTFluxPerson& Person) { @@ -15,78 +17,67 @@ void FDTFluxParticipant::AddTeammate(const FString LastName, const FString First FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString OverflowChars) const { - // Vérifie les cas limites - if (MaxChar <= 0) { - return ""; - } - FString FirstName; - FString LastName; - if (IsTeam()) - { - LastName = Team; - } - else - { - FirstName = Teammate[0].FirstName; - LastName = Teammate[0].LastName; - } - // Récupère la première lettre du prénom en majuscule - FString Initial; - if (!FirstName.IsEmpty()) - { - Initial = FirstName.Left(1).ToUpper() + " "; - } - - // Nom complet en majuscules - FString FormattedLastName = LastName.ToUpper(); - - // Construction du nom final - FString FullName = Initial + FormattedLastName; - UE_LOG(logDTFluxCore, Error, TEXT("FullName for Bib %i is %s"), Bib, *FullName); - - // Tronque si nécessaire - if (FullName.Len() > MaxChar) - { - // On essaie de garder autant de caractères que possible - const int32 AvailableLength = MaxChar - Initial.Len(); - if (AvailableLength <= 0) + if (MaxChar <= 0) { - return Initial; + return ""; } - // Coupe le nom pour qu’il rentre dans la limite - const int32 TruncateLength = FMath::Min(AvailableLength, FormattedLastName.Len()); - FullName = Initial + FormattedLastName.Left(TruncateLength); - - // Si on a coupé trop court, on ajoute le suffixe - if (FormattedLastName.Len() > TruncateLength) + FString FirstName; + FString LastName; + if (IsTeam()) { - // On vérifie qu'il reste de la place pour le suffixe - const int32 CurrentLength = FullName.Len(); - const int32 OverflowLength = OverflowChars.Len(); - - if (CurrentLength + OverflowLength <= MaxChar) - { - FullName += OverflowChars; - } - else - { - // Il faut tronquer davantage pour faire de la place au suffixe - const int32 RemainingSpace = MaxChar - CurrentLength; - if (RemainingSpace > 0) - { - FullName = FullName.Left(MaxChar - OverflowLength) + OverflowChars; - } - else - { - FullName = FullName.Left(MaxChar); - } - } + LastName = Team; + } + else + { + FirstName = Teammate[0].FirstName; + LastName = Teammate[0].LastName; + } + FString Initial; + if (!FirstName.IsEmpty()) + { + Initial = FirstName.Left(1).ToUpper() + " "; } - } - return FullName; + FString FormattedLastName = LastName.ToUpper(); + + FString FullName = Initial + FormattedLastName; + UE_LOG(logDTFluxCore, Error, TEXT("FullName for Bib %i is %s"), Bib, *FullName); + + if (FullName.Len() <= MaxChar) + { + return FullName; + } + + const int32 OverflowLength = OverflowChars.Len(); + + if (OverflowLength > MaxChar) + { + return FullName.Left(MaxChar); + } + + if (Initial.Len() + OverflowLength > MaxChar) + { + return FullName.Left(MaxChar); + } + + const int32 AvailableForLastName = MaxChar - Initial.Len() - OverflowLength; + + if (AvailableForLastName <= 0) + { + return FullName.Left(MaxChar); + } + + FString TruncatedName = Initial + FormattedLastName.Left(AvailableForLastName) + OverflowChars; + + if (TruncatedName.Len() > MaxChar) + { + return TruncatedName.Left(MaxChar); + } + + return TruncatedName; + } } FString FDTFluxParticipant::GetConcatFormattedName(const int MaxChar, const FString OverflowChar) const diff --git a/Source/DTFluxCore/Public/Types/Struct/DTFluxRankingStructs.h b/Source/DTFluxCore/Public/Types/Struct/DTFluxRankingStructs.h index cdbddaf..56ed308 100644 --- a/Source/DTFluxCore/Public/Types/Struct/DTFluxRankingStructs.h +++ b/Source/DTFluxCore/Public/Types/Struct/DTFluxRankingStructs.h @@ -85,11 +85,11 @@ public: UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere) FDateTime StartTime; UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere) - float SpeedRunning; + FString SpeedRunning; UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere) - float SpeedTotal; + FString SpeedTotal; UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere) - float SpeedSwim; + FString SpeedSwim; void Dump() const; }; @@ -103,6 +103,7 @@ public: int ContestId = -1; UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model|Ranking", VisibleAnywhere) int StageId = -1; + UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model|Ranking", VisibleAnywhere) TArray Rankings; }; diff --git a/Source/DTFluxCore/Public/Types/Struct/FDTFluxPursuitInfo.h b/Source/DTFluxCore/Public/Types/Struct/FDTFluxPursuitInfo.h index a05e50b..29a89c8 100644 --- a/Source/DTFluxCore/Public/Types/Struct/FDTFluxPursuitInfo.h +++ b/Source/DTFluxCore/Public/Types/Struct/FDTFluxPursuitInfo.h @@ -32,4 +32,7 @@ struct FDTFluxPursuitInfo //TODO : Set this property to BlueprintReadOnly UPROPERTY(BlueprintReadWrite, EditAnywhere) FDateTime StartTime; + + UPROPERTY() + int ContestId = -1; }; diff --git a/Source/DTFluxCoreSubsystem/Private/DTFluxCoreSubsystem.cpp b/Source/DTFluxCoreSubsystem/Private/DTFluxCoreSubsystem.cpp index a4e3fe3..9d9e560 100644 --- a/Source/DTFluxCoreSubsystem/Private/DTFluxCoreSubsystem.cpp +++ b/Source/DTFluxCoreSubsystem/Private/DTFluxCoreSubsystem.cpp @@ -6,6 +6,7 @@ #include "DTFluxCoreSubsystemModule.h" #include "DTFluxGeneralSettings.h" +#include "DTFluxPursuitManager.h" #include "FileHelpers.h" #include "Assets/DTFluxModelAsset.h" #include "Subsystems/DTFluxNetworkSubsystem.h" @@ -31,6 +32,7 @@ void UDTFluxCoreSubsystem::Initialize(FSubsystemCollectionBase& Collection) { RegisterDelegates(); } + PursuitManager = NewObject(); } void UDTFluxCoreSubsystem::Deinitialize() @@ -345,3 +347,22 @@ TArray UDTFluxCoreSubsystem::GetContests() } return TArray(); } + +void UDTFluxCoreSubsystem::LaunchPursuitSequenceFor(const TArray ContestIds) +{ + TArray Contests = TArray(); + for (const auto& ContestId : ContestIds) + { + FDTFluxContest Contest; + GetContestForId(ContestId, Contest); + Contests.Add(Contest); + if (PursuitManager) + { + PursuitManager->LaunchPursuitSequenceFor(Contests); + } + else + { + UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("PursuitManager is null")); + } + } +} diff --git a/Source/DTFluxCoreSubsystem/Private/DTFluxPursuitManager.cpp b/Source/DTFluxCoreSubsystem/Private/DTFluxPursuitManager.cpp new file mode 100644 index 0000000..aa5ff27 --- /dev/null +++ b/Source/DTFluxCoreSubsystem/Private/DTFluxPursuitManager.cpp @@ -0,0 +1,152 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DTFluxPursuitManager.h" + +#include "DTFluxCoreSubsystemModule.h" + +UDTFluxPursuitManager::UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer): + Super(ObjectInitializer) +{ +} + +// TODO : Add way to pass MaxSimultaneousPursuit and MassStartDelay +// For now it's done in UPROPERTIES +void UDTFluxPursuitManager::LaunchPursuitSequenceFor(const TArray InContests) +{ + if (InitSubSystems()) + { + for (const auto Contest : InContests) + { + FRequestData RequestData; + RequestData.ContestId = Contest.ContestId; + uint8 StageId = Contest.Stages.Last().StageId; + FGuid Guid = NetworkSubsystem->SendTrackedRequestWithCallback(EDTFluxApiDataType::StageRanking, + Contest.ContestId, StageId, -1, + FOnDTFluxTrackedRequestResponse::CreateUObject( + this, + &UDTFluxPursuitManager::OnRequestResponse), + FOnDTFluxTrackedRequestTimeout::CreateUObject( + this, + &UDTFluxPursuitManager::OnRequestTimeoutResponse), + FOnDTFluxRequestResponseError::CreateUObject( + this, + &UDTFluxPursuitManager::OnRequestError)); + + RequestData.RequestIds.Add(Guid); + PendingRequestData.Add(RequestData); + } + } +} + +void UDTFluxPursuitManager::OnRequestResponse(const FGuid& RequestId, FDTFluxServerResponse& Response) +{ + UE_LOG(logDTFluxCoreSubsystem, Log, + TEXT("UDTFluxPursuitManager::OnRequestResponse() Received Ranking For Stage %i"), Response.StageID) + UE_LOG(logDTFluxCoreSubsystem, Log, TEXT("Response is %s"), *UEnum::GetValueAsString(Response.GetResponseType())) + //check if request + if (Response.GetResponseType() == EDTFluxApiDataType::StageRanking) + { + FDTFluxStageRankings Rankings; + FRequestData FoundData; + if (Response.ParseStageRankingResponse(Rankings)) + { + for (auto& PendingReq : PendingRequestData) + { + // Check for a matching PendingReq + if (PendingReq.IsWaitingFor(RequestId, Rankings)) + { + FoundData = PendingReq; + // A request Is Terminated + UE_LOG(logDTFluxCoreSubsystem, Log, + TEXT("UDTFluxPursuitManager::OnRequestResponse() Ranking for Stage %i is complete"), + Response.StageID) + break; + } + } + if (InitPursuit(FoundData)) + { + OnPursuitSequenceReady.Broadcast(NextFocusPursuits, NextFocusPursuits, bFocusIsTruncate); + } + } + } +} + +void UDTFluxPursuitManager::OnRequestTimeoutResponse(const FGuid& RequestId, const FString& TimeoutMessage) +{ + UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Request Timeout [%s]"), *TimeoutMessage); +} + +void UDTFluxPursuitManager::OnRequestError(const FGuid& RequestId, const FString& ErrorMessage) +{ + UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Request Error [%s]"), *ErrorMessage); +} + +bool UDTFluxPursuitManager::InitSubSystems() +{ + if (NetworkSubsystem) + { + return true; + } + NetworkSubsystem = GEngine->GetEngineSubsystem(); + return NetworkSubsystem != nullptr; +} + +bool UDTFluxPursuitManager::InitPursuit(FRequestData Data) +{ + //Clean Data + NextFocusPursuits.Empty(); + NextPursuits.Empty(); + PursuitGrouped.Empty(); + TArray AllRankings; + TArray AllPursuits; + TMap TempGroups; + + // Full the Array Of Rankings + for (auto& KeyPair : Data.StageRankings) + { + for (auto StageRanking : KeyPair.Value.Rankings) + { + int ContestId = KeyPair.Value.ContestId; + FDTFluxPursuitInfo PursuitInfo; + PursuitInfo.StartTime = StageRanking.StartTime; + PursuitInfo.Bib = StageRanking.Bib; + PursuitInfo.ContestId = ContestId; + AllPursuits.Add(PursuitInfo); + } + } + // Sort Rankings + // AllPursuits.Sort([](const FDTFluxPursuitInfo& A, const FDTFluxPursuitInfo& B) { + // return A.StartTime < B.StartTime; + // }); + + for (auto& Pursuit : AllPursuits) + { + if (TempGroups.Contains(Pursuit.StartTime)) + { + TempGroups[Pursuit.StartTime].PursuitGroup.Add(Pursuit); + } + else + { + FDTFluxPursuitGroup Group; + Group.StartTimeGlobal = Pursuit.StartTime; + Group.PursuitGroup.Add(Pursuit); + TempGroups.Add(Pursuit.StartTime, Group); + } + } + TempGroups.KeySort([](const FDateTime& A, const FDateTime& B) + { + return A < B; + }); + PursuitGrouped.Reserve(TempGroups.Num()); + for (const auto& Pair : TempGroups) + { + PursuitGrouped.Add(Pair.Value); + } + + PursuitGrouped.Sort([](const FDTFluxPursuitGroup& A, const FDTFluxPursuitGroup& B) + { + return A.StartTimeGlobal < B.StartTimeGlobal; + }); + return true; +} diff --git a/Source/DTFluxCoreSubsystem/Public/DTFluxCoreSubsystem.h b/Source/DTFluxCoreSubsystem/Public/DTFluxCoreSubsystem.h index 2ff4b80..dfec220 100644 --- a/Source/DTFluxCoreSubsystem/Public/DTFluxCoreSubsystem.h +++ b/Source/DTFluxCoreSubsystem/Public/DTFluxCoreSubsystem.h @@ -1,6 +1,4 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once +#pragma once #include "CoreMinimal.h" #include "Containers/Deque.h" @@ -15,6 +13,7 @@ class UDTFluxNetworkSubsystem; /** Forward Decl */ class UDTFluxModelAsset; +class UDTFluxPursuitManager; /** * @@ -107,12 +106,18 @@ public: UFUNCTION() TArray GetContests(); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem") + void LaunchPursuitSequenceFor(const TArray ContestIds); + protected: // ~Subsystem Interface virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; // ~Subsystem Interface + UPROPERTY() + UDTFluxPursuitManager* PursuitManager = nullptr; UFUNCTION() void SaveDataStorage(); diff --git a/Source/DTFluxCoreSubsystem/Public/DTFluxPursuitManager.h b/Source/DTFluxCoreSubsystem/Public/DTFluxPursuitManager.h new file mode 100644 index 0000000..dbc9949 --- /dev/null +++ b/Source/DTFluxCoreSubsystem/Public/DTFluxPursuitManager.h @@ -0,0 +1,133 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/DTFluxNetworkSubsystem.h" +#include "Types/Struct/DTFluxRaceDataStructs.h" +#include "Types/Struct/FDTFluxPursuitInfo.h" +#include "UObject/Object.h" +#include "DTFluxPursuitManager.generated.h" + + +USTRUCT() +struct FRequestData +{ + GENERATED_BODY() + + UPROPERTY() + TArray RequestIds; + UPROPERTY() + TMap StageRankings; + UPROPERTY() + int ContestId; + + UPROPERTY() + bool bIsReady = false; + + + FRequestData() = default; + + FRequestData(const TArray& InRequestIds, const TMap& InStageRankings) + : RequestIds(InRequestIds), StageRankings(InStageRankings) + { + }; + + /** + * + * @param RequestId + * @param InRankings + * @return True if all needed requests have responses + */ + bool IsWaitingFor(const FGuid& RequestId, const FDTFluxStageRankings& InRankings) + { + if (!StageRankings.Contains(RequestId)) + { + StageRankings.Add(RequestId, InRankings); + } + bIsReady = StageRankings.Num() <= RequestIds.Num(); + return bIsReady; + } +}; + +USTRUCT() +struct FDTFluxPursuitGroup +{ + GENERATED_BODY() + + UPROPERTY() + TArray PursuitGroup = TArray(); + UPROPERTY() + FDateTime StartTimeGlobal = FDateTime::MinValue(); + UPROPERTY() + bool bHasStarted = false; + UPROPERTY() + bool bIsFocus = false; +}; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnPursuitSequenceReady, const TArray, + NextFocusPursuits, + const TArray, NextPursuit, bool, bIsTrtuncate); + +/** + * + */ +UCLASS(BlueprintType) +class DTFLUXCORESUBSYSTEM_API UDTFluxPursuitManager : public UObject +{ + GENERATED_BODY() + +public: + UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + TArray NextFocusPursuits; + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + TArray NextPursuits; + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + bool bFocusIsTruncate = false; + // + // UPROPERTY() + // TArray TargetStages; + + UPROPERTY() + int MaxSimultaneousPursuit = 7; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="DTFlux|Pursuit", + meta=(ClampMin="1", ClampMax="60", UIMin="0", UIMax="60")) + int MassStartDelay = 10; + + UPROPERTY() + TArray PursuitGrouped; + + UPROPERTY() + int CurrentIndex = -1; + + UPROPERTY(BlueprintCallable, Category="DTFlux|Pursuit") + FOnPursuitSequenceReady OnPursuitSequenceReady; + + + UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="pursuit, launch, poursuite")) + void LaunchPursuitSequenceFor(const TArray InContests); + + UFUNCTION() + void OnRequestResponse(const FGuid& RequestId, FDTFluxServerResponse& Response); + + UFUNCTION() + void OnRequestTimeoutResponse(const FGuid& RequestId, const FString& TimeoutMessage); + + UFUNCTION() + void OnRequestError(const FGuid& RequestId, const FString& ErrorMessage); + + UFUNCTION() + bool InitSubSystems(); + +private: + TArray PendingRequestData; + +public: + UFUNCTION() + bool InitPursuit(FRequestData Data); + +private: + UDTFluxNetworkSubsystem* NetworkSubsystem = nullptr; +}; diff --git a/Source/DTFluxNetwork/Private/DTFluxQueuedManager.cpp b/Source/DTFluxNetwork/Private/DTFluxQueuedManager.cpp index 86f3596..aa742c8 100644 --- a/Source/DTFluxNetwork/Private/DTFluxQueuedManager.cpp +++ b/Source/DTFluxNetwork/Private/DTFluxQueuedManager.cpp @@ -90,6 +90,38 @@ FGuid UDTFluxQueuedManager::QueueRequest(EDTFluxRequestType RequestType, int32 C return NewRequest.RequestId; } +bool UDTFluxQueuedManager::MarkRequestAsError(const FGuid& TargetRequestGuid) +{ + // TODO: Implement a retry mechanism + // For now we simply suppress the request and log a message + bool bFoundMatch = false; + FDTFluxQueuedRequest Request; + TQueue TempQueue; + while (PendingRequestsQueue.Dequeue(Request)) + { + if (Request.RequestId == TargetRequestGuid) + { + UE_LOG(logDTFluxNetwork, Error, + TEXT("Marked request %s as error: Type=%d, ContestId=%d, StageId=%d, SplitId=%d"), + *Request.RequestId.ToString(), (int32)Request.RequestType, Request.ContestId, Request.StageId, + Request.SplitId); + } + else + { + TempQueue.Enqueue(Request); + } + } + while (TempQueue.Dequeue(Request)) + { + PendingRequestsQueue.Enqueue(Request); + } + if (bFoundMatch) + { + UE_LOG(logDTFluxNetwork, Error, TEXT("No Request Found with GUID %s"), *TargetRequestGuid.ToString()); + } + return true; +} + bool UDTFluxQueuedManager::MarkRequestAsResponded(const FGuid& TargetRequestGuid) { TQueue TempQueue; @@ -134,7 +166,8 @@ bool UDTFluxQueuedManager::MarkRequestAsResponded(const FDTFluxQueuedRequest& Ta return MarkRequestAsResponded(TargetRequest.RequestId); } -bool UDTFluxQueuedManager::IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId, +bool UDTFluxQueuedManager::IsRequestPending(FGuid& OutRequestId, EDTFluxApiDataType RequestType, int32 ContestId, + int32 StageId, int32 SplitId) { TQueue TempQueue; @@ -148,6 +181,11 @@ bool UDTFluxQueuedManager::IsRequestPending(EDTFluxApiDataType RequestType, int3 if (!bFoundMatch && Request.Matches(RequestType, ContestId, StageId, SplitId)) { bFoundMatch = true; + OutRequestId = Request.RequestId; + UE_LOG(logDTFluxNetwork, Verbose, + TEXT("Found pending request %s: Type=%d, ContestId=%d, StageId=%d, SplitId=%d"), + *Request.RequestId.ToString(), (int32)Request.RequestType, Request.ContestId, Request.StageId, + Request.SplitId); } // Remettre dans la queue temporaire diff --git a/Source/DTFluxNetwork/Private/Struct/DTFluxServerResponseStruct.cpp b/Source/DTFluxNetwork/Private/Struct/DTFluxServerResponseStruct.cpp index c39b476..4d3ce03 100644 --- a/Source/DTFluxNetwork/Private/Struct/DTFluxServerResponseStruct.cpp +++ b/Source/DTFluxNetwork/Private/Struct/DTFluxServerResponseStruct.cpp @@ -370,13 +370,13 @@ bool FDTFluxServerResponse::ParseContestRanking(FDTFluxContestRankings& OutConte bool FDTFluxServerResponse::ParseStageRankingResponse(FDTFluxStageRankings& OutStageRankings) { + // UE_LOG(logDTFluxNetwork, Log, TEXT("Response is stage-ranking type %s"), *RawMessage); if (!ValidateResponseType(TEXT("stage-ranking"))) { UE_LOG(logDTFluxNetwork, Error, TEXT("Response is not a stage-ranking type")); ParsingStatus = EDTFluxResponseStatus::InvalidType; return false; } - FDTFluxStageRankingResponse RankingResponse; if (!FJsonObjectConverter::JsonObjectStringToUStruct(RawMessage, &RankingResponse)) { @@ -384,6 +384,7 @@ bool FDTFluxServerResponse::ParseStageRankingResponse(FDTFluxStageRankings& OutS ParsingStatus = EDTFluxResponseStatus::JsonParseError; return false; } + UE_LOG(logDTFluxNetwork, Log, TEXT("Reponse Update")); OutStageRankings.ContestId = ContestID; OutStageRankings.StageId = StageID; diff --git a/Source/DTFluxNetwork/Private/Subsystems/DTFluxNetworkSubsystem.cpp b/Source/DTFluxNetwork/Private/Subsystems/DTFluxNetworkSubsystem.cpp index 0c77df4..22ee501 100644 --- a/Source/DTFluxNetwork/Private/Subsystems/DTFluxNetworkSubsystem.cpp +++ b/Source/DTFluxNetwork/Private/Subsystems/DTFluxNetworkSubsystem.cpp @@ -81,8 +81,9 @@ FGuid UDTFluxNetworkSubsystem::SendTrackedRequestWithCallback( int32 ContestId, int32 StageId, int32 SplitId, - FOnDTFluxRequestResponse OnCompleted, - FOnDTFluxRequestTimeout OnTimeout, + FOnDTFluxTrackedRequestResponse OnCompleted, + FOnDTFluxTrackedRequestTimeout OnTimeout, + TOptional OnError, float TimeoutSeconds) { FGuid RequestId = SendTrackedRequest(RequestType, ContestId, StageId, SplitId, TimeoutSeconds); @@ -98,11 +99,16 @@ FGuid UDTFluxNetworkSubsystem::SendTrackedRequestWithCallback( { PendingTimeoutCallbacks.Add(RequestId, OnTimeout); } + if (OnError.IsSet() && OnError.GetValue().IsBound()) + { + PendingErrorCallbacks.Add(RequestId, OnError.GetValue()); + } } return RequestId; } + bool UDTFluxNetworkSubsystem::GetTrackedRequest(const FGuid& RequestId, FDTFluxQueuedRequest& OutRequest) const { if (!QueueManager) @@ -155,7 +161,8 @@ bool UDTFluxNetworkSubsystem::IsRequestPending(EDTFluxRequestType RequestType, i { return false; } - return QueueManager->IsRequestPending(RequestType, ContestId, StageId, SplitId); + FGuid OutRequestId; + return QueueManager->IsRequestPending(OutRequestId, RequestType, ContestId, StageId, SplitId); } int32 UDTFluxNetworkSubsystem::GetPendingRequestCount() const @@ -583,8 +590,6 @@ void UDTFluxNetworkSubsystem::Parse(FDTFluxServerResponse& Response) } } - -//TODO reforge API to keep track of Requests void UDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& MessageString) { // UE_LOG(logDTFluxNetwork, Warning, TEXT("Client %s :\nMessage Received : %s"), *WsClient->GetAddress(), *MessageString); @@ -600,8 +605,11 @@ void UDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& M UE_LOG(logDTFluxNetwork, Warning, TEXT("Not a push message")); // Legacy Parse(Response); + return; } } + // // if we are here we have a tracked Message + // QueueManager->MarkRequestAsResponded() } void UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent) @@ -609,6 +617,29 @@ void UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem(const FStrin UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws %s :\nMessage Sent: %s"), *WsClient->GetAddress(), *MessageSent); } +bool UDTFluxNetworkSubsystem::CleanRequestCallbacks(const FGuid& RequestId) +{ + bool bCbSuppressSuccess = false; + bool bErrorCbSuppressSuccess = false; + bool bTimeoutCbSuppressSuccess = false; + if (PendingCallbacks.Contains(RequestId)) + { + PendingCallbacks.Remove(RequestId); + bCbSuppressSuccess = true; + } + if (PendingTimeoutCallbacks.Contains(RequestId)) + { + PendingTimeoutCallbacks.Remove(RequestId); + bTimeoutCbSuppressSuccess = true; + } + if (PendingTimeoutCallbacks.Contains(RequestId)) + { + PendingTimeoutCallbacks.Remove(RequestId); + bErrorCbSuppressSuccess = true; + } + return bCbSuppressSuccess && bErrorCbSuppressSuccess && bTimeoutCbSuppressSuccess; +} + void UDTFluxNetworkSubsystem::OnRequestTimedOut_Internal(const FDTFluxQueuedRequest& TimedOutRequest) { UE_LOG(logDTFluxNetwork, Warning, TEXT("Request %s timed out: Type=%d, Contest=%d, Stage=%d, Split=%d"), @@ -619,7 +650,7 @@ void UDTFluxNetworkSubsystem::OnRequestTimedOut_Internal(const FDTFluxQueuedRequ TimedOutRequest.SplitId); // Appeler le callback de timeout si présent - if (FOnDTFluxRequestTimeout* TimeoutCallback = PendingTimeoutCallbacks.Find(TimedOutRequest.RequestId)) + if (FOnDTFluxTrackedRequestTimeout* TimeoutCallback = PendingTimeoutCallbacks.Find(TimedOutRequest.RequestId)) { if (TimeoutCallback->IsBound()) { @@ -635,32 +666,28 @@ void UDTFluxNetworkSubsystem::OnRequestTimedOut_Internal(const FDTFluxQueuedRequ OnTrackedRequestFailed.Broadcast(TimedOutRequest.RequestId, TimedOutRequest.RequestType, TEXT("Request timeout")); } -bool UDTFluxNetworkSubsystem::TryMatchResponseToQueuedRequest(const FDTFluxServerResponse& Response) +bool UDTFluxNetworkSubsystem::TryMatchResponseToQueuedRequest(FDTFluxServerResponse& Response) { if (!QueueManager) { return false; } - // Essayer de trouver une requête correspondante - // Note: Cette méthode nécessiterait une modification de UDTFluxQueuedManager pour supporter le matching par type et paramètres - - // Pour l'instant, on utilise une approche simple : chercher la première requête du bon type - // Vous devrez probablement modifier UDTFluxQueuedManager pour ajouter une méthode comme FindMatchingRequest() - - - // Implémentation temporaire : on assume qu'il n'y a qu'une requête de chaque type en cours - if (QueueManager->IsRequestPending(Response.GetResponseType(), Response.ContestID, Response.StageID, + FGuid FoundRequestId; + if (QueueManager->IsRequestPending(FoundRequestId, Response.GetResponseType(), Response.ContestID, Response.StageID, Response.SplitID)) { - // Marquer comme répondu - vous devrez adapter cette méthode selon votre logique de matching - // Pour l'instant, on va faire un workaround simple - UE_LOG(logDTFluxNetwork, Log, TEXT("Matched response to queued request: Type=%s, Contest=%d, Stage=%d, Split=%d"), *UEnum::GetValueAsString(Response.GetResponseType()), Response.ContestID, Response.StageID, Response.SplitID); - - return true; + if (PendingCallbacks.Contains(FoundRequestId)) + { + FOnDTFluxTrackedRequestResponse* SuccessCallback = PendingCallbacks.Find(FoundRequestId); + SuccessCallback->ExecuteIfBound(FoundRequestId, Response); + //Suppress Callback; + return CleanRequestCallbacks(FoundRequestId); + } + return QueueManager->MarkRequestAsResponded(FoundRequestId); } return false; @@ -676,13 +703,26 @@ void UDTFluxNetworkSubsystem::CompleteTrackedRequest(const FGuid& RequestId, con } // Appeler le callback de succès si présent - if (FOnDTFluxRequestResponse* SuccessCallback = PendingCallbacks.Find(RequestId)) + if (FOnDTFluxTrackedRequestResponse* SuccessCallback = PendingCallbacks.Find(RequestId)) { if (SuccessCallback->IsBound()) { - SuccessCallback->Execute(RequestId, ResponseData); + EDTFluxResponseStatus ResponseStatus; + FDTFluxServerResponse Response(ResponseData, ResponseStatus); + if (ResponseStatus == EDTFluxResponseStatus::Success) + { + SuccessCallback->Execute(RequestId, Response); + QueueManager->MarkRequestAsResponded(RequestId); + PendingCallbacks.Remove(RequestId); + PendingTimeoutCallbacks.Remove(RequestId); + } + else + { + QueueManager->MarkRequestAsError(RequestId); + // Fail + // FailTrackedRequest() + } } - PendingCallbacks.Remove(RequestId); } // Nettoyer le callback de timeout @@ -698,11 +738,11 @@ void UDTFluxNetworkSubsystem::FailTrackedRequest(const FGuid& RequestId, const F EDTFluxRequestType RequestType) { // Appeler le callback d'erreur si présent - if (FOnDTFluxRequestTimeout* ErrorCallback = PendingTimeoutCallbacks.Find(RequestId)) + if (FOnDTFluxTrackedRequestTimeout* ErrorCallback = PendingTimeoutCallbacks.Find(RequestId)) { if (ErrorCallback->IsBound()) { - ErrorCallback->Execute(RequestId, ErrorMessage); + ErrorCallback->ExecuteIfBound(RequestId, ErrorMessage); } PendingTimeoutCallbacks.Remove(RequestId); } diff --git a/Source/DTFluxNetwork/Public/DTFluxQueuedManager.h b/Source/DTFluxNetwork/Public/DTFluxQueuedManager.h index ef61f99..50a78d8 100644 --- a/Source/DTFluxNetwork/Public/DTFluxQueuedManager.h +++ b/Source/DTFluxNetwork/Public/DTFluxQueuedManager.h @@ -11,107 +11,6 @@ #include "Types/Enum/DTFluxCoreEnum.h" #include "DTFluxQueuedManager.generated.h" -/** - * @brief Structure représentant une requête en file d'attente avec ses métadonnées - */ -USTRUCT(BlueprintType) -struct FDTFluxQueuedRequest : public FDTFluxRequestBase -{ - GENERATED_BODY() - - /** L'identifiant unique de la requête */ - UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") - FGuid RequestId; - - /** L'heure à laquelle la requête a été envoyée */ - UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") - FDateTime CreatedAt; - - /** Le type de requête */ - UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") - EDTFluxApiDataType RequestType = EDTFluxRequestType::None; - - /** Identifiant de la compétition (ContestId) */ - UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") - int32 ContestId = -1; - - /** Identifiant de l'étape (StageId) */ - UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") - int32 StageId = -1; - - /** Identifiant du split (SplitId) */ - UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") - int32 SplitId = -1; - - UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") - FString RawResponse = ""; - - /** Délai maximum avant que la requête soit considérée comme expirée (en secondes) */ - UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request") - float TimeoutSeconds = 2.0f; - - /** Determine si la requête peut être mise en cache */ - UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request") - bool bIsCacheable = false; - - /** Validité du cache si bIsCacheable est mis à true après reception de la réponse (en secondes) */ - UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request") - float CachedValidity = 50.0f; - - /** Indicateur si la requête a reçu une réponse */ - UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request") - bool bHasReceivedResponse = false; - - /** Constructeur par défaut */ - FDTFluxQueuedRequest() - { - RequestId = FGuid::NewGuid(); - CreatedAt = FDateTime::Now(); - } - - /** Constructeur avec paramètres */ - FDTFluxQueuedRequest(EDTFluxRequestType InRequestType, int32 InContestId = -1, int32 InStageId = -1, - int32 InSplitId = -1) - : RequestType(InRequestType) - , ContestId(InContestId) - , StageId(InStageId) - , SplitId(InSplitId) - { - RequestId = FGuid::NewGuid(); - CreatedAt = FDateTime::Now(); - } - - bool operator==(const FDTFluxQueuedRequest& Left) const - { - return RequestId == Left.RequestId; - } - - bool operator!=(const FDTFluxQueuedRequest& Left) const - { - return RequestId != Left.RequestId; - } - - - const FString Serialize() const; - - /** Vérifie si la requête a expiré */ - bool HasTimedOut() const - { - return (FDateTime::Now() - CreatedAt).GetTotalSeconds() > TimeoutSeconds; - } - - - /** Vérifie si cette requête correspond aux paramètres spécifiés */ - bool Matches(EDTFluxRequestType InRequestType, int32 InContestId = -1, int32 InStageId = -1, - int32 InSplitId = -1) const - { - return RequestType == InRequestType && - (InContestId == -1 || ContestId == InContestId) && - (InStageId == -1 || StageId == InStageId) && - (InSplitId == -1 || SplitId == InSplitId); - } -}; - DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestTimedOut, const FDTFluxQueuedRequest&, TimedOutRequest); /** @@ -130,9 +29,11 @@ public: void Initialize(); FGuid QueueRequest(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1, const FString& RawMessage = ""); + bool MarkRequestAsError(const FGuid& TargetRequestGuid); bool MarkRequestAsResponded(const FGuid& TargetRequestGuid); bool MarkRequestAsResponded(const FDTFluxQueuedRequest& TargetRequest); - bool IsRequestPending(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1); + bool IsRequestPending(FGuid& OutRequestId, EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1, + int32 SplitId = -1); FDTFluxQueuedRequest* GetRequestPending(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1); const FDTFluxQueuedRequest* GetRequest(const FGuid& SearchedGuid); @@ -142,7 +43,6 @@ public: void ClearAllRequests(); // bool TryProcessResponse(const FDTFluxServerResponse& Response); - // Interface FTickableGameObject virtual void Tick(float DeltaTime) override; virtual bool IsTickable() const override; diff --git a/Source/DTFluxNetwork/Public/Struct/DTFluxRequestStructs.h b/Source/DTFluxNetwork/Public/Struct/DTFluxRequestStructs.h index 7d2020f..cc57037 100644 --- a/Source/DTFluxNetwork/Public/Struct/DTFluxRequestStructs.h +++ b/Source/DTFluxNetwork/Public/Struct/DTFluxRequestStructs.h @@ -3,6 +3,7 @@ #pragma once #include "CoreMinimal.h" +#include "Types/Enum/DTFluxCoreEnum.h" #include "UObject/Object.h" #include "DTFluxRequestStructs.generated.h" @@ -139,3 +140,104 @@ public: SplitID = InSplitId; } }; + +/** + * @brief Structure représentant une requête en file d'attente avec ses métadonnées + */ +USTRUCT(BlueprintType) +struct FDTFluxQueuedRequest : public FDTFluxRequestBase +{ + GENERATED_BODY() + + /** L'identifiant unique de la requête */ + UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") + FGuid RequestId; + + /** L'heure à laquelle la requête a été envoyée */ + UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") + FDateTime CreatedAt; + + /** Le type de requête */ + UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") + EDTFluxApiDataType RequestType = EDTFluxRequestType::None; + + /** Identifiant de la compétition (ContestId) */ + UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") + int32 ContestId = -1; + + /** Identifiant de l'étape (StageId) */ + UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") + int32 StageId = -1; + + /** Identifiant du split (SplitId) */ + UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") + int32 SplitId = -1; + + UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request") + FString RawResponse = ""; + + /** Délai maximum avant que la requête soit considérée comme expirée (en secondes) */ + UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request") + float TimeoutSeconds = 2.0f; + + /** Determine si la requête peut être mise en cache */ + UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request") + bool bIsCacheable = false; + + /** Validité du cache si bIsCacheable est mis à true après reception de la réponse (en secondes) */ + UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request") + float CachedValidity = 50.0f; + + /** Indicateur si la requête a reçu une réponse */ + UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request") + bool bHasReceivedResponse = false; + + /** Constructeur par défaut */ + FDTFluxQueuedRequest() + { + RequestId = FGuid::NewGuid(); + CreatedAt = FDateTime::Now(); + } + + /** Constructeur avec paramètres */ + FDTFluxQueuedRequest(EDTFluxRequestType InRequestType, int32 InContestId = -1, int32 InStageId = -1, + int32 InSplitId = -1) + : RequestType(InRequestType) + , ContestId(InContestId) + , StageId(InStageId) + , SplitId(InSplitId) + { + RequestId = FGuid::NewGuid(); + CreatedAt = FDateTime::Now(); + } + + bool operator==(const FDTFluxQueuedRequest& Left) const + { + return RequestId == Left.RequestId; + } + + bool operator!=(const FDTFluxQueuedRequest& Left) const + { + return RequestId != Left.RequestId; + } + + + const FString Serialize() const; + + /** Vérifie si la requête a expiré */ + bool HasTimedOut() const + { + return (FDateTime::Now() - CreatedAt).GetTotalSeconds() > TimeoutSeconds; + } + + + /** Vérifie si cette requête correspond aux paramètres spécifiés */ + bool Matches(EDTFluxRequestType InRequestType, int32 InContestId = -1, int32 InStageId = -1, + int32 InSplitId = -1) const + { + return RequestType == InRequestType && + (InContestId == -1 || ContestId == InContestId) && + (InStageId == -1 || StageId == InStageId) && + (InSplitId == -1 || SplitId == InSplitId); + } +}; diff --git a/Source/DTFluxNetwork/Public/Struct/DTFluxServerResponseStruct.h b/Source/DTFluxNetwork/Public/Struct/DTFluxServerResponseStruct.h index 22ca8e0..786ed91 100644 --- a/Source/DTFluxNetwork/Public/Struct/DTFluxServerResponseStruct.h +++ b/Source/DTFluxNetwork/Public/Struct/DTFluxServerResponseStruct.h @@ -117,4 +117,27 @@ private: bool ParseJsonObject(TSharedPtr& OutJsonObject) const; bool ValidateResponseType(const FString& ExpectedType) const; EDTFluxResponseStatus InitializeFromJson(const FString& JsonMessage, bool bLogErrors); + + static FString GetJsonType(const EJson Type) + { + switch (Type) + { + case EJson::None: + return TEXT("None"); + case EJson::Null: + return TEXT("Null"); + case EJson::String: + return TEXT("String"); + case EJson::Number: + return TEXT("Number"); + case EJson::Boolean: + return TEXT("Boolean"); + case EJson::Array: + return TEXT("Array"); + case EJson::Object: + return TEXT("Object"); + default: + return TEXT("Unknown"); + } + } }; diff --git a/Source/DTFluxNetwork/Public/Subsystems/DTFluxNetworkSubsystem.h b/Source/DTFluxNetwork/Public/Subsystems/DTFluxNetworkSubsystem.h index 81a358a..3752f58 100644 --- a/Source/DTFluxNetwork/Public/Subsystems/DTFluxNetworkSubsystem.h +++ b/Source/DTFluxNetwork/Public/Subsystems/DTFluxNetworkSubsystem.h @@ -23,8 +23,9 @@ typedef TSharedPtr FDTFluxHttpClientSP; // Delegates pour les requêtes avec callback -DECLARE_DELEGATE_TwoParams(FOnDTFluxRequestResponse, const FGuid&, const FString&); -DECLARE_DELEGATE_TwoParams(FOnDTFluxRequestTimeout, const FGuid&, const FString&); +DECLARE_DELEGATE_TwoParams(FOnDTFluxRequestResponseError, const FGuid&, const FString&); +DECLARE_DELEGATE_TwoParams(FOnDTFluxTrackedRequestResponse, const FGuid&, FDTFluxServerResponse&); +DECLARE_DELEGATE_TwoParams(FOnDTFluxTrackedRequestTimeout, const FGuid&, const FString& /*ErrorMessage*/); // Delegates Blueprint pour les requêtes avec tracking DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestCompleted, const FGuid&, RequestId, EDTFluxApiDataType, RequestType, const FString&, ResponseData); @@ -128,9 +129,14 @@ public: UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests") FGuid SendTrackedRequest(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1, float TimeoutSeconds = 30.0f); + FGuid SendTrackedRequestWithCallback(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId, int32 SplitId, - FOnDTFluxRequestResponse OnCompleted, FOnDTFluxRequestTimeout OnTimeout, + FOnDTFluxTrackedRequestResponse OnCompleted, + FOnDTFluxTrackedRequestTimeout OnTimeout, + TOptional OnError = TOptional< + FOnDTFluxRequestResponseError>(), float TimeoutSeconds = 30.0f); + UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests") bool GetTrackedRequest(const FGuid& RequestId, FDTFluxQueuedRequest& OutRequest) const; const FDTFluxQueuedRequest* GetTrackedRequestPtr(const FGuid& RequestId) const; @@ -175,8 +181,9 @@ private: UDTFluxQueuedManager* QueueManager; // === MAPPING DES CALLBACKS C++ === - TMap PendingCallbacks; - TMap PendingTimeoutCallbacks; + TMap PendingCallbacks; + TMap PendingTimeoutCallbacks; + TMap PendingErrorCallbacks; // === CLIENTS RÉSEAU === FDTFluxWebSocketClientSP WsClient = nullptr; @@ -221,11 +228,12 @@ private: void Parse(FDTFluxServerResponse& Response); void OnWebSocketMessageEvent_Subsystem(const FString& MessageString); void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent); + bool CleanRequestCallbacks(const FGuid& RequestId); // === GESTION DES REQUÊTES TRACKÉES === UFUNCTION() void OnRequestTimedOut_Internal(const FDTFluxQueuedRequest& TimedOutRequest); - bool TryMatchResponseToQueuedRequest(const FDTFluxServerResponse& Response); + bool TryMatchResponseToQueuedRequest(FDTFluxServerResponse& Response); void CompleteTrackedRequest(const FGuid& RequestId, const FString& ResponseData, EDTFluxRequestType RequestType); void FailTrackedRequest(const FGuid& RequestId, const FString& ErrorMessage, EDTFluxRequestType RequestType); void SendQueuedRequest(const FDTFluxQueuedRequest& QueuedRequest); diff --git a/Source/DTFluxPursuitSystem/DTFluxPursuitSystem.Build.cs b/Source/DTFluxPursuitSystem/DTFluxPursuitSystem.Build.cs deleted file mode 100644 index 7114ac1..0000000 --- a/Source/DTFluxPursuitSystem/DTFluxPursuitSystem.Build.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UnrealBuildTool; - -public class DTFluxPursuitSystem : ModuleRules -{ - public DTFluxPursuitSystem(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; - - PublicDependencyModuleNames.AddRange( - new string[] - { - "Core" - } - ); - - PrivateDependencyModuleNames.AddRange( - new string[] - { - "CoreUObject", - "Engine", - "Slate", - "SlateCore", - "DTFluxCore", - "DTFluxCoreSubsystem" - } - ); - } -} \ No newline at end of file diff --git a/Source/DTFluxPursuitSystem/Private/DTFluxPursuitSystemModule.cpp b/Source/DTFluxPursuitSystem/Private/DTFluxPursuitSystemModule.cpp deleted file mode 100644 index df27623..0000000 --- a/Source/DTFluxPursuitSystem/Private/DTFluxPursuitSystemModule.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "DTFluxPursuitSystemModule.h" - -#define LOCTEXT_NAMESPACE "FDTFluxPursuitSystemModule" - -DEFINE_LOG_CATEGORY(logDTFluxPursuitSystem); - -void FDTFluxPursuitSystem::StartupModule() -{ - -} - -void FDTFluxPursuitSystem::ShutdownModule() -{ - -} - -#undef LOCTEXT_NAMESPACE - -IMPLEMENT_MODULE(FDTFluxPursuitSystem, DTFluxPursuitSystem) \ No newline at end of file diff --git a/Source/DTFluxPursuitSystem/Public/DTFluxPursuitSystemModule.h b/Source/DTFluxPursuitSystem/Public/DTFluxPursuitSystemModule.h deleted file mode 100644 index 17f21ad..0000000 --- a/Source/DTFluxPursuitSystem/Public/DTFluxPursuitSystemModule.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "Modules/ModuleManager.h" - -DECLARE_LOG_CATEGORY_EXTERN(logDTFluxPursuitSystem, All, All) - -class DTFLUXPURSUITSYSTEM_API FDTFluxPursuitSystem : public IModuleInterface -{ -public: - virtual void StartupModule() override; - virtual void ShutdownModule() override; -}; diff --git a/Source/DTFluxUtilities/DTFluxUtilities.Build.cs b/Source/DTFluxUtilities/DTFluxUtilities.Build.cs index d63fbca..7564a76 100644 --- a/Source/DTFluxUtilities/DTFluxUtilities.Build.cs +++ b/Source/DTFluxUtilities/DTFluxUtilities.Build.cs @@ -20,7 +20,8 @@ public class DTFluxUtilities : ModuleRules "Engine", "Slate", "SlateCore", - "DTFluxCore" + "DTFluxCore", + "DTFluxCoreSubsystem", } ); }