diff --git a/DTFluxAPI.uplugin b/DTFluxAPI.uplugin index 0415f28..f999a51 100644 --- a/DTFluxAPI.uplugin +++ b/DTFluxAPI.uplugin @@ -44,6 +44,11 @@ "Name": "DTFluxUtilities", "Type": "Runtime", "LoadingPhase": "Default" + }, + { + "Name": "DTFluxAPIStatus", + "Type": "Editor", + "LoadingPhase": "Default" } ], "Plugins": [ diff --git a/Resources/DTFluxServerStatusWhite.svg b/Resources/DTFluxServerStatusWhite.svg new file mode 100644 index 0000000..0cc21d8 --- /dev/null +++ b/Resources/DTFluxServerStatusWhite.svg @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/DTFluxAPIStatus/DTFluxAPIStatus.Build.cs b/Source/DTFluxAPIStatus/DTFluxAPIStatus.Build.cs new file mode 100644 index 0000000..8c74166 --- /dev/null +++ b/Source/DTFluxAPIStatus/DTFluxAPIStatus.Build.cs @@ -0,0 +1,34 @@ +using UnrealBuildTool; + +public class DTFluxAPIStatus : ModuleRules +{ + public DTFluxAPIStatus(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "Projects", + "DTFluxNetwork", + "DTFluxProjectSettings", + "DTFluxCore", + "EditorStyle", + "ToolWidgets", // Nécessaire pour FSlimHorizontalToolBarBuilder + "UnrealEd", + "Settings" + } + ); + } +} \ No newline at end of file diff --git a/Source/DTFluxAPIStatus/Private/DTFluxAPIStatusModule.cpp b/Source/DTFluxAPIStatus/Private/DTFluxAPIStatusModule.cpp new file mode 100644 index 0000000..b8a1ddb --- /dev/null +++ b/Source/DTFluxAPIStatus/Private/DTFluxAPIStatusModule.cpp @@ -0,0 +1,111 @@ +#include "DTFluxAPIStatusModule.h" + +#include "LevelEditor.h" +#include "widgets/DTFluxStatusWidget.h" +#include "widgets/styles/DTFluxStatusStyle.h" + +#define LOCTEXT_NAMESPACE "FDTFluxAPIStatusModule" + +DEFINE_LOG_CATEGORY(logDTFluxStatus); + +FName DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::StatusTabId = "DTFluxStatusTab"; +FText DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::StatusTabDisplayName = FText::FromString(TEXT("DTFlux Status")); + + +void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::StartupModule() +{ + InitMenuExtension(); + RegisterStatusTab(); + FDTFluxStatusStyle::RegisterStyle(); +} + + +#pragma region MenuExtension + + +void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::InitMenuExtension() +{ + FLevelEditorModule& LevelEditorModule = + FModuleManager::LoadModuleChecked(TEXT("LevelEditor")); + // FDTFluxAPIModule& DTFluxApi = + // FModuleManager::LoadModuleChecked(TEXT("DTFluxAPI")); + const TSharedPtr MenuExtender = MakeShareable(new FExtender()); + + MenuExtender->AddMenuBarExtension( + "Help", + EExtensionHook::Before, + nullptr, + FMenuBarExtensionDelegate::CreateRaw(this, &FDTFluxAPIStatusModule::AddMenu) + ); + LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender); + + +} + +void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::AddMenu(FMenuBarBuilder& MenuBarBuilder) +{ + MenuBarBuilder.AddPullDownMenu( + FText::FromString("DTFlux"), + FText::FromString("DTFlux API Tools"), + FNewMenuDelegate::CreateRaw(this, &FDTFluxAPIStatusModule::FillMenu) + ); +} + +void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::FillMenu(FMenuBuilder& MenuBuilder) +{ + MenuBuilder.BeginSection(NAME_None, FText::FromString("DTFlux API")); + MenuBuilder.AddMenuEntry( + FText::FromString("Status"), + FText::FromString("Launch DTFlux Status"), + FSlateIcon(FDTFluxStatusStyle::GetStyleSetName(), "LevelEditor.Tab.Icon"), + FExecuteAction::CreateRaw(this, &FDTFluxAPIStatusModule::OnButtonClicked) + ); + MenuBuilder.EndSection(); + +} + +void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::OnButtonClicked() +{ + FGlobalTabmanager::Get()->TryInvokeTab(StatusTabId); + UE_LOG(LogTemp, Log, TEXT("Status Tab Launched")) +} + +#pragma endregion EditorTab + +#pragma region +void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::RegisterStatusTab() +{ + FTabSpawnerEntry& SpawnerEntry = + FGlobalTabmanager::Get()->RegisterNomadTabSpawner( + StatusTabId, + FOnSpawnTab::CreateRaw(this, &FDTFluxAPIStatusModule::OnSpawnTab) + ) + .SetDisplayName(StatusTabDisplayName) + .SetTooltipText(FText::FromString(TEXT("Status of DTFlux API external connections"))); +} + +TSharedRef DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs) +{ + + + return + SNew( + SDockTab + ) + .TabRole(ETabRole::NomadTab) + // .ShouldAutosize(true) + [ + SNew(SDTFluxStatusWidget) + ]; +} + +#pragma endregion +void DTFLUXAPISTATUS_API FDTFluxAPIStatusModule::ShutdownModule() +{ + FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(StatusTabId); + FDTFluxStatusStyle::UnregisterStyle(); + +} +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FDTFluxAPIStatusModule, DTFluxAPIStatus) \ No newline at end of file diff --git a/Source/DTFluxAPIStatus/Private/DTFluxStatusWidget.cpp b/Source/DTFluxAPIStatus/Private/DTFluxStatusWidget.cpp new file mode 100644 index 0000000..3b2e089 --- /dev/null +++ b/Source/DTFluxAPIStatus/Private/DTFluxStatusWidget.cpp @@ -0,0 +1,367 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "widgets/DTFluxStatusWidget.h" + +#include "SlateOptMacros.h" +#include "DTFluxAPIStatusModule.h" +#include "EditorStyleSet.h" +#include "ISettingsCategory.h" +#include "ISettingsContainer.h" +#include "ISettingsModule.h" +#include "ISettingsSection.h" +#include "Styling/SlateIconFinder.h" +#include "Types/Enum/DTFluxCoreEnum.h" +#include "Subsystems/DTFluxNetworkSubsystem.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" + + +BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION + +void SDTFluxStatusWidget::OnOpenSettingsClicked() +{ + UE_LOG(logDTFluxStatus, Warning, TEXT("Settings Clicked !!!!")); + ISettingsModule& SettingsModule = FModuleManager::LoadModuleChecked("Settings"); + SettingsModule.ShowViewer("Project", "DTFluxProjectSettings", "DTFluxNetworkSettings"); +} + +FReply SDTFluxStatusWidget::OnRaceDatasClicked() +{ + DTFlux->SendRequest(EDTFluxRequestType::RaceData); + return FReply::Handled(); +} + +FReply SDTFluxStatusWidget::OnTeamListClicked() +{ + DTFlux->SendRequest(EDTFluxRequestType::TeamList); + return FReply::Handled(); +} + +void SDTFluxStatusWidget::Construct(const FArguments& InArgs) +{ + + DTFlux = + GEngine->GetEngineSubsystem(); + ConnectionActionButtonText.Set( + DTFlux->WsStatus != EDTFluxConnectionStatus::Connected ? + FText::FromString("Connect") : + FText::FromString("Disconnect") + ); + + bCanSupportFocus = true; + + + + + FSlimHorizontalToolBarBuilder ToolBarBuilder( + nullptr, + FMultiBoxCustomization::None, + nullptr, + false + ); + + ToolBarBuilder.BeginSection("Settings"); + { + ToolBarBuilder.AddToolBarButton( + FUIAction(FExecuteAction::CreateSP(this, &SDTFluxStatusWidget::OnOpenSettingsClicked)), + NAME_None, + INVTEXT("DTFlux Settings"), + INVTEXT("Ouvrir les paramètres DTFlux"), + FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Settings") + ); + } + ToolBarBuilder.EndSection(); + + + + FSlateFontInfo TitleTextFont = FCoreStyle::Get().GetFontStyle(FName("EmbossedText")); + TitleTextFont.Size = 15; + ChildSlot + [ +#pragma region ToolBarSection + SNew(SVerticalBox) + +SVerticalBox::Slot() + .AutoHeight() + + [ + SNew(SBox) + [ + SNew(SHorizontalBox) + +SHorizontalBox::Slot() + .FillWidth(2.0) + [ + SNew(SSpacer) + ] + +SHorizontalBox::Slot() + .AutoWidth() + .FillWidth(1.0) + .VAlign(VAlign_Center) + .HAlign(HAlign_Right) + [ + ToolBarBuilder.MakeWidget() + ] + ] + ] +#pragma endregion +#pragma region WebsocketStatusSection + // Main VerticalBox + +SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SBorder) + .Padding(6.0f) + [ + SNew(SHorizontalBox) + +SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .HAlign(HAlign_Center) + .MaxWidth(175.0) + .MinWidth(150.0) + [ + SNew(STextBlock ) + .Text(FText::FromString(TEXT("Websocket connection :"))) + .Justification(ETextJustify::Left) + ] + +SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .MinWidth(50.0) + .MaxWidth(100.0) + [ + SAssignNew( WsStatusText, STextBlock) + .Text(this, &SDTFluxStatusWidget::GetWebSocketStatusText) + .Justification(ETextJustify::Left) + .ColorAndOpacity(this, &SDTFluxStatusWidget::GetWebSocketStatusColor) + ] + +SHorizontalBox::Slot() + .MaxWidth(100.0) + .MinWidth(30.0) + [ + SAssignNew(ConnectionActionButton, SButton) + .Text(this, &SDTFluxStatusWidget::GetWebConnectActionButtonText) + .ForegroundColor_Raw(this, &SDTFluxStatusWidget::GetWebConnectActionButtonColor) + .OnClicked(this,&SDTFluxStatusWidget::OnConnectionActionButtonClicked) + .VAlign(VAlign_Center) + .HAlign(HAlign_Center) + .ContentPadding(1.5f) + ] + ] + ] +#pragma endregion +#pragma region DataModelControlSection + +SVerticalBox::Slot() + .VAlign(VAlign_Fill) + .HAlign(HAlign_Fill) + .AutoHeight() + [ + SNew(SBox) + [ + SNew(SHorizontalBox) + +SHorizontalBox::Slot() + [ + SNew(SButton) + .Text(FText::FromString("Get RaceDatas")) + .OnClicked(this, &SDTFluxStatusWidget::OnRaceDatasClicked) + ] + +SHorizontalBox::Slot() + [ + SNew(SButton) + .Text(FText::FromString("Get TeamList")) + .OnClicked(this, &SDTFluxStatusWidget::OnTeamListClicked) + ] + ] + ] +#pragma endregion +#pragma region HTTPStatusSection +// +SVerticalBox::Slot() +// .AutoHeight() +// [ +// SNew(SHorizontalBox) +// +SHorizontalBox::Slot() +// [ +// SNew(STextBlock) +// .Text(FText::FromString(TEXT("HTTP connection :"))) +// .Justification(ETextJustify::Left) +// ] +// +SHorizontalBox::Slot() +// [ +// SNew(STextBlock) +// .Text(FText::FromString(TEXT("invalid"))) +// .Justification(ETextJustify::Center) +// .ColorAndOpacity(FColor::Red) +// ] +// +SHorizontalBox::Slot() +// [ +// SNew(SButton) +// .Text(FText::FromString(TEXT("Connection test"))) +// ] +// ] +#pragma endregion +#pragma region ContestsDataSection + // +SVerticalBox::Slot() + // .AutoHeight() + // .VAlign(VAlign_Fill) + // .HAlign(HAlign_Fill) + // [ + // SNew(SBorder) + // .Padding(1.5f) + // .VAlign(VAlign_Fill) + // .HAlign(HAlign_Fill) + // [ + // SNew(STextBlock) + // .Justification(ETextJustify::Left) + // .Text(FText::FromString("Contest :")) + // .ColorAndOpacity(FColor::White) + // ] + // ] + // +SVerticalBox::Slot() + // .AutoHeight() + // .VAlign(VAlign_Center) + // .HAlign(HAlign_Fill) + // [ + // SNew(SBorder) + // .Padding(1.0f) + // .VAlign(VAlign_Center) + // .HAlign(HAlign_Fill) + // [ + // SNew(SDatastorageView, DTFlux) + // ] + // ] +#pragma endregion +#pragma region ParticipantsDataSection + // +SVerticalBox::Slot() + // .AutoHeight() + // [ + // SNew(SHorizontalBox) + // +SHorizontalBox::Slot() + // [ + // SNew(STextBlock) + // .Text(FText::FromString(TEXT("Participants"))) + // .Justification(ETextJustify::Left) + // ] + // +SHorizontalBox::Slot() + // [ + // SNew(SButton) + // .Text(FText::FromString(TEXT("Show"))) + // ] + // ] +#pragma endregion +#pragma region EventsSection + // +SVerticalBox::Slot() + // .AutoHeight() + // [ + // SNew(SHorizontalBox) + // +SHorizontalBox::Slot() + // [ + // SNew(STextBlock) + // .Text(FText::FromString(TEXT("Future Events"))) + // .Justification(ETextJustify::Left) + // ] + // +SHorizontalBox::Slot() + // [ + // SNew(SButton) + // .Text(FText::FromString(TEXT("Show"))) + // ] + // ] +#pragma endregion +#pragma region SequencesSection + // +SVerticalBox::Slot() + // .AutoHeight() + // [ + // SNew(SHorizontalBox) + // +SHorizontalBox::Slot() + // [ + // SNew(STextBlock) + // .Text(FText::FromString(TEXT("Sequence On Air"))) + // .Justification(ETextJustify::Left) + // ] + // +SHorizontalBox::Slot() + // [ + // SNew(SButton) + // .Text(FText::FromString(TEXT("Show"))) + // ] + // ] +#pragma endregion + + + ]; +} + +FText SDTFluxStatusWidget::GetWebSocketStatusText() const +{ + + FString Status = + UEnum::GetDisplayValueAsText(DTFlux->WsStatus).ToString(); + return + FText::FromString(Status); + // FText::FromString("Unknown"); + +} + +FText SDTFluxStatusWidget::GetWebConnectActionButtonText() const +{ + switch (DTFlux->WsStatus) + { + case EDTFluxConnectionStatus::Connected: + return FText::FromString("Disconnect"); + default: + return FText::FromString("Connect"); + } +} + +FReply SDTFluxStatusWidget::OnConnectionActionButtonClicked() +{ + if(DTFlux) + { + switch (DTFlux->WsStatus) + { + case EDTFluxConnectionStatus::Connected: + DTFlux->Reconnect(); + break; + default: + DTFlux->Connect(); + break; + } + } + return FReply::Handled(); +} +FSlateColor SDTFluxStatusWidget::GetWebSocketStatusColor() const +{ + FColor Color; + switch (DTFlux->WsStatus) + { + case EDTFluxConnectionStatus::Unset: + Color = FColor::Orange; + break; + case EDTFluxConnectionStatus::Connected: + Color = FColor::Green; + break; + case EDTFluxConnectionStatus::NotConnected: + Color = FColor::Orange; + break; + case EDTFluxConnectionStatus::Closed: + Color = FColor::Magenta; + break; + default: + Color = FColor::Red; + break; + } + return FSlateColor(Color); +} + +FSlateColor SDTFluxStatusWidget::GetWebConnectActionButtonColor() const +{ + FColor Color= FColor::Green; + switch (DTFlux->WsStatus) + { + case EDTFluxConnectionStatus::Connected: + Color = FColor::Red; + break; + default: + Color = FColor::Green; + break; + } + return FSlateColor(Color); +} + + +END_SLATE_FUNCTION_BUILD_OPTIMIZATION diff --git a/Source/DTFluxAPIStatus/Private/styles/DTFluxStatusStyle.cpp b/Source/DTFluxAPIStatus/Private/styles/DTFluxStatusStyle.cpp new file mode 100644 index 0000000..7865908 --- /dev/null +++ b/Source/DTFluxAPIStatus/Private/styles/DTFluxStatusStyle.cpp @@ -0,0 +1,46 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "widgets/styles/DTFluxStatusStyle.h" + +#include "Interfaces/IPluginManager.h" +#include "Styling/SlateStyleRegistry.h" +#include "Styling/SlateStyleMacros.h" + +#define RootToContentDir Style->RootToContentDir + +TSharedPtr FDTFluxStatusStyle::StyleSet = nullptr; + +void FDTFluxStatusStyle::RegisterStyle() +{ + if(StyleSet.IsValid()) return; + + StyleSet = Create(); + FSlateStyleRegistry::RegisterSlateStyle(*StyleSet); +} + +void FDTFluxStatusStyle::UnregisterStyle() +{ + if(StyleSet.IsValid()) + { + FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet); + ensure(StyleSet.IsUnique()); + StyleSet.Reset(); + } + + +} + +void FDTFluxStatusStyle::ReloadTextures() +{ +} + +TSharedPtr FDTFluxStatusStyle::Create() +{ + TSharedPtr Style = MakeShareable(new FSlateStyleSet("DTFluxAPIStatusStyle")); + Style->SetContentRoot(IPluginManager::Get().FindPlugin("DTFluxAPI")->GetBaseDir()/TEXT("Resources")); + + Style->Set("LevelEditor.Tab.Icon", new IMAGE_BRUSH_SVG("DTFluxServerStatusWhite", FVector2d(16)) ); + return Style; +} + diff --git a/Source/DTFluxAPIStatus/Public/DTFluxAPIStatusModule.h b/Source/DTFluxAPIStatus/Public/DTFluxAPIStatusModule.h new file mode 100644 index 0000000..112e34b --- /dev/null +++ b/Source/DTFluxAPIStatus/Public/DTFluxAPIStatusModule.h @@ -0,0 +1,31 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +DTFLUXAPISTATUS_API DECLARE_LOG_CATEGORY_EXTERN(logDTFluxStatus, All, All); + +class DTFLUXAPISTATUS_API FDTFluxAPIStatusModule : public IModuleInterface +{ +public: + virtual void StartupModule() override; + virtual void ShutdownModule() override; + +#pragma region MenuExtention + void InitMenuExtension(); + void AddMenu(FMenuBarBuilder& MenuBarBuilder); + void FillMenu(FMenuBuilder& MenuBuilder); + void OnButtonClicked(); + // void OnWsEvent(TEnumAsByte WsResponseEvent) const; +#pragma endregion + +#pragma region EditorTab + void RegisterStatusTab(); + TSharedRef OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs); +private: + static FName StatusTabId; + static FText StatusTabDisplayName; + + TSharedPtr StatusWidget; +#pragma endregion +}; diff --git a/Source/DTFluxAPIStatus/Public/widgets/DTFluxStatusWidget.h b/Source/DTFluxAPIStatus/Public/widgets/DTFluxStatusWidget.h new file mode 100644 index 0000000..ce16311 --- /dev/null +++ b/Source/DTFluxAPIStatus/Public/widgets/DTFluxStatusWidget.h @@ -0,0 +1,44 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Widgets/SCompoundWidget.h" + + +/** + * + */ +class UDTFluxNetworkSubsystem; +class SSuperListView; + +class DTFLUXAPISTATUS_API SDTFluxStatusWidget : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SDTFluxStatusWidget) + { + } + + SLATE_END_ARGS() + + void OnOpenSettingsClicked(); + FReply OnRaceDatasClicked(); + FReply OnTeamListClicked(); + /** Constructs this widget with InArgs */ + void Construct(const FArguments& InArgs); + TAttribute ConnectionActionButtonText; + FReply OnConnectionActionButtonClicked(); + +private: + UDTFluxNetworkSubsystem* DTFlux = nullptr; + // // TODO make a struct + FText GetWebSocketStatusText() const; + FText GetWebConnectActionButtonText() const; + FSlateColor GetWebSocketStatusColor() const; + FSlateColor GetWebConnectActionButtonColor() const; + TSharedPtr WsStatusText; + TSharedPtr ConnectionActionButton; + + + +}; diff --git a/Source/DTFluxAPIStatus/Public/widgets/styles/DTFluxStatusStyle.h b/Source/DTFluxAPIStatus/Public/widgets/styles/DTFluxStatusStyle.h new file mode 100644 index 0000000..608addc --- /dev/null +++ b/Source/DTFluxAPIStatus/Public/widgets/styles/DTFluxStatusStyle.h @@ -0,0 +1,35 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Styling/ISlateStyle.h" + +/** + * + */ +class DTFLUXAPISTATUS_API FDTFluxStatusStyle +{ + +public: + static void RegisterStyle(); + static void UnregisterStyle(); + + static void ReloadTextures(); + + static const ISlateStyle& Get() + { + return *StyleSet; + } + + static const FName& GetStyleSetName() + { + return StyleSet->GetStyleSetName(); + } + +private: + static TSharedPtr Create(); + static TSharedPtr StyleSet; +}; + + diff --git a/Source/DTFluxCore/DTFluxCore.Build.cs b/Source/DTFluxCore/DTFluxCore.Build.cs index 69614a5..8f441ef 100644 --- a/Source/DTFluxCore/DTFluxCore.Build.cs +++ b/Source/DTFluxCore/DTFluxCore.Build.cs @@ -9,7 +9,7 @@ public class DTFluxCore : ModuleRules PublicDependencyModuleNames.AddRange( new string[] { - "Core","JsonUtilities" + "Core", } ); @@ -21,6 +21,7 @@ public class DTFluxCore : ModuleRules "Slate", "SlateCore", "JsonUtilities", + "OutputLog", "Json", } diff --git a/Source/DTFluxCore/Private/Assets/DTFluxModelAsset.cpp b/Source/DTFluxCore/Private/Assets/DTFluxModelAsset.cpp index a7006ca..52dfbcd 100644 --- a/Source/DTFluxCore/Private/Assets/DTFluxModelAsset.cpp +++ b/Source/DTFluxCore/Private/Assets/DTFluxModelAsset.cpp @@ -36,18 +36,20 @@ void UDTFluxModelAsset::AddPerson(const FDTFluxPerson& InPerson) void UDTFluxModelAsset::AddParticipant(const FDTFluxParticipant& InParticipant, const int ContestId) { + UE_LOG(logDTFluxCore, Error, TEXT("%i Person in Participant %i"), InParticipant.GetTeammateNum(), InParticipant.Bib); FDTFluxContest TargetContest; if(GetContestById(ContestId, TargetContest)) { - if(!PersonExists(InParticipant.Person1)) + TArray Teammate = InParticipant.Teammate; + for(auto& Person : InParticipant.Teammate) { - AddPerson(InParticipant.Person1); - } - if(InParticipant.Person2 != 0) - { - if(!PersonExists(InParticipant.Person2)) + UE_LOG(logDTFluxCore, Error, TEXT("AddParticipant() DTFlux Person %s %s %s"), + *Person.FirstName, *Person.LastName, *Person.Gender); + if(!PersonExists(Person)) { - AddPerson(InParticipant.Person2); + UE_LOG(logDTFluxCore, Error, TEXT("AddParticipant() DTFlux Person %s %s %s doesnot exists, adding..."), + *Person.FirstName, *Person.LastName, *Person.Gender); + AddPerson(Person); } } Participants.Add(InParticipant.Bib, InParticipant); @@ -79,30 +81,24 @@ void UDTFluxModelAsset::AddContestRanking(const FDTFluxContestRankings& NewConte ContestRankings.Add(NewContestRankings.ContestId, NewContestRankings); } -bool UDTFluxModelAsset::UpdateStageRanking(const FDTFluxStageRankings& InStageRankings) +void UDTFluxModelAsset::UpdateOrCreateStageRanking(const FDTFluxStageRankings& InStageRankings) { - const int ContestId = InStageRankings.ContestId; - const int StageId = InStageRankings.StageId; - int Index = 0; - int StageRankingArraySize = StageRankings.Num()-1; - for(auto Ranking : StageRankings) - { - if(Ranking.ContestId == ContestId && Ranking.StageId == StageId) - { - Index++; - break; - } - Index++; - } - if(Index != StageRankingArraySize ) - { - StageRankings[Index] = InStageRankings; - return true; - } - return false; + FDTFluxStageKey StageKey = InStageRankings.GetCompositeKey(); + StageRankings.FindOrAdd(StageKey) = InStageRankings; } -bool UDTFluxModelAsset::UpdateSplitRanking(const FDTFluxStageRankings& InStageRankings) +void UDTFluxModelAsset::AddStageRanking(const FDTFluxStageRankings& InStageRankings) { - return true; + StageRankings.Add(InStageRankings.GetCompositeKey(), InStageRankings); +} + +void UDTFluxModelAsset::AddSplitRanking(const FDTFluxSplitRankings& InSplitRanking) +{ + SplitRankings.Add(InSplitRanking.GetCompositeKey(), InSplitRanking); +} + +void UDTFluxModelAsset::UpdateOrCreateSplitRanking(const FDTFluxSplitRankings& InSplitRankings) +{ + FDTFluxSplitKey SplitKey = InSplitRankings.GetCompositeKey(); + SplitRankings.FindOrAdd(SplitKey) = InSplitRankings; } diff --git a/Source/DTFluxCore/Private/Types/Objects/DTFluxPursuitManager.cpp b/Source/DTFluxCore/Private/Types/Objects/DTFluxPursuitManager.cpp new file mode 100644 index 0000000..dbe5e81 --- /dev/null +++ b/Source/DTFluxCore/Private/Types/Objects/DTFluxPursuitManager.cpp @@ -0,0 +1,43 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Types/Objects/DTFluxPursuitManager.h" + +void UDTFluxPursuitManager::InitForStage(const FDTFluxStageRankings& StageRankings) +{ +} + +TArray UDTFluxPursuitManager::GetNextPursuits(int MaxPursuit) +{ + //TODO : Implement me !!! + return PursuitParticipants; +} + +TArray UDTFluxPursuitManager::GetPursuits(int FromIndex, int MaxPursuit) +{ + //TODO : Implement me !!! + return PursuitParticipants; +} + +FDateTime UDTFluxPursuitManager::GetMassStart() +{ + //TODO : Implement me !!! + return MassStart; +} + +FText UDTFluxPursuitManager::GetFormattedName(FDTFluxPursuit& InPursuit, const int MaxChar, + const FString OverflowChar) +{ + return InPursuit.GetFormattedName(MaxChar, OverflowChar); +} + +FText UDTFluxPursuitManager::DisplayPursuit(FDTFluxPursuit& InPursuit, const int MaxWidth, + const FString NameOverflowChar) +{ + return InPursuit.DisplayPursuit(MaxWidth, NameOverflowChar); +} + +bool UDTFluxPursuitManager::IsUnique(const FDTFluxPursuit& InPursuit) +{ + return InPursuit.IsUnique(); +} \ No newline at end of file diff --git a/Source/DTFluxCore/Private/Types/Objects/UDTFluxParticipantFactory.cpp b/Source/DTFluxCore/Private/Types/Objects/UDTFluxParticipantFactory.cpp new file mode 100644 index 0000000..7535cff --- /dev/null +++ b/Source/DTFluxCore/Private/Types/Objects/UDTFluxParticipantFactory.cpp @@ -0,0 +1,25 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Types/Objects/UDTFluxParticipantFactory.h" + +bool UDTFluxParticipantFactory::CreateParticipantFomJson(const FString& JsonString, FDTFluxParticipant& OutParticipant) +{ + TSharedPtr JsonObject; + TSharedRef> Reader = TJsonReaderFactory<>::Create(JsonString); + if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid()) + { + return UDTFluxParticipantFactory::CreateFromJsonCpp(JsonObject, OutParticipant); + } + else + { + OutParticipant = FDTFluxParticipant(); + return false; + } +} + +bool UDTFluxParticipantFactory::CreateFromJsonCpp(const TSharedPtr JsonObject, FDTFluxParticipant& OutParticipant) +{ + OutParticipant = FDTFluxParticipant::CreateFromJson(JsonObject); + return OutParticipant == 0; +} diff --git a/Source/DTFluxCore/Private/Types/Struct/DTFluxCompositeKey.cpp b/Source/DTFluxCore/Private/Types/Struct/DTFluxCompositeKey.cpp new file mode 100644 index 0000000..a6f81ea --- /dev/null +++ b/Source/DTFluxCore/Private/Types/Struct/DTFluxCompositeKey.cpp @@ -0,0 +1,4 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Types/Struct/DTFluxCompositeKey.h" diff --git a/Source/DTFluxCore/Private/Types/Struct/DTFluxPursuitStructs.cpp b/Source/DTFluxCore/Private/Types/Struct/DTFluxPursuitStructs.cpp new file mode 100644 index 0000000..c9d5f90 --- /dev/null +++ b/Source/DTFluxCore/Private/Types/Struct/DTFluxPursuitStructs.cpp @@ -0,0 +1,30 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Types/Struct/DTFluxPursuitStructs.h" + + +FDTFluxPursuit::FDTFluxPursuit() +{ +} + +FDTFluxPursuit::~FDTFluxPursuit() +{ +} + +FText FDTFluxPursuit::GetFormattedName(const int MaxChar, const FString OverflowChar) +{ + //TODO: Implement Me !!! + return Participants[0].GetConcatFormattedName(MaxChar, OverflowChar); +} + +FText FDTFluxPursuit::DisplayPursuit(const int MaxWidth, const FString NameOverflowChar) +{ + //TODO: Implement Me !!! + return Participants[0].GetConcatFormattedName(MaxWidth, NameOverflowChar); +} + +bool FDTFluxPursuit::IsUnique() const +{ + return Participants.Num() == 1; +} diff --git a/Source/DTFluxCore/Private/Types/Struct/DTFluxRankingStructs.cpp b/Source/DTFluxCore/Private/Types/Struct/DTFluxRankingStructs.cpp index 08376d0..96e305d 100644 --- a/Source/DTFluxCore/Private/Types/Struct/DTFluxRankingStructs.cpp +++ b/Source/DTFluxCore/Private/Types/Struct/DTFluxRankingStructs.cpp @@ -10,13 +10,13 @@ void FDTFluxContestRanking::Dump() const Rank, Bib, *Gap, *Time ); }; -void FDTFluxStageRanking::Dump() const -{ - UE_LOG(logDTFluxCore, Log, TEXT("RANKING : %02d. Participant bib %d %s %s %s %s %s"), - Rank, Bib, *Gap, *TimeSwim, - *TimeTransition, *TimeRun, *StartTime.ToString()); -} - +// void FDTFluxStageRanking::Dump() const +// { +// UE_LOG(logDTFluxCore, Log, TEXT("RANKING : %02d. Participant bib %d %s %s %s %s %s"), +// Rank, Bib, *Gap, *TimeSwim, +// *TimeTransition, *TimeRun, *StartTime.ToString()); +// } +// diff --git a/Source/DTFluxCore/Private/Types/Struct/DTFluxSplitSensor.cpp b/Source/DTFluxCore/Private/Types/Struct/DTFluxSplitSensor.cpp new file mode 100644 index 0000000..26655fd --- /dev/null +++ b/Source/DTFluxCore/Private/Types/Struct/DTFluxSplitSensor.cpp @@ -0,0 +1,4 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Types/Struct/DTFluxSplitSensor.h" diff --git a/Source/DTFluxCore/Private/Types/Struct/DTFluxTeamListStruct.cpp b/Source/DTFluxCore/Private/Types/Struct/DTFluxTeamListStruct.cpp index 7c8cf19..ece59ef 100644 --- a/Source/DTFluxCore/Private/Types/Struct/DTFluxTeamListStruct.cpp +++ b/Source/DTFluxCore/Private/Types/Struct/DTFluxTeamListStruct.cpp @@ -4,104 +4,150 @@ #include "Types/Struct/DTFluxTeamListStruct.h" -bool FDTFluxParticipant::IsTeam() const + + +void FDTFluxParticipant::AddTeammate(const FDTFluxPerson& Person) { - return Person2.FirstName.IsEmpty() && Person2.LastName.IsEmpty(); + Teammate.Add(Person); } - -void FDTFluxParticipant::Dump() const +void FDTFluxParticipant::AddTeammate(const FString LastName, const FString FirstName, const FString Gender) { - FString EliteStr = "NO"; - if(Elite) +} + +FText FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString OverflowChars) +{ + // Vérifie les cas limites + if (MaxChar <= 0) { - EliteStr = "YES"; + return FText::GetEmpty(); } - UE_LOG(logDTFluxCore, Log, TEXT("PARTICIPANT with bib: %03d"), Bib); - UE_LOG(logDTFluxCore, Log, TEXT("Fullname : %s %s"), *Person1.FirstName, *Person1.LastName); + FString FirstName; + FString LastName; if(IsTeam()) { - UE_LOG(logDTFluxCore, Log, TEXT("Teamate : %s %s"), *Person2.FirstName, *Person2.LastName); - UE_LOG(logDTFluxCore, Log, TEXT("Team name : %s"), *Team); + LastName = Team; } - UE_LOG(logDTFluxCore, Log, TEXT("Club : %s, Category : %s, IsElite : %s, Status : %s"), - *Club, *Category, *EliteStr, *UEnum::GetValueAsString(Status)); + // Récupère la première lettre du prénom en majuscule + FString Initial; + if (!FirstName.IsEmpty()) + { + Initial = FirstName.Left(1).ToUpper() + " "; + } + + // Nom complet en majuscules + FString FormattedLastName = LastName.ToUpper(); + + // Construction du nom final + FString FullName = Initial + FormattedLastName; + + // Tronque si nécessaire + if (FullName.Len() > MaxChar) + { + // On essaie de garder autant de caractères que possible + const int32 AvailableLength = MaxChar - Initial.Len(); + if (AvailableLength <= 0) + { + // Pas assez de place pour le nom → juste l'initiale ? + return FText::FromString(Initial); + } + + // Coupe le nom pour qu’il rentre dans la limite + const int32 TruncateLength = FMath::Min(AvailableLength, FormattedLastName.Len()); + FullName = Initial + FormattedLastName.Left(TruncateLength); + + // Si on a coupé trop court, on ajoute le suffixe + if (FormattedLastName.Len() > TruncateLength) + { + // On vérifie qu'il reste de la place pour le suffixe + const int32 CurrentLength = FullName.Len(); + const int32 OverflowLength = OverflowChars.Len(); + + if (CurrentLength + OverflowLength <= MaxChar) + { + FullName += OverflowChars; + } + else + { + // Il faut tronquer davantage pour faire de la place au suffixe + const int32 RemainingSpace = MaxChar - CurrentLength; + if (RemainingSpace > 0) + { + FullName = FullName.Left(MaxChar - OverflowLength) + OverflowChars; + } + else + { + FullName = FullName.Left(MaxChar); + } + } + } + } + + return FText::FromString(FullName); } -FString FDTFluxParticipant::GetParticipantFormatedName(bool Truncate, int MaxSize) const +FText FDTFluxParticipant::GetConcatFormattedName(const int MaxChar, const FString OverflowChar) { - FString ParticipantName; - if(Truncate) - { - if(IsTeam()) - { - //Concatenate the team name; - if(Team.Len() > MaxSize - 3) - { - return Team.Left(MaxSize - 3).Append(TEXT("...")); - } - return Team; - } - if(Person1.FirstName.Contains("-") ) - { - FString Formated = ""; - //Compound Firstname - TArray Out; - Person1.FirstName.ParseIntoArray(Out,TEXT("-"),true); - for(const auto& Str : Out) - { - Formated.Append(Str.Left(1).ToUpper()).Append("."); - } - // TODO : Camel Case handling for LastName - Formated.Append(" ").Append(*Person1.LastName); - UE_LOG(logDTFluxCore, Log, TEXT("Firstname is with space compound. Formated Name %s length %02d MAX Size : %02d"), - *Formated, Formated.Len(), MaxSize); - if(Formated.Len() >= MaxSize) - { - UE_LOG(logDTFluxCore, Log, TEXT("Reducing %s Formated"), *Formated); + FString BibText = FString::FromInt(Bib) + " "; + FText FormattedName = GetFormattedName(MaxChar - BibText.Len(), OverflowChar ); + return FText::FromString(BibText + FormattedName.ToString()); +} - return Formated.Left(MaxSize - 3).Append("..."); - } - return Formated; - } - if(Person1.FirstName.Contains(" ")) - { - FString Formated = ""; - //Compound Firstname - TArray Out; - Person1.FirstName.ParseIntoArray(Out,TEXT(" "),true); - for(const auto& Str : Out) - { - Formated.Append(Str.Left(1).ToUpper()).Append("."); - } - // TODO : Camel Case handling for LastName - Formated.Append(" ").Append(*Person1.LastName); - UE_LOG(logDTFluxCore, Log, TEXT("Firstname is with space compound. Formated Name %s length %02d MAX Size : %02d"), - *Formated, Formated.Len(), MaxSize); - if(Formated.Len() >= MaxSize) - { - UE_LOG(logDTFluxCore, Log, TEXT("Reducing %s Formated"), *Formated); - return Formated.Left(MaxSize - 3).Append("..."); - } - return Formated; - } - FString Formated = Person1.FirstName.Left(1).Append(". "); - Formated.Append(Person1.LastName); - UE_LOG(logDTFluxCore, Log, TEXT("Firstname is not compound. Formated Name %s length %02d MAX Size : %02d"), - *Formated, Formated.Len(), MaxSize); - if(Formated.Len() >= MaxSize) - { - UE_LOG(logDTFluxCore, Log, TEXT("Reducing %s Formated"), *Formated); - return Formated.Left(MaxSize - 3).Append("..."); - } - return Formated; - } - else +// Constructeur privé depuis JSON +FDTFluxParticipant::FDTFluxParticipant(const TSharedPtr& JsonObject) + : Bib(JsonObject->GetIntegerField(TEXT("bib"))) + , ContestId(JsonObject->GetIntegerField(TEXT("contestId"))) + , Category(JsonObject->GetStringField(TEXT("category"))) + , Club(JsonObject->GetStringField(TEXT("club"))) + , Elite(JsonObject->GetBoolField(TEXT("elite"))) + , Status(static_cast(JsonObject->GetIntegerField(TEXT("status")))) + , Team(JsonObject->GetStringField(TEXT("team"))) + , bIsMassStartParticipant(false) + , LastSplitId(-1) +{ + UE_LOG(logDTFluxCore, Error, TEXT("Ctor with JSON Object")) + for(uint8 Index = 1; ; Index++) + { + FString FirstNameKey = Index == 1 ? "firstName" : FString::Printf(TEXT("firstName%i"), Index); + FString LastNameKey = Index == 1 ? "lastName" : FString::Printf(TEXT("lastName%i"), Index); + FString GenderKey = Index == 1 ? "gender" : FString::Printf(TEXT("gender%i"), Index); + // max 10 Persons + if(Index >= 10) { - if(!IsTeam()) - { - return FString::Printf(TEXT("%s %s"), *Person1.FirstName, *Person2.LastName); - } - return Team; + break; } + if (!JsonObject->HasField(FirstNameKey) && !JsonObject->HasField(LastNameKey) + && !JsonObject->HasField(GenderKey)) + { + UE_LOG(logDTFluxCore, Error, TEXT("No Corresponding Field!!!")) + break; + } + const FString FirstName = JsonObject->GetStringField(FirstNameKey); + const FString LastName = JsonObject->GetStringField(LastNameKey); + const FString Gender = JsonObject->GetStringField(GenderKey); + if (FirstName.IsEmpty() && LastName.IsEmpty()) + continue; + FDTFluxPerson Person; + Person.FirstName = FirstName; + Person.LastName = LastName; + Person.Gender = Gender; + Teammate.Add(Person); } + UE_LOG(logDTFluxCore, Error, TEXT("Ctor with JSON Object Teammate is now %i long"), Teammate.Num()); + +} + +FDTFluxParticipant FDTFluxParticipant::CreateFromJson(const TSharedPtr& JsonObject) +{ + return FDTFluxParticipant(JsonObject); +} + +int FDTFluxParticipant::GetTeammateNum() const +{ + return Teammate.Num(); +} + +bool FDTFluxParticipant::IsTeam() +{ + return Teammate.Num() < 1; +} \ No newline at end of file diff --git a/Source/DTFluxCore/Public/Assets/DTFluxModelAsset.h b/Source/DTFluxCore/Public/Assets/DTFluxModelAsset.h index 556412e..b517792 100644 --- a/Source/DTFluxCore/Public/Assets/DTFluxModelAsset.h +++ b/Source/DTFluxCore/Public/Assets/DTFluxModelAsset.h @@ -5,6 +5,7 @@ #include "CoreMinimal.h" #include "UObject/Object.h" #include "Dom/JsonObject.h" +#include "Types/Struct/DTFluxCompositeKey.h" #include "Types/Struct/DTFluxRaceDataStructs.h" #include "DTFluxModelAsset.generated.h" @@ -37,10 +38,10 @@ public: TMap ContestRankings; UPROPERTY(BlueprintReadOnly, VisibleAnywhere) - TArray StageRankings; + TMap StageRankings; UPROPERTY(BlueprintReadOnly, VisibleAnywhere) - TArray SplitRankings; + TMap SplitRankings; UFUNCTION(BlueprintCallable, CallInEditor, Category="DTFlux|ModelAsset") void AddContest(const FDTFluxContest &Contest); @@ -60,11 +61,17 @@ public: UFUNCTION(BlueprintCallable, Category="DTFlux|Contest|Utils") FString GetContestNameForId(const int InContestID); - UFUNCTION(BlueprintCallable, Category="DTFlux|Contest|Utils") - bool UpdateStageRanking(const FDTFluxStageRankings& InStageRankings); + UFUNCTION(BlueprintCallable, Category="DTFlux|Stage") + void UpdateOrCreateStageRanking(const FDTFluxStageRankings& InStageRankings); - UFUNCTION(BlueprintCallable, Category="DTFlux|Contest|Utils") - bool UpdateSplitRanking(const FDTFluxStageRankings& InStageRankings); + UFUNCTION(BlueprintCallable, Category="DTFlux|Stage") + void AddStageRanking(const FDTFluxStageRankings& InStageRankings); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Split") + void UpdateOrCreateSplitRanking(const FDTFluxSplitRankings& InSplitRankings); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Split") + void AddSplitRanking(const FDTFluxSplitRankings& InSplitRanking); UFUNCTION() void AddContestRanking(const FDTFluxContestRankings& NewContestRankings); diff --git a/Source/DTFluxCore/Public/Types/Enum/DTFluxCoreEnum.h b/Source/DTFluxCore/Public/Types/Enum/DTFluxCoreEnum.h index 39c5492..db5e7c2 100644 --- a/Source/DTFluxCore/Public/Types/Enum/DTFluxCoreEnum.h +++ b/Source/DTFluxCore/Public/Types/Enum/DTFluxCoreEnum.h @@ -3,6 +3,7 @@ #pragma once #include "CoreMinimal.h" +#include "DTFluxCoreEnum.generated.h" UENUM(BlueprintType) @@ -22,5 +23,7 @@ enum class EDTFluxConnectionStatus : uint8 { Unset = 0 UMETA(DisplayName="Unset"), Connected = 1 << 0 UMETA(DisplayName="Connected"), - Error = 2 << 1 UMETA(DisplayName="Error") + Error = 1 << 1 UMETA(DisplayName="Error"), + Closed = 1 << 2 UMETA(DisplayName="Closed"), + NotConnected= 1 << 3 UMETA(DisplayName="NotConnected") }; diff --git a/Source/DTFluxCore/Public/Types/Enum/DTFluxModelEnums.h b/Source/DTFluxCore/Public/Types/Enum/DTFluxModelEnums.h index b24ca36..6db2d73 100644 --- a/Source/DTFluxCore/Public/Types/Enum/DTFluxModelEnums.h +++ b/Source/DTFluxCore/Public/Types/Enum/DTFluxModelEnums.h @@ -11,12 +11,13 @@ UENUM(BlueprintType, Category="DTFlux|Model") enum class EDTFluxParticipantStatusType : uint8 { - Normal = 0 UMETA(DisplayName="Normal"), - OutOfRace = 1 UMETA(DisplayName="HorsCourse"), - DSQ = 2 UMETA(DisplayName="Disqualifié"), - DNF = 3 UMETA(DisplayName="Abandon"), - DNS = 4 UMETA(DisplayName="NonPartant"), - NotLinedUp = 5 UMETA(DisplayName="NonPresentAuDépart"), + Normal = 0 UMETA(DisplayName="Normal"), + OutOfRace = 1 UMETA(DisplayName="HorsCourse"), + DSQ = 2 UMETA(DisplayName="Disqualifié"), + DNF = 3 UMETA(DisplayName="Abandon"), + DNS = 4 UMETA(DisplayName="NonPartant"), + NotLinedUp = 5 UMETA(DisplayName="NonPresentAuDépart"), + Unknown = 1 << 4 UMETA(DisplayName="Unknown") }; diff --git a/Source/DTFluxCore/Public/Types/Objects/DTFluxPursuitManager.h b/Source/DTFluxCore/Public/Types/Objects/DTFluxPursuitManager.h new file mode 100644 index 0000000..7352305 --- /dev/null +++ b/Source/DTFluxCore/Public/Types/Objects/DTFluxPursuitManager.h @@ -0,0 +1,63 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Types/Struct/DTFluxPursuitStructs.h" +#include "Types/Struct/DTFluxRankingStructs.h" +#include "UObject/Object.h" +#include "DTFluxPursuitManager.generated.h" + +/** + * + */ +UCLASS(BlueprintType) +class DTFLUXCORE_API UDTFluxPursuitManager : public UObject +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit"), Transient) + TArray PursuitParticipants; + + UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit")) + FDateTime MassStart; + + UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit")) + int ContestId; + + UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit")) + int StageId; + + UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit")) + void InitForStage(const FDTFluxStageRankings& StageRankings); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit")) + TArray GetNextPursuits(int MaxPursuit); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit")) + TArray GetPursuits(int FromIndex = 0, int MaxPursuit=10); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit")) + FDateTime GetMassStart(); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit")) + static FText GetFormattedName(FDTFluxPursuit& InPursuit, const int MaxChar = 10, const FString OverflowChar = FString(TEXT("..."))); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit")) + static FText DisplayPursuit(FDTFluxPursuit& InPursuit, const int MaxWidth = 14, const FString NameOverflowChar = FString(TEXT("..."))); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit")) + static bool IsUnique(const FDTFluxPursuit& InPursuit); + +protected: + + +private: + + UPROPERTY(VisibleAnywhere, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit")) + int CurrentIndex; +}; + + diff --git a/Source/DTFluxCore/Public/Types/Objects/UDTFluxParticipantFactory.h b/Source/DTFluxCore/Public/Types/Objects/UDTFluxParticipantFactory.h new file mode 100644 index 0000000..7ac48e6 --- /dev/null +++ b/Source/DTFluxCore/Public/Types/Objects/UDTFluxParticipantFactory.h @@ -0,0 +1,23 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "Types/Struct/DTFluxTeamListStruct.h" +#include "UDTFluxParticipantFactory.generated.h" + +/** + * + */ +UCLASS() +class DTFLUXCORE_API UDTFluxParticipantFactory : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category="DTFlux|Factory") + static bool CreateParticipantFomJson(const FString& JsonString, FDTFluxParticipant& OutParticipant ); + + static bool CreateFromJsonCpp(const TSharedPtr JsonObject, FDTFluxParticipant& OutParticipant); +}; diff --git a/Source/DTFluxCore/Public/Types/Struct/DTFluxCompositeKey.h b/Source/DTFluxCore/Public/Types/Struct/DTFluxCompositeKey.h new file mode 100644 index 0000000..7e3ccec --- /dev/null +++ b/Source/DTFluxCore/Public/Types/Struct/DTFluxCompositeKey.h @@ -0,0 +1,100 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "DTFluxCompositeKey.generated.h" + +/** + * + */ +USTRUCT(BlueprintType) +struct DTFLUXCORE_API FDTFluxStageKey +{ + GENERATED_BODY() + FDTFluxStageKey() = default; + FDTFluxStageKey(const int InContestId, const int InStageId ) + :ContestId(InContestId) + , StageId(InStageId){}; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model") + int ContestId = -1; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model") + int StageId = -1; + + friend uint32 GetTypeHash(const FDTFluxStageKey& Key) + { + return HashCombine( + GetTypeHash(Key.ContestId), + GetTypeHash(Key.StageId) + ); + } + + bool operator==(const FDTFluxStageKey& Other) const + { + return ContestId == Other.ContestId && StageId == Other.StageId; + } + FString GetDisplayName() const + { + return FString::Printf(TEXT("Contest%i -| Stage%i"), ContestId, StageId); + } + + FText GetTooltipText() const + { + return FText::Format(INVTEXT("Contest{0}|Stage{1}"), + FText::AsNumber(ContestId), + FText::AsNumber(StageId)); + } + +}; + +/** + * + */ +USTRUCT(BlueprintType) +struct DTFLUXCORE_API FDTFluxSplitKey +{ + GENERATED_BODY() + FDTFluxSplitKey() = default; + FDTFluxSplitKey(const int InContestId, const int InStageId, const int InSplitId ) + :ContestId(InContestId) + , StageId(InStageId) + , SplitId(InSplitId){}; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model") + int ContestId = -1; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model") + int StageId = -1; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model") + int SplitId = -1; + + friend uint32 GetTypeHash(const FDTFluxSplitKey& Key) + { + return HashCombine( + GetTypeHash(Key.ContestId), + GetTypeHash(Key.StageId), + GetTypeHash(Key.SplitId) + ); + } + + bool operator==(const FDTFluxSplitKey& Other) const + { + return ContestId == Other.ContestId && StageId == Other.StageId && SplitId == Other.SplitId; + } + FString GetDisplayName() const + { + return FString::Printf(TEXT("Contest%i | Stage%i | Split%i"), ContestId, StageId, SplitId); + } + + FText GetTooltipText() const + { + return FText::Format(INVTEXT("Contest{0}|Stage{1}|Split{2}"), + FText::AsNumber(ContestId), + FText::AsNumber(StageId), + FText::AsNumber(SplitId) + ); + } + +}; + diff --git a/Source/DTFluxCore/Public/Types/Struct/DTFluxPursuitStructs.h b/Source/DTFluxCore/Public/Types/Struct/DTFluxPursuitStructs.h new file mode 100644 index 0000000..ed3e6c7 --- /dev/null +++ b/Source/DTFluxCore/Public/Types/Struct/DTFluxPursuitStructs.h @@ -0,0 +1,38 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "DTFluxTeamListStruct.h" +#include "DTFluxPursuitStructs.generated.h" + +/** + * + */ +USTRUCT(BlueprintType, Blueprintable) +struct DTFLUXCORE_API FDTFluxPursuit +{ + GENERATED_BODY() + +public: + FDTFluxPursuit(); + FDTFluxPursuit(const TArray& InParticipants) : Participants(InParticipants){}; + + ~FDTFluxPursuit(); + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category="DTFlux|Pursuit") + TArray Participants; + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category="DTFlux|Pursuit") + FDateTime StartTime; + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category="DTFlux|Pursuit") + int IndexMultiple = 0; + + FText GetFormattedName(const int MaxChar = 15, const FString OverflowChar = FString(TEXT("..."))); + FText DisplayPursuit(const int MaxWidth = 20, const FString NameOverflowChar = FString(TEXT("..."))); + bool IsUnique() const; + +}; + + diff --git a/Source/DTFluxCore/Public/Types/Struct/DTFluxRankingStructs.h b/Source/DTFluxCore/Public/Types/Struct/DTFluxRankingStructs.h index 419f99e..264cf5c 100644 --- a/Source/DTFluxCore/Public/Types/Struct/DTFluxRankingStructs.h +++ b/Source/DTFluxCore/Public/Types/Struct/DTFluxRankingStructs.h @@ -3,6 +3,7 @@ #pragma once #include "CoreMinimal.h" +#include "DTFluxCompositeKey.h" #include "UObject/Object.h" #include "DTFluxRankingStructs.generated.h" @@ -58,7 +59,7 @@ public: * Representing a stage ranking for a participant */ USTRUCT(BlueprintType, Category="DTFlux|Model") -struct DTFLUXCORE_API FDTFluxStageRanking +struct DTFLUXCORE_API FDTFluxDetailedRankingItem { GENERATED_BODY() public: @@ -91,19 +92,76 @@ public: }; USTRUCT(BlueprintType) -struct FDTFluxStageRankings +struct FDTFluxDetailedRankings { GENERATED_BODY() public: - UPROPERTY(BlueprintReadWrite,Category="DTFlux|Model|Ranking", VisibleAnywhere) - TArray Rankings; UPROPERTY(BlueprintReadWrite,Category="DTFlux|Model|Ranking", VisibleAnywhere) int ContestId; UPROPERTY(BlueprintReadWrite,Category="DTFlux|Model|Ranking", VisibleAnywhere) int StageId; - UPROPERTY(BlueprintReadWrite,Category="DTFlux|Model|Ranking", VisibleAnywhere) - int SplitId; + TArray Rankings; +}; +/** + * @struct FDTFluxStageRanking + * Representing a stage ranking for a participant + * This struct is only a cosmetic Struct + */ +USTRUCT(BlueprintType) +struct FDTFluxStageRanking : public FDTFluxDetailedRankingItem +{ + GENERATED_BODY() +}; + + +/** + * @struct FDTFluxSplitRanking + * Representing a split ranking for a participant + * This struct is only a cosmetic Struct + */ +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXCORE_API FDTFluxSplitRanking : public FDTFluxStageRanking +{ + GENERATED_BODY() +}; + + +/** + * @struct FDTFluxStageRankings + * StageRanking Container Struct + */ +USTRUCT(BlueprintType) +struct FDTFluxStageRankings : public FDTFluxDetailedRankings +{ + GENERATED_BODY() + inline FDTFluxStageKey GetKeyFrom(const FDTFluxStageRankings& InRankings) + { + return FDTFluxStageKey(InRankings.ContestId, InRankings.StageId); + } + inline FDTFluxStageKey GetCompositeKey() const + { + return FDTFluxStageKey(ContestId, StageId); + } +}; + + + +USTRUCT(BlueprintType) +struct FDTFluxSplitRankings : public FDTFluxDetailedRankings +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadWrite,Category="DTFlux|Model|Ranking", VisibleAnywhere) + int SplitId; + inline static FDTFluxSplitKey GetKeyFrom(const FDTFluxSplitRankings& InRankings) + { + return FDTFluxSplitKey(InRankings.ContestId, InRankings.StageId, InRankings.SplitId); + } + inline FDTFluxSplitKey GetCompositeKey() const + { + return FDTFluxSplitKey(ContestId, StageId, SplitId); + } }; diff --git a/Source/DTFluxCore/Public/Types/Struct/DTFluxSplitSensor.h b/Source/DTFluxCore/Public/Types/Struct/DTFluxSplitSensor.h new file mode 100644 index 0000000..a1dc0f3 --- /dev/null +++ b/Source/DTFluxCore/Public/Types/Struct/DTFluxSplitSensor.h @@ -0,0 +1,37 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "DTFluxTeamListStruct.h" +#include "UObject/Object.h" +#include "DTFluxSplitSensor.generated.h" + +/** + * + */ +USTRUCT(BlueprintType) +struct FDTFluxSplitSensorInfo +{ + GENERATED_BODY() + +public: + FDTFluxSplitSensorInfo() = default; + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + int Bib = -1; + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + int ContestId = -1; + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + int StageId = -1; + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + int SplitId = -1; + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + FString Time = ""; + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + FString Gap = "-"; + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + int Rank; + +}; + diff --git a/Source/DTFluxCore/Public/Types/Struct/DTFluxTeamListStruct.h b/Source/DTFluxCore/Public/Types/Struct/DTFluxTeamListStruct.h index bd7bfed..b9fa6d0 100644 --- a/Source/DTFluxCore/Public/Types/Struct/DTFluxTeamListStruct.h +++ b/Source/DTFluxCore/Public/Types/Struct/DTFluxTeamListStruct.h @@ -43,20 +43,7 @@ public: FString Club; }; -/** - * @struct FDTFluxTeamListDefinition - * Struct representing the Participant List definition - * Used to exchange data between Objects in the system - */ -USTRUCT(BlueprintType) -struct DTFLUXCORE_API FDTFluxTeamListDefinition -{ - GENERATED_BODY() -public: - UPROPERTY() - // ReSharper disable once IdentifierTypo - TArray Datas; -}; + @@ -101,14 +88,36 @@ USTRUCT(BlueprintType, Category="DTFlux|Model") struct DTFLUXCORE_API FDTFluxParticipant { GENERATED_BODY() + + friend class UDTFluxModelAsset; + friend class UDTFluxParticipantFactory; public: - UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere) - int Bib = -1; - UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere) - FDTFluxPerson Person1; - UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere) - FDTFluxPerson Person2; - UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere) + // Constructeur public par défaut requis par Unreal + FDTFluxParticipant() + : Bib(-1) + ,ContestId(-1) + , Elite(false) + , Status(static_cast(0)) + , bIsMassStartParticipant(false) + , LastSplitId(0) + { + Teammate.Reset(); + } + + + bool operator == ( int Rhs) const + { + return Rhs == 0 && Bib == -1 && Team.IsEmpty() && Club.IsEmpty() && ContestId == -1 + && Teammate.IsEmpty(); + } + + + + UPROPERTY(BlueprintReadOnly, Category="DTFlux|model", EditAnywhere) + int Bib = -1; + UPROPERTY(BlueprintReadOnly, Category="DTFlux|model", EditAnywhere) + int ContestId = -1; + UPROPERTY(BlueprintReadOnly, Category="DTFlux|model", EditAnywhere) FString Category; UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere) FString Club; @@ -121,9 +130,58 @@ public: UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere) bool bIsMassStartParticipant = false; UPROPERTY(BlueprintReadWrite, Category="DTFlux|model") - int LastSplitId = 0; - bool IsTeam() const; - void Dump() const; - FString GetParticipantFormatedName(bool Truncate = false, int MaxSize = 20) const; + int LastSplitId = -1; + + // void Dump() const; + void AddTeammate(const FDTFluxPerson& Person); + void AddTeammate(const FString LastName, const FString FirstName, const FString Gender); + FText GetFormattedName(const int MaxChar = 15, const FString OverflowChar = FString("...")); + FText GetConcatFormattedName(const int MaxChar = 20, const FString OverflowChar = FString("...")); + +private: + + // --- Constructeur privé --- + explicit FDTFluxParticipant(const TSharedPtr& JsonObject); -}; \ No newline at end of file +protected: + UPROPERTY(Category="DTFlux|model", VisibleAnywhere) + TArray Teammate; + // Méthode publique pour construire à partir d'un JSON (utilisée par la factory) + static FDTFluxParticipant CreateFromJson(const TSharedPtr& JsonObject); + int GetTeammateNum() const; + bool IsTeam(); +}; + + +/** + * @struct FDTFluxTeamListDefinition + * Struct representing the Participant List definition + * Used to exchange data between Objects in the system + */ +USTRUCT(BlueprintType) +struct DTFLUXCORE_API FDTFluxTeamListDefinition +{ + GENERATED_BODY() +public: + UPROPERTY() + // ReSharper disable once IdentifierTypo + TArray Participants; +}; + +USTRUCT(BlueprintType) +struct FDTFluxTeamStatusUpdate +{ + GENERATED_BODY() + +public: + FDTFluxTeamStatusUpdate() = default; + FDTFluxTeamStatusUpdate(const int InBib, const int InStatus) + :Bib(InBib) + , Status(static_cast(InStatus)){}; + + + UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant") + int Bib = -1; + UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant") + EDTFluxParticipantStatusType Status = EDTFluxParticipantStatusType::Unknown; +}; diff --git a/Source/DTFluxCoreSubsystem/DTFluxCoreSubsystem.Build.cs b/Source/DTFluxCoreSubsystem/DTFluxCoreSubsystem.Build.cs index 81c796b..038fdcd 100644 --- a/Source/DTFluxCoreSubsystem/DTFluxCoreSubsystem.Build.cs +++ b/Source/DTFluxCoreSubsystem/DTFluxCoreSubsystem.Build.cs @@ -19,7 +19,8 @@ public class DTFluxCoreSubsystem : ModuleRules "CoreUObject", "Engine", "Slate", - "SlateCore", + "SlateCore", + "UnrealEd", "DTFluxNetwork", "DTFluxProjectSettings", "DTFluxCore", diff --git a/Source/DTFluxCoreSubsystem/Private/DTFluxCoreSubsystem.cpp b/Source/DTFluxCoreSubsystem/Private/DTFluxCoreSubsystem.cpp index 19ee5ec..6f2d8e3 100644 --- a/Source/DTFluxCoreSubsystem/Private/DTFluxCoreSubsystem.cpp +++ b/Source/DTFluxCoreSubsystem/Private/DTFluxCoreSubsystem.cpp @@ -6,8 +6,10 @@ #include "DTFluxCoreSubsystemModule.h" #include "DTFluxGeneralSettings.h" +#include "FileHelpers.h" #include "Assets/DTFluxModelAsset.h" #include "Subsystems/DTFluxNetworkSubsystem.h" +#include "UObject/SavePackage.h" void UDTFluxCoreSubsystem::Initialize(FSubsystemCollectionBase& Collection) { @@ -17,22 +19,26 @@ void UDTFluxCoreSubsystem::Initialize(FSubsystemCollectionBase& Collection) { const UDTFluxGeneralSettings* GeneralSettings = GetDefault(); TSoftObjectPtr ModelAsset = GeneralSettings->ModelAsset; - DataStorage = ModelAsset.LoadSynchronous(); - // UE_LOG(logDTFluxCore, Log, TEXT("GeneralSettings is nullptr -> %s"), GeneralSettings == nullptr ? TEXT("TRUE") : TEXT("FALSE")); - // UE_LOG(logDTFluxCore, Log, TEXT("ModelAsset isNull() -> %s"), ModelAsset.IsNull() ? TEXT("TRUE") : TEXT("FALSE")); - // UE_LOG(logDTFluxCore, Log, TEXT("ModelAsset IsValid() -> %s"), ModelAsset.IsValid() ? TEXT("TRUE") : TEXT("FALSE")); - // UE_LOG(logDTFluxCore, Log, TEXT("DataStorage is nullptr -> %s"), DataStorage == nullptr ? TEXT("TRUE") : TEXT("FALSE")); + // if(ModelAsset.IsValid()) + // { + // } + // UE_LOG(logDTFluxCore, Error, TEXT("ModelAsset Not Valid")); + DataStorage = DataStorage = Cast(ModelAsset.LoadSynchronous()); + if(!DataStorage) + { + UE_LOG(logDTFluxCore, Error, TEXT("DataStorage Not Valid")); + } + // else + // { + // MakeStorageEditable(DataStorage); + // } } - //TODO REMOVE This as it's only for testing purpose - NetworkSubsystem = GEngine->GetEngineSubsystem(); + NetworkSubsystem = GEngine->GetEngineSubsystem(); if(NetworkSubsystem->WsStatus != EDTFluxConnectionStatus::Connected) { RegisterDelegates(); - NetworkSubsystem->Connect(); } - - } void UDTFluxCoreSubsystem::Deinitialize() @@ -40,18 +46,66 @@ void UDTFluxCoreSubsystem::Deinitialize() Super::Deinitialize(); } +void UDTFluxCoreSubsystem::SaveDataStorage() +{ + if(DataStorage) + { + DataStorage->MarkPackageDirty(); + UPackage* Package = DataStorage->GetPackage(); + if(Package->IsDirty()) + { + FString PackageName = Package->GetName(); + FString FileExtension = FPackageName::GetAssetPackageExtension(); + FString FilePath = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir() / PackageName.Replace(TEXT("/"), TEXT("/")) + FileExtension); + FString LongPackageName = DataStorage->GetOutermost()->GetName(); + FString RealAssetPath; + bool bExists = FPackageName::DoesPackageExist(PackageName); + if (!bExists) + { + UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Le package n'existe pas ou est un redirecteur")); + } + bool bSuccess = FPackageName::SearchForPackageOnDisk(LongPackageName, &RealAssetPath); + + if (bSuccess && !RealAssetPath.IsEmpty()) + { + UE_LOG(logDTFluxCoreSubsystem, Log, TEXT("Vrai path trouvé : %s"), *RealAssetPath); + } + else + { + UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Aucun path valide trouvé pour sauvegarder l'asset")); + } + UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Saving DataAsset to %s"), *FilePath); + TSoftObjectPtr ModelAsset = GetDefault()->ModelAsset; + FString RealPath = ModelAsset->GetPathName(); + UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("SoftObjectPath to %s"), *RealPath); + + FSavePackageArgs Args; + Args.TopLevelFlags = RF_Public | RF_Standalone; + Args.bSlowTask = false; + Args.SaveFlags = SAVE_None; + GEditor->SavePackage(Package, DataStorage, *FilePath, Args); + UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("DataAsset Saved")) + } + } +} + void UDTFluxCoreSubsystem::RegisterDelegates() { if(NetworkSubsystem) { - NetworkSubsystem->OnReceivedRaceData().AddDynamic(this, &UDTFluxCoreSubsystem::ParseRaceData); - NetworkSubsystem->OnReceivedTeamList().AddDynamic(this, &UDTFluxCoreSubsystem::ParseTeamList); - NetworkSubsystem->OnReceivedContestRanking().AddDynamic(this, &UDTFluxCoreSubsystem::ParseContestRanking); - NetworkSubsystem->OnReceivedStageRanking().BindUFunction(this, "ParseStageOrSplitRanking"); + NetworkSubsystem->OnReceivedRaceData().BindUFunction(this, "ProcessRaceData"); + NetworkSubsystem->OnReceivedTeamList().BindUFunction(this, "ProcessTeamList"); + NetworkSubsystem->OnReceivedContestRanking().BindUFunction(this, "ProcessContestRanking"); + NetworkSubsystem->OnReceivedStageRanking().BindUFunction(this, "ProcessStageRanking"); + NetworkSubsystem->OnReceivedSplitRanking().BindUFunction(this, "ProcessSplitRanking"); + NetworkSubsystem->OnReceivedTeamUpdate().BindUFunction(this, "ProcessTeamList"); + NetworkSubsystem->OnReceivedTeamStatusUpdate().BindUFunction(this, "ProcessTeamStatusUpdate"); + NetworkSubsystem->OnReceivedSplitSensor().BindUFunction(this, "ProcessSplitSensor"); + } } -void UDTFluxCoreSubsystem::ParseRaceData(const FDTFluxRaceData& RaceDataDefinition) +void UDTFluxCoreSubsystem::ProcessRaceData(const FDTFluxRaceData& RaceDataDefinition) { if( RaceDataDefinition.Datas.Num() > 0 ) @@ -69,66 +123,65 @@ void UDTFluxCoreSubsystem::ParseRaceData(const FDTFluxRaceData& RaceDataDefiniti { UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage is null")); } + SaveDataStorage(); return; } UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("RaceDataDefinition is empty !!!")); + } -void UDTFluxCoreSubsystem::ParseTeamList(const FDTFluxTeamListDefinition& TeamListDefinition) +void UDTFluxCoreSubsystem::ProcessTeamList(const FDTFluxTeamListDefinition& TeamListDefinition) { - UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received TeamList with %i Items"), TeamListDefinition.Datas.Num()); - for(auto InParticipantDefinition : TeamListDefinition.Datas) + UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received TeamList with %i Items"), TeamListDefinition.Participants.Num()); + for(const auto& Participant : TeamListDefinition.Participants) { - FDTFluxParticipant NewParticipant; - NewParticipant.Person1.Gender = InParticipantDefinition.Gender; - NewParticipant.Person1.FirstName = InParticipantDefinition.FirstName; - NewParticipant.Person1.LastName = InParticipantDefinition.LastName; - if(InParticipantDefinition.Team != "") - { - NewParticipant.Person2.Gender = InParticipantDefinition.Gender2; - NewParticipant.Person2.FirstName = InParticipantDefinition.FirstName2; - NewParticipant.Person2.LastName = InParticipantDefinition.LastName2; - } - NewParticipant.Bib = InParticipantDefinition.Bib; - NewParticipant.Category = InParticipantDefinition.Category; - NewParticipant.Club = InParticipantDefinition.Club; - NewParticipant.Status = static_cast(InParticipantDefinition.Status); - UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Add Participant %s %s in %i ContestId"), - *NewParticipant.Person1.FirstName, *NewParticipant.Person1.LastName, InParticipantDefinition.ContestId ); - DataStorage->AddParticipant(NewParticipant, InParticipantDefinition.ContestId); + + UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Add Participant %i in %i ContestId"), + Participant.Bib, Participant.ContestId ); + + DataStorage->AddParticipant(Participant, Participant.ContestId); } + + } -void UDTFluxCoreSubsystem::ParseContestRanking(const FDTFluxContestRankings& ContestRankings) +void UDTFluxCoreSubsystem::ProcessContestRanking(const FDTFluxContestRankings& ContestRankings) { UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received ContestRankings with %i Items"), ContestRankings.Rankings.Num()); FDTFluxContestRankings NewContestRankings = ContestRankings; NewContestRankings.SetName( DataStorage->GetContestNameForId(ContestRankings.ContestId)); DataStorage->AddContestRanking(NewContestRankings); UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestRankings added for Contest %s"), *NewContestRankings.ContestName); + DataStorage->MarkPackageDirty(); + } -void UDTFluxCoreSubsystem::ParseStageOrSplitRanking(const FDTFluxStageRankings& StageOrSplitRankings) +void UDTFluxCoreSubsystem::ProcessStageRanking(const FDTFluxStageRankings& StageRankings) { - if(StageOrSplitRankings.SplitId == -1) - { - UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received StageRankings with %i Items"), StageOrSplitRankings.Rankings.Num()); - if(!DataStorage->UpdateStageRanking(StageOrSplitRankings)) - { - DataStorage->StageRankings.Add(StageOrSplitRankings); - } - } - else - { - UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received SplitRankings with %i Items"), StageOrSplitRankings.Rankings.Num()); - } + UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received StageRankings with %i Items"), StageRankings.Rankings.Num()); + DataStorage->UpdateOrCreateStageRanking(StageRankings); + DataStorage->MarkPackageDirty(); +} +void UDTFluxCoreSubsystem::ProcessSplitRanking(const FDTFluxSplitRankings& SplitRankings) +{ + UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received SplitRanking with %i Items"), SplitRankings.Rankings.Num()); + DataStorage->UpdateOrCreateSplitRanking(SplitRankings); + DataStorage->MarkPackageDirty(); + } -void UDTFluxCoreSubsystem::OnDataReceived() +void UDTFluxCoreSubsystem::ProcessTeamStatusUpdate() { + //TODO IMPLEMENT ME !!!! } +void UDTFluxCoreSubsystem::ProcessSplitSensor() +{ + //TODO IMPLEMENT ME !!!! +} + + void UDTFluxCoreSubsystem::SendRequest(const FString& Message) { if(NetworkSubsystem) @@ -191,5 +244,3 @@ void UDTFluxCoreSubsystem::RefreshStorage() { // TODO Implement this } - - diff --git a/Source/DTFluxCoreSubsystem/Public/DTFluxCoreSubsystem.h b/Source/DTFluxCoreSubsystem/Public/DTFluxCoreSubsystem.h index d259253..dab27f2 100644 --- a/Source/DTFluxCoreSubsystem/Public/DTFluxCoreSubsystem.h +++ b/Source/DTFluxCoreSubsystem/Public/DTFluxCoreSubsystem.h @@ -13,7 +13,7 @@ -class UFDTFluxNetworkSubsystem; +class UDTFluxNetworkSubsystem; /** Forward Decl */ class UDTFluxModelAsset; @@ -82,26 +82,29 @@ public: UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem") void RefreshStorage(); - - protected: // ~Subsystem Interface virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; // ~Subsystem Interface + void SaveDataStorage(); private: - UFDTFluxNetworkSubsystem* NetworkSubsystem = nullptr; + UDTFluxNetworkSubsystem* NetworkSubsystem = nullptr; UFUNCTION() - void ParseRaceData(const FDTFluxRaceData& RaceDataDefinition); + void ProcessRaceData(const FDTFluxRaceData& RaceDataDefinition); UFUNCTION() - void ParseTeamList(const FDTFluxTeamListDefinition& TeamListDefinition); + void ProcessTeamList(const FDTFluxTeamListDefinition& TeamListDefinition); UFUNCTION() - void ParseContestRanking(const FDTFluxContestRankings& ContestRankings); + void ProcessContestRanking(const FDTFluxContestRankings& ContestRankings); UFUNCTION() - void ParseStageOrSplitRanking(const FDTFluxStageRankings& StageOrSplitRankings); + void ProcessStageRanking(const FDTFluxStageRankings& StageRankings); UFUNCTION() - void OnDataReceived(); + void ProcessSplitRanking(const FDTFluxSplitRankings& SplitRankings); + UFUNCTION() + void ProcessTeamStatusUpdate(); + UFUNCTION() + void ProcessSplitSensor(); UFUNCTION() void SendRequest(const FString& Message); UFUNCTION() diff --git a/Source/DTFluxNetwork/Private/Clients/DTFluxWebSocketClient.cpp b/Source/DTFluxNetwork/Private/Clients/DTFluxWebSocketClient.cpp index b261dd2..cff9db7 100644 --- a/Source/DTFluxNetwork/Private/Clients/DTFluxWebSocketClient.cpp +++ b/Source/DTFluxNetwork/Private/Clients/DTFluxWebSocketClient.cpp @@ -17,6 +17,24 @@ FDTFluxWebSocketClient::FDTFluxWebSocketClient() FDTFluxWebSocketClient::LastId++; } +bool FDTFluxWebSocketClient::CanSend() const +{ + return Ws.IsValid(); +} + +TSharedPtr FDTFluxWebSocketClient::GetClient( + const TArray> InClients, const FName InName) +{ + for(auto Client: InClients) + { + if(Client->ClientId == InName) + { + return Client; + } + } + return nullptr; +} + void FDTFluxWebSocketClient::Connect() { Ws = FWebSocketsModule::Get().CreateWebSocket(WsAddress); @@ -80,7 +98,10 @@ void FDTFluxWebSocketClient::AddError(const FDTFluxWsClientError Error) void FDTFluxWebSocketClient::Send(const FString& Message) const { - Ws->Send(Message); + if(Ws.IsValid()) + { + Ws->Send(Message); + } } diff --git a/Source/DTFluxNetwork/Private/Subsystems/DTFluxNetworkSubsystem.cpp b/Source/DTFluxNetwork/Private/Subsystems/DTFluxNetworkSubsystem.cpp index ec9b914..4c2e890 100644 --- a/Source/DTFluxNetwork/Private/Subsystems/DTFluxNetworkSubsystem.cpp +++ b/Source/DTFluxNetwork/Private/Subsystems/DTFluxNetworkSubsystem.cpp @@ -12,18 +12,31 @@ #include "Struct/DTFluxRequestStructs.h" #include "Struct/DTFluxRaceDataServerResponse.h" #include "Struct/DTFluxRankingServerResponse.h" +#include "Struct/DTFluxSplitSensorServerResponse.h" #include "Struct/DTFluxTeamListServerResponse.h" +#include "Types/Objects/UDTFluxParticipantFactory.h" #include "Types/Struct/DTFluxRaceDataStructs.h" +#include "Types/Struct/DTFluxSplitSensor.h" -void UFDTFluxNetworkSubsystem::Connect() +void UDTFluxNetworkSubsystem::Connect() { - WsClient->SetAddress(ConstructWsAddress(WsSettings.Address, WsSettings.Path, WsSettings.Port)); - WsClient->Connect(); + WsClient->SetAddress(ConstructWsAddress(WsSettings.Address, WsSettings.Path, WsSettings.Port)); + WsClient->Connect(); } -void UFDTFluxNetworkSubsystem::SendRequest(const EDTFluxRequestType RequestType, int InContestId, int InStageId, - int InSplitId) +void UDTFluxNetworkSubsystem::Disconnect() +{ + WsClient->Disconnect(); +} + +void UDTFluxNetworkSubsystem::Reconnect() +{ + ReconnectWs(FName("Ws_Client_0")); +} + +void UDTFluxNetworkSubsystem::SendRequest(const EDTFluxRequestType RequestType, int InContestId, int InStageId, + int InSplitId) { FString Message; switch (RequestType) @@ -52,12 +65,24 @@ void UFDTFluxNetworkSubsystem::SendRequest(const EDTFluxRequestType RequestType, SendMessage(Message); } -void UFDTFluxNetworkSubsystem::SendMessage(const FString& Message) +void UDTFluxNetworkSubsystem::SendMessage(const FString& Message) { - WsClient->Send(Message); + UE_LOG(logDTFluxCore, Warning, TEXT("Sending Message %s"), *Message); + + + if(WsClient.IsValid() && WsClient->CanSend()) + { + WsClient->Send(Message); + UE_LOG(logDTFluxNetwork, Log, TEXT("Can send request")); + + } + else + { + UE_LOG(logDTFluxNetwork, Error, TEXT("[Websocket Not Connected]. Connect before sending requests...")); + } } -void UFDTFluxNetworkSubsystem::Initialize(FSubsystemCollectionBase& Collection) +void UDTFluxNetworkSubsystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); FDTFluxCoreModule& DTFluxCore = FModuleManager::Get().LoadModuleChecked("DTFluxCore"); @@ -81,22 +106,17 @@ void UFDTFluxNetworkSubsystem::Initialize(FSubsystemCollectionBase& Collection) } } -void UFDTFluxNetworkSubsystem::Deinitialize() +void UDTFluxNetworkSubsystem::Deinitialize() { Super::Deinitialize(); } -void UFDTFluxNetworkSubsystem::WsSettingsChanged(const FDTFluxWsSettings& NewWsSettings) +void UDTFluxNetworkSubsystem::WsSettingsChanged(const FDTFluxWsSettings& NewWsSettings) { // TODO Implement a ClientSelector To retrieve impacted WsClients and populate changes or maybe create a delegate bool bNeedsReload = WsSettings != NewWsSettings; WsSettings = NewWsSettings; - // UE_LOG(logDTFluxNetwork, Warning, TEXT("WSocket Settings Changed \n\t Address : %s Path : %s Port : %i\n\tbShouldConnectAtStatup : %s, bShouldAutoReconnectOnClosed %s, bShouldAutoReconnectOnError %s"), - // *NewWsSettings.Address, *NewWsSettings.Path, NewWsSettings.Port, - // NewWsSettings.bShouldConnectAtStartup ? TEXT("True") : TEXT("False"), - // NewWsSettings.bShouldAutoReconnectOnClosed ? TEXT("True") : TEXT("False"), - // NewWsSettings.bShouldAutoReconnectOnError ? TEXT("True") : TEXT("False") ); if( bNeedsReload || WsSettings.bShouldConnectAtStartup) { UE_LOG(logDTFluxNetwork, Warning, TEXT("WSocket Settings needs Reloding client")) @@ -105,46 +125,46 @@ void UFDTFluxNetworkSubsystem::WsSettingsChanged(const FDTFluxWsSettings& NewWsS } -void UFDTFluxNetworkSubsystem::HttpSettingsChanged(const FDTFluxHttpSettings& NewHttpSettings) +void UDTFluxNetworkSubsystem::HttpSettingsChanged(const FDTFluxHttpSettings& NewHttpSettings) { // TODO Implement a ClientSelector To retrieve impacted HttpClients and populate changes or maybe create a delegate HttpSettings = NewHttpSettings; } -void UFDTFluxNetworkSubsystem::ReconnectWs(const FName WsClientId) +void UDTFluxNetworkSubsystem::ReconnectWs(const FName WsClientId) { FString NewAddress = ConstructWsAddress(WsSettings.Address, WsSettings.Path, WsSettings.Port); WsClient->SetAddress(NewAddress); WsClient->Reconnect(); } -void UFDTFluxNetworkSubsystem::ReconnectHttp(const FName WsClientId) +void UDTFluxNetworkSubsystem::ReconnectHttp(const FName WsClientId) { } -void UFDTFluxNetworkSubsystem::RegisterWebSocketEvents() +void UDTFluxNetworkSubsystem::RegisterWebSocketEvents() { OnWsConnectedEventDelegateHandle = - WsClient->RegisterConnectedEvent().AddUObject(this, &UFDTFluxNetworkSubsystem::OnWebSocketConnected_Subsystem); + WsClient->RegisterConnectedEvent().AddUObject(this, &UDTFluxNetworkSubsystem::OnWebSocketConnected_Subsystem); OnWsConnectionErrorEventDelegateHandle = WsClient->RegisterConnectionError() - .AddUObject(this, &UFDTFluxNetworkSubsystem::OnWebSocketConnectionError_Subsystem); + .AddUObject(this, &UDTFluxNetworkSubsystem::OnWebSocketConnectionError_Subsystem); OnWsClosedEventDelegateHandle = WsClient->RegisterClosedEvent() - .AddUObject(this, &UFDTFluxNetworkSubsystem::OnWebSocketClosedEvent_Subsystem); + .AddUObject(this, &UDTFluxNetworkSubsystem::OnWebSocketClosedEvent_Subsystem); OnWsMessageEventDelegateHandle = WsClient->RegisterMessageEvent() - .AddUObject(this, &UFDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem); + .AddUObject(this, &UDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem); OnWsMessageSentEventDelegateHandle = WsClient->RegisterMessageSentEvent() - .AddUObject(this, &UFDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem); + .AddUObject(this, &UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem); } -void UFDTFluxNetworkSubsystem::RegisterHttpEvents() +void UDTFluxNetworkSubsystem::RegisterHttpEvents() { } -void UFDTFluxNetworkSubsystem::UnregisterWebSocketEvents() +void UDTFluxNetworkSubsystem::UnregisterWebSocketEvents() { if(OnWsConnectedEventDelegateHandle.IsValid()) { @@ -168,35 +188,219 @@ void UFDTFluxNetworkSubsystem::UnregisterWebSocketEvents() } } -void UFDTFluxNetworkSubsystem::UnregisterHttpEvents() +void UDTFluxNetworkSubsystem::UnregisterHttpEvents() { } -void UFDTFluxNetworkSubsystem::OnWebSocketConnected_Subsystem() +void UDTFluxNetworkSubsystem::OnWebSocketConnected_Subsystem() { + WsStatus = EDTFluxConnectionStatus::Connected; OnWebSocketConnected.Broadcast(); UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws Is Connected with %s"), *WsClient->GetAddress()) } -void UFDTFluxNetworkSubsystem::OnWebSocketConnectionError_Subsystem(const FString& Error) +void UDTFluxNetworkSubsystem::OnWebSocketConnectionError_Subsystem(const FString& Error) { UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws Error with %s : %s"), *WsClient->GetAddress(), *Error); + WsStatus = EDTFluxConnectionStatus::Error; if(WsSettings.bShouldAutoReconnectOnError) { WsClient->Reconnect(); } } -void UFDTFluxNetworkSubsystem::OnWebSocketClosedEvent_Subsystem(int32 StatusCode, const FString& Reason, bool bWasClean) +void UDTFluxNetworkSubsystem::OnWebSocketClosedEvent_Subsystem(int32 StatusCode, const FString& Reason, bool bWasClean) { UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws Error with %s :\n Reason : %s \tStatusCode : %i, bWasClean : %s"), *WsClient->GetAddress(), *Reason, StatusCode, bWasClean ? TEXT("True") : TEXT("False")); + WsStatus = EDTFluxConnectionStatus::Closed; +} + +void UDTFluxNetworkSubsystem::ParseTeamListResponse(const FDTFluxServerResponse& ServerResponse) +{ + TSharedPtr JsonObject; + TSharedRef> Reader = TJsonReaderFactory<>::Create(ServerResponse.RawMessage); + + if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid()) + { + UE_LOG(logDTFluxNetwork, Error, TEXT("JSON invalide : %s"), *ServerResponse.RawMessage); + return; + } + const TArray>* DataArray; + if (!JsonObject->TryGetArrayField(TEXT("datas"), DataArray)) + { + UE_LOG(logDTFluxNetwork, Error, TEXT("Aucun champ 'datas' trouvé dans le team-list")); + return; + } + FDTFluxTeamListDefinition TeamListDefinition; + for (const TSharedPtr& Value : *DataArray) + { + if (Value->Type == EJson::Object) + { + const TSharedPtr Item = Value->AsObject(); + + FDTFluxParticipant Participant; + UDTFluxParticipantFactory::CreateFromJsonCpp(Item, Participant); + TeamListDefinition.Participants.Add(Participant); + } + } + + UE_LOG(logDTFluxNetwork, Warning, TEXT("Inserting %i Participants [%s]"), TeamListDefinition.Participants.Num(), + OnTeamListReceived.ExecuteIfBound(TeamListDefinition) ? TEXT("SUCCESS_SEND") : TEXT("NOT_BOUNDED")); +} + +void UDTFluxNetworkSubsystem::ParseRaceData(const FDTFluxServerResponse& Response) +{ + FDTFluxRaceDataResponse RaceData; + if(FJsonObjectConverter::JsonObjectStringToUStruct(Response.RawMessage, &RaceData)) + { + //convert + FDTFluxRaceData RaceDataDefinition; + for(auto Contest : RaceData.Datas) + { + FDTFluxContest NewContest; + NewContest.Name = Contest.Name; + NewContest.ContestId = Contest.Id; + NewContest.Date = Contest.Date; + UE_LOG(logDTFluxNetwork, Warning, TEXT("Contest %i [%s] Starting at %s \nStages: \n"), Contest.Id, *Contest.Date.ToString(),*Contest.Name); + for(auto Stage : Contest.Stages) + { + FDTFluxStage NewStage; + NewStage.StageId = Stage.Id; + NewStage.Name = Stage.Name; + FString StartTimeFString = FString::Printf(TEXT("%s %s"), + *NewContest.Date.ToFormattedString(TEXT("%Y-%m-%d")), + *Stage.StartTime + ); + FString EndTimeFString = FString::Printf(TEXT("%s %s"), + *NewContest.Date.ToFormattedString(TEXT("%Y-%m-%d")), + *Stage.EndTime + ); + FString CutOffFString = FString::Printf(TEXT("%s %s"), + *NewContest.Date.ToFormattedString(TEXT("%Y-%m-%d")), + *Stage.CutOff + ); + FDateTime::Parse(StartTimeFString, NewStage.StartTime); + FDateTime::Parse(EndTimeFString, NewStage.EndTime); + FDateTime::Parse(CutOffFString, NewStage.CutOff); + NewContest.Stages.Add(NewStage); + UE_LOG(logDTFluxNetwork, Warning, TEXT("Stage %i [%s]: \nSTartTime Received [%s] -> Datetime[%s], CutOff [%s], EndTime [%s] \n"), Stage.Id, *Stage.Name, + *Stage.StartTime, *NewStage.StartTime.ToString(), *NewStage.CutOff.ToString(), *NewStage.EndTime.ToString()); + } + UE_LOG(logDTFluxNetwork, Warning, TEXT("Contest %i [%s]\nSplits: \n"), Contest.Id, *Contest.Name); + for(auto Split: Contest.Splits) + { + FDTFluxSplit NewSplit; + NewSplit.SplitId = Split.Id; + NewSplit.Name = Split.Name; + NewContest.Splits.Add(NewSplit); + UE_LOG(logDTFluxNetwork, Warning, TEXT("Split %i [%s]: \n"), Split.Id, *Split.Name); + } + RaceDataDefinition.Datas.Add(NewContest); + } + UE_LOG(logDTFluxNetwork, Warning, TEXT("Sending %i Contests, [%s]"), RaceDataDefinition.Datas.Num(), + OnRaceDataReceived.ExecuteIfBound(RaceDataDefinition) ? TEXT("SUCCESS_SEND") : TEXT("NOT_BOUNDED")); + return; + } + UE_LOG(logDTFluxNetwork, Error, TEXT("ParseRaceData() for JSON Response : %s"), *Response.RawMessage); +} + +void UDTFluxNetworkSubsystem::ParseContestRanking(const FDTFluxServerResponse& Response) +{ + FDTFluxContestRankingResponse ContestRankingResponse; + if(FJsonObjectConverter::JsonObjectStringToUStruct(Response.RawMessage, &ContestRankingResponse)) + { + FDTFluxContestRankings ContestRankings; + ContestRankings.ContestId = ContestRankingResponse.ContestID; + for(auto& RankingItem : ContestRankingResponse.Datas) + { + FDTFluxContestRanking Temp = RankingItem; + ContestRankings.Rankings.Add(Temp); + } + UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws ContestRanking Data Sent for Contest %i, [%s]"), ContestRankings.ContestId, + OnContestRankingReceived.ExecuteIfBound(ContestRankings) ? TEXT("SUCCESS_SEND") : TEXT("NOT_BOUNDED")); + return; + } + UE_LOG(logDTFluxNetwork, Error, TEXT("ParseContestRanking() for JSON Response : %s"), *Response.RawMessage); + +} + +void UDTFluxNetworkSubsystem::ParseStageRankingResponse(const FDTFluxServerResponse& Response) +{ + FDTFluxStageRankingResponse RankingResponse; + if(FJsonObjectConverter::JsonObjectStringToUStruct(Response.RawMessage, &RankingResponse)) + { + FDTFluxStageRankings NewRankings; + NewRankings.ContestId = Response.ContestID; + NewRankings.StageId = Response.StageID; + NewRankings.Rankings = static_cast>(RankingResponse.Datas); + UE_LOG(logDTFluxNetwork, Warning, TEXT("StageRanking Data Sent for Contest %i, Stage %i\n[Result] : %s"), + NewRankings.ContestId, NewRankings.StageId, + OnStageRankingReceived.ExecuteIfBound(NewRankings) ? TEXT("SUCCESS_SEND") : TEXT("NOT_BOUNDED") + ); + return; + } + UE_LOG(logDTFluxNetwork, Error, TEXT("ParseStageRankingResponse() for JSON Response : %s"), *Response.RawMessage); +} + +void UDTFluxNetworkSubsystem::ParseSplitRankingResponse(const FDTFluxServerResponse& Response) +{ + FDTFluxSplitRankingResponse SplitRankingResponse; + if(FJsonObjectConverter::JsonObjectStringToUStruct(Response.RawMessage, &SplitRankingResponse)) + { + FDTFluxSplitRankings NewSplitRankings; + NewSplitRankings.ContestId = Response.ContestID; + NewSplitRankings.StageId = Response.StageID; + NewSplitRankings.SplitId = Response.SplitID; + NewSplitRankings.Rankings = static_cast>(SplitRankingResponse.Datas); + UE_LOG(logDTFluxNetwork, Warning, TEXT("SplitRanking Data Sent for Contest %i, Stage %i and Split %i\n[Result] : %s"), + NewSplitRankings.ContestId, NewSplitRankings.StageId, NewSplitRankings.SplitId, + OnSplitRankingReceived.ExecuteIfBound(NewSplitRankings) ? TEXT("SUCCESS_SEND") : TEXT("NOT_BOUNDED")); + return; + } + UE_LOG(logDTFluxNetwork, Error, TEXT("ParseSplitRankingResponse() for JSON Response : %s"), *Response.RawMessage); + +} + +void UDTFluxNetworkSubsystem::ParseStatusUpdateResponse(const FDTFluxServerResponse& Response) +{ + FDTFluxTeamStatusUpdate StatusUpdateResponse; + + if (FJsonObjectConverter::JsonObjectStringToUStruct(Response.RawMessage, &StatusUpdateResponse)) + { + UE_LOG(logDTFluxNetwork, Warning, TEXT("Status Update for bib %i \n[Result] : %s\n"), + StatusUpdateResponse.Bib, + OnTeamStatusUpdateReceived.ExecuteIfBound(StatusUpdateResponse) ? TEXT("SUCCESS_SEND") : TEXT("NOT_BOUNDED")); + return; + } + UE_LOG(logDTFluxNetwork, Error, TEXT("ParseStatusUpdateResponse() for JSON Response : %s"), *Response.RawMessage); +} + +void UDTFluxNetworkSubsystem::ParseSplitSensorResponse(const FDTFluxServerResponse& Response) +{ + FDTFluxSplitSensorResponse SplitSensorResponse; + if(FJsonObjectConverter::JsonObjectStringToUStruct(Response.RawMessage, &SplitSensorResponse)) + { + for(const auto& SplitSensorInfoResponse : SplitSensorResponse.Datas) + { + FDTFluxSplitSensorInfo NewSplitSensorInfo; + NewSplitSensorInfo.Bib = SplitSensorInfoResponse.Bib; + NewSplitSensorInfo.ContestId = SplitSensorInfoResponse.ContestID; + NewSplitSensorInfo.StageId = SplitSensorInfoResponse.StageID; + NewSplitSensorInfo.SplitId = SplitSensorInfoResponse.SplitID; + NewSplitSensorInfo.Time = SplitSensorInfoResponse.Time; + UE_LOG(logDTFluxNetwork, Warning, TEXT("Status Update for bib %i in Contest %i, Stage %i in split %i\n[Result] : %s\n"), + NewSplitSensorInfo.Bib, NewSplitSensorInfo.ContestId, NewSplitSensorInfo.StageId, NewSplitSensorInfo.SplitId, + OnSplitSensorReceived.ExecuteIfBound(NewSplitSensorInfo) ? TEXT("SUCCESS_SEND") : TEXT("NOT_BOUNDED")); + } + } + UE_LOG(logDTFluxNetwork, Error, TEXT("ParseSplitSensorResponse() failed for JSON Response : %s"), *Response.RawMessage); } //TODO reforge API to keep track of Requests -void UFDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& MessageString) +void UDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& MessageString) { - UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws %s :\nMessage Received : %s"), *WsClient->GetAddress(), *MessageString); + UE_LOG(logDTFluxNetwork, Warning, TEXT("Client %s :\nMessage Received : %s"), *WsClient->GetAddress(), *MessageString); //Do Something With the message FDTFluxServerResponse Response; Response.ReceivedAt = FDateTime::Now(); @@ -209,97 +413,50 @@ void UFDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& // return DataReceived.Broadcast(Response); if(Response.Type.Contains("race-data")) { - - FDTFluxRaceDataResponse RaceData; - FJsonObjectConverter::JsonObjectStringToUStruct(Response.RawMessage, &RaceData); - //convert - FDTFluxRaceData RaceDataDefinition; - for(auto Contest : RaceData.Datas) - { - FDTFluxContest NewContest; - NewContest.Name = Contest.Name; - NewContest.ContestId = Contest.Id; - NewContest.Date = Contest.Date; - UE_LOG(logDTFluxNetwork, Warning, TEXT("Contest %i [%s] Starting at %s \nStages: \n"), Contest.Id, *Contest.Date.ToString(),*Contest.Name); - for(auto Stage : Contest.Stages) - { - FDTFluxStage NewStage; - NewStage.StageId = Stage.Id; - NewStage.Name = Stage.Name; - FString StartTimeFString = FString::Printf(TEXT("%s %s"), - *NewContest.Date.ToFormattedString(TEXT("%Y-%m-%d")), - *Stage.StartTime - ); - FString EndTimeFString = FString::Printf(TEXT("%s %s"), - *NewContest.Date.ToFormattedString(TEXT("%Y-%m-%d")), - *Stage.EndTime - ); - FString CutOffFString = FString::Printf(TEXT("%s %s"), - *NewContest.Date.ToFormattedString(TEXT("%Y-%m-%d")), - *Stage.CutOff - ); - FDateTime::Parse(StartTimeFString, NewStage.StartTime); - FDateTime::Parse(EndTimeFString, NewStage.EndTime); - FDateTime::Parse(CutOffFString, NewStage.CutOff); - NewContest.Stages.Add(NewStage); - UE_LOG(logDTFluxNetwork, Warning, TEXT("Stage %i [%s]: \nSTartTime Received [%s] -> Datetime[%s], CutOff [%s], EndTime [%s] \n"), Stage.Id, *Stage.Name, - *Stage.StartTime, *NewStage.StartTime.ToString(), *NewStage.CutOff.ToString(), *NewStage.EndTime.ToString()); - } - UE_LOG(logDTFluxNetwork, Warning, TEXT("Contest %i [%s]\nSplits: \n"), Contest.Id, *Contest.Name); - for(auto Split: Contest.Splits) - { - FDTFluxSplit NewSplit; - NewSplit.SplitId = Split.Id; - NewSplit.Name = Split.Name; - NewContest.Splits.Add(NewSplit); - UE_LOG(logDTFluxNetwork, Warning, TEXT("Split %i [%s]: \n"), Split.Id, *Split.Name); - } - RaceDataDefinition.Datas.Add(NewContest); - } - UE_LOG(logDTFluxNetwork, Warning, TEXT("Sending %i Contests"), RaceDataDefinition.Datas.Num()); - return OnRaceDataReceived.Broadcast(RaceDataDefinition); + UE_LOG(logDTFluxNetwork, Warning, TEXT("Race-Data Received")); + return ParseRaceData(Response); } if(Response.Type.Contains("team-list")) { - UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws Team-List Data")); - FDTFluxTeamListDefinition TeamListDefinition; - FJsonObjectConverter::JsonObjectStringToUStruct(Response.RawMessage, &TeamListDefinition); - UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws Team-List Data Sent")); - return OnTeamListReceived.Broadcast(TeamListDefinition); + UE_LOG(logDTFluxNetwork, Warning, TEXT("Team-List Received")); + return ParseTeamListResponse(Response); } if(Response.Type.Contains("contest-ranking")) { - UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws Contest-Ranking Data")); - FDTFluxContestRankingResponse ContestRankingResponse; - FJsonObjectConverter::JsonObjectStringToUStruct(Response.RawMessage, &ContestRankingResponse); - FDTFluxContestRankings ContestRankings; - ContestRankings.ContestId = ContestRankingResponse.ContestID; - for(auto& RankingItem : ContestRankingResponse.Datas) - { - FDTFluxContestRanking Temp = RankingItem; - ContestRankings.Rankings.Add(Temp); - } - UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws ContestRanking Data Sent for Contest %i"), ContestRankings.ContestId); - return OnContestRankingReceived.Broadcast(ContestRankings); + UE_LOG(logDTFluxNetwork, Warning, TEXT("Contest-Ranking Received")); + return ParseContestRanking(Response); } if(Response.Type.Contains("stage-ranking") ) { - // StageRanking - UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws Stage-Ranking Data")); - FDTFluxStageRankingResponse StageRankingResponse; - FJsonObjectConverter::JsonObjectStringToUStruct(Response.RawMessage, &StageRankingResponse); - FDTFluxStageRankings StageRankings; - StageRankings.ContestId = StageRankingResponse.ContestId; - StageRankings.StageId = StageRankingResponse.StageId; - StageRankings.SplitId = StageRankingResponse.SplitId; - for(FDTFluxStageRanking& InRanking : StageRankingResponse.Datas) + if(Response.SplitID == -1) { - FDTFluxStageRanking Temp = InRanking; - StageRankings.Rankings.Add(InRanking); + // StageRanking + UE_LOG(logDTFluxNetwork, Warning, TEXT("Stage-Ranking Data")); + ParseStageRankingResponse(Response); } - UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws StageRanking Data Sent for Contest %i and Stage %i\n[Result] : %s"), - StageRankings.ContestId, StageRankings.StageId, OnStageRankingReceived.ExecuteIfBound(StageRankings) ? TEXT("Executed") : TEXT("Not Bound !!!")); - return; + else + { + // StageRanking + UE_LOG(logDTFluxNetwork, Warning, TEXT("Split-Ranking Data")); + return ParseSplitRankingResponse(Response); + } + } + if(Response.Type.Contains("split-sensor")) + { + UE_LOG(logDTFluxNetwork, Warning, TEXT("split-sensor Data")); + ParseSplitSensorResponse(Response); + + } + if(Response.Type.Contains("status-update")) + { + UE_LOG(logDTFluxNetwork, Warning, TEXT("status-update Data")); + ParseStatusUpdateResponse(Response); + + } + if(Response.Type.Contains("team-update")) + { + UE_LOG(logDTFluxNetwork, Warning, TEXT("team-update Data")); + ParseTeamListResponse(Response); } } @@ -309,12 +466,12 @@ void UFDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& } -void UFDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent) +void UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent) { UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws %s :\nMessage Sent: %s"), *WsClient->GetAddress(), *MessageSent); } -FString UFDTFluxNetworkSubsystem::ConstructWsAddress(const FString& Address, const FString& Path, const int& Port) +FString UDTFluxNetworkSubsystem::ConstructWsAddress(const FString& Address, const FString& Path, const int& Port) { FString NewAddress; if( !Address.Contains("ws://") && !Address.Contains("wss://")) @@ -324,4 +481,5 @@ FString UFDTFluxNetworkSubsystem::ConstructWsAddress(const FString& Address, con NewAddress +=Address + FString(":") + FString::FromInt(Port) + Path; return NewAddress; // UE_LOG(logDTFluxNetwork, Log, TEXT("NewAddress : %s"), *NewAddress); + } diff --git a/Source/DTFluxNetwork/Public/Clients/DTFluxWebSocketClient.h b/Source/DTFluxNetwork/Public/Clients/DTFluxWebSocketClient.h index 36ddcf9..10d819e 100644 --- a/Source/DTFluxNetwork/Public/Clients/DTFluxWebSocketClient.h +++ b/Source/DTFluxNetwork/Public/Clients/DTFluxWebSocketClient.h @@ -52,18 +52,12 @@ class DTFLUXNETWORK_API FDTFluxWebSocketClient : public TSharedFromThis GetClient(const TArray> InClients, const FName InName) -{ - for(auto Client: InClients) - { - if(Client->ClientId == InName) - { - return Client; - } - } - return nullptr; -} +static TSharedPtr GetClient(const TArray> InClients, const FName InName); + void Connect(); void Reconnect(); diff --git a/Source/DTFluxNetwork/Public/Struct/DTFluxRankingServerResponse.h b/Source/DTFluxNetwork/Public/Struct/DTFluxRankingServerResponse.h index a1c7299..e707e53 100644 --- a/Source/DTFluxNetwork/Public/Struct/DTFluxRankingServerResponse.h +++ b/Source/DTFluxNetwork/Public/Struct/DTFluxRankingServerResponse.h @@ -33,7 +33,18 @@ public: }; USTRUCT(BlueprintType) -struct DTFLUXNETWORK_API FDTFluxStageRankingResponseItem : public FDTFluxStageRanking +struct DTFLUXNETWORK_API FDTFluxStageRankingResponseItem : public FDTFluxDetailedRankingItem +{ + GENERATED_BODY() + +public: + UPROPERTY() + FString Type = "team-stage-ranking"; +}; + + +USTRUCT(BlueprintType) +struct DTFLUXNETWORK_API FDTFluxSplitRankingResponseItem : public FDTFluxDetailedRankingItem { GENERATED_BODY() @@ -43,7 +54,7 @@ public: }; USTRUCT(BlueprintType) -struct DTFLUXNETWORK_API FDTFluxStageRankingResponse : public FDTFluxStageRankings +struct DTFLUXNETWORK_API FDTFluxStageRankingResponse { GENERATED_BODY() @@ -56,3 +67,18 @@ public: // ReSharper disable once IdentifierTypo TArray Datas; }; + +USTRUCT(BlueprintType) +struct DTFLUXNETWORK_API FDTFluxSplitRankingResponse +{ + GENERATED_BODY() + +public: + UPROPERTY() + FString Type = "stage-ranking"; + + + UPROPERTY() + // ReSharper disable once IdentifierTypo + TArray Datas; +}; diff --git a/Source/DTFluxNetwork/Public/Struct/DTFluxSplitSensorServerResponse.h b/Source/DTFluxNetwork/Public/Struct/DTFluxSplitSensorServerResponse.h index c6a5292..b319fe2 100644 --- a/Source/DTFluxNetwork/Public/Struct/DTFluxSplitSensorServerResponse.h +++ b/Source/DTFluxNetwork/Public/Struct/DTFluxSplitSensorServerResponse.h @@ -25,10 +25,7 @@ public: int SplitID; UPROPERTY() FString Time = "-"; - UPROPERTY() - FString Gap = "-"; - UPROPERTY() - int Rank; + }; USTRUCT(BlueprintType) diff --git a/Source/DTFluxNetwork/Public/Subsystems/DTFluxNetworkSubsystem.h b/Source/DTFluxNetwork/Public/Subsystems/DTFluxNetworkSubsystem.h index 030677e..b0caa60 100644 --- a/Source/DTFluxNetwork/Public/Subsystems/DTFluxNetworkSubsystem.h +++ b/Source/DTFluxNetwork/Public/Subsystems/DTFluxNetworkSubsystem.h @@ -6,8 +6,11 @@ #include "Struct/DTFluxServerResponseStruct.h" #include "Subsystems/EngineSubsystem.h" #include "Types/DTFluxNetworkSettingsTypes.h" -#include "Types/Enum/DTfluxCoreEnum.h" +#include "Types/Enum/DTFluxCoreEnum.h" +#include "Types/Struct/DTFluxRaceDataStructs.h" #include "Types/Struct/DTFluxRankingStructs.h" +#include "Types/Struct/DTFluxSplitSensor.h" +#include "Types/Struct/DTFluxTeamListStruct.h" #include "DTFluxNetworkSubsystem.generated.h" @@ -21,53 +24,79 @@ typedef TSharedPtr FDTFluxHttpClientSP; * */ UCLASS(Blueprintable) -class DTFLUXNETWORK_API UFDTFluxNetworkSubsystem : public UEngineSubsystem +class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem { GENERATED_BODY() public: + UPROPERTY() EDTFluxConnectionStatus WsStatus = EDTFluxConnectionStatus::Unset; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWebSocketConnected); UPROPERTY(BlueprintAssignable, Category="DTFlux|Network") FOnWebSocketConnected OnWebSocketConnected; - DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRaceDataReceived, const FDTFluxRaceData&, RaceDataDefinition); + DECLARE_DELEGATE_OneParam(FOnRaceDataReceived, const FDTFluxRaceData& /*RaceDataDefinition*/); FOnRaceDataReceived OnRaceDataReceived; FOnRaceDataReceived& OnReceivedRaceData() { return OnRaceDataReceived; }; - DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTeamListReceived, const FDTFluxTeamListDefinition&, TeamListDefinition); + DECLARE_DELEGATE_OneParam(FOnTeamListReceived, const FDTFluxTeamListDefinition& /*TeamListDefinition*/); FOnTeamListReceived OnTeamListReceived; FOnTeamListReceived& OnReceivedTeamList() { return OnTeamListReceived; }; - DECLARE_DELEGATE_OneParam(FOnStageRankingReceived, const FDTFluxStageRankings&); + DECLARE_DELEGATE_OneParam(FOnStageRankingReceived, const FDTFluxStageRankings& /*StageRankings*/); FOnStageRankingReceived OnStageRankingReceived; FOnStageRankingReceived& OnReceivedStageRanking() { return OnStageRankingReceived; } + DECLARE_DELEGATE_OneParam(FOnSplitRankingReceived, const FDTFluxSplitRankings& /*SplitRankings*/); + FOnSplitRankingReceived OnSplitRankingReceived; + FOnSplitRankingReceived& OnReceivedSplitRanking() + { + return OnSplitRankingReceived; + } - DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnContestRankingReceived, const FDTFluxContestRankings&, ContestRankings); + DECLARE_DELEGATE_OneParam(FOnContestRankingReceived, const FDTFluxContestRankings& /*ContestRankings*/); FOnContestRankingReceived OnContestRankingReceived; FOnContestRankingReceived& OnReceivedContestRanking() { return OnContestRankingReceived; }; + DECLARE_DELEGATE_OneParam(FOnSplitSensorReceived, const FDTFluxSplitSensorInfo& /*ContestRankings*/); + FOnSplitSensorReceived OnSplitSensorReceived; + FOnSplitSensorReceived& OnReceivedSplitSensor() + { + return OnSplitSensorReceived; + }; - + DECLARE_DELEGATE_OneParam(FOnTeamUpdateReceived, const FDTFluxParticipant& /*ParticipantToUpdate*/); + FOnTeamUpdateReceived OnTeamUpdateReceived; + FOnTeamUpdateReceived& OnReceivedTeamUpdate() + { + return OnTeamUpdateReceived; + }; + + DECLARE_DELEGATE_OneParam(FOnTeamStatusUpdateReceived, const FDTFluxTeamStatusUpdate& /*TeamToUpdate*/); + FOnTeamStatusUpdateReceived OnTeamStatusUpdateReceived; + FOnTeamStatusUpdateReceived& OnReceivedTeamStatusUpdate() + { + return OnTeamStatusUpdateReceived; + }; UFUNCTION(BlueprintCallable, Category="DTFlux|Network") void Connect(); - // - // UFUNCTION(BlueprintCallable, Category="DTFlux|Network") - // void Reconnect(); + UFUNCTION(BlueprintCallable, Category="DTFlux|Network") + void Disconnect(); + UFUNCTION(BlueprintCallable, Category="DTFlux|Network") + void Reconnect(); UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem") @@ -103,6 +132,13 @@ private: void OnWebSocketConnected_Subsystem(); void OnWebSocketConnectionError_Subsystem(const FString& Error); void OnWebSocketClosedEvent_Subsystem(int32 StatusCode , const FString& Reason, bool bWasClean); + void ParseTeamListResponse(const FDTFluxServerResponse& ServerResponse); + void ParseRaceData(const FDTFluxServerResponse& Response); + void ParseContestRanking(const FDTFluxServerResponse& Response); + void ParseStageRankingResponse(const FDTFluxServerResponse& Response); + void ParseSplitRankingResponse(const FDTFluxServerResponse& Response); + void ParseStatusUpdateResponse(const FDTFluxServerResponse& Response); + void ParseSplitSensorResponse(const FDTFluxServerResponse& Response); void OnWebSocketMessageEvent_Subsystem(const FString& MessageString); void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent); diff --git a/Source/DTFluxProjectSettings/DTFluxProjectSettings.Build.cs b/Source/DTFluxProjectSettings/DTFluxProjectSettings.Build.cs index 60ec482..30c821f 100644 --- a/Source/DTFluxProjectSettings/DTFluxProjectSettings.Build.cs +++ b/Source/DTFluxProjectSettings/DTFluxProjectSettings.Build.cs @@ -21,7 +21,9 @@ public class DTFluxProjectSettings : ModuleRules "Slate", "SlateCore", "DeveloperSettings", - "DTFluxCore" + "DTFluxCore", + "Settings", + "DeveloperSettings" } ); diff --git a/Source/DTFluxProjectSettings/Private/DTFluxGeneralSettings.cpp b/Source/DTFluxProjectSettings/Private/DTFluxGeneralSettings.cpp index 2552fad..2452696 100644 --- a/Source/DTFluxProjectSettings/Private/DTFluxGeneralSettings.cpp +++ b/Source/DTFluxProjectSettings/Private/DTFluxGeneralSettings.cpp @@ -9,7 +9,8 @@ UDTFluxGeneralSettings::UDTFluxGeneralSettings() { CategoryName = "DTFlux API"; - UE_LOG(logDTFluxProjectSettings, Log, TEXT("ModelAssetLoded isNull() -> %s"), ModelAsset.IsNull() ? TEXT("TRUE") : TEXT("FALSE")); - - UE_LOG(logDTFluxProjectSettings, Log, TEXT("ModelAssetLoded IsValid() -> %s"), ModelAsset.IsValid() ? TEXT("TRUE") : TEXT("FALSE")); + // UE_LOG(logDTFluxProjectSettings, Log, TEXT("ModelAssetLoded isNull() -> %s"), ModelAsset.IsNull() ? TEXT("TRUE") : TEXT("FALSE")); + // + UE_LOG(logDTFluxProjectSettings, Log, TEXT("Category Name -> %s"), *GetCategoryName().ToString()); + } diff --git a/Source/DTFluxProjectSettings/Private/DTFluxProjectSettingsModule.cpp b/Source/DTFluxProjectSettings/Private/DTFluxProjectSettingsModule.cpp index ac227ba..00012b4 100644 --- a/Source/DTFluxProjectSettings/Private/DTFluxProjectSettingsModule.cpp +++ b/Source/DTFluxProjectSettings/Private/DTFluxProjectSettingsModule.cpp @@ -1,17 +1,52 @@ #include "DTFluxProjectSettingsModule.h" +#include "DTFluxGeneralSettings.h" +#include "DTFluxNetworkSettings.h" +#include "ISettingsModule.h" + #define LOCTEXT_NAMESPACE "FDTFluxProjectSettingsModule" DTFLUXPROJECTSETTINGS_API DEFINE_LOG_CATEGORY(logDTFluxProjectSettings) void FDTFluxProjectSettingsModule::StartupModule() { - + RegisterSettings(); } void FDTFluxProjectSettingsModule::ShutdownModule() { - + UnregisterSettings(); +} + +void FDTFluxProjectSettingsModule::RegisterSettings() +{ + + if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) + { + SettingsModule->RegisterSettings("Project", "DTFluxProjectSettings", "DTFluxGeneralSettings", + FText::FromString("DTFlux General Settings"), + FText::FromString("DTFluxAPI Network Settings"), + GetMutableDefault() + ); + SettingsModule->RegisterSettings("Project", "DTFluxProjectSettings", "DTFluxNetworkSettings", + FText::FromString("DTFlux Network Settings"), + FText::FromString("DTFluxAPI Network Settings"), + GetMutableDefault() + ); + } + + +} + +void FDTFluxProjectSettingsModule::UnregisterSettings() +{ + if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) + { + SettingsModule->UnregisterSettings("Project", "DTFluxProjectSettings", "DTFluxGeneralSettings"); + SettingsModule->UnregisterSettings("Project", "DTFluxProjectSettings", "DTFluxNetworkSettings"); + } + + } #undef LOCTEXT_NAMESPACE diff --git a/Source/DTFluxProjectSettings/Public/DTFluxGeneralSettings.h b/Source/DTFluxProjectSettings/Public/DTFluxGeneralSettings.h index 5f94a8f..2a28ea6 100644 --- a/Source/DTFluxProjectSettings/Public/DTFluxGeneralSettings.h +++ b/Source/DTFluxProjectSettings/Public/DTFluxGeneralSettings.h @@ -16,7 +16,7 @@ class DTFLUXPROJECTSETTINGS_API UDTFluxGeneralSettings : public UDeveloperSettin GENERATED_BODY() public: - + virtual bool SupportsAutoRegistration() const override{ return false; } UDTFluxGeneralSettings(); UPROPERTY(Category="General", Config, EditAnywhere, BlueprintReadOnly, DisplayName="Datastorage File") TSoftObjectPtr ModelAsset; diff --git a/Source/DTFluxProjectSettings/Public/DTFluxNetworkSettings.h b/Source/DTFluxProjectSettings/Public/DTFluxNetworkSettings.h index 4f17769..bb0dbf8 100644 --- a/Source/DTFluxProjectSettings/Public/DTFluxNetworkSettings.h +++ b/Source/DTFluxProjectSettings/Public/DTFluxNetworkSettings.h @@ -48,7 +48,7 @@ public: UPROPERTY(Category="CHRONO PROXY|HTTP", Config, EditAnywhere, BlueprintReadOnly, DisplayName="HTTP Port") int HTTPPort = 8080; - + virtual bool SupportsAutoRegistration() const override{ return false; } #if WITH_EDITOR virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; diff --git a/Source/DTFluxProjectSettings/Public/DTFluxProjectSettingsModule.h b/Source/DTFluxProjectSettings/Public/DTFluxProjectSettingsModule.h index 8006d53..5ebf803 100644 --- a/Source/DTFluxProjectSettings/Public/DTFluxProjectSettingsModule.h +++ b/Source/DTFluxProjectSettings/Public/DTFluxProjectSettingsModule.h @@ -21,4 +21,7 @@ public: virtual void StartupModule() override; virtual void ShutdownModule() override; + + void RegisterSettings(); + void UnregisterSettings(); };