Compare commits
1 Commits
d6b8874827
...
feature/Ra
| Author | SHA1 | Date | |
|---|---|---|---|
| d419681172 |
41
Resources/DTFluxRaceResult16x16.svg
Normal file
41
Resources/DTFluxRaceResult16x16.svg
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="512"
|
||||||
|
height="512"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:export-filename="RaceResult.icone16x16.svg"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
sodipodi:docname="RaceResult.icone16x16.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px" /><defs
|
||||||
|
id="defs1" /><g
|
||||||
|
inkscape:label="Calque 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(3.0517578e-5)"><rect
|
||||||
|
style="fill:#e22c2b;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
|
||||||
|
id="rect1"
|
||||||
|
width="512"
|
||||||
|
height="512"
|
||||||
|
x="-3.0517578e-05"
|
||||||
|
y="0" /><path
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:15;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
|
||||||
|
d="m 154.64305,277.08769 -1.3605,-151.92206 146.48007,103.3977 v 184.12046 l 54.87334,-50.79185 V 199.53942 L 215.41187,92.967228 V 328.78654 Z"
|
||||||
|
id="path1" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
@ -31,7 +31,6 @@ 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,109 +25,45 @@ 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);
|
|
||||||
|
|
||||||
UToolMenus::RegisterStartupCallback(
|
MenuExtender->AddMenuBarExtension(
|
||||||
FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FDTFluxAPIStatusModule::RegisterMenus)
|
"Help",
|
||||||
|
EExtensionHook::Before,
|
||||||
|
nullptr,
|
||||||
|
FMenuBarExtensionDelegate::CreateRaw(this, &FDTFluxAPIStatusModule::AddMenu)
|
||||||
);
|
);
|
||||||
|
LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxAPIStatusModule::RegisterMenus()
|
void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::AddMenu(FMenuBarBuilder& MenuBarBuilder)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxStatus, Warning, TEXT("Creating DTFlux menu"));
|
MenuBarBuilder.AddPullDownMenu(
|
||||||
|
|
||||||
// 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"),
|
||||||
FText::FromString("DTFlux API Tools"),
|
FText::FromString("DTFlux API Tools"),
|
||||||
FNewToolMenuDelegate::CreateLambda([](UToolMenu* Menu)
|
FNewMenuDelegate::CreateRaw(this, &FDTFluxAPIStatusModule::FillMenu)
|
||||||
{
|
|
||||||
// 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 DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::FillMenu(FMenuBuilder& MenuBuilder)
|
||||||
|
|
||||||
void FDTFluxAPIStatusModule::CreateSubmenu(UToolMenu* Menu)
|
|
||||||
{
|
{
|
||||||
FToolMenuSection& DTFluxAPISection = Menu->FindOrAddSection("DTFluxAPI");
|
MenuBuilder.BeginSection(NAME_None, FText::FromString("DTFlux API"));
|
||||||
DTFluxAPISection.Label = FText::FromString("DTFlux API");
|
MenuBuilder.AddMenuEntry(
|
||||||
// Cette section est vide pour le moment, prête pour de futurs boutons
|
FText::FromString("Status"),
|
||||||
|
FText::FromString("Launch DTFlux Status"),
|
||||||
|
|
||||||
// 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"),
|
FSlateIcon(FDTFluxStatusStyle::GetStyleSetName(), "LevelEditor.Tab.Icon"),
|
||||||
FUIAction(FExecuteAction::CreateRaw(this, &FDTFluxAPIStatusModule::OnButtonClicked))
|
FExecuteAction::CreateRaw(this, &FDTFluxAPIStatusModule::OnButtonClicked)
|
||||||
);
|
);
|
||||||
|
MenuBuilder.EndSection();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
|
|||||||
@ -338,8 +338,7 @@ void SDTFluxStatusWidget::PopulateComboBoxItems()
|
|||||||
{
|
{
|
||||||
FString Separator = " | ";
|
FString Separator = " | ";
|
||||||
FString RootSeparator = " -> ";
|
FString RootSeparator = " -> ";
|
||||||
TArray<FDTFluxContest> DataFromSubsystem = TArray<FDTFluxContest>();
|
TArray<FDTFluxContest> DataFromSubsystem = DTFluxCore->GetContests();
|
||||||
DTFluxCore->GetContests(DataFromSubsystem);
|
|
||||||
|
|
||||||
for (const auto& Contest : DataFromSubsystem)
|
for (const auto& Contest : DataFromSubsystem)
|
||||||
{
|
{
|
||||||
@ -417,7 +416,7 @@ FSlateColor SDTFluxStatusWidget::GetComboItemRankingColor(const TSharedPtr<FComb
|
|||||||
|
|
||||||
FReply SDTFluxStatusWidget::OnRankingButtonClicked() const
|
FReply SDTFluxStatusWidget::OnRankingButtonClicked() const
|
||||||
{
|
{
|
||||||
if (DTFluxCore)
|
if (DTFluxNetwork)
|
||||||
{
|
{
|
||||||
// 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;
|
||||||
@ -433,19 +432,13 @@ 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"));
|
||||||
DTFluxCore->TrackedRequestContestRankings({ForContest});
|
RequestType = EDTFluxApiDataType::ContestRanking;
|
||||||
|
DTFluxNetwork->SendRequest(RequestType, ForContest);
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
}
|
}
|
||||||
if (ForSplit == -1)
|
RequestType = ForSplit == -1 ? EDTFluxApiDataType::StageRanking : EDTFluxApiDataType::SplitRanking;
|
||||||
{
|
|
||||||
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();
|
||||||
@ -27,6 +27,8 @@ void FDTFluxStatusStyle::UnregisterStyle()
|
|||||||
ensure(StyleSet.IsUnique());
|
ensure(StyleSet.IsUnique());
|
||||||
StyleSet.Reset();
|
StyleSet.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxStatusStyle::ReloadTextures()
|
void FDTFluxStatusStyle::ReloadTextures()
|
||||||
@ -41,3 +43,4 @@ TSharedPtr<ISlateStyle> FDTFluxStatusStyle::Create()
|
|||||||
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,11 +13,10 @@ public:
|
|||||||
|
|
||||||
#pragma region MenuExtention
|
#pragma region MenuExtention
|
||||||
void InitMenuExtension();
|
void InitMenuExtension();
|
||||||
void RegisterMenus();
|
void AddMenu(FMenuBarBuilder& MenuBarBuilder);
|
||||||
void CreateSubmenu(UToolMenu* Menu);
|
void FillMenu(FMenuBuilder& MenuBuilder);
|
||||||
// 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
|
||||||
@ -26,6 +25,7 @@ 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
|
||||||
};
|
};
|
||||||
|
|||||||
@ -100,20 +100,6 @@ void FDTFluxModelAssetCustomization::CustomizeDetailsWithoutRawDataAsset(IDetail
|
|||||||
|
|
||||||
void FDTFluxModelAssetCustomization::CustomizeDetailsWithRawDataAccess(IDetailLayoutBuilder& DetailBuilder)
|
void FDTFluxModelAssetCustomization::CustomizeDetailsWithRawDataAccess(IDetailLayoutBuilder& DetailBuilder)
|
||||||
{
|
{
|
||||||
// Edit object
|
|
||||||
TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
|
|
||||||
DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
|
|
||||||
|
|
||||||
if (ObjectsBeingCustomized.Num() > 0)
|
|
||||||
{
|
|
||||||
ModelAsset = Cast<UDTFluxModelAsset>(ObjectsBeingCustomized[0].Get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ModelAsset.IsValid())
|
|
||||||
{
|
|
||||||
UE_LOG(LogTemp, Error, TEXT("No valid DTFluxModelAsset found"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// ===== WIDGET PRINCIPAL =====
|
// ===== WIDGET PRINCIPAL =====
|
||||||
IDetailCategoryBuilder& MainCategory = DetailBuilder.EditCategory(
|
IDetailCategoryBuilder& MainCategory = DetailBuilder.EditCategory(
|
||||||
"DTFlux Model Explorer",
|
"DTFlux Model Explorer",
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
#include "Widget/DTFluxAssetModelDetailsWidget.h"
|
#include "Widget/DTFluxAssetModelDetailsWidget.h"
|
||||||
|
|
||||||
#include "DTFluxAssetsEditorModule.h"
|
|
||||||
#include "Widgets/Layout/SBorder.h"
|
#include "Widgets/Layout/SBorder.h"
|
||||||
#include "Widgets/Layout/SBox.h"
|
#include "Widgets/Layout/SBox.h"
|
||||||
#include "Widgets/Text/STextBlock.h"
|
#include "Widgets/Text/STextBlock.h"
|
||||||
@ -24,19 +23,19 @@ TSharedRef<SWidget> SHierarchicalTreeItemRow::GenerateWidgetForColumn(const FNam
|
|||||||
{
|
{
|
||||||
if (!Item.IsValid())
|
if (!Item.IsValid())
|
||||||
{
|
{
|
||||||
// UE_LOG(logDTFluxAssetEditor, Warning, TEXT("GenerateWidgetForColumn: Invalid item for column %s"), *ColumnName.ToString());
|
UE_LOG(LogTemp, Warning, TEXT("GenerateWidgetForColumn: Invalid item for column %s"), *ColumnName.ToString());
|
||||||
return SNew(STextBlock).Text(FText::FromString("Invalid Item"));
|
return SNew(STextBlock).Text(FText::FromString("Invalid Item"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ParentWidget)
|
if (!ParentWidget)
|
||||||
{
|
{
|
||||||
// UE_LOG(logDTFluxAssetEditor, Warning, TEXT("GenerateWidgetForColumn: Invalid ParentWidget for column %s"),
|
UE_LOG(LogTemp, Warning, TEXT("GenerateWidgetForColumn: Invalid ParentWidget for column %s"),
|
||||||
// *ColumnName.ToString());
|
*ColumnName.ToString());
|
||||||
return SNew(STextBlock).Text(FText::FromString("Invalid Parent"));
|
return SNew(STextBlock).Text(FText::FromString("Invalid Parent"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// UE_LOG(logDTFluxAssetEditor, VeryVerbose, TEXT("GenerateWidgetForColumn: %s for item %s"),
|
UE_LOG(LogTemp, VeryVerbose, TEXT("GenerateWidgetForColumn: %s for item %s"),
|
||||||
// *ColumnName.ToString(), *Item->Name);
|
*ColumnName.ToString(), *Item->Name);
|
||||||
|
|
||||||
if (ColumnName == "Name")
|
if (ColumnName == "Name")
|
||||||
{
|
{
|
||||||
@ -94,7 +93,7 @@ TSharedRef<SWidget> SHierarchicalTreeItemRow::GenerateWidgetForColumn(const FNam
|
|||||||
.OverflowPolicy(ETextOverflowPolicy::Ellipsis);
|
.OverflowPolicy(ETextOverflowPolicy::Ellipsis);
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(logDTFluxAssetEditor, Warning, TEXT("GenerateWidgetForColumn: Unknown column %s"), *ColumnName.ToString());
|
UE_LOG(LogTemp, Warning, TEXT("GenerateWidgetForColumn: Unknown column %s"), *ColumnName.ToString());
|
||||||
return SNew(STextBlock).Text(FText::FromString(FString::Printf(TEXT("Unknown: %s"), *ColumnName.ToString())));
|
return SNew(STextBlock).Text(FText::FromString(FString::Printf(TEXT("Unknown: %s"), *ColumnName.ToString())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,7 +411,7 @@ void SDTFluxAssetModelDetailsWidget::BuildContestHierarchy()
|
|||||||
RootItems.Add(ContestItem);
|
RootItems.Add(ContestItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("Built contest hierarchy with %d root contests"), RootItems.Num());
|
UE_LOG(LogTemp, Log, TEXT("Built contest hierarchy with %d root contests"), RootItems.Num());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDTFluxAssetModelDetailsWidget::BuildParticipantList()
|
void SDTFluxAssetModelDetailsWidget::BuildParticipantList()
|
||||||
@ -421,11 +420,11 @@ void SDTFluxAssetModelDetailsWidget::BuildParticipantList()
|
|||||||
|
|
||||||
if (!ModelAsset)
|
if (!ModelAsset)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxAssetEditor, Warning, TEXT("BuildParticipantList: ModelAsset is null!"));
|
UE_LOG(LogTemp, Warning, TEXT("BuildParticipantList: ModelAsset is null!"));
|
||||||
// return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("BuildParticipantList: ModelAsset has %d participants"), ModelAsset->Participants.Num());
|
UE_LOG(LogTemp, Log, TEXT("BuildParticipantList: ModelAsset has %d participants"), ModelAsset->Participants.Num());
|
||||||
|
|
||||||
// Créer la liste des participants (pas de hiérarchie pour les participants)
|
// Créer la liste des participants (pas de hiérarchie pour les participants)
|
||||||
for (const auto& ParticipantPair : ModelAsset->Participants)
|
for (const auto& ParticipantPair : ModelAsset->Participants)
|
||||||
@ -434,12 +433,12 @@ void SDTFluxAssetModelDetailsWidget::BuildParticipantList()
|
|||||||
auto ParticipantItem = FHierarchicalTreeItem::CreateParticipant(Participant);
|
auto ParticipantItem = FHierarchicalTreeItem::CreateParticipant(Participant);
|
||||||
ParticipantItems.Add(ParticipantItem);
|
ParticipantItems.Add(ParticipantItem);
|
||||||
|
|
||||||
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("BuildParticipantList: Added participant %s (Bib: %d)"),
|
UE_LOG(LogTemp, Log, TEXT("BuildParticipantList: Added participant %s (Bib: %d)"),
|
||||||
// *ParticipantItem->Name, ParticipantItem->Bib);
|
*ParticipantItem->Name, ParticipantItem->Bib);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("BuildParticipantList: Built participant list with %d participants"),
|
UE_LOG(LogTemp, Log, TEXT("BuildParticipantList: Built participant list with %d participants"),
|
||||||
// ParticipantItems.Num());
|
ParticipantItems.Num());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== CALLBACKS TREEVIEW =====
|
// ===== CALLBACKS TREEVIEW =====
|
||||||
@ -449,12 +448,12 @@ TSharedRef<ITableRow> SDTFluxAssetModelDetailsWidget::OnGenerateRowForTree(
|
|||||||
{
|
{
|
||||||
if (!Item.IsValid())
|
if (!Item.IsValid())
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxAssetEditor, Warning, TEXT("OnGenerateRowForTree: Invalid item!"));
|
UE_LOG(LogTemp, Warning, TEXT("OnGenerateRowForTree: Invalid item!"));
|
||||||
return SNew(STableRow<FHierarchicalTreeItemPtr>, OwnerTable);
|
return SNew(STableRow<FHierarchicalTreeItemPtr>, OwnerTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("OnGenerateRowForTree: Generating row for %s (Type: %d)"),
|
UE_LOG(LogTemp, Log, TEXT("OnGenerateRowForTree: Generating row for %s (Type: %d)"),
|
||||||
// *Item->Name, (int32)Item->Type);
|
*Item->Name, (int32)Item->Type);
|
||||||
|
|
||||||
return SNew(SHierarchicalTreeItemRow, OwnerTable)
|
return SNew(SHierarchicalTreeItemRow, OwnerTable)
|
||||||
.Item(Item)
|
.Item(Item)
|
||||||
@ -537,7 +536,7 @@ FReply SDTFluxAssetModelDetailsWidget::OnExpandAllClicked()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("Expanded all contests"));
|
UE_LOG(LogTemp, Log, TEXT("Expanded all contests"));
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,14 +550,14 @@ FReply SDTFluxAssetModelDetailsWidget::OnCollapseAllClicked()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("Collapsed all contests"));
|
UE_LOG(LogTemp, Log, TEXT("Collapsed all contests"));
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
}
|
}
|
||||||
|
|
||||||
FReply SDTFluxAssetModelDetailsWidget::OnRefreshClicked()
|
FReply SDTFluxAssetModelDetailsWidget::OnRefreshClicked()
|
||||||
{
|
{
|
||||||
RefreshData();
|
RefreshData();
|
||||||
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("Data refreshed"));
|
UE_LOG(LogTemp, Log, TEXT("Data refreshed"));
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,17 +567,21 @@ void SDTFluxAssetModelDetailsWidget::RefreshData()
|
|||||||
{
|
{
|
||||||
if (!ModelAsset)
|
if (!ModelAsset)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxAssetEditor, Warning, TEXT("ModelAsset is null!"));
|
UE_LOG(LogTemp, Warning, TEXT("ModelAsset is null!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("RefreshData: Starting refresh for ModelAsset %s"), *ModelAsset->GetName());
|
UE_LOG(LogTemp, Log, TEXT("RefreshData: Starting refresh for ModelAsset %s"), *ModelAsset->GetName());
|
||||||
|
|
||||||
|
// Nettoyer les données existantes
|
||||||
RootItems.Empty();
|
RootItems.Empty();
|
||||||
ParticipantItems.Empty();
|
ParticipantItems.Empty();
|
||||||
|
|
||||||
|
// Construire la hiérarchie
|
||||||
BuildContestHierarchy();
|
BuildContestHierarchy();
|
||||||
BuildParticipantList();
|
BuildParticipantList();
|
||||||
|
|
||||||
|
// Refresh les vues
|
||||||
if (ContestTreeView.IsValid())
|
if (ContestTreeView.IsValid())
|
||||||
{
|
{
|
||||||
ContestTreeView->RequestTreeRefresh();
|
ContestTreeView->RequestTreeRefresh();
|
||||||
@ -588,8 +591,9 @@ void SDTFluxAssetModelDetailsWidget::RefreshData()
|
|||||||
{
|
{
|
||||||
ParticipantTreeView->RequestTreeRefresh();
|
ParticipantTreeView->RequestTreeRefresh();
|
||||||
}
|
}
|
||||||
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("RefreshData: Completed successfully - %d contests, %d participants"), RootItems.Num(),
|
|
||||||
// ParticipantItems.Num());
|
UE_LOG(LogTemp, Log, TEXT("RefreshData: Completed successfully - %d contests, %d participants"), RootItems.Num(),
|
||||||
|
ParticipantItems.Num());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== MÉTHODES UTILITAIRES =====
|
// ===== MÉTHODES UTILITAIRES =====
|
||||||
@ -631,6 +635,7 @@ FText SDTFluxAssetModelDetailsWidget::GetStatsText() const
|
|||||||
{
|
{
|
||||||
if (!ModelAsset)
|
if (!ModelAsset)
|
||||||
return FText::FromString("No data");
|
return FText::FromString("No data");
|
||||||
|
|
||||||
return FText::FromString(FString::Printf(
|
return FText::FromString(FString::Printf(
|
||||||
TEXT("Contests: [%d] Participants: [%d] Persons: [%d]"),
|
TEXT("Contests: [%d] Participants: [%d] Persons: [%d]"),
|
||||||
ModelAsset->Contests.Num(),
|
ModelAsset->Contests.Num(),
|
||||||
|
|||||||
@ -108,7 +108,7 @@ struct FHierarchicalTreeItem
|
|||||||
Item->ContestId = InContestId;
|
Item->ContestId = InContestId;
|
||||||
Item->SplitId = Split.SplitId;
|
Item->SplitId = Split.SplitId;
|
||||||
Item->ID = FString::Printf(TEXT("%d"), Split.SplitId);
|
Item->ID = FString::Printf(TEXT("%d"), Split.SplitId);
|
||||||
Item->Details = FString::Printf(TEXT("rankings"));
|
Item->Details = FString::Printf(TEXT("%d rankings"), Split.SplitRankings.Num());
|
||||||
Item->Status = TEXT("-");
|
Item->Status = TEXT("-");
|
||||||
Item->Extra = TEXT("-");
|
Item->Extra = TEXT("-");
|
||||||
return Item;
|
return Item;
|
||||||
@ -137,7 +137,7 @@ struct FHierarchicalTreeItem
|
|||||||
typedef TSharedPtr<FHierarchicalTreeItem> FHierarchicalTreeItemPtr;
|
typedef TSharedPtr<FHierarchicalTreeItem> FHierarchicalTreeItemPtr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ModelAsset TreeviewWidget
|
* Widget avec STreeView simple et efficace
|
||||||
*/
|
*/
|
||||||
class DTFLUXASSETSEDITOR_API SDTFluxAssetModelDetailsWidget : public SCompoundWidget
|
class DTFLUXASSETSEDITOR_API SDTFluxAssetModelDetailsWidget : public SCompoundWidget
|
||||||
{
|
{
|
||||||
@ -152,33 +152,40 @@ public:
|
|||||||
void Construct(const FArguments& InArgs);
|
void Construct(const FArguments& InArgs);
|
||||||
void RefreshData();
|
void RefreshData();
|
||||||
|
|
||||||
|
// Méthodes publiques pour la sous-classe Row
|
||||||
FSlateColor GetItemTypeColor(FHierarchicalTreeItem::EItemType Type) const;
|
FSlateColor GetItemTypeColor(FHierarchicalTreeItem::EItemType Type) const;
|
||||||
const FSlateBrush* GetItemIcon(FHierarchicalTreeItem::EItemType Type) const;
|
const FSlateBrush* GetItemIcon(FHierarchicalTreeItem::EItemType Type) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Données
|
||||||
UDTFluxModelAsset* ModelAsset = nullptr;
|
UDTFluxModelAsset* ModelAsset = nullptr;
|
||||||
TArray<FHierarchicalTreeItemPtr> RootItems;
|
TArray<FHierarchicalTreeItemPtr> RootItems; // Contests racines avec hiérarchie
|
||||||
TArray<FHierarchicalTreeItemPtr> ParticipantItems;
|
TArray<FHierarchicalTreeItemPtr> ParticipantItems; // Participants séparés
|
||||||
|
|
||||||
|
// Widgets - TreeView simple
|
||||||
TSharedPtr<STreeView<FHierarchicalTreeItemPtr>> ContestTreeView;
|
TSharedPtr<STreeView<FHierarchicalTreeItemPtr>> ContestTreeView;
|
||||||
TSharedPtr<STreeView<FHierarchicalTreeItemPtr>> ParticipantTreeView;
|
TSharedPtr<STreeView<FHierarchicalTreeItemPtr>> ParticipantTreeView;
|
||||||
TSharedPtr<STextBlock> StatsText;
|
TSharedPtr<STextBlock> StatsText;
|
||||||
TSharedPtr<STextBlock> SelectionText;
|
TSharedPtr<STextBlock> SelectionText;
|
||||||
|
|
||||||
|
// Méthodes de construction
|
||||||
void BuildContestHierarchy();
|
void BuildContestHierarchy();
|
||||||
void BuildParticipantList();
|
void BuildParticipantList();
|
||||||
|
|
||||||
|
// Callbacks TreeView
|
||||||
TSharedRef<ITableRow> OnGenerateRowForTree(FHierarchicalTreeItemPtr Item,
|
TSharedRef<ITableRow> OnGenerateRowForTree(FHierarchicalTreeItemPtr Item,
|
||||||
const TSharedRef<STableViewBase>& OwnerTable);
|
const TSharedRef<STableViewBase>& OwnerTable);
|
||||||
void OnGetChildrenForTree(FHierarchicalTreeItemPtr Item, TArray<FHierarchicalTreeItemPtr>& OutChildren);
|
void OnGetChildrenForTree(FHierarchicalTreeItemPtr Item, TArray<FHierarchicalTreeItemPtr>& OutChildren);
|
||||||
void OnTreeSelectionChanged(FHierarchicalTreeItemPtr SelectedItem, ESelectInfo::Type SelectInfo);
|
void OnTreeSelectionChanged(FHierarchicalTreeItemPtr SelectedItem, ESelectInfo::Type SelectInfo);
|
||||||
void OnSetExpansionRecursive(FHierarchicalTreeItemPtr Item, bool bIsExpanded);
|
void OnSetExpansionRecursive(FHierarchicalTreeItemPtr Item, bool bIsExpanded);
|
||||||
|
|
||||||
|
// Callbacks des boutons
|
||||||
FReply OnRefreshClicked();
|
FReply OnRefreshClicked();
|
||||||
FReply OnExpandAllClicked();
|
FReply OnExpandAllClicked();
|
||||||
FReply OnCollapseAllClicked();
|
FReply OnCollapseAllClicked();
|
||||||
|
|
||||||
EActiveTimerReturnType ForceInitialLayout(double InCurrentTime, float InDeltaTime);
|
EActiveTimerReturnType ForceInitialLayout(double InCurrentTime, float InDeltaTime);
|
||||||
|
|
||||||
|
// Utilitaires
|
||||||
FText GetStatsText() const;
|
FText GetStatsText() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,20 +13,6 @@ UDTFluxModelAsset::UDTFluxModelAsset(const FObjectInitializer& ObjectInitializer
|
|||||||
void UDTFluxModelAsset::AddContest(const FDTFluxContest& Contest)
|
void UDTFluxModelAsset::AddContest(const FDTFluxContest& Contest)
|
||||||
{
|
{
|
||||||
Contests.Add(Contest.Name, Contest);
|
Contests.Add(Contest.Name, Contest);
|
||||||
// initialisation
|
|
||||||
for (const auto& Stage : Contest.Stages)
|
|
||||||
{
|
|
||||||
FinishedStagesCache.Add(FDTFluxStageKey(Contest.ContestId, Stage.StageId), Stage.IsFinished());
|
|
||||||
}
|
|
||||||
TArray<FDTFluxSplit> Splits = Contest.Splits;
|
|
||||||
Splits.Sort([](const FDTFluxSplit& A, const FDTFluxSplit& B)
|
|
||||||
{
|
|
||||||
return A.SplitId < B.SplitId;
|
|
||||||
});
|
|
||||||
// last and Penultimate split cache for contest
|
|
||||||
LastSplitIdCache.Add(Contest.ContestId, Splits.Pop().SplitId);
|
|
||||||
PenultimateSplitIdCache.Add(Contest.ContestId, Splits.Pop().SplitId);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxModelAsset::GetContestById(const int InContestId, FDTFluxContest& OutContest)
|
bool UDTFluxModelAsset::GetContestById(const int InContestId, FDTFluxContest& OutContest)
|
||||||
@ -146,38 +132,6 @@ bool UDTFluxModelAsset::GetParticipantByBib(int Bib, FDTFluxParticipant& OutPart
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxModelAsset::IsStageFinished(FDTFluxStageKey StageKey)
|
|
||||||
{
|
|
||||||
if (!FinishedStagesCache.Contains(StageKey))
|
|
||||||
{
|
|
||||||
if (FinishedStagesCache[StageKey])
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//maybe stage is finished because we have not be able to set it ?
|
|
||||||
return CheckStageIsFinished(StageKey);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxModelAsset::CacheSplitSensorInfo(const FDTFluxSplitSensorKey SplitSensorKey,
|
|
||||||
const FDTFluxSplitSensorInfo& SplitSensorInfo)
|
|
||||||
{
|
|
||||||
SplitSensorInfoCache.Add(SplitSensorKey, SplitSensorInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool UDTFluxModelAsset::CheckStageIsFinished(FDTFluxStageKey StageKey)
|
|
||||||
{
|
|
||||||
FDTFluxStage Stage;
|
|
||||||
if (GetStage(StageKey, Stage))
|
|
||||||
{
|
|
||||||
FinishedStagesCache.Add(StageKey, Stage.IsFinished());
|
|
||||||
return FinishedStagesCache[StageKey];
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void UDTFluxModelAsset::UpdateOrCreateStageRanking(const FDTFluxStageRankings& InStageRankings)
|
void UDTFluxModelAsset::UpdateOrCreateStageRanking(const FDTFluxStageRankings& InStageRankings)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,69 +1,3 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
||||||
|
|
||||||
bool FDTFluxStage::IsFinished() const
|
|
||||||
{
|
|
||||||
return EndTime <= FDateTime::Now();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FDTFluxContest::IsFinished() const
|
|
||||||
{
|
|
||||||
return EndTime <= FDateTime::Now();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxContest::UpdateEndTime()
|
|
||||||
{
|
|
||||||
TArray<FDTFluxStage> TempStages = Stages;
|
|
||||||
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
|
||||||
{
|
|
||||||
return A.EndTime < B.EndTime;
|
|
||||||
});
|
|
||||||
EndTime = TempStages.Last().EndTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FDTFluxContest::GetLastStageId()
|
|
||||||
{
|
|
||||||
if (LastStageId <= 0)
|
|
||||||
{
|
|
||||||
UpdateLastStageId();
|
|
||||||
}
|
|
||||||
return LastStageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxContest::UpdateLastStageId()
|
|
||||||
{
|
|
||||||
TArray<FDTFluxStage> TempStages = Stages;
|
|
||||||
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
|
||||||
{
|
|
||||||
return A.StageId < B.StageId;
|
|
||||||
});
|
|
||||||
LastStageId = TempStages.Last().StageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
FDTFluxStage& FDTFluxContest::GetLastStage() const
|
|
||||||
{
|
|
||||||
TArray<FDTFluxStage> TempStages = Stages;
|
|
||||||
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
|
||||||
{
|
|
||||||
return A.StageId < B.StageId;
|
|
||||||
});
|
|
||||||
return TempStages.Last();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FDTFluxContest::GetStage(const int StageID, FDTFluxStage& OutStage) const
|
|
||||||
{
|
|
||||||
if (Stages.Num() == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const FDTFluxStage& Stage : Stages)
|
|
||||||
{
|
|
||||||
if (Stage.StageId == StageID)
|
|
||||||
{
|
|
||||||
OutStage = Stage;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -3,12 +3,6 @@
|
|||||||
#include "Types/Struct/DTFluxRankingStructs.h"
|
#include "Types/Struct/DTFluxRankingStructs.h"
|
||||||
#include "DTFluxCoreModule.h"
|
#include "DTFluxCoreModule.h"
|
||||||
|
|
||||||
bool FDTFluxBaseRankings::IsSealed(const FDateTime EndTime) const
|
|
||||||
|
|
||||||
{
|
|
||||||
return ReceivedAt >= EndTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxContestRanking::Dump() const
|
void FDTFluxContestRanking::Dump() const
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCore, Log,
|
UE_LOG(logDTFluxCore, Log,
|
||||||
|
|||||||
@ -1,56 +1,93 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
#include "Types/Struct/DTFluxTeamListStruct.h"
|
#include "Types/Struct/DTFluxTeamListStruct.h"
|
||||||
|
|
||||||
#include "DTFluxCoreModule.h"
|
#include "DTFluxCoreModule.h"
|
||||||
#include "Dom/JsonObject.h"
|
|
||||||
|
|
||||||
// ===================================
|
|
||||||
// FDTFluxPerson Implementation
|
|
||||||
// ===================================
|
|
||||||
|
|
||||||
bool FDTFluxPerson::operator==(const FDTFluxPerson& Right) const
|
void FDTFluxParticipant::AddTeammate(const FDTFluxPerson& Person)
|
||||||
{
|
{
|
||||||
return GetNormalizedString() == Right.GetNormalizedString();
|
Teammate.Add(Person);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxPerson::operator!=(const FDTFluxPerson& Right) const
|
void FDTFluxParticipant::AddTeammate(const FString LastName, const FString FirstName, const FString Gender)
|
||||||
{
|
{
|
||||||
return !(*this == Right);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxPerson::operator==(const int Length) const
|
FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString OverflowChars) const
|
||||||
{
|
{
|
||||||
return GetNormalizedString().Len() == Length;
|
{
|
||||||
|
if (MaxChar <= 0)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxPerson::operator!=(const int Length) const
|
FString FirstName;
|
||||||
|
FString LastName;
|
||||||
|
if (IsTeam())
|
||||||
{
|
{
|
||||||
return !(*this == Length);
|
LastName = Team;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FirstName = Teammate[0].FirstName;
|
||||||
|
LastName = Teammate[0].LastName;
|
||||||
|
}
|
||||||
|
FString Initial;
|
||||||
|
if (!FirstName.IsEmpty())
|
||||||
|
{
|
||||||
|
Initial = FirstName.Left(1).ToUpper() + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
FString FDTFluxPerson::GetNormalizedString() const
|
FString FormattedLastName = LastName.ToUpper();
|
||||||
|
|
||||||
|
FString FullName = Initial + FormattedLastName;
|
||||||
|
UE_LOG(logDTFluxCore, Error, TEXT("FullName for Bib %i is %s"), Bib, *FullName);
|
||||||
|
|
||||||
|
if (FullName.Len() <= MaxChar)
|
||||||
{
|
{
|
||||||
return FirstName.ToLower() + LastName.ToLower() + Gender.ToLower();
|
return FullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxPerson::IsValid() const
|
const int32 OverflowLength = OverflowChars.Len();
|
||||||
|
|
||||||
|
if (OverflowLength > MaxChar)
|
||||||
{
|
{
|
||||||
return !FirstName.TrimStartAndEnd().IsEmpty() &&
|
return FullName.Left(MaxChar);
|
||||||
!LastName.TrimStartAndEnd().IsEmpty() &&
|
|
||||||
!Gender.TrimStartAndEnd().IsEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FDTFluxParticipant::FDTFluxParticipant()
|
if (Initial.Len() + OverflowLength > MaxChar)
|
||||||
: Bib(-1)
|
|
||||||
, ContestId(-1)
|
|
||||||
, Elite(false)
|
|
||||||
, Status(static_cast<EDTFluxParticipantStatusType>(0))
|
|
||||||
, bIsMassStartParticipant(false)
|
|
||||||
, CurrentSplit(-1)
|
|
||||||
{
|
{
|
||||||
Teammate.Reset();
|
return FullName.Left(MaxChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int32 AvailableForLastName = MaxChar - Initial.Len() - OverflowLength;
|
||||||
|
|
||||||
|
if (AvailableForLastName <= 0)
|
||||||
|
{
|
||||||
|
return FullName.Left(MaxChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString TruncatedName = Initial + FormattedLastName.Left(AvailableForLastName) + OverflowChars;
|
||||||
|
|
||||||
|
if (TruncatedName.Len() > MaxChar)
|
||||||
|
{
|
||||||
|
return TruncatedName.Left(MaxChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TruncatedName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FString FDTFluxParticipant::GetConcatFormattedName(const int MaxChar, const FString OverflowChar) const
|
||||||
|
{
|
||||||
|
FString BibText = FString::FromInt(Bib) + " ";
|
||||||
|
FString FormattedName = GetFormattedName(MaxChar - BibText.Len(), OverflowChar);
|
||||||
|
return BibText + FormattedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructeur privé depuis JSON
|
||||||
FDTFluxParticipant::FDTFluxParticipant(const TSharedPtr<FJsonObject>& JsonObject)
|
FDTFluxParticipant::FDTFluxParticipant(const TSharedPtr<FJsonObject>& JsonObject)
|
||||||
: Bib(JsonObject->GetIntegerField(TEXT("bib")))
|
: Bib(JsonObject->GetIntegerField(TEXT("bib")))
|
||||||
, ContestId(JsonObject->GetIntegerField(TEXT("contestId")))
|
, ContestId(JsonObject->GetIntegerField(TEXT("contestId")))
|
||||||
@ -59,88 +96,43 @@ FDTFluxParticipant::FDTFluxParticipant(const TSharedPtr<FJsonObject>& JsonObject
|
|||||||
, Elite(JsonObject->GetBoolField(TEXT("elite")))
|
, Elite(JsonObject->GetBoolField(TEXT("elite")))
|
||||||
, Status(static_cast<EDTFluxParticipantStatusType>(JsonObject->GetIntegerField(TEXT("status"))))
|
, Status(static_cast<EDTFluxParticipantStatusType>(JsonObject->GetIntegerField(TEXT("status"))))
|
||||||
, Team(JsonObject->GetStringField(TEXT("team")))
|
, Team(JsonObject->GetStringField(TEXT("team")))
|
||||||
|
, bIsMassStartParticipant(false)
|
||||||
, CurrentSplit(-1)
|
, CurrentSplit(-1)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCore, Log, TEXT("Creating participant from JSON - Bib: %d, Contest: %d"), Bib, ContestId);
|
UE_LOG(logDTFluxCore, Error, TEXT("Ctor with JSON Object"))
|
||||||
|
for (uint8 Index = 1; ; Index++)
|
||||||
for (uint8 Index = 1; Index <= 10; Index++)
|
|
||||||
{
|
{
|
||||||
FString FirstNameKey = Index == 1 ? TEXT("firstName") : FString::Printf(TEXT("firstName%d"), Index);
|
FString FirstNameKey = Index == 1 ? "firstName" : FString::Printf(TEXT("firstName%i"), Index);
|
||||||
FString LastNameKey = Index == 1 ? TEXT("lastName") : FString::Printf(TEXT("lastName%d"), Index);
|
FString LastNameKey = Index == 1 ? "lastName" : FString::Printf(TEXT("lastName%i"), Index);
|
||||||
FString GenderKey = Index == 1 ? TEXT("gender") : FString::Printf(TEXT("gender%d"), Index);
|
FString GenderKey = Index == 1 ? "gender" : FString::Printf(TEXT("gender%i"), Index);
|
||||||
|
// max 10 Persons
|
||||||
// Vérifie si au moins un des champs existe
|
if (Index >= 10)
|
||||||
if (!JsonObject->HasField(FirstNameKey) && !JsonObject->HasField(LastNameKey) && !JsonObject->
|
|
||||||
HasField(GenderKey))
|
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!JsonObject->HasField(FirstNameKey) && !JsonObject->HasField(LastNameKey)
|
||||||
|
&& !JsonObject->HasField(GenderKey))
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCore, Error, TEXT("No Corresponding Field!!!"))
|
||||||
|
break;
|
||||||
|
}
|
||||||
const FString FirstName = JsonObject->GetStringField(FirstNameKey);
|
const FString FirstName = JsonObject->GetStringField(FirstNameKey);
|
||||||
const FString LastName = JsonObject->GetStringField(LastNameKey);
|
const FString LastName = JsonObject->GetStringField(LastNameKey);
|
||||||
const FString Gender = JsonObject->GetStringField(GenderKey);
|
const FString Gender = JsonObject->GetStringField(GenderKey);
|
||||||
|
if (FirstName.IsEmpty() && LastName.IsEmpty())
|
||||||
if (FirstName.TrimStartAndEnd().IsEmpty() && LastName.TrimStartAndEnd().IsEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
FDTFluxPerson Person;
|
FDTFluxPerson Person;
|
||||||
Person.FirstName = FirstName.TrimStartAndEnd();
|
Person.FirstName = FirstName;
|
||||||
Person.LastName = LastName.TrimStartAndEnd();
|
Person.LastName = LastName;
|
||||||
Person.Gender = Gender.TrimStartAndEnd();
|
Person.Gender = Gender;
|
||||||
|
|
||||||
if (Person.IsValid())
|
|
||||||
{
|
|
||||||
Teammate.Add(Person);
|
Teammate.Add(Person);
|
||||||
UE_LOG(logDTFluxCore, Verbose, TEXT("Added person %d: %s %s (%s)"),
|
|
||||||
Index, *Person.FirstName, *Person.LastName, *Person.Gender);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxCore, Warning, TEXT("Invalid person data at index %d: '%s' '%s' '%s'"),
|
|
||||||
Index, *FirstName, *LastName, *Gender);
|
|
||||||
}
|
}
|
||||||
|
UE_LOG(logDTFluxCore, Error, TEXT("Ctor with JSON Object Teammate is now %i long"), Teammate.Num());
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(logDTFluxCore, Log, TEXT("Participant created with %d teammates"), Teammate.Num());
|
FDTFluxParticipant FDTFluxParticipant::CreateFromJson(const TSharedPtr<FJsonObject>& JsonObject)
|
||||||
}
|
|
||||||
|
|
||||||
bool FDTFluxParticipant::IsDefault() const
|
|
||||||
{
|
{
|
||||||
return Bib == -1
|
return FDTFluxParticipant(JsonObject);
|
||||||
&& ContestId == -1
|
|
||||||
&& Category.IsEmpty()
|
|
||||||
&& Club.IsEmpty()
|
|
||||||
&& !Elite
|
|
||||||
&& Status == static_cast<EDTFluxParticipantStatusType>(0)
|
|
||||||
&& Team.IsEmpty()
|
|
||||||
&& !bIsMassStartParticipant
|
|
||||||
&& CurrentSplit == -1
|
|
||||||
&& Teammate.IsEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxParticipant::AddTeammate(const FDTFluxPerson& Person)
|
|
||||||
{
|
|
||||||
if (Person.IsValid())
|
|
||||||
{
|
|
||||||
Teammate.Add(Person);
|
|
||||||
UE_LOG(logDTFluxCore, Verbose, TEXT("Added teammate: %s %s"), *Person.FirstName, *Person.LastName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxCore, Warning, TEXT("Cannot add invalid teammate: %s %s"), *Person.FirstName, *Person.LastName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxParticipant::AddTeammate(const FString& LastName, const FString& FirstName, const FString& Gender)
|
|
||||||
{
|
|
||||||
FDTFluxPerson Person;
|
|
||||||
Person.FirstName = FirstName.TrimStartAndEnd();
|
|
||||||
Person.LastName = LastName.TrimStartAndEnd();
|
|
||||||
Person.Gender = Gender.TrimStartAndEnd();
|
|
||||||
|
|
||||||
AddTeammate(Person);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FDTFluxParticipant::GetTeammateNum() const
|
int FDTFluxParticipant::GetTeammateNum() const
|
||||||
@ -150,134 +142,5 @@ int FDTFluxParticipant::GetTeammateNum() const
|
|||||||
|
|
||||||
bool FDTFluxParticipant::IsTeam() const
|
bool FDTFluxParticipant::IsTeam() const
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCore, Verbose, TEXT("IsTeam() -> %s, Teamate Num %i"),
|
return Teammate.Num() < 1;
|
||||||
Team.IsEmpty() ? TEXT("TRUE") : TEXT("FALSE"), Teammate.Num());
|
|
||||||
return !Team.IsEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
const TArray<FDTFluxPerson>& FDTFluxParticipant::GetTeammate() const
|
|
||||||
{
|
|
||||||
return Teammate;
|
|
||||||
}
|
|
||||||
|
|
||||||
FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString& Separator,
|
|
||||||
const FString& OverflowChar) const
|
|
||||||
{
|
|
||||||
if (MaxChar <= 0)
|
|
||||||
{
|
|
||||||
return TEXT("");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Teammate.Num() == 0)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
if (IsTeam())
|
|
||||||
{
|
|
||||||
FString FullName;
|
|
||||||
UE_LOG(logDTFluxCore, Verbose, TEXT("Participant is a team"));
|
|
||||||
if (!Team.IsEmpty())
|
|
||||||
{
|
|
||||||
FullName = Team;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TArray<FString> Names;
|
|
||||||
for (const FDTFluxPerson& Person : Teammate)
|
|
||||||
{
|
|
||||||
Names.Add(Person.LastName);
|
|
||||||
}
|
|
||||||
FullName = FString::Join(Names, TEXT("/"));
|
|
||||||
}
|
|
||||||
FullName = FullName.ToUpper();
|
|
||||||
if (FullName.Len() <= MaxChar)
|
|
||||||
{
|
|
||||||
return FullName;
|
|
||||||
}
|
|
||||||
return FullName.Left(MaxChar) + OverflowChar;
|
|
||||||
}
|
|
||||||
FString FirstName = Teammate[0].FirstName;
|
|
||||||
FString LastName = Teammate[0].LastName;
|
|
||||||
FString Initial;
|
|
||||||
if (!FirstName.IsEmpty())
|
|
||||||
{
|
|
||||||
Initial = FirstName.Left(1).ToUpper() + Separator;
|
|
||||||
}
|
|
||||||
const FString FormattedLastName = LastName.ToUpper();
|
|
||||||
FString FullName = Initial + FormattedLastName;
|
|
||||||
if (FullName.Len() <= MaxChar)
|
|
||||||
{
|
|
||||||
return FullName;
|
|
||||||
}
|
|
||||||
return FullName.Left(MaxChar) + OverflowChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
FString FDTFluxParticipant::GetConcatFormattedName(const int MaxChar, const FString& Separator,
|
|
||||||
const FString& OverflowChar, const FString& BibSeparator) const
|
|
||||||
{
|
|
||||||
FString BibText = FString::FromInt(Bib) + BibSeparator;
|
|
||||||
int32 RemainingChars = MaxChar - BibText.Len();
|
|
||||||
|
|
||||||
if (RemainingChars <= 0)
|
|
||||||
{
|
|
||||||
return BibText.Left(MaxChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
FString FormattedName = GetFormattedName(RemainingChars, Separator, OverflowChar);
|
|
||||||
return BibText + FormattedName;
|
|
||||||
}
|
|
||||||
|
|
||||||
FText FDTFluxParticipant::GetFormattedNameText(const int MaxChar, const FString& Separator,
|
|
||||||
const FString& OverflowChar) const
|
|
||||||
{
|
|
||||||
return FText::FromString(GetFormattedName(MaxChar, Separator, OverflowChar));
|
|
||||||
}
|
|
||||||
|
|
||||||
FText FDTFluxParticipant::GetConcatFormattedNameText(const int MaxChar, const FString& Separator,
|
|
||||||
const FString& OverflowChar, const FString& BibSeparator) const
|
|
||||||
{
|
|
||||||
return FText::FromString(GetConcatFormattedName(MaxChar, Separator, OverflowChar, BibSeparator));
|
|
||||||
}
|
|
||||||
|
|
||||||
FString FDTFluxParticipant::GetFormattedName(const FDTFluxParticipant& Participant, const int MaxChar,
|
|
||||||
const FString& Separator, const FString& OverflowChar)
|
|
||||||
{
|
|
||||||
return Participant.GetFormattedName(MaxChar, Separator, OverflowChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
FString FDTFluxParticipant::GetConcatFormattedName(const FDTFluxParticipant& Participant, const int MaxChar,
|
|
||||||
const FString& Separator, const FString& OverflowChar,
|
|
||||||
const FString& BibSeparator)
|
|
||||||
{
|
|
||||||
return Participant.GetConcatFormattedName(MaxChar, Separator, OverflowChar, BibSeparator);
|
|
||||||
}
|
|
||||||
|
|
||||||
FText FDTFluxParticipant::GetFormattedNameText(const FDTFluxParticipant& Participant, const int MaxChar,
|
|
||||||
const FString& Separator, const FString& OverflowChar)
|
|
||||||
{
|
|
||||||
return Participant.GetFormattedNameText(MaxChar, Separator, OverflowChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
FText FDTFluxParticipant::GetConcatFormattedNameText(const FDTFluxParticipant& Participant, const int MaxChar,
|
|
||||||
const FString& Separator, const FString& OverflowChar,
|
|
||||||
const FString& BibSeparator)
|
|
||||||
{
|
|
||||||
return Participant.GetConcatFormattedNameText(MaxChar, Separator, OverflowChar, BibSeparator);
|
|
||||||
}
|
|
||||||
|
|
||||||
FDTFluxParticipant FDTFluxParticipant::CreateFromJson(const TSharedPtr<FJsonObject>& JsonObject)
|
|
||||||
{
|
|
||||||
if (!JsonObject.IsValid())
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxCore, Error, TEXT("Cannot create participant from invalid JSON object"));
|
|
||||||
return FDTFluxParticipant();
|
|
||||||
}
|
|
||||||
|
|
||||||
return FDTFluxParticipant(JsonObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
FDTFluxTeamStatusUpdate::FDTFluxTeamStatusUpdate(const int InBib, const int InStatus)
|
|
||||||
: Bib(InBib)
|
|
||||||
, Status(static_cast<EDTFluxParticipantStatusType>(InStatus))
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,24 +35,15 @@ public:
|
|||||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
||||||
TMap<FString /* ContestName */, FDTFluxContest> Contests;
|
TMap<FString /* ContestName */, FDTFluxContest> Contests;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
TMap<int /*ContestId*/, FDTFluxContestRankings> ContestRankings;
|
TMap<int /*ContestId*/, FDTFluxContestRankings> ContestRankings;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
TMap<FDTFluxStageKey, FDTFluxStageRankings> StageRankings;
|
TMap<FDTFluxStageKey, FDTFluxStageRankings> StageRankings;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
TMap<FDTFluxSplitKey, FDTFluxSplitRankings> SplitRankings;
|
TMap<FDTFluxSplitKey, FDTFluxSplitRankings> SplitRankings;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
|
||||||
TMap<FDTFluxSplitSensorKey, FDTFluxSplitSensorInfo> SplitSensorInfoCache;
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
|
||||||
TMap<int /*ContestId*/, int /*SplitId*/> LastSplitIdCache;
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
|
||||||
TMap<int/*ContestId*/, int /*Penultimate*/>PenultimateSplitIdCache;
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, CallInEditor, Category="DTFlux|ModelAsset")
|
UFUNCTION(BlueprintCallable, CallInEditor, Category="DTFlux|ModelAsset")
|
||||||
void AddContest(const FDTFluxContest& Contest);
|
void AddContest(const FDTFluxContest& Contest);
|
||||||
|
|
||||||
@ -98,15 +89,4 @@ public:
|
|||||||
|
|
||||||
UFUNCTION(BlueprintCallable, CallInEditor, Category="DTFlux|Participant")
|
UFUNCTION(BlueprintCallable, CallInEditor, Category="DTFlux|Participant")
|
||||||
bool GetParticipantByBib(int Bib, FDTFluxParticipant& OutParticipant);
|
bool GetParticipantByBib(int Bib, FDTFluxParticipant& OutParticipant);
|
||||||
|
|
||||||
UFUNCTION()
|
|
||||||
bool IsStageFinished(FDTFluxStageKey StageKey);
|
|
||||||
UFUNCTION()
|
|
||||||
void CacheSplitSensorInfo(const FDTFluxSplitSensorKey SplitSensorKey, const FDTFluxSplitSensorInfo& SplitSensorInfo);
|
|
||||||
|
|
||||||
private:
|
|
||||||
UPROPERTY()
|
|
||||||
TMap<FDTFluxStageKey, bool /*bIsFinished*/> FinishedStagesCache;
|
|
||||||
UFUNCTION()
|
|
||||||
bool CheckStageIsFinished(FDTFluxStageKey StageKey);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -24,17 +24,17 @@ enum class EDTFluxParticipantStatusType : uint8
|
|||||||
UENUM(BlueprintType, meta=(Bitflags, UseEnumValuesAsMaskValuesInEditor=true))
|
UENUM(BlueprintType, meta=(Bitflags, UseEnumValuesAsMaskValuesInEditor=true))
|
||||||
enum class EParticipantSortingType : uint8
|
enum class EParticipantSortingType : uint8
|
||||||
{
|
{
|
||||||
|
|
||||||
None = 0 << 1 UMETA(DisplayName="Normal"),
|
None = 0 << 1 UMETA(DisplayName="Normal"),
|
||||||
Alpha = 1 << 1 UMETA(DisplayName="Aplha"),
|
Alpha = 1 << 1 UMETA(DisplayName="Aplha"),
|
||||||
PoursuiteStartTime = 1 << 2 UMETA(DisplayName="Poursuite StartTime"),
|
PoursuiteStartTime = 1 << 2 UMETA(DisplayName="Poursuite StartTime"),
|
||||||
Rank = 1 << 3 UMETA(DisplayName="Rank"),
|
Rank = 1 << 3 UMETA(DisplayName="Rank"),
|
||||||
IgnoreEmpty = 1 << 4 UMETA(DisplayName="IgnoreEmpty"),
|
IgnoreEmpty = 1 << 4 UMETA(DisplayName="IgnoreEmpty"),
|
||||||
};
|
};
|
||||||
|
|
||||||
ENUM_CLASS_FLAGS(EParticipantSortingType);
|
ENUM_CLASS_FLAGS(EParticipantSortingType);
|
||||||
|
|
||||||
|
|
||||||
UENUM(BlueprintType)
|
UENUM(BlueprintType, meta=(Bitflags, UseEnumValuesAsMaskValuesInEditor=true))
|
||||||
enum class EDTFluxFinisherType : uint8
|
enum class EDTFluxFinisherType : uint8
|
||||||
{
|
{
|
||||||
None = 0b0000000 UMETA(DisplayName="Unknown"),
|
None = 0b0000000 UMETA(DisplayName="Unknown"),
|
||||||
@ -42,6 +42,7 @@ enum class EDTFluxFinisherType : uint8
|
|||||||
Winner = 0b0000010 UMETA(DisplayName="Winner"),
|
Winner = 0b0000010 UMETA(DisplayName="Winner"),
|
||||||
Spotter = 0b0000100 UMETA(DisplayName="Spotter"),
|
Spotter = 0b0000100 UMETA(DisplayName="Spotter"),
|
||||||
};
|
};
|
||||||
|
ENUM_CLASS_FLAGS(EDTFluxFinisherType);
|
||||||
|
|
||||||
UENUM(BlueprintType, meta=(Bitflags, UseEnumValuesAsMaskValuesInEditor=true))
|
UENUM(BlueprintType, meta=(Bitflags, UseEnumValuesAsMaskValuesInEditor=true))
|
||||||
enum class EDTFluxSplitType : uint8
|
enum class EDTFluxSplitType : uint8
|
||||||
@ -51,7 +52,6 @@ enum class EDTFluxSplitType : uint8
|
|||||||
Finish = 0b00000010 UMETA(DisplayName="FinishSplit"),
|
Finish = 0b00000010 UMETA(DisplayName="FinishSplit"),
|
||||||
Regular = 0b00000100 UMETA(DisplayName="Regular"),
|
Regular = 0b00000100 UMETA(DisplayName="Regular"),
|
||||||
};
|
};
|
||||||
|
|
||||||
ENUM_CLASS_FLAGS(EDTFluxSplitType);
|
ENUM_CLASS_FLAGS(EDTFluxSplitType);
|
||||||
|
|
||||||
|
|
||||||
@ -69,7 +69,6 @@ enum class EDTFluxSortingFilter : uint8
|
|||||||
AscendingByRank = Ascending | ByRank UMETA(DisplayName="AscendingByRank"),
|
AscendingByRank = Ascending | ByRank UMETA(DisplayName="AscendingByRank"),
|
||||||
DescendingByRank= Descending | ByRank UMETA(DisplayName="DescendingByRank")
|
DescendingByRank= Descending | ByRank UMETA(DisplayName="DescendingByRank")
|
||||||
};
|
};
|
||||||
|
|
||||||
ENUM_CLASS_FLAGS(EDTFluxSortingFilter);
|
ENUM_CLASS_FLAGS(EDTFluxSortingFilter);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -109,61 +109,3 @@ struct DTFLUXCORE_API FDTFluxSplitKey : public FDTFluxCompositeKey
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
|
||||||
struct DTFLUXCORE_API FDTFluxSplitSensorKey : public FDTFluxCompositeKey
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
public:
|
|
||||||
FDTFluxSplitSensorKey() = default;
|
|
||||||
|
|
||||||
FDTFluxSplitSensorKey(const int InContestId, const int InStageId, const int InSplitId, const int InBib) :
|
|
||||||
ContestId(InContestId),
|
|
||||||
StageId(InStageId),
|
|
||||||
SplitId(InSplitId),
|
|
||||||
Bib(InBib){};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model")
|
|
||||||
int ContestId = 0;
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model")
|
|
||||||
int StageId = 0;
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model")
|
|
||||||
int SplitId = 0;
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model")
|
|
||||||
int Bib = 0;
|
|
||||||
|
|
||||||
|
|
||||||
friend uint32 GetTypeHash(const FDTFluxSplitSensorKey& Key)
|
|
||||||
{
|
|
||||||
return HashCombine(
|
|
||||||
GetTypeHash(Key.ContestId),
|
|
||||||
GetTypeHash(Key.StageId),
|
|
||||||
GetTypeHash(Key.SplitId),
|
|
||||||
GetTypeHash(Key.Bib)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const FDTFluxSplitSensorKey& Other) const
|
|
||||||
{
|
|
||||||
return ContestId == Other.ContestId && StageId == Other.StageId
|
|
||||||
&& SplitId == Other.SplitId && Bib == Other.Bib;
|
|
||||||
}
|
|
||||||
|
|
||||||
FString GetDisplayName() const
|
|
||||||
{
|
|
||||||
return FString::Printf(TEXT("Contest%i | Stage%i | Split%i | Bib%i"), ContestId, StageId, SplitId, Bib);
|
|
||||||
}
|
|
||||||
|
|
||||||
FText GetTooltipText() const
|
|
||||||
{
|
|
||||||
return FText::Format(INVTEXT("Contest{0}|Stage{1}|Split{2}"),
|
|
||||||
FText::AsNumber(ContestId),
|
|
||||||
FText::AsNumber(StageId),
|
|
||||||
FText::AsNumber(SplitId),
|
|
||||||
FText::AsNumber(Bib)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@ -15,7 +15,6 @@ USTRUCT(BlueprintType, Category="DTFlux|Model")
|
|||||||
struct DTFLUXCORE_API FDTFluxFinisherData
|
struct DTFLUXCORE_API FDTFluxFinisherData
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
|
||||||
int ContestId;
|
int ContestId;
|
||||||
@ -24,7 +23,9 @@ public:
|
|||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
|
||||||
int Bib = -1;
|
int Bib = -1;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
|
||||||
FString Time;
|
FDTFluxStageRanking SplitRanking;
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
|
||||||
|
FDTFluxStageRanking StageRanking;
|
||||||
};
|
};
|
||||||
|
|
||||||
USTRUCT(BlueprintType, Category="FDTFlux|Model")
|
USTRUCT(BlueprintType, Category="FDTFlux|Model")
|
||||||
@ -63,3 +64,4 @@ struct DTFLUXCORE_API FDTFluxContestFinished
|
|||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
|
||||||
TArray<FDTFluxStageRanking> Rankings;
|
TArray<FDTFluxStageRanking> Rankings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,12 @@ public:
|
|||||||
int SplitId = -1;
|
int SplitId = -1;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
||||||
FString Name;
|
FString Name;
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
|
TArray<FDTFluxStageRanking> SplitRankings;
|
||||||
|
// void Dump() const;
|
||||||
|
// // void InsertOrReplace(const FDTFluxStageRankingResponseItem& SplitRankingItemResp);
|
||||||
|
// void SortByRank();
|
||||||
|
// TArray<FDTFluxSplitRanking> GetSplitRanking(const int From = 0, const int DisplayNumber = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,7 +51,6 @@ public:
|
|||||||
FDateTime EndTime;
|
FDateTime EndTime;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
FDateTime CutOff;
|
FDateTime CutOff;
|
||||||
bool IsFinished() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,6 +89,68 @@ public:
|
|||||||
bool GetStage(const int StageID, FDTFluxStage& OutStage) const;
|
bool GetStage(const int StageID, FDTFluxStage& OutStage) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool FDTFluxContest::IsFinished() const
|
||||||
|
{
|
||||||
|
return EndTime <= FDateTime::Now();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void FDTFluxContest::UpdateEndTime()
|
||||||
|
{
|
||||||
|
TArray<FDTFluxStage> TempStages = Stages;
|
||||||
|
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
||||||
|
{
|
||||||
|
return A.EndTime < B.EndTime;
|
||||||
|
});
|
||||||
|
EndTime = TempStages.Last().EndTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int FDTFluxContest::GetLastStageId()
|
||||||
|
{
|
||||||
|
if (LastStageId <= 0)
|
||||||
|
{
|
||||||
|
UpdateLastStageId();
|
||||||
|
}
|
||||||
|
return LastStageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void FDTFluxContest::UpdateLastStageId()
|
||||||
|
{
|
||||||
|
TArray<FDTFluxStage> TempStages = Stages;
|
||||||
|
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
||||||
|
{
|
||||||
|
return A.StageId < B.StageId;
|
||||||
|
});
|
||||||
|
LastStageId = TempStages.Last().StageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline FDTFluxStage& FDTFluxContest::GetLastStage() const
|
||||||
|
{
|
||||||
|
TArray<FDTFluxStage> TempStages = Stages;
|
||||||
|
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
||||||
|
{
|
||||||
|
return A.StageId < B.StageId;
|
||||||
|
});
|
||||||
|
return TempStages.Last();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool FDTFluxContest::GetStage(const int StageID, FDTFluxStage& OutStage) const
|
||||||
|
{
|
||||||
|
if (Stages.Num() == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const FDTFluxStage& Stage : Stages)
|
||||||
|
{
|
||||||
|
if (Stage.StageId == StageID)
|
||||||
|
{
|
||||||
|
OutStage = Stage;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
USTRUCT()
|
USTRUCT()
|
||||||
struct DTFLUXCORE_API FDTFluxRaceData
|
struct DTFLUXCORE_API FDTFluxRaceData
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,18 +8,6 @@
|
|||||||
#include "DTFluxRankingStructs.generated.h"
|
#include "DTFluxRankingStructs.generated.h"
|
||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
|
||||||
struct DTFLUXCORE_API FDTFluxBaseRankings
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
|
|
||||||
public:
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere)
|
|
||||||
FDateTime ReceivedAt = FDateTime::Now();
|
|
||||||
|
|
||||||
bool IsSealed(const FDateTime EndTime) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @struct FDTFluxContestRanking
|
* @struct FDTFluxContestRanking
|
||||||
* Representing a contest ranking for a participant
|
* Representing a contest ranking for a participant
|
||||||
@ -49,7 +37,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
USTRUCT(BlueprintType)
|
||||||
struct FDTFluxContestRankings : public FDTFluxBaseRankings
|
struct FDTFluxContestRankings
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
@ -69,7 +57,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @struct FDTFluxDetailedRankingItem
|
* @struct FDTFluxStageRanking
|
||||||
* Representing a stage ranking for a participant
|
* Representing a stage ranking for a participant
|
||||||
*/
|
*/
|
||||||
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
||||||
@ -106,7 +94,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
USTRUCT(BlueprintType)
|
||||||
struct FDTFluxDetailedRankings : public FDTFluxBaseRankings
|
struct FDTFluxDetailedRankings
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
@ -137,7 +125,7 @@ struct FDTFluxStageRanking : public FDTFluxDetailedRankingItem
|
|||||||
* This struct is only a cosmetic Struct
|
* This struct is only a cosmetic Struct
|
||||||
*/
|
*/
|
||||||
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
||||||
struct DTFLUXCORE_API FDTFluxSplitRanking : public FDTFluxDetailedRankingItem
|
struct DTFLUXCORE_API FDTFluxSplitRanking : public FDTFluxStageRanking
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
};
|
};
|
||||||
@ -177,7 +165,6 @@ struct FDTFluxStageRankings : public FDTFluxDetailedRankings
|
|||||||
Ranking.TimeStart.ParseIntoArray(Exploded, TEXT(":"), true);
|
Ranking.TimeStart.ParseIntoArray(Exploded, TEXT(":"), true);
|
||||||
if (Exploded.Num() == 3)
|
if (Exploded.Num() == 3)
|
||||||
{
|
{
|
||||||
//TODO: Pas sur que ce soit super de le mettre à ce jour ???
|
|
||||||
FDateTime Now = FDateTime::Now();
|
FDateTime Now = FDateTime::Now();
|
||||||
RankingStartTime = FDateTime(Now.GetYear(), Now.GetMonth(), Now.GetDay(),
|
RankingStartTime = FDateTime(Now.GetYear(), Now.GetMonth(), Now.GetDay(),
|
||||||
FCString::Atoi(*Exploded[0]), FCString::Atoi(*Exploded[1]),
|
FCString::Atoi(*Exploded[0]), FCString::Atoi(*Exploded[1]),
|
||||||
|
|||||||
@ -17,17 +17,7 @@ struct FDTFluxSplitSensorInfo
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
FDTFluxSplitSensorInfo() = default;
|
FDTFluxSplitSensorInfo() = default;
|
||||||
FDTFluxSplitSensorInfo(const FString InSplitName):
|
|
||||||
Bib(-1),
|
|
||||||
ContestId(-1),
|
|
||||||
StageId(-1),
|
|
||||||
SplitId(-1),
|
|
||||||
Time(""),
|
|
||||||
Gap("-"),
|
|
||||||
Rank(-1),
|
|
||||||
SplitName(InSplitName)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
int Bib = -1;
|
int Bib = -1;
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
@ -41,23 +31,7 @@ public:
|
|||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
FString Gap = "-";
|
FString Gap = "-";
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
int Rank = -1;
|
int Rank;
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
|
||||||
FString SplitName = "";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
|
||||||
struct FDTFluxSplitHistory
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
|
|
||||||
public:
|
|
||||||
FDTFluxSplitHistory() = default;
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
|
||||||
FDTFluxParticipant Participant = FDTFluxParticipant();
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
|
||||||
TArray<FDTFluxSplitSensorInfo> SplitSensors = TArray<FDTFluxSplitSensorInfo>();
|
|
||||||
};
|
|
||||||
|
|||||||
@ -7,10 +7,6 @@
|
|||||||
#include "Types/Enum/DTFluxModelEnums.h"
|
#include "Types/Enum/DTFluxModelEnums.h"
|
||||||
#include "DTFluxTeamListStruct.generated.h"
|
#include "DTFluxTeamListStruct.generated.h"
|
||||||
|
|
||||||
// Forward declarations
|
|
||||||
class UDTFluxModelAsset;
|
|
||||||
class UDTFluxParticipantFactory;
|
|
||||||
|
|
||||||
USTRUCT()
|
USTRUCT()
|
||||||
struct DTFLUXCORE_API FDTFluxTeamListItemDefinition
|
struct DTFLUXCORE_API FDTFluxTeamListItemDefinition
|
||||||
{
|
{
|
||||||
@ -19,78 +15,76 @@ struct DTFLUXCORE_API FDTFluxTeamListItemDefinition
|
|||||||
public:
|
public:
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Type = "team-list-item";
|
FString Type = "team-list-item";
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int ContestId = 0;
|
int ContestId;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int Bib = 0;
|
int Bib;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString FirstName;
|
FString FirstName;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString LastName;
|
FString LastName;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString FirstName2 = "";
|
FString FirstName2 = "";
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString LastName2 = "";
|
FString LastName2 = "";
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Team = "";
|
FString Team = "";
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Gender;
|
FString Gender;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Gender2;
|
FString Gender2;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
bool Elite = false;
|
bool Elite;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Category;
|
FString Category;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int Status = 0;
|
int Status;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Club;
|
FString Club;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
||||||
struct DTFLUXCORE_API FDTFluxPerson
|
struct DTFLUXCORE_API FDTFluxPerson
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
FString FirstName;
|
FString FirstName;
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
|
||||||
FString LastName;
|
FString LastName;
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
|
||||||
FString Gender;
|
FString Gender;
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
|
||||||
FString FunctionLine1 = TEXT("");
|
FString FunctionLine1 = TEXT("");
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
|
||||||
FString FunctionLine2 = TEXT("");
|
FString FunctionLine2 = TEXT("");
|
||||||
|
|
||||||
bool operator==(const FDTFluxPerson& Right) const;
|
bool operator==(const FDTFluxPerson& Right) const
|
||||||
bool operator!=(const FDTFluxPerson& Right) const;
|
{
|
||||||
bool operator==(const int Length) const;
|
return FirstName.ToLower() + LastName.ToLower() + Gender.ToLower()
|
||||||
bool operator!=(const int Length) const;
|
== Right.FirstName.ToLower() + Right.LastName.ToLower() + Right.Gender.ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
FString GetNormalizedString() const;
|
bool operator==(const int Length) const
|
||||||
|
{
|
||||||
|
return (FirstName.ToLower() + LastName.ToLower() + Gender.ToLower()).Len() == Length;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsValid() const;
|
bool operator!=(const int Length) const
|
||||||
|
{
|
||||||
|
return !(*this == Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const FDTFluxPerson& Right) const
|
||||||
|
{
|
||||||
|
return FirstName.ToLower() + LastName.ToLower() + Gender.ToLower()
|
||||||
|
!= Right.FirstName.ToLower() + Right.LastName.ToLower() + Right.Gender.ToLower();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
||||||
struct DTFLUXCORE_API FDTFluxParticipant
|
struct DTFLUXCORE_API FDTFluxParticipant
|
||||||
{
|
{
|
||||||
@ -100,98 +94,117 @@ struct DTFLUXCORE_API FDTFluxParticipant
|
|||||||
friend class UDTFluxParticipantFactory;
|
friend class UDTFluxParticipantFactory;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FDTFluxParticipant();
|
// Constructeur public par défaut requis par Unreal
|
||||||
|
FDTFluxParticipant()
|
||||||
|
: Bib(-1)
|
||||||
|
, ContestId(-1)
|
||||||
|
, Elite(false)
|
||||||
|
, Status(static_cast<EDTFluxParticipantStatusType>(0))
|
||||||
|
, bIsMassStartParticipant(false)
|
||||||
|
, CurrentSplit(-1)
|
||||||
|
{
|
||||||
|
Teammate.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", EditAnywhere)
|
|
||||||
|
/**
|
||||||
|
* Vérifie si le participant est dans son état par défaut (non initialisé)
|
||||||
|
* @return True si tous les champs sont à leur valeur par défaut
|
||||||
|
*/
|
||||||
|
bool IsDefault() const
|
||||||
|
{
|
||||||
|
return Bib == -1
|
||||||
|
&& ContestId == -1
|
||||||
|
&& Category.IsEmpty()
|
||||||
|
&& Club.IsEmpty()
|
||||||
|
&& !Elite
|
||||||
|
&& Status == static_cast<EDTFluxParticipantStatusType>(0)
|
||||||
|
&& Team.IsEmpty()
|
||||||
|
&& !bIsMassStartParticipant
|
||||||
|
&& CurrentSplit == -1
|
||||||
|
&& Teammate.IsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|model", EditAnywhere)
|
||||||
int Bib = -1;
|
int Bib = -1;
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|model", EditAnywhere)
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", EditAnywhere)
|
|
||||||
int ContestId = -1;
|
int ContestId = -1;
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|model", EditAnywhere)
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", EditAnywhere)
|
|
||||||
FString Category;
|
FString Category;
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
|
||||||
FString Club;
|
FString Club;
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
bool Elite;
|
||||||
bool Elite = false;
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
|
||||||
EDTFluxParticipantStatusType Status;
|
EDTFluxParticipantStatusType Status;
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
|
||||||
FString Team;
|
FString Team;
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
|
||||||
bool bIsMassStartParticipant = false;
|
bool bIsMassStartParticipant = false;
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
|
|
||||||
int CurrentSplit = -1;
|
int CurrentSplit = -1;
|
||||||
|
|
||||||
bool IsDefault() const;
|
// void Dump() const;
|
||||||
|
|
||||||
void AddTeammate(const FDTFluxPerson& Person);
|
void AddTeammate(const FDTFluxPerson& Person);
|
||||||
void AddTeammate(const FString& LastName, const FString& FirstName, const FString& Gender);
|
void AddTeammate(const FString LastName, const FString FirstName, const FString Gender);
|
||||||
|
|
||||||
int GetTeammateNum() const;
|
FText GetFormattedNameText(const int MaxChar = 15, const FString OverflowChar = FString("...")) const
|
||||||
|
{
|
||||||
|
return FText::FromString(GetFormattedName(MaxChar, OverflowChar));
|
||||||
|
};
|
||||||
|
|
||||||
bool IsTeam() const;
|
FText GetConcatFormattedNameText(const int MaxChar = 20, const FString OverflowChar = FString("...")) const
|
||||||
|
{
|
||||||
|
return FText::FromString(GetConcatFormattedName(MaxChar, OverflowChar));
|
||||||
|
};
|
||||||
|
FString GetFormattedName(const int MaxChar = 15, const FString OverflowChar = FString("...")) const;
|
||||||
|
FString GetConcatFormattedName(const int MaxChar = 20, const FString OverflowChar = FString("...")) const;
|
||||||
|
|
||||||
|
static FString GetFormattedName(const FDTFluxParticipant& Participant, const int MaxChar = 15,
|
||||||
|
const FString OverflowChar = FString("..."))
|
||||||
|
{
|
||||||
|
return Participant.GetFormattedName(MaxChar, OverflowChar);
|
||||||
|
};
|
||||||
|
|
||||||
const TArray<FDTFluxPerson>& GetTeammate() const;
|
static FString GetConcatFormattedName(const FDTFluxParticipant& Participant, const int MaxChar = 15,
|
||||||
|
const FString OverflowChar = FString("..."))
|
||||||
|
{
|
||||||
|
return Participant.GetConcatFormattedName(MaxChar, OverflowChar);
|
||||||
|
};
|
||||||
|
|
||||||
FString GetFormattedName(const int MaxChar = 15,
|
static FText GetFormattedNameText(const FDTFluxParticipant& Participant, const int MaxChar = 15,
|
||||||
const FString& Separator = FString(". "),
|
const FString OverflowChar = FString("..."))
|
||||||
const FString& OverflowChar = FString("...")) const;
|
{
|
||||||
|
return Participant.GetFormattedNameText();
|
||||||
|
};
|
||||||
|
|
||||||
FString GetConcatFormattedName(const int MaxChar = 20,
|
static FText GetConcatFormattedNameText(const FDTFluxParticipant& Participant, const int MaxChar = 15,
|
||||||
const FString& Separator = FString(". "),
|
const FString OverflowChar = FString("..."))
|
||||||
const FString& OverflowChar = FString("..."),
|
{
|
||||||
const FString& BibSeparator = FString(". ")) const;
|
return Participant.GetConcatFormattedNameText();
|
||||||
|
};
|
||||||
FText GetFormattedNameText(const int MaxChar = 15,
|
const TArray<FDTFluxPerson> GetTeammate() const { return Teammate; }
|
||||||
const FString& Separator = FString(". "),
|
|
||||||
const FString& OverflowChar = FString("...")) const;
|
|
||||||
|
|
||||||
FText GetConcatFormattedNameText(const int MaxChar = 20,
|
|
||||||
const FString& Separator = FString(". "),
|
|
||||||
const FString& OverflowChar = FString("..."),
|
|
||||||
const FString& BibSeparator = FString(". ")) const;
|
|
||||||
|
|
||||||
static FString GetFormattedName(const FDTFluxParticipant& Participant,
|
|
||||||
const int MaxChar = 15,
|
|
||||||
const FString& Separator = FString(". "),
|
|
||||||
const FString& OverflowChar = FString("..."));
|
|
||||||
|
|
||||||
static FString GetConcatFormattedName(const FDTFluxParticipant& Participant,
|
|
||||||
const int MaxChar = 15,
|
|
||||||
const FString& Separator = FString(". "),
|
|
||||||
const FString& OverflowChar = FString("..."),
|
|
||||||
const FString& BibSeparator = FString(". "));
|
|
||||||
|
|
||||||
static FText GetFormattedNameText(const FDTFluxParticipant& Participant,
|
|
||||||
const int MaxChar = 15,
|
|
||||||
const FString& Separator = FString(". "),
|
|
||||||
const FString& OverflowChar = FString("..."));
|
|
||||||
|
|
||||||
static FText GetConcatFormattedNameText(const FDTFluxParticipant& Participant,
|
|
||||||
const int MaxChar = 15,
|
|
||||||
const FString& Separator = FString(". "),
|
|
||||||
const FString& OverflowChar = FString("..."),
|
|
||||||
const FString& BibSeparator = FString(". "));
|
|
||||||
|
|
||||||
static FDTFluxParticipant CreateFromJson(const TSharedPtr<FJsonObject>& JsonObject);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
UPROPERTY(Category="DTFlux|Model", VisibleAnywhere)
|
|
||||||
TArray<FDTFluxPerson> Teammate;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// --- Constructeur privé ---
|
||||||
explicit FDTFluxParticipant(const TSharedPtr<FJsonObject>& JsonObject);
|
explicit FDTFluxParticipant(const TSharedPtr<FJsonObject>& JsonObject);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
UPROPERTY(Category="DTFlux|model", VisibleAnywhere)
|
||||||
|
TArray<FDTFluxPerson> Teammate;
|
||||||
|
// Méthode publique pour construire à partir d'un JSON (utilisée par la factory)
|
||||||
|
static FDTFluxParticipant CreateFromJson(const TSharedPtr<FJsonObject>& JsonObject);
|
||||||
|
int GetTeammateNum() const;
|
||||||
|
bool IsTeam() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct FDTFluxTeamListDefinition
|
||||||
|
* Struct representing the Participant List definition
|
||||||
|
* Used to exchange data between Objects in the system
|
||||||
|
*/
|
||||||
USTRUCT(BlueprintType)
|
USTRUCT(BlueprintType)
|
||||||
struct DTFLUXCORE_API FDTFluxTeamListDefinition
|
struct DTFLUXCORE_API FDTFluxTeamListDefinition
|
||||||
{
|
{
|
||||||
@ -199,22 +212,27 @@ struct DTFLUXCORE_API FDTFluxTeamListDefinition
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
|
// ReSharper disable once IdentifierTypo
|
||||||
TArray<FDTFluxParticipant> Participants;
|
TArray<FDTFluxParticipant> Participants;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
USTRUCT(BlueprintType)
|
||||||
struct DTFLUXCORE_API FDTFluxTeamStatusUpdate
|
struct FDTFluxTeamStatusUpdate
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FDTFluxTeamStatusUpdate() = default;
|
FDTFluxTeamStatusUpdate() = default;
|
||||||
FDTFluxTeamStatusUpdate(const int InBib, const int InStatus);
|
|
||||||
|
FDTFluxTeamStatusUpdate(const int InBib, const int InStatus)
|
||||||
|
: Bib(InBib)
|
||||||
|
, Status(static_cast<EDTFluxParticipantStatusType>(InStatus))
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant")
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant")
|
||||||
int Bib = -1;
|
int Bib = -1;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant")
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant")
|
||||||
EDTFluxParticipantStatusType Status = EDTFluxParticipantStatusType::Unknown;
|
EDTFluxParticipantStatusType Status = EDTFluxParticipantStatusType::Unknown;
|
||||||
};
|
};
|
||||||
@ -10,7 +10,6 @@
|
|||||||
#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)
|
||||||
@ -33,7 +32,7 @@ void UDTFluxCoreSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|||||||
{
|
{
|
||||||
RegisterDelegates();
|
RegisterDelegates();
|
||||||
}
|
}
|
||||||
PursuitManager = NewObject<UDTFluxPursuitManager>(this);
|
PursuitManager = NewObject<UDTFluxPursuitManager>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::Deinitialize()
|
void UDTFluxCoreSubsystem::Deinitialize()
|
||||||
@ -53,89 +52,6 @@ void UDTFluxCoreSubsystem::SaveDataStorage()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::ProcessTrackedResponse(FDTFluxServerResponse& InResponse)
|
|
||||||
{
|
|
||||||
switch (InResponse.GetResponseType())
|
|
||||||
{
|
|
||||||
case EDTFluxApiDataType::ContestRanking:
|
|
||||||
{
|
|
||||||
FDTFluxContestRankings Rankings;
|
|
||||||
if (InResponse.ParseContestRanking(Rankings))
|
|
||||||
{
|
|
||||||
OnContestRankings.Broadcast(Rankings.ContestId, 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))
|
|
||||||
{
|
|
||||||
FDTFluxStageKey StageKey(Rankings.ContestId, Rankings.StageId);
|
|
||||||
OnStageRankings.Broadcast(StageKey, 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))
|
|
||||||
{
|
|
||||||
FDTFluxSplitKey SplitKey(Rankings.ContestId, Rankings.StageId, Rankings.SplitId);
|
|
||||||
OnSplitRankings.Broadcast(SplitKey, 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)
|
||||||
@ -168,6 +84,9 @@ 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
|
||||||
@ -185,77 +104,12 @@ void UDTFluxCoreSubsystem::RegisterDelegates()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::IsStageRankingSealed(FDTFluxStageKey StageKey)
|
|
||||||
{
|
|
||||||
FDTFluxStageRankings StageRankings;
|
|
||||||
if (GetStageRankingsWithKey(StageKey, StageRankings))
|
|
||||||
{
|
|
||||||
FDTFluxStage Stage;
|
|
||||||
if (GetStageDefinition(StageKey, Stage))
|
|
||||||
{
|
|
||||||
return StageRankings.IsSealed(Stage.EndTime);
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Unable to find Stage %i"), StageKey.StageId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Unable to find StageRankings for StageKey %i"), StageKey.StageId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::IsContestRankingSealed(int ContestId)
|
|
||||||
{
|
|
||||||
if (DataStorage)
|
|
||||||
{
|
|
||||||
FDTFluxContestRankings ContestRankings;
|
|
||||||
if (GetContestRankings(ContestId, ContestRankings))
|
|
||||||
{
|
|
||||||
FDTFluxContest Contest;
|
|
||||||
if (GetContestForId(ContestId, Contest))
|
|
||||||
{
|
|
||||||
return ContestRankings.IsSealed(Contest.EndTime);
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Unable to find Contest %i"), ContestId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Unable to find ContestRankings for ContestId %i"), ContestId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage not available"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
EDTFluxFinisherType UDTFluxCoreSubsystem::GetSplitSensorType(const FDTFluxSplitSensorInfo& SplitSensorInfo)
|
|
||||||
{
|
|
||||||
if (DataStorage != nullptr)
|
|
||||||
{
|
|
||||||
if (DataStorage->LastSplitIdCache.Contains(SplitSensorInfo.ContestId))
|
|
||||||
{
|
|
||||||
int LastSplitIdForContest = DataStorage->LastSplitIdCache[SplitSensorInfo.ContestId];
|
|
||||||
if (LastSplitIdForContest == SplitSensorInfo.SplitId)
|
|
||||||
{
|
|
||||||
if (SplitSensorInfo.Rank == 1 )
|
|
||||||
{
|
|
||||||
return EDTFluxFinisherType::Winner;
|
|
||||||
}
|
|
||||||
return EDTFluxFinisherType::Finish;
|
|
||||||
}
|
|
||||||
return EDTFluxFinisherType::None;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("LastSplitIdCache not found for ContestId %i"), SplitSensorInfo.ContestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage not available"));
|
|
||||||
return EDTFluxFinisherType::None;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::ProcessRaceData(const FDTFluxRaceData& RaceDataDefinition)
|
void UDTFluxCoreSubsystem::ProcessRaceData(const FDTFluxRaceData& RaceDataDefinition)
|
||||||
{
|
{
|
||||||
if (RaceDataDefinition.Datas.Num() > 0)
|
if (RaceDataDefinition.Datas.Num() > 0)
|
||||||
{
|
{
|
||||||
// UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Receiving RaceDataDefinition [%s]"),
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Receiving RaceDataDefinition [%s]"),
|
||||||
// *RaceDataDefinition.Datas[0].Name);
|
*RaceDataDefinition.Datas[0].Name);
|
||||||
if (DataStorage != nullptr)
|
if (DataStorage != nullptr)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage Name %s"), *DataStorage->EventName);
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage Name %s"), *DataStorage->EventName);
|
||||||
@ -297,24 +151,21 @@ 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);
|
||||||
if (bShouldKeepRankings)
|
|
||||||
{
|
|
||||||
SaveDataStorage();
|
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);
|
||||||
if (bShouldKeepRankings) { SaveDataStorage(); }
|
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);
|
||||||
if (bShouldKeepRankings) { SaveDataStorage(); }
|
SaveDataStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::ProcessTeamStatusUpdate(const FDTFluxTeamStatusUpdate& NewParticipantStatus)
|
void UDTFluxCoreSubsystem::ProcessTeamStatusUpdate(const FDTFluxTeamStatusUpdate& NewParticipantStatus)
|
||||||
@ -332,51 +183,20 @@ void UDTFluxCoreSubsystem::ProcessTeamUpdate(const FDTFluxTeamListDefinition& Te
|
|||||||
|
|
||||||
void UDTFluxCoreSubsystem::ProcessSplitSensor(const FDTFluxSplitSensorInfo& SplitSensorInfo)
|
void UDTFluxCoreSubsystem::ProcessSplitSensor(const FDTFluxSplitSensorInfo& SplitSensorInfo)
|
||||||
{
|
{
|
||||||
|
FDTFluxContest Contest;
|
||||||
if (DataStorage != nullptr)
|
FDTFluxStageKey StageKey(SplitSensorInfo.ContestId, SplitSensorInfo.StageId);
|
||||||
{
|
FDTFluxStage Stage;
|
||||||
FString DebugString = FString::Printf(TEXT("Received SplitSensorInfo for Bib %i"), SplitSensorInfo.Bib);
|
DataStorage->GetStage(StageKey, Stage);
|
||||||
DebugString += FString::Printf(TEXT("ContestId[%i] StageId[%i] SplitId[%i] Time[%s], Gap[%s] Rank[%i]"),
|
|
||||||
SplitSensorInfo.ContestId, SplitSensorInfo.StageId, SplitSensorInfo.SplitId, *SplitSensorInfo.Time,
|
|
||||||
*SplitSensorInfo.Gap, SplitSensorInfo.Rank);
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received SplitSensorInfo :\n%s"), *DebugString)
|
|
||||||
// Gestion Cache Split Sensor
|
|
||||||
FDTFluxSplitSensorKey SplitSensorKey(SplitSensorInfo.ContestId, SplitSensorInfo.StageId, SplitSensorInfo.SplitId, -1);
|
|
||||||
FDTFluxSplitSensorInfo NewSplitSensorInfo = SplitSensorInfo;
|
|
||||||
if (DataStorage->SplitSensorInfoCache.Contains(SplitSensorKey))
|
|
||||||
{
|
|
||||||
NewSplitSensorInfo.SplitName = DataStorage->SplitSensorInfoCache[SplitSensorKey].SplitName;
|
|
||||||
}
|
|
||||||
SplitSensorKey.Bib = SplitSensorInfo.Bib;
|
|
||||||
DataStorage->SplitSensorInfoCache.Add(SplitSensorKey, NewSplitSensorInfo);
|
|
||||||
// Update Current currentSplit
|
|
||||||
FDTFluxParticipant Participant;
|
FDTFluxParticipant Participant;
|
||||||
if (DataStorage->Participants.Contains(SplitSensorInfo.Bib))
|
DataStorage->GetParticipantByBib(SplitSensorInfo.Bib, Participant);
|
||||||
{
|
|
||||||
DataStorage->Participants[SplitSensorInfo.Bib].CurrentSplit = SplitSensorInfo.SplitId;
|
DataStorage->GetContestById(SplitSensorInfo.ContestId, Contest);
|
||||||
}
|
UE_LOG(logDTFluxCoreSubsystem, Log, TEXT("%s|%s Split %i Sensor for Participant [Bib] %i [FullName] %s"),
|
||||||
// Gestion Finnish Status
|
*Contest.Name, *Stage.Name,
|
||||||
switch (GetSplitSensorType(SplitSensorInfo))
|
SplitSensorInfo.SplitId, SplitSensorInfo.Bib, *Participant.GetFormattedName());
|
||||||
{
|
|
||||||
case EDTFluxFinisherType::Winner:
|
|
||||||
{
|
|
||||||
OnWinner.Broadcast(SplitSensorInfo);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDTFluxFinisherType::Finish :
|
|
||||||
{
|
|
||||||
OnFinisher.Broadcast(SplitSensorInfo);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
OnSplitSensor.Broadcast(SplitSensorInfo);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::SendRequest(const FString& Message)
|
void UDTFluxCoreSubsystem::SendRequest(const FString& Message)
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
@ -385,437 +205,78 @@ void UDTFluxCoreSubsystem::SendRequest(const FString& Message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::InitParticipantTracking(const int Bib, const int ContestId, const int StageId)
|
void UDTFluxCoreSubsystem::SendTeamListRequest()
|
||||||
{
|
|
||||||
FDTFluxContest Contest;
|
|
||||||
if (GetContestForId(ContestId, Contest))
|
|
||||||
{
|
|
||||||
// get all splits
|
|
||||||
TArray<FDTFluxSplitSensorInfo> SplitSensorInfos;
|
|
||||||
FDTFluxSplitSensorKey SplitSensorKey;
|
|
||||||
SplitSensorKey.ContestId = ContestId;
|
|
||||||
SplitSensorKey.StageId = StageId;
|
|
||||||
SplitSensorKey.Bib = Bib;
|
|
||||||
for (auto Split : Contest.Splits)
|
|
||||||
{
|
|
||||||
SplitSensorKey.SplitId = Split.SplitId;
|
|
||||||
if (DataStorage->SplitSensorInfoCache.Contains(SplitSensorKey))
|
|
||||||
{
|
|
||||||
SplitSensorInfos.Add(DataStorage->SplitSensorInfoCache[SplitSensorKey]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SplitSensorInfos.Add(FDTFluxSplitSensorInfo(Split.Name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FDTFluxSplitHistory History;
|
|
||||||
History.SplitSensors = SplitSensorInfos;
|
|
||||||
OnParticipantTrackingReady.Broadcast(History);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
FDTFluxSplitHistory SplitHistory;
|
|
||||||
if (GetParticipant(Bib, SplitHistory.Participant))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
FString Text = "sqfhds";
|
|
||||||
FName Key = FName(Text);
|
|
||||||
}
|
|
||||||
|
|
||||||
FGuid UDTFluxCoreSubsystem::InitContestRankingsDisplay(const int ContestId)
|
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
{
|
{
|
||||||
if (DataStorage)
|
NetworkSubsystem->SendRequest(EDTFluxRequestType::TeamList);
|
||||||
{
|
|
||||||
// no need to request ContestRankings;
|
|
||||||
if (IsContestRankingSealed(ContestId))
|
|
||||||
{
|
|
||||||
const FGuid DisplayRequestId = FGuid::NewGuid();
|
|
||||||
OnContestRankingDisplayReady.Broadcast(DisplayRequestId, true);
|
|
||||||
return DisplayRequestId;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
FOnDTFluxRequestSuccess OnSuccess = FOnDTFluxRequestSuccess::CreateLambda(
|
|
||||||
[this](const FDTFluxTrackedRequest& Request)
|
|
||||||
{
|
|
||||||
FDTFluxContestRankings Rankings = FDTFluxContestRankings();
|
|
||||||
if (Request.ParsedResponse.IsSet())
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Request IsSet()"));
|
|
||||||
TSharedPtr<FDTFluxServerResponse> ResponsePtr = Request.ParsedResponse.GetValue();
|
|
||||||
ResponsePtr->ParseContestRanking(Rankings);
|
|
||||||
this->DataStorage->AddContestRanking(Rankings);
|
|
||||||
this->OnContestRankingDisplayReady.Broadcast(Request.RequestId, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Request IsSet(FALSE)"));
|
|
||||||
this->OnStageRankingDisplayReady.Broadcast(Request.RequestId, false);
|
|
||||||
});
|
|
||||||
FOnDTFluxRequestError OnError = FOnDTFluxRequestError::CreateLambda(
|
|
||||||
[this](const FDTFluxTrackedRequest& InReq, const FString& InError)
|
|
||||||
{
|
|
||||||
this->OnStageRankingDisplayReady.Broadcast(InReq.RequestId, false);
|
|
||||||
});
|
|
||||||
FGuid DisplayRequestId = NetworkSubsystem->SendTrackedRequestWithCallbacks(
|
|
||||||
EDTFluxApiDataType::ContestRanking, ContestId, -1, -1, OnSuccess, OnError, true);
|
|
||||||
return DisplayRequestId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DTFluxDataStorage unavailable ..."));
|
|
||||||
OnContestRankingDisplayReady.Broadcast(FGuid(), false);
|
|
||||||
return FGuid();
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DTFluxNetworkSubsystem unavailable ..."));
|
|
||||||
OnContestRankingDisplayReady.Broadcast(FGuid(), false);
|
|
||||||
return FGuid();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FGuid UDTFluxCoreSubsystem::InitStageRankingsDisplay(const int ContestId, const int StageId)
|
void UDTFluxCoreSubsystem::SendRaceDataRequest()
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
{
|
{
|
||||||
if (DataStorage)
|
NetworkSubsystem->SendRequest(EDTFluxRequestType::RaceData);
|
||||||
{
|
|
||||||
// no need to request StageRankings;
|
|
||||||
if (IsStageRankingSealed(FDTFluxStageKey(ContestId, StageId)))
|
|
||||||
{
|
|
||||||
const FGuid DisplayRequestId = FGuid::NewGuid();
|
|
||||||
OnStageRankingDisplayReady.Broadcast(DisplayRequestId, true);
|
|
||||||
return DisplayRequestId;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
FOnDTFluxRequestSuccess OnSuccess = FOnDTFluxRequestSuccess::CreateLambda(
|
|
||||||
[this](const FDTFluxTrackedRequest& Request)
|
|
||||||
{
|
|
||||||
FDTFluxStageRankings Rankings = FDTFluxStageRankings();
|
|
||||||
if (Request.ParsedResponse.IsSet())
|
|
||||||
{
|
|
||||||
TSharedPtr<FDTFluxServerResponse> ResponsePtr = Request.ParsedResponse.GetValue();
|
|
||||||
ResponsePtr->ParseStageRanking(Rankings);
|
|
||||||
this->DataStorage->AddStageRanking(Rankings);
|
|
||||||
this->OnStageRankingDisplayReady.Broadcast(Request.RequestId, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->OnStageRankingDisplayReady.Broadcast(Request.RequestId, false);
|
|
||||||
});
|
|
||||||
FOnDTFluxRequestError OnError = FOnDTFluxRequestError::CreateLambda(
|
|
||||||
[this](const FDTFluxTrackedRequest& InReq, const FString& InError)
|
|
||||||
{
|
|
||||||
this->OnStageRankingDisplayReady.Broadcast(InReq.RequestId, false);
|
|
||||||
});
|
|
||||||
FGuid DisplayRequestId = NetworkSubsystem->SendTrackedRequestWithCallbacks(
|
|
||||||
EDTFluxApiDataType::StageRanking, ContestId, StageId, -1, OnSuccess, OnError, true);
|
|
||||||
return DisplayRequestId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DTFluxNetworkSubsystem unavailable ..."));
|
|
||||||
const FGuid RequestId = FGuid::NewGuid();
|
|
||||||
OnStageRankingDisplayReady.Broadcast(RequestId, false);
|
|
||||||
return RequestId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FGuid UDTFluxCoreSubsystem::InitSplitRankingsDisplay(const int ContestId, const int StageId, const int SplitId)
|
void UDTFluxCoreSubsystem::SendContestRankingRequest(int InContestId)
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
{
|
{
|
||||||
if (DataStorage)
|
NetworkSubsystem->SendRequest(EDTFluxRequestType::ContestRanking, InContestId);
|
||||||
{
|
|
||||||
FOnDTFluxRequestSuccess OnSuccess = FOnDTFluxRequestSuccess::CreateLambda(
|
|
||||||
[this](const FDTFluxTrackedRequest& Request)
|
|
||||||
{
|
|
||||||
FDTFluxSplitRankings Rankings = FDTFluxSplitRankings();
|
|
||||||
if (Request.ParsedResponse.IsSet())
|
|
||||||
{
|
|
||||||
TSharedPtr<FDTFluxServerResponse> ResponsePtr = Request.ParsedResponse.GetValue();
|
|
||||||
ResponsePtr->ParseSplitRanking(Rankings);
|
|
||||||
this->DataStorage->AddSplitRanking(Rankings);
|
|
||||||
this->OnSplitRankingDisplayReady.Broadcast(Request.RequestId, true);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
this->OnSplitRankingDisplayReady.Broadcast(Request.RequestId, false);
|
|
||||||
});
|
|
||||||
FOnDTFluxRequestError OnError = FOnDTFluxRequestError::CreateLambda(
|
|
||||||
[this](const FDTFluxTrackedRequest& InReq, const FString& InError)
|
|
||||||
{
|
|
||||||
this->OnSplitRankingDisplayReady.Broadcast(InReq.RequestId, false);
|
|
||||||
});
|
|
||||||
FGuid DisplayRequestId = NetworkSubsystem->SendTrackedRequestWithCallbacks(
|
|
||||||
EDTFluxApiDataType::SplitRanking, ContestId, StageId, SplitId, OnSuccess, OnError, true);
|
|
||||||
return DisplayRequestId;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DTFluxDatastorage unavailable ..."));
|
|
||||||
OnSplitRankingDisplayReady.Broadcast(FGuid(), false);
|
|
||||||
return FGuid();
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DTFluxNetworkSubsystem unavailable ..."));
|
|
||||||
OnSplitRankingDisplayReady.Broadcast(FGuid(), false);
|
|
||||||
return FGuid();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::GetStageRankingForBib(const int ContestId, const int StageId, const int Bib,
|
void UDTFluxCoreSubsystem::SendStageRankingRequest(int InContestId, int InStageId, bool bShouldIncludeSplitRanking)
|
||||||
FDTFluxStageRanking& OutStageRanking)
|
|
||||||
{
|
{
|
||||||
if (DataStorage)
|
// 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)
|
||||||
{
|
{
|
||||||
FDTFluxStageKey StageKey(ContestId, StageId);
|
|
||||||
if (DataStorage->StageRankings.Contains(StageKey))
|
if (DataStorage->StageRankings.Contains(StageKey))
|
||||||
{
|
{
|
||||||
FDTFluxStageRankings StageRankings = DataStorage->StageRankings[StageKey];
|
return DataStorage->StageRankings[StageKey];
|
||||||
for (auto& Ranking : StageRankings.Rankings)
|
|
||||||
{
|
|
||||||
if (Ranking.Bib == Bib)
|
|
||||||
{
|
|
||||||
OutStageRanking = static_cast<FDTFluxStageRanking>(Ranking);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Cannot find StageRankings for key [%s]"), *StageKey.GetDisplayName());
|
||||||
}
|
return FDTFluxStageRankings();
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Unable to find StageRanking for Bib %i"), Bib);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage not available"));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::GetSplitRankingForBib(const int ContestId, const int StageId, const int SplitId,
|
void UDTFluxCoreSubsystem::RequestAllSplitRankingOfStage(int InContestId, int InStageId, int InSplitId)
|
||||||
const int Bib, FDTFluxSplitRanking& OutSplitRankings)
|
|
||||||
{
|
{
|
||||||
if (DataStorage)
|
// TODO Implement this
|
||||||
{
|
|
||||||
FDTFluxSplitKey SplitKey(ContestId, StageId, SplitId);
|
|
||||||
if (DataStorage->SplitRankings.Contains(SplitKey))
|
|
||||||
{
|
|
||||||
FDTFluxSplitRankings SplitRankings = DataStorage->SplitRankings[SplitKey];
|
|
||||||
for (auto& Ranking : SplitRankings.Rankings)
|
|
||||||
{
|
|
||||||
if (Ranking.Bib == Bib)
|
|
||||||
{
|
|
||||||
OutSplitRankings = static_cast<FDTFluxSplitRanking>(Ranking);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Unable to find SplitRanking for Bib %i"), Bib);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage not available"));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::GetContestRanking(const int ContestId, FDTFluxContestRanking& OutContestRanking)
|
const FDTFluxParticipant UDTFluxCoreSubsystem::GetParticipant(int InBib)
|
||||||
{
|
|
||||||
if (DataStorage)
|
|
||||||
{
|
|
||||||
FDTFluxContest Contest;
|
|
||||||
if (GetContestForId(ContestId, Contest))
|
|
||||||
{
|
|
||||||
for (auto& Ranking : DataStorage->ContestRankings[ContestId].Rankings)
|
|
||||||
{
|
|
||||||
OutContestRanking = Ranking;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Unable to find ContestRanking for ContestId %i"), ContestId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage not available"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::GetContestRankings(const int ContestId,
|
|
||||||
FDTFluxContestRankings& OutContestRankings)
|
|
||||||
{
|
|
||||||
if (DataStorage->ContestRankings.Contains(ContestId))
|
|
||||||
{
|
|
||||||
OutContestRankings = DataStorage->ContestRankings[ContestId];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (NetworkSubsystem)
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Requesting ContestRankings for ContestId %i"), ContestId);
|
|
||||||
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, bool bEnableCache)
|
|
||||||
{
|
|
||||||
if (NetworkSubsystem)
|
|
||||||
{
|
|
||||||
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, bEnableCache);
|
|
||||||
RequestIds.Add(ContestRequest);
|
|
||||||
}
|
|
||||||
return RequestIds;
|
|
||||||
}
|
|
||||||
return TArray<FGuid>();
|
|
||||||
}
|
|
||||||
|
|
||||||
TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestStageRankings(const TArray<FDTFluxStageKey> ForStages,
|
|
||||||
bool bEnableCache)
|
|
||||||
{
|
|
||||||
if (NetworkSubsystem)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
for (auto StageKey : ForStages)
|
|
||||||
{
|
|
||||||
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::StageRanking,
|
|
||||||
StageKey.ContestId, StageKey.StageId, -1, OnSuccess, OnError, bEnableCache);
|
|
||||||
RequestIds.Add(ContestRequest);
|
|
||||||
}
|
|
||||||
return RequestIds;
|
|
||||||
}
|
|
||||||
return TArray<FGuid>();
|
|
||||||
}
|
|
||||||
|
|
||||||
TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestSplitRankings(const TArray<FDTFluxSplitKey> ForSplits,
|
|
||||||
bool bEnableCache)
|
|
||||||
{
|
|
||||||
if (NetworkSubsystem)
|
|
||||||
{
|
|
||||||
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, bEnableCache);
|
|
||||||
RequestIds.Add(ContestRequest);
|
|
||||||
}
|
|
||||||
return RequestIds;
|
|
||||||
}
|
|
||||||
return TArray<FGuid>();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::GetParticipant(int InBib, FDTFluxParticipant& OutParticipant)
|
|
||||||
{
|
{
|
||||||
if (DataStorage->Participants.Contains(InBib))
|
if (DataStorage->Participants.Contains(InBib))
|
||||||
{
|
{
|
||||||
OutParticipant = DataStorage->Participants[InBib];
|
return DataStorage->Participants[InBib];
|
||||||
return true;
|
}
|
||||||
|
return FDTFluxParticipant();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
void UDTFluxCoreSubsystem::RefreshStorage()
|
||||||
|
{
|
||||||
|
// TODO Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<int> UDTFluxCoreSubsystem::GetCurrentContestsId()
|
TArray<int> UDTFluxCoreSubsystem::GetCurrentContestsId()
|
||||||
@ -823,30 +284,24 @@ TArray<int> UDTFluxCoreSubsystem::GetCurrentContestsId()
|
|||||||
return GetContestsIdForTime(FDateTime::Now());
|
return GetContestsIdForTime(FDateTime::Now());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::GetCurrentContests(TArray<FDTFluxContest>& OutContests)
|
TArray<FDTFluxContest> UDTFluxCoreSubsystem::GetCurrentContests()
|
||||||
{
|
{
|
||||||
return GetContestsForTime(FDateTime::Now(), OutContests);
|
return GetContestsForTime(FDateTime::Now());
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<int> UDTFluxCoreSubsystem::GetContestsIdForTime(const FDateTime Time)
|
TArray<int> UDTFluxCoreSubsystem::GetContestsIdForTime(const FDateTime Time)
|
||||||
{
|
{
|
||||||
if (DataStorage)
|
TArray<int> Contests;
|
||||||
|
for (const auto& Pair : DataStorage->Contests)
|
||||||
{
|
{
|
||||||
TArray<FDTFluxContest> Contests;
|
FDTFluxContest Contest = Pair.Value;
|
||||||
if (GetContestsForTime(Time, Contests))
|
int ContestId = Contest.ContestId;
|
||||||
|
if (Contest.Date < Time && Contest.EndTime > Time)
|
||||||
{
|
{
|
||||||
TArray<int> ContestIds = TArray<int>();
|
Contests.Add(ContestId);
|
||||||
for (const auto& Contest : Contests)
|
|
||||||
{
|
|
||||||
ContestIds.Add(Contest.ContestId);
|
|
||||||
}
|
}
|
||||||
return ContestIds;
|
|
||||||
}
|
}
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("No Contest running for Time [%s]"), *Time.ToString());
|
return Contests;
|
||||||
return TArray<int>();
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage not available"));
|
|
||||||
return TArray<int>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::GetContestForId(const int Id, FDTFluxContest& OutContest)
|
bool UDTFluxCoreSubsystem::GetContestForId(const int Id, FDTFluxContest& OutContest)
|
||||||
@ -863,119 +318,51 @@ bool UDTFluxCoreSubsystem::GetContestForId(const int Id, FDTFluxContest& OutCont
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::GetContestsForTime(const FDateTime Time, TArray<FDTFluxContest>& OutContests)
|
TArray<FDTFluxContest> UDTFluxCoreSubsystem::GetContestsForTime(const FDateTime Time)
|
||||||
{
|
{
|
||||||
if (DataStorage)
|
TArray<FDTFluxContest> Contests;
|
||||||
{
|
|
||||||
OutContests.Empty();
|
|
||||||
for (const auto& Pair : DataStorage->Contests)
|
for (const auto& Pair : DataStorage->Contests)
|
||||||
{
|
{
|
||||||
FDTFluxContest Contest = Pair.Value;
|
FDTFluxContest Contest = Pair.Value;
|
||||||
int ContestId = Contest.ContestId;
|
int ContestId = Contest.ContestId;
|
||||||
|
|
||||||
//ils ont commencé.
|
|
||||||
if (Contest.Date < Time && Contest.EndTime > Time)
|
if (Contest.Date < Time && Contest.EndTime > Time)
|
||||||
{
|
{
|
||||||
// ils sont finis
|
Contests.Add(Contest);
|
||||||
if (!Contest.IsFinished())
|
|
||||||
{
|
|
||||||
OutContests.Add(Contest);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return Contests;
|
||||||
if (!OutContests.IsEmpty())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("No Contest running for Time [%s]"), *Time.ToString());
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage not available"));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::GetContests(TArray<FDTFluxContest>& OutContests)
|
void UDTFluxCoreSubsystem::RequestRankingsForStages(TArray<FDTFluxStage> RequestedStages) const
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<FDTFluxContest> UDTFluxCoreSubsystem::GetContests()
|
||||||
{
|
{
|
||||||
OutContests.Empty();
|
|
||||||
if (DataStorage)
|
if (DataStorage)
|
||||||
{
|
{
|
||||||
|
TArray<FDTFluxContest> OutContests;
|
||||||
DataStorage->Contests.GenerateValueArray(OutContests);
|
DataStorage->Contests.GenerateValueArray(OutContests);
|
||||||
return !OutContests.IsEmpty();
|
return OutContests;
|
||||||
}
|
}
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage not available"));
|
return TArray<FDTFluxContest>();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::GetContest(const int ContestId, FDTFluxContest& OutContest)
|
void UDTFluxCoreSubsystem::LaunchPursuitSequenceFor(const TArray<int> ContestIds)
|
||||||
{
|
{
|
||||||
OutContest = FDTFluxContest();
|
TArray<FDTFluxContest> Contests = TArray<FDTFluxContest>();
|
||||||
if (GetContestForId(ContestId, OutContest))
|
for (const auto& ContestId : ContestIds)
|
||||||
{
|
{
|
||||||
return;
|
FDTFluxContest Contest;
|
||||||
|
GetContestForId(ContestId, Contest);
|
||||||
|
Contests.Add(Contest);
|
||||||
|
if (PursuitManager)
|
||||||
|
{
|
||||||
|
PursuitManager->LaunchPursuitSequenceFor(Contests);
|
||||||
}
|
}
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestId %d not found in ContestDefinition"), ContestId)
|
else
|
||||||
}
|
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::GetStageDefinition(const FDTFluxStageKey StageKey, FDTFluxStage& OutStageDefinition)
|
|
||||||
{
|
{
|
||||||
int ContestId = StageKey.ContestId;
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("PursuitManager is null"));
|
||||||
int StageId = StageKey.StageId;
|
|
||||||
FDTFluxContest ContestDefinition;
|
|
||||||
if (GetContestForId(ContestId, ContestDefinition))
|
|
||||||
{
|
|
||||||
for (auto& Stage : ContestDefinition.Stages)
|
|
||||||
{
|
|
||||||
if (Stage.StageId == StageId)
|
|
||||||
{
|
|
||||||
OutStageDefinition = Stage;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OutStageDefinition = FDTFluxStage();
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestId %d, StageId %d not found in ContestDefinition"),
|
|
||||||
ContestId, StageId)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UDTFluxCoreSubsystem::GetSplitDefinition(const FDTFluxSplitKey SplitKey, FDTFluxSplit& OutSplitDefinition)
|
|
||||||
{
|
|
||||||
int ContestId = SplitKey.ContestId;
|
|
||||||
int SplitId = SplitKey.SplitId;
|
|
||||||
FDTFluxContest ContestDefinition;
|
|
||||||
if (GetContestForId(ContestId, ContestDefinition))
|
|
||||||
{
|
|
||||||
for (auto& Split : ContestDefinition.Splits)
|
|
||||||
{
|
|
||||||
if (Split.SplitId == SplitId)
|
|
||||||
{
|
|
||||||
OutSplitDefinition = Split;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OutSplitDefinition = FDTFluxSplit();
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestId %d, SplitId %d not found in ContestDefinition"),
|
|
||||||
ContestId, SplitId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::GetStage(const int ContestId, const int StageId, FDTFluxStage& OutStageDefinition)
|
|
||||||
{
|
|
||||||
if (GetStageDefinition(FDTFluxStageKey(ContestId, StageId),
|
|
||||||
OutStageDefinition))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OutStageDefinition = FDTFluxStage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::GetSplit(const int ContestId, const int StageId, const int SplitId,
|
|
||||||
FDTFluxSplit& OutSplitDefinition)
|
|
||||||
{
|
|
||||||
if (GetSplitDefinition(FDTFluxSplitKey(ContestId, StageId, SplitId),
|
|
||||||
OutSplitDefinition))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OutSplitDefinition = FDTFluxSplit();
|
|
||||||
}
|
|
||||||
|
|||||||
@ -3,210 +3,83 @@
|
|||||||
|
|
||||||
#include "DTFluxPursuitManager.h"
|
#include "DTFluxPursuitManager.h"
|
||||||
|
|
||||||
#include <ImportExport.h>
|
|
||||||
|
|
||||||
#include "DTFluxCoreSubsystem.h"
|
|
||||||
#include "DTFluxCoreSubsystemModule.h"
|
#include "DTFluxCoreSubsystemModule.h"
|
||||||
#include "Dataflow/DataflowContextCache.h"
|
|
||||||
|
|
||||||
UDTFluxPursuitManager::UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer):
|
UDTFluxPursuitManager::UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer):
|
||||||
Super(ObjectInitializer)
|
Super(ObjectInitializer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxPursuitManager::InitPursuit(const TArray<int> InContestIds, const int MaxSimultaneousPursuit)
|
// TODO : Add way to pass MaxSimultaneousPursuit and MassStartDelay
|
||||||
|
// For now it's done in UPROPERTIES
|
||||||
|
void UDTFluxPursuitManager::LaunchPursuitSequenceFor(const TArray<FDTFluxContest> InContests)
|
||||||
{
|
{
|
||||||
CoreSubsystem = Cast<UDTFluxCoreSubsystem>(GetOuter());
|
if (InitSubSystems())
|
||||||
if (!CoreSubsystem)
|
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("CoreSubsystem is not Available !!!"));
|
for (const auto Contest : InContests)
|
||||||
return;
|
|
||||||
}
|
|
||||||
AllRankings.Reset();
|
|
||||||
for (const auto& ContestId : InContestIds)
|
|
||||||
{
|
{
|
||||||
FDTFluxContest Contest;
|
FRequestData RequestData;
|
||||||
if (CoreSubsystem->GetContestForId(ContestId, Contest))
|
RequestData.ContestId = Contest.ContestId;
|
||||||
{
|
uint8 StageId = Contest.Stages.Last().StageId;
|
||||||
BindRankings();
|
FGuid Guid = NetworkSubsystem->SendTrackedRequestWithCallback(EDTFluxApiDataType::StageRanking,
|
||||||
FDTFluxStageKey StageKey = FDTFluxStageKey(ContestId, Contest.GetLastStageId());
|
Contest.ContestId, StageId, -1,
|
||||||
FDTFluxStageRankings TempStageRankings;
|
FOnDTFluxTrackedRequestResponse::CreateUObject(
|
||||||
//Obtenir les ranking Frais.
|
this,
|
||||||
CoreSubsystem->GetStageRankingsWithKey(StageKey, TempStageRankings, false);
|
&UDTFluxPursuitManager::OnRequestResponse),
|
||||||
PendingStageRanking.Add(StageKey, false);
|
FOnDTFluxTrackedRequestTimeout::CreateUObject(
|
||||||
|
this,
|
||||||
|
&UDTFluxPursuitManager::OnRequestTimeoutResponse),
|
||||||
|
FOnDTFluxRequestResponseError::CreateUObject(
|
||||||
|
this,
|
||||||
|
&UDTFluxPursuitManager::OnRequestError));
|
||||||
|
|
||||||
|
RequestData.RequestIds.Add(Guid);
|
||||||
|
PendingRequestData.Add(RequestData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxPursuitManager::SetPursuitInfoIsMassStart(FDTFluxPursuitGroup& NextFocusGroup)
|
void UDTFluxPursuitManager::OnRequestResponse(const FGuid& RequestId, FDTFluxServerResponse& Response)
|
||||||
{
|
{
|
||||||
for (auto& Pursuit : NextFocusGroup.PursuitGroup)
|
UE_LOG(logDTFluxCoreSubsystem, Log,
|
||||||
|
TEXT("UDTFluxPursuitManager::OnRequestResponse() Received Ranking For Stage %i"), Response.StageID)
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Log, TEXT("Response is %s"), *UEnum::GetValueAsString(Response.GetResponseType()))
|
||||||
|
//check if request
|
||||||
|
if (Response.GetResponseType() == EDTFluxApiDataType::StageRanking)
|
||||||
{
|
{
|
||||||
Pursuit.bIsMassStart = Pursuit.StartTime >= MassStartTime;
|
FDTFluxStageRankings Rankings;
|
||||||
|
FRequestData FoundData;
|
||||||
|
if (Response.ParseStageRankingResponse(Rankings))
|
||||||
|
{
|
||||||
|
for (auto& PendingReq : PendingRequestData)
|
||||||
|
{
|
||||||
|
// Check for a matching PendingReq
|
||||||
|
if (PendingReq.IsWaitingFor(RequestId, Rankings))
|
||||||
|
{
|
||||||
|
FoundData = PendingReq;
|
||||||
|
// A request Is Terminated
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Log,
|
||||||
|
TEXT("UDTFluxPursuitManager::OnRequestResponse() Ranking for Stage %i is complete"),
|
||||||
|
Response.StageID)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (InitPursuit(FoundData))
|
||||||
|
{
|
||||||
|
OnPursuitSequenceReady.Broadcast(NextFocusPursuits, NextFocusPursuits, bFocusIsTruncate);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxPursuitManager::DebugFocusNext(const TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext)
|
void UDTFluxPursuitManager::OnRequestTimeoutResponse(const FGuid& RequestId, const FString& TimeoutMessage)
|
||||||
{
|
{
|
||||||
FString FocusBibs;
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Request Timeout [%s]"), *TimeoutMessage);
|
||||||
for (const auto& Pursuit : OutPursuitFocusNext)
|
|
||||||
{
|
|
||||||
FocusBibs += FString::Printf(TEXT("%d "), Pursuit.Bib);
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus Bibs: %s"), *FocusBibs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxPursuitManager::DebugOutPoursuitNext(const TArray<FDTFluxPursuitInfo>& OutPursuitNext)
|
void UDTFluxPursuitManager::OnRequestError(const FGuid& RequestId, const FString& ErrorMessage)
|
||||||
{
|
{
|
||||||
FString NextBibs;
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Request Error [%s]"), *ErrorMessage);
|
||||||
for (int32 i = 0; i < OutPursuitNext.Num(); i++)
|
|
||||||
{
|
|
||||||
NextBibs += FString::Printf(TEXT("%d "), OutPursuitNext[i].Bib);
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next Bibs: %s"), *NextBibs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext,
|
|
||||||
TArray<FDTFluxPursuitInfo>& OutPursuitNext, bool& BIsFocusTruncate,
|
|
||||||
const int MaxSimultaneousPursuit)
|
|
||||||
{
|
|
||||||
FDateTime MetricsStartFunction = FDateTime::UtcNow();
|
|
||||||
FDateTime CurrentTime = FDateTime::Now();
|
|
||||||
|
|
||||||
// UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== GetPursuit CALLED ==="));
|
|
||||||
// UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("MaxSimultaneousPursuit: %d"), MaxSimultaneousPursuit);
|
|
||||||
// UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Available groups: %d"), GroupedPursuit.Num());
|
|
||||||
|
|
||||||
// BAd Parameter
|
|
||||||
if (MaxSimultaneousPursuit <= 0)
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Invalid MaxSimultaneousPursuit: %d"), MaxSimultaneousPursuit);
|
|
||||||
OutPursuitFocusNext.Reset();
|
|
||||||
OutPursuitNext.Reset();
|
|
||||||
BIsFocusTruncate = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (bIsSequenceDone || GroupedPursuit.IsEmpty())
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("No groups available or sequence completed"));
|
|
||||||
OutPursuitFocusNext.Reset();
|
|
||||||
OutPursuitNext.Reset();
|
|
||||||
BIsFocusTruncate = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int32 i = GroupedPursuit.Num() - 1; i >= 0; i--) // Parcours inverse pour éviter les problèmes d'index
|
|
||||||
{
|
|
||||||
const FDTFluxPursuitGroup& Group = GroupedPursuit[i];
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Log, TEXT("Group.StartTimeGlobal(%s) < CurrentTime(%s) "),
|
|
||||||
*Group.StartTimeGlobal.ToString(), *CurrentTime.ToString())
|
|
||||||
// Vérifier si le StartTime du groupe est déjà passé
|
|
||||||
if (Group.StartTimeGlobal < CurrentTime)
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning,
|
|
||||||
TEXT("Removing expired group: StartTime=%s (Current=%s), Participants=%d"),
|
|
||||||
*Group.StartTimeGlobal.ToString(),
|
|
||||||
*CurrentTime.ToString(),
|
|
||||||
Group.PursuitGroup.Num());
|
|
||||||
|
|
||||||
GroupedPursuit.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
OutPursuitFocusNext.Reset();
|
|
||||||
OutPursuitNext.Reset();
|
|
||||||
|
|
||||||
// === VÉRIFICATION CRITIQUE : S'assurer qu'il reste des groupes après suppression ===
|
|
||||||
if (GroupedPursuit.IsEmpty())
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("All groups were expired and removed - no groups available"));
|
|
||||||
OutPursuitFocusNext.Reset();
|
|
||||||
OutPursuitNext.Reset();
|
|
||||||
BIsFocusTruncate = false;
|
|
||||||
bIsSequenceDone = true; // Marquer la séquence comme terminée
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FDTFluxPursuitGroup FocusGroup = GroupedPursuit[0];
|
|
||||||
GroupedPursuit.RemoveAt(0);
|
|
||||||
|
|
||||||
SetPursuitInfoIsMassStart(FocusGroup);
|
|
||||||
OutPursuitFocusNext = FocusGroup.PursuitGroup;
|
|
||||||
BIsFocusTruncate = FocusGroup.PursuitGroup.Num() > 1;
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning,
|
|
||||||
TEXT("Focus Group: StartTime=%s, Participants=%d"),
|
|
||||||
*FocusGroup.StartTimeGlobal.ToString(),
|
|
||||||
FocusGroup.PursuitGroup.Num());
|
|
||||||
|
|
||||||
int32 TargetNextCount = MaxSimultaneousPursuit - 1;
|
|
||||||
int32 AddedNextCount = 0;
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Target Next Count: %d"), TargetNextCount);
|
|
||||||
|
|
||||||
for (int32 GroupIndex = 0;
|
|
||||||
GroupIndex < GroupedPursuit.Num() && AddedNextCount < TargetNextCount;
|
|
||||||
GroupIndex++)
|
|
||||||
{
|
|
||||||
FDTFluxPursuitGroup& NextGroup = GroupedPursuit[GroupIndex];
|
|
||||||
|
|
||||||
if (NextGroup.PursuitGroup.Num() == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 AvailableInGroup = NextGroup.PursuitGroup.Num();
|
|
||||||
int32 NeededFromGroup = FMath::Min(TargetNextCount - AddedNextCount, AvailableInGroup);
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning,
|
|
||||||
TEXT("Processing Next Group %d: StartTime=%s, Available=%d, Taking=%d"),
|
|
||||||
GroupIndex,
|
|
||||||
*NextGroup.StartTimeGlobal.ToString(),
|
|
||||||
AvailableInGroup,
|
|
||||||
NeededFromGroup);
|
|
||||||
|
|
||||||
for (int32 ParticipantIndex = 0; ParticipantIndex < NeededFromGroup; ParticipantIndex++)
|
|
||||||
{
|
|
||||||
FDTFluxPursuitInfo NextParticipant = NextGroup.PursuitGroup[ParticipantIndex]; // Copie
|
|
||||||
|
|
||||||
NextParticipant.bIsMassStart = NextParticipant.StartTime >= MassStartTime;
|
|
||||||
|
|
||||||
OutPursuitNext.Add(NextParticipant);
|
|
||||||
AddedNextCount++;
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, VeryVerbose,
|
|
||||||
TEXT("Added to Next: Bib %d from Group %d"),
|
|
||||||
NextParticipant.Bib, GroupIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// === LOGS DE RÉSUMÉ ===
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== PURSUIT RESULTS ==="));
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus: %d participants"), OutPursuitFocusNext.Num());
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next: %d participants"), OutPursuitNext.Num());
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Remaining groups for future: %d"), GroupedPursuit.Num());
|
|
||||||
|
|
||||||
if (OutPursuitFocusNext.Num() > 0)
|
|
||||||
{
|
|
||||||
DebugFocusNext(OutPursuitFocusNext);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log détaillé des Next (limité pour éviter spam)
|
|
||||||
if (OutPursuitNext.Num() > 0)
|
|
||||||
{
|
|
||||||
DebugOutPoursuitNext(OutPursuitNext);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérifier si la séquence est terminée
|
|
||||||
if (GroupedPursuit.IsEmpty())
|
|
||||||
{
|
|
||||||
bIsSequenceDone = true;
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Pursuit sequence will be completed after this round"));
|
|
||||||
}
|
|
||||||
FTimespan Duration = FDateTime::UtcNow() - MetricsStartFunction;
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Metrics Time Out: %d fraction seconds"),
|
|
||||||
Duration.GetDuration().GetFractionMicro());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxPursuitManager::InitSubSystems()
|
bool UDTFluxPursuitManager::InitSubSystems()
|
||||||
@ -219,64 +92,22 @@ bool UDTFluxPursuitManager::InitSubSystems()
|
|||||||
return NetworkSubsystem != nullptr;
|
return NetworkSubsystem != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxPursuitManager::BindRankings()
|
bool UDTFluxPursuitManager::InitPursuit(FRequestData Data)
|
||||||
{
|
{
|
||||||
if (CoreSubsystem)
|
//Clean Data
|
||||||
{
|
NextFocusPursuits.Empty();
|
||||||
if (!bIsRankingBounded)
|
NextPursuits.Empty();
|
||||||
{
|
PursuitGrouped.Empty();
|
||||||
CoreSubsystem->OnStageRankings.AddDynamic(this, &UDTFluxPursuitManager::OnRankingsReceived);
|
TArray<FDTFluxDetailedRankingItem> AllRankings;
|
||||||
bIsRankingBounded = true;
|
|
||||||
}
|
|
||||||
return bIsRankingBounded;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("CoreSubsystem is not Available !!!"));
|
|
||||||
return bIsRankingBounded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxPursuitManager::UnbindRankings()
|
|
||||||
{
|
|
||||||
if (CoreSubsystem)
|
|
||||||
{
|
|
||||||
if (bIsRankingBounded)
|
|
||||||
{
|
|
||||||
CoreSubsystem->OnStageRankings.RemoveDynamic(this, &UDTFluxPursuitManager::OnRankingsReceived);
|
|
||||||
bIsRankingBounded = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bIsRankingBounded = false;
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("CoreSubsystem is not Available !!!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxPursuitManager::OnRankingsReceived(const FDTFluxStageKey NewStageKey,
|
|
||||||
const FDTFluxStageRankings NewStageRankings)
|
|
||||||
{
|
|
||||||
if (PendingStageRanking.Contains(NewStageKey))
|
|
||||||
{
|
|
||||||
PendingStageRanking.Remove(NewStageKey);
|
|
||||||
AllRankings.Add(NewStageRankings);
|
|
||||||
if (PendingStageRanking.IsEmpty())
|
|
||||||
{
|
|
||||||
//everything is ready to go compute and start
|
|
||||||
UnbindRankings();
|
|
||||||
LaunchPursuitSequence();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UDTFluxPursuitManager::LaunchPursuitSequence()
|
|
||||||
{
|
|
||||||
GroupedPursuit.Empty();
|
|
||||||
TArray<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& Ranking : AllRankings)
|
for (auto& KeyPair : Data.StageRankings)
|
||||||
{
|
{
|
||||||
for (auto StageRanking : Ranking.Rankings)
|
for (auto StageRanking : KeyPair.Value.Rankings)
|
||||||
{
|
{
|
||||||
int ContestId = Ranking.ContestId;
|
int ContestId = KeyPair.Value.ContestId;
|
||||||
FDTFluxPursuitInfo PursuitInfo;
|
FDTFluxPursuitInfo PursuitInfo;
|
||||||
PursuitInfo.StartTime = StageRanking.StartTime;
|
PursuitInfo.StartTime = StageRanking.StartTime;
|
||||||
PursuitInfo.Bib = StageRanking.Bib;
|
PursuitInfo.Bib = StageRanking.Bib;
|
||||||
@ -284,67 +115,38 @@ bool UDTFluxPursuitManager::LaunchPursuitSequence()
|
|||||||
AllPursuits.Add(PursuitInfo);
|
AllPursuits.Add(PursuitInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("AllPursuits.Num() = %i"), AllPursuits.Num());
|
// 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))
|
||||||
{
|
{
|
||||||
FDTFluxPursuitGroup& Group = TempGroups[Pursuit.StartTime];
|
TempGroups[Pursuit.StartTime].PursuitGroup.Add(Pursuit);
|
||||||
Group.PursuitGroup.Add(Pursuit);
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning,
|
|
||||||
TEXT("Adding [%i] To PursuitGroup starting At %s, PursuitGroup.Num() %i"),
|
|
||||||
Pursuit.Bib, *Pursuit.StartTime.ToString(), Group.PursuitGroup.Num());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FDTFluxPursuitGroup NewGroup;
|
FDTFluxPursuitGroup Group;
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("New Group starting At %s, Adding Bib [%i]"),
|
Group.StartTimeGlobal = Pursuit.StartTime;
|
||||||
*Pursuit.StartTime.ToString(), Pursuit.Bib);
|
Group.PursuitGroup.Add(Pursuit);
|
||||||
NewGroup.StartTimeGlobal = Pursuit.StartTime;
|
TempGroups.Add(Pursuit.StartTime, Group);
|
||||||
NewGroup.PursuitGroup.Add(Pursuit);
|
|
||||||
TempGroups.Add(Pursuit.StartTime, NewGroup);
|
|
||||||
for (const auto& Group : TempGroups)
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Group.StartTime = %s, Group.PursuitGroup.Num() = %i"),
|
|
||||||
*Group.Key.ToString(), Group.Value.PursuitGroup.Num());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TempGroups.KeySort([](const FDateTime& A, const FDateTime& B)
|
TempGroups.KeySort([](const FDateTime& A, const FDateTime& B)
|
||||||
{
|
{
|
||||||
return A < B;
|
return A < B;
|
||||||
});
|
});
|
||||||
TMap<FDateTime, int> StartTimeFrequency;
|
PursuitGrouped.Reserve(TempGroups.Num());
|
||||||
int32 MaxFrequency = 0;
|
|
||||||
GroupedPursuit.Reserve(TempGroups.Num());
|
|
||||||
// parcours du TMap
|
|
||||||
for (const auto& Pair : TempGroups)
|
for (const auto& Pair : TempGroups)
|
||||||
{
|
{
|
||||||
if (Pair.Value.StartTimeGlobal != FDateTime::MinValue() && Pair.Value.StartTimeGlobal != FDateTime::MaxValue())
|
PursuitGrouped.Add(Pair.Value);
|
||||||
{
|
|
||||||
// récuperation de la ref de la valeur actuel de la fréquence dans la TMap Freq
|
|
||||||
int& CurrentFreq = StartTimeFrequency.FindOrAdd(Pair.Value.StartTimeGlobal, 0);
|
|
||||||
CurrentFreq = Pair.Value.PursuitGroup.Num();
|
|
||||||
if (CurrentFreq > MaxFrequency)
|
|
||||||
{
|
|
||||||
MaxFrequency = CurrentFreq;
|
|
||||||
MassStartTime = Pair.Value.StartTimeGlobal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GroupedPursuit.Add(Pair.Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupedPursuit.Sort([](const FDTFluxPursuitGroup& A, const FDTFluxPursuitGroup& B)
|
PursuitGrouped.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);
|
|
||||||
FPursuitStarterData PursuitData = FPursuitStarterData(FocusPursuits, NextPursuits, MassStartTime, bIsFocusTruncate);
|
|
||||||
OnPursuitSequenceReady.Broadcast(PursuitData);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "DTFluxCoreSubsystemModule.h"
|
|
||||||
#include "Assets/DTFluxModelAsset.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"
|
||||||
@ -17,51 +14,27 @@ class UDTFluxNetworkSubsystem;
|
|||||||
/** Forward Decl */
|
/** Forward Decl */
|
||||||
class UDTFluxModelAsset;
|
class UDTFluxModelAsset;
|
||||||
class UDTFluxPursuitManager;
|
class UDTFluxPursuitManager;
|
||||||
struct FDTFluxServerResponse;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
UCLASS(BlueprintType, meta=(DisplayName="DTFlux Core Subsystem"))
|
UCLASS()
|
||||||
class DTFLUXCORESUBSYSTEM_API UDTFluxCoreSubsystem : public UEngineSubsystem
|
class DTFLUXCORESUBSYSTEM_API UDTFluxCoreSubsystem : public UEngineSubsystem
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSplitRankings, FDTFluxSplitKey, SplitKey, FDTFluxSplitRankings,
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSplitRankings, FDTFluxSplitRankings&, SplitRankings);
|
||||||
SplitRankings);
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnSplitRankings OnSplitRankings;
|
FOnSplitRankings OnSplitRankings;
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnStageRankings, FDTFluxStageKey, StageKey, FDTFluxStageRankings,
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStageRankings, FDTFluxStageRankings&, StageRankings);
|
||||||
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_TwoParams(FOnContestRankingDisplayReady, const FGuid, RequestId, const bool,
|
|
||||||
bSuccesRequest);
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
|
||||||
FOnContestRankingDisplayReady OnContestRankingDisplayReady;
|
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnStageRankingDisplayReady, const FGuid, RequestId, const bool,
|
|
||||||
bSuccesRequest);
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
|
||||||
FOnStageRankingDisplayReady OnStageRankingDisplayReady;
|
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSplitRankingDisplayReady, const FGuid, RequestId, const bool,
|
|
||||||
bSuccesRequest);
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
|
||||||
FOnSplitRankingDisplayReady OnSplitRankingDisplayReady;
|
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnContestRankings, const int, ContestId, FDTFluxContestRankings,
|
|
||||||
ContestRankings);
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnContestRankings OnContestRankings;
|
FOnContestRankings OnContestRankings;
|
||||||
@ -70,125 +43,87 @@ public:
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem")
|
DECLARE_DELEGATE_TwoParams(FOnRequestedStageRankings, const FDTFluxStageKey&, const FDTFluxContestRankings&);
|
||||||
UDTFluxPursuitManager* PursuitManager = nullptr;
|
FOnRequestedStageRankings OnRequestedStageRankings;
|
||||||
|
//
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSplitSensor, FDTFluxSplitSensorInfo, SplitSensorInfo);
|
// DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnTeamUpdate, FDateTime, ReceivedAt, FDTFluxParticipant, TeamUpdatedList);
|
||||||
|
// UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
// FOnTeamUpdate OnTeamUpdate;
|
||||||
FOnSplitSensor OnSplitSensor;
|
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFinisher, FDTFluxSplitSensorInfo, SplitSensorInfo);
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
|
||||||
FOnFinisher OnFinisher;
|
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPreFinish, FDTFluxSplitSensorInfo, SplitSensorInfo);
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
|
||||||
FOnPreFinish OnPreFinish;
|
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWinner, FDTFluxSplitSensorInfo, SplitSensorInfo);
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
|
||||||
FOnWinner OnWinner;
|
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnParticipantTrackingReady, FDTFluxSplitHistory, SplitHistory);
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
|
||||||
FOnParticipantTrackingReady OnParticipantTrackingReady;
|
|
||||||
|
|
||||||
//TODO : this must be a ProjectSetting
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem")
|
|
||||||
bool bShouldKeepRankings = true;
|
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void InitParticipantTracking(const int Bib, const int ContestId, const int StageId);
|
void SendTeamListRequest();
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
FGuid InitContestRankingsDisplay(const int ContestIds);
|
void SendRaceDataRequest();
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
FGuid InitStageRankingsDisplay(const int ContestId, const int StageId);
|
void SendContestRankingRequest(int InContestId);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
FGuid InitSplitRankingsDisplay(const int ContestId, const int StageId, const int SplitId);
|
void SendStageRankingRequest(int InContestId, int InStageId, bool bShouldIncludeSplitRanking = true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
bool GetStageRankingForBib(const int ContestId, const int StageId, const int Bib,
|
void RequestAllStageRankingOfContest(int InContestId, int InStageId, bool bShouldIncludeSplitRanking = true);
|
||||||
FDTFluxStageRanking& OutStageRankings);
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
bool GetSplitRankingForBib(const int ContestId, const int StageId, const int SplitId, const int Bib,
|
void SendSplitRankingRequest(int InContestId, int InStageId, int InSplitId);
|
||||||
FDTFluxSplitRanking& OutSplitRankings);
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
bool GetContestRanking(const int ContestId, FDTFluxContestRanking& OutContestRanking);
|
void RequestAllSplitRankingOfContest(int InContestId, int InStageId);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
bool GetContestRankings(const int ContestId, FDTFluxContestRankings& OutContestRankings);
|
FDTFluxStageRankings GetStageRankings(FDTFluxStageKey StageKey);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
bool GetStageRankings(const int ContestId, const int StageId, FDTFluxStageRankings& OutStageRankings);
|
void RequestAllSplitRankingOfStage(int InContestId, int InStageId, int InSplitId);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
bool GetSplitRankings(const int ContestId, const int StageId, const int SplitId,
|
const FDTFluxParticipant GetParticipant(int InBib);
|
||||||
FDTFluxSplitRankings& OutSplitRankings);
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
bool GetStageRankingsWithKey(const FDTFluxStageKey StageKey, FDTFluxStageRankings& OutStageRankings,
|
|
||||||
const bool bShouldUseCached = true);
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
bool GetSplitRankingsWithKey(const FDTFluxSplitKey SplitKey, FDTFluxSplitRankings& OutSplitRankings,
|
|
||||||
const bool bShouldUseCached = true);
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
TArray<FGuid> TrackedRequestContestRankings(const TArray<int> ForContests, bool bEnableCache = true);
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
TArray<FGuid> TrackedRequestStageRankings(const TArray<FDTFluxStageKey> ForStages, bool bEnableCache = true);
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
TArray<FGuid> TrackedRequestSplitRankings(const TArray<FDTFluxSplitKey> ForSplits, bool bEnableCache = true);
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
bool GetParticipant(int InBib, FDTFluxParticipant& OutParticipant);
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
void RefreshStorage();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
TArray<int> GetCurrentContestsId();
|
TArray<int> GetCurrentContestsId();
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION()
|
||||||
bool GetCurrentContests(TArray<FDTFluxContest>& OutContests);
|
TArray<FDTFluxContest> GetCurrentContests();
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION()
|
||||||
TArray<int> GetContestsIdForTime(const FDateTime Time);
|
TArray<int> GetContestsIdForTime(const FDateTime Time);
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION()
|
||||||
bool GetContestForId(const int Id, FDTFluxContest& OutContest);
|
bool GetContestForId(const int Id, FDTFluxContest& OutContest);
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
bool GetContestsForTime(const FDateTime Time, TArray<FDTFluxContest>& OutContests);
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
bool GetContests(TArray<FDTFluxContest>& OutContests);
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
void GetContest(const int ContestId, FDTFluxContest& OutContest);
|
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
bool GetStageDefinition(const FDTFluxStageKey StageKey, FDTFluxStage& OutStageDefinition);
|
TArray<FDTFluxContest> GetContestsForTime(const FDateTime Time);
|
||||||
|
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
bool GetSplitDefinition(const FDTFluxSplitKey SplitKey, FDTFluxSplit& OutSplitDefinition);
|
void RequestRankingsForStages(const TArray<FDTFluxStage> RequestedStages) const;
|
||||||
|
UFUNCTION()
|
||||||
|
TArray<FDTFluxContest> GetContests();
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void GetStage(const int ContestId, const int StageId, FDTFluxStage& OutStageDefinition);
|
void LaunchPursuitSequenceFor(const TArray<int> ContestIds);
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
void GetSplit(const int ContestId, const int StageId, const int SplitId, FDTFluxSplit& OutSplitDefinition);
|
|
||||||
|
|
||||||
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);
|
||||||
@ -210,11 +145,8 @@ private:
|
|||||||
void SendRequest(const FString& Message);
|
void SendRequest(const FString& Message);
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void RegisterDelegates();
|
void RegisterDelegates();
|
||||||
UFUNCTION()
|
|
||||||
bool IsStageRankingSealed(FDTFluxStageKey StageKey);
|
|
||||||
UFUNCTION()
|
|
||||||
bool IsContestRankingSealed(int ContestId);
|
|
||||||
|
|
||||||
EDTFluxFinisherType GetSplitSensorType(const FDTFluxSplitSensorInfo& SplitSensorInfo);
|
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
UDTFluxModelAsset* DataStorage = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,7 +29,6 @@ public:
|
|||||||
|
|
||||||
UFUNCTION(Blueprintable, Category="DTFlux|Core Subsystem|Tools")
|
UFUNCTION(Blueprintable, Category="DTFlux|Core Subsystem|Tools")
|
||||||
static float ConvertTimeStringToSeconds(const FString& TimeString);
|
static float ConvertTimeStringToSeconds(const FString& TimeString);
|
||||||
|
|
||||||
UFUNCTION(Blueprintable, Category="DTFlux|Core Subsystem|Tools")
|
UFUNCTION(Blueprintable, Category="DTFlux|Core Subsystem|Tools")
|
||||||
static bool CompareTimeString(const FString& A_TimeStr, const FString& B_TimeStr, bool bAscendant = true);
|
static bool CompareTimeString(const FString& A_TimeStr, const FString& B_TimeStr, bool bAscendant = true);
|
||||||
|
|
||||||
|
|||||||
@ -8,33 +8,44 @@
|
|||||||
#include "DTFluxPursuitManager.generated.h"
|
#include "DTFluxPursuitManager.generated.h"
|
||||||
|
|
||||||
|
|
||||||
class UDTFluxCoreSubsystem;
|
USTRUCT()
|
||||||
|
struct FRequestData
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
|
||||||
struct FPursuitStarterData
|
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
UPROPERTY()
|
||||||
FPursuitStarterData() = default;
|
TArray<FGuid> RequestIds;
|
||||||
|
UPROPERTY()
|
||||||
|
TMap<FGuid, FDTFluxStageRankings> StageRankings;
|
||||||
|
UPROPERTY()
|
||||||
|
int ContestId;
|
||||||
|
|
||||||
FPursuitStarterData(const TArray<FDTFluxPursuitInfo>& InPursuitFocusNext,
|
UPROPERTY()
|
||||||
const TArray<FDTFluxPursuitInfo>& InPursuitNext, const FDateTime& InMassStartTime,
|
bool bIsReady = false;
|
||||||
const bool InIsFocusTruncate)
|
|
||||||
: PursuitFocusNext(InPursuitFocusNext), PursuitNext(InPursuitNext), MassStartTime(InMassStartTime),
|
|
||||||
bIsFocusTruncate(InIsFocusTruncate)
|
FRequestData() = default;
|
||||||
|
|
||||||
|
FRequestData(const TArray<FGuid>& InRequestIds, const TMap<FGuid, FDTFluxStageRankings>& InStageRankings)
|
||||||
|
: RequestIds(InRequestIds), StageRankings(InStageRankings)
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
/**
|
||||||
TArray<FDTFluxPursuitInfo> PursuitFocusNext = TArray<FDTFluxPursuitInfo>();
|
*
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
* @param RequestId
|
||||||
TArray<FDTFluxPursuitInfo> PursuitNext = TArray<FDTFluxPursuitInfo>();
|
* @param InRankings
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
* @return True if all needed requests have responses
|
||||||
FDateTime MassStartTime = FDateTime::MinValue();
|
*/
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
bool IsWaitingFor(const FGuid& RequestId, const FDTFluxStageRankings& InRankings)
|
||||||
bool bIsFocusTruncate = false;
|
{
|
||||||
|
if (!StageRankings.Contains(RequestId))
|
||||||
|
{
|
||||||
|
StageRankings.Add(RequestId, InRankings);
|
||||||
|
}
|
||||||
|
bIsReady = StageRankings.Num() <= RequestIds.Num();
|
||||||
|
return bIsReady;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
USTRUCT()
|
USTRUCT()
|
||||||
@ -42,7 +53,6 @@ struct FDTFluxPursuitGroup
|
|||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TArray<FDTFluxPursuitInfo> PursuitGroup = TArray<FDTFluxPursuitInfo>();
|
TArray<FDTFluxPursuitInfo> PursuitGroup = TArray<FDTFluxPursuitInfo>();
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
@ -53,6 +63,10 @@ struct FDTFluxPursuitGroup
|
|||||||
bool bIsFocus = false;
|
bool bIsFocus = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnPursuitSequenceReady, const TArray<FDTFluxPursuitInfo>,
|
||||||
|
NextFocusPursuits,
|
||||||
|
const TArray<FDTFluxPursuitInfo>, NextPursuit, bool, bIsTrtuncate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -64,66 +78,56 @@ class DTFLUXCORESUBSYSTEM_API UDTFluxPursuitManager : public UObject
|
|||||||
public:
|
public:
|
||||||
UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer);
|
UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer);
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
|
TArray<FDTFluxPursuitInfo> NextFocusPursuits;
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPursuitSequenceReady, const FPursuitStarterData, PursuitData);
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
|
TArray<FDTFluxPursuitInfo> NextPursuits;
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
|
||||||
FOnPursuitSequenceReady OnPursuitSequenceReady;
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
bool bFocusIsTruncate = false;
|
bool bFocusIsTruncate = false;
|
||||||
|
//
|
||||||
|
// UPROPERTY()
|
||||||
|
// TArray<FDTFluxStage> TargetStages;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int PursuitMaxSimultaneousPursuit = 7;
|
int MaxSimultaneousPursuit = 7;
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="DTFlux|Pursuit",
|
||||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="DTFlux|Pursuit")
|
meta=(ClampMin="1", ClampMax="60", UIMin="0", UIMax="60"))
|
||||||
int MassStartDelay = 10;
|
int MassStartDelay = 10;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FDateTime MassStartTime = FDateTime::MinValue();
|
TArray<FDTFluxPursuitGroup> PursuitGrouped;
|
||||||
|
|
||||||
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 InitPursuit(const TArray<int> InContestIds, const int MaxSimultaneousPursuit = 7);
|
void LaunchPursuitSequenceFor(const TArray<FDTFluxContest> InContests);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="pursuit, launch, poursuite"))
|
UFUNCTION()
|
||||||
void GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext, TArray<FDTFluxPursuitInfo>& OutPursuitNext,
|
void OnRequestResponse(const FGuid& RequestId, FDTFluxServerResponse& Response);
|
||||||
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 BindRankings();
|
bool InitPursuit(FRequestData Data);
|
||||||
|
|
||||||
UFUNCTION()
|
|
||||||
void UnbindRankings();
|
|
||||||
|
|
||||||
UFUNCTION()
|
|
||||||
void OnRankingsReceived(const FDTFluxStageKey NewStageKey, const FDTFluxStageRankings NewStageRankings);
|
|
||||||
|
|
||||||
|
|
||||||
void DebugFocusNext(const TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext);
|
|
||||||
void DebugOutPoursuitNext(const TArray<FDTFluxPursuitInfo>& OutPursuitNext);
|
|
||||||
|
|
||||||
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();
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,318 +0,0 @@
|
|||||||
#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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,83 +1,51 @@
|
|||||||
// ================================================================================================
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
// DTFluxRequestManager.cpp - Implémentation du gestionnaire de requêtes
|
|
||||||
// ================================================================================================
|
|
||||||
|
|
||||||
#include "DTFluxQueuedManager.h"
|
#include "DTFluxQueuedManager.h"
|
||||||
|
|
||||||
#include "DTFluxAsyncParser.h"
|
|
||||||
#include "DTFluxNetworkModule.h"
|
#include "DTFluxNetworkModule.h"
|
||||||
#include "Struct/DTFluxServerResponseStruct.h"
|
|
||||||
#include "Struct/DTFluxRequestStructs.h"
|
|
||||||
#include "JsonObjectConverter.h"
|
#include "JsonObjectConverter.h"
|
||||||
|
|
||||||
bool FDTFluxTrackedRequest::HasTimedOut() const
|
|
||||||
{
|
|
||||||
if (State != EDTFluxRequestState::Pending && State != EDTFluxRequestState::Sent)
|
|
||||||
return false;
|
|
||||||
return (FDateTime::Now() - CreatedAt).GetTotalSeconds() > Config.TimeoutSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FDTFluxTrackedRequest::CanRetry() const
|
const FString FDTFluxQueuedRequest::Serialize() const
|
||||||
{
|
|
||||||
return CurrentRetries < Config.MaxRetries &&
|
|
||||||
(State == EDTFluxRequestState::Failed || State == EDTFluxRequestState::TimedOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float FDTFluxTrackedRequest::GetRetryDelay() const
|
|
||||||
{
|
|
||||||
return FMath::Pow(Config.RetryBackoffMultiplier, CurrentRetries);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FDTFluxTrackedRequest::Matches(EDTFluxApiDataType InType, int32 InContestId, int32 InStageId,
|
|
||||||
int32 InSplitId) const
|
|
||||||
{
|
|
||||||
return RequestType == InType && ContestId == InContestId && StageId == InStageId && SplitId == InSplitId;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void FDTFluxTrackedRequest::SetRawResponse(const FString& RawData)
|
|
||||||
{
|
|
||||||
RawResponseData = RawData;
|
|
||||||
ParsedResponse.Reset();
|
|
||||||
bIsResponseParsed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FString FDTFluxTrackedRequest::Serialize() const
|
|
||||||
{
|
{
|
||||||
FString JSONString;
|
FString JSONString;
|
||||||
switch (RequestType)
|
switch (RequestType)
|
||||||
{
|
{
|
||||||
case EDTFluxApiDataType::RaceData:
|
case EDTFluxRequestType::RaceData:
|
||||||
|
|
||||||
{
|
{
|
||||||
FDTFluxRaceDataRequest RaceData;
|
FDTFluxRaceDataRequest RaceData;
|
||||||
FJsonObjectConverter::UStructToJsonObjectString(RaceData, JSONString);
|
FJsonObjectConverter::UStructToJsonObjectString(RaceData, JSONString);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EDTFluxApiDataType::TeamList:
|
|
||||||
|
case EDTFluxRequestType::TeamList:
|
||||||
{
|
{
|
||||||
const FDTFluxTeamListRequest TeamList;
|
const FDTFluxTeamListRequest TeamList;
|
||||||
FJsonObjectConverter::UStructToJsonObjectString(TeamList, JSONString);
|
FJsonObjectConverter::UStructToJsonObjectString(TeamList, JSONString);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EDTFluxApiDataType::ContestRanking:
|
|
||||||
|
case EDTFluxRequestType::ContestRanking:
|
||||||
{
|
{
|
||||||
FDTFluxContestRankingRequest ContestRanking(ContestId);
|
FDTFluxContestRankingRequest ContestRanking(ContestId);
|
||||||
FJsonObjectConverter::UStructToJsonObjectString(ContestRanking, JSONString);
|
FJsonObjectConverter::UStructToJsonObjectString(ContestRanking, JSONString);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EDTFluxApiDataType::StageRanking:
|
|
||||||
|
case EDTFluxRequestType::StageRanking:
|
||||||
{
|
{
|
||||||
FDTFluxStageRankingRequest StageRanking(ContestId, StageId);
|
FDTFluxStageRankingRequest StageRanking(ContestId, StageId);
|
||||||
FJsonObjectConverter::UStructToJsonObjectString(StageRanking, JSONString);
|
FJsonObjectConverter::UStructToJsonObjectString(StageRanking, JSONString);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EDTFluxApiDataType::SplitRanking:
|
|
||||||
|
case EDTFluxRequestType::SplitRanking:
|
||||||
{
|
{
|
||||||
FDTFluxSplitRankingRequest SplitRanking(ContestId, StageId, SplitId);
|
FDTFluxSplitRankingRequest SplitRanking(ContestId, StageId, SplitId);
|
||||||
FJsonObjectConverter::UStructToJsonObjectString(SplitRanking, JSONString);
|
FJsonObjectConverter::UStructToJsonObjectString(SplitRanking, JSONString);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
JSONString = "";
|
JSONString = "";
|
||||||
break;
|
break;
|
||||||
@ -85,586 +53,387 @@ FString FDTFluxTrackedRequest::Serialize() const
|
|||||||
return JSONString;
|
return JSONString;
|
||||||
}
|
}
|
||||||
|
|
||||||
FDTFluxQueuedRequestManager::FDTFluxQueuedRequestManager()
|
UDTFluxQueuedManager::UDTFluxQueuedManager()
|
||||||
|
: bIsInitialized(false)
|
||||||
|
, CheckInterval(0.5f)
|
||||||
|
, TimeSinceLastCheck(0.0f)
|
||||||
{
|
{
|
||||||
AsyncParser = MakeUnique<FDTFluxAsyncParser>();
|
|
||||||
UE_LOG(logDTFluxNetwork, Verbose, TEXT("RequestManager created"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FDTFluxQueuedRequestManager::~FDTFluxQueuedRequestManager()
|
UDTFluxQueuedManager::~UDTFluxQueuedManager()
|
||||||
{
|
{
|
||||||
Shutdown();
|
ClearAllRequests();
|
||||||
UE_LOG(logDTFluxNetwork, Verbose, TEXT("RequestManager destroyed"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::Initialize(const FDTFluxRequestConfig& InDefaultConfig)
|
void UDTFluxQueuedManager::Initialize()
|
||||||
{
|
{
|
||||||
if (bIsInitialized.load())
|
if (!bIsInitialized)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxNetwork, Warning, TEXT("RequestManager already initialized"));
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Initializing DTFluxQueuedManager"));
|
||||||
return;
|
bIsInitialized = true;
|
||||||
}
|
|
||||||
|
|
||||||
DefaultConfig = InDefaultConfig;
|
|
||||||
bIsInitialized.store(true);
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("RequestManager initialized with timeout=%.1fs"),
|
|
||||||
DefaultConfig.TimeoutSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::Shutdown()
|
|
||||||
{
|
|
||||||
if (!bIsInitialized.load())
|
|
||||||
return;
|
|
||||||
|
|
||||||
bIsInitialized.store(false);
|
|
||||||
// Nettoyer toutes les données
|
|
||||||
{
|
|
||||||
FScopeLock RequestsLock_Local(&RequestsLock);
|
|
||||||
FScopeLock CallbacksLock_Local(&CallbacksLock);
|
|
||||||
|
|
||||||
AllRequests.Empty();
|
|
||||||
SuccessCallbacks.Empty();
|
|
||||||
ErrorCallbacks.Empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("RequestManager shutdown"));
|
|
||||||
}
|
|
||||||
|
|
||||||
FGuid FDTFluxQueuedRequestManager::CreateTrackedRequest(
|
|
||||||
EDTFluxApiDataType RequestType,
|
|
||||||
int32 ContestId,
|
|
||||||
int32 StageId,
|
|
||||||
int32 SplitId,
|
|
||||||
const FDTFluxRequestConfig& CustomConfig)
|
|
||||||
{
|
|
||||||
if (!bIsInitialized.load())
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxNetwork, Error, TEXT("RequestManager not initialized"));
|
|
||||||
return FGuid();
|
|
||||||
}
|
|
||||||
// Create new request
|
|
||||||
auto NewRequest = MakeShared<FDTFluxTrackedRequest>();
|
|
||||||
NewRequest->RequestType = RequestType;
|
|
||||||
NewRequest->ContestId = ContestId;
|
|
||||||
NewRequest->StageId = StageId;
|
|
||||||
NewRequest->SplitId = SplitId;
|
|
||||||
NewRequest->Config = (CustomConfig.TimeoutSeconds > 0) ? CustomConfig : DefaultConfig;
|
|
||||||
|
|
||||||
FGuid RequestId = NewRequest->RequestId;
|
|
||||||
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
AllRequests.Add(RequestId, NewRequest);
|
|
||||||
TotalRequests++;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("Created tracked request %s: Type=%s, Contest=%d, Stage=%d, Split=%d"),
|
|
||||||
*RequestId.ToString(), *UEnum::GetValueAsString(RequestType), ContestId, StageId, SplitId);
|
|
||||||
return RequestId;
|
|
||||||
}
|
|
||||||
|
|
||||||
FGuid FDTFluxQueuedRequestManager::CreateTrackedRequestWithCallbacks(
|
|
||||||
EDTFluxApiDataType RequestType,
|
|
||||||
int32 ContestId,
|
|
||||||
int32 StageId,
|
|
||||||
int32 SplitId,
|
|
||||||
FOnDTFluxRequestSuccess OnSuccess,
|
|
||||||
FOnDTFluxRequestError OnError,
|
|
||||||
const FDTFluxRequestConfig& CustomConfig)
|
|
||||||
{
|
|
||||||
FGuid RequestId = CreateTrackedRequest(RequestType, ContestId, StageId, SplitId, CustomConfig);
|
|
||||||
|
|
||||||
if (RequestId.IsValid())
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&CallbacksLock);
|
|
||||||
|
|
||||||
if (OnSuccess.IsBound())
|
|
||||||
{
|
|
||||||
SuccessCallbacks.Add(RequestId, OnSuccess);
|
|
||||||
}
|
|
||||||
if (OnError.IsBound())
|
|
||||||
{
|
|
||||||
ErrorCallbacks.Add(RequestId, OnError);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestId;
|
FGuid UDTFluxQueuedManager::QueueRequest(EDTFluxRequestType RequestType, int32 ContestId, int32 StageId, int32 SplitId,
|
||||||
|
const FString& RawMessage)
|
||||||
|
{
|
||||||
|
// Créer la requête avec les structs existants
|
||||||
|
FDTFluxQueuedRequest NewRequest(RequestType, ContestId, StageId, SplitId);
|
||||||
|
NewRequest.RawResponse = RawMessage;
|
||||||
|
|
||||||
|
// Ajouter à la queue des requêtes en attente
|
||||||
|
PendingRequestsQueue.Enqueue(NewRequest);
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Queued request %s: Type=%d, ContestId=%d, StageId=%d, SplitId=%d"),
|
||||||
|
*NewRequest.RequestId.ToString(), (int32)RequestType, ContestId, StageId, SplitId);
|
||||||
|
|
||||||
|
return NewRequest.RequestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxQueuedRequestManager::MarkRequestAsSent(const FGuid& RequestId)
|
bool UDTFluxQueuedManager::MarkRequestAsError(const FGuid& TargetRequestGuid)
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&RequestsLock);
|
// TODO: Implement a retry mechanism
|
||||||
|
// For now we simply suppress the request and log a message
|
||||||
if (TSharedPtr<FDTFluxTrackedRequest>* RequestPtr = AllRequests.Find(RequestId))
|
bool bFoundMatch = false;
|
||||||
|
FDTFluxQueuedRequest Request;
|
||||||
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TempQueue;
|
||||||
|
while (PendingRequestsQueue.Dequeue(Request))
|
||||||
{
|
{
|
||||||
TSharedPtr<FDTFluxTrackedRequest> Request = *RequestPtr;
|
if (Request.RequestId == TargetRequestGuid)
|
||||||
Request->SentAt = FDateTime::Now();
|
|
||||||
Request->LastAttemptTime = FDateTime::Now();
|
|
||||||
ChangeRequestState(Request, EDTFluxRequestState::Sent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FDTFluxQueuedRequestManager::CompleteRequest(const FGuid& RequestId, const FString& RawResponseData,
|
|
||||||
bool bUseAsyncParsing)
|
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("FDTFluxQueuedRequestManager::CompleteRequest() %s"), *RequestId.ToString());
|
UE_LOG(logDTFluxNetwork, Error,
|
||||||
TSharedPtr<FDTFluxTrackedRequest> Request;
|
TEXT("Marked request %s as error: Type=%d, ContestId=%d, StageId=%d, SplitId=%d"),
|
||||||
{
|
*Request.RequestId.ToString(), (int32)Request.RequestType, Request.ContestId, Request.StageId,
|
||||||
FScopeLock Lock(&RequestsLock);
|
Request.SplitId);
|
||||||
if (TSharedPtr<FDTFluxTrackedRequest>* RequestPtr = AllRequests.Find(RequestId))
|
|
||||||
{
|
|
||||||
Request = *RequestPtr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!Request.IsValid())
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxNetwork, Warning, TEXT("Request %s not found"), *RequestId.ToString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store RawResponse
|
|
||||||
Request->SetRawResponse(RawResponseData);
|
|
||||||
Request->CompletedAt = FDateTime::Now();
|
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("Request %s completed at %s"), *RequestId.ToString(),
|
|
||||||
*Request->CompletedAt.ToString());
|
|
||||||
// Decide to parse based upon config
|
|
||||||
bool bHasCallbacks = false;
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&CallbacksLock);
|
|
||||||
bHasCallbacks = SuccessCallbacks.Contains(RequestId) || ErrorCallbacks.Contains(RequestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bHasCallbacks && bUseAsyncParsing && !RawResponseData.IsEmpty())
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxNetwork, Log,
|
|
||||||
TEXT("Request %s [bHasCallbacks=%s], [bUseAsyncParsing=%s], [bIsRawResponseEmpty=%s]"),
|
|
||||||
*RequestId.ToString(),
|
|
||||||
bHasCallbacks ? TEXT("true") : TEXT("false"), bUseAsyncParsing ? TEXT("true") : TEXT("false"),
|
|
||||||
RawResponseData.IsEmpty() ? TEXT("true") : TEXT("false"));
|
|
||||||
|
|
||||||
// Async parsing for Cb
|
|
||||||
FOnParsingCompleted OnCompleted = FOnParsingCompleted::CreateRaw(
|
|
||||||
this, &FDTFluxQueuedRequestManager::OnParsingCompleted
|
|
||||||
);
|
|
||||||
|
|
||||||
FOnParsingFailed OnFailed = FOnParsingFailed::CreateRaw(
|
|
||||||
this, &FDTFluxQueuedRequestManager::OnParsingFailed
|
|
||||||
);
|
|
||||||
// Maybe send to parser in another place
|
|
||||||
AsyncParser->ParseResponseAsync(RequestId, RawResponseData, OnCompleted, OnFailed);
|
|
||||||
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Started async parsing for request %s"), *RequestId.ToString());
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxNetwork, Warning, TEXT("request %s completed without sync"), *RequestId.ToString());
|
TempQueue.Enqueue(Request);
|
||||||
// Compléter immédiatement sans parsing ou avec parsing sync
|
|
||||||
EDTFluxRequestState NewState = EDTFluxRequestState::Completed;
|
|
||||||
ChangeRequestState(Request, NewState);
|
|
||||||
// Déclencher les callbacks avec les données brutes
|
|
||||||
TriggerCallbacks(*Request);
|
|
||||||
CleanupCallbacks(RequestId);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
while (TempQueue.Dequeue(Request))
|
||||||
/**
|
|
||||||
* @todo Check protocol errors ???
|
|
||||||
* @param RequestId
|
|
||||||
* @param ErrorMessage
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
bool FDTFluxQueuedRequestManager::FailRequest(const FGuid& RequestId, const FString& ErrorMessage)
|
|
||||||
{
|
{
|
||||||
TSharedPtr<FDTFluxTrackedRequest> Request;
|
PendingRequestsQueue.Enqueue(Request);
|
||||||
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
if (TSharedPtr<FDTFluxTrackedRequest>* RequestPtr = AllRequests.Find(RequestId))
|
|
||||||
{
|
|
||||||
Request = *RequestPtr;
|
|
||||||
}
|
}
|
||||||
}
|
if (bFoundMatch)
|
||||||
|
|
||||||
if (!Request.IsValid())
|
|
||||||
{
|
{
|
||||||
return false;
|
UE_LOG(logDTFluxNetwork, Error, TEXT("No Request Found with GUID %s"), *TargetRequestGuid.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Request->LastErrorMessage = ErrorMessage;
|
|
||||||
ChangeRequestState(Request, EDTFluxRequestState::Failed);
|
|
||||||
|
|
||||||
TriggerCallbacks(*Request);
|
|
||||||
CleanupCallbacks(RequestId);
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed request %s: %s"), *RequestId.ToString(), *ErrorMessage);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxQueuedRequestManager::RetryRequest(const FGuid& RequestId)
|
bool UDTFluxQueuedManager::MarkRequestAsResponded(const FGuid& TargetRequestGuid)
|
||||||
{
|
{
|
||||||
TSharedPtr<FDTFluxTrackedRequest> Request;
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TempQueue;
|
||||||
|
bool bFoundMatch = false;
|
||||||
|
|
||||||
|
// Parcourir toutes les requêtes en attente
|
||||||
|
FDTFluxQueuedRequest Request;
|
||||||
|
while (PendingRequestsQueue.Dequeue(Request))
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&RequestsLock);
|
if (!bFoundMatch && Request.RequestId == TargetRequestGuid)
|
||||||
if (TSharedPtr<FDTFluxTrackedRequest>* RequestPtr = AllRequests.Find(RequestId))
|
|
||||||
{
|
{
|
||||||
Request = *RequestPtr;
|
// Marquer comme ayant reçu une réponse
|
||||||
|
Request.bHasReceivedResponse = true;
|
||||||
|
bFoundMatch = true;
|
||||||
|
|
||||||
|
// Ajouter à la queue des requêtes terminées
|
||||||
|
CompletedRequestsQueue.Enqueue(Request);
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxNetwork, Verbose,
|
||||||
|
TEXT("Marked request %s as responded: Type=%d, ContestId=%d, StageId=%d, SplitId=%d"),
|
||||||
|
*Request.RequestId.ToString(), (int32)Request.RequestType, Request.ContestId, Request.StageId,
|
||||||
|
Request.SplitId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Remettre dans la queue temporaire
|
||||||
|
TempQueue.Enqueue(Request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Request.IsValid() || !Request->CanRetry())
|
// Remettre les requêtes non traitées dans la queue principale
|
||||||
|
while (TempQueue.Dequeue(Request))
|
||||||
{
|
{
|
||||||
return false;
|
PendingRequestsQueue.Enqueue(Request);
|
||||||
}
|
}
|
||||||
|
|
||||||
Request->CurrentRetries++;
|
return bFoundMatch;
|
||||||
Request->LastAttemptTime = FDateTime::Now();
|
|
||||||
ChangeRequestState(Request, EDTFluxRequestState::Retrying);
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("Retrying request %s (attempt %d/%d)"),
|
|
||||||
*RequestId.ToString(), Request->CurrentRetries, Request->Config.MaxRetries);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxQueuedRequestManager::FindPendingRequest(
|
bool UDTFluxQueuedManager::MarkRequestAsResponded(const FDTFluxQueuedRequest& TargetRequest)
|
||||||
FGuid& OutRequestId,
|
{
|
||||||
EDTFluxApiDataType RequestType,
|
return MarkRequestAsResponded(TargetRequest.RequestId);
|
||||||
int32 ContestId,
|
}
|
||||||
|
|
||||||
|
bool UDTFluxQueuedManager::IsRequestPending(FGuid& OutRequestId, EDTFluxApiDataType RequestType, int32 ContestId,
|
||||||
int32 StageId,
|
int32 StageId,
|
||||||
int32 SplitId) const
|
int32 SplitId)
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&RequestsLock);
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TempQueue;
|
||||||
|
bool bFoundMatch = false;
|
||||||
|
|
||||||
for (const auto& [RequestId, Request] : AllRequests)
|
// Parcourir toutes les requêtes en attente
|
||||||
|
FDTFluxQueuedRequest Request;
|
||||||
|
while (PendingRequestsQueue.Dequeue(Request))
|
||||||
{
|
{
|
||||||
if ((Request->State == EDTFluxRequestState::Pending || Request->State == EDTFluxRequestState::Sent) &&
|
// Vérifier si cette requête correspond
|
||||||
Request->Matches(RequestType, ContestId, StageId, SplitId))
|
if (!bFoundMatch && Request.Matches(RequestType, ContestId, StageId, SplitId))
|
||||||
{
|
{
|
||||||
OutRequestId = RequestId;
|
bFoundMatch = true;
|
||||||
return true;
|
OutRequestId = Request.RequestId;
|
||||||
}
|
UE_LOG(logDTFluxNetwork, Verbose,
|
||||||
|
TEXT("Found pending request %s: Type=%d, ContestId=%d, StageId=%d, SplitId=%d"),
|
||||||
|
*Request.RequestId.ToString(), (int32)Request.RequestType, Request.ContestId, Request.StageId,
|
||||||
|
Request.SplitId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// Remettre dans la queue temporaire
|
||||||
|
TempQueue.Enqueue(Request);
|
||||||
}
|
}
|
||||||
|
|
||||||
// === ACCESSEURS ===
|
// Remettre toutes les requêtes dans la queue principale
|
||||||
|
while (TempQueue.Dequeue(Request))
|
||||||
bool FDTFluxQueuedRequestManager::GetRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const
|
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&RequestsLock);
|
PendingRequestsQueue.Enqueue(Request);
|
||||||
|
|
||||||
if (const TSharedPtr<FDTFluxTrackedRequest>* RequestPtr = AllRequests.Find(RequestId))
|
|
||||||
{
|
|
||||||
OutRequest = **RequestPtr;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return bFoundMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FDTFluxTrackedRequest* FDTFluxQueuedRequestManager::GetRequestPtr(const FGuid& RequestId) const
|
FDTFluxQueuedRequest* UDTFluxQueuedManager::GetRequestPending(EDTFluxRequestType RequestType, int32 ContestId,
|
||||||
|
int32 StageId, int32 SplitId)
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&RequestsLock);
|
auto SearchInQueue = [&RequestType, ContestId, StageId, SplitId](
|
||||||
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc>& Queue) -> FDTFluxQueuedRequest*
|
||||||
|
{
|
||||||
|
// Copie temporaire de la queue pour la recherche
|
||||||
|
TQueue<FDTFluxQueuedRequest> TempQueue;
|
||||||
|
|
||||||
if (const TSharedPtr<FDTFluxTrackedRequest>* RequestPtr = AllRequests.Find(RequestId))
|
FDTFluxQueuedRequest* FoundItem = nullptr;
|
||||||
|
FDTFluxQueuedRequest Item;
|
||||||
|
while (Queue.Dequeue(Item))
|
||||||
{
|
{
|
||||||
return RequestPtr->Get();
|
if (Item.RequestType == RequestType && Item.ContestId == ContestId && Item.StageId == StageId && Item.
|
||||||
|
SplitId == SplitId) // Assuming RequestId is your GUID field
|
||||||
|
{
|
||||||
|
FoundItem = &Item;
|
||||||
}
|
}
|
||||||
|
// Remettre dans la queue temporaire
|
||||||
|
TempQueue.Enqueue(Item);
|
||||||
|
}
|
||||||
|
while (TempQueue.Dequeue(Item))
|
||||||
|
{
|
||||||
|
Queue.Enqueue(Item);
|
||||||
|
}
|
||||||
|
return FoundItem;
|
||||||
|
};
|
||||||
|
return SearchInQueue(PendingRequestsQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
const FDTFluxQueuedRequest* UDTFluxQueuedManager::GetRequest(const FGuid& SearchedGuid)
|
||||||
|
{
|
||||||
|
auto SearchInQueue = [&SearchedGuid](TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc>& Queue) -> FDTFluxQueuedRequest*
|
||||||
|
{
|
||||||
|
// Copie temporaire de la queue pour la recherche
|
||||||
|
TQueue<FDTFluxQueuedRequest> TempQueue;
|
||||||
|
|
||||||
|
FDTFluxQueuedRequest* FoundItem = nullptr;
|
||||||
|
FDTFluxQueuedRequest Item;
|
||||||
|
while (Queue.Dequeue(Item))
|
||||||
|
{
|
||||||
|
if (Item.RequestId == SearchedGuid) // Assuming RequestId is your GUID field
|
||||||
|
{
|
||||||
|
// Trouver l'élément dans la queue originale
|
||||||
|
// On doit refaire une copie car on ne peut pas retourner l'adresse de 'Item'
|
||||||
|
FoundItem = &Item;
|
||||||
|
}
|
||||||
|
// Remettre dans la queue temporaire
|
||||||
|
TempQueue.Enqueue(Item);
|
||||||
|
}
|
||||||
|
while (TempQueue.Dequeue(Item))
|
||||||
|
{
|
||||||
|
Queue.Enqueue(Item);
|
||||||
|
}
|
||||||
|
return FoundItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Chercher dans chaque queue
|
||||||
|
if (FDTFluxQueuedRequest* Found = SearchInQueue(PendingRequestsQueue))
|
||||||
|
return Found;
|
||||||
|
|
||||||
|
if (const FDTFluxQueuedRequest* Found = SearchInQueue(CompletedRequestsQueue))
|
||||||
|
return Found;
|
||||||
|
|
||||||
|
if (const FDTFluxQueuedRequest* Found = SearchInQueue(TimedOutRequestsQueue))
|
||||||
|
return Found;
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<FDTFluxTrackedRequest> FDTFluxQueuedRequestManager::GetRequestsByState(EDTFluxRequestState State) const
|
int32 UDTFluxQueuedManager::GetPendingRequestCount()
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&RequestsLock);
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TempQueue;
|
||||||
|
|
||||||
TArray<FDTFluxTrackedRequest> Results;
|
|
||||||
|
|
||||||
for (const auto& [RequestId, Request] : AllRequests)
|
|
||||||
{
|
|
||||||
if (Request->State == State)
|
|
||||||
{
|
|
||||||
Results.Add(*Request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Results;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 FDTFluxQueuedRequestManager::GetRequestCount(EDTFluxRequestState State) const
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
|
|
||||||
int32 Count = 0;
|
int32 Count = 0;
|
||||||
|
|
||||||
for (const auto& [RequestId, Request] : AllRequests)
|
// Compter les requêtes en attente
|
||||||
{
|
FDTFluxQueuedRequest Request;
|
||||||
if (Request->State == State)
|
while (PendingRequestsQueue.Dequeue(Request))
|
||||||
{
|
{
|
||||||
Count++;
|
Count++;
|
||||||
|
TempQueue.Enqueue(Request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remettre toutes les requêtes dans la queue principale
|
||||||
|
while (TempQueue.Dequeue(Request))
|
||||||
|
{
|
||||||
|
PendingRequestsQueue.Enqueue(Request);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Count;
|
return Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
FDTFluxQueuedRequestManager::FRequestStatistics FDTFluxQueuedRequestManager::GetStatistics() const
|
int32 UDTFluxQueuedManager::CleanupTimedOutRequests()
|
||||||
{
|
{
|
||||||
FScopeLock RequestsLock_Local(&RequestsLock);
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TempQueue;
|
||||||
FScopeLock MetricsLock_Local(&MetricsLock);
|
int32 TimeoutCount = 0;
|
||||||
|
|
||||||
FRequestStatistics Stats;
|
// Parcourir toutes les requêtes en attente
|
||||||
|
FDTFluxQueuedRequest Request;
|
||||||
for (const auto& [RequestId, Request] : AllRequests)
|
while (PendingRequestsQueue.Dequeue(Request))
|
||||||
{
|
{
|
||||||
switch (Request->State)
|
if (Request.HasTimedOut())
|
||||||
{
|
{
|
||||||
case EDTFluxRequestState::Pending:
|
// Ajouter à la queue des requêtes expirées
|
||||||
case EDTFluxRequestState::Sent:
|
TimedOutRequestsQueue.Enqueue(Request);
|
||||||
case EDTFluxRequestState::Retrying:
|
TimeoutCount++;
|
||||||
Stats.Pending++;
|
|
||||||
break;
|
|
||||||
case EDTFluxRequestState::Completed:
|
|
||||||
Stats.Completed++;
|
|
||||||
break;
|
|
||||||
case EDTFluxRequestState::Failed:
|
|
||||||
case EDTFluxRequestState::TimedOut:
|
|
||||||
Stats.Failed++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Stats.TotalRequests = TotalRequests;
|
UE_LOG(logDTFluxNetwork, Warning,
|
||||||
return Stats;
|
TEXT("Request %s timed out: Type=%d, ContestId=%d, StageId=%d, SplitId=%d"),
|
||||||
}
|
*Request.RequestId.ToString(), (int32)Request.RequestType, Request.ContestId, Request.StageId,
|
||||||
|
Request.SplitId);
|
||||||
// === NETTOYAGE ===
|
|
||||||
int32 FDTFluxQueuedRequestManager::CleanupCompletedRequests(float OlderThanSeconds)
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
|
|
||||||
TArray<FGuid> OldRequests;
|
|
||||||
const FDateTime Threshold = FDateTime::Now() - FTimespan::FromSeconds(OlderThanSeconds);
|
|
||||||
|
|
||||||
for (const auto& [RequestId, Request] : AllRequests)
|
|
||||||
{
|
|
||||||
if ((Request->State == EDTFluxRequestState::Completed || Request->State == EDTFluxRequestState::Failed ||
|
|
||||||
Request->State == EDTFluxRequestState::TimedOut) && Request->CompletedAt < Threshold)
|
|
||||||
{
|
|
||||||
OldRequests.Add(RequestId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const FGuid& RequestId : OldRequests)
|
|
||||||
{
|
|
||||||
AllRequests.Remove(RequestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return OldRequests.Num();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::ClearAllRequests()
|
|
||||||
{
|
|
||||||
FScopeLock RequestsLock_Local(&RequestsLock);
|
|
||||||
FScopeLock CallbacksLock_Local(&CallbacksLock);
|
|
||||||
|
|
||||||
AllRequests.Empty();
|
|
||||||
SuccessCallbacks.Empty();
|
|
||||||
ErrorCallbacks.Empty();
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("Cleared all requests"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::Tick(float DeltaTime)
|
|
||||||
{
|
|
||||||
if (!bIsInitialized.load())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Mise à jour des timers
|
|
||||||
TimeSinceLastTimeoutCheck += DeltaTime;
|
|
||||||
TimeSinceLastRetryCheck += DeltaTime;
|
|
||||||
|
|
||||||
// Vérifier les timeouts
|
|
||||||
if (TimeSinceLastTimeoutCheck >= TimeoutCheckInterval)
|
|
||||||
{
|
|
||||||
ProcessTimeouts();
|
|
||||||
TimeSinceLastTimeoutCheck = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérifier les retries
|
|
||||||
if (TimeSinceLastRetryCheck >= RetryCheckInterval)
|
|
||||||
{
|
|
||||||
ProcessRetries();
|
|
||||||
TimeSinceLastRetryCheck = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::ChangeRequestState(TSharedPtr<FDTFluxTrackedRequest> Request,
|
|
||||||
EDTFluxRequestState NewState)
|
|
||||||
{
|
|
||||||
if (!Request.IsValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const EDTFluxRequestState OldState = Request->State;
|
|
||||||
Request->State = NewState;
|
|
||||||
|
|
||||||
// Déclencher l'événement de changement d'état
|
|
||||||
OnRequestStateChanged.Broadcast(Request->RequestId, NewState);
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, VeryVerbose, TEXT("DTFluxQueuedRequestManager: Request %s state changed from %s to %s"),
|
|
||||||
*Request->RequestId.ToString(),
|
|
||||||
*UEnum::GetValueAsString(OldState),
|
|
||||||
*UEnum::GetValueAsString(NewState));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::ProcessTimeouts()
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
|
|
||||||
TArray<TSharedPtr<FDTFluxTrackedRequest>> TimedOutRequests;
|
|
||||||
|
|
||||||
for (const auto& Pair : AllRequests)
|
|
||||||
{
|
|
||||||
const auto& Request = Pair.Value;
|
|
||||||
if (Request->HasTimedOut())
|
|
||||||
{
|
|
||||||
TimedOutRequests.Add(Request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& Request : TimedOutRequests)
|
|
||||||
{
|
|
||||||
Request->LastErrorMessage = FString::Printf(
|
|
||||||
TEXT("Request timed out after %.1f seconds"), Request->Config.TimeoutSeconds);
|
|
||||||
|
|
||||||
if (Request->CanRetry())
|
|
||||||
{
|
|
||||||
Request->CurrentRetries++;
|
|
||||||
ChangeRequestState(Request, EDTFluxRequestState::Retrying);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ChangeRequestState(Request, EDTFluxRequestState::TimedOut);
|
// Remettre dans la queue temporaire
|
||||||
TriggerCallbacks(*Request);
|
TempQueue.Enqueue(Request);
|
||||||
OnRequestFailed.Broadcast(*Request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::ProcessRetries()
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
|
|
||||||
const FDateTime Now = FDateTime::Now();
|
|
||||||
TArray<TSharedPtr<FDTFluxTrackedRequest>> ReadyToRetry;
|
|
||||||
|
|
||||||
for (const auto& Pair : AllRequests)
|
|
||||||
{
|
|
||||||
const auto& Request = Pair.Value;
|
|
||||||
if (Request->State == EDTFluxRequestState::Retrying)
|
|
||||||
{
|
|
||||||
const float ElapsedSinceLastAttempt = (Now - Request->LastAttemptTime).GetTotalSeconds();
|
|
||||||
if (ElapsedSinceLastAttempt >= Request->GetRetryDelay())
|
|
||||||
{
|
|
||||||
ReadyToRetry.Add(Request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& Request : ReadyToRetry)
|
|
||||||
{
|
|
||||||
Request->LastAttemptTime = Now;
|
|
||||||
ChangeRequestState(Request, EDTFluxRequestState::Pending);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::TriggerCallbacks(const FDTFluxTrackedRequest& Request)
|
// Remettre les requêtes non expirées dans la queue principale
|
||||||
|
while (TempQueue.Dequeue(Request))
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&CallbacksLock);
|
PendingRequestsQueue.Enqueue(Request);
|
||||||
|
|
||||||
if (Request.State == EDTFluxRequestState::Completed)
|
|
||||||
{
|
|
||||||
// Success Cb
|
|
||||||
const FOnDTFluxRequestSuccess* SuccessCallback = SuccessCallbacks.Find(Request.RequestId);
|
|
||||||
if (SuccessCallback && SuccessCallback->IsBound())
|
|
||||||
{
|
|
||||||
SuccessCallback->Execute(Request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (Request.State == EDTFluxRequestState::Failed || Request.State == EDTFluxRequestState::TimedOut)
|
|
||||||
{
|
|
||||||
// Error Cb
|
|
||||||
const FOnDTFluxRequestError* ErrorCallback = ErrorCallbacks.Find(Request.RequestId);
|
|
||||||
if (ErrorCallback && ErrorCallback->IsBound())
|
|
||||||
{
|
|
||||||
ErrorCallback->Execute(Request, Request.LastErrorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::CleanupCallbacks(const FGuid& RequestId)
|
return TimeoutCount;
|
||||||
{
|
|
||||||
FScopeLock Lock(&CallbacksLock);
|
|
||||||
SuccessCallbacks.Remove(RequestId);
|
|
||||||
ErrorCallbacks.Remove(RequestId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::OnParsingCompleted(const FGuid& RequestId,
|
int32 UDTFluxQueuedManager::CleanCashedRequests()
|
||||||
TSharedPtr<FDTFluxServerResponse> ParsedResponse, bool bSuccess)
|
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("FDTFluxQueuedRequestManager::OnParsingCompleted() request %s"),
|
int32 CleanedRequestsCount = 0;
|
||||||
*RequestId.ToString())
|
|
||||||
TSharedPtr<FDTFluxTrackedRequest> Request;
|
// Queue temporaire pour stocker les requêtes encore valides
|
||||||
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> ValidCompletedRequests;
|
||||||
|
|
||||||
|
// Traiter toutes les requêtes terminées
|
||||||
|
FDTFluxQueuedRequest CompletedRequest;
|
||||||
|
while (CompletedRequestsQueue.Dequeue(CompletedRequest))
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&RequestsLock);
|
// Vérifier si la requête est cacheable et a reçu une réponse
|
||||||
|
if (CompletedRequest.bIsCacheable && CompletedRequest.bHasReceivedResponse)
|
||||||
auto* RequestPtr = AllRequests.Find(RequestId);
|
|
||||||
if (!RequestPtr || !RequestPtr->IsValid())
|
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxNetwork, Error,
|
// Calculer l'âge de la requête en secondes
|
||||||
TEXT(
|
float RequestAge = (FDateTime::Now() - CompletedRequest.CreatedAt).GetTotalSeconds();
|
||||||
"DTFluxQueuedRequestManager::OnParsingCompleted() RequestId%s [InvalidRequestId=%s], [RequestPtrValid=%s]"
|
|
||||||
),
|
|
||||||
*RequestId.ToString(), RequestPtr ? TEXT("true") : TEXT("false"),
|
|
||||||
RequestPtr->IsValid() ? TEXT("true") : TEXT("false"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Request = *RequestPtr;
|
// Vérifier si le cache est encore valide
|
||||||
}
|
if (RequestAge <= CompletedRequest.CachedValidity)
|
||||||
|
|
||||||
if (bSuccess && ParsedResponse.IsValid())
|
|
||||||
{
|
{
|
||||||
Request->ParsedResponse = ParsedResponse;
|
// Le cache est encore valide, conserver la requête
|
||||||
Request->bIsResponseParsed = true;
|
ValidCompletedRequests.Enqueue(CompletedRequest);
|
||||||
EDTFluxRequestState NewState = EDTFluxRequestState::Completed;
|
|
||||||
|
|
||||||
ChangeRequestState(Request, NewState);
|
|
||||||
UE_LOG(logDTFluxNetwork, Log,
|
|
||||||
TEXT("DTFluxQueuedRequestManager: Async parsing completed for request %s"),
|
|
||||||
*RequestId.ToString());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Request->LastErrorMessage = TEXT("Async parsing failed");
|
// Le cache a expiré, compter cette requête comme nettoyée
|
||||||
ChangeRequestState(Request, EDTFluxRequestState::Failed);
|
CleanedRequestsCount++;
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Error, TEXT("Async parsing failed for request %s"), *RequestId.ToString());
|
UE_LOG(LogTemp, Verbose,
|
||||||
|
TEXT("DTFluxQueuedManager: Cleaned expired cached request %s (Age: %.2fs, Validity: %.2fs)"),
|
||||||
|
*CompletedRequest.RequestId.ToString(), RequestAge, CompletedRequest.CachedValidity);
|
||||||
}
|
}
|
||||||
TriggerCallbacks(*Request);
|
|
||||||
CleanupCallbacks(RequestId);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
void FDTFluxQueuedRequestManager::OnParsingFailed(const FGuid& RequestId, const FString& ErrorMessage)
|
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxNetwork, Error, TEXT("DTFluxQueuedRequestManager: Async parsing failed for request %s: %s"),
|
// Requête non cacheable ou sans réponse, la conserver
|
||||||
*RequestId.ToString(),
|
ValidCompletedRequests.Enqueue(CompletedRequest);
|
||||||
*ErrorMessage);
|
}
|
||||||
FailRequest(RequestId, FString::Printf(TEXT("Parsing failed: %s"), *ErrorMessage));
|
}
|
||||||
|
|
||||||
|
// Restaurer la queue avec uniquement les requêtes valides
|
||||||
|
while (ValidCompletedRequests.Dequeue(CompletedRequest))
|
||||||
|
{
|
||||||
|
CompletedRequestsQueue.Enqueue(CompletedRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log du résultat si des requêtes ont été nettoyées
|
||||||
|
if (CleanedRequestsCount > 0)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxQueuedManager: Cleaned %d expired cached requests"), CleanedRequestsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CleanedRequestsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxQueuedManager::ClearAllRequests()
|
||||||
|
{
|
||||||
|
// Vider toutes les queues
|
||||||
|
FDTFluxQueuedRequest DummyRequest;
|
||||||
|
while (PendingRequestsQueue.Dequeue(DummyRequest))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
while (CompletedRequestsQueue.Dequeue(DummyRequest))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
while (TimedOutRequestsQueue.Dequeue(DummyRequest))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Cleared all pending requests"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UDTFluxQueuedManager::Tick(float DeltaTime)
|
||||||
|
{
|
||||||
|
if (!bIsInitialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Incrémenter le temps écoulé
|
||||||
|
TimeSinceLastCheck += DeltaTime;
|
||||||
|
|
||||||
|
// Vérifier si c'est le moment de nettoyer les requêtes expirées
|
||||||
|
if (TimeSinceLastCheck >= CheckInterval)
|
||||||
|
{
|
||||||
|
TimeSinceLastCheck = 0.0f;
|
||||||
|
CleanupTimedOutRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traiter les requêtes expirées
|
||||||
|
FDTFluxQueuedRequest TimedOutRequest;
|
||||||
|
while (TimedOutRequestsQueue.Dequeue(TimedOutRequest))
|
||||||
|
{
|
||||||
|
// Déclencher l'événement pour chaque requête expirée
|
||||||
|
OnRequestTimedOut.Broadcast(TimedOutRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxQueuedManager::IsTickable() const
|
||||||
|
{
|
||||||
|
return bIsInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
TStatId UDTFluxQueuedManager::GetStatId() const
|
||||||
|
{
|
||||||
|
RETURN_QUICK_DECLARE_CYCLE_STAT(UDTFluxQueuedManager, STATGROUP_Tickables);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
#include "Struct/DTFluxServerResponseStruct.h"
|
#pragma once
|
||||||
#include "DTFluxNetworkModule.h"
|
|
||||||
#include "Types/Objects/UDTFluxParticipantFactory.h"
|
|
||||||
|
|
||||||
|
#include "Struct/DTFluxServerResponseStruct.h"
|
||||||
|
|
||||||
// === IMPLÉMENTATION DES CONSTRUCTEURS ===
|
// === IMPLÉMENTATION DES CONSTRUCTEURS ===
|
||||||
|
|
||||||
@ -195,7 +194,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::ParseTeamList(FDTFluxTeamListDefinition& OutTeamList)
|
bool FDTFluxServerResponse::ParseTeamListResponse(FDTFluxTeamListDefinition& OutTeamList)
|
||||||
{
|
{
|
||||||
ParsingStatus = EDTFluxResponseStatus::Unset;
|
ParsingStatus = EDTFluxResponseStatus::Unset;
|
||||||
if (!ValidateResponseType(TEXT("team-list")))
|
if (!ValidateResponseType(TEXT("team-list")))
|
||||||
@ -257,9 +256,9 @@ bool FDTFluxServerResponse::ParseTeamList(FDTFluxTeamListDefinition& OutTeamList
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseTeamUpdate(FDTFluxTeamListDefinition& OutTeamUpdate)
|
bool FDTFluxServerResponse::ParseTeamUpdateResponse(FDTFluxTeamListDefinition& OutTeamUpdate)
|
||||||
{
|
{
|
||||||
return ParseTeamList(OutTeamUpdate);
|
return ParseTeamListResponse(OutTeamUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseRaceData(FDTFluxRaceData& OutRaceData)
|
bool FDTFluxServerResponse::ParseRaceData(FDTFluxRaceData& OutRaceData)
|
||||||
@ -363,13 +362,13 @@ bool FDTFluxServerResponse::ParseContestRanking(FDTFluxContestRankings& OutConte
|
|||||||
OutContestRankings.Rankings.Add(Ranking);
|
OutContestRankings.Rankings.Add(Ranking);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UE_LOG(logDTFluxNetwork, Log, TEXT("Successfully parsed contest ranking for Contest %d with %d entries"),
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Successfully parsed contest ranking for Contest %d with %d entries"),
|
||||||
// OutContestRankings.ContestId, OutContestRankings.Rankings.Num());
|
OutContestRankings.ContestId, OutContestRankings.Rankings.Num());
|
||||||
ParsingStatus = EDTFluxResponseStatus::Success;
|
ParsingStatus = EDTFluxResponseStatus::Success;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseStageRanking(FDTFluxStageRankings& OutStageRankings)
|
bool FDTFluxServerResponse::ParseStageRankingResponse(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")))
|
||||||
@ -397,7 +396,7 @@ bool FDTFluxServerResponse::ParseStageRanking(FDTFluxStageRankings& OutStageRank
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseSplitRanking(FDTFluxSplitRankings& OutSplitRankings)
|
bool FDTFluxServerResponse::ParseSplitRankingResponse(FDTFluxSplitRankings& OutSplitRankings)
|
||||||
{
|
{
|
||||||
if (!ValidateResponseType(TEXT("stage-ranking")) || SplitID == -1)
|
if (!ValidateResponseType(TEXT("stage-ranking")) || SplitID == -1)
|
||||||
{
|
{
|
||||||
@ -428,7 +427,7 @@ bool FDTFluxServerResponse::ParseSplitRanking(FDTFluxSplitRankings& OutSplitRank
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseStatusUpdate(FDTFluxTeamStatusUpdate& OutStatusUpdate)
|
bool FDTFluxServerResponse::ParseStatusUpdateResponse(FDTFluxTeamStatusUpdate& OutStatusUpdate)
|
||||||
{
|
{
|
||||||
if (!ValidateResponseType(TEXT("status-update")))
|
if (!ValidateResponseType(TEXT("status-update")))
|
||||||
{
|
{
|
||||||
@ -449,7 +448,7 @@ bool FDTFluxServerResponse::ParseStatusUpdate(FDTFluxTeamStatusUpdate& OutStatus
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxServerResponse::ParseSplitSensor(TArray<FDTFluxSplitSensorInfo>& OutSplitSensorInfos)
|
bool FDTFluxServerResponse::ParseSplitSensorResponse(TArray<FDTFluxSplitSensorInfo>& OutSplitSensorInfos)
|
||||||
{
|
{
|
||||||
if (!ValidateResponseType(TEXT("split-sensor")))
|
if (!ValidateResponseType(TEXT("split-sensor")))
|
||||||
{
|
{
|
||||||
@ -475,14 +474,12 @@ bool FDTFluxServerResponse::ParseSplitSensor(TArray<FDTFluxSplitSensorInfo>& Out
|
|||||||
NewSplitSensorInfo.StageId = SplitSensorInfoResponse.StageID;
|
NewSplitSensorInfo.StageId = SplitSensorInfoResponse.StageID;
|
||||||
NewSplitSensorInfo.SplitId = SplitSensorInfoResponse.SplitID;
|
NewSplitSensorInfo.SplitId = SplitSensorInfoResponse.SplitID;
|
||||||
NewSplitSensorInfo.Time = SplitSensorInfoResponse.Time;
|
NewSplitSensorInfo.Time = SplitSensorInfoResponse.Time;
|
||||||
NewSplitSensorInfo.Gap = SplitSensorInfoResponse.Gap;
|
|
||||||
NewSplitSensorInfo.Rank = SplitSensorInfoResponse.Rank;
|
|
||||||
|
|
||||||
OutSplitSensorInfos.Add(NewSplitSensorInfo);
|
OutSplitSensorInfos.Add(NewSplitSensorInfo);
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Split sensor info for bib %d in Contest %d, Stage %d, Split %d Rank [%i] Gap [%s] Time [%s]"),
|
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Split sensor info for bib %d in Contest %d, Stage %d, Split %d"),
|
||||||
NewSplitSensorInfo.Bib, NewSplitSensorInfo.ContestId, NewSplitSensorInfo.StageId,
|
NewSplitSensorInfo.Bib, NewSplitSensorInfo.ContestId, NewSplitSensorInfo.StageId,
|
||||||
NewSplitSensorInfo.SplitId, NewSplitSensorInfo.Rank, *NewSplitSensorInfo.Gap,*NewSplitSensorInfo.Time);
|
NewSplitSensorInfo.SplitId);
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("Successfully parsed %d split sensor entries"), OutSplitSensorInfos.Num());
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Successfully parsed %d split sensor entries"), OutSplitSensorInfos.Num());
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,171 +0,0 @@
|
|||||||
// ================================================================================================
|
|
||||||
// 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,375 +1,66 @@
|
|||||||
// ================================================================================================
|
// 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 "HAL/CriticalSection.h"
|
#include "Struct/DTFluxRequestStructs.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"
|
||||||
|
|
||||||
class FDTFluxAsyncParser;
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestTimedOut, const FDTFluxQueuedRequest&, TimedOutRequest);
|
||||||
|
|
||||||
|
|
||||||
// ================================================================================================
|
|
||||||
// ENUMS ET STRUCTURES POUR LES REQUÊTES
|
|
||||||
// ================================================================================================
|
|
||||||
|
|
||||||
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"),
|
|
||||||
Retrying UMETA(DisplayName = "Retrying")
|
|
||||||
};
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
|
||||||
struct DTFLUXNETWORK_API FDTFluxRequestConfig
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
|
||||||
float TimeoutSeconds = 5.0f;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
|
||||||
int32 MaxRetries = 3;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
|
||||||
float RetryBackoffMultiplier = 1.5f;
|
|
||||||
};
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
|
||||||
struct DTFLUXNETWORK_API FDTFluxTrackedRequest
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
float GetRetryDelay() const;
|
|
||||||
bool Matches(EDTFluxApiDataType InType, int32 InContestId = -1, int32 InStageId = -1, int32 InSplitId = -1) 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 timeout, retry et parsing asynchrone
|
* @brief Classe Tickable gérant les requêtes WebSockets qui ne sont pas traçables nativement par l'API.
|
||||||
* Implémentation C++ pure avec SmartPointers pour des performances optimales
|
* Cette classe utilise TQueue pour gérer efficacement les requêtes en attente et vérifie leur état dans le tick.
|
||||||
*/
|
*/
|
||||||
class DTFLUXNETWORK_API FDTFluxQueuedRequestManager : public FTickableGameObject
|
UCLASS()
|
||||||
|
class DTFLUXNETWORK_API UDTFluxQueuedManager : public UObject, public FTickableGameObject
|
||||||
{
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FDTFluxQueuedRequestManager();
|
/** Constructeur par défaut */
|
||||||
virtual ~FDTFluxQueuedRequestManager();
|
UDTFluxQueuedManager();
|
||||||
|
virtual ~UDTFluxQueuedManager() override;
|
||||||
// === LIFECYCLE ===
|
void Initialize();
|
||||||
|
FGuid QueueRequest(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1,
|
||||||
/**
|
const FString& RawMessage = "");
|
||||||
* Initialiser le gestionnaire de requêtes
|
bool MarkRequestAsError(const FGuid& TargetRequestGuid);
|
||||||
* @param DefaultConfig Configuration par défaut pour les nouvelles requêtes
|
bool MarkRequestAsResponded(const FGuid& TargetRequestGuid);
|
||||||
*/
|
bool MarkRequestAsResponded(const FDTFluxQueuedRequest& TargetRequest);
|
||||||
void Initialize(const FDTFluxRequestConfig& DefaultConfig = FDTFluxRequestConfig());
|
bool IsRequestPending(FGuid& OutRequestId, EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
||||||
|
int32 SplitId = -1);
|
||||||
/**
|
FDTFluxQueuedRequest* GetRequestPending(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
||||||
* Arrêter le gestionnaire et nettoyer toutes les ressources
|
int32 SplitId = -1);
|
||||||
*/
|
const FDTFluxQueuedRequest* GetRequest(const FGuid& SearchedGuid);
|
||||||
void Shutdown();
|
int32 GetPendingRequestCount();
|
||||||
|
int32 CleanupTimedOutRequests();
|
||||||
/**
|
int32 CleanCashedRequests();
|
||||||
* 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 ===
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
// === 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 Completed = 0;
|
|
||||||
int32 Failed = 0;
|
|
||||||
int32 TotalRequests = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
FRequestStatistics GetStatistics() const;
|
|
||||||
|
|
||||||
// === NETTOYAGE ===
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
void ClearAllRequests();
|
void ClearAllRequests();
|
||||||
|
// bool TryProcessResponse(const FDTFluxServerResponse& Response);
|
||||||
|
|
||||||
// === EVENTS ===
|
// Interface FTickableGameObject
|
||||||
|
|
||||||
FOnRequestStateChangedNative OnRequestStateChanged;
|
|
||||||
FOnRequestCompletedNative OnRequestCompleted;
|
|
||||||
FOnRequestFailedNative OnRequestFailed;
|
|
||||||
|
|
||||||
// === INTERFACE TICKABLE ===
|
|
||||||
|
|
||||||
virtual void Tick(float DeltaTime) override;
|
virtual void Tick(float DeltaTime) override;
|
||||||
virtual bool IsTickable() const override { return true; };
|
virtual bool IsTickable() const override;
|
||||||
|
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) ===
|
|
||||||
const FDTFluxAsyncParser* GetAsyncParser() const { return AsyncParser.Get(); }
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Network")
|
||||||
|
FOnRequestTimedOut OnRequestTimedOut;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// === CONFIGURATION ===
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> PendingRequestsQueue;
|
||||||
FDTFluxRequestConfig DefaultConfig;
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> CompletedRequestsQueue;
|
||||||
std::atomic<bool> bIsInitialized{false};
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TimedOutRequestsQueue;
|
||||||
|
|
||||||
// === TIMING POUR LE TICK ===
|
bool bIsInitialized;
|
||||||
float TimeSinceLastTimeoutCheck = 0.0f;
|
float CheckInterval;
|
||||||
float TimeSinceLastRetryCheck = 0.0f;
|
float TimeSinceLastCheck;
|
||||||
|
|
||||||
static constexpr float TimeoutCheckInterval = 1.0f;
|
|
||||||
static constexpr float RetryCheckInterval = 0.5f;
|
|
||||||
|
|
||||||
// === STOCKAGE THREAD-SAFE ===
|
|
||||||
mutable FCriticalSection RequestsLock;
|
|
||||||
TMap<FGuid, TSharedPtr<FDTFluxTrackedRequest>> AllRequests;
|
|
||||||
|
|
||||||
// === CALLBACKS C++ ===
|
|
||||||
mutable FCriticalSection CallbacksLock;
|
|
||||||
TMap<FGuid, FOnDTFluxRequestSuccess> SuccessCallbacks;
|
|
||||||
TMap<FGuid, FOnDTFluxRequestError> ErrorCallbacks;
|
|
||||||
|
|
||||||
// === MÉTRIQUES ===
|
|
||||||
mutable FCriticalSection MetricsLock;
|
|
||||||
mutable int32 TotalRequests = 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();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
// === 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);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,11 +4,13 @@
|
|||||||
|
|
||||||
#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"
|
||||||
@ -83,14 +85,14 @@ public:
|
|||||||
|
|
||||||
EDTFluxResponseStatus TryParse(bool bLogErrors = true);
|
EDTFluxResponseStatus TryParse(bool bLogErrors = true);
|
||||||
|
|
||||||
bool ParseTeamList(FDTFluxTeamListDefinition& OutTeamList);
|
bool ParseTeamListResponse(FDTFluxTeamListDefinition& OutTeamList);
|
||||||
bool ParseTeamUpdate(FDTFluxTeamListDefinition& OutTeamUpdate);
|
bool ParseTeamUpdateResponse(FDTFluxTeamListDefinition& OutTeamUpdate);
|
||||||
bool ParseRaceData(FDTFluxRaceData& OutRaceData);
|
bool ParseRaceData(FDTFluxRaceData& OutRaceData);
|
||||||
bool ParseContestRanking(FDTFluxContestRankings& OutContestRankings);
|
bool ParseContestRanking(FDTFluxContestRankings& OutContestRankings);
|
||||||
bool ParseStageRanking(FDTFluxStageRankings& OutStageRankings);
|
bool ParseStageRankingResponse(FDTFluxStageRankings& OutStageRankings);
|
||||||
bool ParseSplitRanking(FDTFluxSplitRankings& OutSplitRankings);
|
bool ParseSplitRankingResponse(FDTFluxSplitRankings& OutSplitRankings);
|
||||||
bool ParseStatusUpdate(FDTFluxTeamStatusUpdate& OutStatusUpdate);
|
bool ParseStatusUpdateResponse(FDTFluxTeamStatusUpdate& OutStatusUpdate);
|
||||||
bool ParseSplitSensor(TArray<FDTFluxSplitSensorInfo>& OutSplitSensorInfos);
|
bool ParseSplitSensorResponse(TArray<FDTFluxSplitSensorInfo>& OutSplitSensorInfos);
|
||||||
|
|
||||||
|
|
||||||
// === MÉTHODES UTILITAIRES ===
|
// === MÉTHODES UTILITAIRES ===
|
||||||
|
|||||||
@ -14,21 +14,17 @@ struct DTFLUXNETWORK_API FDTFluxSplitSensorItemResponse
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int Bib = -1;
|
int Bib;
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Type = "split-sensor-item";
|
FString Type = "split-sensor-item";
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int ContestID =-1;
|
int ContestID;
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int StageID =-1;
|
int StageID;
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int SplitID = -1;
|
int SplitID;
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Time = "-";
|
FString Time = "-";
|
||||||
UPROPERTY()
|
|
||||||
int Rank = -1;
|
|
||||||
UPROPERTY()
|
|
||||||
FString Gap = "";
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,197 +1,209 @@
|
|||||||
// ================================================================================================
|
// 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 "DTFluxQueuedManager.h"
|
#include "Types/Struct/DTFluxRaceDataStructs.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 FDTFluxQueuedRequestManager;
|
class UDTFluxQueuedManager;
|
||||||
|
|
||||||
typedef TSharedPtr<FDTFluxWebSocketClient> FDTFluxWebSocketClientSP;
|
typedef TSharedPtr<FDTFluxWebSocketClient> FDTFluxWebSocketClientSP;
|
||||||
|
class FDTFluxHttpClient;
|
||||||
|
typedef TSharedPtr<FDTFluxHttpClient> FDTFluxHttpClientSP;
|
||||||
|
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
UCLASS(Blueprintable)
|
UCLASS(Blueprintable)
|
||||||
class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem
|
class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Network")
|
UPROPERTY()
|
||||||
EDTFluxConnectionStatus WsStatus = EDTFluxConnectionStatus::Unset;
|
EDTFluxConnectionStatus WsStatus = EDTFluxConnectionStatus::Unset;
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWebSocketConnected);
|
||||||
/**
|
|
||||||
* Se connecter au serveur WebSocket
|
|
||||||
*/
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
|
||||||
void Connect();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Se déconnecter du serveur WebSocket
|
|
||||||
*/
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
|
||||||
void Disconnect();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reconnecter au serveur WebSocket
|
|
||||||
*/
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
|
||||||
void Reconnect();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Envoyer une requête trackée avec cache, timeout et retry
|
|
||||||
* @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 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
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Envoyer une requête trackée avec callbacks C++ (non Blueprint)
|
|
||||||
* @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 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
|
|
||||||
);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
|
||||||
bool GetTrackedRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const;
|
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
|
||||||
bool HasRequestReceivedResponse(const FGuid& RequestId) const;
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
|
||||||
FString GetRequestResponseData(const FGuid& RequestId) const;
|
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
|
||||||
bool IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
|
||||||
int32 SplitId = -1) const;
|
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
|
||||||
int32 GetPendingRequestCount() const;
|
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
|
||||||
void GetRequestStatistics(int32& OutPending, int32& OutCompleted, int32& OutFailed) const;
|
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Legacy")
|
|
||||||
void SendRequest(const EDTFluxApiDataType RequestType, int InContestId = -1, int InStageId = -1,
|
|
||||||
int InSplitId = -1);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
|
||||||
void SendMessage(const FString& Message);
|
|
||||||
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Network")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Network")
|
||||||
FOnWebSocketConnected OnWebSocketConnected;
|
FOnWebSocketConnected OnWebSocketConnected;
|
||||||
|
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnRaceDataReceived, const FDTFluxRaceData& /*RaceDataDefinition*/);
|
||||||
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests")
|
|
||||||
FOnDTFluxTrackedRequestCompleted OnTrackedRequestCompleted;
|
|
||||||
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests")
|
|
||||||
FOnDTFluxTrackedRequestFailed OnTrackedRequestFailed;
|
|
||||||
|
|
||||||
FOnRaceDataReceived OnRaceDataReceived;
|
FOnRaceDataReceived OnRaceDataReceived;
|
||||||
|
|
||||||
|
FOnRaceDataReceived& OnReceivedRaceData()
|
||||||
|
{
|
||||||
|
return OnRaceDataReceived;
|
||||||
|
};
|
||||||
|
|
||||||
|
// === DELEGATES POUR LES DONNÉES REÇUES (PUSH) ===
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnTeamListReceived, const FDTFluxTeamListDefinition& /*TeamListDefinition*/);
|
||||||
FOnTeamListReceived OnTeamListReceived;
|
FOnTeamListReceived OnTeamListReceived;
|
||||||
|
|
||||||
|
FOnTeamListReceived& OnReceivedTeamList()
|
||||||
|
{
|
||||||
|
return OnTeamListReceived;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnStageRankingReceived, const FDTFluxStageRankings& /*StageRankings*/);
|
||||||
FOnStageRankingReceived OnStageRankingReceived;
|
FOnStageRankingReceived OnStageRankingReceived;
|
||||||
|
|
||||||
|
FOnStageRankingReceived& OnReceivedStageRanking()
|
||||||
|
{
|
||||||
|
return OnStageRankingReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnSplitRankingReceived, const FDTFluxSplitRankings& /*SplitRankings*/);
|
||||||
FOnSplitRankingReceived OnSplitRankingReceived;
|
FOnSplitRankingReceived OnSplitRankingReceived;
|
||||||
|
|
||||||
|
FOnSplitRankingReceived& OnReceivedSplitRanking()
|
||||||
|
{
|
||||||
|
return OnSplitRankingReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnContestRankingReceived, const FDTFluxContestRankings& /*ContestRankings*/);
|
||||||
FOnContestRankingReceived OnContestRankingReceived;
|
FOnContestRankingReceived OnContestRankingReceived;
|
||||||
|
|
||||||
|
FOnContestRankingReceived& OnReceivedContestRanking()
|
||||||
|
{
|
||||||
|
return OnContestRankingReceived;
|
||||||
|
};
|
||||||
|
|
||||||
|
// === DELEGATES POUR LES DONNÉES REÇUES (PULL) ===
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnSplitSensorReceived, const FDTFluxSplitSensorInfo& /*ContestRankings*/);
|
||||||
FOnSplitSensorReceived OnSplitSensorReceived;
|
FOnSplitSensorReceived OnSplitSensorReceived;
|
||||||
|
|
||||||
|
FOnSplitSensorReceived& OnReceivedSplitSensor()
|
||||||
|
{
|
||||||
|
return OnSplitSensorReceived;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnTeamUpdateReceived, const FDTFluxTeamListDefinition& /*ParticipantToUpdate*/);
|
||||||
FOnTeamUpdateReceived OnTeamUpdateReceived;
|
FOnTeamUpdateReceived OnTeamUpdateReceived;
|
||||||
|
|
||||||
|
FOnTeamUpdateReceived& OnReceivedTeamUpdate()
|
||||||
|
{
|
||||||
|
return OnTeamUpdateReceived;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_DELEGATE_OneParam(FOnTeamStatusUpdateReceived, const FDTFluxTeamStatusUpdate& /*TeamToUpdate*/);
|
||||||
FOnTeamStatusUpdateReceived OnTeamStatusUpdateReceived;
|
FOnTeamStatusUpdateReceived OnTeamStatusUpdateReceived;
|
||||||
|
|
||||||
FOnRaceDataReceived& OnReceivedRaceData() { return OnRaceDataReceived; }
|
FOnTeamStatusUpdateReceived& OnReceivedTeamStatusUpdate()
|
||||||
FOnTeamListReceived& OnReceivedTeamList() { return OnTeamListReceived; }
|
{
|
||||||
FOnStageRankingReceived& OnReceivedStageRanking() { return OnStageRankingReceived; }
|
return OnTeamStatusUpdateReceived;
|
||||||
FOnSplitRankingReceived& OnReceivedSplitRanking() { return OnSplitRankingReceived; }
|
};
|
||||||
FOnContestRankingReceived& OnReceivedContestRanking() { return OnContestRankingReceived; }
|
|
||||||
FOnSplitSensorReceived& OnReceivedSplitSensor() { return OnSplitSensorReceived; }
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Network")
|
||||||
FOnTeamUpdateReceived& OnReceivedTeamUpdate() { return OnTeamUpdateReceived; }
|
void Connect();
|
||||||
FOnTeamStatusUpdateReceived& OnReceivedTeamStatusUpdate() { return OnTeamStatusUpdateReceived; }
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Network")
|
||||||
|
void Disconnect();
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Network")
|
||||||
|
void Reconnect();
|
||||||
|
|
||||||
|
|
||||||
|
// === REQUÊTES AVEC QUEUE ET TRACKING ===
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
||||||
|
FGuid SendTrackedRequest(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
||||||
|
int32 SplitId = -1, float TimeoutSeconds = 30.0f);
|
||||||
|
|
||||||
TSharedPtr<FDTFluxQueuedRequestManager> GetRequestManager() const { return RequestManager; }
|
FGuid SendTrackedRequestWithCallback(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId, int32 SplitId,
|
||||||
|
FOnDTFluxTrackedRequestResponse OnCompleted,
|
||||||
|
FOnDTFluxTrackedRequestTimeout OnTimeout,
|
||||||
|
TOptional<FOnDTFluxRequestResponseError> OnError = TOptional<
|
||||||
|
FOnDTFluxRequestResponseError>(),
|
||||||
|
float TimeoutSeconds = 30.0f);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
||||||
|
bool GetTrackedRequest(const FGuid& RequestId, FDTFluxQueuedRequest& OutRequest) const;
|
||||||
|
const FDTFluxQueuedRequest* GetTrackedRequestPtr(const FGuid& RequestId) const;
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests", CallInEditor)
|
||||||
|
bool HasRequestReceivedResponse(const FGuid& RequestId) const;
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
||||||
|
FString GetRequestResponseData(const FGuid& RequestId) const;
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
||||||
|
bool IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
||||||
|
int32 SplitId = -1) const;
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
||||||
|
int32 GetPendingRequestCount() const;
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
||||||
|
UDTFluxQueuedManager* GetQueueManager() const;
|
||||||
|
|
||||||
|
// === EVENTS BLUEPRINT POUR LE TRACKING ===
|
||||||
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Tracked Requests")
|
||||||
|
FOnDTFluxTrackedRequestCompleted OnTrackedRequestCompleted;
|
||||||
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Tracked Requests")
|
||||||
|
FOnDTFluxTrackedRequestFailed OnTrackedRequestFailed;
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
|
||||||
|
// === REQUÊTES DIRECTES (LEGACY) ===
|
||||||
|
void SendRequest(const EDTFluxApiDataType RequestType, int InContestId = -1, int InStageId = -1,
|
||||||
|
int InSplitId = -1);
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Network")
|
||||||
|
void SendMessage(const FString& Message);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// ~Subsystem Interface
|
||||||
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 ===
|
||||||
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 ===
|
||||||
FDTFluxWebSocketClientSP WsClient = nullptr;
|
FDTFluxWebSocketClientSP WsClient = nullptr;
|
||||||
|
FDTFluxHttpClientSP HttpClient = nullptr;
|
||||||
|
|
||||||
TSharedPtr<FDTFluxQueuedRequestManager> RequestManager;
|
// === MÉTHODES DE CONFIGURATION ===
|
||||||
|
UFUNCTION()
|
||||||
|
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 ===
|
||||||
void RegisterWebSocketEvents();
|
void RegisterWebSocketEvents();
|
||||||
void UnregisterWebSocketEvents() const;
|
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);
|
|
||||||
|
|
||||||
FDelegateHandle OnWsConnectedEventDelegateHandle;
|
FDelegateHandle OnWsConnectedEventDelegateHandle;
|
||||||
FDelegateHandle OnWsConnectionErrorEventDelegateHandle;
|
FDelegateHandle OnWsConnectionErrorEventDelegateHandle;
|
||||||
@ -199,28 +211,12 @@ private:
|
|||||||
FDelegateHandle OnWsMessageEventDelegateHandle;
|
FDelegateHandle OnWsMessageEventDelegateHandle;
|
||||||
FDelegateHandle OnWsMessageSentEventDelegateHandle;
|
FDelegateHandle OnWsMessageSentEventDelegateHandle;
|
||||||
|
|
||||||
|
// === GESTION DES ÉVÉNEMENTS HTTP ===
|
||||||
|
void RegisterHttpEvents();
|
||||||
|
void UnregisterHttpEvents();
|
||||||
|
|
||||||
/**
|
// === PARSING DES RÉPONSES ===
|
||||||
* Essayer de matcher une réponse à une requête trackée
|
void ParseTeamListResponse(FDTFluxServerResponse& ServerResponse);
|
||||||
* @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);
|
||||||
@ -229,42 +225,19 @@ private:
|
|||||||
void ParseSplitSensorResponse(FDTFluxServerResponse& Response);
|
void ParseSplitSensorResponse(FDTFluxServerResponse& Response);
|
||||||
EDTFluxResponseStatus ProcessPushMessage(FDTFluxServerResponse& Response);
|
EDTFluxResponseStatus ProcessPushMessage(FDTFluxServerResponse& Response);
|
||||||
|
|
||||||
|
void Parse(FDTFluxServerResponse& Response);
|
||||||
|
void OnWebSocketMessageEvent_Subsystem(const FString& MessageString);
|
||||||
|
void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent);
|
||||||
|
bool CleanRequestCallbacks(const FGuid& RequestId);
|
||||||
|
|
||||||
/**
|
// === GESTION DES REQUÊTES TRACKÉES ===
|
||||||
* 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);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback appelé quand les paramètres WebSocket changent
|
|
||||||
*/
|
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void WsSettingsChanged(const FDTFluxWsSettings& NewWsSettings);
|
void OnRequestTimedOut_Internal(const FDTFluxQueuedRequest& TimedOutRequest);
|
||||||
|
bool TryMatchResponseToQueuedRequest(FDTFluxServerResponse& Response);
|
||||||
|
void CompleteTrackedRequest(const FGuid& RequestId, const FString& ResponseData, EDTFluxRequestType RequestType);
|
||||||
|
void FailTrackedRequest(const FGuid& RequestId, const FString& ErrorMessage, EDTFluxRequestType RequestType);
|
||||||
|
void SendQueuedRequest(const FDTFluxQueuedRequest& QueuedRequest);
|
||||||
|
|
||||||
/**
|
// === UTILITAIRES ===
|
||||||
* Reconnecter le client WebSocket
|
|
||||||
*/
|
|
||||||
void ReconnectWs(const FName WsClientId);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
};
|
};
|
||||||
|
|||||||
34
Source/DTFluxRaceResult/DTFluxRaceResult.Build.cs
Normal file
34
Source/DTFluxRaceResult/DTFluxRaceResult.Build.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using UnrealBuildTool;
|
||||||
|
|
||||||
|
public class DTFluxRaceResult : ModuleRules
|
||||||
|
{
|
||||||
|
public DTFluxRaceResult(ReadOnlyTargetRules Target) : base(Target)
|
||||||
|
{
|
||||||
|
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||||
|
|
||||||
|
PublicDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"Core"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
PrivateDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"CoreUObject",
|
||||||
|
"Engine",
|
||||||
|
"Slate",
|
||||||
|
"SlateCore",
|
||||||
|
"DTFluxProjectSettings",
|
||||||
|
"UMG",
|
||||||
|
"WebBrowser",
|
||||||
|
"Projects",
|
||||||
|
"ToolMenus",
|
||||||
|
"HTTP",
|
||||||
|
"JsonUtilities",
|
||||||
|
"Json"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
161
Source/DTFluxRaceResult/Private/DTFluxRaceResultModule.cpp
Normal file
161
Source/DTFluxRaceResult/Private/DTFluxRaceResultModule.cpp
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
#include "DTFluxRaceResultModule.h"
|
||||||
|
|
||||||
|
#include "LevelEditor.h"
|
||||||
|
#include "Widget/SDTFluxRaceResultWidget.h"
|
||||||
|
#include "Widget/Style/DTFluxRaceResultStyle.h"
|
||||||
|
|
||||||
|
#define LOCTEXT_NAMESPACE "FDTFluxRaceResultModule"
|
||||||
|
|
||||||
|
DEFINE_LOG_CATEGORY(logDTFluxRaceResult)
|
||||||
|
|
||||||
|
FName DTFLUXRACERESULT_API FDTFluxRaceResult::RaceResultTabId = "DTFluxRaceResult";
|
||||||
|
FText DTFLUXRACERESULT_API FDTFluxRaceResult::RaceResultTabDisplayName = FText::FromString(TEXT("DTFlux RaceResult"));
|
||||||
|
|
||||||
|
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::StartupModule()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Warning, TEXT("DTFluxRaceResult Module Started"))
|
||||||
|
FDTFluxRaceResultStyle::RegisterStyle();
|
||||||
|
InitMenuExtension();
|
||||||
|
RegisterRaceResultTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma region MenuExtension
|
||||||
|
|
||||||
|
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::InitMenuExtension()
|
||||||
|
{
|
||||||
|
UToolMenus::RegisterStartupCallback(
|
||||||
|
FSimpleMulticastDelegate::FDelegate::CreateRaw(this,
|
||||||
|
&FDTFluxRaceResult::RegisterMenuExtensions));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::RegisterMenuExtensions()
|
||||||
|
{
|
||||||
|
|
||||||
|
// Étendre le menu enregistré
|
||||||
|
if (UToolMenu* DTFluxMenu = UToolMenus::Get()->ExtendMenu("DTFlux.MainMenu"))
|
||||||
|
{
|
||||||
|
FToolMenuSection& ToolsSection = DTFluxMenu->FindOrAddSection("Tools");
|
||||||
|
ToolsSection.AddMenuEntry(
|
||||||
|
"DTFluxRaceResult",
|
||||||
|
FText::FromString("RaceResult"),
|
||||||
|
FText::FromString("Launch DTFlux RaceResult Control Panel"),
|
||||||
|
FSlateIcon(FDTFluxRaceResultStyle::GetStyleSetName(), "LevelEditor.Tab.IconRaceResult"),
|
||||||
|
FUIAction(FExecuteAction::CreateRaw(this, &FDTFluxRaceResult::OnButtonClicked))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxRaceResult::CreateSubmenu(UToolMenu* Menu)
|
||||||
|
{
|
||||||
|
// Section 2 : Tools
|
||||||
|
FToolMenuSection& ToolsSection = Menu->FindOrAddSection("Tools");
|
||||||
|
ToolsSection.Label = FText::FromString("Tools");
|
||||||
|
ToolsSection.AddMenuEntry(
|
||||||
|
"DTFluxRaceResult",
|
||||||
|
FText::FromString("RaceResult"),
|
||||||
|
FText::FromString("Launch Race Result WebAdmin Console"),
|
||||||
|
FSlateIcon(FDTFluxRaceResultStyle::GetStyleSetName(), "LevelEditor.Tab.IconRaceResult"),
|
||||||
|
// Adaptez selon votre icône
|
||||||
|
FUIAction(FExecuteAction::CreateRaw(this, &FDTFluxRaceResult::OnButtonClicked))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::OnButtonClicked()
|
||||||
|
{
|
||||||
|
FGlobalTabmanager::Get()->TryInvokeTab(RaceResultTabId);
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("Race Result Launched"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion EditorTab
|
||||||
|
|
||||||
|
#pragma region
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::RegisterRaceResultTab()
|
||||||
|
{
|
||||||
|
FTabSpawnerEntry& SpawnerEntry =
|
||||||
|
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(
|
||||||
|
RaceResultTabId,
|
||||||
|
FOnSpawnTab::CreateRaw(this, &FDTFluxRaceResult::OnSpawnTab)
|
||||||
|
)
|
||||||
|
.SetDisplayName(RaceResultTabDisplayName)
|
||||||
|
.SetTooltipText(FText::FromString(TEXT("Race Result Control Panel")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SDockTab> DTFLUXRACERESULT_API FDTFluxRaceResult::OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
SNew(
|
||||||
|
SDockTab
|
||||||
|
)
|
||||||
|
.TabRole(ETabRole::NomadTab)
|
||||||
|
// .ShouldAutosize(true)
|
||||||
|
[
|
||||||
|
SNew(SDTFluxRaceResultWidget)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::ShutdownModule()
|
||||||
|
{
|
||||||
|
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(RaceResultTabId);
|
||||||
|
FDTFluxRaceResultStyle::UnregisterStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// // Dans votre code de debug
|
||||||
|
// void DTFLUXRACERESULT_API FDTFluxRaceResult::DebugMenus()
|
||||||
|
// {
|
||||||
|
// UToolMenus* ToolMenus = UToolMenus::Get();
|
||||||
|
// if (ToolMenus)
|
||||||
|
// {
|
||||||
|
// TArray<FName> MenuNames;
|
||||||
|
// ToolMenus->GetAllMenuNames(MenuNames);
|
||||||
|
//
|
||||||
|
// UE_LOG(logDTFluxRaceResult, Warning, TEXT("=== ALL AVAILABLE MENUS ==="));
|
||||||
|
// for (const FName& MenuName : MenuNames)
|
||||||
|
// {
|
||||||
|
// UE_LOG(logDTFluxRaceResult, Warning, TEXT("Menu: %s"), *MenuName.ToString());
|
||||||
|
//
|
||||||
|
// // Obtenir les sections de chaque menu
|
||||||
|
// UToolMenu* Menu = ToolMenus->FindMenu(MenuName);
|
||||||
|
// if (Menu)
|
||||||
|
// {
|
||||||
|
// for (const FToolMenuSection& Section : Menu->Sections)
|
||||||
|
// {
|
||||||
|
// UE_LOG(logDTFluxRaceResult, Warning, TEXT(" Section: %s"), *Section.Name.ToString());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// UE_LOG(logDTFluxRaceResult, Warning, TEXT("=== END MENU LIST ==="));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// void DTFLUXRACERESULT_API FDTFluxRaceResult::AddMenu(FMenuBarBuilder& MenuBarBuilder)
|
||||||
|
// {
|
||||||
|
// MenuBarBuilder.AddPullDownMenu(
|
||||||
|
// FText::FromString("DTFlux"),
|
||||||
|
// FText::FromString("DTFlux API Tools"),
|
||||||
|
// FNewMenuDelegate::CreateRaw(this, &FDTFluxRaceResult::FillMenu)
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void DTFLUXRACERESULT_API FDTFluxRaceResult::FillMenu(FMenuBuilder& MenuBuilder)
|
||||||
|
// {
|
||||||
|
// MenuBuilder.AddMenuEntry(
|
||||||
|
// FText::FromString("RaceResult ControlPanel"),
|
||||||
|
// FText::FromString("Launch RaceResult Control Panel"),
|
||||||
|
// FSlateIcon(FDTFluxRaceResultStyle::GetStyleSetName(), "LevelEditor.Tab.IconRaceResult"),
|
||||||
|
// FExecuteAction::CreateRaw(this, &FDTFluxRaceResult::OnButtonClicked)
|
||||||
|
// );
|
||||||
|
// MenuBuilder.EndSection();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
#undef LOCTEXT_NAMESPACE
|
||||||
|
|
||||||
|
IMPLEMENT_MODULE(FDTFluxRaceResult, DTFluxRaceResult)
|
||||||
@ -0,0 +1,283 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Widget/SDTFluxRaceResultWidget.h"
|
||||||
|
|
||||||
|
#include "DTFluxRaceResultModule.h"
|
||||||
|
#include "HttpModule.h"
|
||||||
|
#include "IWebBrowserCookieManager.h"
|
||||||
|
#include "IWebBrowserWindow.h"
|
||||||
|
#include "SlateOptMacros.h"
|
||||||
|
#include "WebBrowserModule.h"
|
||||||
|
#include "Interfaces/IHttpRequest.h"
|
||||||
|
#include "Interfaces/IHttpResponse.h"
|
||||||
|
|
||||||
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::Construct(const FArguments& InArgs)
|
||||||
|
{
|
||||||
|
|
||||||
|
FWebBrowserInitSettings browserInitSettings = FWebBrowserInitSettings();
|
||||||
|
|
||||||
|
IWebBrowserModule::Get().CustomInitialize(browserInitSettings);
|
||||||
|
WindowSettings.InitialURL = TEXT("about:blank");
|
||||||
|
WindowSettings.BrowserFrameRate = 25;
|
||||||
|
|
||||||
|
if (IWebBrowserModule::IsAvailable() && IWebBrowserModule::Get().IsWebModuleAvailable())
|
||||||
|
{
|
||||||
|
IWebBrowserSingleton* WebBrowserSingleton = IWebBrowserModule::Get().GetSingleton();
|
||||||
|
Browser = WebBrowserSingleton->CreateBrowserWindow(WindowSettings);
|
||||||
|
// Browser->OnLoadUrl().BindRaw(this, &SDTFluxRaceResultWidget::OnLoadOverride);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ChildSlot
|
||||||
|
[
|
||||||
|
SNew(SBox)
|
||||||
|
.Padding(5.0f)
|
||||||
|
[
|
||||||
|
SNew(SBorder)
|
||||||
|
.BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder"))
|
||||||
|
[
|
||||||
|
SNew(SVerticalBox)
|
||||||
|
+ SVerticalBox::Slot()
|
||||||
|
[
|
||||||
|
SAssignNew(WebBrowser, SWebBrowser, Browser)
|
||||||
|
.ShowControls(true)
|
||||||
|
.SupportsTransparency(true)
|
||||||
|
.OnUrlChanged(this, &SDTFluxRaceResultWidget::OnUrlChanged)
|
||||||
|
.OnBeforeNavigation(this, &SDTFluxRaceResultWidget::OnBeforeNavigation)
|
||||||
|
.OnLoadCompleted(FSimpleDelegate::CreateRaw(this, &SDTFluxRaceResultWidget::OnLoadCompleted))
|
||||||
|
.OnLoadError(FSimpleDelegate::CreateRaw(this, &SDTFluxRaceResultWidget::OnLoadError))
|
||||||
|
.OnLoadStarted(FSimpleDelegate::CreateRaw(this, &SDTFluxRaceResultWidget::OnLoadStarted))
|
||||||
|
.ShowErrorMessage(true)
|
||||||
|
.ShowAddressBar(true)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnCookieSet(bool bSuccess)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::LoadContentViaHTTP()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Loading initial content via HTTP: %s"), *RaceResultUrl);
|
||||||
|
LoadSpecificURL(RaceResultUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::LoadSpecificURL(const FString& Url)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Loading via HTTP: %s"), *Url);
|
||||||
|
|
||||||
|
// Créer la requête HTTP
|
||||||
|
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
|
||||||
|
|
||||||
|
// Ajouter l'authentification Basic pour TOUTES les requêtes
|
||||||
|
FString Credentials = Username + TEXT(":") + Password;
|
||||||
|
FString EncodedCredentials = FBase64::Encode(Credentials);
|
||||||
|
|
||||||
|
Request->SetURL(Url);
|
||||||
|
Request->SetVerb("GET");
|
||||||
|
Request->SetHeader("Authorization", "Basic " + EncodedCredentials);
|
||||||
|
Request->SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
|
||||||
|
Request->SetHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
|
||||||
|
Request->SetHeader("Accept-Language", "en-US,en;q=0.5");
|
||||||
|
Request->SetHeader("Accept-Encoding", "gzip, deflate");
|
||||||
|
Request->SetHeader("Connection", "keep-alive");
|
||||||
|
|
||||||
|
Request->OnProcessRequestComplete().BindRaw(this, &SDTFluxRaceResultWidget::OnHTTPContentLoaded);
|
||||||
|
Request->ProcessRequest();
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("HTTP request sent with Basic Auth"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnHTTPContentLoaded(TSharedPtr<IHttpRequest> Request,
|
||||||
|
TSharedPtr<IHttpResponse> HttpResponse, bool bWasSuccessful)
|
||||||
|
{
|
||||||
|
if (bWasSuccessful && HttpResponse.IsValid())
|
||||||
|
{
|
||||||
|
int32 ResponseCode = HttpResponse->GetResponseCode();
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("HTTP Response Code: %d"), ResponseCode);
|
||||||
|
|
||||||
|
if (ResponseCode == 200)
|
||||||
|
{
|
||||||
|
FString Content = HttpResponse->GetContentAsString();
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Content loaded successfully, size: %d characters"), Content.Len());
|
||||||
|
|
||||||
|
// Traiter le contenu HTML
|
||||||
|
FString ProcessedContent = ProcessHTMLContent(Content, Request->GetURL());
|
||||||
|
|
||||||
|
// Charger le contenu dans le browser via LoadString
|
||||||
|
if (Browser.IsValid())
|
||||||
|
{
|
||||||
|
Browser->LoadString(ProcessedContent, Request->GetURL());
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Content loaded into browser"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ResponseCode == 401)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Error, TEXT("Authentication failed - 401 Unauthorized. Check your credentials."));
|
||||||
|
}
|
||||||
|
else if (ResponseCode == 403)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Error, TEXT("Access forbidden - 403 Forbidden"));
|
||||||
|
}
|
||||||
|
else if (ResponseCode == 404)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Error, TEXT("Page not found - 404 Not Found"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Error, TEXT("HTTP request failed with code: %d"), ResponseCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Error, TEXT("HTTP request failed completely"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnLoadOverride()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnUrlChanged(const FText& NewUrl)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("URL changed to: %s"), *NewUrl.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
FString SDTFluxRaceResultWidget::ProcessHTMLContent(const FString& Content, const FString& BaseUrl)
|
||||||
|
{
|
||||||
|
FString ProcessedContent = Content;
|
||||||
|
|
||||||
|
// Extraire le domaine de base
|
||||||
|
FString BaseDomain = ExtractDomain(BaseUrl);
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Processing HTML content, base domain: %s"), *BaseDomain);
|
||||||
|
|
||||||
|
// Convertir tous les liens relatifs en liens absolus
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("src=\"/"), *FString::Printf(TEXT("src=\"%s/"), *BaseDomain));
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("href=\"/"), *FString::Printf(TEXT("href=\"%s/"), *BaseDomain));
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("src='/"), *FString::Printf(TEXT("src='%s/"), *BaseDomain));
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("href='/"), *FString::Printf(TEXT("href='%s/"), *BaseDomain));
|
||||||
|
|
||||||
|
// Gérer les liens relatifs avec ./
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("src=\"./"), *FString::Printf(TEXT("src=\"%s/"), *BaseDomain));
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("href=\"./"), *FString::Printf(TEXT("href=\"%s/"), *BaseDomain));
|
||||||
|
|
||||||
|
// Ajouter une balise base pour aider avec les liens relatifs restants
|
||||||
|
FString BaseTag = FString::Printf(TEXT("<base href=\"%s/\">"), *BaseDomain);
|
||||||
|
if (ProcessedContent.Contains(TEXT("<head>")))
|
||||||
|
{
|
||||||
|
FString HeadReplacement = TEXT("<head>") + BaseTag;
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("<head>"), *HeadReplacement);
|
||||||
|
}
|
||||||
|
else if (ProcessedContent.Contains(TEXT("<HEAD>")))
|
||||||
|
{
|
||||||
|
FString HeadReplacement = TEXT("<HEAD>") + BaseTag;
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("<HEAD>"), *HeadReplacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("HTML content processed"));
|
||||||
|
return ProcessedContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FString SDTFluxRaceResultWidget::ExtractDomain(const FString& Url)
|
||||||
|
{
|
||||||
|
FString Domain = Url;
|
||||||
|
|
||||||
|
// Enlever le protocole
|
||||||
|
if (Url.StartsWith(TEXT("https://")))
|
||||||
|
{
|
||||||
|
Domain = Url.Mid(8);
|
||||||
|
}
|
||||||
|
else if (Url.StartsWith(TEXT("http://")))
|
||||||
|
{
|
||||||
|
Domain = Url.Mid(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trouver le premier slash (début du path)
|
||||||
|
int32 SlashIndex;
|
||||||
|
if (Domain.FindChar(TEXT('/'), SlashIndex))
|
||||||
|
{
|
||||||
|
Domain = Url.Left(Url.Len() - (Domain.Len() - SlashIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enlever le port si présent
|
||||||
|
FString DomainPart = Domain;
|
||||||
|
if (Domain.StartsWith(TEXT("https://")))
|
||||||
|
{
|
||||||
|
DomainPart = Domain.Mid(8);
|
||||||
|
}
|
||||||
|
else if (Domain.StartsWith(TEXT("http://")))
|
||||||
|
{
|
||||||
|
DomainPart = Domain.Mid(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 ColonIndex;
|
||||||
|
if (DomainPart.FindChar(TEXT(':'), ColonIndex))
|
||||||
|
{
|
||||||
|
Domain = Domain.Left(Domain.Len() - (DomainPart.Len() - ColonIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDTFluxRaceResultWidget::OnBeforeNavigation(const FString& Url, const FWebNavigationRequest& Request)
|
||||||
|
{
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Loading via HTTP: %s"), *Url);
|
||||||
|
|
||||||
|
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HTTPRequest = FHttpModule::Get().CreateRequest();
|
||||||
|
|
||||||
|
// Authentification pour toutes les requêtes
|
||||||
|
FString Credentials = Username + TEXT(":") + Password;
|
||||||
|
FString EncodedCredentials = FBase64::Encode(Credentials);
|
||||||
|
|
||||||
|
HTTPRequest->SetURL(RaceResultUrl);
|
||||||
|
HTTPRequest->SetVerb("GET");
|
||||||
|
HTTPRequest->SetHeader("Authorization", "Basic " + EncodedCredentials);
|
||||||
|
HTTPRequest->SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
|
||||||
|
HTTPRequest->SetHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||||
|
|
||||||
|
HTTPRequest->OnProcessRequestComplete().BindRaw(this, &SDTFluxRaceResultWidget::OnHTTPContentLoaded);
|
||||||
|
HTTPRequest->ProcessRequest();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnLoadCompleted()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Load Completed"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnLoadStarted()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Load Started"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnLoadError()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Load Error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnBeforeResourceLoad(FString Url, FString ResourceType, FContextRequestHeaders& AdditionalHeaders, const bool AllowUserCredentials)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::SetupBasicAuth()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Widget/Style/DTFluxRaceResultStyle.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "Interfaces/IPluginManager.h"
|
||||||
|
#include "Styling/SlateStyleRegistry.h"
|
||||||
|
#include "Styling/SlateStyleMacros.h"
|
||||||
|
|
||||||
|
#define RootToContentDir Style->RootToContentDir
|
||||||
|
|
||||||
|
TSharedPtr<ISlateStyle> FDTFluxRaceResultStyle::StyleSet = nullptr;
|
||||||
|
|
||||||
|
void FDTFluxRaceResultStyle::RegisterStyle()
|
||||||
|
{
|
||||||
|
if(StyleSet.IsValid()) return;
|
||||||
|
|
||||||
|
StyleSet = Create();
|
||||||
|
FSlateStyleRegistry::RegisterSlateStyle(*StyleSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxRaceResultStyle::UnregisterStyle()
|
||||||
|
{
|
||||||
|
if(StyleSet.IsValid())
|
||||||
|
{
|
||||||
|
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet);
|
||||||
|
ensure(StyleSet.IsUnique());
|
||||||
|
StyleSet.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxRaceResultStyle::ReloadTextures()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<ISlateStyle> FDTFluxRaceResultStyle::Create()
|
||||||
|
{
|
||||||
|
TSharedPtr<FSlateStyleSet> Style = MakeShareable(new FSlateStyleSet("DTFluxRaceResultStyle"));
|
||||||
|
Style->SetContentRoot(IPluginManager::Get().FindPlugin("DTFluxAPI")->GetBaseDir()/TEXT("Resources"));
|
||||||
|
|
||||||
|
Style->Set("LevelEditor.Tab.IconRaceResult", new IMAGE_BRUSH_SVG("DTFluxRaceResult16x16", FVector2d(16)) );
|
||||||
|
return Style;
|
||||||
|
}
|
||||||
|
|
||||||
33
Source/DTFluxRaceResult/Public/DTFluxRaceResultModule.h
Normal file
33
Source/DTFluxRaceResult/Public/DTFluxRaceResultModule.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Modules/ModuleManager.h"
|
||||||
|
|
||||||
|
DTFLUXRACERESULT_API DECLARE_LOG_CATEGORY_EXTERN(logDTFluxRaceResult, All, All);
|
||||||
|
|
||||||
|
class DTFLUXRACERESULT_API FDTFluxRaceResult : public IModuleInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void StartupModule() override;
|
||||||
|
virtual void ShutdownModule() override;
|
||||||
|
|
||||||
|
#pragma region MenuExtention
|
||||||
|
void RegisterMenuExtensions();
|
||||||
|
void InitMenuExtension();
|
||||||
|
void CreateSubmenu(UToolMenu* Menu);
|
||||||
|
|
||||||
|
// void AddMenu(FMenuBarBuilder& MenuBarBuilder);
|
||||||
|
// void FillMenu(FMenuBuilder& MenuBuilder);
|
||||||
|
void OnButtonClicked();
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region EditorTab
|
||||||
|
void RegisterRaceResultTab();
|
||||||
|
TSharedRef<SDockTab> OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs);
|
||||||
|
private:
|
||||||
|
// static void DebugMenus();
|
||||||
|
static FName RaceResultTabId;
|
||||||
|
static FText RaceResultTabDisplayName;
|
||||||
|
TSharedPtr<class SDTFluxRaceResultWidget> RaceResultWidget;
|
||||||
|
#pragma endregion
|
||||||
|
};
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "SWebBrowser.h"
|
||||||
|
#include "Widgets/SCompoundWidget.h"
|
||||||
|
|
||||||
|
class IHttpResponse;
|
||||||
|
class IHttpRequest;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DTFLUXRACERESULT_API SDTFluxRaceResultWidget : public SCompoundWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SLATE_BEGIN_ARGS(SDTFluxRaceResultWidget)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SLATE_END_ARGS()
|
||||||
|
|
||||||
|
/** Constructs this widget with InArgs */
|
||||||
|
void Construct(const FArguments& InArgs);
|
||||||
|
void OnCookieSet(bool bSuccess);
|
||||||
|
void LoadSpecificURL(const FString& Url);
|
||||||
|
void LoadContentViaHTTP();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TSharedPtr<SWebBrowser> WebBrowser;
|
||||||
|
TSharedPtr<IWebBrowserWindow> Browser;
|
||||||
|
TSharedPtr<IWebBrowserAdapter> BrowserAdapter;
|
||||||
|
FCreateBrowserWindowSettings WindowSettings;
|
||||||
|
|
||||||
|
void OnUrlChanged(const FText& NewUrl);
|
||||||
|
FString ProcessHTMLContent(const FString& Content, const FString& BaseUrl);
|
||||||
|
FString ExtractDomain(const FString& Url);
|
||||||
|
void OnHTTPContentLoaded(TSharedPtr<IHttpRequest> Request, TSharedPtr<IHttpResponse> HttpResponse, bool bWasSuccessful);
|
||||||
|
bool OnBeforeNavigation(const FString& Url, const FWebNavigationRequest& Request);
|
||||||
|
void OnLoadCompleted();
|
||||||
|
void OnLoadStarted();
|
||||||
|
void OnLoadError();
|
||||||
|
|
||||||
|
void OnLoadOverride();
|
||||||
|
|
||||||
|
void OnBeforeResourceLoad(FString Url, FString ResourceType, FContextRequestHeaders& AdditionalHeaders, const bool AllowUserCredentials);
|
||||||
|
|
||||||
|
FString RaceResultUrl = "https://raceresult.tds-france.com";
|
||||||
|
FString Username = "sporkrono";
|
||||||
|
FString Password = "Notre 3ème décennie d'action pour le climat";
|
||||||
|
|
||||||
|
|
||||||
|
void SetupBasicAuth();
|
||||||
|
};
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Styling/ISlateStyle.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DTFLUXRACERESULT_API FDTFluxRaceResultStyle
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void RegisterStyle();
|
||||||
|
static void UnregisterStyle();
|
||||||
|
|
||||||
|
static void ReloadTextures();
|
||||||
|
|
||||||
|
static const ISlateStyle& Get()
|
||||||
|
{
|
||||||
|
return *StyleSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const FName& GetStyleSetName()
|
||||||
|
{
|
||||||
|
return StyleSet->GetStyleSetName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static TSharedPtr<ISlateStyle> Create();
|
||||||
|
static TSharedPtr<ISlateStyle> StyleSet;
|
||||||
|
};
|
||||||
@ -9,7 +9,7 @@ public class DTFluxUtilities : ModuleRules
|
|||||||
PublicDependencyModuleNames.AddRange(
|
PublicDependencyModuleNames.AddRange(
|
||||||
new string[]
|
new string[]
|
||||||
{
|
{
|
||||||
"Core"
|
"Core",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
#include "DTFluxDatesUtilities.h"
|
#include "DTFluxDatesUtilities.h"
|
||||||
|
|
||||||
#include "DTFluxUtilitiesModule.h"
|
#include "DTFluxUtilitiesModule.h"
|
||||||
@ -19,16 +20,14 @@ bool DTFluxDatesUtilities::CompileDateAndTime(const FString& Time, const FString
|
|||||||
{
|
{
|
||||||
TArray<FString> ExplodedTime;
|
TArray<FString> ExplodedTime;
|
||||||
Time.ParseIntoArray(ExplodedTime, TEXT(":"));
|
Time.ParseIntoArray(ExplodedTime, TEXT(":"));
|
||||||
if (ExplodedTime.Num() != 3 && !ExplodedTime[0].IsNumeric() && !ExplodedTime[1].IsNumeric() & !ExplodedTime[2].
|
if(ExplodedTime.Num() != 3 && !ExplodedTime[0].IsNumeric() && !ExplodedTime[1].IsNumeric() & !ExplodedTime[2].IsNumeric())
|
||||||
IsNumeric())
|
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxUtilities, Error, TEXT("Bad Time Format [%s]. Unable to parse"), *Time);
|
UE_LOG(logDTFluxUtilities, Error, TEXT("Bad Time Format [%s]. Unable to parse"), *Time);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TArray<FString> ExplodedDate;
|
TArray<FString> ExplodedDate;
|
||||||
Date.ParseIntoArray(ExplodedDate, TEXT("-"));
|
Date.ParseIntoArray(ExplodedDate, TEXT("-"));
|
||||||
if (ExplodedDate.Num() != 3 && !ExplodedDate[0].IsNumeric() && !ExplodedDate[1].IsNumeric() && !ExplodedDate[2].
|
if(ExplodedDate.Num() != 3 && !ExplodedDate[0].IsNumeric() && !ExplodedDate[1].IsNumeric() && !ExplodedDate[2].IsNumeric() )
|
||||||
IsNumeric())
|
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxUtilities, Error, TEXT("Bad Date Format [%s]. Unable to parse"), *Date);
|
UE_LOG(logDTFluxUtilities, Error, TEXT("Bad Date Format [%s]. Unable to parse"), *Date);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -4,54 +4,16 @@
|
|||||||
#include "FTDFluxUtils.h"
|
#include "FTDFluxUtils.h"
|
||||||
|
|
||||||
#include "DTFluxCoreSubsystem.h"
|
#include "DTFluxCoreSubsystem.h"
|
||||||
#include "DTFluxUtilitiesModule.h"
|
|
||||||
|
|
||||||
FText UFTDFluxUtils::GetFormatedName(const int& Bib, const int MaxChar, const FString Separator,
|
FText UFTDFluxUtils::GetFormatedName(const int& Bib, const int MaxChar, const FString OverFlowChar)
|
||||||
const FString OverFlowChar)
|
|
||||||
{
|
{
|
||||||
UDTFluxCoreSubsystem* CoreSubsystem = GEngine->GetEngineSubsystem<UDTFluxCoreSubsystem>();
|
UDTFluxCoreSubsystem* CoreSubsystem = GEngine->GetEngineSubsystem<UDTFluxCoreSubsystem>();
|
||||||
if (CoreSubsystem != nullptr)
|
const FDTFluxParticipant OutParticipant = CoreSubsystem->GetParticipant(Bib);
|
||||||
{
|
return OutParticipant.GetFormattedNameText(MaxChar, OverFlowChar);
|
||||||
FDTFluxParticipant OutParticipant;
|
|
||||||
CoreSubsystem->GetParticipant(Bib, OutParticipant);
|
|
||||||
return OutParticipant.GetFormattedNameText(MaxChar, Separator, OverFlowChar);
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxUtilities, Error, TEXT("DTFluxCoreSubsystem not available"));
|
|
||||||
return FText();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FText UFTDFluxUtils::GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar,
|
FText UFTDFluxUtils::GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar,
|
||||||
const FString Separator,
|
|
||||||
const FString OverFlowChar)
|
const FString OverFlowChar)
|
||||||
{
|
{
|
||||||
return Participant.GetFormattedNameText(MaxChar, Separator, OverFlowChar);
|
return Participant.GetFormattedNameText(MaxChar, OverFlowChar);
|
||||||
}
|
|
||||||
|
|
||||||
void UFTDFluxUtils::GetFullName(const int Bib, FText& OutFullName)
|
|
||||||
{
|
|
||||||
OutFullName = FText();
|
|
||||||
UDTFluxCoreSubsystem* CoreSubsystem = GEngine->GetEngineSubsystem<UDTFluxCoreSubsystem>();
|
|
||||||
if(CoreSubsystem != nullptr)
|
|
||||||
{
|
|
||||||
FDTFluxParticipant Participant;
|
|
||||||
if(CoreSubsystem->GetParticipant(Bib, Participant))
|
|
||||||
{
|
|
||||||
FString FormattedName = "";
|
|
||||||
if (Participant.IsTeam())
|
|
||||||
{
|
|
||||||
OutFullName = FText::FromString(Participant.Team);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Participant.GetTeammate().IsEmpty())
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxUtilities, Warning, TEXT("Non teammate found with Bib %i"), Bib)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OutFullName = FText::FromString(FString::Printf(TEXT("%s %s"), *Participant.GetTeammate()[0].FirstName,
|
|
||||||
*Participant.GetTeammate()[0].LastName));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxUtilities, Warning, TEXT("Participant not found with Bib %i"), Bib);
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxUtilities, Error, TEXT("DTFluxCoreSubsystem not available"));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
class DTFLUXUTILITIES_API DTFluxDatesUtilities
|
class DTFLUXUTILITIES_API DTFluxDatesUtilities
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "DTFluxCore/Public/Types/Struct/DTFluxTeamListStruct.h"
|
#include "DTFluxCore/Public/Types/Struct/DTFluxTeamListStruct.h"
|
||||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||||
#include "Types/Struct/DTFluxRankingStructs.h"
|
|
||||||
#include "FTDFluxUtils.generated.h"
|
#include "FTDFluxUtils.generated.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,56 +17,9 @@ class DTFLUXUTILITIES_API UFTDFluxUtils : public UBlueprintFunctionLibrary
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
||||||
static FText GetFormatedName(const int& Bib, const int MaxChar = 10, const FString Separator = ".",
|
static FText GetFormatedName(const int& Bib, const int MaxChar = 10,
|
||||||
const FString OverFlowChar = "...");
|
const FString OverFlowChar = "...");
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
||||||
static FText GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar = 10,
|
static FText GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar = 10,
|
||||||
const FString Separator = ".",
|
|
||||||
const FString OverFlowChar = "...");
|
const FString OverFlowChar = "...");
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="convert, StageRankings, DTFlux"))
|
|
||||||
static void CastToDTFluxStageRanking(const FDTFluxDetailedRankingItem& ItemRanking, FDTFluxStageRanking& OutRanking)
|
|
||||||
{
|
|
||||||
CastRankingItem<FDTFluxStageRanking>(ItemRanking, OutRanking);
|
|
||||||
}
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="convert, StageRankings, DTFlux"))
|
|
||||||
static void CastToDTFluxStageRankingArray(const TArray<FDTFluxDetailedRankingItem>& ItemRanking,
|
|
||||||
TArray<FDTFluxStageRanking>& OutRanking)
|
|
||||||
{
|
|
||||||
CastRankingArray<FDTFluxStageRanking>(ItemRanking, OutRanking);
|
|
||||||
}
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="convert, StageRankings, DTFlux"))
|
|
||||||
static void CastToDTFluxSplitRanking(const FDTFluxDetailedRankingItem& ItemRanking, FDTFluxSplitRanking& OutRanking)
|
|
||||||
{
|
|
||||||
CastRankingItem<FDTFluxSplitRanking>(ItemRanking, OutRanking);
|
|
||||||
}
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="convert, StageRankings, DTFlux"))
|
|
||||||
static void CastToDTFluxSplitRankingArray(const TArray<FDTFluxDetailedRankingItem>& ItemRanking,
|
|
||||||
TArray<FDTFluxSplitRanking>& OutRanking)
|
|
||||||
{
|
|
||||||
CastRankingArray<FDTFluxSplitRanking>(ItemRanking, OutRanking);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static void CastRankingItem(const FDTFluxDetailedRankingItem& ItemRanking, T& OutRanking)
|
|
||||||
{
|
|
||||||
OutRanking = static_cast<T>(ItemRanking);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static void CastRankingArray(const TArray<FDTFluxDetailedRankingItem>& ItemRanking, TArray<T>& OutRanking)
|
|
||||||
{
|
|
||||||
OutRanking.Empty();
|
|
||||||
for (auto& Item : ItemRanking)
|
|
||||||
{
|
|
||||||
OutRanking.Add(static_cast<T>(Item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils")
|
|
||||||
static void GetFullName(const int Bib, FText& OutFullName);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user