Compare commits
35 Commits
e6d878aeef
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 892e282d1d | |||
| adc46114d8 | |||
| c88718fe35 | |||
| 8fe2750a54 | |||
| 33d3ea1e37 | |||
| 0bc765cd29 | |||
| dbe904e388 | |||
| 1f35fcbcf5 | |||
| 65c32d9240 | |||
| 609f623737 | |||
| e02ed8538f | |||
| 9b85bfc94a | |||
| da89e35eb2 | |||
| d6b8874827 | |||
| 193d8d4094 | |||
| 3291eb366d | |||
| 714e616fb1 | |||
| 17368f359f | |||
| 880ca9a3b1 | |||
| c02993057f | |||
| 69536766b1 | |||
| e63f54a882 | |||
| 6b58525349 | |||
| 505e4b7af2 | |||
| ae8b694f69 | |||
| a0b3ad6d8c | |||
| f644fb99a8 | |||
| fdae0fddc8 | |||
| f304685f01 | |||
| 07c83b5c8e | |||
| de5ed7f328 | |||
| 0a175f7813 | |||
| 4bbdf43ffa | |||
| 51e5898d4b | |||
| 1c04ae6bd7 |
@ -49,6 +49,11 @@
|
|||||||
"Name": "DTFluxAPIStatus",
|
"Name": "DTFluxAPIStatus",
|
||||||
"Type": "Editor",
|
"Type": "Editor",
|
||||||
"LoadingPhase": "Default"
|
"LoadingPhase": "Default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "DTFluxRemote",
|
||||||
|
"Type": "Editor",
|
||||||
|
"LoadingPhase": "Default"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Plugins": [
|
"Plugins": [
|
||||||
|
|||||||
@ -338,7 +338,8 @@ void SDTFluxStatusWidget::PopulateComboBoxItems()
|
|||||||
{
|
{
|
||||||
FString Separator = " | ";
|
FString Separator = " | ";
|
||||||
FString RootSeparator = " -> ";
|
FString RootSeparator = " -> ";
|
||||||
TArray<FDTFluxContest> DataFromSubsystem = DTFluxCore->GetContests();
|
TArray<FDTFluxContest> DataFromSubsystem = TArray<FDTFluxContest>();
|
||||||
|
DTFluxCore->GetContests(DataFromSubsystem);
|
||||||
|
|
||||||
for (const auto& Contest : DataFromSubsystem)
|
for (const auto& Contest : DataFromSubsystem)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
// 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"
|
||||||
@ -23,19 +24,19 @@ TSharedRef<SWidget> SHierarchicalTreeItemRow::GenerateWidgetForColumn(const FNam
|
|||||||
{
|
{
|
||||||
if (!Item.IsValid())
|
if (!Item.IsValid())
|
||||||
{
|
{
|
||||||
UE_LOG(LogTemp, Warning, TEXT("GenerateWidgetForColumn: Invalid item for column %s"), *ColumnName.ToString());
|
// UE_LOG(logDTFluxAssetEditor, 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(LogTemp, Warning, TEXT("GenerateWidgetForColumn: Invalid ParentWidget for column %s"),
|
// UE_LOG(logDTFluxAssetEditor, 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(LogTemp, VeryVerbose, TEXT("GenerateWidgetForColumn: %s for item %s"),
|
// UE_LOG(logDTFluxAssetEditor, VeryVerbose, TEXT("GenerateWidgetForColumn: %s for item %s"),
|
||||||
*ColumnName.ToString(), *Item->Name);
|
// *ColumnName.ToString(), *Item->Name);
|
||||||
|
|
||||||
if (ColumnName == "Name")
|
if (ColumnName == "Name")
|
||||||
{
|
{
|
||||||
@ -93,7 +94,7 @@ TSharedRef<SWidget> SHierarchicalTreeItemRow::GenerateWidgetForColumn(const FNam
|
|||||||
.OverflowPolicy(ETextOverflowPolicy::Ellipsis);
|
.OverflowPolicy(ETextOverflowPolicy::Ellipsis);
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(LogTemp, Warning, TEXT("GenerateWidgetForColumn: Unknown column %s"), *ColumnName.ToString());
|
UE_LOG(logDTFluxAssetEditor, 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())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,7 +412,7 @@ void SDTFluxAssetModelDetailsWidget::BuildContestHierarchy()
|
|||||||
RootItems.Add(ContestItem);
|
RootItems.Add(ContestItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(LogTemp, Log, TEXT("Built contest hierarchy with %d root contests"), RootItems.Num());
|
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("Built contest hierarchy with %d root contests"), RootItems.Num());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDTFluxAssetModelDetailsWidget::BuildParticipantList()
|
void SDTFluxAssetModelDetailsWidget::BuildParticipantList()
|
||||||
@ -420,11 +421,11 @@ void SDTFluxAssetModelDetailsWidget::BuildParticipantList()
|
|||||||
|
|
||||||
if (!ModelAsset)
|
if (!ModelAsset)
|
||||||
{
|
{
|
||||||
UE_LOG(LogTemp, Warning, TEXT("BuildParticipantList: ModelAsset is null!"));
|
UE_LOG(logDTFluxAssetEditor, Warning, TEXT("BuildParticipantList: ModelAsset is null!"));
|
||||||
return;
|
// return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(LogTemp, Log, TEXT("BuildParticipantList: ModelAsset has %d participants"), ModelAsset->Participants.Num());
|
// UE_LOG(logDTFluxAssetEditor, 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)
|
||||||
@ -433,12 +434,12 @@ void SDTFluxAssetModelDetailsWidget::BuildParticipantList()
|
|||||||
auto ParticipantItem = FHierarchicalTreeItem::CreateParticipant(Participant);
|
auto ParticipantItem = FHierarchicalTreeItem::CreateParticipant(Participant);
|
||||||
ParticipantItems.Add(ParticipantItem);
|
ParticipantItems.Add(ParticipantItem);
|
||||||
|
|
||||||
UE_LOG(LogTemp, Log, TEXT("BuildParticipantList: Added participant %s (Bib: %d)"),
|
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("BuildParticipantList: Added participant %s (Bib: %d)"),
|
||||||
*ParticipantItem->Name, ParticipantItem->Bib);
|
// *ParticipantItem->Name, ParticipantItem->Bib);
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(LogTemp, Log, TEXT("BuildParticipantList: Built participant list with %d participants"),
|
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("BuildParticipantList: Built participant list with %d participants"),
|
||||||
ParticipantItems.Num());
|
// ParticipantItems.Num());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== CALLBACKS TREEVIEW =====
|
// ===== CALLBACKS TREEVIEW =====
|
||||||
@ -448,12 +449,12 @@ TSharedRef<ITableRow> SDTFluxAssetModelDetailsWidget::OnGenerateRowForTree(
|
|||||||
{
|
{
|
||||||
if (!Item.IsValid())
|
if (!Item.IsValid())
|
||||||
{
|
{
|
||||||
UE_LOG(LogTemp, Warning, TEXT("OnGenerateRowForTree: Invalid item!"));
|
UE_LOG(logDTFluxAssetEditor, Warning, TEXT("OnGenerateRowForTree: Invalid item!"));
|
||||||
return SNew(STableRow<FHierarchicalTreeItemPtr>, OwnerTable);
|
return SNew(STableRow<FHierarchicalTreeItemPtr>, OwnerTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(LogTemp, Log, TEXT("OnGenerateRowForTree: Generating row for %s (Type: %d)"),
|
// UE_LOG(logDTFluxAssetEditor, 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)
|
||||||
@ -536,7 +537,7 @@ FReply SDTFluxAssetModelDetailsWidget::OnExpandAllClicked()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(LogTemp, Log, TEXT("Expanded all contests"));
|
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("Expanded all contests"));
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,14 +551,14 @@ FReply SDTFluxAssetModelDetailsWidget::OnCollapseAllClicked()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(LogTemp, Log, TEXT("Collapsed all contests"));
|
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("Collapsed all contests"));
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
}
|
}
|
||||||
|
|
||||||
FReply SDTFluxAssetModelDetailsWidget::OnRefreshClicked()
|
FReply SDTFluxAssetModelDetailsWidget::OnRefreshClicked()
|
||||||
{
|
{
|
||||||
RefreshData();
|
RefreshData();
|
||||||
UE_LOG(LogTemp, Log, TEXT("Data refreshed"));
|
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("Data refreshed"));
|
||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,21 +568,17 @@ void SDTFluxAssetModelDetailsWidget::RefreshData()
|
|||||||
{
|
{
|
||||||
if (!ModelAsset)
|
if (!ModelAsset)
|
||||||
{
|
{
|
||||||
UE_LOG(LogTemp, Warning, TEXT("ModelAsset is null!"));
|
UE_LOG(logDTFluxAssetEditor, Warning, TEXT("ModelAsset is null!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(LogTemp, Log, TEXT("RefreshData: Starting refresh for ModelAsset %s"), *ModelAsset->GetName());
|
// UE_LOG(logDTFluxAssetEditor, 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();
|
||||||
@ -591,9 +588,8 @@ void SDTFluxAssetModelDetailsWidget::RefreshData()
|
|||||||
{
|
{
|
||||||
ParticipantTreeView->RequestTreeRefresh();
|
ParticipantTreeView->RequestTreeRefresh();
|
||||||
}
|
}
|
||||||
|
// UE_LOG(logDTFluxAssetEditor, Log, TEXT("RefreshData: Completed successfully - %d contests, %d participants"), RootItems.Num(),
|
||||||
UE_LOG(LogTemp, Log, TEXT("RefreshData: Completed successfully - %d contests, %d participants"), RootItems.Num(),
|
// ParticipantItems.Num());
|
||||||
ParticipantItems.Num());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== MÉTHODES UTILITAIRES =====
|
// ===== MÉTHODES UTILITAIRES =====
|
||||||
@ -635,7 +631,6 @@ 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("%d rankings"), Split.SplitRankings.Num());
|
Item->Details = FString::Printf(TEXT("rankings"));
|
||||||
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Widget avec STreeView simple et efficace
|
* ModelAsset TreeviewWidget
|
||||||
*/
|
*/
|
||||||
class DTFLUXASSETSEDITOR_API SDTFluxAssetModelDetailsWidget : public SCompoundWidget
|
class DTFLUXASSETSEDITOR_API SDTFluxAssetModelDetailsWidget : public SCompoundWidget
|
||||||
{
|
{
|
||||||
@ -152,40 +152,33 @@ 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; // Contests racines avec hiérarchie
|
TArray<FHierarchicalTreeItemPtr> RootItems;
|
||||||
TArray<FHierarchicalTreeItemPtr> ParticipantItems; // Participants séparés
|
TArray<FHierarchicalTreeItemPtr> ParticipantItems;
|
||||||
|
|
||||||
// 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,6 +13,20 @@ 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)
|
||||||
@ -132,6 +146,38 @@ 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)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
||||||
|
|
||||||
|
bool FDTFluxStage::IsFinished() const
|
||||||
|
{
|
||||||
|
return EndTime <= FDateTime::Now();
|
||||||
|
}
|
||||||
|
|
||||||
bool FDTFluxContest::IsFinished() const
|
bool FDTFluxContest::IsFinished() const
|
||||||
{
|
{
|
||||||
return EndTime <= FDateTime::Now();
|
return EndTime <= FDateTime::Now();
|
||||||
@ -62,6 +67,3 @@ bool FDTFluxContest::GetStage(const int StageID, FDTFluxStage& OutStage) const
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,12 @@
|
|||||||
#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,
|
||||||
|
|||||||
@ -150,7 +150,9 @@ int FDTFluxParticipant::GetTeammateNum() const
|
|||||||
|
|
||||||
bool FDTFluxParticipant::IsTeam() const
|
bool FDTFluxParticipant::IsTeam() const
|
||||||
{
|
{
|
||||||
return Teammate.Num() > 1;
|
UE_LOG(logDTFluxCore, Verbose, TEXT("IsTeam() -> %s, Teamate Num %i"),
|
||||||
|
Team.IsEmpty() ? TEXT("TRUE") : TEXT("FALSE"), Teammate.Num());
|
||||||
|
return !Team.IsEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
const TArray<FDTFluxPerson>& FDTFluxParticipant::GetTeammate() const
|
const TArray<FDTFluxPerson>& FDTFluxParticipant::GetTeammate() const
|
||||||
@ -165,13 +167,18 @@ FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString& S
|
|||||||
{
|
{
|
||||||
return TEXT("");
|
return TEXT("");
|
||||||
}
|
}
|
||||||
FString FirstName;
|
|
||||||
FString LastName;
|
if (Teammate.Num() == 0)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
if (IsTeam())
|
if (IsTeam())
|
||||||
{
|
{
|
||||||
|
FString FullName;
|
||||||
|
UE_LOG(logDTFluxCore, Verbose, TEXT("Participant is a team"));
|
||||||
if (!Team.IsEmpty())
|
if (!Team.IsEmpty())
|
||||||
{
|
{
|
||||||
LastName = Team;
|
FullName = Team;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -180,24 +187,23 @@ FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString& S
|
|||||||
{
|
{
|
||||||
Names.Add(Person.LastName);
|
Names.Add(Person.LastName);
|
||||||
}
|
}
|
||||||
LastName = FString::Join(Names, TEXT("/"));
|
FullName = FString::Join(Names, TEXT("/"));
|
||||||
}
|
}
|
||||||
}
|
FullName = FullName.ToUpper();
|
||||||
else if (Teammate.Num() > 0)
|
if (FullName.Len() <= MaxChar)
|
||||||
{
|
{
|
||||||
FirstName = Teammate[0].FirstName;
|
return FullName;
|
||||||
LastName = Teammate[0].LastName;
|
|
||||||
}
|
}
|
||||||
else
|
return FullName.Left(MaxChar) + OverflowChar;
|
||||||
{
|
|
||||||
LastName = TEXT("Unknown");
|
|
||||||
}
|
}
|
||||||
|
FString FirstName = Teammate[0].FirstName;
|
||||||
|
FString LastName = Teammate[0].LastName;
|
||||||
FString Initial;
|
FString Initial;
|
||||||
if (!FirstName.IsEmpty())
|
if (!FirstName.IsEmpty())
|
||||||
{
|
{
|
||||||
Initial = FirstName.Left(1).ToUpper() + Separator;
|
Initial = FirstName.Left(1).ToUpper() + Separator;
|
||||||
}
|
}
|
||||||
FString FormattedLastName = LastName.ToUpper();
|
const FString FormattedLastName = LastName.ToUpper();
|
||||||
FString FullName = Initial + FormattedLastName;
|
FString FullName = Initial + FormattedLastName;
|
||||||
if (FullName.Len() <= MaxChar)
|
if (FullName.Len() <= MaxChar)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -35,15 +35,24 @@ public:
|
|||||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
||||||
TMap<FString /* ContestName */, FDTFluxContest> Contests;
|
TMap<FString /* ContestName */, FDTFluxContest> Contests;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
||||||
TMap<int /*ContestId*/, FDTFluxContestRankings> ContestRankings;
|
TMap<int /*ContestId*/, FDTFluxContestRankings> ContestRankings;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
||||||
TMap<FDTFluxStageKey, FDTFluxStageRankings> StageRankings;
|
TMap<FDTFluxStageKey, FDTFluxStageRankings> StageRankings;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
||||||
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);
|
||||||
|
|
||||||
@ -89,4 +98,15 @@ 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, meta=(Bitflags, UseEnumValuesAsMaskValuesInEditor=true))
|
UENUM(BlueprintType)
|
||||||
enum class EDTFluxFinisherType : uint8
|
enum class EDTFluxFinisherType : uint8
|
||||||
{
|
{
|
||||||
None = 0b0000000 UMETA(DisplayName="Unknown"),
|
None = 0b0000000 UMETA(DisplayName="Unknown"),
|
||||||
@ -42,7 +42,6 @@ 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
|
||||||
@ -52,6 +51,7 @@ 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,6 +69,7 @@ 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,3 +109,61 @@ 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,6 +15,7 @@ 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;
|
||||||
@ -23,9 +24,7 @@ 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")
|
||||||
FDTFluxStageRanking SplitRanking;
|
FString Time;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
|
|
||||||
FDTFluxStageRanking StageRanking;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
USTRUCT(BlueprintType, Category="FDTFlux|Model")
|
USTRUCT(BlueprintType, Category="FDTFlux|Model")
|
||||||
@ -64,4 +63,3 @@ struct DTFLUXCORE_API FDTFluxContestFinished
|
|||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
|
||||||
TArray<FDTFluxStageRanking> Rankings;
|
TArray<FDTFluxStageRanking> Rankings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -23,12 +23,7 @@ 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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,6 +46,7 @@ public:
|
|||||||
FDateTime EndTime;
|
FDateTime EndTime;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
||||||
FDateTime CutOff;
|
FDateTime CutOff;
|
||||||
|
bool IsFinished() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -8,6 +8,18 @@
|
|||||||
#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
|
||||||
@ -37,7 +49,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
USTRUCT(BlueprintType)
|
||||||
struct FDTFluxContestRankings
|
struct FDTFluxContestRankings : public FDTFluxBaseRankings
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
@ -57,7 +69,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @struct FDTFluxStageRanking
|
* @struct FDTFluxDetailedRankingItem
|
||||||
* Representing a stage ranking for a participant
|
* Representing a stage ranking for a participant
|
||||||
*/
|
*/
|
||||||
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
||||||
@ -94,7 +106,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
USTRUCT(BlueprintType)
|
||||||
struct FDTFluxDetailedRankings
|
struct FDTFluxDetailedRankings : public FDTFluxBaseRankings
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
@ -125,7 +137,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 FDTFluxStageRanking
|
struct DTFLUXCORE_API FDTFluxSplitRanking : public FDTFluxDetailedRankingItem
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
};
|
};
|
||||||
|
|||||||
@ -17,7 +17,17 @@ 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)
|
||||||
@ -31,7 +41,23 @@ public:
|
|||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
FString Gap = "-";
|
FString Gap = "-";
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
int Rank;
|
int Rank = -1;
|
||||||
|
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>();
|
||||||
|
};
|
||||||
|
|||||||
@ -185,12 +185,77 @@ 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);
|
||||||
@ -267,17 +332,49 @@ void UDTFluxCoreSubsystem::ProcessTeamUpdate(const FDTFluxTeamListDefinition& Te
|
|||||||
|
|
||||||
void UDTFluxCoreSubsystem::ProcessSplitSensor(const FDTFluxSplitSensorInfo& SplitSensorInfo)
|
void UDTFluxCoreSubsystem::ProcessSplitSensor(const FDTFluxSplitSensorInfo& SplitSensorInfo)
|
||||||
{
|
{
|
||||||
FDTFluxContest Contest;
|
|
||||||
FDTFluxStageKey StageKey(SplitSensorInfo.ContestId, SplitSensorInfo.StageId);
|
|
||||||
FDTFluxStage Stage;
|
|
||||||
DataStorage->GetStage(StageKey, Stage);
|
|
||||||
FDTFluxParticipant Participant;
|
|
||||||
DataStorage->GetParticipantByBib(SplitSensorInfo.Bib, Participant);
|
|
||||||
|
|
||||||
DataStorage->GetContestById(SplitSensorInfo.ContestId, Contest);
|
if (DataStorage != nullptr)
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Log, TEXT("%s|%s Split %i Sensor for Participant [Bib] %i [FullName] %s"),
|
{
|
||||||
*Contest.Name, *Stage.Name,
|
FString DebugString = FString::Printf(TEXT("Received SplitSensorInfo for Bib %i"), SplitSensorInfo.Bib);
|
||||||
SplitSensorInfo.SplitId, SplitSensorInfo.Bib, *Participant.GetFormattedName());
|
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;
|
||||||
|
if (DataStorage->Participants.Contains(SplitSensorInfo.Bib))
|
||||||
|
{
|
||||||
|
DataStorage->Participants[SplitSensorInfo.Bib].CurrentSplit = SplitSensorInfo.SplitId;
|
||||||
|
}
|
||||||
|
// Gestion Finnish Status
|
||||||
|
switch (GetSplitSensorType(SplitSensorInfo))
|
||||||
|
{
|
||||||
|
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)
|
||||||
@ -288,6 +385,232 @@ void UDTFluxCoreSubsystem::SendRequest(const FString& Message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UDTFluxCoreSubsystem::InitParticipantTracking(const int Bib, const int ContestId, const int StageId)
|
||||||
|
{
|
||||||
|
FDTFluxContest Contest;
|
||||||
|
if (GetContestForId(ContestId, Contest))
|
||||||
|
{
|
||||||
|
// get all splits
|
||||||
|
TArray<FDTFluxSplitSensorInfo> SplitSensorInfos;
|
||||||
|
FDTFluxSplitSensorKey SplitSensorKey;
|
||||||
|
for (auto Split : Contest.Splits)
|
||||||
|
{
|
||||||
|
SplitSensorKey = FDTFluxSplitSensorKey();
|
||||||
|
SplitSensorKey.ContestId = ContestId;
|
||||||
|
SplitSensorKey.StageId = StageId;
|
||||||
|
SplitSensorKey.Bib = Bib;
|
||||||
|
SplitSensorKey.SplitId = Split.SplitId;
|
||||||
|
if (DataStorage->SplitSensorInfoCache.Contains(SplitSensorKey))
|
||||||
|
{
|
||||||
|
FDTFluxSplitSensorInfo SplitSensorInfoToAdd = DataStorage->SplitSensorInfoCache[SplitSensorKey];
|
||||||
|
SplitSensorInfos.Add(SplitSensorInfoToAdd);
|
||||||
|
FString DebugString = FString::Printf(TEXT("SplitSensorInfo for Bib %i "), Bib);
|
||||||
|
DebugString += FString::Printf(TEXT("SplitSensorInfo [Rank] %i "), SplitSensorInfoToAdd.Rank);
|
||||||
|
DebugString += FString::Printf(TEXT("SplitSensorInfo [Rank] %s "), *SplitSensorInfoToAdd.Gap);
|
||||||
|
DebugString += FString::Printf(TEXT("SplitSensorInfo [Rank] %s "), *SplitSensorInfoToAdd.Time);
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("SplitSensorInfoCache contains SplitSensorInfo for Bib %i\nData : %s"), Bib, *DebugString);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SplitSensorInfos.Add(FDTFluxSplitSensorInfo(Split.Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FDTFluxSplitHistory History;
|
||||||
|
History.SplitSensors = SplitSensorInfos;
|
||||||
|
if (GetParticipant(Bib, History.Participant))
|
||||||
|
{
|
||||||
|
OnParticipantTrackingReady.Broadcast(History);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FGuid UDTFluxCoreSubsystem::InitContestRankingsDisplay(const int ContestId)
|
||||||
|
{
|
||||||
|
if (NetworkSubsystem)
|
||||||
|
{
|
||||||
|
if (DataStorage)
|
||||||
|
{
|
||||||
|
// no need to request ContestRankings;
|
||||||
|
if (IsContestRankingSealed(ContestId))
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestRankings already Sealed for ContestId %i"), 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)
|
||||||
|
{
|
||||||
|
if (NetworkSubsystem)
|
||||||
|
{
|
||||||
|
if (DataStorage)
|
||||||
|
{
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
if (NetworkSubsystem)
|
||||||
|
{
|
||||||
|
if (DataStorage)
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
FDTFluxStageRanking& OutStageRanking)
|
||||||
|
{
|
||||||
|
if (DataStorage)
|
||||||
|
{
|
||||||
|
FDTFluxStageKey StageKey(ContestId, StageId);
|
||||||
|
if (DataStorage->StageRankings.Contains(StageKey))
|
||||||
|
{
|
||||||
|
FDTFluxStageRankings StageRankings = DataStorage->StageRankings[StageKey];
|
||||||
|
for (auto& Ranking : StageRankings.Rankings)
|
||||||
|
{
|
||||||
|
if (Ranking.Bib == Bib)
|
||||||
|
{
|
||||||
|
OutStageRanking = static_cast<FDTFluxStageRanking>(Ranking);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
const int Bib, FDTFluxSplitRanking& OutSplitRankings)
|
||||||
|
{
|
||||||
|
if (DataStorage)
|
||||||
|
{
|
||||||
|
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::GetContestRankings(const int ContestId,
|
bool UDTFluxCoreSubsystem::GetContestRankings(const int ContestId,
|
||||||
FDTFluxContestRankings& OutContestRankings)
|
FDTFluxContestRankings& OutContestRankings)
|
||||||
{
|
{
|
||||||
@ -298,8 +621,10 @@ bool UDTFluxCoreSubsystem::GetContestRankings(const int ContestId,
|
|||||||
}
|
}
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
{
|
{
|
||||||
TArray<int> TackedContestIds = {ContestId};
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Requesting ContestRankings for ContestId %i"), ContestId);
|
||||||
TrackedRequestContestRankings(TackedContestIds);
|
TArray<int> TrackedContestIds;
|
||||||
|
TrackedContestIds.Add(ContestId);
|
||||||
|
TrackedRequestContestRankings(TrackedContestIds);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("NetworkSubsystem unavailable"));
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("NetworkSubsystem unavailable"));
|
||||||
@ -421,7 +746,6 @@ TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestStageRankings(const TArray<FDT
|
|||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("StageRanking Request [%s] Error %s"),
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("StageRanking Request [%s] Error %s"),
|
||||||
*InReq.RequestId.ToString(), *InError);
|
*InReq.RequestId.ToString(), *InError);
|
||||||
});
|
});
|
||||||
// if Contest is not ended
|
|
||||||
for (auto StageKey : ForStages)
|
for (auto StageKey : ForStages)
|
||||||
{
|
{
|
||||||
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::StageRanking,
|
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::StageRanking,
|
||||||
@ -468,13 +792,15 @@ TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestSplitRankings(const TArray<FDT
|
|||||||
return TArray<FGuid>();
|
return TArray<FGuid>();
|
||||||
}
|
}
|
||||||
|
|
||||||
const FDTFluxParticipant UDTFluxCoreSubsystem::GetParticipant(int InBib)
|
bool UDTFluxCoreSubsystem::GetParticipant(int InBib, FDTFluxParticipant& OutParticipant)
|
||||||
{
|
{
|
||||||
if (DataStorage->Participants.Contains(InBib))
|
if (DataStorage->Participants.Contains(InBib))
|
||||||
{
|
{
|
||||||
return DataStorage->Participants[InBib];
|
OutParticipant = DataStorage->Participants[InBib];
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return FDTFluxParticipant();
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<int> UDTFluxCoreSubsystem::GetCurrentContestsId()
|
TArray<int> UDTFluxCoreSubsystem::GetCurrentContestsId()
|
||||||
@ -482,24 +808,30 @@ TArray<int> UDTFluxCoreSubsystem::GetCurrentContestsId()
|
|||||||
return GetContestsIdForTime(FDateTime::Now());
|
return GetContestsIdForTime(FDateTime::Now());
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<FDTFluxContest> UDTFluxCoreSubsystem::GetCurrentContests()
|
bool UDTFluxCoreSubsystem::GetCurrentContests(TArray<FDTFluxContest>& OutContests)
|
||||||
{
|
{
|
||||||
return GetContestsForTime(FDateTime::Now());
|
return GetContestsForTime(FDateTime::Now(), OutContests);
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<int> UDTFluxCoreSubsystem::GetContestsIdForTime(const FDateTime Time) const
|
TArray<int> UDTFluxCoreSubsystem::GetContestsIdForTime(const FDateTime Time)
|
||||||
{
|
{
|
||||||
TArray<int> Contests;
|
if (DataStorage)
|
||||||
for (const auto& Pair : DataStorage->Contests)
|
|
||||||
{
|
{
|
||||||
FDTFluxContest Contest = Pair.Value;
|
TArray<FDTFluxContest> Contests;
|
||||||
int ContestId = Contest.ContestId;
|
if (GetContestsForTime(Time, Contests))
|
||||||
if (Contest.Date < Time && Contest.EndTime > Time)
|
|
||||||
{
|
{
|
||||||
Contests.Add(ContestId);
|
TArray<int> ContestIds = TArray<int>();
|
||||||
|
for (const auto& Contest : Contests)
|
||||||
|
{
|
||||||
|
ContestIds.Add(Contest.ContestId);
|
||||||
}
|
}
|
||||||
|
return ContestIds;
|
||||||
}
|
}
|
||||||
return Contests;
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("No Contest running for Time [%s]"), *Time.ToString());
|
||||||
|
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)
|
||||||
@ -516,32 +848,119 @@ bool UDTFluxCoreSubsystem::GetContestForId(const int Id, FDTFluxContest& OutCont
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<FDTFluxContest> UDTFluxCoreSubsystem::GetContestsForTime(const FDateTime Time)
|
bool UDTFluxCoreSubsystem::GetContestsForTime(const FDateTime Time, TArray<FDTFluxContest>& OutContests)
|
||||||
{
|
{
|
||||||
TArray<FDTFluxContest> Contests;
|
if (DataStorage)
|
||||||
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
Contests.Add(Contest);
|
// ils sont finis
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::RequestRankingsForStages(TArray<FDTFluxStage> RequestedStages) const
|
bool UDTFluxCoreSubsystem::GetContests(TArray<FDTFluxContest>& OutContests)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TArray<FDTFluxContest> UDTFluxCoreSubsystem::GetContests()
|
|
||||||
{
|
{
|
||||||
|
OutContests.Empty();
|
||||||
if (DataStorage)
|
if (DataStorage)
|
||||||
{
|
{
|
||||||
TArray<FDTFluxContest> OutContests;
|
|
||||||
DataStorage->Contests.GenerateValueArray(OutContests);
|
DataStorage->Contests.GenerateValueArray(OutContests);
|
||||||
return OutContests;
|
return !OutContests.IsEmpty();
|
||||||
}
|
}
|
||||||
return TArray<FDTFluxContest>();
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage not available"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxCoreSubsystem::GetContest(const int ContestId, FDTFluxContest& OutContest)
|
||||||
|
{
|
||||||
|
OutContest = FDTFluxContest();
|
||||||
|
if (GetContestForId(ContestId, OutContest))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestId %d not found in ContestDefinition"), ContestId)
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxCoreSubsystem::GetStageDefinition(const FDTFluxStageKey StageKey, FDTFluxStage& OutStageDefinition)
|
||||||
|
{
|
||||||
|
int ContestId = StageKey.ContestId;
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,12 +28,14 @@ void UDTFluxPursuitManager::InitPursuit(const TArray<int> InContestIds, const in
|
|||||||
FDTFluxContest Contest;
|
FDTFluxContest Contest;
|
||||||
if (CoreSubsystem->GetContestForId(ContestId, Contest))
|
if (CoreSubsystem->GetContestForId(ContestId, Contest))
|
||||||
{
|
{
|
||||||
BindRankings();
|
// BindRankings();
|
||||||
FDTFluxStageKey StageKey = FDTFluxStageKey(ContestId, Contest.GetLastStageId());
|
FDTFluxStageKey StageKey = FDTFluxStageKey(ContestId, Contest.GetLastStageId());
|
||||||
FDTFluxStageRankings TempStageRankings;
|
FDTFluxStageRankings TempStageRankings;
|
||||||
//Obtenir les ranking Frais.
|
//Obtenir les ranking Frais.
|
||||||
CoreSubsystem->GetStageRankingsWithKey(StageKey, TempStageRankings, false);
|
CoreSubsystem->GetStageRankingsWithKey(StageKey, TempStageRankings, true);
|
||||||
PendingStageRanking.Add(StageKey, false);
|
AllRankings.Add(TempStageRankings);
|
||||||
|
LaunchPursuitSequence();
|
||||||
|
// CoreSubsystem->GetStageRankings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,25 +48,6 @@ void UDTFluxPursuitManager::SetPursuitInfoIsMassStart(FDTFluxPursuitGroup& NextF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxPursuitManager::DebugFocusNext(const TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext)
|
|
||||||
{
|
|
||||||
FString FocusBibs;
|
|
||||||
for (const auto& Pursuit : OutPursuitFocusNext)
|
|
||||||
{
|
|
||||||
FocusBibs += FString::Printf(TEXT("%d "), Pursuit.Bib);
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus Bibs: %s"), *FocusBibs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxPursuitManager::DebugOutPoursuitNext(const TArray<FDTFluxPursuitInfo>& OutPursuitNext)
|
|
||||||
{
|
|
||||||
FString NextBibs;
|
|
||||||
for (int32 i = 0; i < OutPursuitNext.Num(); i++)
|
|
||||||
{
|
|
||||||
NextBibs += FString::Printf(TEXT("%d "), OutPursuitNext[i].Bib);
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next Bibs: %s"), *NextBibs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext,
|
void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext,
|
||||||
TArray<FDTFluxPursuitInfo>& OutPursuitNext, bool& BIsFocusTruncate,
|
TArray<FDTFluxPursuitInfo>& OutPursuitNext, bool& BIsFocusTruncate,
|
||||||
@ -73,9 +56,9 @@ void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFoc
|
|||||||
FDateTime MetricsStartFunction = FDateTime::UtcNow();
|
FDateTime MetricsStartFunction = FDateTime::UtcNow();
|
||||||
FDateTime CurrentTime = FDateTime::Now();
|
FDateTime CurrentTime = FDateTime::Now();
|
||||||
|
|
||||||
// UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== GetPursuit CALLED ==="));
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== GetPursuit CALLED ==="));
|
||||||
// UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("MaxSimultaneousPursuit: %d"), MaxSimultaneousPursuit);
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("MaxSimultaneousPursuit: %d"), MaxSimultaneousPursuit);
|
||||||
// UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Available groups: %d"), GroupedPursuit.Num());
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Available groups: %d"), GroupedPursuit.Num());
|
||||||
|
|
||||||
// BAd Parameter
|
// BAd Parameter
|
||||||
if (MaxSimultaneousPursuit <= 0)
|
if (MaxSimultaneousPursuit <= 0)
|
||||||
@ -111,19 +94,16 @@ void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFoc
|
|||||||
GroupedPursuit.RemoveAt(i);
|
GroupedPursuit.RemoveAt(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
OutPursuitFocusNext.Reset();
|
OutPursuitFocusNext.Reset();
|
||||||
OutPursuitNext.Reset();
|
OutPursuitNext.Reset();
|
||||||
|
|
||||||
// === VÉRIFICATION CRITIQUE : S'assurer qu'il reste des groupes après suppression ===
|
|
||||||
if (GroupedPursuit.IsEmpty())
|
if (GroupedPursuit.IsEmpty())
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("All groups were expired and removed - no groups available"));
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("All groups were expired and removed - no groups available"));
|
||||||
OutPursuitFocusNext.Reset();
|
OutPursuitFocusNext.Reset();
|
||||||
OutPursuitNext.Reset();
|
OutPursuitNext.Reset();
|
||||||
BIsFocusTruncate = false;
|
BIsFocusTruncate = false;
|
||||||
bIsSequenceDone = true; // Marquer la séquence comme terminée
|
bIsSequenceDone = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +160,6 @@ void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFoc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === LOGS DE RÉSUMÉ ===
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== PURSUIT RESULTS ==="));
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== PURSUIT RESULTS ==="));
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus: %d participants"), OutPursuitFocusNext.Num());
|
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("Next: %d participants"), OutPursuitNext.Num());
|
||||||
@ -322,7 +301,6 @@ bool UDTFluxPursuitManager::LaunchPursuitSequence()
|
|||||||
{
|
{
|
||||||
if (Pair.Value.StartTimeGlobal != FDateTime::MinValue() && Pair.Value.StartTimeGlobal != FDateTime::MaxValue())
|
if (Pair.Value.StartTimeGlobal != FDateTime::MinValue() && Pair.Value.StartTimeGlobal != FDateTime::MaxValue())
|
||||||
{
|
{
|
||||||
// récuperation de la ref de la valeur actuel de la fréquence dans la TMap Freq
|
|
||||||
int& CurrentFreq = StartTimeFrequency.FindOrAdd(Pair.Value.StartTimeGlobal, 0);
|
int& CurrentFreq = StartTimeFrequency.FindOrAdd(Pair.Value.StartTimeGlobal, 0);
|
||||||
CurrentFreq = Pair.Value.PursuitGroup.Num();
|
CurrentFreq = Pair.Value.PursuitGroup.Num();
|
||||||
if (CurrentFreq > MaxFrequency)
|
if (CurrentFreq > MaxFrequency)
|
||||||
@ -348,3 +326,25 @@ bool UDTFluxPursuitManager::LaunchPursuitSequence()
|
|||||||
OnPursuitSequenceReady.Broadcast(PursuitData);
|
OnPursuitSequenceReady.Broadcast(PursuitData);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void UDTFluxPursuitManager::DebugFocusNext(const TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext)
|
||||||
|
{
|
||||||
|
FString FocusBibs;
|
||||||
|
for (const auto& Pursuit : OutPursuitFocusNext)
|
||||||
|
{
|
||||||
|
FocusBibs += FString::Printf(TEXT("%d "), Pursuit.Bib);
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus Bibs: %s"), *FocusBibs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxPursuitManager::DebugOutPoursuitNext(const TArray<FDTFluxPursuitInfo>& OutPursuitNext)
|
||||||
|
{
|
||||||
|
FString NextBibs;
|
||||||
|
for (int32 i = 0; i < OutPursuitNext.Num(); i++)
|
||||||
|
{
|
||||||
|
NextBibs += FString::Printf(TEXT("%d "), OutPursuitNext[i].Bib);
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next Bibs: %s"), *NextBibs);
|
||||||
|
}
|
||||||
@ -41,6 +41,25 @@ public:
|
|||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnStageRankings OnStageRankings;
|
FOnStageRankings OnStageRankings;
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnContestRankings, const int, ContestId, FDTFluxContestRankings,
|
||||||
ContestRankings);
|
ContestRankings);
|
||||||
|
|
||||||
@ -59,135 +78,101 @@ public:
|
|||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem")
|
||||||
UDTFluxPursuitManager* PursuitManager = nullptr;
|
UDTFluxPursuitManager* PursuitManager = nullptr;
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSplitSensor, FDTFluxSplitSensorInfo, SplitSensorInfo);
|
||||||
bool GetContestRankings(const int ContestId, FDTFluxContestRankings& OutContestRankings);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
bool GetStageRankings(const int ContestId, const int StageId, FDTFluxStageRankings& OutStageRankings);
|
FOnSplitSensor OnSplitSensor;
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFinisher, FDTFluxSplitSensorInfo, SplitSensorInfo);
|
||||||
bool GetSplitRankings(const int ContestId, const int StageId, const int SplitId,
|
|
||||||
FDTFluxSplitRankings& OutSplitRankings);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
bool GetStageRankingsWithKey(const FDTFluxStageKey StageKey, FDTFluxStageRankings& OutStageRankings,
|
FOnFinisher OnFinisher;
|
||||||
const bool bShouldUseCached = true);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPreFinish, FDTFluxSplitSensorInfo, SplitSensorInfo);
|
||||||
bool GetSplitRankingsWithKey(const FDTFluxSplitKey SplitKey, FDTFluxSplitRankings& OutSplitRankings,
|
|
||||||
const bool bShouldUseCached = true);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
TArray<FGuid> TrackedRequestContestRankings(const TArray<int> ForContests, bool bEnableCache = true);
|
FOnPreFinish OnPreFinish;
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWinner, FDTFluxSplitSensorInfo, SplitSensorInfo);
|
||||||
TArray<FGuid> TrackedRequestStageRankings(const TArray<FDTFluxStageKey> ForStages, bool bEnableCache = true);
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
TArray<FGuid> TrackedRequestSplitRankings(const TArray<FDTFluxSplitKey> ForSplits, bool bEnableCache = true);
|
FOnWinner OnWinner;
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnParticipantTrackingReady, FDTFluxSplitHistory, SplitHistory);
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
const FDTFluxParticipant GetParticipant(int InBib);
|
FOnParticipantTrackingReady OnParticipantTrackingReady;
|
||||||
|
|
||||||
//TODO : this must be a ProjectSetting
|
//TODO : this must be a ProjectSetting
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem")
|
||||||
bool bShouldKeepRankings = true;
|
bool bShouldKeepRankings = true;
|
||||||
|
|
||||||
UFUNCTION()
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
void InitParticipantTracking(const int Bib, const int ContestId, const int StageId);
|
||||||
|
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
FGuid InitContestRankingsDisplay(const int ContestIds);
|
||||||
|
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
FGuid InitStageRankingsDisplay(const int ContestId, const int StageId);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
FGuid InitSplitRankingsDisplay(const int ContestId, const int StageId, const int SplitId);
|
||||||
|
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
bool GetStageRankingForBib(const int ContestId, const int StageId, const int Bib,
|
||||||
|
FDTFluxStageRanking& OutStageRankings);
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
bool GetSplitRankingForBib(const int ContestId, const int StageId, const int SplitId, const int Bib,
|
||||||
|
FDTFluxSplitRanking& OutSplitRankings);
|
||||||
|
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
bool GetContestRankings(const int ContestId, FDTFluxContestRankings& OutContestRankings);
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
bool GetStageRankings(const int ContestId, const int StageId, FDTFluxStageRankings& OutStageRankings);
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
bool GetSplitRankings(const int ContestId, const int StageId, const int SplitId,
|
||||||
|
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")
|
||||||
TArray<int> GetCurrentContestsId();
|
TArray<int> GetCurrentContestsId();
|
||||||
UFUNCTION()
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
TArray<FDTFluxContest> GetCurrentContests();
|
bool GetCurrentContests(TArray<FDTFluxContest>& OutContests);
|
||||||
UFUNCTION()
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
TArray<int> GetContestsIdForTime(const FDateTime Time) const;
|
TArray<int> GetContestsIdForTime(const FDateTime Time);
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
bool GetContestForId(const int Id, FDTFluxContest& OutContest);
|
bool GetContestForId(const int Id, FDTFluxContest& OutContest);
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
TArray<FDTFluxContest> GetContestsForTime(const FDateTime Time);
|
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()
|
||||||
void RequestRankingsForStages(const TArray<FDTFluxStage> RequestedStages) const;
|
bool GetStageDefinition(const FDTFluxStageKey StageKey, FDTFluxStage& OutStageDefinition);
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
TArray<FDTFluxContest> GetContests();
|
bool GetSplitDefinition(const FDTFluxSplitKey SplitKey, FDTFluxSplit& OutSplitDefinition);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void GetContest(const int ContestId, FDTFluxContest& OutContest)
|
void GetStage(const int ContestId, const int StageId, FDTFluxStage& OutStageDefinition);
|
||||||
{
|
|
||||||
OutContest = FDTFluxContest();
|
|
||||||
if (GetContestForId(ContestId, OutContest))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestId %d not found in ContestDefinition"), ContestId)
|
|
||||||
}
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
bool GetStageDefinition(const FDTFluxStageKey StageKey, FDTFluxStage& OutStageDefinition)
|
void GetSplit(const int ContestId, const int StageId, const int SplitId, FDTFluxSplit& OutSplitDefinition);
|
||||||
{
|
|
||||||
int ContestId = StageKey.ContestId;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
bool 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
void GetStage(const int ContestId, const int StageId, FDTFluxStage& OutStageDefinition)
|
|
||||||
{
|
|
||||||
if (GetStageDefinition(FDTFluxStageKey(ContestId, StageId),
|
|
||||||
OutStageDefinition))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OutStageDefinition = FDTFluxStage();
|
|
||||||
}
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
|
||||||
void GetSplit(const int ContestId, const int StageId, const int SplitId, FDTFluxSplit& OutSplitDefinition)
|
|
||||||
{
|
|
||||||
if (GetSplitDefinition(FDTFluxSplitKey(ContestId, StageId, SplitId),
|
|
||||||
OutSplitDefinition))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OutSplitDefinition = FDTFluxSplit();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// ~Subsystem Interface
|
// ~Subsystem Interface
|
||||||
@ -224,4 +209,11 @@ 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);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,6 +29,7 @@ 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);
|
||||||
|
|
||||||
|
|||||||
@ -363,8 +363,8 @@ 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;
|
||||||
}
|
}
|
||||||
@ -475,12 +475,14 @@ 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"),
|
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Split sensor info for bib %d in Contest %d, Stage %d, Split %d Rank [%i] Gap [%s] Time [%s]"),
|
||||||
NewSplitSensorInfo.Bib, NewSplitSensorInfo.ContestId, NewSplitSensorInfo.StageId,
|
NewSplitSensorInfo.Bib, NewSplitSensorInfo.ContestId, NewSplitSensorInfo.StageId,
|
||||||
NewSplitSensorInfo.SplitId);
|
NewSplitSensorInfo.SplitId, NewSplitSensorInfo.Rank, *NewSplitSensorInfo.Gap,*NewSplitSensorInfo.Time);
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
||||||
|
|||||||
@ -554,9 +554,6 @@ void UDTFluxNetworkSubsystem::ReconnectWs(const FName WsClientId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================================================================================
|
|
||||||
// MÉTHODES DE PARSING LEGACY (COMPATIBILITÉ TOTALE)
|
|
||||||
// ================================================================================================
|
|
||||||
|
|
||||||
void UDTFluxNetworkSubsystem::ParseTeamListResponse(FDTFluxServerResponse& Response)
|
void UDTFluxNetworkSubsystem::ParseTeamListResponse(FDTFluxServerResponse& Response)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -14,17 +14,21 @@ struct DTFLUXNETWORK_API FDTFluxSplitSensorItemResponse
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int Bib;
|
int Bib = -1;
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Type = "split-sensor-item";
|
FString Type = "split-sensor-item";
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int ContestID;
|
int ContestID =-1;
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int StageID;
|
int StageID =-1;
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int SplitID;
|
int SplitID = -1;
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Time = "-";
|
FString Time = "-";
|
||||||
|
UPROPERTY()
|
||||||
|
int Rank = -1;
|
||||||
|
UPROPERTY()
|
||||||
|
FString Gap = "";
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -17,20 +17,12 @@ class FDTFluxQueuedRequestManager;
|
|||||||
|
|
||||||
typedef TSharedPtr<FDTFluxWebSocketClient> FDTFluxWebSocketClientSP;
|
typedef TSharedPtr<FDTFluxWebSocketClient> FDTFluxWebSocketClientSP;
|
||||||
|
|
||||||
// ================================================================================================
|
|
||||||
// DELEGATES BLUEPRINT POUR LES REQUÊTES TRACKÉES
|
|
||||||
// ================================================================================================
|
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestCompleted, const FGuid&, RequestId,
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestCompleted, const FGuid&, RequestId,
|
||||||
EDTFluxApiDataType, RequestType, const FString&, ResponseData);
|
EDTFluxApiDataType, RequestType, const FString&, ResponseData);
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestFailed, const FGuid&, RequestId,
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestFailed, const FGuid&, RequestId,
|
||||||
EDTFluxApiDataType, RequestType, const FString&, ErrorMessage);
|
EDTFluxApiDataType, RequestType, const FString&, ErrorMessage);
|
||||||
|
|
||||||
// ================================================================================================
|
|
||||||
// DELEGATES LEGACY POUR LA COMPATIBILITÉ
|
|
||||||
// ================================================================================================
|
|
||||||
|
|
||||||
DECLARE_DELEGATE_OneParam(FOnRaceDataReceived, const FDTFluxRaceData& /*RaceDataDefinition*/);
|
DECLARE_DELEGATE_OneParam(FOnRaceDataReceived, const FDTFluxRaceData& /*RaceDataDefinition*/);
|
||||||
DECLARE_DELEGATE_OneParam(FOnTeamListReceived, const FDTFluxTeamListDefinition& /*TeamListDefinition*/);
|
DECLARE_DELEGATE_OneParam(FOnTeamListReceived, const FDTFluxTeamListDefinition& /*TeamListDefinition*/);
|
||||||
DECLARE_DELEGATE_OneParam(FOnStageRankingReceived, const FDTFluxStageRankings& /*StageRankings*/);
|
DECLARE_DELEGATE_OneParam(FOnStageRankingReceived, const FDTFluxStageRankings& /*StageRankings*/);
|
||||||
@ -42,25 +34,16 @@ DECLARE_DELEGATE_OneParam(FOnTeamStatusUpdateReceived, const FDTFluxTeamStatusUp
|
|||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWebSocketConnected);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWebSocketConnected);
|
||||||
|
|
||||||
// ================================================================================================
|
|
||||||
// NETWORK SUBSYSTEM - Interface UObject avec compatibilité Blueprint
|
|
||||||
// ================================================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subsystem réseau DTFlux avec support complet des requêtes trackées et compatibilité legacy
|
|
||||||
* Combine l'efficacité du RequestManager C++ avec l'interface Blueprint UObject
|
|
||||||
*/
|
|
||||||
UCLASS(Blueprintable)
|
UCLASS(Blueprintable)
|
||||||
class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem
|
class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// === ÉTAT DE CONNEXION ===
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Network")
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Network")
|
||||||
EDTFluxConnectionStatus WsStatus = EDTFluxConnectionStatus::Unset;
|
EDTFluxConnectionStatus WsStatus = EDTFluxConnectionStatus::Unset;
|
||||||
|
|
||||||
// === CONNEXION WEBSOCKET (Legacy) ===
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Se connecter au serveur WebSocket
|
* Se connecter au serveur WebSocket
|
||||||
@ -80,8 +63,6 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
||||||
void Reconnect();
|
void Reconnect();
|
||||||
|
|
||||||
// === REQUÊTES TRACKÉES (Nouveau système optimisé) ===
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Envoyer une requête trackée avec cache, timeout et retry
|
* Envoyer une requête trackée avec cache, timeout et retry
|
||||||
* @param RequestType Type de requête (ContestRanking, StageRanking, etc.)
|
* @param RequestType Type de requête (ContestRanking, StageRanking, etc.)
|
||||||
@ -128,82 +109,49 @@ public:
|
|||||||
int32 MaxRetries = 3
|
int32 MaxRetries = 3
|
||||||
);
|
);
|
||||||
|
|
||||||
// === ACCESSEURS BLUEPRINT POUR LES REQUÊTES TRACKÉES ===
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupérer une requête trackée par son ID
|
|
||||||
*/
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
bool GetTrackedRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const;
|
bool GetTrackedRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifier si une requête a reçu une réponse
|
|
||||||
*/
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
bool HasRequestReceivedResponse(const FGuid& RequestId) const;
|
bool HasRequestReceivedResponse(const FGuid& RequestId) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupérer les données de réponse d'une requête
|
|
||||||
*/
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
FString GetRequestResponseData(const FGuid& RequestId) const;
|
FString GetRequestResponseData(const FGuid& RequestId) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifier si une requête similaire est en attente
|
|
||||||
*/
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
bool IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
bool IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
||||||
int32 SplitId = -1) const;
|
int32 SplitId = -1) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Compter le nombre de requêtes en attente
|
|
||||||
*/
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
int32 GetPendingRequestCount() const;
|
int32 GetPendingRequestCount() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupérer les statistiques du gestionnaire de requêtes
|
|
||||||
*/
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
void GetRequestStatistics(int32& OutPending, int32& OutCompleted, int32& OutFailed) const;
|
void GetRequestStatistics(int32& OutPending, int32& OutCompleted, int32& OutFailed) const;
|
||||||
|
|
||||||
// === REQUÊTES LEGACY (Compatibilité totale) ===
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Envoyer une requête en mode legacy (pour compatibilité)
|
|
||||||
*/
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Legacy")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Legacy")
|
||||||
void SendRequest(const EDTFluxApiDataType RequestType, int InContestId = -1, int InStageId = -1,
|
void SendRequest(const EDTFluxApiDataType RequestType, int InContestId = -1, int InStageId = -1,
|
||||||
int InSplitId = -1);
|
int InSplitId = -1);
|
||||||
|
|
||||||
/**
|
|
||||||
* Envoyer un message brut via WebSocket
|
|
||||||
*/
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
|
||||||
void SendMessage(const FString& Message);
|
void SendMessage(const FString& Message);
|
||||||
|
|
||||||
// === EVENTS BLUEPRINT ===
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event déclenché lors de la connexion WebSocket
|
|
||||||
*/
|
|
||||||
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Network")
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Network")
|
||||||
FOnWebSocketConnected OnWebSocketConnected;
|
FOnWebSocketConnected OnWebSocketConnected;
|
||||||
|
|
||||||
/**
|
|
||||||
* Event déclenché quand une requête trackée se termine avec succès
|
|
||||||
*/
|
|
||||||
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests")
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests")
|
||||||
FOnDTFluxTrackedRequestCompleted OnTrackedRequestCompleted;
|
FOnDTFluxTrackedRequestCompleted OnTrackedRequestCompleted;
|
||||||
|
|
||||||
/**
|
|
||||||
* Event déclenché quand une requête trackée échoue
|
|
||||||
*/
|
|
||||||
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests")
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests")
|
||||||
FOnDTFluxTrackedRequestFailed OnTrackedRequestFailed;
|
FOnDTFluxTrackedRequestFailed OnTrackedRequestFailed;
|
||||||
|
|
||||||
// === DELEGATES LEGACY (Compatibilité totale) ===
|
|
||||||
|
|
||||||
FOnRaceDataReceived OnRaceDataReceived;
|
FOnRaceDataReceived OnRaceDataReceived;
|
||||||
FOnTeamListReceived OnTeamListReceived;
|
FOnTeamListReceived OnTeamListReceived;
|
||||||
FOnStageRankingReceived OnStageRankingReceived;
|
FOnStageRankingReceived OnStageRankingReceived;
|
||||||
@ -213,7 +161,6 @@ public:
|
|||||||
FOnTeamUpdateReceived OnTeamUpdateReceived;
|
FOnTeamUpdateReceived OnTeamUpdateReceived;
|
||||||
FOnTeamStatusUpdateReceived OnTeamStatusUpdateReceived;
|
FOnTeamStatusUpdateReceived OnTeamStatusUpdateReceived;
|
||||||
|
|
||||||
// Accesseurs pour la compatibilité legacy
|
|
||||||
FOnRaceDataReceived& OnReceivedRaceData() { return OnRaceDataReceived; }
|
FOnRaceDataReceived& OnReceivedRaceData() { return OnRaceDataReceived; }
|
||||||
FOnTeamListReceived& OnReceivedTeamList() { return OnTeamListReceived; }
|
FOnTeamListReceived& OnReceivedTeamList() { return OnTeamListReceived; }
|
||||||
FOnStageRankingReceived& OnReceivedStageRanking() { return OnStageRankingReceived; }
|
FOnStageRankingReceived& OnReceivedStageRanking() { return OnStageRankingReceived; }
|
||||||
@ -223,29 +170,21 @@ public:
|
|||||||
FOnTeamUpdateReceived& OnReceivedTeamUpdate() { return OnTeamUpdateReceived; }
|
FOnTeamUpdateReceived& OnReceivedTeamUpdate() { return OnTeamUpdateReceived; }
|
||||||
FOnTeamStatusUpdateReceived& OnReceivedTeamStatusUpdate() { return OnTeamStatusUpdateReceived; }
|
FOnTeamStatusUpdateReceived& OnReceivedTeamStatusUpdate() { return OnTeamStatusUpdateReceived; }
|
||||||
|
|
||||||
// === ACCESSEUR PUBLIC POUR LE REQUEST MANAGER ===
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accéder au gestionnaire de requêtes (pour usage avancé)
|
|
||||||
*/
|
|
||||||
TSharedPtr<FDTFluxQueuedRequestManager> GetRequestManager() const { return RequestManager; }
|
TSharedPtr<FDTFluxQueuedRequestManager> GetRequestManager() const { return RequestManager; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// === LIFECYCLE DU SUBSYSTEM ===
|
|
||||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||||
virtual void Deinitialize() override;
|
virtual void Deinitialize() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// === CONFIGURATION ===
|
|
||||||
FDTFluxWsSettings WsSettings;
|
FDTFluxWsSettings WsSettings;
|
||||||
|
|
||||||
// === CLIENTS RÉSEAU ===
|
|
||||||
FDTFluxWebSocketClientSP WsClient = nullptr;
|
FDTFluxWebSocketClientSP WsClient = nullptr;
|
||||||
|
|
||||||
// === REQUEST MANAGER C++ ===
|
|
||||||
TSharedPtr<FDTFluxQueuedRequestManager> RequestManager;
|
TSharedPtr<FDTFluxQueuedRequestManager> RequestManager;
|
||||||
|
|
||||||
// === GESTION DES ÉVÉNEMENTS WEBSOCKET ===
|
|
||||||
void RegisterWebSocketEvents();
|
void RegisterWebSocketEvents();
|
||||||
void UnregisterWebSocketEvents() const;
|
void UnregisterWebSocketEvents() const;
|
||||||
void OnWebSocketConnected_Subsystem();
|
void OnWebSocketConnected_Subsystem();
|
||||||
@ -254,14 +193,12 @@ private:
|
|||||||
void OnWebSocketMessageEvent_Subsystem(const FString& MessageString);
|
void OnWebSocketMessageEvent_Subsystem(const FString& MessageString);
|
||||||
void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent);
|
void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent);
|
||||||
|
|
||||||
// Handles pour les événements WebSocket
|
|
||||||
FDelegateHandle OnWsConnectedEventDelegateHandle;
|
FDelegateHandle OnWsConnectedEventDelegateHandle;
|
||||||
FDelegateHandle OnWsConnectionErrorEventDelegateHandle;
|
FDelegateHandle OnWsConnectionErrorEventDelegateHandle;
|
||||||
FDelegateHandle OnWsClosedEventDelegateHandle;
|
FDelegateHandle OnWsClosedEventDelegateHandle;
|
||||||
FDelegateHandle OnWsMessageEventDelegateHandle;
|
FDelegateHandle OnWsMessageEventDelegateHandle;
|
||||||
FDelegateHandle OnWsMessageSentEventDelegateHandle;
|
FDelegateHandle OnWsMessageSentEventDelegateHandle;
|
||||||
|
|
||||||
// === PARSING ET TRAITEMENT DES RÉPONSES ===
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Essayer de matcher une réponse à une requête trackée
|
* Essayer de matcher une réponse à une requête trackée
|
||||||
@ -292,7 +229,6 @@ private:
|
|||||||
void ParseSplitSensorResponse(FDTFluxServerResponse& Response);
|
void ParseSplitSensorResponse(FDTFluxServerResponse& Response);
|
||||||
EDTFluxResponseStatus ProcessPushMessage(FDTFluxServerResponse& Response);
|
EDTFluxResponseStatus ProcessPushMessage(FDTFluxServerResponse& Response);
|
||||||
|
|
||||||
// === CALLBACKS POUR LE REQUEST MANAGER ===
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback appelé quand une requête trackée se termine
|
* Callback appelé quand une requête trackée se termine
|
||||||
@ -304,7 +240,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
void OnRequestFailed_Internal(const FDTFluxTrackedRequest& FailedRequest);
|
void OnRequestFailed_Internal(const FDTFluxTrackedRequest& FailedRequest);
|
||||||
|
|
||||||
// === CONFIGURATION DYNAMIQUE ===
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback appelé quand les paramètres WebSocket changent
|
* Callback appelé quand les paramètres WebSocket changent
|
||||||
@ -317,7 +252,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
void ReconnectWs(const FName WsClientId);
|
void ReconnectWs(const FName WsClientId);
|
||||||
|
|
||||||
// === UTILITAIRES ===
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construire une adresse WebSocket complète
|
* Construire une adresse WebSocket complète
|
||||||
|
|||||||
@ -23,7 +23,7 @@ public class DTFluxProjectSettings : ModuleRules
|
|||||||
"DeveloperSettings",
|
"DeveloperSettings",
|
||||||
"DTFluxCore",
|
"DTFluxCore",
|
||||||
"Settings",
|
"Settings",
|
||||||
"DeveloperSettings"
|
"DeveloperSettings","AvalancheMedia"
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "DTFluxGeneralSettings.h"
|
#include "DTFluxGeneralSettings.h"
|
||||||
|
#include "Assets/DTFluxModelAsset.h"
|
||||||
|
|
||||||
#include "DTFluxProjectSettingsModule.h"
|
#include "DTFluxProjectSettingsModule.h"
|
||||||
|
|
||||||
@ -14,3 +15,17 @@ UDTFluxGeneralSettings::UDTFluxGeneralSettings()
|
|||||||
UE_LOG(logDTFluxProjectSettings, Log, TEXT("Category Name -> %s"), *GetCategoryName().ToString());
|
UE_LOG(logDTFluxProjectSettings, Log, TEXT("Category Name -> %s"), *GetCategoryName().ToString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UDTFluxGeneralSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||||
|
{
|
||||||
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||||
|
if (PropertyChangedEvent.Property &&
|
||||||
|
PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UDTFluxGeneralSettings, RemoteTargetRundown))
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("RemoteTargetRundown property changed to: %s"),
|
||||||
|
RemoteTargetRundown.IsNull() ? TEXT("None") : *RemoteTargetRundown.ToString());
|
||||||
|
|
||||||
|
OnRemoteRundownChanged.Broadcast(RemoteTargetRundown);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -3,10 +3,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "Assets/DTFluxModelAsset.h"
|
|
||||||
#include "Engine/DeveloperSettings.h"
|
#include "Engine/DeveloperSettings.h"
|
||||||
#include "DTFluxGeneralSettings.generated.h"
|
#include "DTFluxGeneralSettings.generated.h"
|
||||||
|
|
||||||
|
class UAvaRundown;
|
||||||
|
class UDTFluxModelAsset;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -20,5 +21,14 @@ public:
|
|||||||
UDTFluxGeneralSettings();
|
UDTFluxGeneralSettings();
|
||||||
UPROPERTY(Category="General", Config, EditAnywhere, BlueprintReadOnly, DisplayName="Datastorage File")
|
UPROPERTY(Category="General", Config, EditAnywhere, BlueprintReadOnly, DisplayName="Datastorage File")
|
||||||
TSoftObjectPtr<UDTFluxModelAsset> ModelAsset;
|
TSoftObjectPtr<UDTFluxModelAsset> ModelAsset;
|
||||||
|
UPROPERTY(Category="General|Remote HTTP", Config, EditAnywhere, BlueprintReadOnly, DisplayName="Rundown Remote Target")
|
||||||
|
TSoftObjectPtr<UAvaRundown> RemoteTargetRundown;
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||||
|
|
||||||
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnRemoteRundownChanged, const TSoftObjectPtr<UAvaRundown>& );
|
||||||
|
FOnRemoteRundownChanged OnRemoteRundownChanged;
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
31
Source/DTFluxRemote/DTFluxRemote.Build.cs
Normal file
31
Source/DTFluxRemote/DTFluxRemote.Build.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using UnrealBuildTool;
|
||||||
|
|
||||||
|
public class DTFluxRemote : ModuleRules
|
||||||
|
{
|
||||||
|
public DTFluxRemote(ReadOnlyTargetRules Target) : base(Target)
|
||||||
|
{
|
||||||
|
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||||
|
|
||||||
|
PublicDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"Core",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
PrivateDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"CoreUObject",
|
||||||
|
"Engine",
|
||||||
|
"Slate",
|
||||||
|
"SlateCore",
|
||||||
|
"HttpServer",
|
||||||
|
"JsonUtilities",
|
||||||
|
"Json",
|
||||||
|
"DTFluxProjectSettings",
|
||||||
|
"AvalancheMedia"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
Source/DTFluxRemote/Private/DTFluxRemoteMessage.cpp
Normal file
6
Source/DTFluxRemote/Private/DTFluxRemoteMessage.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
|
#include "DTFluxRemoteMessage.h"
|
||||||
|
|
||||||
|
|
||||||
19
Source/DTFluxRemote/Private/DTFluxRemoteModule.cpp
Normal file
19
Source/DTFluxRemote/Private/DTFluxRemoteModule.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "DTFluxRemoteModule.h"
|
||||||
|
|
||||||
|
#define LOCTEXT_NAMESPACE "FDTFluxRemoteModule"
|
||||||
|
|
||||||
|
DEFINE_LOG_CATEGORY(logDTFluxRemote);
|
||||||
|
|
||||||
|
void FDTFluxRemoteModule::StartupModule()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxRemoteModule::ShutdownModule()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef LOCTEXT_NAMESPACE
|
||||||
|
|
||||||
|
IMPLEMENT_MODULE(FDTFluxRemoteModule, DTFluxRemote)
|
||||||
549
Source/DTFluxRemote/Private/DTFluxRemoteSubsystem.cpp
Normal file
549
Source/DTFluxRemote/Private/DTFluxRemoteSubsystem.cpp
Normal file
@ -0,0 +1,549 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
|
#include "DTFluxRemoteSubsystem.h"
|
||||||
|
#include "DTFluxRemoteSubsystem.h"
|
||||||
|
|
||||||
|
#include "DTFluxGeneralSettings.h"
|
||||||
|
#include "DTFluxRemoteModule.h"
|
||||||
|
#include "DTFluxRemoteModule.h"
|
||||||
|
#include "HttpServerModule.h"
|
||||||
|
#include "IHttpRouter.h"
|
||||||
|
#include "Rundown/AvaRundown.h"
|
||||||
|
#include "Json.h"
|
||||||
|
#include "JsonObjectConverter.h"
|
||||||
|
#include "Engine/Engine.h"
|
||||||
|
#include "Misc/DateTime.h"
|
||||||
|
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||||
|
{
|
||||||
|
Super::Initialize(Collection);
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("DTFlux API Subsystem Initialized"));
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
// S'abonner aux changements de settings
|
||||||
|
if (UDTFluxGeneralSettings* Settings = GetMutableDefault<UDTFluxGeneralSettings>())
|
||||||
|
{
|
||||||
|
SettingsRundownChangedHandle = Settings->OnRemoteRundownChanged.AddUObject(
|
||||||
|
this, &UDTFluxRemoteSubsystem::OnSettingsRundownChanged
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
LoadRundownFromSettings();
|
||||||
|
// Auto-start server (optionnel)
|
||||||
|
StartHTTPServer(63350);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::Deinitialize()
|
||||||
|
{
|
||||||
|
StopHTTPServer();
|
||||||
|
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
// Se désabonner du delegate
|
||||||
|
if (UDTFluxGeneralSettings* Settings = GetMutableDefault<UDTFluxGeneralSettings>())
|
||||||
|
{
|
||||||
|
if (SettingsRundownChangedHandle.IsValid())
|
||||||
|
{
|
||||||
|
Settings->OnRemoteRundownChanged.Remove(SettingsRundownChangedHandle);
|
||||||
|
SettingsRundownChangedHandle.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Décharger proprement le rundown
|
||||||
|
UnloadCurrentRundown();
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("DTFlux API Subsystem Deinitialized"));
|
||||||
|
|
||||||
|
Super::Deinitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::StartHTTPServer(int32 Port)
|
||||||
|
{
|
||||||
|
if (bServerRunning)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Warning, TEXT("HTTP Server already running on port %d"), ServerPort);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerPort = Port;
|
||||||
|
|
||||||
|
// Get HTTP Server Module
|
||||||
|
FHttpServerModule& HttpServerModule = FHttpServerModule::Get();
|
||||||
|
|
||||||
|
// Create router
|
||||||
|
HttpRouter = HttpServerModule.GetHttpRouter(ServerPort);
|
||||||
|
|
||||||
|
if (!HttpRouter.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to create HTTP router for port %d"), ServerPort);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup routes
|
||||||
|
SetupRoutes();
|
||||||
|
|
||||||
|
// Start listening
|
||||||
|
HttpServerModule.StartAllListeners();
|
||||||
|
|
||||||
|
bServerRunning = true;
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("DTFlux HTTP API Server started on port %d"), ServerPort);
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Base URL: http://localhost:%d/dtflux/api/v1"), ServerPort);
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Available routes:"));
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT(" PUT /dtflux/api/v1/title"));
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT(" PUT /dtflux/api/v1/title-bib"));
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT(" PUT /dtflux/api/v1/commands"));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::StopHTTPServer()
|
||||||
|
{
|
||||||
|
if (!bServerRunning)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove route handlers
|
||||||
|
if (HttpRouter.IsValid())
|
||||||
|
{
|
||||||
|
HttpRouter->UnbindRoute(TitleRouteHandle);
|
||||||
|
HttpRouter->UnbindRoute(TitleBibRouteHandle);
|
||||||
|
HttpRouter->UnbindRoute(CommandsRouteHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop server
|
||||||
|
FHttpServerModule& HttpServerModule = FHttpServerModule::Get();
|
||||||
|
HttpServerModule.StopAllListeners();
|
||||||
|
|
||||||
|
HttpRouter.Reset();
|
||||||
|
bServerRunning = false;
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("DTFlux HTTP API Server stopped"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::IsHTTPServerRunning() const
|
||||||
|
{
|
||||||
|
return bServerRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::ResetPendingTitleData()
|
||||||
|
{
|
||||||
|
bHasPendingTitleRequest = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::ResetPendingBibData()
|
||||||
|
{
|
||||||
|
bHasPendingTitleBibRequest = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::SetupRoutes()
|
||||||
|
{
|
||||||
|
if (!HttpRouter.IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route: POST /dtflux/api/v1/title
|
||||||
|
TitleRouteHandle = HttpRouter->BindRoute(
|
||||||
|
FHttpPath(TEXT("/dtflux/api/v1/title")),
|
||||||
|
EHttpServerRequestVerbs::VERB_PUT,
|
||||||
|
FHttpRequestHandler::CreateUObject(this, &UDTFluxRemoteSubsystem::HandleTitleRequest)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Route: POST /dtflux/api/v1/title-bib
|
||||||
|
TitleBibRouteHandle = HttpRouter->BindRoute(
|
||||||
|
FHttpPath(TEXT("/dtflux/api/v1/title-bib")),
|
||||||
|
EHttpServerRequestVerbs::VERB_PUT,
|
||||||
|
FHttpRequestHandler::CreateUObject(this, &UDTFluxRemoteSubsystem::HandleTitleBibRequest)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Route: POST /dtflux/api/v1/commands
|
||||||
|
CommandsRouteHandle = HttpRouter->BindRoute(
|
||||||
|
FHttpPath(TEXT("/dtflux/api/v1/commands")),
|
||||||
|
EHttpServerRequestVerbs::VERB_PUT,
|
||||||
|
FHttpRequestHandler::CreateUObject(this, &UDTFluxRemoteSubsystem::HandleCommandsRequest)
|
||||||
|
);
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("HTTP routes configured successfully"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::HandleTitleRequest(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Received Title request"));
|
||||||
|
|
||||||
|
// Parse JSON
|
||||||
|
TSharedPtr<FJsonObject> JsonObject = ParseJsonFromRequest(Request);
|
||||||
|
if (!JsonObject.IsValid())
|
||||||
|
{
|
||||||
|
OnComplete(FHttpServerResponse::Create(CreateErrorResponse(TEXT("Invalid JSON")), TEXT("application/json")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse title data
|
||||||
|
FDTFluxRemoteTitleData TitleData;
|
||||||
|
if (!ParseTitleData(JsonObject, TitleData))
|
||||||
|
{
|
||||||
|
OnComplete(FHttpServerResponse::Create(CreateErrorResponse(TEXT("Invalid title data format")), TEXT("application/json")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast event (execute on Game Thread)
|
||||||
|
AsyncTask(ENamedThreads::GameThread, [this, TitleData]()
|
||||||
|
{
|
||||||
|
OnTitleReceived.Broadcast(TitleData);
|
||||||
|
if (RemotedRundown && RemotedRundown->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
PendingTitleData = TitleData;
|
||||||
|
bHasPendingTitleRequest = true;
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Playing page %i"), TitleData.RundownPageId);
|
||||||
|
RemotedRundown->PlayPage(TitleData.RundownPageId, EAvaRundownPagePlayType::PlayFromStart);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Warning, TEXT("No rundown loaded"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
OnComplete(FHttpServerResponse::Create(CreateSuccessResponse(TEXT("Title data received")), TEXT("application/json")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::HandleTitleBibRequest(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Received Title-Bib request"));
|
||||||
|
|
||||||
|
TSharedPtr<FJsonObject> JsonObject = ParseJsonFromRequest(Request);
|
||||||
|
if (!JsonObject.IsValid())
|
||||||
|
{
|
||||||
|
OnComplete(FHttpServerResponse::Create(CreateErrorResponse(TEXT("Invalid JSON")), TEXT("application/json")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FDTFluxRemoteBibData BibData;
|
||||||
|
if (!ParseTitleBibData(JsonObject, BibData))
|
||||||
|
{
|
||||||
|
OnComplete(FHttpServerResponse::Create(CreateErrorResponse(TEXT("Invalid title-bib data format")), TEXT("application/json")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncTask(ENamedThreads::GameThread, [this, BibData]()
|
||||||
|
{
|
||||||
|
OnTitleBibReceived.Broadcast(BibData);
|
||||||
|
});
|
||||||
|
|
||||||
|
OnComplete(FHttpServerResponse::Create(CreateSuccessResponse(TEXT("Title-bib data received")), TEXT("application/json")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::HandleCommandsRequest(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Received Commands request"));
|
||||||
|
|
||||||
|
TSharedPtr<FJsonObject> JsonObject = ParseJsonFromRequest(Request);
|
||||||
|
if (!JsonObject.IsValid())
|
||||||
|
{
|
||||||
|
OnComplete(FHttpServerResponse::Create(CreateErrorResponse(TEXT("Invalid JSON")), TEXT("application/json")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FDTFluxRemoteCommandData CommandData;
|
||||||
|
if (!ParseCommandData(JsonObject, CommandData))
|
||||||
|
{
|
||||||
|
OnComplete(FHttpServerResponse::Create(CreateErrorResponse(TEXT("Invalid command data format")), TEXT("application/json")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncTask(ENamedThreads::GameThread, [this, CommandData]()
|
||||||
|
{
|
||||||
|
OnCommandReceived.Broadcast(CommandData);
|
||||||
|
if (RemotedRundown && RemotedRundown->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
RemotedRundown->StopPage(CommandData.RundownPageId, EAvaRundownPageStopOptions::None, false);
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Stoping page %i"), CommandData.RundownPageId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Warning, TEXT("No rundown loaded"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
OnComplete(FHttpServerResponse::Create(CreateErrorResponse(TEXT("OK")), TEXT("application/json")));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<FJsonObject> UDTFluxRemoteSubsystem::ParseJsonFromRequest(const FHttpServerRequest& Request)
|
||||||
|
{
|
||||||
|
// Get request body
|
||||||
|
TArray<uint8> Body = Request.Body;
|
||||||
|
FString JsonString;
|
||||||
|
if (Body.Num() > 0)
|
||||||
|
{
|
||||||
|
// Ajouter un null terminator si nécessaire
|
||||||
|
if (Body.Last() != 0)
|
||||||
|
{
|
||||||
|
Body.Add(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonString = FString(UTF8_TO_TCHAR(reinterpret_cast<const char*>(Body.GetData())));
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRemote, Verbose, TEXT("Received JSON: %s"), *JsonString);
|
||||||
|
|
||||||
|
// Parse JSON
|
||||||
|
TSharedPtr<FJsonObject> JsonObject;
|
||||||
|
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
|
||||||
|
|
||||||
|
if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to parse JSON: %s"), *JsonString);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString UDTFluxRemoteSubsystem::CreateSuccessResponse(const FString& Message)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonObject> ResponseObject = MakeShareable(new FJsonObject);
|
||||||
|
ResponseObject->SetBoolField(TEXT("success"), true);
|
||||||
|
ResponseObject->SetStringField(TEXT("message"), Message);
|
||||||
|
ResponseObject->SetStringField(TEXT("timestamp"), FDateTime::Now().ToIso8601());
|
||||||
|
|
||||||
|
FString OutputString;
|
||||||
|
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
|
||||||
|
FJsonSerializer::Serialize(ResponseObject.ToSharedRef(), Writer);
|
||||||
|
|
||||||
|
return OutputString;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString UDTFluxRemoteSubsystem::CreateErrorResponse(const FString& Error, int32 Code)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonObject> ResponseObject = MakeShareable(new FJsonObject);
|
||||||
|
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||||
|
ResponseObject->SetStringField(TEXT("error"), Error);
|
||||||
|
ResponseObject->SetNumberField(TEXT("code"), Code);
|
||||||
|
ResponseObject->SetStringField(TEXT("timestamp"), FDateTime::Now().ToIso8601());
|
||||||
|
|
||||||
|
FString OutputString;
|
||||||
|
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
|
||||||
|
FJsonSerializer::Serialize(ResponseObject.ToSharedRef(), Writer);
|
||||||
|
|
||||||
|
return OutputString;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::ParseTitleData(const TSharedPtr<FJsonObject>& JsonObject, FDTFluxRemoteTitleData& OutData)
|
||||||
|
{
|
||||||
|
if (!JsonObject.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Invalid JSON object for %s"), TEXT("FDTFluxRemoteTitleData"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FJsonObjectConverter::JsonObjectToUStruct<FDTFluxRemoteTitleData>(JsonObject.ToSharedRef(), &OutData))
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Successfully parsed %s"), TEXT("FDTFluxRemoteTitleData"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to convert JSON to %s struct"),TEXT("FDTFluxRemoteTitleData"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::ParseTitleBibData(const TSharedPtr<FJsonObject>& JsonObject, FDTFluxRemoteBibData& OutData)
|
||||||
|
{
|
||||||
|
if (!JsonObject.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Invalid JSON object for %s"), TEXT("FDTFluxRemoteBibData"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FJsonObjectConverter::JsonObjectToUStruct<FDTFluxRemoteBibData>(JsonObject.ToSharedRef(), &OutData))
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Successfully parsed %s"), TEXT("FDTFluxRemoteBibData"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to convert JSON to %s struct"),TEXT("FDTFluxRemoteBibData"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::ParseCommandData(const TSharedPtr<FJsonObject>& JsonObject, FDTFluxRemoteCommandData& OutData)
|
||||||
|
{
|
||||||
|
if (!JsonObject.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Invalid JSON object for %s"), TEXT("FDTFluxRemoteCommandData"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FJsonObjectConverter::JsonObjectToUStruct<FDTFluxRemoteCommandData>(JsonObject.ToSharedRef(), &OutData))
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Successfully parsed %s"), TEXT("FDTFluxRemoteCommandData"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to convert JSON to %s struct"),TEXT("FDTFluxRemoteCommandData"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::UnloadCurrentRundown()
|
||||||
|
{
|
||||||
|
if (RemotedRundown)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Unloading current rundown"));
|
||||||
|
// Ici vous pouvez ajouter une logique de nettoyage si nécessaire
|
||||||
|
// Par exemple : RemotedRundown->StopAllPages();
|
||||||
|
RemotedRundown = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxRemoteSubsystem::LoadRundownFromSettings()
|
||||||
|
{
|
||||||
|
const UDTFluxGeneralSettings* Settings = GetDefault<UDTFluxGeneralSettings>();
|
||||||
|
if (!Settings)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Warning, TEXT("Cannot access DTFlux settings"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSoftObjectPtr<UAvaRundown> RundownAsset = Settings->RemoteTargetRundown;
|
||||||
|
|
||||||
|
if (RundownAsset.IsNull())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("No rundown specified in settings"));
|
||||||
|
UnloadCurrentRundown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RemotedRundown && RemotedRundown == RundownAsset.LoadSynchronous())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Rundown already loaded: %s"), *RundownAsset.ToString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Décharger l'ancien rundown d'abord
|
||||||
|
UnloadCurrentRundown();
|
||||||
|
RundownAsset = RundownAsset.LoadSynchronous();
|
||||||
|
// Charger le nouveau rundown
|
||||||
|
if ( RundownAsset.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Successfully loaded rundown from settings: %s"), *RundownAsset.ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to load rundown from settings: %s"), *RundownAsset.ToString());
|
||||||
|
}
|
||||||
|
LoadRundown(RundownAsset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::LoadRundown(const TSoftObjectPtr<UAvaRundown>& RundownAsset)
|
||||||
|
{
|
||||||
|
if (RundownAsset.IsNull())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Warning, TEXT("Cannot load rundown: asset is null"));
|
||||||
|
UnloadCurrentRundown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Charger le rundown de manière synchrone
|
||||||
|
UAvaRundown* LoadedRundown = RundownAsset.LoadSynchronous();
|
||||||
|
|
||||||
|
// Vérifier si le rundown est déjà chargé
|
||||||
|
if (RemotedRundown && RemotedRundown == LoadedRundown)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Rundown already loaded: %s"), *RundownAsset.ToString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Décharger l'ancien rundown d'abord
|
||||||
|
UnloadCurrentRundown();
|
||||||
|
|
||||||
|
// Assigner le nouveau rundown
|
||||||
|
RemotedRundown = LoadedRundown;
|
||||||
|
|
||||||
|
// Vérifier que le chargement a réussi
|
||||||
|
if (RemotedRundown && RemotedRundown->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
RemotedRundown->InitializePlaybackContext();
|
||||||
|
UE_LOG(logDTFluxRemote, Log, TEXT("Successfully loaded rundown: %s"), *RundownAsset.ToString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRemote, Error, TEXT("Failed to load rundown: %s"), *RundownAsset.ToString());
|
||||||
|
RemotedRundown = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
void UDTFluxRemoteSubsystem::OnSettingsRundownChanged(const TSoftObjectPtr<UAvaRundown>& NewRundown)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Manual processing functions for testing
|
||||||
|
bool UDTFluxRemoteSubsystem::ProcessTitleData(const FString& JsonString)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonObject> JsonObject;
|
||||||
|
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
|
||||||
|
|
||||||
|
if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FDTFluxRemoteTitleData TitleData;
|
||||||
|
if (ParseTitleData(JsonObject, TitleData))
|
||||||
|
{
|
||||||
|
OnTitleReceived.Broadcast(TitleData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::ProcessTitleBibData(const FString& JsonString)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonObject> JsonObject;
|
||||||
|
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
|
||||||
|
|
||||||
|
if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FDTFluxRemoteBibData TitleBibData;
|
||||||
|
if (ParseTitleBibData(JsonObject, TitleBibData))
|
||||||
|
{
|
||||||
|
OnTitleBibReceived.Broadcast(TitleBibData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxRemoteSubsystem::ProcessCommandData(const FString& JsonString)
|
||||||
|
{
|
||||||
|
TSharedPtr<FJsonObject> JsonObject;
|
||||||
|
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
|
||||||
|
|
||||||
|
if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FDTFluxRemoteCommandData CommandData;
|
||||||
|
if (ParseCommandData(JsonObject, CommandData))
|
||||||
|
{
|
||||||
|
OnCommandReceived.Broadcast(CommandData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
220
Source/DTFluxRemote/Private/DTFluxRemotedLevelController.cpp
Normal file
220
Source/DTFluxRemote/Private/DTFluxRemotedLevelController.cpp
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
// DTFluxRemoteActor.cpp
|
||||||
|
#include "DTFluxRemotedLevelController.h"
|
||||||
|
#include "DTFluxRemoteSubsystem.h"
|
||||||
|
#include "Engine/Engine.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
|
|
||||||
|
ADTFluxRemotedLevelController::ADTFluxRemotedLevelController()
|
||||||
|
{
|
||||||
|
PrimaryActorTick.bCanEverTick = false;
|
||||||
|
RemoteSubsystem = nullptr;
|
||||||
|
bEventsBound = false;
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Constructor called"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::PostInitializeComponents()
|
||||||
|
{
|
||||||
|
Super::PostInitializeComponents();
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: PostInitializeComponents called"));
|
||||||
|
|
||||||
|
// Essayer de bind dès que possible
|
||||||
|
InitializeSubsystemBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::BeginPlay()
|
||||||
|
{
|
||||||
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: BeginPlay called"));
|
||||||
|
|
||||||
|
// S'assurer que le binding est fait (au cas où PostInitializeComponents aurait échoué)
|
||||||
|
if (!bEventsBound)
|
||||||
|
{
|
||||||
|
InitializeSubsystemBinding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::InitializeSubsystemBinding()
|
||||||
|
{
|
||||||
|
// Éviter le double binding
|
||||||
|
if (bEventsBound)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Events already bound, skipping"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer le subsystem
|
||||||
|
if (UWorld* World = GetWorld())
|
||||||
|
{
|
||||||
|
RemoteSubsystem = GEngine->GetEngineSubsystem<UDTFluxRemoteSubsystem>();
|
||||||
|
|
||||||
|
if (RemoteSubsystem)
|
||||||
|
{
|
||||||
|
// Bind les events du subsystem
|
||||||
|
RemoteSubsystem->OnTitleReceived.AddDynamic(
|
||||||
|
this, &ADTFluxRemotedLevelController::OnTitleDataReceived
|
||||||
|
);
|
||||||
|
RemoteSubsystem->OnTitleBibReceived.AddDynamic(
|
||||||
|
this, &ADTFluxRemotedLevelController::OnTitleBibDataReceived
|
||||||
|
);
|
||||||
|
|
||||||
|
RemoteSubsystem->OnCommandReceived.AddDynamic(
|
||||||
|
this, &ADTFluxRemotedLevelController::OnCommandDataReceived
|
||||||
|
);
|
||||||
|
|
||||||
|
bEventsBound = true;
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Successfully bound to subsystem events"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("DTFluxRemoteActor: DTFluxRemoteSubsystem not available yet"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("DTFluxRemoteActor: World not available yet"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::EnsureSubsystemBinding()
|
||||||
|
{
|
||||||
|
if (!bEventsBound)
|
||||||
|
{
|
||||||
|
InitializeSubsystemBinding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||||
|
{
|
||||||
|
// Unbind les events pour éviter les fuites mémoire
|
||||||
|
if (RemoteSubsystem && bEventsBound)
|
||||||
|
{
|
||||||
|
if (TitleReceivedHandle.IsValid())
|
||||||
|
{
|
||||||
|
RemoteSubsystem->OnTitleReceived.RemoveDynamic(this, &ADTFluxRemotedLevelController::OnTitleDataReceived);
|
||||||
|
TitleReceivedHandle.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TitleBibReceivedHandle.IsValid())
|
||||||
|
{
|
||||||
|
RemoteSubsystem->OnTitleBibReceived.RemoveDynamic(this, &ADTFluxRemotedLevelController::OnTitleBibDataReceived);
|
||||||
|
TitleBibReceivedHandle.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CommandReceivedHandle.IsValid())
|
||||||
|
{
|
||||||
|
RemoteSubsystem->OnCommandReceived.RemoveDynamic(this, &ADTFluxRemotedLevelController::OnCommandDataReceived);
|
||||||
|
CommandReceivedHandle.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bEventsBound = false;
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Unbound from subsystem events"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Super::EndPlay(EndPlayReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::OnTitleDataReceived(const FDTFluxRemoteTitleData& TitleData)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Received Title Data - %s %s (RundownPageId: %d)"),
|
||||||
|
*TitleData.FirstName, *TitleData.LastName, TitleData.RundownPageId);
|
||||||
|
|
||||||
|
// Broadcast l'event Blueprint
|
||||||
|
OnTitleReceived.Broadcast(TitleData);
|
||||||
|
|
||||||
|
// Appeler l'event Blueprint implémentable
|
||||||
|
BP_OnTitleDataReceived(TitleData);
|
||||||
|
|
||||||
|
// Appeler la fonction virtuelle C++ (peut être overridée dans les classes dérivées)
|
||||||
|
HandleTitleData(TitleData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::OnTitleBibDataReceived(const FDTFluxRemoteBibData& BibData)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Received Title Bib Data - Bib: %d"), BibData.Bib);
|
||||||
|
|
||||||
|
// Broadcast l'event Blueprint
|
||||||
|
OnTitleBibReceived.Broadcast(BibData);
|
||||||
|
|
||||||
|
// Appeler l'event Blueprint implémentable
|
||||||
|
BP_OnTitleBibDataReceived(BibData);
|
||||||
|
|
||||||
|
// Appeler la fonction virtuelle C++ (peut être overridée dans les classes dérivées)
|
||||||
|
HandleTitleBibData(BibData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::OnCommandDataReceived(const FDTFluxRemoteCommandData& CommandData)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("DTFluxRemoteActor: Received Command Data - Type: %s, : RundownPageId %i"),
|
||||||
|
*CommandData.Type, CommandData.RundownPageId);
|
||||||
|
|
||||||
|
// Broadcast l'event Blueprint
|
||||||
|
OnCommandReceived.Broadcast(CommandData);
|
||||||
|
|
||||||
|
// Appeler l'event Blueprint implémentable
|
||||||
|
BP_OnCommandDataReceived(CommandData);
|
||||||
|
|
||||||
|
// Appeler la fonction virtuelle C++ (peut être overridée dans les classes dérivées)
|
||||||
|
HandleCommandData(CommandData);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ADTFluxRemotedLevelController::IsSubsystemAvailable() const
|
||||||
|
{
|
||||||
|
return RemoteSubsystem && RemoteSubsystem->IsValidLowLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ADTFluxRemotedLevelController::IsHTTPServerRunning() const
|
||||||
|
{
|
||||||
|
if (RemoteSubsystem)
|
||||||
|
{
|
||||||
|
return RemoteSubsystem->IsHTTPServerRunning();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::StartHTTPServer(int32 Port)
|
||||||
|
{
|
||||||
|
if (RemoteSubsystem)
|
||||||
|
{
|
||||||
|
RemoteSubsystem->StartHTTPServer(Port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("DTFluxRemoteActor: Cannot start HTTP server - subsystem not available"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::StopHTTPServer()
|
||||||
|
{
|
||||||
|
if (RemoteSubsystem)
|
||||||
|
{
|
||||||
|
RemoteSubsystem->StopHTTPServer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("DTFluxRemoteActor: Cannot stop HTTP server - subsystem not available"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implémentations par défaut des fonctions virtuelles C++
|
||||||
|
void ADTFluxRemotedLevelController::HandleTitleData_Implementation(const FDTFluxRemoteTitleData& TitleData)
|
||||||
|
{
|
||||||
|
// Implémentation par défaut - peut être overridée dans les classes dérivées
|
||||||
|
UE_LOG(LogTemp, Verbose, TEXT("DTFluxRemoteActor: Handling Title Data (default implementation)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::HandleTitleBibData_Implementation(const FDTFluxRemoteBibData& BibData)
|
||||||
|
{
|
||||||
|
// Implémentation par défaut - peut être overridée dans les classes dérivées
|
||||||
|
UE_LOG(LogTemp, Verbose, TEXT("DTFluxRemoteActor: Handling Title Bib Data (default implementation)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADTFluxRemotedLevelController::HandleCommandData_Implementation(const FDTFluxRemoteCommandData& CommandData)
|
||||||
|
{
|
||||||
|
// Implémentation par défaut - peut être overridée dans les classes dérivées
|
||||||
|
UE_LOG(LogTemp, Verbose, TEXT("DTFluxRemoteActor: Handling Command Data (default implementation)"));
|
||||||
|
}
|
||||||
76
Source/DTFluxRemote/Public/DTFluxRemoteMessage.h
Normal file
76
Source/DTFluxRemote/Public/DTFluxRemoteMessage.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "DTFluxRemoteMessage.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FDTFluxRemoteBasicData
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
|
FDateTime UpdateAt = FDateTime::Now();
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
|
int RundownPageId = -1;
|
||||||
|
FDTFluxRemoteBasicData() = default;
|
||||||
|
FDTFluxRemoteBasicData(const FDateTime& InUpdateAt): UpdateAt(InUpdateAt){};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FDTFluxRemoteTitleData : public FDTFluxRemoteBasicData
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
|
FString FirstName = "";
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
|
FString LastName = "";
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
|
FString Function1 = "";
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
|
FString Function2 = "";
|
||||||
|
|
||||||
|
|
||||||
|
FDTFluxRemoteTitleData() = default;
|
||||||
|
FDTFluxRemoteTitleData(const FString InFirstName, const FString InLastName, const FString InFunction1, const FString InFunction2):
|
||||||
|
FirstName(InFirstName), LastName(InLastName), Function1(InFunction1), Function2(InFunction2){};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FDTFluxRemoteBibData : public FDTFluxRemoteBasicData
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
|
int Bib = -1;
|
||||||
|
|
||||||
|
FDTFluxRemoteBibData() = default;
|
||||||
|
FDTFluxRemoteBibData(int InBib): Bib(InBib){};
|
||||||
|
};
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FDTFluxRemoteCommandData : public FDTFluxRemoteBasicData
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
FDTFluxRemoteCommandData() = default;
|
||||||
|
|
||||||
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="DTFlux|Remote")
|
||||||
|
FString Type = "Stop";
|
||||||
|
|
||||||
|
|
||||||
|
FDTFluxRemoteCommandData(FString InType):
|
||||||
|
Type(InType){};
|
||||||
|
};
|
||||||
|
|
||||||
13
Source/DTFluxRemote/Public/DTFluxRemoteModule.h
Normal file
13
Source/DTFluxRemote/Public/DTFluxRemoteModule.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Modules/ModuleManager.h"
|
||||||
|
|
||||||
|
DTFLUXREMOTE_API DECLARE_LOG_CATEGORY_EXTERN(logDTFluxRemote, Log, All);
|
||||||
|
|
||||||
|
class DTFLUXREMOTE_API FDTFluxRemoteModule : public IModuleInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void StartupModule() override;
|
||||||
|
virtual void ShutdownModule() override;
|
||||||
|
};
|
||||||
117
Source/DTFluxRemote/Public/DTFluxRemoteSubsystem.h
Normal file
117
Source/DTFluxRemote/Public/DTFluxRemoteSubsystem.h
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Subsystems/EngineSubsystem.h"
|
||||||
|
#include "DTFluxRemoteMessage.h"
|
||||||
|
#include "HttpRouteHandle.h"
|
||||||
|
#include "IHttpRouter.h"
|
||||||
|
#include "DTFluxRemoteSubsystem.generated.h"
|
||||||
|
|
||||||
|
class UAvaRundown;
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTitleReceived, const FDTFluxRemoteTitleData&, TitleData);
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTitleBibReceived, const FDTFluxRemoteBibData&, TitleBibData);
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCommandReceived, const FDTFluxRemoteCommandData&, CommandData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType, Category="DTFlux|Remote")
|
||||||
|
class DTFLUXREMOTE_API UDTFluxRemoteSubsystem : public UEngineSubsystem
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
public:
|
||||||
|
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||||
|
virtual void Deinitialize() override;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux API")
|
||||||
|
FOnTitleReceived OnTitleReceived;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux API")
|
||||||
|
FOnTitleBibReceived OnTitleBibReceived;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux API")
|
||||||
|
FOnCommandReceived OnCommandReceived;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API")
|
||||||
|
bool StartHTTPServer(int32 Port = 63350);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API")
|
||||||
|
void StopHTTPServer();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API", BlueprintPure)
|
||||||
|
bool IsHTTPServerRunning() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API", BlueprintPure)
|
||||||
|
int32 GetServerPort() const { return ServerPort; }
|
||||||
|
|
||||||
|
// Manual data processing (for testing)
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API")
|
||||||
|
bool ProcessTitleData(const FString& JsonString);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API")
|
||||||
|
bool ProcessTitleBibData(const FString& JsonString);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API")
|
||||||
|
bool ProcessCommandData(const FString& JsonString);
|
||||||
|
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DTFlux API")
|
||||||
|
bool bHasPendingTitleRequest = false;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DTFlux API")
|
||||||
|
bool bHasPendingTitleBibRequest = false;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DTFlux API")
|
||||||
|
FDTFluxRemoteTitleData PendingTitleData;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DTFlux API")
|
||||||
|
FDTFluxRemoteBibData PendingTitleBibData;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API")
|
||||||
|
void ResetPendingTitleData();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux API")
|
||||||
|
void ResetPendingBibData();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetupRoutes();
|
||||||
|
|
||||||
|
bool HandleTitleRequest(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete);
|
||||||
|
bool HandleTitleBibRequest(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete);
|
||||||
|
bool HandleCommandsRequest(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete);
|
||||||
|
|
||||||
|
TSharedPtr<FJsonObject> ParseJsonFromRequest(const FHttpServerRequest& Request);
|
||||||
|
FString CreateSuccessResponse(const FString& Message = TEXT("Success"));
|
||||||
|
FString CreateErrorResponse(const FString& Error, int32 Code = 400);
|
||||||
|
|
||||||
|
bool ParseTitleData(const TSharedPtr<FJsonObject>& JsonObject, FDTFluxRemoteTitleData& OutData);
|
||||||
|
bool ParseTitleBibData(const TSharedPtr<FJsonObject>& JsonObject, FDTFluxRemoteBibData& OutData);
|
||||||
|
bool ParseCommandData(const TSharedPtr<FJsonObject>& JsonObject, FDTFluxRemoteCommandData& OutData);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TSharedPtr<IHttpRouter> HttpRouter;
|
||||||
|
TSoftObjectPtr<UAvaRundown> RemotedRundown;
|
||||||
|
int32 ServerPort = 63350;
|
||||||
|
bool bServerRunning = false;
|
||||||
|
|
||||||
|
FHttpRouteHandle TitleRouteHandle;
|
||||||
|
FHttpRouteHandle TitleBibRouteHandle;
|
||||||
|
FHttpRouteHandle CommandsRouteHandle;
|
||||||
|
|
||||||
|
void UnloadCurrentRundown();
|
||||||
|
void LoadRundownFromSettings();
|
||||||
|
bool LoadRundown(const TSoftObjectPtr<UAvaRundown>& RundownAsset);
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
FDelegateHandle SettingsRundownChangedHandle;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
// Callback pour les changements de settings
|
||||||
|
UFUNCTION()
|
||||||
|
void OnSettingsRundownChanged(const TSoftObjectPtr<UAvaRundown>& NewRundown);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
94
Source/DTFluxRemote/Public/DTFluxRemotedLevelController.h
Normal file
94
Source/DTFluxRemote/Public/DTFluxRemotedLevelController.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// DTFluxRemotedLevelController.h
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "GameFramework/Actor.h"
|
||||||
|
#include "DTFluxRemoteSubsystem.h"
|
||||||
|
#include "DTFluxRemotedLevelController.generated.h"
|
||||||
|
|
||||||
|
UCLASS(BlueprintType, Blueprintable)
|
||||||
|
class DTFLUXREMOTE_API ADTFluxRemotedLevelController : public AActor
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
ADTFluxRemotedLevelController();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void PostInitializeComponents() override;
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||||
|
|
||||||
|
// Subsystem et binding
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux")
|
||||||
|
UDTFluxRemoteSubsystem* RemoteSubsystem;
|
||||||
|
|
||||||
|
FDelegateHandle TitleReceivedHandle;
|
||||||
|
FDelegateHandle TitleBibReceivedHandle;
|
||||||
|
FDelegateHandle CommandReceivedHandle;
|
||||||
|
bool bEventsBound;
|
||||||
|
|
||||||
|
// Fonctions de binding
|
||||||
|
void InitializeSubsystemBinding();
|
||||||
|
|
||||||
|
// ✅ CORRECTION : Callbacks avec UFUNCTION()
|
||||||
|
UFUNCTION()
|
||||||
|
void OnTitleDataReceived(const FDTFluxRemoteTitleData& TitleData);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnTitleBibDataReceived(const FDTFluxRemoteBibData& BibData);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnCommandDataReceived(const FDTFluxRemoteCommandData& CommandData);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Events Blueprint-friendly
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux Events")
|
||||||
|
FOnTitleReceived OnTitleReceived;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux Events")
|
||||||
|
FOnTitleBibReceived OnTitleBibReceived;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "DTFlux Events")
|
||||||
|
FOnCommandReceived OnCommandReceived;
|
||||||
|
|
||||||
|
// Fonctions utilitaires
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
bool IsSubsystemAvailable() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
bool IsHTTPServerRunning() const;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
void StartHTTPServer(int32 Port = 63350);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
void StopHTTPServer();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
void EnsureSubsystemBinding();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Events Blueprint implémentables
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category = "DTFlux Events")
|
||||||
|
void BP_OnTitleDataReceived(const FDTFluxRemoteTitleData& TitleData);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category = "DTFlux Events")
|
||||||
|
void BP_OnTitleBibDataReceived(const FDTFluxRemoteBibData& BibData);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category = "DTFlux Events")
|
||||||
|
void BP_OnCommandDataReceived(const FDTFluxRemoteCommandData& CommandData);
|
||||||
|
|
||||||
|
// Fonctions virtuelles C++
|
||||||
|
UFUNCTION(BlueprintNativeEvent, Category = "DTFlux Events")
|
||||||
|
void HandleTitleData(const FDTFluxRemoteTitleData& TitleData);
|
||||||
|
virtual void HandleTitleData_Implementation(const FDTFluxRemoteTitleData& TitleData);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintNativeEvent, Category = "DTFlux Events")
|
||||||
|
void HandleTitleBibData(const FDTFluxRemoteBibData& BibData);
|
||||||
|
virtual void HandleTitleBibData_Implementation(const FDTFluxRemoteBibData& BibData);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintNativeEvent, Category = "DTFlux Events")
|
||||||
|
void HandleCommandData(const FDTFluxRemoteCommandData& CommandData);
|
||||||
|
virtual void HandleCommandData_Implementation(const FDTFluxRemoteCommandData& CommandData);
|
||||||
|
};
|
||||||
@ -9,7 +9,7 @@ public class DTFluxUtilities : ModuleRules
|
|||||||
PublicDependencyModuleNames.AddRange(
|
PublicDependencyModuleNames.AddRange(
|
||||||
new string[]
|
new string[]
|
||||||
{
|
{
|
||||||
"Core",
|
"Core"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -22,6 +22,7 @@ public class DTFluxUtilities : ModuleRules
|
|||||||
"SlateCore",
|
"SlateCore",
|
||||||
"DTFluxCore",
|
"DTFluxCore",
|
||||||
"DTFluxCoreSubsystem",
|
"DTFluxCoreSubsystem",
|
||||||
|
"AvalancheMedia",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
67
Source/DTFluxUtilities/Private/DTFluxUtils.cpp
Normal file
67
Source/DTFluxUtilities/Private/DTFluxUtils.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
|
#include "DTFluxUtils.h"
|
||||||
|
|
||||||
|
#include "DTFluxCoreSubsystem.h"
|
||||||
|
#include "DTFluxUtilitiesModule.h"
|
||||||
|
|
||||||
|
FText UFTDFluxUtils::GetFormatedName(const int& Bib, const int MaxChar, const FString Separator,
|
||||||
|
const FString OverFlowChar)
|
||||||
|
{
|
||||||
|
UDTFluxCoreSubsystem* CoreSubsystem = GEngine->GetEngineSubsystem<UDTFluxCoreSubsystem>();
|
||||||
|
if (CoreSubsystem != nullptr)
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
const FString Separator,
|
||||||
|
const FString OverFlowChar)
|
||||||
|
{
|
||||||
|
return Participant.GetFormattedNameText(MaxChar, Separator, 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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<FDTFluxSplitSensorInfo> UFTDFluxUtils::SortSplitRankingsByRank(const TArray<FDTFluxSplitSensorInfo>& Rankings)
|
||||||
|
{
|
||||||
|
TArray<FDTFluxSplitSensorInfo> CopyRankings = Rankings;
|
||||||
|
CopyRankings.Sort([](const FDTFluxSplitSensorInfo& A, const FDTFluxSplitSensorInfo& B)
|
||||||
|
{
|
||||||
|
return A.Rank < B.Rank;
|
||||||
|
});
|
||||||
|
return CopyRankings;
|
||||||
|
}
|
||||||
@ -1,21 +0,0 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
|
||||||
|
|
||||||
|
|
||||||
#include "FTDFluxUtils.h"
|
|
||||||
|
|
||||||
#include "DTFluxCoreSubsystem.h"
|
|
||||||
|
|
||||||
FText UFTDFluxUtils::GetFormatedName(const int& Bib, const int MaxChar, const FString Separator,
|
|
||||||
const FString OverFlowChar)
|
|
||||||
{
|
|
||||||
UDTFluxCoreSubsystem* CoreSubsystem = GEngine->GetEngineSubsystem<UDTFluxCoreSubsystem>();
|
|
||||||
const FDTFluxParticipant OutParticipant = CoreSubsystem->GetParticipant(Bib);
|
|
||||||
return OutParticipant.GetFormattedNameText(MaxChar, Separator, OverFlowChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
FText UFTDFluxUtils::GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar,
|
|
||||||
const FString Separator,
|
|
||||||
const FString OverFlowChar)
|
|
||||||
{
|
|
||||||
return Participant.GetFormattedNameText(MaxChar, Separator, OverFlowChar);
|
|
||||||
}
|
|
||||||
114
Source/DTFluxUtilities/Private/RundownController.cpp
Normal file
114
Source/DTFluxUtilities/Private/RundownController.cpp
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// ===============================================
|
||||||
|
// 3. SOURCE FILE (.CPP) - TOUS LES INCLUDES
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// YourRundownController.cpp
|
||||||
|
#include "RundownController.h"
|
||||||
|
|
||||||
|
#include "DTFluxUtilitiesModule.h"
|
||||||
|
#include "Rundown/AvaRundown.h"
|
||||||
|
#include "Rundown/AvaRundownPage.h"
|
||||||
|
#include "Engine/Engine.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
|
#include "UObject/UObjectGlobals.h"
|
||||||
|
#include "UObject/ConstructorHelpers.h"
|
||||||
|
#include "TimerManager.h"
|
||||||
|
#include "Logging/LogMacros.h"
|
||||||
|
#include "Components/StaticMeshComponent.h"
|
||||||
|
#include "Materials/MaterialInterface.h"
|
||||||
|
#include "Blueprint/UserWidget.h"
|
||||||
|
|
||||||
|
DEFINE_LOG_CATEGORY_STATIC(LogYourRundownController, Log, All);
|
||||||
|
|
||||||
|
// ===============================================
|
||||||
|
// 4. IMPLÉMENTATION SIMPLE
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
ARundownController::ARundownController()
|
||||||
|
{
|
||||||
|
PrimaryActorTick.bCanEverTick = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ARundownController::LoadRundown(const TSoftObjectPtr<UAvaRundown> RundownAsset)
|
||||||
|
{
|
||||||
|
if (RundownAsset.IsNull())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxUtilities, Error, TEXT("RundownAsset Null"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Charger l'asset rundown
|
||||||
|
CurrentRundown = RundownAsset.LoadSynchronous();
|
||||||
|
|
||||||
|
if (!CurrentRundown)
|
||||||
|
{
|
||||||
|
UE_LOG(LogYourRundownController, Error, TEXT("Failed to load rundown: %s"), *RundownAsset.ToString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialiser le contexte de playback
|
||||||
|
CurrentRundown->InitializePlaybackContext();
|
||||||
|
|
||||||
|
UE_LOG(LogYourRundownController, Log, TEXT("Successfully loaded rundown: %s"), *RundownAsset.ToString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ARundownController::PlayPage(int32 PageId)
|
||||||
|
{
|
||||||
|
if (!CurrentRundown)
|
||||||
|
{
|
||||||
|
UE_LOG(LogYourRundownController, Error, TEXT("No rundown loaded. Call LoadRundown first."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que la page existe
|
||||||
|
const FAvaRundownPage& Page = CurrentRundown->GetPage(PageId);
|
||||||
|
if (!Page.IsValidPage())
|
||||||
|
{
|
||||||
|
UE_LOG(LogYourRundownController, Error, TEXT("Invalid page ID: %d"), PageId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jouer la page
|
||||||
|
bool bSuccess = CurrentRundown->PlayPage(PageId, EAvaRundownPagePlayType::PlayFromStart);
|
||||||
|
|
||||||
|
if (bSuccess)
|
||||||
|
{
|
||||||
|
CurrentPageId = PageId;
|
||||||
|
UE_LOG(LogYourRundownController, Log, TEXT("Playing page %d: %s"), PageId, *Page.GetPageName());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogYourRundownController, Warning, TEXT("Failed to play page %d"), PageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ARundownController::StopPage(int32 PageId)
|
||||||
|
{
|
||||||
|
if (!CurrentRundown)
|
||||||
|
{
|
||||||
|
UE_LOG(LogYourRundownController, Error, TEXT("No rundown loaded"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bSuccess = CurrentRundown->StopPage(PageId, EAvaRundownPageStopOptions::Default, false);
|
||||||
|
|
||||||
|
if (bSuccess)
|
||||||
|
{
|
||||||
|
UE_LOG(LogYourRundownController, Log, TEXT("Stopped page %d"), PageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString ARundownController::ListePages()
|
||||||
|
{
|
||||||
|
if (!CurrentRundown)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxUtilities, Error, TEXT("No rundown loaded"));
|
||||||
|
return FString();
|
||||||
|
}
|
||||||
|
return FString();
|
||||||
|
}
|
||||||
77
Source/DTFluxUtilities/Public/DTFluxUtils.h
Normal file
77
Source/DTFluxUtilities/Public/DTFluxUtils.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "DTFluxCore/Public/Types/Struct/DTFluxTeamListStruct.h"
|
||||||
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||||
|
#include "Types/Struct/DTFluxSplitSensor.h"
|
||||||
|
#include "DTFluxUtils.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class DTFLUXUTILITIES_API UFTDFluxUtils : public UBlueprintFunctionLibrary
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
||||||
|
static FText GetFormatedName(const int& Bib, const int MaxChar = 10, const FString Separator = ".",
|
||||||
|
const FString OverFlowChar = "...");
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
||||||
|
static FText GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar = 10,
|
||||||
|
const FString Separator = ".",
|
||||||
|
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);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils")
|
||||||
|
static TArray<FDTFluxSplitSensorInfo> SortSplitRankingsByRank(const TArray<FDTFluxSplitSensorInfo>& Rankings);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
|
||||||
#include "DTFluxCore/Public/Types/Struct/DTFluxTeamListStruct.h"
|
|
||||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
|
||||||
#include "FTDFluxUtils.generated.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
UCLASS()
|
|
||||||
class DTFLUXUTILITIES_API UFTDFluxUtils : public UBlueprintFunctionLibrary
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
|
|
||||||
public:
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
|
||||||
static FText GetFormatedName(const int& Bib, const int MaxChar = 10, const FString Separator = ".",
|
|
||||||
const FString OverFlowChar = "...");
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
|
||||||
static FText GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar = 10,
|
|
||||||
const FString Separator = ".",
|
|
||||||
const FString OverFlowChar = "...");
|
|
||||||
};
|
|
||||||
41
Source/DTFluxUtilities/Public/RundownController.h
Normal file
41
Source/DTFluxUtilities/Public/RundownController.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "GameFramework/Actor.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
|
#include "TimerManager.h"
|
||||||
|
#include "Rundown/AvaRundown.h"
|
||||||
|
#include "UObject/SoftObjectPath.h"
|
||||||
|
#include "UObject/ObjectMacros.h"
|
||||||
|
#include "RundownController.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
UCLASS(BlueprintType, Blueprintable)
|
||||||
|
class DTFLUXUTILITIES_API ARundownController : public AActor
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
ARundownController();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
bool LoadRundown(const TSoftObjectPtr<UAvaRundown> RundownAsset);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
bool PlayPage(int32 PageId);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
bool StopPage(int32 PageId);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "DTFlux")
|
||||||
|
FString ListePages();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux")
|
||||||
|
TObjectPtr<UAvaRundown> CurrentRundown;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category = "DTFlux")
|
||||||
|
int32 CurrentPageId = 1;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user