Adding Status and Last server response handled but not tested

This commit is contained in:
2025-07-03 17:28:51 +02:00
parent 2855fa1e98
commit fa5493adcf
43 changed files with 2035 additions and 379 deletions

View File

@ -44,6 +44,11 @@
"Name": "DTFluxUtilities",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name": "DTFluxAPIStatus",
"Type": "Editor",
"LoadingPhase": "Default"
}
],
"Plugins": [

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32"
height="32"
viewBox="0 0 32 32"
version="1.1"
id="svg1"
sodipodi:docname="DTFluxServerStatusProject.svg"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
inkscape:export-filename="..\..\..\0000WorkTechnique\0002dev\Yotta2025\Plugins\DTFluxApi\Resources\DTFluxServerStatusWhite.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
inkscape:export-bgcolor="#ffffff00"
inkscape:zoom="12.473806"
inkscape:cx="2.4852078"
inkscape:cy="16.715027"
inkscape:window-width="3440"
inkscape:window-height="1351"
inkscape:window-x="-9"
inkscape:window-y="1"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#f9f9f9;stroke-width:2.00998;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 9.9141729,1.5371294 H 2.0032533 V 30.462871 h 7.8293644"
id="path1" />
<rect
style="fill:none;stroke:#f9f9f9;stroke-width:1.80686;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="rect2"
width="19.193144"
height="4.2450275"
x="6.4034286"
y="3.8339815" />
<rect
style="fill:none;stroke:#f9f9f9;stroke-width:1.74838;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="rect3"
width="19.251621"
height="3.9626296"
x="6.483469"
y="24.212114" />
<ellipse
style="fill:none;stroke:#f9f9f9;stroke-width:2.03983;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3"
cx="16.10928"
cy="16.480982"
rx="4.8213382"
ry="4.8742285" />
<path
style="fill:none;stroke:#f9f9f9;stroke-width:0.744524;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 15.911604,16.712764 2.263593,-1.701009"
id="path4" />
<path
style="fill:none;stroke:#f9f9f9;stroke-width:1.2784;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 16.417491,11.616727 V 9.7754388"
id="path5" />
<rect
style="display:none;fill:none;stroke:#f9f9f9;stroke-width:2.034;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
id="rect5"
width="18.966003"
height="10.100974"
x="6.5169978"
y="11.082275" />
<path
style="fill:none;stroke:#f9f9f9;stroke-width:2.00998;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 22.304385,30.58532 h 7.91092 V 1.6595782 h -7.829364"
id="path6" />
<ellipse
style="fill:#f9f9f9;fill-opacity:1;stroke:none;stroke-width:7.96575;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
id="path7"
cx="8.8708906"
cy="5.9564953"
rx="0.48769841"
ry="0.4282518" />
<ellipse
style="display:none;fill:#000000;fill-opacity:1;stroke:#f9f9f9;stroke-width:7.96575;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
id="ellipse7"
cx="8.8708906"
cy="13.527604"
rx="0.48769841"
ry="0.4282518" />
<ellipse
style="display:none;fill:#000000;fill-opacity:1;stroke:#f9f9f9;stroke-width:7.96575;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
id="ellipse8"
cx="8.8708906"
cy="18.456623"
rx="0.48769841"
ry="0.4282518" />
<ellipse
style="display:inline;fill:#f9f9f9;fill-opacity:1;stroke:none;stroke-width:7.96575;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
id="ellipse9"
cx="8.8708906"
cy="26.246185"
rx="0.48769841"
ry="0.4282518" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -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"
}
);
}
}

View File

@ -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<FLevelEditorModule>(TEXT("LevelEditor"));
// FDTFluxAPIModule& DTFluxApi =
// FModuleManager::LoadModuleChecked<FDTFluxAPIModule>(TEXT("DTFluxAPI"));
const TSharedPtr<FExtender> 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<SDockTab> 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)

View File

@ -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<ISettingsModule>("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<UDTFluxNetworkSubsystem>();
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

View File

@ -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<ISlateStyle> 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<ISlateStyle> FDTFluxStatusStyle::Create()
{
TSharedPtr<FSlateStyleSet> 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;
}

View File

@ -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<EDTFluxWsStatus> WsResponseEvent) const;
#pragma endregion
#pragma region EditorTab
void RegisterStatusTab();
TSharedRef<SDockTab> OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs);
private:
static FName StatusTabId;
static FText StatusTabDisplayName;
TSharedPtr<class SDTFluxStatusWidget> StatusWidget;
#pragma endregion
};

View File

@ -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<FText> ConnectionActionButtonText;
FReply OnConnectionActionButtonClicked();
private:
UDTFluxNetworkSubsystem* DTFlux = nullptr;
// // TODO make a struct
FText GetWebSocketStatusText() const;
FText GetWebConnectActionButtonText() const;
FSlateColor GetWebSocketStatusColor() const;
FSlateColor GetWebConnectActionButtonColor() const;
TSharedPtr<STextBlock> WsStatusText;
TSharedPtr<SButton> ConnectionActionButton;
};

View File

@ -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<ISlateStyle> Create();
static TSharedPtr<ISlateStyle> StyleSet;
};

View File

@ -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",
}

View File

@ -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<FDTFluxPerson> Teammate = InParticipant.Teammate;
for(auto& Person : InParticipant.Teammate)
{
AddPerson(InParticipant.Person1);
}
if(InParticipant.Person2 != 0)
UE_LOG(logDTFluxCore, Error, TEXT("AddParticipant() DTFlux Person %s %s %s"),
*Person.FirstName, *Person.LastName, *Person.Gender);
if(!PersonExists(Person))
{
if(!PersonExists(InParticipant.Person2))
{
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;
}

View File

@ -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<FDTFluxPursuit> UDTFluxPursuitManager::GetNextPursuits(int MaxPursuit)
{
//TODO : Implement me !!!
return PursuitParticipants;
}
TArray<FDTFluxPursuit> 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();
}

View File

@ -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<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> 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<FJsonObject> JsonObject, FDTFluxParticipant& OutParticipant)
{
OutParticipant = FDTFluxParticipant::CreateFromJson(JsonObject);
return OutParticipant == 0;
}

View File

@ -0,0 +1,4 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Types/Struct/DTFluxCompositeKey.h"

View File

@ -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;
}

View File

@ -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());
// }
//

View File

@ -0,0 +1,4 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Types/Struct/DTFluxSplitSensor.h"

View File

@ -4,104 +4,150 @@
#include "Types/Struct/DTFluxTeamListStruct.h"
bool FDTFluxParticipant::IsTeam() const
{
return Person2.FirstName.IsEmpty() && Person2.LastName.IsEmpty();
}
void FDTFluxParticipant::Dump() const
void FDTFluxParticipant::AddTeammate(const FDTFluxPerson& Person)
{
FString EliteStr = "NO";
if(Elite)
{
EliteStr = "YES";
Teammate.Add(Person);
}
UE_LOG(logDTFluxCore, Log, TEXT("PARTICIPANT with bib: %03d"), Bib);
UE_LOG(logDTFluxCore, Log, TEXT("Fullname : %s %s"), *Person1.FirstName, *Person1.LastName);
void FDTFluxParticipant::AddTeammate(const FString LastName, const FString FirstName, const FString Gender)
{
}
FText FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString OverflowChars)
{
// Vérifie les cas limites
if (MaxChar <= 0)
{
return FText::GetEmpty();
}
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() + " ";
}
FString FDTFluxParticipant::GetParticipantFormatedName(bool Truncate, int MaxSize) const
{
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<FString> 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);
// Nom complet en majuscules
FString FormattedLastName = LastName.ToUpper();
return Formated.Left(MaxSize - 3).Append("...");
}
return Formated;
}
if(Person1.FirstName.Contains(" "))
// Construction du nom final
FString FullName = Initial + FormattedLastName;
// Tronque si nécessaire
if (FullName.Len() > MaxChar)
{
FString Formated = "";
//Compound Firstname
TArray<FString> Out;
Person1.FirstName.ParseIntoArray(Out,TEXT(" "),true);
for(const auto& Str : Out)
// On essaie de garder autant de caractères que possible
const int32 AvailableLength = MaxChar - Initial.Len();
if (AvailableLength <= 0)
{
Formated.Append(Str.Left(1).ToUpper()).Append(".");
// Pas assez de place pour le nom → juste l'initiale ?
return FText::FromString(Initial);
}
// 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)
// Coupe le nom pour quil 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)
{
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)
// 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)
{
UE_LOG(logDTFluxCore, Log, TEXT("Reducing %s Formated"), *Formated);
return Formated.Left(MaxSize - 3).Append("...");
}
return Formated;
FullName += OverflowChars;
}
else
{
if(!IsTeam())
// Il faut tronquer davantage pour faire de la place au suffixe
const int32 RemainingSpace = MaxChar - CurrentLength;
if (RemainingSpace > 0)
{
return FString::Printf(TEXT("%s %s"), *Person1.FirstName, *Person2.LastName);
FullName = FullName.Left(MaxChar - OverflowLength) + OverflowChars;
}
return Team;
else
{
FullName = FullName.Left(MaxChar);
}
}
}
}
return FText::FromString(FullName);
}
FText FDTFluxParticipant::GetConcatFormattedName(const int MaxChar, const FString OverflowChar)
{
FString BibText = FString::FromInt(Bib) + " ";
FText FormattedName = GetFormattedName(MaxChar - BibText.Len(), OverflowChar );
return FText::FromString(BibText + FormattedName.ToString());
}
// Constructeur privé depuis JSON
FDTFluxParticipant::FDTFluxParticipant(const TSharedPtr<FJsonObject>& 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<EDTFluxParticipantStatusType>(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)
{
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<FJsonObject>& JsonObject)
{
return FDTFluxParticipant(JsonObject);
}
int FDTFluxParticipant::GetTeammateNum() const
{
return Teammate.Num();
}
bool FDTFluxParticipant::IsTeam()
{
return Teammate.Num() < 1;
}

View File

@ -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<int /*ContestId*/, FDTFluxContestRankings> ContestRankings;
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
TArray<FDTFluxStageRankings> StageRankings;
TMap<FDTFluxStageKey, FDTFluxStageRankings> StageRankings;
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
TArray<FDTFluxStageRankings> SplitRankings;
TMap<FDTFluxSplitKey, FDTFluxSplitRankings> 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);

View File

@ -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")
};

View File

@ -17,6 +17,7 @@ enum class EDTFluxParticipantStatusType : uint8
DNF = 3 UMETA(DisplayName="Abandon"),
DNS = 4 UMETA(DisplayName="NonPartant"),
NotLinedUp = 5 UMETA(DisplayName="NonPresentAuDépart"),
Unknown = 1 << 4 UMETA(DisplayName="Unknown")
};

View File

@ -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<FDTFluxPursuit> 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<FDTFluxPursuit> GetNextPursuits(int MaxPursuit);
UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="Poursuit pursuit poursuit"))
TArray<FDTFluxPursuit> 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;
};

View File

@ -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<FJsonObject> JsonObject, FDTFluxParticipant& OutParticipant);
};

View File

@ -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)
);
}
};

View File

@ -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<FDTFluxParticipant>& InParticipants) : Participants(InParticipants){};
~FDTFluxPursuit();
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category="DTFlux|Pursuit")
TArray<FDTFluxParticipant> 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;
};

View File

@ -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<FDTFluxStageRanking> 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<FDTFluxDetailedRankingItem> 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);
}
};

View File

@ -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;
};

View File

@ -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<FDTFluxTeamListItemDefinition> 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)
// Constructeur public par défaut requis par Unreal
FDTFluxParticipant()
: Bib(-1)
,ContestId(-1)
, Elite(false)
, Status(static_cast<EDTFluxParticipantStatusType>(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(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
FDTFluxPerson Person1;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
FDTFluxPerson Person2;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
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<FJsonObject>& JsonObject);
protected:
UPROPERTY(Category="DTFlux|model", VisibleAnywhere)
TArray<FDTFluxPerson> Teammate;
// Méthode publique pour construire à partir d'un JSON (utilisée par la factory)
static FDTFluxParticipant CreateFromJson(const TSharedPtr<FJsonObject>& JsonObject);
int GetTeammateNum() const;
bool IsTeam();
};
/**
* @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<FDTFluxParticipant> Participants;
};
USTRUCT(BlueprintType)
struct FDTFluxTeamStatusUpdate
{
GENERATED_BODY()
public:
FDTFluxTeamStatusUpdate() = default;
FDTFluxTeamStatusUpdate(const int InBib, const int InStatus)
:Bib(InBib)
, Status(static_cast<EDTFluxParticipantStatusType>(InStatus)){};
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant")
int Bib = -1;
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant")
EDTFluxParticipantStatusType Status = EDTFluxParticipantStatusType::Unknown;
};

View File

@ -20,6 +20,7 @@ public class DTFluxCoreSubsystem : ModuleRules
"Engine",
"Slate",
"SlateCore",
"UnrealEd",
"DTFluxNetwork",
"DTFluxProjectSettings",
"DTFluxCore",

View File

@ -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<UDTFluxGeneralSettings>();
TSoftObjectPtr<UDTFluxModelAsset> 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<UDTFluxModelAsset>(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<UFDTFluxNetworkSubsystem>();
NetworkSubsystem = GEngine->GetEngineSubsystem<UDTFluxNetworkSubsystem>();
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<UDTFluxModelAsset> ModelAsset = GetDefault<UDTFluxGeneralSettings>()->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<EDTFluxParticipantStatusType>(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);
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received StageRankings with %i Items"), StageRankings.Rankings.Num());
DataStorage->UpdateOrCreateStageRanking(StageRankings);
DataStorage->MarkPackageDirty();
}
}
else
void UDTFluxCoreSubsystem::ProcessSplitRanking(const FDTFluxSplitRankings& SplitRankings)
{
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received SplitRankings with %i Items"), StageOrSplitRankings.Rankings.Num());
}
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
}

View File

@ -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()

View File

@ -17,6 +17,24 @@ FDTFluxWebSocketClient::FDTFluxWebSocketClient()
FDTFluxWebSocketClient::LastId++;
}
bool FDTFluxWebSocketClient::CanSend() const
{
return Ws.IsValid();
}
TSharedPtr<FDTFluxWebSocketClient> FDTFluxWebSocketClient::GetClient(
const TArray<TSharedPtr<FDTFluxWebSocketClient>> InClients, const FName InName)
{
for(auto Client: InClients)
{
if(Client->ClientId == InName)
{
return Client;
}
}
return nullptr;
}
void FDTFluxWebSocketClient::Connect()
{
Ws = FWebSocketsModule::Get().CreateWebSocket(WsAddress);
@ -79,9 +97,12 @@ void FDTFluxWebSocketClient::AddError(const FDTFluxWsClientError Error)
}
void FDTFluxWebSocketClient::Send(const FString& Message) const
{
if(Ws.IsValid())
{
Ws->Send(Message);
}
}
void FDTFluxWebSocketClient::Bind_Internal()

View File

@ -12,17 +12,30 @@
#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();
}
void UFDTFluxNetworkSubsystem::SendRequest(const EDTFluxRequestType RequestType, int InContestId, int InStageId,
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;
@ -52,12 +65,24 @@ void UFDTFluxNetworkSubsystem::SendRequest(const EDTFluxRequestType RequestType,
SendMessage(Message);
}
void UFDTFluxNetworkSubsystem::SendMessage(const FString& Message)
void UDTFluxNetworkSubsystem::SendMessage(const FString& 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<FDTFluxCoreModule>("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,50 +188,72 @@ 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;
}
//TODO reforge API to keep track of Requests
void UFDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& MessageString)
{
UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws %s :\nMessage Received : %s"), *WsClient->GetAddress(), *MessageString);
//Do Something With the message
FDTFluxServerResponse Response;
Response.ReceivedAt = FDateTime::Now();
Response.RawMessage = MessageString;
Response.FailureReason = FText::FromString("--");
if(FJsonObjectConverter::JsonObjectStringToUStruct(MessageString, &Response, 0, 0, false, &(Response.FailureReason)))
{
if(Response.Code == -1)
{
// return DataReceived.Broadcast(Response);
if(Response.Type.Contains("race-data"))
void UDTFluxNetworkSubsystem::ParseTeamListResponse(const FDTFluxServerResponse& ServerResponse)
{
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> 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<TSharedPtr<FJsonValue>>* 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<FJsonValue>& Value : *DataArray)
{
if (Value->Type == EJson::Object)
{
const TSharedPtr<FJsonObject> 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;
FJsonObjectConverter::JsonObjectStringToUStruct<FDTFluxRaceDataResponse>(Response.RawMessage, &RaceData);
if(FJsonObjectConverter::JsonObjectStringToUStruct<FDTFluxRaceDataResponse>(Response.RawMessage, &RaceData))
{
//convert
FDTFluxRaceData RaceDataDefinition;
for(auto Contest : RaceData.Datas)
@ -256,22 +298,18 @@ void UFDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString&
}
RaceDataDefinition.Datas.Add(NewContest);
}
UE_LOG(logDTFluxNetwork, Warning, TEXT("Sending %i Contests"), RaceDataDefinition.Datas.Num());
return OnRaceDataReceived.Broadcast(RaceDataDefinition);
UE_LOG(logDTFluxNetwork, Warning, TEXT("Sending %i Contests, [%s]"), RaceDataDefinition.Datas.Num(),
OnRaceDataReceived.ExecuteIfBound(RaceDataDefinition) ? TEXT("SUCCESS_SEND") : TEXT("NOT_BOUNDED"));
return;
}
if(Response.Type.Contains("team-list"))
{
UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws Team-List Data"));
FDTFluxTeamListDefinition TeamListDefinition;
FJsonObjectConverter::JsonObjectStringToUStruct<FDTFluxTeamListDefinition>(Response.RawMessage, &TeamListDefinition);
UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws Team-List Data Sent"));
return OnTeamListReceived.Broadcast(TeamListDefinition);
UE_LOG(logDTFluxNetwork, Error, TEXT("ParseRaceData() for JSON Response : %s"), *Response.RawMessage);
}
if(Response.Type.Contains("contest-ranking"))
void UDTFluxNetworkSubsystem::ParseContestRanking(const FDTFluxServerResponse& Response)
{
UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws Contest-Ranking Data"));
FDTFluxContestRankingResponse ContestRankingResponse;
FJsonObjectConverter::JsonObjectStringToUStruct<FDTFluxContestRankingResponse>(Response.RawMessage, &ContestRankingResponse);
if(FJsonObjectConverter::JsonObjectStringToUStruct<FDTFluxContestRankingResponse>(Response.RawMessage, &ContestRankingResponse))
{
FDTFluxContestRankings ContestRankings;
ContestRankings.ContestId = ContestRankingResponse.ContestID;
for(auto& RankingItem : ContestRankingResponse.Datas)
@ -279,27 +317,146 @@ void UFDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString&
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("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<FDTFluxStageRankingResponse>(Response.RawMessage, &RankingResponse))
{
FDTFluxStageRankings NewRankings;
NewRankings.ContestId = Response.ContestID;
NewRankings.StageId = Response.StageID;
NewRankings.Rankings = static_cast<TArray<FDTFluxDetailedRankingItem>>(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<FDTFluxSplitRankingResponse>(Response.RawMessage, &SplitRankingResponse))
{
FDTFluxSplitRankings NewSplitRankings;
NewSplitRankings.ContestId = Response.ContestID;
NewSplitRankings.StageId = Response.StageID;
NewSplitRankings.SplitId = Response.SplitID;
NewSplitRankings.Rankings = static_cast<TArray<FDTFluxDetailedRankingItem>>(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<FDTFluxTeamStatusUpdate>(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 UDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& 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();
Response.RawMessage = MessageString;
Response.FailureReason = FText::FromString("--");
if(FJsonObjectConverter::JsonObjectStringToUStruct(MessageString, &Response, 0, 0, false, &(Response.FailureReason)))
{
if(Response.Code == -1)
{
// return DataReceived.Broadcast(Response);
if(Response.Type.Contains("race-data"))
{
UE_LOG(logDTFluxNetwork, Warning, TEXT("Race-Data Received"));
return ParseRaceData(Response);
}
if(Response.Type.Contains("team-list"))
{
UE_LOG(logDTFluxNetwork, Warning, TEXT("Team-List Received"));
return ParseTeamListResponse(Response);
}
if(Response.Type.Contains("contest-ranking"))
{
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<FDTFluxStageRankingResponse>(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);
}

View File

@ -53,17 +53,11 @@ public:
FDTFluxWebSocketClient();
static TSharedPtr<FDTFluxWebSocketClient> GetClient(const TArray<TSharedPtr<FDTFluxWebSocketClient>> InClients, const FName InName)
{
for(auto Client: InClients)
{
if(Client->ClientId == InName)
{
return Client;
}
}
return nullptr;
}
bool CanSend() const;
static TSharedPtr<FDTFluxWebSocketClient> GetClient(const TArray<TSharedPtr<FDTFluxWebSocketClient>> InClients, const FName InName);
void Connect();
void Reconnect();

View File

@ -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<FDTFluxStageRankingResponseItem> Datas;
};
USTRUCT(BlueprintType)
struct DTFLUXNETWORK_API FDTFluxSplitRankingResponse
{
GENERATED_BODY()
public:
UPROPERTY()
FString Type = "stage-ranking";
UPROPERTY()
// ReSharper disable once IdentifierTypo
TArray<FDTFluxSplitRankingResponseItem> Datas;
};

View File

@ -25,10 +25,7 @@ public:
int SplitID;
UPROPERTY()
FString Time = "-";
UPROPERTY()
FString Gap = "-";
UPROPERTY()
int Rank;
};
USTRUCT(BlueprintType)

View File

@ -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<FDTFluxHttpClient> 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);

View File

@ -21,7 +21,9 @@ public class DTFluxProjectSettings : ModuleRules
"Slate",
"SlateCore",
"DeveloperSettings",
"DTFluxCore"
"DTFluxCore",
"Settings",
"DeveloperSettings"
}
);

View File

@ -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 isNull() -> %s"), ModelAsset.IsNull() ? TEXT("TRUE") : TEXT("FALSE"));
//
UE_LOG(logDTFluxProjectSettings, Log, TEXT("Category Name -> %s"), *GetCategoryName().ToString());
UE_LOG(logDTFluxProjectSettings, Log, TEXT("ModelAssetLoded IsValid() -> %s"), ModelAsset.IsValid() ? TEXT("TRUE") : TEXT("FALSE"));
}

View File

@ -1,16 +1,51 @@
#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<ISettingsModule>("Settings"))
{
SettingsModule->RegisterSettings("Project", "DTFluxProjectSettings", "DTFluxGeneralSettings",
FText::FromString("DTFlux General Settings"),
FText::FromString("DTFluxAPI Network Settings"),
GetMutableDefault<UDTFluxGeneralSettings>()
);
SettingsModule->RegisterSettings("Project", "DTFluxProjectSettings", "DTFluxNetworkSettings",
FText::FromString("DTFlux Network Settings"),
FText::FromString("DTFluxAPI Network Settings"),
GetMutableDefault<UDTFluxNetworkSettings>()
);
}
}
void FDTFluxProjectSettingsModule::UnregisterSettings()
{
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
SettingsModule->UnregisterSettings("Project", "DTFluxProjectSettings", "DTFluxGeneralSettings");
SettingsModule->UnregisterSettings("Project", "DTFluxProjectSettings", "DTFluxNetworkSettings");
}
}

View File

@ -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<UDTFluxModelAsset> ModelAsset;

View File

@ -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;

View File

@ -21,4 +21,7 @@ public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
void RegisterSettings();
void UnregisterSettings();
};