Compare commits
4 Commits
feature/Ra
...
d92ca63ea4
| Author | SHA1 | Date | |
|---|---|---|---|
| d92ca63ea4 | |||
| f1f300a351 | |||
| 73413e44b4 | |||
| bc6a928312 |
@ -31,6 +31,7 @@ public class DTFluxAPIStatus : ModuleRules
|
|||||||
"DTFluxCoreSubsystem",
|
"DTFluxCoreSubsystem",
|
||||||
"InputCore",
|
"InputCore",
|
||||||
"OutputLog",
|
"OutputLog",
|
||||||
|
"ToolMenus",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,9 +14,9 @@ FText DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::StatusTabDisplayName = FText::
|
|||||||
|
|
||||||
void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::StartupModule()
|
void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::StartupModule()
|
||||||
{
|
{
|
||||||
|
FDTFluxStatusStyle::RegisterStyle();
|
||||||
InitMenuExtension();
|
InitMenuExtension();
|
||||||
RegisterStatusTab();
|
RegisterStatusTab();
|
||||||
FDTFluxStatusStyle::RegisterStyle();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -25,45 +25,109 @@ void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::StartupModule()
|
|||||||
|
|
||||||
void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::InitMenuExtension()
|
void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::InitMenuExtension()
|
||||||
{
|
{
|
||||||
FLevelEditorModule& LevelEditorModule =
|
// FLevelEditorModule& LevelEditorModule =
|
||||||
FModuleManager::LoadModuleChecked<FLevelEditorModule>(TEXT("LevelEditor"));
|
// FModuleManager::LoadModuleChecked<FLevelEditorModule>(TEXT("LevelEditor"));
|
||||||
// FDTFluxAPIModule& DTFluxApi =
|
// // FDTFluxAPIModule& DTFluxApi =
|
||||||
// FModuleManager::LoadModuleChecked<FDTFluxAPIModule>(TEXT("DTFluxAPI"));
|
// // FModuleManager::LoadModuleChecked<FDTFluxAPIModule>(TEXT("DTFluxAPI"));
|
||||||
const TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
|
// const TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
|
||||||
|
//
|
||||||
|
// MenuExtender->AddMenuBarExtension(
|
||||||
|
// "Help",
|
||||||
|
// EExtensionHook::Before,
|
||||||
|
// nullptr,
|
||||||
|
// FMenuBarExtensionDelegate::CreateRaw(this, &FDTFluxAPIStatusModule::AddMenu)
|
||||||
|
// );
|
||||||
|
// LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
|
||||||
|
|
||||||
MenuExtender->AddMenuBarExtension(
|
UToolMenus::RegisterStartupCallback(
|
||||||
"Help",
|
FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FDTFluxAPIStatusModule::RegisterMenus)
|
||||||
EExtensionHook::Before,
|
|
||||||
nullptr,
|
|
||||||
FMenuBarExtensionDelegate::CreateRaw(this, &FDTFluxAPIStatusModule::AddMenu)
|
|
||||||
);
|
|
||||||
LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::AddMenu(FMenuBarBuilder& MenuBarBuilder)
|
|
||||||
{
|
|
||||||
MenuBarBuilder.AddPullDownMenu(
|
|
||||||
FText::FromString("DTFlux"),
|
|
||||||
FText::FromString("DTFlux API Tools"),
|
|
||||||
FNewMenuDelegate::CreateRaw(this, &FDTFluxAPIStatusModule::FillMenu)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::FillMenu(FMenuBuilder& MenuBuilder)
|
|
||||||
{
|
|
||||||
MenuBuilder.BeginSection(NAME_None, FText::FromString("DTFlux API"));
|
|
||||||
MenuBuilder.AddMenuEntry(
|
|
||||||
FText::FromString("Status"),
|
|
||||||
FText::FromString("Launch DTFlux Status"),
|
|
||||||
FSlateIcon(FDTFluxStatusStyle::GetStyleSetName(), "LevelEditor.Tab.Icon"),
|
|
||||||
FExecuteAction::CreateRaw(this, &FDTFluxAPIStatusModule::OnButtonClicked)
|
|
||||||
);
|
);
|
||||||
MenuBuilder.EndSection();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FDTFluxAPIStatusModule::RegisterMenus()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxStatus, Warning, TEXT("Creating DTFlux menu"));
|
||||||
|
|
||||||
|
// 1. Enregistrer le menu DTFlux
|
||||||
|
UToolMenu* DTFluxMenu = UToolMenus::Get()->RegisterMenu("DTFlux.MainMenu");
|
||||||
|
if (DTFluxMenu)
|
||||||
|
{
|
||||||
|
CreateSubmenu(DTFluxMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Ajouter ce menu à la barre principale
|
||||||
|
if (UToolMenu* MainMenu = UToolMenus::Get()->ExtendMenu("MainFrame.MainMenu"))
|
||||||
|
{
|
||||||
|
FToolMenuSection& DTFluxMenuSection = MainMenu->FindOrAddSection("DTFlux");
|
||||||
|
DTFluxMenuSection.Label = FText::FromString("DTFlux");
|
||||||
|
|
||||||
|
DTFluxMenuSection.AddSubMenu(
|
||||||
|
"DTFluxSubmenu",
|
||||||
|
FText::FromString("DTFlux"),
|
||||||
|
FText::FromString("DTFlux API Tools"),
|
||||||
|
FNewToolMenuDelegate::CreateLambda([](UToolMenu* Menu)
|
||||||
|
{
|
||||||
|
// Référencer le menu enregistré
|
||||||
|
if (UToolMenu* RegisteredMenu = UToolMenus::Get()->FindMenu("DTFlux.MainMenu"))
|
||||||
|
{
|
||||||
|
// Copier la structure du menu enregistré
|
||||||
|
for (const FToolMenuSection& Section : RegisteredMenu->Sections)
|
||||||
|
{
|
||||||
|
Menu->Sections.Add(Section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.Tab.Icon")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxAPIStatusModule::CreateSubmenu(UToolMenu* Menu)
|
||||||
|
{
|
||||||
|
FToolMenuSection& DTFluxAPISection = Menu->FindOrAddSection("DTFluxAPI");
|
||||||
|
DTFluxAPISection.Label = FText::FromString("DTFlux API");
|
||||||
|
// Cette section est vide pour le moment, prête pour de futurs boutons
|
||||||
|
|
||||||
|
|
||||||
|
// Section 2 : Tools
|
||||||
|
FToolMenuSection& ToolsSection = Menu->FindOrAddSection("Tools");
|
||||||
|
ToolsSection.Label = FText::FromString("Tools");
|
||||||
|
|
||||||
|
|
||||||
|
// Ajouter le bouton Status dans la section Tools
|
||||||
|
DTFluxAPISection.AddMenuEntry(
|
||||||
|
"DTFluxStatus",
|
||||||
|
FText::FromString("DTFlux Status"),
|
||||||
|
FText::FromString("Launch DTFlux Status Control Panel"),
|
||||||
|
FSlateIcon(FDTFluxStatusStyle::GetStyleSetName(), "LevelEditor.Tab.Icon"),
|
||||||
|
FUIAction(FExecuteAction::CreateRaw(this, &FDTFluxAPIStatusModule::OnButtonClicked))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::AddMenu(FMenuBarBuilder& MenuBarBuilder)
|
||||||
|
// {
|
||||||
|
// MenuBarBuilder.AddPullDownMenu(
|
||||||
|
// FText::FromString("DTFlux"),
|
||||||
|
// FText::FromString("DTFlux API Tools"),
|
||||||
|
// FNewMenuDelegate::CreateRaw(this, &FDTFluxAPIStatusModule::FillMenu)
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::FillMenu(FMenuBuilder& MenuBuilder)
|
||||||
|
// {
|
||||||
|
// MenuBuilder.BeginSection(NAME_None, FText::FromString("DTFlux API"));
|
||||||
|
// MenuBuilder.AddMenuEntry(
|
||||||
|
// FText::FromString("Status"),
|
||||||
|
// FText::FromString("Launch DTFlux Status"),
|
||||||
|
// FSlateIcon(FDTFluxStatusStyle::GetStyleSetName(), "LevelEditor.Tab.Icon"),
|
||||||
|
// FExecuteAction::CreateRaw(this, &FDTFluxAPIStatusModule::OnButtonClicked)
|
||||||
|
// );
|
||||||
|
// MenuBuilder.EndSection();
|
||||||
|
// }
|
||||||
|
|
||||||
void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::OnButtonClicked()
|
void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::OnButtonClicked()
|
||||||
{
|
{
|
||||||
FGlobalTabmanager::Get()->TryInvokeTab(StatusTabId);
|
FGlobalTabmanager::Get()->TryInvokeTab(StatusTabId);
|
||||||
|
|||||||
@ -416,7 +416,7 @@ FSlateColor SDTFluxStatusWidget::GetComboItemRankingColor(const TSharedPtr<FComb
|
|||||||
|
|
||||||
FReply SDTFluxStatusWidget::OnRankingButtonClicked() const
|
FReply SDTFluxStatusWidget::OnRankingButtonClicked() const
|
||||||
{
|
{
|
||||||
if (DTFluxNetwork)
|
if (DTFluxCore)
|
||||||
{
|
{
|
||||||
// Exemple d'envoi de requête basée sur la sélection
|
// Exemple d'envoi de requête basée sur la sélection
|
||||||
int ForContest = SelectedContestComboBoxItem.IsValid() ? SelectedContestComboBoxItem->ContestId : -1;
|
int ForContest = SelectedContestComboBoxItem.IsValid() ? SelectedContestComboBoxItem->ContestId : -1;
|
||||||
@ -432,13 +432,19 @@ FReply SDTFluxStatusWidget::OnRankingButtonClicked() const
|
|||||||
if (ForStage == -1)
|
if (ForStage == -1)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxStatus, Warning, TEXT("Stage not selected !!!! Requesting contest Ranking"));
|
UE_LOG(logDTFluxStatus, Warning, TEXT("Stage not selected !!!! Requesting contest Ranking"));
|
||||||
RequestType = EDTFluxApiDataType::ContestRanking;
|
DTFluxCore->TrackedRequestContestRankings({ForContest});
|
||||||
DTFluxNetwork->SendRequest(RequestType, ForContest);
|
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
}
|
}
|
||||||
RequestType = ForSplit == -1 ? EDTFluxApiDataType::StageRanking : EDTFluxApiDataType::SplitRanking;
|
if (ForSplit == -1)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxStatus, Warning, TEXT("Split not selected !!!! Requesting stage Ranking"));
|
||||||
|
FDTFluxStageKey StageKey = {ForContest, ForStage};
|
||||||
|
DTFluxCore->TrackedRequestStageRankings({StageKey});
|
||||||
|
return FReply::Handled();
|
||||||
|
}
|
||||||
|
FDTFluxSplitKey SplitKey = {ForContest, ForStage, ForSplit};
|
||||||
|
DTFluxCore->TrackedRequestSplitRankings({SplitKey});
|
||||||
UE_LOG(logDTFluxStatus, Warning, TEXT("Requesting %s Ranking"), *UEnum::GetValueAsString(RequestType));
|
UE_LOG(logDTFluxStatus, Warning, TEXT("Requesting %s Ranking"), *UEnum::GetValueAsString(RequestType));
|
||||||
DTFluxNetwork->SendRequest(RequestType, ForContest, ForStage, ForSplit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
@ -13,7 +13,7 @@ TSharedPtr<ISlateStyle> FDTFluxStatusStyle::StyleSet = nullptr;
|
|||||||
|
|
||||||
void FDTFluxStatusStyle::RegisterStyle()
|
void FDTFluxStatusStyle::RegisterStyle()
|
||||||
{
|
{
|
||||||
if(StyleSet.IsValid()) return;
|
if (StyleSet.IsValid()) return;
|
||||||
|
|
||||||
StyleSet = Create();
|
StyleSet = Create();
|
||||||
FSlateStyleRegistry::RegisterSlateStyle(*StyleSet);
|
FSlateStyleRegistry::RegisterSlateStyle(*StyleSet);
|
||||||
@ -21,14 +21,12 @@ void FDTFluxStatusStyle::RegisterStyle()
|
|||||||
|
|
||||||
void FDTFluxStatusStyle::UnregisterStyle()
|
void FDTFluxStatusStyle::UnregisterStyle()
|
||||||
{
|
{
|
||||||
if(StyleSet.IsValid())
|
if (StyleSet.IsValid())
|
||||||
{
|
{
|
||||||
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet);
|
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet);
|
||||||
ensure(StyleSet.IsUnique());
|
ensure(StyleSet.IsUnique());
|
||||||
StyleSet.Reset();
|
StyleSet.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxStatusStyle::ReloadTextures()
|
void FDTFluxStatusStyle::ReloadTextures()
|
||||||
@ -38,9 +36,8 @@ void FDTFluxStatusStyle::ReloadTextures()
|
|||||||
TSharedPtr<ISlateStyle> FDTFluxStatusStyle::Create()
|
TSharedPtr<ISlateStyle> FDTFluxStatusStyle::Create()
|
||||||
{
|
{
|
||||||
TSharedPtr<FSlateStyleSet> Style = MakeShareable(new FSlateStyleSet("DTFluxAPIStatusStyle"));
|
TSharedPtr<FSlateStyleSet> Style = MakeShareable(new FSlateStyleSet("DTFluxAPIStatusStyle"));
|
||||||
Style->SetContentRoot(IPluginManager::Get().FindPlugin("DTFluxAPI")->GetBaseDir()/TEXT("Resources"));
|
Style->SetContentRoot(IPluginManager::Get().FindPlugin("DTFluxAPI")->GetBaseDir() / TEXT("Resources"));
|
||||||
|
|
||||||
Style->Set("LevelEditor.Tab.Icon", new IMAGE_BRUSH_SVG("DTFluxServerStatusWhite", FVector2d(16)) );
|
Style->Set("LevelEditor.Tab.Icon", new IMAGE_BRUSH_SVG("DTFluxServerStatusWhite", FVector2d(16)));
|
||||||
return Style;
|
return Style;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,10 +13,11 @@ public:
|
|||||||
|
|
||||||
#pragma region MenuExtention
|
#pragma region MenuExtention
|
||||||
void InitMenuExtension();
|
void InitMenuExtension();
|
||||||
void AddMenu(FMenuBarBuilder& MenuBarBuilder);
|
void RegisterMenus();
|
||||||
void FillMenu(FMenuBuilder& MenuBuilder);
|
void CreateSubmenu(UToolMenu* Menu);
|
||||||
|
// void AddMenu(FMenuBarBuilder& MenuBarBuilder);
|
||||||
|
// void FillMenu(FMenuBuilder& MenuBuilder);
|
||||||
void OnButtonClicked();
|
void OnButtonClicked();
|
||||||
// void OnWsEvent(TEnumAsByte<EDTFluxWsStatus> WsResponseEvent) const;
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region EditorTab
|
#pragma region EditorTab
|
||||||
@ -25,7 +26,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
static FName StatusTabId;
|
static FName StatusTabId;
|
||||||
static FText StatusTabDisplayName;
|
static FText StatusTabDisplayName;
|
||||||
|
|
||||||
TSharedPtr<class SDTFluxStatusWidget> StatusWidget;
|
TSharedPtr<class SDTFluxStatusWidget> StatusWidget;
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include "FileHelpers.h"
|
#include "FileHelpers.h"
|
||||||
#include "Assets/DTFluxModelAsset.h"
|
#include "Assets/DTFluxModelAsset.h"
|
||||||
#include "Subsystems/DTFluxNetworkSubsystem.h"
|
#include "Subsystems/DTFluxNetworkSubsystem.h"
|
||||||
|
#include "Struct/DTFluxServerResponseStruct.h"
|
||||||
#include "UObject/SavePackage.h"
|
#include "UObject/SavePackage.h"
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
void UDTFluxCoreSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||||
@ -32,7 +33,7 @@ void UDTFluxCoreSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|||||||
{
|
{
|
||||||
RegisterDelegates();
|
RegisterDelegates();
|
||||||
}
|
}
|
||||||
PursuitManager = NewObject<UDTFluxPursuitManager>();
|
PursuitManager = NewObject<UDTFluxPursuitManager>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::Deinitialize()
|
void UDTFluxCoreSubsystem::Deinitialize()
|
||||||
@ -52,6 +53,84 @@ void UDTFluxCoreSubsystem::SaveDataStorage()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UDTFluxCoreSubsystem::ProcessTrackedResponse(FDTFluxServerResponse& InResponse)
|
||||||
|
{
|
||||||
|
switch (InResponse.GetResponseType())
|
||||||
|
{
|
||||||
|
case EDTFluxApiDataType::ContestRanking:
|
||||||
|
{
|
||||||
|
FDTFluxContestRankings Rankings;
|
||||||
|
if (InResponse.ParseContestRanking(Rankings))
|
||||||
|
{
|
||||||
|
ProcessContestRanking(Rankings);
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestRanking added for Contest %s"),
|
||||||
|
*Rankings.ContestName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Unable to parse ContestRanking"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EDTFluxApiDataType::StageRanking:
|
||||||
|
{
|
||||||
|
FDTFluxStageRankings Rankings;
|
||||||
|
if (InResponse.ParseStageRanking(Rankings))
|
||||||
|
{
|
||||||
|
ProcessStageRanking(Rankings);
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestRanking added for Stage %i of Contest %i"),
|
||||||
|
Rankings.StageId, Rankings.ContestId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Unable to parse StageRanking"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EDTFluxApiDataType::SplitRanking:
|
||||||
|
{
|
||||||
|
FDTFluxSplitRankings Rankings;
|
||||||
|
if (InResponse.ParseSplitRanking(Rankings))
|
||||||
|
{
|
||||||
|
ProcessSplitRanking(Rankings);
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning,
|
||||||
|
TEXT("ContestRanking added for Split %i of Stage %i of Contest %i"),
|
||||||
|
Rankings.SplitId, Rankings.StageId, Rankings.ContestId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Unable to parse SplitRanking"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EDTFluxApiDataType::RaceData:
|
||||||
|
{
|
||||||
|
FDTFluxRaceData RaceData;
|
||||||
|
if (InResponse.ParseRaceData(RaceData))
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("RaceDataDefinition added for Contest %s"),
|
||||||
|
*RaceData.Datas[0].Name);
|
||||||
|
ProcessRaceData(RaceData);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EDTFluxApiDataType::TeamList:
|
||||||
|
{
|
||||||
|
FDTFluxTeamListDefinition TeamList;
|
||||||
|
if (InResponse.ParseTeamList(TeamList))
|
||||||
|
{
|
||||||
|
ProcessTeamList(TeamList);
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Log, TEXT("Process TeamList"))
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Unknown DataType %s"),
|
||||||
|
*UEnum::GetValueAsString(InResponse.GetResponseType()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::RegisterDelegates()
|
void UDTFluxCoreSubsystem::RegisterDelegates()
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
@ -84,9 +163,6 @@ void UDTFluxCoreSubsystem::RegisterDelegates()
|
|||||||
&UDTFluxCoreSubsystem::ProcessSplitRanking
|
&UDTFluxCoreSubsystem::ProcessSplitRanking
|
||||||
);
|
);
|
||||||
|
|
||||||
// ⚠️ ATTENTION : Vous avez un doublon ici !
|
|
||||||
// NetworkSubsystem->OnReceivedTeamUpdate().BindUFunction(this, "ProcessTeamList");
|
|
||||||
|
|
||||||
NetworkSubsystem->OnReceivedTeamStatusUpdate().BindUObject(
|
NetworkSubsystem->OnReceivedTeamStatusUpdate().BindUObject(
|
||||||
this,
|
this,
|
||||||
&UDTFluxCoreSubsystem::ProcessTeamStatusUpdate
|
&UDTFluxCoreSubsystem::ProcessTeamStatusUpdate
|
||||||
@ -151,21 +227,24 @@ void UDTFluxCoreSubsystem::ProcessContestRanking(const FDTFluxContestRankings& C
|
|||||||
DataStorage->AddContestRanking(NewContestRankings);
|
DataStorage->AddContestRanking(NewContestRankings);
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestRankings added for Contest %s"),
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestRankings added for Contest %s"),
|
||||||
*NewContestRankings.ContestName);
|
*NewContestRankings.ContestName);
|
||||||
SaveDataStorage();
|
if (bShouldKeepRankings)
|
||||||
|
{
|
||||||
|
SaveDataStorage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::ProcessStageRanking(const FDTFluxStageRankings& StageRankings)
|
void UDTFluxCoreSubsystem::ProcessStageRanking(const FDTFluxStageRankings& StageRankings)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received StageRankings with %i Items"), StageRankings.Rankings.Num());
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received StageRankings with %i Items"), StageRankings.Rankings.Num());
|
||||||
DataStorage->UpdateOrCreateStageRanking(StageRankings);
|
DataStorage->UpdateOrCreateStageRanking(StageRankings);
|
||||||
SaveDataStorage();
|
if (bShouldKeepRankings) { SaveDataStorage(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::ProcessSplitRanking(const FDTFluxSplitRankings& SplitRankings)
|
void UDTFluxCoreSubsystem::ProcessSplitRanking(const FDTFluxSplitRankings& SplitRankings)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received SplitRanking with %i Items"), SplitRankings.Rankings.Num());
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received SplitRanking with %i Items"), SplitRankings.Rankings.Num());
|
||||||
DataStorage->UpdateOrCreateSplitRanking(SplitRankings);
|
DataStorage->UpdateOrCreateSplitRanking(SplitRankings);
|
||||||
SaveDataStorage();
|
if (bShouldKeepRankings) { SaveDataStorage(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::ProcessTeamStatusUpdate(const FDTFluxTeamStatusUpdate& NewParticipantStatus)
|
void UDTFluxCoreSubsystem::ProcessTeamStatusUpdate(const FDTFluxTeamStatusUpdate& NewParticipantStatus)
|
||||||
@ -205,64 +284,182 @@ void UDTFluxCoreSubsystem::SendRequest(const FString& Message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::SendTeamListRequest()
|
bool UDTFluxCoreSubsystem::GetContestRankings(const int ContestId,
|
||||||
|
FDTFluxContestRankings& OutContestRankings)
|
||||||
|
{
|
||||||
|
if (DataStorage->ContestRankings.Contains(ContestId))
|
||||||
|
{
|
||||||
|
OutContestRankings = DataStorage->ContestRankings[ContestId];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (NetworkSubsystem)
|
||||||
|
{
|
||||||
|
TArray<int> TackedContestIds = {ContestId};
|
||||||
|
TrackedRequestContestRankings(TackedContestIds);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("NetworkSubsystem unavailable"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxCoreSubsystem::GetStageRankings(const int ContestId, const int StageId,
|
||||||
|
FDTFluxStageRankings& OutStageRankings)
|
||||||
|
{
|
||||||
|
return GetStageRankingsWithKey(FDTFluxStageKey(ContestId, StageId), OutStageRankings);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxCoreSubsystem::GetSplitRankings(const int ContestId, const int StageId, const int SplitId,
|
||||||
|
FDTFluxSplitRankings& OutSplitRankings)
|
||||||
|
{
|
||||||
|
return GetSplitRankingsWithKey(FDTFluxSplitKey(ContestId, StageId, SplitId), OutSplitRankings);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxCoreSubsystem::GetStageRankingsWithKey(const FDTFluxStageKey StageKey,
|
||||||
|
FDTFluxStageRankings& OutStageRankings, const bool bShouldUseCached)
|
||||||
|
{
|
||||||
|
//We Have the data
|
||||||
|
if (DataStorage->StageRankings.Contains(StageKey) && bShouldUseCached)
|
||||||
|
{
|
||||||
|
OutStageRankings = DataStorage->StageRankings[StageKey];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (NetworkSubsystem)
|
||||||
|
{
|
||||||
|
TArray<FDTFluxStageKey> TackedStageKeys = {StageKey};
|
||||||
|
TrackedRequestStageRankings(TackedStageKeys);
|
||||||
|
OutStageRankings = FDTFluxStageRankings();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("NetworkSubsystem unavailable"))
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxCoreSubsystem::GetSplitRankingsWithKey(const FDTFluxSplitKey SplitKey,
|
||||||
|
FDTFluxSplitRankings& OutSplitRankings, const bool bShouldUseCached)
|
||||||
|
{
|
||||||
|
//We Have the data
|
||||||
|
if (DataStorage->SplitRankings.Contains(SplitKey) && bShouldUseCached)
|
||||||
|
{
|
||||||
|
OutSplitRankings = DataStorage->SplitRankings[SplitKey];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (NetworkSubsystem)
|
||||||
|
{
|
||||||
|
TArray<FDTFluxSplitKey> TackedSplitKey = {SplitKey};
|
||||||
|
TrackedRequestSplitRankings(TackedSplitKey);
|
||||||
|
OutSplitRankings = FDTFluxSplitRankings();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("NetworkSubsystem unavailable"))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestContestRankings(const TArray<int> ForContests)
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
{
|
{
|
||||||
NetworkSubsystem->SendRequest(EDTFluxRequestType::TeamList);
|
TArray<FGuid> RequestIds;
|
||||||
|
FOnDTFluxRequestSuccess OnSuccess = FOnDTFluxRequestSuccess::CreateLambda(
|
||||||
|
[this](const FDTFluxTrackedRequest& Request)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Log, TEXT("ContestRanking Request %s %s Success"),
|
||||||
|
*Request.RequestId.ToString(), *UEnum::GetValueAsString(Request.RequestType));
|
||||||
|
if (Request.ParsedResponse.IsSet())
|
||||||
|
{
|
||||||
|
ProcessTrackedResponse(*Request.ParsedResponse.GetValue());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FOnDTFluxRequestError OnError = FOnDTFluxRequestError::CreateLambda(
|
||||||
|
[this](const FDTFluxTrackedRequest& InReq, const FString& InError)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Log, TEXT("ContestRanking Request [%s] Error %s"),
|
||||||
|
*InReq.RequestId.ToString(), *InError);
|
||||||
|
});
|
||||||
|
// if Contest is not ended
|
||||||
|
for (auto ContestId : ForContests)
|
||||||
|
{
|
||||||
|
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::ContestRanking,
|
||||||
|
ContestId, -1, -1, OnSuccess, OnError);
|
||||||
|
RequestIds.Add(ContestRequest);
|
||||||
|
}
|
||||||
|
return RequestIds;
|
||||||
}
|
}
|
||||||
|
return TArray<FGuid>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::SendRaceDataRequest()
|
TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestStageRankings(const TArray<FDTFluxStageKey> ForStages)
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
{
|
{
|
||||||
NetworkSubsystem->SendRequest(EDTFluxRequestType::RaceData);
|
TArray<FGuid> RequestIds;
|
||||||
|
FOnDTFluxRequestSuccess OnSuccess = FOnDTFluxRequestSuccess::CreateLambda(
|
||||||
|
[this](const FDTFluxTrackedRequest& Request)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Stage Request %s %s Success"),
|
||||||
|
*Request.RequestId.ToString(), *UEnum::GetValueAsString(Request.RequestType));
|
||||||
|
if (Request.ParsedResponse.IsSet())
|
||||||
|
{
|
||||||
|
ProcessTrackedResponse(*Request.ParsedResponse.GetValue());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FOnDTFluxRequestError OnError = FOnDTFluxRequestError::CreateLambda(
|
||||||
|
[this](const FDTFluxTrackedRequest& InReq, const FString& InError)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("StageRanking Request [%s] Error %s"),
|
||||||
|
*InReq.RequestId.ToString(), *InError);
|
||||||
|
});
|
||||||
|
// if Contest is not ended
|
||||||
|
for (auto StageKey : ForStages)
|
||||||
|
{
|
||||||
|
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::StageRanking,
|
||||||
|
StageKey.ContestId, StageKey.StageId, -1, OnSuccess, OnError);
|
||||||
|
RequestIds.Add(ContestRequest);
|
||||||
|
}
|
||||||
|
return RequestIds;
|
||||||
}
|
}
|
||||||
|
return TArray<FGuid>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::SendContestRankingRequest(int InContestId)
|
TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestSplitRankings(const TArray<FDTFluxSplitKey> ForSplits)
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
{
|
{
|
||||||
NetworkSubsystem->SendRequest(EDTFluxRequestType::ContestRanking, InContestId);
|
TArray<FGuid> RequestIds;
|
||||||
|
FOnDTFluxRequestSuccess OnSuccess = FOnDTFluxRequestSuccess::CreateLambda(
|
||||||
|
[this](const FDTFluxTrackedRequest& Request)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Stage Request %s %s Success"),
|
||||||
|
*Request.RequestId.ToString(), *UEnum::GetValueAsString(Request.RequestType));
|
||||||
|
if (Request.ParsedResponse.IsSet())
|
||||||
|
{
|
||||||
|
ProcessTrackedResponse(*Request.ParsedResponse.GetValue());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FOnDTFluxRequestError OnError = FOnDTFluxRequestError::CreateLambda(
|
||||||
|
[this](const FDTFluxTrackedRequest& InReq, const FString& InError)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("StageRanking Request [%s] Error %s"),
|
||||||
|
*InReq.RequestId.ToString(), *InError);
|
||||||
|
});
|
||||||
|
// if Contest is not ended
|
||||||
|
for (auto SplitKey : ForSplits)
|
||||||
|
{
|
||||||
|
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::SplitRanking,
|
||||||
|
SplitKey.ContestId, SplitKey.StageId, SplitKey.SplitId, OnSuccess, OnError);
|
||||||
|
RequestIds.Add(ContestRequest);
|
||||||
|
}
|
||||||
|
return RequestIds;
|
||||||
}
|
}
|
||||||
}
|
return TArray<FGuid>();
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::SendStageRankingRequest(int InContestId, int InStageId, bool bShouldIncludeSplitRanking)
|
|
||||||
{
|
|
||||||
// TODO Implement this
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::RequestAllStageRankingOfContest(int InContestId, int InStageId,
|
|
||||||
bool bShouldIncludeSplitRanking)
|
|
||||||
{
|
|
||||||
// TODO Implement this
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::SendSplitRankingRequest(int InContestId, int InStageId, int InSplitId)
|
|
||||||
{
|
|
||||||
// TODO Implement this
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::RequestAllSplitRankingOfContest(int InContestId, int InStageId)
|
|
||||||
{
|
|
||||||
// TODO Implement this
|
|
||||||
}
|
|
||||||
|
|
||||||
FDTFluxStageRankings UDTFluxCoreSubsystem::GetStageRankings(FDTFluxStageKey StageKey)
|
|
||||||
{
|
|
||||||
if (DataStorage->StageRankings.Contains(StageKey))
|
|
||||||
{
|
|
||||||
return DataStorage->StageRankings[StageKey];
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Cannot find StageRankings for key [%s]"), *StageKey.GetDisplayName());
|
|
||||||
return FDTFluxStageRankings();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::RequestAllSplitRankingOfStage(int InContestId, int InStageId, int InSplitId)
|
|
||||||
{
|
|
||||||
// TODO Implement this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FDTFluxParticipant UDTFluxCoreSubsystem::GetParticipant(int InBib)
|
const FDTFluxParticipant UDTFluxCoreSubsystem::GetParticipant(int InBib)
|
||||||
@ -274,11 +471,6 @@ const FDTFluxParticipant UDTFluxCoreSubsystem::GetParticipant(int InBib)
|
|||||||
return FDTFluxParticipant();
|
return FDTFluxParticipant();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::RefreshStorage()
|
|
||||||
{
|
|
||||||
// TODO Implement this
|
|
||||||
}
|
|
||||||
|
|
||||||
TArray<int> UDTFluxCoreSubsystem::GetCurrentContestsId()
|
TArray<int> UDTFluxCoreSubsystem::GetCurrentContestsId()
|
||||||
{
|
{
|
||||||
return GetContestsIdForTime(FDateTime::Now());
|
return GetContestsIdForTime(FDateTime::Now());
|
||||||
@ -289,7 +481,7 @@ TArray<FDTFluxContest> UDTFluxCoreSubsystem::GetCurrentContests()
|
|||||||
return GetContestsForTime(FDateTime::Now());
|
return GetContestsForTime(FDateTime::Now());
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<int> UDTFluxCoreSubsystem::GetContestsIdForTime(const FDateTime Time)
|
TArray<int> UDTFluxCoreSubsystem::GetContestsIdForTime(const FDateTime Time) const
|
||||||
{
|
{
|
||||||
TArray<int> Contests;
|
TArray<int> Contests;
|
||||||
for (const auto& Pair : DataStorage->Contests)
|
for (const auto& Pair : DataStorage->Contests)
|
||||||
@ -347,22 +539,3 @@ TArray<FDTFluxContest> UDTFluxCoreSubsystem::GetContests()
|
|||||||
}
|
}
|
||||||
return TArray<FDTFluxContest>();
|
return TArray<FDTFluxContest>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::LaunchPursuitSequenceFor(const TArray<int> ContestIds)
|
|
||||||
{
|
|
||||||
TArray<FDTFluxContest> Contests = TArray<FDTFluxContest>();
|
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
#include "DTFluxPursuitManager.h"
|
#include "DTFluxPursuitManager.h"
|
||||||
|
|
||||||
|
#include <ImportExport.h>
|
||||||
|
|
||||||
|
#include "DTFluxCoreSubsystem.h"
|
||||||
#include "DTFluxCoreSubsystemModule.h"
|
#include "DTFluxCoreSubsystemModule.h"
|
||||||
|
|
||||||
UDTFluxPursuitManager::UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer):
|
UDTFluxPursuitManager::UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer):
|
||||||
@ -10,78 +13,96 @@ UDTFluxPursuitManager::UDTFluxPursuitManager(const FObjectInitializer& ObjectIni
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO : Add way to pass MaxSimultaneousPursuit and MassStartDelay
|
void UDTFluxPursuitManager::InitPursuit(const TArray<int> InContestIds, const int MaxSimultaneousPursuit)
|
||||||
// For now it's done in UPROPERTIES
|
|
||||||
void UDTFluxPursuitManager::LaunchPursuitSequenceFor(const TArray<FDTFluxContest> InContests)
|
|
||||||
{
|
{
|
||||||
if (InitSubSystems())
|
CoreSubsystem = Cast<UDTFluxCoreSubsystem>(GetOuter());
|
||||||
|
if (!CoreSubsystem)
|
||||||
{
|
{
|
||||||
for (const auto Contest : InContests)
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("CoreSubsystem is not Available !!!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AllRankings.Reset();
|
||||||
|
for (const auto& ContestId : InContestIds)
|
||||||
|
{
|
||||||
|
FDTFluxContest Contest;
|
||||||
|
if (CoreSubsystem->GetContestForId(ContestId, Contest))
|
||||||
{
|
{
|
||||||
FRequestData RequestData;
|
BindRankings();
|
||||||
RequestData.ContestId = Contest.ContestId;
|
FDTFluxStageKey StageKey = FDTFluxStageKey(ContestId, Contest.GetLastStageId());
|
||||||
uint8 StageId = Contest.Stages.Last().StageId;
|
FDTFluxStageRankings TempStageRankings;
|
||||||
FGuid Guid = NetworkSubsystem->SendTrackedRequestWithCallback(EDTFluxApiDataType::StageRanking,
|
//Obtenir les ranking Frais.
|
||||||
Contest.ContestId, StageId, -1,
|
CoreSubsystem->GetStageRankingsWithKey(StageKey, TempStageRankings, false);
|
||||||
FOnDTFluxTrackedRequestResponse::CreateUObject(
|
PendingStageRanking.Add(StageKey, false);
|
||||||
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)
|
void UDTFluxPursuitManager::SetPursuitInfoIsMassStart(FDTFluxPursuitGroup NextFocusGroup)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Log,
|
for (auto& Pursuit : NextFocusGroup.PursuitGroup)
|
||||||
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;
|
Pursuit.bIsMassStart = Pursuit.StartTime >= MassStartTime;
|
||||||
FRequestData FoundData;
|
}
|
||||||
if (Response.ParseStageRankingResponse(Rankings))
|
}
|
||||||
|
|
||||||
|
void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext,
|
||||||
|
TArray<FDTFluxPursuitInfo>& OutPursuitNext, bool& BIsFocusTruncate,
|
||||||
|
const int MaxSimultaneousPursuit)
|
||||||
|
{
|
||||||
|
if (MaxSimultaneousPursuit <= 0)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("MaxSimultaneousPursuit must be > 0"));
|
||||||
|
OutPursuitFocusNext = TArray<FDTFluxPursuitInfo>();
|
||||||
|
OutPursuitNext = TArray<FDTFluxPursuitInfo>();
|
||||||
|
BIsFocusTruncate = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bIsSequenceDone && MaxSimultaneousPursuit <= 0)
|
||||||
|
{
|
||||||
|
OutPursuitFocusNext = TArray<FDTFluxPursuitInfo>();
|
||||||
|
OutPursuitNext = TArray<FDTFluxPursuitInfo>();
|
||||||
|
BIsFocusTruncate = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OutPursuitFocusNext.Reset();
|
||||||
|
OutPursuitNext.Reset();
|
||||||
|
if (!GroupedPursuit.IsEmpty())
|
||||||
|
{
|
||||||
|
FDTFluxPursuitGroup NextFocusGroup = GroupedPursuit[0];
|
||||||
|
GroupedPursuit.RemoveAt(0);
|
||||||
|
SetPursuitInfoIsMassStart(NextFocusGroup);
|
||||||
|
OutPursuitFocusNext = NextFocusGroup.PursuitGroup;
|
||||||
|
bFocusIsTruncate = NextFocusGroup.PursuitGroup.Num() > 1;
|
||||||
|
for (int RemainingPursuitNum = MaxSimultaneousPursuit - 1; RemainingPursuitNum != 0;)
|
||||||
{
|
{
|
||||||
for (auto& PendingReq : PendingRequestData)
|
if (!GroupedPursuit.IsEmpty())
|
||||||
{
|
{
|
||||||
// Check for a matching PendingReq
|
FDTFluxPursuitGroup NextGroup = GroupedPursuit[0];
|
||||||
if (PendingReq.IsWaitingFor(RequestId, Rankings))
|
SetPursuitInfoIsMassStart(NextGroup);
|
||||||
|
if (NextGroup.PursuitGroup.Num() >= RemainingPursuitNum)
|
||||||
{
|
{
|
||||||
FoundData = PendingReq;
|
// extract the number we need
|
||||||
// A request Is Terminated
|
for (int i = 0; i < RemainingPursuitNum; i++)
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Log,
|
{
|
||||||
TEXT("UDTFluxPursuitManager::OnRequestResponse() Ranking for Stage %i is complete"),
|
FDTFluxPursuitInfo Pursuit = NextGroup.PursuitGroup[0];
|
||||||
Response.StageID)
|
OutPursuitNext.Add(Pursuit);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OutPursuitNext.Append(NextGroup.PursuitGroup);
|
||||||
|
RemainingPursuitNum -= NextGroup.PursuitGroup.Num();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (InitPursuit(FoundData))
|
else
|
||||||
{
|
{
|
||||||
OnPursuitSequenceReady.Broadcast(NextFocusPursuits, NextFocusPursuits, bFocusIsTruncate);
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
bool UDTFluxPursuitManager::InitSubSystems()
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
@ -92,22 +113,64 @@ bool UDTFluxPursuitManager::InitSubSystems()
|
|||||||
return NetworkSubsystem != nullptr;
|
return NetworkSubsystem != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxPursuitManager::InitPursuit(FRequestData Data)
|
bool UDTFluxPursuitManager::BindRankings()
|
||||||
{
|
{
|
||||||
//Clean Data
|
if (CoreSubsystem)
|
||||||
NextFocusPursuits.Empty();
|
{
|
||||||
NextPursuits.Empty();
|
if (!bIsRankingBounded)
|
||||||
PursuitGrouped.Empty();
|
{
|
||||||
TArray<FDTFluxDetailedRankingItem> AllRankings;
|
CoreSubsystem->OnRequestedStageRankings.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->OnRequestedStageRankings.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<FDTFluxPursuitInfo> AllPursuits;
|
TArray<FDTFluxPursuitInfo> AllPursuits;
|
||||||
TMap<FDateTime, FDTFluxPursuitGroup> TempGroups;
|
TMap<FDateTime, FDTFluxPursuitGroup> TempGroups;
|
||||||
|
bIsSequenceDone = false;
|
||||||
// Full the Array Of Rankings
|
// Full the Array Of Rankings
|
||||||
for (auto& KeyPair : Data.StageRankings)
|
for (auto& Ranking : AllRankings)
|
||||||
{
|
{
|
||||||
for (auto StageRanking : KeyPair.Value.Rankings)
|
for (auto StageRanking : Ranking.Rankings)
|
||||||
{
|
{
|
||||||
int ContestId = KeyPair.Value.ContestId;
|
int ContestId = Ranking.ContestId;
|
||||||
FDTFluxPursuitInfo PursuitInfo;
|
FDTFluxPursuitInfo PursuitInfo;
|
||||||
PursuitInfo.StartTime = StageRanking.StartTime;
|
PursuitInfo.StartTime = StageRanking.StartTime;
|
||||||
PursuitInfo.Bib = StageRanking.Bib;
|
PursuitInfo.Bib = StageRanking.Bib;
|
||||||
@ -115,11 +178,6 @@ bool UDTFluxPursuitManager::InitPursuit(FRequestData Data)
|
|||||||
AllPursuits.Add(PursuitInfo);
|
AllPursuits.Add(PursuitInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sort Rankings
|
|
||||||
// AllPursuits.Sort([](const FDTFluxPursuitInfo& A, const FDTFluxPursuitInfo& B) {
|
|
||||||
// return A.StartTime < B.StartTime;
|
|
||||||
// });
|
|
||||||
|
|
||||||
for (auto& Pursuit : AllPursuits)
|
for (auto& Pursuit : AllPursuits)
|
||||||
{
|
{
|
||||||
if (TempGroups.Contains(Pursuit.StartTime))
|
if (TempGroups.Contains(Pursuit.StartTime))
|
||||||
@ -138,15 +196,37 @@ bool UDTFluxPursuitManager::InitPursuit(FRequestData Data)
|
|||||||
{
|
{
|
||||||
return A < B;
|
return A < B;
|
||||||
});
|
});
|
||||||
PursuitGrouped.Reserve(TempGroups.Num());
|
TMap<FDateTime, int> StartTimeFrequency;
|
||||||
|
int32 MaxFrequency = 0;
|
||||||
|
GroupedPursuit.Reserve(TempGroups.Num());
|
||||||
for (const auto& Pair : TempGroups)
|
for (const auto& Pair : TempGroups)
|
||||||
{
|
{
|
||||||
PursuitGrouped.Add(Pair.Value);
|
if (Pair.Value.StartTimeGlobal != FDateTime::MinValue() && Pair.Value.StartTimeGlobal != FDateTime::MaxValue())
|
||||||
|
{
|
||||||
|
StartTimeFrequency.FindOrAdd(Pair.Value.StartTimeGlobal)++;
|
||||||
|
const FDateTime& PropertyValue = Pair.Value.StartTimeGlobal; // Votre propriété
|
||||||
|
int32& Count = StartTimeFrequency.FindOrAdd(PropertyValue, 0);
|
||||||
|
Count++;
|
||||||
|
if (Count > MaxFrequency)
|
||||||
|
{
|
||||||
|
MaxFrequency = Count;
|
||||||
|
MassStartTime = PropertyValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GroupedPursuit.Add(Pair.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
PursuitGrouped.Sort([](const FDTFluxPursuitGroup& A, const FDTFluxPursuitGroup& B)
|
GroupedPursuit.Sort([](const FDTFluxPursuitGroup& A, const FDTFluxPursuitGroup& B)
|
||||||
{
|
{
|
||||||
return A.StartTimeGlobal < B.StartTimeGlobal;
|
return A.StartTimeGlobal < B.StartTimeGlobal;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
TArray<FDTFluxPursuitInfo> FocusPursuits;
|
||||||
|
TArray<FDTFluxPursuitInfo> NextPursuits;
|
||||||
|
bool bIsFocusTruncate = false;
|
||||||
|
|
||||||
|
GetPursuit(FocusPursuits, NextPursuits, bIsFocusTruncate);
|
||||||
|
FPursuitStaterData PursuitData = FPursuitStaterData(FocusPursuits, NextPursuits, MassStartTime, bIsFocusTruncate);
|
||||||
|
CoreSubsystem->OnPursuitSequenceReady.Broadcast(PursuitData);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "Containers/Deque.h"
|
#include "Containers/Deque.h"
|
||||||
|
#include "Types/Struct/FDTFluxPursuitInfo.h"
|
||||||
#include "Subsystems/EngineSubsystem.h"
|
#include "Subsystems/EngineSubsystem.h"
|
||||||
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
||||||
#include "Types/Struct/DTFluxTeamListStruct.h"
|
#include "Types/Struct/DTFluxTeamListStruct.h"
|
||||||
@ -14,88 +15,119 @@ class UDTFluxNetworkSubsystem;
|
|||||||
/** Forward Decl */
|
/** Forward Decl */
|
||||||
class UDTFluxModelAsset;
|
class UDTFluxModelAsset;
|
||||||
class UDTFluxPursuitManager;
|
class UDTFluxPursuitManager;
|
||||||
|
struct FDTFluxServerResponse;
|
||||||
|
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FPursuitStaterData
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
FPursuitStaterData() = default;
|
||||||
|
|
||||||
|
FPursuitStaterData(const TArray<FDTFluxPursuitInfo>& InPursuitFocusNext,
|
||||||
|
const TArray<FDTFluxPursuitInfo>& InPursuitNext, const FDateTime& InMassStartTime,
|
||||||
|
const bool InIsFocusTruncate)
|
||||||
|
: PursuitFocusNext(InPursuitFocusNext), PursuitNext(InPursuitNext), MassStartTime(InMassStartTime),
|
||||||
|
bIsFocusTruncate(InIsFocusTruncate)
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
||||||
|
TArray<FDTFluxPursuitInfo> PursuitFocusNext = TArray<FDTFluxPursuitInfo>();
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
||||||
|
TArray<FDTFluxPursuitInfo> PursuitNext = TArray<FDTFluxPursuitInfo>();
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
||||||
|
FDateTime MassStartTime = FDateTime::MinValue();
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
||||||
|
bool bIsFocusTruncate = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPursuitSequenceReady, const FPursuitStaterData, PursuitInfoSequenceItem);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
UCLASS()
|
UCLASS(BlueprintType, meta=(DisplayName="DTFlux Core Subsystem"))
|
||||||
class DTFLUXCORESUBSYSTEM_API UDTFluxCoreSubsystem : public UEngineSubsystem
|
class DTFLUXCORESUBSYSTEM_API UDTFluxCoreSubsystem : public UEngineSubsystem
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSplitRankings, FDTFluxSplitRankings&, SplitRankings);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSplitRankings, FDTFluxSplitRankings, SplitRankings);
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnSplitRankings OnSplitRankings;
|
FOnSplitRankings OnSplitRankings;
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStageRankings, FDTFluxStageRankings, StageRankings);
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStageRankings, FDTFluxStageRankings&, StageRankings);
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnStageRankings OnStageRankings;
|
FOnStageRankings OnStageRankings;
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnContestRankings, FDTFluxContestRankings, ContestRankings);
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnContestRankings, FDTFluxContestRankings&, ContestRankings);
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnContestRankings OnContestRankings;
|
FOnContestRankings OnContestRankings;
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTeamList);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTeamList);
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnTeamList OnTeamList;
|
FOnTeamList OnTeamList;
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTeamStatusUpdate, FDTFluxParticipant, TeamUpdated);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTeamStatusUpdate, FDTFluxParticipant, TeamUpdated);
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnTeamStatusUpdate OnTeamStatusUpdate;
|
FOnTeamStatusUpdate OnTeamStatusUpdate;
|
||||||
|
|
||||||
DECLARE_DELEGATE_TwoParams(FOnRequestedStageRankings, const FDTFluxStageKey&, const FDTFluxContestRankings&);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnRequestedStageRankings, const FDTFluxStageKey, StageKey,
|
||||||
|
const FDTFluxStageRankings, StageRankings);
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnRequestedStageRankings OnRequestedStageRankings;
|
FOnRequestedStageRankings OnRequestedStageRankings;
|
||||||
//
|
|
||||||
// DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnTeamUpdate, FDateTime, ReceivedAt, FDTFluxParticipant, TeamUpdatedList);
|
|
||||||
// UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
|
||||||
// FOnTeamUpdate OnTeamUpdate;
|
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
|
FOnPursuitSequenceReady OnPursuitSequenceReady;
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void SendTeamListRequest();
|
bool GetContestRankings(const int ContestId, FDTFluxContestRankings& OutContestRankings);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void SendRaceDataRequest();
|
bool GetStageRankings(const int ContestId, const int StageId, FDTFluxStageRankings& OutStageRankings);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void SendContestRankingRequest(int InContestId);
|
bool GetSplitRankings(const int ContestId, const int StageId, const int SplitId,
|
||||||
|
FDTFluxSplitRankings& OutSplitRankings);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void SendStageRankingRequest(int InContestId, int InStageId, bool bShouldIncludeSplitRanking = true);
|
bool GetStageRankingsWithKey(const FDTFluxStageKey StageKey, FDTFluxStageRankings& OutStageRankings,
|
||||||
|
const bool bShouldUseCached = true);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void RequestAllStageRankingOfContest(int InContestId, int InStageId, bool bShouldIncludeSplitRanking = true);
|
bool GetSplitRankingsWithKey(const FDTFluxSplitKey SplitKey, FDTFluxSplitRankings& OutSplitRankings,
|
||||||
|
const bool bShouldUseCached = true);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void SendSplitRankingRequest(int InContestId, int InStageId, int InSplitId);
|
TArray<FGuid> TrackedRequestContestRankings(const TArray<int> ForContests);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void RequestAllSplitRankingOfContest(int InContestId, int InStageId);
|
TArray<FGuid> TrackedRequestStageRankings(const TArray<FDTFluxStageKey> ForStages);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
FDTFluxStageRankings GetStageRankings(FDTFluxStageKey StageKey);
|
TArray<FGuid> TrackedRequestSplitRankings(const TArray<FDTFluxSplitKey> ForSplits);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem")
|
||||||
void RequestAllSplitRankingOfStage(int InContestId, int InStageId, int InSplitId);
|
UDTFluxPursuitManager* PursuitManager = nullptr;
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
const FDTFluxParticipant GetParticipant(int InBib);
|
const FDTFluxParticipant GetParticipant(int InBib);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
//TODO : this must be a ProjectSetting
|
||||||
void RefreshStorage();
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem")
|
||||||
|
bool bShouldKeepRankings = true;
|
||||||
|
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
TArray<int> GetCurrentContestsId();
|
TArray<int> GetCurrentContestsId();
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
TArray<FDTFluxContest> GetCurrentContests();
|
TArray<FDTFluxContest> GetCurrentContests();
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
TArray<int> GetContestsIdForTime(const FDateTime Time);
|
TArray<int> GetContestsIdForTime(const FDateTime Time) const;
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
bool GetContestForId(const int Id, FDTFluxContest& OutContest);
|
bool GetContestForId(const int Id, FDTFluxContest& OutContest);
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
@ -106,24 +138,20 @@ public:
|
|||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
TArray<FDTFluxContest> GetContests();
|
TArray<FDTFluxContest> GetContests();
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
void LaunchPursuitSequenceFor(const TArray<int> ContestIds);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// ~Subsystem Interface
|
// ~Subsystem Interface
|
||||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||||
virtual void Deinitialize() override;
|
virtual void Deinitialize() override;
|
||||||
// ~Subsystem Interface
|
// ~Subsystem Interface
|
||||||
|
|
||||||
UPROPERTY()
|
|
||||||
UDTFluxPursuitManager* PursuitManager = nullptr;
|
|
||||||
|
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void SaveDataStorage();
|
void SaveDataStorage();
|
||||||
|
UFUNCTION()
|
||||||
|
void ProcessTrackedResponse(FDTFluxServerResponse& InResponse);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UDTFluxNetworkSubsystem* NetworkSubsystem = nullptr;
|
UDTFluxNetworkSubsystem* NetworkSubsystem = nullptr;
|
||||||
|
UPROPERTY()
|
||||||
|
UDTFluxModelAsset* DataStorage = nullptr;
|
||||||
|
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void ProcessRaceData(const FDTFluxRaceData& RaceDataDefinition);
|
void ProcessRaceData(const FDTFluxRaceData& RaceDataDefinition);
|
||||||
@ -145,8 +173,4 @@ private:
|
|||||||
void SendRequest(const FString& Message);
|
void SendRequest(const FString& Message);
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void RegisterDelegates();
|
void RegisterDelegates();
|
||||||
|
|
||||||
|
|
||||||
UPROPERTY()
|
|
||||||
UDTFluxModelAsset* DataStorage = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,6 +8,8 @@
|
|||||||
#include "DTFluxPursuitManager.generated.h"
|
#include "DTFluxPursuitManager.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
class UDTFluxCoreSubsystem;
|
||||||
|
|
||||||
USTRUCT()
|
USTRUCT()
|
||||||
struct FRequestData
|
struct FRequestData
|
||||||
{
|
{
|
||||||
@ -27,7 +29,7 @@ struct FRequestData
|
|||||||
FRequestData() = default;
|
FRequestData() = default;
|
||||||
|
|
||||||
FRequestData(const TArray<FGuid>& InRequestIds, const TMap<FGuid, FDTFluxStageRankings>& InStageRankings)
|
FRequestData(const TArray<FGuid>& InRequestIds, const TMap<FGuid, FDTFluxStageRankings>& InStageRankings)
|
||||||
: RequestIds(InRequestIds), StageRankings(InStageRankings)
|
: RequestIds(InRequestIds), StageRankings(InStageRankings), ContestId(-1)
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,9 +65,6 @@ struct FDTFluxPursuitGroup
|
|||||||
bool bIsFocus = false;
|
bool bIsFocus = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnPursuitSequenceReady, const TArray<FDTFluxPursuitInfo>,
|
|
||||||
NextFocusPursuits,
|
|
||||||
const TArray<FDTFluxPursuitInfo>, NextPursuit, bool, bIsTrtuncate);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -78,56 +77,58 @@ class DTFLUXCORESUBSYSTEM_API UDTFluxPursuitManager : public UObject
|
|||||||
public:
|
public:
|
||||||
UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer);
|
UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer);
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
|
||||||
TArray<FDTFluxPursuitInfo> NextFocusPursuits;
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
|
||||||
TArray<FDTFluxPursuitInfo> NextPursuits;
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
bool bFocusIsTruncate = false;
|
bool bFocusIsTruncate = false;
|
||||||
//
|
|
||||||
// UPROPERTY()
|
|
||||||
// TArray<FDTFluxStage> TargetStages;
|
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int MaxSimultaneousPursuit = 7;
|
int PursuitMaxSimultaneousPursuit = 7;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="DTFlux|Pursuit",
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="DTFlux|Pursuit",
|
||||||
meta=(ClampMin="1", ClampMax="60", UIMin="0", UIMax="60"))
|
meta=(ClampMin="1", ClampMax="60", UIMin="0", UIMax="60"))
|
||||||
int MassStartDelay = 10;
|
int MassStartDelay = 10;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TArray<FDTFluxPursuitGroup> PursuitGrouped;
|
FDateTime MassStartTime = FDateTime::MinValue();
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TArray<FDTFluxPursuitGroup> GroupedPursuit;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int CurrentIndex = -1;
|
int CurrentIndex = -1;
|
||||||
|
|
||||||
UPROPERTY(BlueprintCallable, Category="DTFlux|Pursuit")
|
|
||||||
FOnPursuitSequenceReady OnPursuitSequenceReady;
|
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="pursuit, launch, poursuite"))
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="pursuit, launch, poursuite"))
|
||||||
void LaunchPursuitSequenceFor(const TArray<FDTFluxContest> InContests);
|
void InitPursuit(const TArray<int> InContestIds, const int MaxSimultaneousPursuit = 7);
|
||||||
|
|
||||||
UFUNCTION()
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="pursuit, launch, poursuite"))
|
||||||
void OnRequestResponse(const FGuid& RequestId, FDTFluxServerResponse& Response);
|
void GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext, TArray<FDTFluxPursuitInfo>& OutPursuitNext,
|
||||||
|
bool& BIsFocusTruncate, const int MaxSimultaneousPursuit = 7);
|
||||||
UFUNCTION()
|
|
||||||
void OnRequestTimeoutResponse(const FGuid& RequestId, const FString& TimeoutMessage);
|
|
||||||
|
|
||||||
UFUNCTION()
|
|
||||||
void OnRequestError(const FGuid& RequestId, const FString& ErrorMessage);
|
|
||||||
|
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
bool InitSubSystems();
|
bool InitSubSystems();
|
||||||
|
|
||||||
private:
|
|
||||||
TArray<FRequestData> PendingRequestData;
|
|
||||||
|
|
||||||
public:
|
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
bool InitPursuit(FRequestData Data);
|
bool BindRankings();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void UnbindRankings();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRankingsReceived(const FDTFluxStageKey NewStageKey, const FDTFluxStageRankings NewStageRankings);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
TMap<FDTFluxStageKey, bool> PendingStageRanking;
|
||||||
|
TArray<FDTFluxStageRankings> AllRankings;
|
||||||
|
UDTFluxCoreSubsystem* CoreSubsystem = nullptr;
|
||||||
UDTFluxNetworkSubsystem* NetworkSubsystem = nullptr;
|
UDTFluxNetworkSubsystem* NetworkSubsystem = nullptr;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
bool bIsSequenceDone = true;
|
||||||
|
UPROPERTY()
|
||||||
|
bool bIsRankingBounded = false;
|
||||||
|
UFUNCTION()
|
||||||
|
void SetPursuitInfoIsMassStart(FDTFluxPursuitGroup NextFocusGroup);
|
||||||
|
UFUNCTION()
|
||||||
|
bool LaunchPursuitSequence();
|
||||||
};
|
};
|
||||||
|
|||||||
318
Source/DTFluxNetwork/Private/DTFluxAsyncParser.cpp
Normal file
318
Source/DTFluxNetwork/Private/DTFluxAsyncParser.cpp
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
#include "DTFluxAsyncParser.h"
|
||||||
|
|
||||||
|
#include "DTFluxNetworkModule.h"
|
||||||
|
#include "Struct/DTFluxServerResponseStruct.h"
|
||||||
|
#include "Async/AsyncWork.h"
|
||||||
|
|
||||||
|
// ================================================================================================
|
||||||
|
// IMPLÉMENTATION DE LA TÂCHE DE PARSING
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
DECLARE_STATS_GROUP(TEXT("DTFlux"), STATGROUP_DTFlux, STATCAT_Advanced);
|
||||||
|
|
||||||
|
DECLARE_CYCLE_STAT(TEXT("DTFlux Parsing Task"), STAT_FDTFluxParsingTask, STATGROUP_DTFlux);
|
||||||
|
DECLARE_CYCLE_STAT(TEXT("DTFlux Parsing Task DoWork"), STAT_FDTFluxParsingTask_DoWork, STATGROUP_DTFlux);
|
||||||
|
|
||||||
|
FDTFluxParsingTask::FDTFluxParsingTask(
|
||||||
|
const FGuid& InRequestId,
|
||||||
|
const FString& InRawJsonData,
|
||||||
|
FOnParsingCompleted InOnCompleted,
|
||||||
|
FOnParsingFailed InOnFailed
|
||||||
|
)
|
||||||
|
: RequestId(InRequestId)
|
||||||
|
, RawJsonData(InRawJsonData)
|
||||||
|
, OnCompleted(InOnCompleted)
|
||||||
|
, OnFailed(InOnFailed)
|
||||||
|
, StartTime(FPlatformTime::Seconds())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxParsingTask::DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
|
||||||
|
{
|
||||||
|
SCOPE_CYCLE_COUNTER(STAT_FDTFluxParsingTask_DoWork);
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxNetwork, VeryVerbose, TEXT("Starting async parsing for request %s"), *RequestId.ToString());
|
||||||
|
|
||||||
|
TSharedPtr<FDTFluxServerResponse> ParsedResponse;
|
||||||
|
bool bParsingSuccess = false;
|
||||||
|
FString ErrorMessage;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// === PARSING SUR LE THREAD WORKER ===
|
||||||
|
|
||||||
|
EDTFluxResponseStatus Status;
|
||||||
|
ParsedResponse = MakeShared<FDTFluxServerResponse>(RawJsonData, Status, false); // Pas de logs sur worker thread
|
||||||
|
|
||||||
|
if (Status == EDTFluxResponseStatus::Success)
|
||||||
|
{
|
||||||
|
bParsingSuccess = true;
|
||||||
|
UE_LOG(logDTFluxNetwork, VeryVerbose, TEXT("Async parsing successful for request %s"),
|
||||||
|
*RequestId.ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ErrorMessage = FString::Printf(TEXT("Parsing failed with status: %s"),
|
||||||
|
*UEnum::GetValueAsString(Status));
|
||||||
|
UE_LOG(logDTFluxNetwork, Warning, TEXT("Async parsing failed for request %s: %s"),
|
||||||
|
*RequestId.ToString(), *ErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
ErrorMessage = FString::Printf(TEXT("Exception during parsing: %s"), ANSI_TO_TCHAR(e.what()));
|
||||||
|
UE_LOG(logDTFluxNetwork, Error, TEXT("Exception during async parsing for request %s: %s"),
|
||||||
|
*RequestId.ToString(), *ErrorMessage);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
ErrorMessage = TEXT("Unknown exception during parsing");
|
||||||
|
UE_LOG(logDTFluxNetwork, Error, TEXT("Unknown exception during async parsing for request %s"),
|
||||||
|
*RequestId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const float ParsingTime = (FPlatformTime::Seconds() - StartTime) * 1000.0f; // En millisecondes
|
||||||
|
|
||||||
|
// === PROGRAMMER LA CALLBACK SUR LE MAIN THREAD ===
|
||||||
|
|
||||||
|
FFunctionGraphTask::CreateAndDispatchWhenReady(
|
||||||
|
[this, ParsedResponse, bParsingSuccess, ErrorMessage, ParsingTime]()
|
||||||
|
{
|
||||||
|
// Cette lambda s'exécute sur le main thread
|
||||||
|
if (bParsingSuccess && ParsedResponse.IsValid())
|
||||||
|
{
|
||||||
|
OnCompleted.ExecuteIfBound(RequestId, ParsedResponse, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnFailed.ExecuteIfBound(RequestId, ErrorMessage);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TStatId(),
|
||||||
|
nullptr,
|
||||||
|
ENamedThreads::GameThread // Forcer l'exécution sur le main thread
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================================================================
|
||||||
|
// IMPLÉMENTATION DU PARSER ASYNCHRONE
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
FDTFluxAsyncParser::FDTFluxAsyncParser()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxNetwork, Log, TEXT("AsyncParser initialized"));
|
||||||
|
}
|
||||||
|
|
||||||
|
FDTFluxAsyncParser::~FDTFluxAsyncParser()
|
||||||
|
{
|
||||||
|
CancelAllParsing();
|
||||||
|
UE_LOG(logDTFluxNetwork, Log, TEXT("AsyncParser destroyed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxAsyncParser::ParseResponseAsync(
|
||||||
|
const FGuid& RequestId,
|
||||||
|
const FString& RawJsonData,
|
||||||
|
FOnParsingCompleted OnCompleted,
|
||||||
|
FOnParsingFailed OnFailed)
|
||||||
|
{
|
||||||
|
if (RawJsonData.IsEmpty())
|
||||||
|
{
|
||||||
|
OnFailed.ExecuteIfBound(RequestId, TEXT("Empty JSON data"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Starting async parsing for request %s"), *RequestId.ToString());
|
||||||
|
|
||||||
|
// Créer la tâche de parsing
|
||||||
|
FGraphEventRef Task = FFunctionGraphTask::CreateAndDispatchWhenReady(
|
||||||
|
[RequestId, RawJsonData, OnCompleted, OnFailed]()
|
||||||
|
{
|
||||||
|
// Ce code s'exécute sur le worker thread
|
||||||
|
const double StartTime = FPlatformTime::Seconds();
|
||||||
|
|
||||||
|
TSharedPtr<FDTFluxServerResponse> ParsedResponse;
|
||||||
|
bool bParsingSuccess = false;
|
||||||
|
FString ErrorMessage;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EDTFluxResponseStatus Status;
|
||||||
|
ParsedResponse = MakeShared<FDTFluxServerResponse>(RawJsonData, Status, false);
|
||||||
|
|
||||||
|
if (Status == EDTFluxResponseStatus::Success)
|
||||||
|
{
|
||||||
|
bParsingSuccess = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ErrorMessage = FString::Printf(TEXT("Parsing failed with status: %s"),
|
||||||
|
*UEnum::GetValueAsString(Status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
ErrorMessage = FString::Printf(TEXT("Exception during parsing: %s"), ANSI_TO_TCHAR(e.what()));
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
ErrorMessage = TEXT("Unknown exception during parsing");
|
||||||
|
}
|
||||||
|
|
||||||
|
const float ParsingTime = (FPlatformTime::Seconds() - StartTime) * 1000.0f;
|
||||||
|
FFunctionGraphTask::CreateAndDispatchWhenReady(
|
||||||
|
[RequestId, ParsedResponse, bParsingSuccess, ErrorMessage, OnCompleted, OnFailed]()
|
||||||
|
{
|
||||||
|
// Cette lambda s'exécute sur le main thread
|
||||||
|
if (bParsingSuccess && ParsedResponse.IsValid())
|
||||||
|
{
|
||||||
|
OnCompleted.ExecuteIfBound(RequestId, ParsedResponse, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnFailed.ExecuteIfBound(RequestId, ErrorMessage);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TStatId(),
|
||||||
|
nullptr,
|
||||||
|
ENamedThreads::GameThread // Forcer main thread
|
||||||
|
);
|
||||||
|
},
|
||||||
|
TStatId(),
|
||||||
|
nullptr,
|
||||||
|
ENamedThreads::AnyBackgroundThreadNormalTask
|
||||||
|
);
|
||||||
|
|
||||||
|
// Tracker la tâche
|
||||||
|
{
|
||||||
|
FScopeLock Lock(&TasksLock);
|
||||||
|
ActiveTasks.Add(Task);
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Queued async parsing task for request %s"), *RequestId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<FDTFluxServerResponse> FDTFluxAsyncParser::ParseResponseSync(
|
||||||
|
const FString& RawJsonData,
|
||||||
|
float TimeoutSeconds)
|
||||||
|
{
|
||||||
|
if (RawJsonData.IsEmpty())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variables pour la synchronisation
|
||||||
|
TSharedPtr<FDTFluxServerResponse> Result;
|
||||||
|
std::atomic<bool> bCompleted{false};
|
||||||
|
|
||||||
|
// Lancer le parsing async avec callback sync
|
||||||
|
FOnParsingCompleted OnCompleted = FOnParsingCompleted::CreateLambda(
|
||||||
|
[&Result, &bCompleted](const FGuid& RequestId, TSharedPtr<FDTFluxServerResponse> ParsedResponse, bool bSuccess)
|
||||||
|
{
|
||||||
|
if (bSuccess)
|
||||||
|
{
|
||||||
|
Result = ParsedResponse;
|
||||||
|
}
|
||||||
|
bCompleted.store(true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
FOnParsingFailed OnFailed = FOnParsingFailed::CreateLambda(
|
||||||
|
[&bCompleted](const FGuid& RequestId, const FString& ErrorMessage)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxNetwork, Warning, TEXT("Sync parsing failed: %s"), *ErrorMessage);
|
||||||
|
bCompleted.store(true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
FGuid TempId = FGuid::NewGuid();
|
||||||
|
ParseResponseAsync(TempId, RawJsonData, OnCompleted, OnFailed);
|
||||||
|
|
||||||
|
// Attendre avec timeout
|
||||||
|
const double StartTime = FPlatformTime::Seconds();
|
||||||
|
while (!bCompleted.load() && (FPlatformTime::Seconds() - StartTime) < TimeoutSeconds)
|
||||||
|
{
|
||||||
|
FPlatformProcess::Sleep(0.001f); // 1ms
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxAsyncParser::CancelAllParsing()
|
||||||
|
{
|
||||||
|
FScopeLock Lock(&TasksLock);
|
||||||
|
|
||||||
|
for (const FGraphEventRef& Task : ActiveTasks)
|
||||||
|
{
|
||||||
|
// Note: On ne peut pas vraiment "cancel" une tâche TaskGraph en cours,
|
||||||
|
// mais on peut marquer qu'on ne veut plus les résultats
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveTasks.Empty();
|
||||||
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Cancelled all pending parsing tasks"));
|
||||||
|
}
|
||||||
|
|
||||||
|
FDTFluxAsyncParser::FParsingStats FDTFluxAsyncParser::GetStats() const
|
||||||
|
{
|
||||||
|
FScopeLock StatsLock_Local(&StatsLock);
|
||||||
|
FScopeLock TasksLock_Local(&TasksLock);
|
||||||
|
|
||||||
|
FParsingStats Stats;
|
||||||
|
Stats.TasksInProgress = ActiveTasks.Num();
|
||||||
|
Stats.TasksCompleted = TasksCompletedCount;
|
||||||
|
Stats.TasksFailed = TasksFailedCount;
|
||||||
|
|
||||||
|
if (ParsingTimes.Num() > 0)
|
||||||
|
{
|
||||||
|
float Sum = 0.0f;
|
||||||
|
for (float Time : ParsingTimes)
|
||||||
|
{
|
||||||
|
Sum += Time;
|
||||||
|
}
|
||||||
|
Stats.AverageParsingTimeMs = Sum / ParsingTimes.Num();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxAsyncParser::ResetStats()
|
||||||
|
{
|
||||||
|
FScopeLock Lock(&StatsLock);
|
||||||
|
TasksCompletedCount = 0;
|
||||||
|
TasksFailedCount = 0;
|
||||||
|
ParsingTimes.Empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxAsyncParser::OnTaskCompleted(bool bSuccess, float ParsingTimeMs)
|
||||||
|
{
|
||||||
|
FScopeLock Lock(&StatsLock);
|
||||||
|
|
||||||
|
if (bSuccess)
|
||||||
|
{
|
||||||
|
TasksCompletedCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TasksFailedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParsingTimes.Add(ParsingTimeMs);
|
||||||
|
|
||||||
|
// Garder seulement les 100 derniers temps pour la moyenne
|
||||||
|
if (ParsingTimes.Num() > 100)
|
||||||
|
{
|
||||||
|
ParsingTimes.RemoveAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxAsyncParser::CleanupCompletedTasks()
|
||||||
|
{
|
||||||
|
FScopeLock Lock(&TasksLock);
|
||||||
|
|
||||||
|
for (auto It = ActiveTasks.CreateIterator(); It; ++It)
|
||||||
|
{
|
||||||
|
const FGraphEventRef& Task = *It;
|
||||||
|
if (Task.IsValid() && Task->IsComplete())
|
||||||
|
{
|
||||||
|
It.RemoveCurrent(); // Supprime l'élément actuel de manière sécurisée
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#include "Struct/DTFluxServerResponseStruct.h"
|
||||||
|
#include "DTFluxNetworkModule.h"
|
||||||
|
#include "Types/Objects/UDTFluxParticipantFactory.h"
|
||||||
|
|
||||||
#include "Struct/DTFluxServerResponseStruct.h"
|
|
||||||
|
|
||||||
// === IMPLÉMENTATION DES CONSTRUCTEURS ===
|
// === IMPLÉMENTATION DES CONSTRUCTEURS ===
|
||||||
|
|
||||||
@ -194,7 +195,7 @@ FString FDTFluxServerResponse::ToDebugString() const
|
|||||||
*UEnum::GetValueAsString(ParsingStatus), *Type, Code, ContestID, StageID, SplitID, *ReceivedAt.ToString());
|
*UEnum::GetValueAsString(ParsingStatus), *Type, Code, ContestID, StageID, SplitID, *ReceivedAt.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseTeamListResponse(FDTFluxTeamListDefinition& OutTeamList)
|
bool FDTFluxServerResponse::ParseTeamList(FDTFluxTeamListDefinition& OutTeamList)
|
||||||
{
|
{
|
||||||
ParsingStatus = EDTFluxResponseStatus::Unset;
|
ParsingStatus = EDTFluxResponseStatus::Unset;
|
||||||
if (!ValidateResponseType(TEXT("team-list")))
|
if (!ValidateResponseType(TEXT("team-list")))
|
||||||
@ -256,9 +257,9 @@ bool FDTFluxServerResponse::ParseTeamListResponse(FDTFluxTeamListDefinition& Out
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseTeamUpdateResponse(FDTFluxTeamListDefinition& OutTeamUpdate)
|
bool FDTFluxServerResponse::ParseTeamUpdate(FDTFluxTeamListDefinition& OutTeamUpdate)
|
||||||
{
|
{
|
||||||
return ParseTeamListResponse(OutTeamUpdate);
|
return ParseTeamList(OutTeamUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseRaceData(FDTFluxRaceData& OutRaceData)
|
bool FDTFluxServerResponse::ParseRaceData(FDTFluxRaceData& OutRaceData)
|
||||||
@ -368,7 +369,7 @@ bool FDTFluxServerResponse::ParseContestRanking(FDTFluxContestRankings& OutConte
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseStageRankingResponse(FDTFluxStageRankings& OutStageRankings)
|
bool FDTFluxServerResponse::ParseStageRanking(FDTFluxStageRankings& OutStageRankings)
|
||||||
{
|
{
|
||||||
// UE_LOG(logDTFluxNetwork, Log, TEXT("Response is stage-ranking type %s"), *RawMessage);
|
// UE_LOG(logDTFluxNetwork, Log, TEXT("Response is stage-ranking type %s"), *RawMessage);
|
||||||
if (!ValidateResponseType(TEXT("stage-ranking")))
|
if (!ValidateResponseType(TEXT("stage-ranking")))
|
||||||
@ -396,7 +397,7 @@ bool FDTFluxServerResponse::ParseStageRankingResponse(FDTFluxStageRankings& OutS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseSplitRankingResponse(FDTFluxSplitRankings& OutSplitRankings)
|
bool FDTFluxServerResponse::ParseSplitRanking(FDTFluxSplitRankings& OutSplitRankings)
|
||||||
{
|
{
|
||||||
if (!ValidateResponseType(TEXT("stage-ranking")) || SplitID == -1)
|
if (!ValidateResponseType(TEXT("stage-ranking")) || SplitID == -1)
|
||||||
{
|
{
|
||||||
@ -427,7 +428,7 @@ bool FDTFluxServerResponse::ParseSplitRankingResponse(FDTFluxSplitRankings& OutS
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseStatusUpdateResponse(FDTFluxTeamStatusUpdate& OutStatusUpdate)
|
bool FDTFluxServerResponse::ParseStatusUpdate(FDTFluxTeamStatusUpdate& OutStatusUpdate)
|
||||||
{
|
{
|
||||||
if (!ValidateResponseType(TEXT("status-update")))
|
if (!ValidateResponseType(TEXT("status-update")))
|
||||||
{
|
{
|
||||||
@ -448,7 +449,7 @@ bool FDTFluxServerResponse::ParseStatusUpdateResponse(FDTFluxTeamStatusUpdate& O
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseSplitSensorResponse(TArray<FDTFluxSplitSensorInfo>& OutSplitSensorInfos)
|
bool FDTFluxServerResponse::ParseSplitSensor(TArray<FDTFluxSplitSensorInfo>& OutSplitSensorInfos)
|
||||||
{
|
{
|
||||||
if (!ValidateResponseType(TEXT("split-sensor")))
|
if (!ValidateResponseType(TEXT("split-sensor")))
|
||||||
{
|
{
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
171
Source/DTFluxNetwork/Public/DTFluxAsyncParser.h
Normal file
171
Source/DTFluxNetwork/Public/DTFluxAsyncParser.h
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
// ================================================================================================
|
||||||
|
// DTFluxAsyncParser.h - Async Response Parser
|
||||||
|
// ================================================================================================
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "HAL/CriticalSection.h"
|
||||||
|
#include "Async/TaskGraphInterfaces.h"
|
||||||
|
#include "Struct/DTFluxServerResponseStruct.h"
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
|
||||||
|
// ================================================================================================
|
||||||
|
// DELEGATES POUR LE PARSING ASYNCHRONE
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
DECLARE_DELEGATE_ThreeParams(FOnParsingCompleted, const FGuid& /*RequestId*/,
|
||||||
|
TSharedPtr<FDTFluxServerResponse> /*ParsedResponse*/, bool /*bSuccess*/);
|
||||||
|
DECLARE_DELEGATE_TwoParams(FOnParsingFailed, const FGuid& /*RequestId*/, const FString& /*ErrorMessage*/);
|
||||||
|
|
||||||
|
// ================================================================================================
|
||||||
|
// ASYNC PARSER - Délégation du parsing avec TaskGraph
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gestionnaire centralisé pour le parsing asynchrone des réponses JSON
|
||||||
|
* Utilise le TaskGraph d'Unreal Engine pour déléguer le parsing sur des worker threads
|
||||||
|
*/
|
||||||
|
class DTFLUXNETWORK_API FDTFluxAsyncParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FDTFluxAsyncParser();
|
||||||
|
~FDTFluxAsyncParser();
|
||||||
|
|
||||||
|
// === INTERFACE PUBLIQUE ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lancer le parsing asynchrone d'une réponse JSON
|
||||||
|
* @param RequestId - ID de la requête pour le suivi
|
||||||
|
* @param RawJsonData - Données JSON brutes à parser
|
||||||
|
* @param OnCompleted - Callback appelé en cas de succès (sur main thread)
|
||||||
|
* @param OnFailed - Callback appelé en cas d'échec (sur main thread)
|
||||||
|
*/
|
||||||
|
void ParseResponseAsync(
|
||||||
|
const FGuid& RequestId,
|
||||||
|
const FString& RawJsonData,
|
||||||
|
FOnParsingCompleted OnCompleted,
|
||||||
|
FOnParsingFailed OnFailed = FOnParsingFailed()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parsing synchrone avec timeout (pour les cas urgents)
|
||||||
|
* @param RawJsonData - Données JSON à parser
|
||||||
|
* @param TimeoutSeconds - Timeout maximum pour le parsing
|
||||||
|
* @return Réponse parsée ou nullptr en cas d'échec
|
||||||
|
*/
|
||||||
|
TSharedPtr<FDTFluxServerResponse> ParseResponseSync(
|
||||||
|
const FString& RawJsonData,
|
||||||
|
float TimeoutSeconds = 1.0f
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annuler toutes les tâches de parsing en attente
|
||||||
|
*/
|
||||||
|
void CancelAllParsing();
|
||||||
|
|
||||||
|
// === STATISTIQUES ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statistiques de performance du parsing
|
||||||
|
*/
|
||||||
|
struct FParsingStats
|
||||||
|
{
|
||||||
|
int32 TasksInProgress = 0; // Tâches actuellement en cours
|
||||||
|
int32 TasksCompleted = 0; // Tâches terminées avec succès
|
||||||
|
int32 TasksFailed = 0; // Tâches échouées
|
||||||
|
float AverageParsingTimeMs = 0.0f; // Temps moyen de parsing en ms
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtenir les statistiques de parsing
|
||||||
|
*/
|
||||||
|
FParsingStats GetStats() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Réinitialiser les statistiques
|
||||||
|
*/
|
||||||
|
void ResetStats();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// === TRACKING DES TÂCHES ===
|
||||||
|
mutable FCriticalSection TasksLock;
|
||||||
|
TSet<FGraphEventRef> ActiveTasks;
|
||||||
|
|
||||||
|
// === STATISTIQUES ===
|
||||||
|
mutable FCriticalSection StatsLock;
|
||||||
|
mutable int32 TasksCompletedCount = 0;
|
||||||
|
mutable int32 TasksFailedCount = 0;
|
||||||
|
mutable TArray<float> ParsingTimes; // Historique des temps de parsing
|
||||||
|
|
||||||
|
// === MÉTHODES PRIVÉES ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback appelé quand une tâche se termine
|
||||||
|
* @param bSuccess - Succès ou échec de la tâche
|
||||||
|
* @param ParsingTimeMs - Temps de parsing en millisecondes
|
||||||
|
*/
|
||||||
|
void OnTaskCompleted(bool bSuccess, float ParsingTimeMs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nettoyer les tâches terminées de la liste active
|
||||||
|
*/
|
||||||
|
void CleanupCompletedTasks();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ================================================================================================
|
||||||
|
// TÂCHE DE PARSING POUR LE TASKGRAPH
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tâche de parsing JSON exécutée sur un thread worker
|
||||||
|
* Compatible avec le TaskGraph d'Unreal Engine
|
||||||
|
*/
|
||||||
|
class FDTFluxParsingTask
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FDTFluxParsingTask(
|
||||||
|
const FGuid& InRequestId,
|
||||||
|
const FString& InRawJsonData,
|
||||||
|
FOnParsingCompleted InOnCompleted,
|
||||||
|
FOnParsingFailed InOnFailed
|
||||||
|
);
|
||||||
|
|
||||||
|
// === INTERFACE TASK GRAPH ===
|
||||||
|
|
||||||
|
FORCEINLINE TStatId GetStatId() const
|
||||||
|
{
|
||||||
|
RETURN_QUICK_DECLARE_CYCLE_STAT(FDTFluxParsingTask, STATGROUP_TaskGraphTasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FORCEINLINE TStatId GetStatId_DoWork()
|
||||||
|
{
|
||||||
|
RETURN_QUICK_DECLARE_CYCLE_STAT(FDTFluxParsingTask_DoWork, STATGROUP_TaskGraphTasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FORCEINLINE ENamedThreads::Type GetDesiredThread()
|
||||||
|
{
|
||||||
|
// Exécuter sur un thread worker (pas le main thread)
|
||||||
|
return ENamedThreads::AnyBackgroundThreadNormalTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FORCEINLINE ESubsequentsMode::Type GetSubsequentsMode()
|
||||||
|
{
|
||||||
|
return ESubsequentsMode::TrackSubsequents;
|
||||||
|
}
|
||||||
|
|
||||||
|
// === EXÉCUTION DE LA TÂCHE ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Méthode principale d'exécution de la tâche
|
||||||
|
* Appelée automatiquement par le TaskGraph sur un worker thread
|
||||||
|
*/
|
||||||
|
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FGuid RequestId;
|
||||||
|
FString RawJsonData;
|
||||||
|
FOnParsingCompleted OnCompleted;
|
||||||
|
FOnParsingFailed OnFailed;
|
||||||
|
double StartTime;
|
||||||
|
};
|
||||||
@ -1,66 +1,444 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// ================================================================================================
|
||||||
|
// DTFluxRequestManager.h - Gestionnaire C++ optimisé avec cache, timeout et retry
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "UObject/Object.h"
|
|
||||||
#include "Containers/Queue.h"
|
|
||||||
#include "Tickable.h"
|
#include "Tickable.h"
|
||||||
#include "Struct/DTFluxRequestStructs.h"
|
#include "HAL/CriticalSection.h"
|
||||||
#include "Struct/DTFluxServerResponseStruct.h"
|
#include "Struct/DTFluxServerResponseStruct.h"
|
||||||
#include "Types/Enum/DTFluxCoreEnum.h"
|
#include "Types/Enum/DTFluxCoreEnum.h"
|
||||||
#include "DTFluxQueuedManager.generated.h"
|
#include "DTFluxQueuedManager.generated.h"
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestTimedOut, const FDTFluxQueuedRequest&, TimedOutRequest);
|
class FDTFluxAsyncParser;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Classe Tickable gérant les requêtes WebSockets qui ne sont pas traçables nativement par l'API.
|
// ================================================================================================
|
||||||
* Cette classe utilise TQueue pour gérer efficacement les requêtes en attente et vérifie leur état dans le tick.
|
// ENUMS ET STRUCTURES POUR LES REQUÊTES
|
||||||
*/
|
// ================================================================================================
|
||||||
UCLASS()
|
|
||||||
class DTFLUXNETWORK_API UDTFluxQueuedManager : public UObject, public FTickableGameObject
|
UENUM(BlueprintType)
|
||||||
|
enum class EDTFluxRequestState : uint8
|
||||||
|
{
|
||||||
|
Pending UMETA(DisplayName = "Pending"),
|
||||||
|
Sent UMETA(DisplayName = "Sent"),
|
||||||
|
Completed UMETA(DisplayName = "Completed"),
|
||||||
|
Failed UMETA(DisplayName = "Failed"),
|
||||||
|
TimedOut UMETA(DisplayName = "TimedOut"),
|
||||||
|
Cached UMETA(DisplayName = "Cached"),
|
||||||
|
Retrying UMETA(DisplayName = "Retrying")
|
||||||
|
};
|
||||||
|
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct DTFLUXNETWORK_API FDTFluxRequestConfig
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||||
/** Constructeur par défaut */
|
float TimeoutSeconds = 5.0f;
|
||||||
UDTFluxQueuedManager();
|
|
||||||
virtual ~UDTFluxQueuedManager() override;
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||||
void Initialize();
|
int32 MaxRetries = 3;
|
||||||
FGuid QueueRequest(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1,
|
|
||||||
const FString& RawMessage = "");
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||||
bool MarkRequestAsError(const FGuid& TargetRequestGuid);
|
float RetryBackoffMultiplier = 1.5f;
|
||||||
bool MarkRequestAsResponded(const FGuid& TargetRequestGuid);
|
|
||||||
bool MarkRequestAsResponded(const FDTFluxQueuedRequest& TargetRequest);
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||||
bool IsRequestPending(FGuid& OutRequestId, EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
bool bEnableCache = true;
|
||||||
int32 SplitId = -1);
|
|
||||||
FDTFluxQueuedRequest* GetRequestPending(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||||
int32 SplitId = -1);
|
float CacheValiditySeconds = 60.0f;
|
||||||
const FDTFluxQueuedRequest* GetRequest(const FGuid& SearchedGuid);
|
};
|
||||||
int32 GetPendingRequestCount();
|
|
||||||
int32 CleanupTimedOutRequests();
|
USTRUCT(BlueprintType)
|
||||||
int32 CleanCashedRequests();
|
struct DTFLUXNETWORK_API FDTFluxTrackedRequest
|
||||||
void ClearAllRequests();
|
{
|
||||||
// bool TryProcessResponse(const FDTFluxServerResponse& Response);
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
// === IDENTIFICATION ===
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
FGuid RequestId = FGuid::NewGuid();
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
EDTFluxApiDataType RequestType = EDTFluxApiDataType::None;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
int32 ContestId = -1;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
int32 StageId = -1;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
int32 SplitId = -1;
|
||||||
|
|
||||||
|
// === ÉTAT ET TIMING ===
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
EDTFluxRequestState State = EDTFluxRequestState::Pending;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
FDateTime CreatedAt = FDateTime::Now();
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
FDateTime SentAt = FDateTime::MinValue();
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
FDateTime CompletedAt = FDateTime::MinValue();
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
FDateTime LastAttemptTime = FDateTime::Now();
|
||||||
|
|
||||||
|
// === CONFIGURATION ===
|
||||||
|
FDTFluxRequestConfig Config;
|
||||||
|
|
||||||
|
// === RETRY LOGIC ===
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
int32 CurrentRetries = 0;
|
||||||
|
|
||||||
|
// === DONNÉES DE RÉPONSE ===
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
FString RawResponseData;
|
||||||
|
|
||||||
|
// Réponse parsée (lazy loading)
|
||||||
|
mutable TOptional<TSharedPtr<FDTFluxServerResponse>> ParsedResponse;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
bool bIsResponseParsed = false;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly)
|
||||||
|
FString LastErrorMessage;
|
||||||
|
|
||||||
|
// === MÉTHODES UTILITAIRES ===
|
||||||
|
|
||||||
|
bool HasTimedOut() const;
|
||||||
|
bool CanRetry() const;
|
||||||
|
bool IsCacheValid() const;
|
||||||
|
float GetRetryDelay() const;
|
||||||
|
bool Matches(EDTFluxApiDataType InType, int32 InContestId = -1, int32 InStageId = -1, int32 InSplitId = -1) const;
|
||||||
|
FString GetCacheKey() const;
|
||||||
|
void SetRawResponse(const FString& RawData);
|
||||||
|
FString Serialize() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ================================================================================================
|
||||||
|
// DELEGATES POUR LES CALLBACKS
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnDTFluxRequestSuccess, const FDTFluxTrackedRequest&);
|
||||||
|
DECLARE_DELEGATE_TwoParams(FOnDTFluxRequestError, const FDTFluxTrackedRequest&, const FString& /*ErrorMessage*/);
|
||||||
|
|
||||||
|
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnRequestStateChangedNative, const FGuid& /*RequestId*/,
|
||||||
|
EDTFluxRequestState& /*NewState*/);
|
||||||
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnRequestCompletedNative, const FDTFluxTrackedRequest& /*CompletedRequest*/);
|
||||||
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnRequestFailedNative, const FDTFluxTrackedRequest& /*FailedRequest*/);
|
||||||
|
|
||||||
|
// ================================================================================================
|
||||||
|
// REQUEST MANAGER - Classe C++ principale avec SmartPointers
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gestionnaire de requêtes trackées avec cache, timeout, retry et parsing asynchrone
|
||||||
|
* Implémentation C++ pure avec SmartPointers pour des performances optimales
|
||||||
|
*/
|
||||||
|
class DTFLUXNETWORK_API FDTFluxQueuedRequestManager : public FTickableGameObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FDTFluxQueuedRequestManager();
|
||||||
|
virtual ~FDTFluxQueuedRequestManager();
|
||||||
|
|
||||||
|
// === LIFECYCLE ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialiser le gestionnaire de requêtes
|
||||||
|
* @param DefaultConfig Configuration par défaut pour les nouvelles requêtes
|
||||||
|
*/
|
||||||
|
void Initialize(const FDTFluxRequestConfig& DefaultConfig = FDTFluxRequestConfig());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arrêter le gestionnaire et nettoyer toutes les ressources
|
||||||
|
*/
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifier si le gestionnaire est initialisé
|
||||||
|
*/
|
||||||
|
bool IsInitialized() const { return bIsInitialized.load(); }
|
||||||
|
|
||||||
|
// === CRÉATION DE REQUÊTES ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Créer une nouvelle requête trackée
|
||||||
|
* @param RequestType Type de requête (ContestRanking, StageRanking, etc.)
|
||||||
|
* @param ContestId ID du contest (-1 si non applicable)
|
||||||
|
* @param StageId ID du stage (-1 si non applicable)
|
||||||
|
* @param SplitId ID du split (-1 si non applicable)
|
||||||
|
* @param CustomConfig Configuration spécifique pour cette requête
|
||||||
|
* @return GUID de la requête créée
|
||||||
|
*/
|
||||||
|
FGuid CreateTrackedRequest(
|
||||||
|
EDTFluxApiDataType RequestType,
|
||||||
|
int32 ContestId = -1,
|
||||||
|
int32 StageId = -1,
|
||||||
|
int32 SplitId = -1,
|
||||||
|
const FDTFluxRequestConfig& CustomConfig = FDTFluxRequestConfig()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Créer une requête trackée avec callbacks C++
|
||||||
|
* @param RequestType Type de requête
|
||||||
|
* @param ContestId ID du contest
|
||||||
|
* @param StageId ID du stage
|
||||||
|
* @param SplitId ID du split
|
||||||
|
* @param OnSuccess Callback appelé en cas de succès
|
||||||
|
* @param OnError Callback appelé en cas d'erreur
|
||||||
|
* @param CustomConfig Configuration spécifique
|
||||||
|
* @return GUID de la requête créée
|
||||||
|
*/
|
||||||
|
FGuid CreateTrackedRequestWithCallbacks(
|
||||||
|
EDTFluxApiDataType RequestType,
|
||||||
|
int32 ContestId,
|
||||||
|
int32 StageId,
|
||||||
|
int32 SplitId,
|
||||||
|
FOnDTFluxRequestSuccess OnSuccess,
|
||||||
|
FOnDTFluxRequestError OnError,
|
||||||
|
const FDTFluxRequestConfig& CustomConfig = FDTFluxRequestConfig()
|
||||||
|
);
|
||||||
|
|
||||||
|
// === GESTION DES REQUÊTES ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marquer une requête comme envoyée
|
||||||
|
*/
|
||||||
|
bool MarkRequestAsSent(const FGuid& RequestId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compléter une requête avec la réponse reçue
|
||||||
|
* @param RequestId ID de la requête
|
||||||
|
* @param RawResponseData Données JSON brutes de la réponse
|
||||||
|
* @param bUseAsyncParsing Utiliser le parsing asynchrone (recommandé)
|
||||||
|
*/
|
||||||
|
bool CompleteRequest(const FGuid& RequestId, const FString& RawResponseData, bool bUseAsyncParsing = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marquer une requête comme échouée
|
||||||
|
*/
|
||||||
|
bool FailRequest(const FGuid& RequestId, const FString& ErrorMessage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relancer une requête (si retry possible)
|
||||||
|
*/
|
||||||
|
bool RetryRequest(const FGuid& RequestId);
|
||||||
|
|
||||||
|
// === RECHERCHE ET CACHE ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chercher une requête en attente correspondant aux critères
|
||||||
|
*/
|
||||||
|
bool FindPendingRequest(
|
||||||
|
FGuid& OutRequestId,
|
||||||
|
EDTFluxApiDataType RequestType,
|
||||||
|
int32 ContestId = -1,
|
||||||
|
int32 StageId = -1,
|
||||||
|
int32 SplitId = -1
|
||||||
|
) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupérer une réponse depuis le cache (données brutes)
|
||||||
|
*/
|
||||||
|
bool GetFromCache(
|
||||||
|
EDTFluxApiDataType RequestType,
|
||||||
|
FString& OutRawResponse,
|
||||||
|
int32 ContestId = -1,
|
||||||
|
int32 StageId = -1,
|
||||||
|
int32 SplitId = -1
|
||||||
|
) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupérer une réponse parsée depuis le cache
|
||||||
|
*/
|
||||||
|
bool GetParsedFromCache(
|
||||||
|
EDTFluxApiDataType RequestType,
|
||||||
|
TSharedPtr<FDTFluxServerResponse>& OutResponse,
|
||||||
|
int32 ContestId = -1,
|
||||||
|
int32 StageId = -1,
|
||||||
|
int32 SplitId = -1
|
||||||
|
) const;
|
||||||
|
|
||||||
|
// === ACCESSEURS ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupérer une requête par son ID
|
||||||
|
*/
|
||||||
|
bool GetRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupérer un pointeur vers une requête (plus efficace)
|
||||||
|
*/
|
||||||
|
const FDTFluxTrackedRequest* GetRequestPtr(const FGuid& RequestId) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupérer toutes les requêtes dans un état donné
|
||||||
|
*/
|
||||||
|
TArray<FDTFluxTrackedRequest> GetRequestsByState(EDTFluxRequestState State) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compter les requêtes dans un état donné
|
||||||
|
*/
|
||||||
|
int32 GetRequestCount(EDTFluxRequestState State = EDTFluxRequestState::Pending) const;
|
||||||
|
|
||||||
|
// === STATISTIQUES ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statistiques complètes du gestionnaire de requêtes
|
||||||
|
*/
|
||||||
|
struct FRequestStatistics
|
||||||
|
{
|
||||||
|
int32 Pending = 0;
|
||||||
|
int32 Cached = 0;
|
||||||
|
int32 Completed = 0;
|
||||||
|
int32 Failed = 0;
|
||||||
|
int32 TotalRequests = 0;
|
||||||
|
int32 CacheHits = 0;
|
||||||
|
int32 CacheMisses = 0;
|
||||||
|
float HitRate = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
FRequestStatistics GetStatistics() const;
|
||||||
|
|
||||||
|
// === NETTOYAGE ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nettoyer les entrées de cache expirées
|
||||||
|
* @return Nombre d'entrées supprimées
|
||||||
|
*/
|
||||||
|
int32 CleanupExpiredCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nettoyer les requêtes terminées anciennes
|
||||||
|
* @param OlderThanSeconds Supprimer les requêtes plus anciennes que ce délai
|
||||||
|
* @return Nombre de requêtes supprimées
|
||||||
|
*/
|
||||||
|
int32 CleanupCompletedRequests(float OlderThanSeconds = 300.0f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vider toutes les requêtes et le cache
|
||||||
|
*/
|
||||||
|
void ClearAllRequests();
|
||||||
|
|
||||||
|
// === EVENTS ===
|
||||||
|
|
||||||
|
FOnRequestStateChangedNative OnRequestStateChanged;
|
||||||
|
FOnRequestCompletedNative OnRequestCompleted;
|
||||||
|
FOnRequestFailedNative OnRequestFailed;
|
||||||
|
|
||||||
|
// === INTERFACE TICKABLE ===
|
||||||
|
|
||||||
// Interface FTickableGameObject
|
|
||||||
virtual void Tick(float DeltaTime) override;
|
virtual void Tick(float DeltaTime) override;
|
||||||
virtual bool IsTickable() const override;
|
virtual bool IsTickable() const override { return true; };
|
||||||
virtual TStatId GetStatId() const override;
|
|
||||||
|
virtual TStatId GetStatId() const override
|
||||||
|
{
|
||||||
|
RETURN_QUICK_DECLARE_CYCLE_STAT(FDTFluxQueuedRequestManager, STATGROUP_Tickables);
|
||||||
|
};
|
||||||
virtual bool IsTickableWhenPaused() const override { return true; }
|
virtual bool IsTickableWhenPaused() const override { return true; }
|
||||||
virtual bool IsTickableInEditor() const override { return true; }
|
virtual bool IsTickableInEditor() const override { return true; }
|
||||||
// Interface ~FTickableGameObject
|
|
||||||
|
|
||||||
|
// === ACCESSEUR POUR LE PARSER (debug/stats) ===
|
||||||
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Network")
|
const FDTFluxAsyncParser* GetAsyncParser() const { return AsyncParser.Get(); }
|
||||||
FOnRequestTimedOut OnRequestTimedOut;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> PendingRequestsQueue;
|
// === CONFIGURATION ===
|
||||||
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> CompletedRequestsQueue;
|
FDTFluxRequestConfig DefaultConfig;
|
||||||
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TimedOutRequestsQueue;
|
std::atomic<bool> bIsInitialized{false};
|
||||||
|
|
||||||
bool bIsInitialized;
|
// === TIMING POUR LE TICK ===
|
||||||
float CheckInterval;
|
float TimeSinceLastTimeoutCheck = 0.0f;
|
||||||
float TimeSinceLastCheck;
|
float TimeSinceLastCacheCleanup = 0.0f;
|
||||||
|
float TimeSinceLastRetryCheck = 0.0f;
|
||||||
|
|
||||||
|
static constexpr float TimeoutCheckInterval = 1.0f;
|
||||||
|
static constexpr float CacheCleanupInterval = 30.0f;
|
||||||
|
static constexpr float RetryCheckInterval = 0.5f;
|
||||||
|
|
||||||
|
// === STOCKAGE THREAD-SAFE ===
|
||||||
|
mutable FCriticalSection RequestsLock;
|
||||||
|
TMap<FGuid, TSharedPtr<FDTFluxTrackedRequest>> AllRequests;
|
||||||
|
TMap<FString, FGuid> CacheKeyToRequestId;
|
||||||
|
|
||||||
|
// === CALLBACKS C++ ===
|
||||||
|
mutable FCriticalSection CallbacksLock;
|
||||||
|
TMap<FGuid, FOnDTFluxRequestSuccess> SuccessCallbacks;
|
||||||
|
TMap<FGuid, FOnDTFluxRequestError> ErrorCallbacks;
|
||||||
|
|
||||||
|
// === MÉTRIQUES ===
|
||||||
|
mutable FCriticalSection MetricsLock;
|
||||||
|
mutable int32 TotalRequests = 0;
|
||||||
|
mutable int32 CacheHits = 0;
|
||||||
|
mutable int32 CacheMisses = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// === PARSER ASYNCHRONE ===
|
||||||
|
TUniquePtr<FDTFluxAsyncParser> AsyncParser;
|
||||||
|
|
||||||
|
// === MÉTHODES PRIVÉES ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changer l'état d'une requête et notifier les observers
|
||||||
|
*/
|
||||||
|
void ChangeRequestState(TSharedPtr<FDTFluxTrackedRequest> Request, EDTFluxRequestState NewState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traiter les requêtes en timeout (appelé périodiquement)
|
||||||
|
*/
|
||||||
|
void ProcessTimeouts();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traiter les requêtes à relancer (appelé périodiquement)
|
||||||
|
*/
|
||||||
|
void ProcessRetries();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nettoyer le cache périodiquement
|
||||||
|
*/
|
||||||
|
void ProcessCacheCleanup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Déclencher les callbacks pour une requête
|
||||||
|
*/
|
||||||
|
void TriggerCallbacks(const FDTFluxTrackedRequest& Request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nettoyer les callbacks d'une requête
|
||||||
|
*/
|
||||||
|
void CleanupCallbacks(const FGuid& RequestId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enregistrer un hit cache dans les métriques
|
||||||
|
*/
|
||||||
|
void RecordCacheHit() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enregistrer un miss cache dans les métriques
|
||||||
|
*/
|
||||||
|
void RecordCacheMiss() const;
|
||||||
|
|
||||||
|
|
||||||
|
// === CALLBACKS POUR LE PARSING ASYNCHRONE ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback appelé quand le parsing asynchrone réussit
|
||||||
|
*/
|
||||||
|
void OnParsingCompleted(const FGuid& RequestId, TSharedPtr<FDTFluxServerResponse> ParsedResponse, bool bSuccess);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback appelé quand le parsing asynchrone échoue
|
||||||
|
*/
|
||||||
|
void OnParsingFailed(const FGuid& RequestId, const FString& ErrorMessage);
|
||||||
|
|
||||||
|
// === UTILITAIRES STATIQUES ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Générer une clé de cache unique pour une requête
|
||||||
|
*/
|
||||||
|
static FString GenerateCacheKey(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId, int32 SplitId);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,13 +4,11 @@
|
|||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "UObject/Object.h"
|
#include "UObject/Object.h"
|
||||||
#include "DTFluxNetworkModule.h"
|
|
||||||
#include "DTFluxRaceDataServerResponse.h"
|
#include "DTFluxRaceDataServerResponse.h"
|
||||||
#include "DTFluxRankingServerResponse.h"
|
#include "DTFluxRankingServerResponse.h"
|
||||||
#include "DTFluxSplitSensorServerResponse.h"
|
#include "DTFluxSplitSensorServerResponse.h"
|
||||||
#include "JsonObjectConverter.h"
|
#include "JsonObjectConverter.h"
|
||||||
#include "Types/Enum/DTFluxCoreEnum.h"
|
#include "Types/Enum/DTFluxCoreEnum.h"
|
||||||
#include "Types/Objects/UDTFluxParticipantFactory.h"
|
|
||||||
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
||||||
#include "Types/Struct/DTFluxRankingStructs.h"
|
#include "Types/Struct/DTFluxRankingStructs.h"
|
||||||
#include "Types/Struct/DTFluxSplitSensor.h"
|
#include "Types/Struct/DTFluxSplitSensor.h"
|
||||||
@ -85,14 +83,14 @@ public:
|
|||||||
|
|
||||||
EDTFluxResponseStatus TryParse(bool bLogErrors = true);
|
EDTFluxResponseStatus TryParse(bool bLogErrors = true);
|
||||||
|
|
||||||
bool ParseTeamListResponse(FDTFluxTeamListDefinition& OutTeamList);
|
bool ParseTeamList(FDTFluxTeamListDefinition& OutTeamList);
|
||||||
bool ParseTeamUpdateResponse(FDTFluxTeamListDefinition& OutTeamUpdate);
|
bool ParseTeamUpdate(FDTFluxTeamListDefinition& OutTeamUpdate);
|
||||||
bool ParseRaceData(FDTFluxRaceData& OutRaceData);
|
bool ParseRaceData(FDTFluxRaceData& OutRaceData);
|
||||||
bool ParseContestRanking(FDTFluxContestRankings& OutContestRankings);
|
bool ParseContestRanking(FDTFluxContestRankings& OutContestRankings);
|
||||||
bool ParseStageRankingResponse(FDTFluxStageRankings& OutStageRankings);
|
bool ParseStageRanking(FDTFluxStageRankings& OutStageRankings);
|
||||||
bool ParseSplitRankingResponse(FDTFluxSplitRankings& OutSplitRankings);
|
bool ParseSplitRanking(FDTFluxSplitRankings& OutSplitRankings);
|
||||||
bool ParseStatusUpdateResponse(FDTFluxTeamStatusUpdate& OutStatusUpdate);
|
bool ParseStatusUpdate(FDTFluxTeamStatusUpdate& OutStatusUpdate);
|
||||||
bool ParseSplitSensorResponse(TArray<FDTFluxSplitSensorInfo>& OutSplitSensorInfos);
|
bool ParseSplitSensor(TArray<FDTFluxSplitSensorInfo>& OutSplitSensorInfos);
|
||||||
|
|
||||||
|
|
||||||
// === MÉTHODES UTILITAIRES ===
|
// === MÉTHODES UTILITAIRES ===
|
||||||
|
|||||||
@ -1,41 +1,54 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// ================================================================================================
|
||||||
|
// DTFluxNetworkSubsystem.h - Interface UObject avec compatibilité Blueprint
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "DTFluxQueuedManager.h"
|
|
||||||
#include "Struct/DTFluxServerResponseStruct.h"
|
|
||||||
#include "Subsystems/EngineSubsystem.h"
|
#include "Subsystems/EngineSubsystem.h"
|
||||||
#include "Types/DTFluxNetworkSettingsTypes.h"
|
#include "Types/DTFluxNetworkSettingsTypes.h"
|
||||||
#include "Types/Enum/DTFluxCoreEnum.h"
|
#include "Types/Enum/DTFluxCoreEnum.h"
|
||||||
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
#include "DTFluxQueuedManager.h"
|
||||||
#include "Types/Struct/DTFluxRankingStructs.h"
|
|
||||||
#include "Types/Struct/DTFluxSplitSensor.h"
|
|
||||||
#include "Types/Struct/DTFluxTeamListStruct.h"
|
|
||||||
#include "DTFluxNetworkSubsystem.generated.h"
|
#include "DTFluxNetworkSubsystem.generated.h"
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
class FDTFluxWebSocketClient;
|
class FDTFluxWebSocketClient;
|
||||||
class UDTFluxQueuedManager;
|
class FDTFluxQueuedRequestManager;
|
||||||
|
|
||||||
typedef TSharedPtr<FDTFluxWebSocketClient> FDTFluxWebSocketClientSP;
|
typedef TSharedPtr<FDTFluxWebSocketClient> FDTFluxWebSocketClientSP;
|
||||||
class FDTFluxHttpClient;
|
|
||||||
typedef TSharedPtr<FDTFluxHttpClient> FDTFluxHttpClientSP;
|
|
||||||
|
|
||||||
|
// ================================================================================================
|
||||||
|
// DELEGATES BLUEPRINT POUR LES REQUÊTES TRACKÉES
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
// Delegates pour les requêtes avec callback
|
|
||||||
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,
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestCompleted, const FGuid&, RequestId,
|
||||||
EDTFluxApiDataType, RequestType, const FString&, ResponseData);
|
EDTFluxApiDataType, RequestType, const FString&, ResponseData);
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestFailed, const FGuid&, RequestId,
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestFailed, const FGuid&, RequestId,
|
||||||
EDTFluxApiDataType, RequestType, const FString&, ErrorMessage);
|
EDTFluxApiDataType, RequestType, const FString&, ErrorMessage);
|
||||||
|
|
||||||
|
// ================================================================================================
|
||||||
|
// DELEGATES LEGACY POUR LA COMPATIBILITÉ
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnRaceDataReceived, const FDTFluxRaceData& /*RaceDataDefinition*/);
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnTeamListReceived, const FDTFluxTeamListDefinition& /*TeamListDefinition*/);
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnStageRankingReceived, const FDTFluxStageRankings& /*StageRankings*/);
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnSplitRankingReceived, const FDTFluxSplitRankings& /*SplitRankings*/);
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnContestRankingReceived, const FDTFluxContestRankings& /*ContestRankings*/);
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnSplitSensorReceived, const FDTFluxSplitSensorInfo& /*SplitSensorInfo*/);
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnTeamUpdateReceived, const FDTFluxTeamListDefinition& /*ParticipantToUpdate*/);
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnTeamStatusUpdateReceived, const FDTFluxTeamStatusUpdate& /*TeamToUpdate*/);
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWebSocketConnected);
|
||||||
|
|
||||||
|
// ================================================================================================
|
||||||
|
// NETWORK SUBSYSTEM - Interface UObject avec compatibilité Blueprint
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Subsystem réseau DTFlux avec support complet des requêtes trackées et compatibilité legacy
|
||||||
|
* Combine l'efficacité du RequestManager C++ avec l'interface Blueprint UObject
|
||||||
*/
|
*/
|
||||||
UCLASS(Blueprintable)
|
UCLASS(Blueprintable)
|
||||||
class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem
|
class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem
|
||||||
@ -43,180 +56,236 @@ class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY()
|
// === ÉTAT DE CONNEXION ===
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Network")
|
||||||
EDTFluxConnectionStatus WsStatus = EDTFluxConnectionStatus::Unset;
|
EDTFluxConnectionStatus WsStatus = EDTFluxConnectionStatus::Unset;
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWebSocketConnected);
|
// === CONNEXION WEBSOCKET (Legacy) ===
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Network")
|
/**
|
||||||
FOnWebSocketConnected OnWebSocketConnected;
|
* Se connecter au serveur WebSocket
|
||||||
|
*/
|
||||||
DECLARE_DELEGATE_OneParam(FOnRaceDataReceived, const FDTFluxRaceData& /*RaceDataDefinition*/);
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
||||||
FOnRaceDataReceived OnRaceDataReceived;
|
|
||||||
|
|
||||||
FOnRaceDataReceived& OnReceivedRaceData()
|
|
||||||
{
|
|
||||||
return OnRaceDataReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
// === DELEGATES POUR LES DONNÉES REÇUES (PUSH) ===
|
|
||||||
DECLARE_DELEGATE_OneParam(FOnTeamListReceived, const FDTFluxTeamListDefinition& /*TeamListDefinition*/);
|
|
||||||
FOnTeamListReceived OnTeamListReceived;
|
|
||||||
|
|
||||||
FOnTeamListReceived& OnReceivedTeamList()
|
|
||||||
{
|
|
||||||
return OnTeamListReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_DELEGATE_OneParam(FOnStageRankingReceived, const FDTFluxStageRankings& /*StageRankings*/);
|
|
||||||
FOnStageRankingReceived OnStageRankingReceived;
|
|
||||||
|
|
||||||
FOnStageRankingReceived& OnReceivedStageRanking()
|
|
||||||
{
|
|
||||||
return OnStageRankingReceived;
|
|
||||||
}
|
|
||||||
|
|
||||||
DECLARE_DELEGATE_OneParam(FOnSplitRankingReceived, const FDTFluxSplitRankings& /*SplitRankings*/);
|
|
||||||
FOnSplitRankingReceived OnSplitRankingReceived;
|
|
||||||
|
|
||||||
FOnSplitRankingReceived& OnReceivedSplitRanking()
|
|
||||||
{
|
|
||||||
return OnSplitRankingReceived;
|
|
||||||
}
|
|
||||||
|
|
||||||
DECLARE_DELEGATE_OneParam(FOnContestRankingReceived, const FDTFluxContestRankings& /*ContestRankings*/);
|
|
||||||
FOnContestRankingReceived OnContestRankingReceived;
|
|
||||||
|
|
||||||
FOnContestRankingReceived& OnReceivedContestRanking()
|
|
||||||
{
|
|
||||||
return OnContestRankingReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
// === DELEGATES POUR LES DONNÉES REÇUES (PULL) ===
|
|
||||||
DECLARE_DELEGATE_OneParam(FOnSplitSensorReceived, const FDTFluxSplitSensorInfo& /*ContestRankings*/);
|
|
||||||
FOnSplitSensorReceived OnSplitSensorReceived;
|
|
||||||
|
|
||||||
FOnSplitSensorReceived& OnReceivedSplitSensor()
|
|
||||||
{
|
|
||||||
return OnSplitSensorReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_DELEGATE_OneParam(FOnTeamUpdateReceived, const FDTFluxTeamListDefinition& /*ParticipantToUpdate*/);
|
|
||||||
FOnTeamUpdateReceived OnTeamUpdateReceived;
|
|
||||||
|
|
||||||
FOnTeamUpdateReceived& OnReceivedTeamUpdate()
|
|
||||||
{
|
|
||||||
return OnTeamUpdateReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_DELEGATE_OneParam(FOnTeamStatusUpdateReceived, const FDTFluxTeamStatusUpdate& /*TeamToUpdate*/);
|
|
||||||
FOnTeamStatusUpdateReceived OnTeamStatusUpdateReceived;
|
|
||||||
|
|
||||||
FOnTeamStatusUpdateReceived& OnReceivedTeamStatusUpdate()
|
|
||||||
{
|
|
||||||
return OnTeamStatusUpdateReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Network")
|
|
||||||
void Connect();
|
void Connect();
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Network")
|
|
||||||
|
/**
|
||||||
|
* Se déconnecter du serveur WebSocket
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
||||||
void Disconnect();
|
void Disconnect();
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Network")
|
|
||||||
|
/**
|
||||||
|
* Reconnecter au serveur WebSocket
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
||||||
void Reconnect();
|
void Reconnect();
|
||||||
|
|
||||||
|
// === REQUÊTES TRACKÉES (Nouveau système optimisé) ===
|
||||||
|
|
||||||
// === REQUÊTES AVEC QUEUE ET TRACKING ===
|
/**
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
* Envoyer une requête trackée avec cache, timeout et retry
|
||||||
FGuid SendTrackedRequest(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
* @param RequestType Type de requête (ContestRanking, StageRanking, etc.)
|
||||||
int32 SplitId = -1, float TimeoutSeconds = 30.0f);
|
* @param ContestId ID du contest (-1 si non applicable)
|
||||||
|
* @param StageId ID du stage (-1 si non applicable)
|
||||||
|
* @param SplitId ID du split (-1 si non applicable)
|
||||||
|
* @param TimeoutSeconds Timeout en secondes
|
||||||
|
* @param MaxRetries Nombre maximum de tentatives
|
||||||
|
* @param bEnableCache Activer le cache pour cette requête
|
||||||
|
* @return GUID de la requête pour le suivi
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
|
FGuid SendTrackedRequest(
|
||||||
|
EDTFluxApiDataType RequestType,
|
||||||
|
int32 ContestId = -1,
|
||||||
|
int32 StageId = -1,
|
||||||
|
int32 SplitId = -1,
|
||||||
|
float TimeoutSeconds = 5.0f,
|
||||||
|
int32 MaxRetries = 3,
|
||||||
|
bool bEnableCache = true
|
||||||
|
);
|
||||||
|
|
||||||
FGuid SendTrackedRequestWithCallback(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId, int32 SplitId,
|
/**
|
||||||
FOnDTFluxTrackedRequestResponse OnCompleted,
|
* Envoyer une requête trackée avec callbacks C++ (non Blueprint)
|
||||||
FOnDTFluxTrackedRequestTimeout OnTimeout,
|
* @param RequestType Type de requête
|
||||||
TOptional<FOnDTFluxRequestResponseError> OnError = TOptional<
|
* @param ContestId ID du contest
|
||||||
FOnDTFluxRequestResponseError>(),
|
* @param StageId ID du stage
|
||||||
float TimeoutSeconds = 30.0f);
|
* @param SplitId ID du split
|
||||||
|
* @param OnSuccess Callback appelé en cas de succès
|
||||||
|
* @param OnError Callback appelé en cas d'erreur
|
||||||
|
* @param TimeoutSeconds Timeout en secondes
|
||||||
|
* @param MaxRetries Nombre maximum de tentatives
|
||||||
|
* @param bEnableCache Activer le cache
|
||||||
|
* @return GUID de la requête
|
||||||
|
*/
|
||||||
|
FGuid SendTrackedRequestWithCallbacks(
|
||||||
|
EDTFluxApiDataType RequestType,
|
||||||
|
int32 ContestId,
|
||||||
|
int32 StageId,
|
||||||
|
int32 SplitId,
|
||||||
|
FOnDTFluxRequestSuccess& OnSuccess,
|
||||||
|
FOnDTFluxRequestError& OnError,
|
||||||
|
float TimeoutSeconds = 5.0f,
|
||||||
|
int32 MaxRetries = 3,
|
||||||
|
bool bEnableCache = true
|
||||||
|
);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
// === ACCESSEURS BLUEPRINT POUR LES REQUÊTES TRACKÉES ===
|
||||||
bool GetTrackedRequest(const FGuid& RequestId, FDTFluxQueuedRequest& OutRequest) const;
|
|
||||||
const FDTFluxQueuedRequest* GetTrackedRequestPtr(const FGuid& RequestId) const;
|
/**
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests", CallInEditor)
|
* Récupérer une requête trackée par son ID
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
|
bool GetTrackedRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifier si une requête a reçu une réponse
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
bool HasRequestReceivedResponse(const FGuid& RequestId) const;
|
bool HasRequestReceivedResponse(const FGuid& RequestId) const;
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
|
||||||
|
/**
|
||||||
|
* Récupérer les données de réponse d'une requête
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
FString GetRequestResponseData(const FGuid& RequestId) const;
|
FString GetRequestResponseData(const FGuid& RequestId) const;
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
|
||||||
|
/**
|
||||||
|
* Vérifier si une requête similaire est en attente
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
bool IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
bool IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
||||||
int32 SplitId = -1) const;
|
int32 SplitId = -1) const;
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
|
||||||
|
/**
|
||||||
|
* Compter le nombre de requêtes en attente
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
int32 GetPendingRequestCount() const;
|
int32 GetPendingRequestCount() const;
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
|
||||||
UDTFluxQueuedManager* GetQueueManager() const;
|
|
||||||
|
|
||||||
// === EVENTS BLUEPRINT POUR LE TRACKING ===
|
/**
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Tracked Requests")
|
* Récupérer les statistiques du gestionnaire de requêtes
|
||||||
FOnDTFluxTrackedRequestCompleted OnTrackedRequestCompleted;
|
*/
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Tracked Requests")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
FOnDTFluxTrackedRequestFailed OnTrackedRequestFailed;
|
void GetRequestStatistics(int32& OutPending, int32& OutCached, int32& OutCompleted, int32& OutFailed,
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
float& OutHitRate) const;
|
||||||
|
|
||||||
// === REQUÊTES DIRECTES (LEGACY) ===
|
// === REQUÊTES LEGACY (Compatibilité totale) ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Envoyer une requête en mode legacy (pour compatibilité)
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Legacy")
|
||||||
void SendRequest(const EDTFluxApiDataType RequestType, int InContestId = -1, int InStageId = -1,
|
void SendRequest(const EDTFluxApiDataType RequestType, int InContestId = -1, int InStageId = -1,
|
||||||
int InSplitId = -1);
|
int InSplitId = -1);
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Network")
|
|
||||||
|
/**
|
||||||
|
* Envoyer un message brut via WebSocket
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
||||||
void SendMessage(const FString& Message);
|
void SendMessage(const FString& Message);
|
||||||
|
|
||||||
|
// === EVENTS BLUEPRINT ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event déclenché lors de la connexion WebSocket
|
||||||
|
*/
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Network")
|
||||||
|
FOnWebSocketConnected OnWebSocketConnected;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event déclenché quand une requête trackée se termine avec succès
|
||||||
|
*/
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests")
|
||||||
|
FOnDTFluxTrackedRequestCompleted OnTrackedRequestCompleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event déclenché quand une requête trackée échoue
|
||||||
|
*/
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests")
|
||||||
|
FOnDTFluxTrackedRequestFailed OnTrackedRequestFailed;
|
||||||
|
|
||||||
|
// === DELEGATES LEGACY (Compatibilité totale) ===
|
||||||
|
|
||||||
|
FOnRaceDataReceived OnRaceDataReceived;
|
||||||
|
FOnTeamListReceived OnTeamListReceived;
|
||||||
|
FOnStageRankingReceived OnStageRankingReceived;
|
||||||
|
FOnSplitRankingReceived OnSplitRankingReceived;
|
||||||
|
FOnContestRankingReceived OnContestRankingReceived;
|
||||||
|
FOnSplitSensorReceived OnSplitSensorReceived;
|
||||||
|
FOnTeamUpdateReceived OnTeamUpdateReceived;
|
||||||
|
FOnTeamStatusUpdateReceived OnTeamStatusUpdateReceived;
|
||||||
|
|
||||||
|
// Accesseurs pour la compatibilité legacy
|
||||||
|
FOnRaceDataReceived& OnReceivedRaceData() { return OnRaceDataReceived; }
|
||||||
|
FOnTeamListReceived& OnReceivedTeamList() { return OnTeamListReceived; }
|
||||||
|
FOnStageRankingReceived& OnReceivedStageRanking() { return OnStageRankingReceived; }
|
||||||
|
FOnSplitRankingReceived& OnReceivedSplitRanking() { return OnSplitRankingReceived; }
|
||||||
|
FOnContestRankingReceived& OnReceivedContestRanking() { return OnContestRankingReceived; }
|
||||||
|
FOnSplitSensorReceived& OnReceivedSplitSensor() { return OnSplitSensorReceived; }
|
||||||
|
FOnTeamUpdateReceived& OnReceivedTeamUpdate() { return OnTeamUpdateReceived; }
|
||||||
|
FOnTeamStatusUpdateReceived& OnReceivedTeamStatusUpdate() { return OnTeamStatusUpdateReceived; }
|
||||||
|
|
||||||
|
// === ACCESSEUR PUBLIC POUR LE REQUEST MANAGER ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accéder au gestionnaire de requêtes (pour usage avancé)
|
||||||
|
*/
|
||||||
|
TSharedPtr<FDTFluxQueuedRequestManager> GetRequestManager() const { return RequestManager; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// ~Subsystem Interface
|
// === LIFECYCLE DU SUBSYSTEM ===
|
||||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||||
virtual void Deinitialize() override;
|
virtual void Deinitialize() override;
|
||||||
// ~Subsystem Interface
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// === CONFIGURATION ===
|
// === CONFIGURATION ===
|
||||||
FDTFluxWsSettings WsSettings;
|
FDTFluxWsSettings WsSettings;
|
||||||
FDTFluxHttpSettings HttpSettings;
|
|
||||||
|
|
||||||
UPROPERTY()
|
|
||||||
UDTFluxQueuedManager* QueueManager;
|
|
||||||
|
|
||||||
// === MAPPING DES CALLBACKS C++ ===
|
|
||||||
TMap<FGuid, FOnDTFluxTrackedRequestResponse> PendingCallbacks;
|
|
||||||
TMap<FGuid, FOnDTFluxTrackedRequestTimeout> PendingTimeoutCallbacks;
|
|
||||||
TMap<FGuid, FOnDTFluxRequestResponseError> PendingErrorCallbacks;
|
|
||||||
|
|
||||||
// === CLIENTS RÉSEAU ===
|
// === CLIENTS RÉSEAU ===
|
||||||
FDTFluxWebSocketClientSP WsClient = nullptr;
|
FDTFluxWebSocketClientSP WsClient = nullptr;
|
||||||
FDTFluxHttpClientSP HttpClient = nullptr;
|
|
||||||
|
|
||||||
// === MÉTHODES DE CONFIGURATION ===
|
// === REQUEST MANAGER C++ ===
|
||||||
UFUNCTION()
|
TSharedPtr<FDTFluxQueuedRequestManager> RequestManager;
|
||||||
void WsSettingsChanged(const FDTFluxWsSettings& NewWsSettings);
|
|
||||||
UFUNCTION()
|
|
||||||
void HttpSettingsChanged(const FDTFluxHttpSettings& NewHttpSettings);
|
|
||||||
void ReconnectWs(const FName WsClientId);
|
|
||||||
void ReconnectHttp(const FName WsClientId);
|
|
||||||
|
|
||||||
// === GESTION DES ÉVÉNEMENTS WEBSOCKET ===
|
// === GESTION DES ÉVÉNEMENTS WEBSOCKET ===
|
||||||
void RegisterWebSocketEvents();
|
void RegisterWebSocketEvents();
|
||||||
void UnregisterWebSocketEvents();
|
void UnregisterWebSocketEvents();
|
||||||
|
|
||||||
void OnWebSocketConnected_Subsystem();
|
void OnWebSocketConnected_Subsystem();
|
||||||
void OnWebSocketConnectionError_Subsystem(const FString& Error);
|
void OnWebSocketConnectionError_Subsystem(const FString& Error);
|
||||||
void OnWebSocketClosedEvent_Subsystem(int32 StatusCode, const FString& Reason, bool bWasClean);
|
void OnWebSocketClosedEvent_Subsystem(int32 StatusCode, const FString& Reason, bool bWasClean);
|
||||||
|
void OnWebSocketMessageEvent_Subsystem(const FString& MessageString);
|
||||||
|
void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent);
|
||||||
|
|
||||||
|
// Handles pour les événements WebSocket
|
||||||
FDelegateHandle OnWsConnectedEventDelegateHandle;
|
FDelegateHandle OnWsConnectedEventDelegateHandle;
|
||||||
FDelegateHandle OnWsConnectionErrorEventDelegateHandle;
|
FDelegateHandle OnWsConnectionErrorEventDelegateHandle;
|
||||||
FDelegateHandle OnWsClosedEventDelegateHandle;
|
FDelegateHandle OnWsClosedEventDelegateHandle;
|
||||||
FDelegateHandle OnWsMessageEventDelegateHandle;
|
FDelegateHandle OnWsMessageEventDelegateHandle;
|
||||||
FDelegateHandle OnWsMessageSentEventDelegateHandle;
|
FDelegateHandle OnWsMessageSentEventDelegateHandle;
|
||||||
|
|
||||||
// === GESTION DES ÉVÉNEMENTS HTTP ===
|
// === PARSING ET TRAITEMENT DES RÉPONSES ===
|
||||||
void RegisterHttpEvents();
|
|
||||||
void UnregisterHttpEvents();
|
|
||||||
|
|
||||||
// === PARSING DES RÉPONSES ===
|
/**
|
||||||
void ParseTeamListResponse(FDTFluxServerResponse& ServerResponse);
|
* Essayer de matcher une réponse à une requête trackée
|
||||||
|
* @param MessageString Message JSON reçu
|
||||||
|
* @return true si la réponse correspond à une requête trackée
|
||||||
|
*/
|
||||||
|
bool TryMatchResponseToQueuedRequest(const FString& MessageString);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traiter une réponse en mode legacy
|
||||||
|
* @param MessageString Message JSON à traiter
|
||||||
|
*/
|
||||||
|
void ProcessLegacyResponse(const FString& MessageString);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traiter une réponse déjà parsée
|
||||||
|
* @param ParsedResponse Réponse parsée à traiter
|
||||||
|
*/
|
||||||
|
void ProcessParsedResponse(TSharedPtr<FDTFluxServerResponse> ParsedResponse);
|
||||||
|
|
||||||
|
// === MÉTHODES DE PARSING LEGACY (pour compatibilité) ===
|
||||||
|
void ParseTeamListResponse(FDTFluxServerResponse& Response);
|
||||||
void ParseRaceData(FDTFluxServerResponse& Response);
|
void ParseRaceData(FDTFluxServerResponse& Response);
|
||||||
void ParseContestRanking(FDTFluxServerResponse& Response);
|
void ParseContestRanking(FDTFluxServerResponse& Response);
|
||||||
void ParseStageRankingResponse(FDTFluxServerResponse& Response);
|
void ParseStageRankingResponse(FDTFluxServerResponse& Response);
|
||||||
@ -225,19 +294,45 @@ private:
|
|||||||
void ParseSplitSensorResponse(FDTFluxServerResponse& Response);
|
void ParseSplitSensorResponse(FDTFluxServerResponse& Response);
|
||||||
EDTFluxResponseStatus ProcessPushMessage(FDTFluxServerResponse& Response);
|
EDTFluxResponseStatus ProcessPushMessage(FDTFluxServerResponse& Response);
|
||||||
|
|
||||||
void Parse(FDTFluxServerResponse& Response);
|
// === CALLBACKS POUR LE REQUEST MANAGER ===
|
||||||
void OnWebSocketMessageEvent_Subsystem(const FString& MessageString);
|
|
||||||
void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent);
|
|
||||||
bool CleanRequestCallbacks(const FGuid& RequestId);
|
|
||||||
|
|
||||||
// === GESTION DES REQUÊTES TRACKÉES ===
|
/**
|
||||||
|
* Callback appelé quand une requête trackée se termine
|
||||||
|
*/
|
||||||
|
void OnRequestCompleted_Internal(const FDTFluxTrackedRequest& CompletedRequest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback appelé quand une requête trackée échoue
|
||||||
|
*/
|
||||||
|
void OnRequestFailed_Internal(const FDTFluxTrackedRequest& FailedRequest);
|
||||||
|
|
||||||
|
// === CONFIGURATION DYNAMIQUE ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback appelé quand les paramètres WebSocket changent
|
||||||
|
*/
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void OnRequestTimedOut_Internal(const FDTFluxQueuedRequest& TimedOutRequest);
|
void WsSettingsChanged(const FDTFluxWsSettings& NewWsSettings);
|
||||||
bool TryMatchResponseToQueuedRequest(FDTFluxServerResponse& Response);
|
|
||||||
void CompleteTrackedRequest(const FGuid& RequestId, const FString& ResponseData, EDTFluxRequestType RequestType);
|
/**
|
||||||
void FailTrackedRequest(const FGuid& RequestId, const FString& ErrorMessage, EDTFluxRequestType RequestType);
|
* Reconnecter le client WebSocket
|
||||||
void SendQueuedRequest(const FDTFluxQueuedRequest& QueuedRequest);
|
*/
|
||||||
|
void ReconnectWs(const FName WsClientId);
|
||||||
|
|
||||||
// === UTILITAIRES ===
|
// === UTILITAIRES ===
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construire une adresse WebSocket complète
|
||||||
|
*/
|
||||||
static FString ConstructWsAddress(const FString& Address, const FString& Path, const int& Port);
|
static FString ConstructWsAddress(const FString& Address, const FString& Path, const int& Port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Envoyer une requête trackée via le réseau
|
||||||
|
*/
|
||||||
|
void SendQueuedRequest(const FDTFluxTrackedRequest& QueuedRequest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Déterminer si on doit utiliser le parsing asynchrone
|
||||||
|
*/
|
||||||
|
bool ShouldUseAsyncParsing(const FString& JsonData) const;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user