Compare commits
6 Commits
b63f2dd7b5
...
feature/Ra
| Author | SHA1 | Date | |
|---|---|---|---|
| d419681172 | |||
| 03eb1132ef | |||
| 8f884f6224 | |||
| 9bb5e760f2 | |||
| 760a764816 | |||
| 43a7fb7400 |
@ -49,11 +49,6 @@
|
|||||||
"Name": "DTFluxAPIStatus",
|
"Name": "DTFluxAPIStatus",
|
||||||
"Type": "Editor",
|
"Type": "Editor",
|
||||||
"LoadingPhase": "Default"
|
"LoadingPhase": "Default"
|
||||||
},
|
|
||||||
{
|
|
||||||
"Name": "DTFluxPursuitSystem",
|
|
||||||
"Type": "Runtime",
|
|
||||||
"LoadingPhase": "Default"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Plugins": [
|
"Plugins": [
|
||||||
|
|||||||
41
Resources/DTFluxRaceResult16x16.svg
Normal file
41
Resources/DTFluxRaceResult16x16.svg
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="512"
|
||||||
|
height="512"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:export-filename="RaceResult.icone16x16.svg"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
sodipodi:docname="RaceResult.icone16x16.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="px" /><defs
|
||||||
|
id="defs1" /><g
|
||||||
|
inkscape:label="Calque 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(3.0517578e-5)"><rect
|
||||||
|
style="fill:#e22c2b;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
|
||||||
|
id="rect1"
|
||||||
|
width="512"
|
||||||
|
height="512"
|
||||||
|
x="-3.0517578e-05"
|
||||||
|
y="0" /><path
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:15;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
|
||||||
|
d="m 154.64305,277.08769 -1.3605,-151.92206 146.48007,103.3977 v 184.12046 l 54.87334,-50.79185 V 199.53942 L 215.41187,92.967228 V 328.78654 Z"
|
||||||
|
id="path1" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
@ -36,6 +36,11 @@ EActiveTimerReturnType SDTFluxAssetModelDetailsWidget::ForceInitialLayout(double
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxModelAssetCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
void FDTFluxModelAssetCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
||||||
|
{
|
||||||
|
CustomizeDetailsWithRawDataAccess(DetailBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxModelAssetCustomization::CustomizeDetailsWithoutRawDataAsset(IDetailLayoutBuilder& DetailBuilder)
|
||||||
{
|
{
|
||||||
// Edit object
|
// Edit object
|
||||||
TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
|
TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
|
||||||
|
|||||||
@ -11,6 +11,7 @@ public:
|
|||||||
// IDetailCustomization interface
|
// IDetailCustomization interface
|
||||||
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
|
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
|
||||||
|
|
||||||
|
void CustomizeDetailsWithoutRawDataAsset(IDetailLayoutBuilder& DetailBuilder);
|
||||||
void CustomizeDetailsWithRawDataAccess(IDetailLayoutBuilder& DetailBuilder);
|
void CustomizeDetailsWithRawDataAccess(IDetailLayoutBuilder& DetailBuilder);
|
||||||
|
|
||||||
// Crée une instance de cette customization
|
// Crée une instance de cette customization
|
||||||
|
|||||||
@ -7,23 +7,5 @@ void FDTFluxContestRanking::Dump() const
|
|||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCore, Log,
|
UE_LOG(logDTFluxCore, Log,
|
||||||
TEXT("FDTFluxContestRanking ->> \n \"rank\" : %d, Participant with Bib %d \"Gap\" : %s, \"Time\" : %s "),
|
TEXT("FDTFluxContestRanking ->> \n \"rank\" : %d, Participant with Bib %d \"Gap\" : %s, \"Time\" : %s "),
|
||||||
Rank, Bib, *Gap, *Time );
|
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 FDTFluxSplitRanking::Dump() const
|
|
||||||
// {
|
|
||||||
// UE_LOG(logDTFluxCore, Log, TEXT("SplitGapItem"))
|
|
||||||
// // Participant.Dump();
|
|
||||||
// UE_LOG(logDTFluxCore, Log, TEXT("Bib %02d Rank %02d Gap %s Time %s"), Bib, Rank, *Gap, *Time);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "Types/Struct/DTFluxTeamListStruct.h"
|
#include "Types/Struct/DTFluxTeamListStruct.h"
|
||||||
|
|
||||||
|
#include "DTFluxCoreModule.h"
|
||||||
|
|
||||||
|
|
||||||
void FDTFluxParticipant::AddTeammate(const FDTFluxPerson& Person)
|
void FDTFluxParticipant::AddTeammate(const FDTFluxPerson& Person)
|
||||||
{
|
{
|
||||||
@ -15,11 +17,12 @@ void FDTFluxParticipant::AddTeammate(const FString LastName, const FString First
|
|||||||
|
|
||||||
FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString OverflowChars) const
|
FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString OverflowChars) const
|
||||||
{
|
{
|
||||||
// Vérifie les cas limites
|
{
|
||||||
if (MaxChar <= 0)
|
if (MaxChar <= 0)
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
FString FirstName;
|
FString FirstName;
|
||||||
FString LastName;
|
FString LastName;
|
||||||
if (IsTeam())
|
if (IsTeam())
|
||||||
@ -31,62 +34,50 @@ FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString Ov
|
|||||||
FirstName = Teammate[0].FirstName;
|
FirstName = Teammate[0].FirstName;
|
||||||
LastName = Teammate[0].LastName;
|
LastName = Teammate[0].LastName;
|
||||||
}
|
}
|
||||||
// Récupère la première lettre du prénom en majuscule
|
|
||||||
FString Initial;
|
FString Initial;
|
||||||
if (!FirstName.IsEmpty())
|
if (!FirstName.IsEmpty())
|
||||||
{
|
{
|
||||||
Initial = FirstName.Left(1).ToUpper() + " ";
|
Initial = FirstName.Left(1).ToUpper() + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nom complet en majuscules
|
|
||||||
FString FormattedLastName = LastName.ToUpper();
|
FString FormattedLastName = LastName.ToUpper();
|
||||||
|
|
||||||
// Construction du nom final
|
|
||||||
FString FullName = Initial + FormattedLastName;
|
FString FullName = Initial + FormattedLastName;
|
||||||
UE_LOG(logDTFluxCore, Error, TEXT("FullName for Bib %i is %s"), Bib, *FullName);
|
UE_LOG(logDTFluxCore, Error, TEXT("FullName for Bib %i is %s"), Bib, *FullName);
|
||||||
|
|
||||||
// Tronque si nécessaire
|
if (FullName.Len() <= MaxChar)
|
||||||
if (FullName.Len() > MaxChar)
|
|
||||||
{
|
{
|
||||||
// On essaie de garder autant de caractères que possible
|
return FullName;
|
||||||
const int32 AvailableLength = MaxChar - Initial.Len();
|
|
||||||
if (AvailableLength <= 0)
|
|
||||||
{
|
|
||||||
return 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();
|
const int32 OverflowLength = OverflowChars.Len();
|
||||||
|
|
||||||
if (CurrentLength + OverflowLength <= MaxChar)
|
if (OverflowLength > MaxChar)
|
||||||
{
|
{
|
||||||
FullName += OverflowChars;
|
return FullName.Left(MaxChar);
|
||||||
}
|
|
||||||
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 FullName;
|
if (Initial.Len() + OverflowLength > MaxChar)
|
||||||
|
{
|
||||||
|
return FullName.Left(MaxChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32 AvailableForLastName = MaxChar - Initial.Len() - OverflowLength;
|
||||||
|
|
||||||
|
if (AvailableForLastName <= 0)
|
||||||
|
{
|
||||||
|
return FullName.Left(MaxChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString TruncatedName = Initial + FormattedLastName.Left(AvailableForLastName) + OverflowChars;
|
||||||
|
|
||||||
|
if (TruncatedName.Len() > MaxChar)
|
||||||
|
{
|
||||||
|
return TruncatedName.Left(MaxChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TruncatedName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FString FDTFluxParticipant::GetConcatFormattedName(const int MaxChar, const FString OverflowChar) const
|
FString FDTFluxParticipant::GetConcatFormattedName(const int MaxChar, const FString OverflowChar) const
|
||||||
|
|||||||
@ -85,11 +85,11 @@ public:
|
|||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere)
|
||||||
FDateTime StartTime;
|
FDateTime StartTime;
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere)
|
||||||
float SpeedRunning;
|
FString SpeedRunning;
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere)
|
||||||
float SpeedTotal;
|
FString SpeedTotal;
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", VisibleAnywhere)
|
||||||
float SpeedSwim;
|
FString SpeedSwim;
|
||||||
void Dump() const;
|
void Dump() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -103,6 +103,7 @@ public:
|
|||||||
int ContestId = -1;
|
int ContestId = -1;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model|Ranking", VisibleAnywhere)
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model|Ranking", VisibleAnywhere)
|
||||||
int StageId = -1;
|
int StageId = -1;
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model|Ranking", VisibleAnywhere)
|
||||||
TArray<FDTFluxDetailedRankingItem> Rankings;
|
TArray<FDTFluxDetailedRankingItem> Rankings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -21,12 +21,18 @@ struct FDTFluxPursuitInfo
|
|||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
//TODO : Set this property to BlueprintReadOnly
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||||
bool bIsMassStart = false;
|
bool bIsMassStart = false;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
//TODO : Set this property to BlueprintReadOnly
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||||
int Bib = -1;
|
int Bib = -1;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
//TODO : Set this property to BlueprintReadOnly
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||||
FDateTime StartTime;
|
FDateTime StartTime;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
int ContestId = -1;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "DTFluxCoreSubsystemModule.h"
|
#include "DTFluxCoreSubsystemModule.h"
|
||||||
#include "DTFluxGeneralSettings.h"
|
#include "DTFluxGeneralSettings.h"
|
||||||
|
#include "DTFluxPursuitManager.h"
|
||||||
#include "FileHelpers.h"
|
#include "FileHelpers.h"
|
||||||
#include "Assets/DTFluxModelAsset.h"
|
#include "Assets/DTFluxModelAsset.h"
|
||||||
#include "Subsystems/DTFluxNetworkSubsystem.h"
|
#include "Subsystems/DTFluxNetworkSubsystem.h"
|
||||||
@ -31,6 +32,7 @@ void UDTFluxCoreSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|||||||
{
|
{
|
||||||
RegisterDelegates();
|
RegisterDelegates();
|
||||||
}
|
}
|
||||||
|
PursuitManager = NewObject<UDTFluxPursuitManager>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::Deinitialize()
|
void UDTFluxCoreSubsystem::Deinitialize()
|
||||||
@ -263,6 +265,15 @@ void UDTFluxCoreSubsystem::RequestAllSplitRankingOfStage(int InContestId, int In
|
|||||||
// TODO Implement this
|
// TODO Implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FDTFluxParticipant UDTFluxCoreSubsystem::GetParticipant(int InBib)
|
||||||
|
{
|
||||||
|
if (DataStorage->Participants.Contains(InBib))
|
||||||
|
{
|
||||||
|
return DataStorage->Participants[InBib];
|
||||||
|
}
|
||||||
|
return FDTFluxParticipant();
|
||||||
|
}
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::RefreshStorage()
|
void UDTFluxCoreSubsystem::RefreshStorage()
|
||||||
{
|
{
|
||||||
// TODO Implement this
|
// TODO Implement this
|
||||||
@ -293,6 +304,20 @@ TArray<int> UDTFluxCoreSubsystem::GetContestsIdForTime(const FDateTime Time)
|
|||||||
return Contests;
|
return Contests;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UDTFluxCoreSubsystem::GetContestForId(const int Id, FDTFluxContest& OutContest)
|
||||||
|
{
|
||||||
|
for (auto KeyPair : DataStorage->Contests)
|
||||||
|
{
|
||||||
|
if (KeyPair.Value.ContestId == Id)
|
||||||
|
{
|
||||||
|
OutContest = KeyPair.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Cannot find Contest for Id [%i]"), Id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
TArray<FDTFluxContest> UDTFluxCoreSubsystem::GetContestsForTime(const FDateTime Time)
|
TArray<FDTFluxContest> UDTFluxCoreSubsystem::GetContestsForTime(const FDateTime Time)
|
||||||
{
|
{
|
||||||
TArray<FDTFluxContest> Contests;
|
TArray<FDTFluxContest> Contests;
|
||||||
@ -322,3 +347,22 @@ TArray<FDTFluxContest> UDTFluxCoreSubsystem::GetContests()
|
|||||||
}
|
}
|
||||||
return TArray<FDTFluxContest>();
|
return TArray<FDTFluxContest>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UDTFluxCoreSubsystem::LaunchPursuitSequenceFor(const TArray<int> ContestIds)
|
||||||
|
{
|
||||||
|
TArray<FDTFluxContest> Contests = TArray<FDTFluxContest>();
|
||||||
|
for (const auto& ContestId : ContestIds)
|
||||||
|
{
|
||||||
|
FDTFluxContest Contest;
|
||||||
|
GetContestForId(ContestId, Contest);
|
||||||
|
Contests.Add(Contest);
|
||||||
|
if (PursuitManager)
|
||||||
|
{
|
||||||
|
PursuitManager->LaunchPursuitSequenceFor(Contests);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("PursuitManager is null"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
152
Source/DTFluxCoreSubsystem/Private/DTFluxPursuitManager.cpp
Normal file
152
Source/DTFluxCoreSubsystem/Private/DTFluxPursuitManager.cpp
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
|
#include "DTFluxPursuitManager.h"
|
||||||
|
|
||||||
|
#include "DTFluxCoreSubsystemModule.h"
|
||||||
|
|
||||||
|
UDTFluxPursuitManager::UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer):
|
||||||
|
Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO : Add way to pass MaxSimultaneousPursuit and MassStartDelay
|
||||||
|
// For now it's done in UPROPERTIES
|
||||||
|
void UDTFluxPursuitManager::LaunchPursuitSequenceFor(const TArray<FDTFluxContest> InContests)
|
||||||
|
{
|
||||||
|
if (InitSubSystems())
|
||||||
|
{
|
||||||
|
for (const auto Contest : InContests)
|
||||||
|
{
|
||||||
|
FRequestData RequestData;
|
||||||
|
RequestData.ContestId = Contest.ContestId;
|
||||||
|
uint8 StageId = Contest.Stages.Last().StageId;
|
||||||
|
FGuid Guid = NetworkSubsystem->SendTrackedRequestWithCallback(EDTFluxApiDataType::StageRanking,
|
||||||
|
Contest.ContestId, StageId, -1,
|
||||||
|
FOnDTFluxTrackedRequestResponse::CreateUObject(
|
||||||
|
this,
|
||||||
|
&UDTFluxPursuitManager::OnRequestResponse),
|
||||||
|
FOnDTFluxTrackedRequestTimeout::CreateUObject(
|
||||||
|
this,
|
||||||
|
&UDTFluxPursuitManager::OnRequestTimeoutResponse),
|
||||||
|
FOnDTFluxRequestResponseError::CreateUObject(
|
||||||
|
this,
|
||||||
|
&UDTFluxPursuitManager::OnRequestError));
|
||||||
|
|
||||||
|
RequestData.RequestIds.Add(Guid);
|
||||||
|
PendingRequestData.Add(RequestData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxPursuitManager::OnRequestResponse(const FGuid& RequestId, FDTFluxServerResponse& Response)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Log,
|
||||||
|
TEXT("UDTFluxPursuitManager::OnRequestResponse() Received Ranking For Stage %i"), Response.StageID)
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Log, TEXT("Response is %s"), *UEnum::GetValueAsString(Response.GetResponseType()))
|
||||||
|
//check if request
|
||||||
|
if (Response.GetResponseType() == EDTFluxApiDataType::StageRanking)
|
||||||
|
{
|
||||||
|
FDTFluxStageRankings Rankings;
|
||||||
|
FRequestData FoundData;
|
||||||
|
if (Response.ParseStageRankingResponse(Rankings))
|
||||||
|
{
|
||||||
|
for (auto& PendingReq : PendingRequestData)
|
||||||
|
{
|
||||||
|
// Check for a matching PendingReq
|
||||||
|
if (PendingReq.IsWaitingFor(RequestId, Rankings))
|
||||||
|
{
|
||||||
|
FoundData = PendingReq;
|
||||||
|
// A request Is Terminated
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Log,
|
||||||
|
TEXT("UDTFluxPursuitManager::OnRequestResponse() Ranking for Stage %i is complete"),
|
||||||
|
Response.StageID)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (InitPursuit(FoundData))
|
||||||
|
{
|
||||||
|
OnPursuitSequenceReady.Broadcast(NextFocusPursuits, NextFocusPursuits, bFocusIsTruncate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxPursuitManager::OnRequestTimeoutResponse(const FGuid& RequestId, const FString& TimeoutMessage)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Request Timeout [%s]"), *TimeoutMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxPursuitManager::OnRequestError(const FGuid& RequestId, const FString& ErrorMessage)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Request Error [%s]"), *ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxPursuitManager::InitSubSystems()
|
||||||
|
{
|
||||||
|
if (NetworkSubsystem)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
NetworkSubsystem = GEngine->GetEngineSubsystem<UDTFluxNetworkSubsystem>();
|
||||||
|
return NetworkSubsystem != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDTFluxPursuitManager::InitPursuit(FRequestData Data)
|
||||||
|
{
|
||||||
|
//Clean Data
|
||||||
|
NextFocusPursuits.Empty();
|
||||||
|
NextPursuits.Empty();
|
||||||
|
PursuitGrouped.Empty();
|
||||||
|
TArray<FDTFluxDetailedRankingItem> AllRankings;
|
||||||
|
TArray<FDTFluxPursuitInfo> AllPursuits;
|
||||||
|
TMap<FDateTime, FDTFluxPursuitGroup> TempGroups;
|
||||||
|
|
||||||
|
// Full the Array Of Rankings
|
||||||
|
for (auto& KeyPair : Data.StageRankings)
|
||||||
|
{
|
||||||
|
for (auto StageRanking : KeyPair.Value.Rankings)
|
||||||
|
{
|
||||||
|
int ContestId = KeyPair.Value.ContestId;
|
||||||
|
FDTFluxPursuitInfo PursuitInfo;
|
||||||
|
PursuitInfo.StartTime = StageRanking.StartTime;
|
||||||
|
PursuitInfo.Bib = StageRanking.Bib;
|
||||||
|
PursuitInfo.ContestId = ContestId;
|
||||||
|
AllPursuits.Add(PursuitInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sort Rankings
|
||||||
|
// AllPursuits.Sort([](const FDTFluxPursuitInfo& A, const FDTFluxPursuitInfo& B) {
|
||||||
|
// return A.StartTime < B.StartTime;
|
||||||
|
// });
|
||||||
|
|
||||||
|
for (auto& Pursuit : AllPursuits)
|
||||||
|
{
|
||||||
|
if (TempGroups.Contains(Pursuit.StartTime))
|
||||||
|
{
|
||||||
|
TempGroups[Pursuit.StartTime].PursuitGroup.Add(Pursuit);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FDTFluxPursuitGroup Group;
|
||||||
|
Group.StartTimeGlobal = Pursuit.StartTime;
|
||||||
|
Group.PursuitGroup.Add(Pursuit);
|
||||||
|
TempGroups.Add(Pursuit.StartTime, Group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TempGroups.KeySort([](const FDateTime& A, const FDateTime& B)
|
||||||
|
{
|
||||||
|
return A < B;
|
||||||
|
});
|
||||||
|
PursuitGrouped.Reserve(TempGroups.Num());
|
||||||
|
for (const auto& Pair : TempGroups)
|
||||||
|
{
|
||||||
|
PursuitGrouped.Add(Pair.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
PursuitGrouped.Sort([](const FDTFluxPursuitGroup& A, const FDTFluxPursuitGroup& B)
|
||||||
|
{
|
||||||
|
return A.StartTimeGlobal < B.StartTimeGlobal;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@ -1,6 +1,4 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
#pragma once
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "Containers/Deque.h"
|
#include "Containers/Deque.h"
|
||||||
@ -15,6 +13,7 @@
|
|||||||
class UDTFluxNetworkSubsystem;
|
class UDTFluxNetworkSubsystem;
|
||||||
/** Forward Decl */
|
/** Forward Decl */
|
||||||
class UDTFluxModelAsset;
|
class UDTFluxModelAsset;
|
||||||
|
class UDTFluxPursuitManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -85,6 +84,9 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void RequestAllSplitRankingOfStage(int InContestId, int InStageId, int InSplitId);
|
void RequestAllSplitRankingOfStage(int InContestId, int InStageId, int InSplitId);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
const FDTFluxParticipant GetParticipant(int InBib);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
void RefreshStorage();
|
void RefreshStorage();
|
||||||
|
|
||||||
@ -95,6 +97,8 @@ public:
|
|||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
TArray<int> GetContestsIdForTime(const FDateTime Time);
|
TArray<int> GetContestsIdForTime(const FDateTime Time);
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
|
bool GetContestForId(const int Id, FDTFluxContest& OutContest);
|
||||||
|
UFUNCTION()
|
||||||
TArray<FDTFluxContest> GetContestsForTime(const FDateTime Time);
|
TArray<FDTFluxContest> GetContestsForTime(const FDateTime Time);
|
||||||
|
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
@ -102,12 +106,18 @@ public:
|
|||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
TArray<FDTFluxContest> GetContests();
|
TArray<FDTFluxContest> GetContests();
|
||||||
|
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
|
void LaunchPursuitSequenceFor(const TArray<int> ContestIds);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// ~Subsystem Interface
|
// ~Subsystem Interface
|
||||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||||
virtual void Deinitialize() override;
|
virtual void Deinitialize() override;
|
||||||
// ~Subsystem Interface
|
// ~Subsystem Interface
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
UDTFluxPursuitManager* PursuitManager = nullptr;
|
||||||
|
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void SaveDataStorage();
|
void SaveDataStorage();
|
||||||
|
|||||||
133
Source/DTFluxCoreSubsystem/Public/DTFluxPursuitManager.h
Normal file
133
Source/DTFluxCoreSubsystem/Public/DTFluxPursuitManager.h
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Subsystems/DTFluxNetworkSubsystem.h"
|
||||||
|
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
||||||
|
#include "Types/Struct/FDTFluxPursuitInfo.h"
|
||||||
|
#include "UObject/Object.h"
|
||||||
|
#include "DTFluxPursuitManager.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FRequestData
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TArray<FGuid> RequestIds;
|
||||||
|
UPROPERTY()
|
||||||
|
TMap<FGuid, FDTFluxStageRankings> StageRankings;
|
||||||
|
UPROPERTY()
|
||||||
|
int ContestId;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
bool bIsReady = false;
|
||||||
|
|
||||||
|
|
||||||
|
FRequestData() = default;
|
||||||
|
|
||||||
|
FRequestData(const TArray<FGuid>& InRequestIds, const TMap<FGuid, FDTFluxStageRankings>& InStageRankings)
|
||||||
|
: RequestIds(InRequestIds), StageRankings(InStageRankings)
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param RequestId
|
||||||
|
* @param InRankings
|
||||||
|
* @return True if all needed requests have responses
|
||||||
|
*/
|
||||||
|
bool IsWaitingFor(const FGuid& RequestId, const FDTFluxStageRankings& InRankings)
|
||||||
|
{
|
||||||
|
if (!StageRankings.Contains(RequestId))
|
||||||
|
{
|
||||||
|
StageRankings.Add(RequestId, InRankings);
|
||||||
|
}
|
||||||
|
bIsReady = StageRankings.Num() <= RequestIds.Num();
|
||||||
|
return bIsReady;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FDTFluxPursuitGroup
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TArray<FDTFluxPursuitInfo> PursuitGroup = TArray<FDTFluxPursuitInfo>();
|
||||||
|
UPROPERTY()
|
||||||
|
FDateTime StartTimeGlobal = FDateTime::MinValue();
|
||||||
|
UPROPERTY()
|
||||||
|
bool bHasStarted = false;
|
||||||
|
UPROPERTY()
|
||||||
|
bool bIsFocus = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnPursuitSequenceReady, const TArray<FDTFluxPursuitInfo>,
|
||||||
|
NextFocusPursuits,
|
||||||
|
const TArray<FDTFluxPursuitInfo>, NextPursuit, bool, bIsTrtuncate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType)
|
||||||
|
class DTFLUXCORESUBSYSTEM_API UDTFluxPursuitManager : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer);
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
|
TArray<FDTFluxPursuitInfo> NextFocusPursuits;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
|
TArray<FDTFluxPursuitInfo> NextPursuits;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
|
bool bFocusIsTruncate = false;
|
||||||
|
//
|
||||||
|
// UPROPERTY()
|
||||||
|
// TArray<FDTFluxStage> TargetStages;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
int MaxSimultaneousPursuit = 7;
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="DTFlux|Pursuit",
|
||||||
|
meta=(ClampMin="1", ClampMax="60", UIMin="0", UIMax="60"))
|
||||||
|
int MassStartDelay = 10;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TArray<FDTFluxPursuitGroup> PursuitGrouped;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
int CurrentIndex = -1;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintCallable, Category="DTFlux|Pursuit")
|
||||||
|
FOnPursuitSequenceReady OnPursuitSequenceReady;
|
||||||
|
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Pursuit", meta=(Keywords="pursuit, launch, poursuite"))
|
||||||
|
void LaunchPursuitSequenceFor(const TArray<FDTFluxContest> InContests);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRequestResponse(const FGuid& RequestId, FDTFluxServerResponse& Response);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRequestTimeoutResponse(const FGuid& RequestId, const FString& TimeoutMessage);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRequestError(const FGuid& RequestId, const FString& ErrorMessage);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
bool InitSubSystems();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TArray<FRequestData> PendingRequestData;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UFUNCTION()
|
||||||
|
bool InitPursuit(FRequestData Data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
UDTFluxNetworkSubsystem* NetworkSubsystem = nullptr;
|
||||||
|
};
|
||||||
@ -90,6 +90,38 @@ FGuid UDTFluxQueuedManager::QueueRequest(EDTFluxRequestType RequestType, int32 C
|
|||||||
return NewRequest.RequestId;
|
return NewRequest.RequestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UDTFluxQueuedManager::MarkRequestAsError(const FGuid& TargetRequestGuid)
|
||||||
|
{
|
||||||
|
// TODO: Implement a retry mechanism
|
||||||
|
// For now we simply suppress the request and log a message
|
||||||
|
bool bFoundMatch = false;
|
||||||
|
FDTFluxQueuedRequest Request;
|
||||||
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TempQueue;
|
||||||
|
while (PendingRequestsQueue.Dequeue(Request))
|
||||||
|
{
|
||||||
|
if (Request.RequestId == TargetRequestGuid)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxNetwork, Error,
|
||||||
|
TEXT("Marked request %s as error: Type=%d, ContestId=%d, StageId=%d, SplitId=%d"),
|
||||||
|
*Request.RequestId.ToString(), (int32)Request.RequestType, Request.ContestId, Request.StageId,
|
||||||
|
Request.SplitId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TempQueue.Enqueue(Request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (TempQueue.Dequeue(Request))
|
||||||
|
{
|
||||||
|
PendingRequestsQueue.Enqueue(Request);
|
||||||
|
}
|
||||||
|
if (bFoundMatch)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxNetwork, Error, TEXT("No Request Found with GUID %s"), *TargetRequestGuid.ToString());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool UDTFluxQueuedManager::MarkRequestAsResponded(const FGuid& TargetRequestGuid)
|
bool UDTFluxQueuedManager::MarkRequestAsResponded(const FGuid& TargetRequestGuid)
|
||||||
{
|
{
|
||||||
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TempQueue;
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TempQueue;
|
||||||
@ -134,7 +166,8 @@ bool UDTFluxQueuedManager::MarkRequestAsResponded(const FDTFluxQueuedRequest& Ta
|
|||||||
return MarkRequestAsResponded(TargetRequest.RequestId);
|
return MarkRequestAsResponded(TargetRequest.RequestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxQueuedManager::IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId,
|
bool UDTFluxQueuedManager::IsRequestPending(FGuid& OutRequestId, EDTFluxApiDataType RequestType, int32 ContestId,
|
||||||
|
int32 StageId,
|
||||||
int32 SplitId)
|
int32 SplitId)
|
||||||
{
|
{
|
||||||
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TempQueue;
|
TQueue<FDTFluxQueuedRequest, EQueueMode::Mpsc> TempQueue;
|
||||||
@ -148,6 +181,11 @@ bool UDTFluxQueuedManager::IsRequestPending(EDTFluxApiDataType RequestType, int3
|
|||||||
if (!bFoundMatch && Request.Matches(RequestType, ContestId, StageId, SplitId))
|
if (!bFoundMatch && Request.Matches(RequestType, ContestId, StageId, SplitId))
|
||||||
{
|
{
|
||||||
bFoundMatch = true;
|
bFoundMatch = true;
|
||||||
|
OutRequestId = Request.RequestId;
|
||||||
|
UE_LOG(logDTFluxNetwork, Verbose,
|
||||||
|
TEXT("Found pending request %s: Type=%d, ContestId=%d, StageId=%d, SplitId=%d"),
|
||||||
|
*Request.RequestId.ToString(), (int32)Request.RequestType, Request.ContestId, Request.StageId,
|
||||||
|
Request.SplitId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remettre dans la queue temporaire
|
// Remettre dans la queue temporaire
|
||||||
|
|||||||
@ -370,13 +370,13 @@ bool FDTFluxServerResponse::ParseContestRanking(FDTFluxContestRankings& OutConte
|
|||||||
|
|
||||||
bool FDTFluxServerResponse::ParseStageRankingResponse(FDTFluxStageRankings& OutStageRankings)
|
bool FDTFluxServerResponse::ParseStageRankingResponse(FDTFluxStageRankings& OutStageRankings)
|
||||||
{
|
{
|
||||||
|
// UE_LOG(logDTFluxNetwork, Log, TEXT("Response is stage-ranking type %s"), *RawMessage);
|
||||||
if (!ValidateResponseType(TEXT("stage-ranking")))
|
if (!ValidateResponseType(TEXT("stage-ranking")))
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxNetwork, Error, TEXT("Response is not a stage-ranking type"));
|
UE_LOG(logDTFluxNetwork, Error, TEXT("Response is not a stage-ranking type"));
|
||||||
ParsingStatus = EDTFluxResponseStatus::InvalidType;
|
ParsingStatus = EDTFluxResponseStatus::InvalidType;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FDTFluxStageRankingResponse RankingResponse;
|
FDTFluxStageRankingResponse RankingResponse;
|
||||||
if (!FJsonObjectConverter::JsonObjectStringToUStruct<FDTFluxStageRankingResponse>(RawMessage, &RankingResponse))
|
if (!FJsonObjectConverter::JsonObjectStringToUStruct<FDTFluxStageRankingResponse>(RawMessage, &RankingResponse))
|
||||||
{
|
{
|
||||||
@ -384,6 +384,7 @@ bool FDTFluxServerResponse::ParseStageRankingResponse(FDTFluxStageRankings& OutS
|
|||||||
ParsingStatus = EDTFluxResponseStatus::JsonParseError;
|
ParsingStatus = EDTFluxResponseStatus::JsonParseError;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Reponse Update"));
|
||||||
|
|
||||||
OutStageRankings.ContestId = ContestID;
|
OutStageRankings.ContestId = ContestID;
|
||||||
OutStageRankings.StageId = StageID;
|
OutStageRankings.StageId = StageID;
|
||||||
|
|||||||
@ -81,8 +81,9 @@ FGuid UDTFluxNetworkSubsystem::SendTrackedRequestWithCallback(
|
|||||||
int32 ContestId,
|
int32 ContestId,
|
||||||
int32 StageId,
|
int32 StageId,
|
||||||
int32 SplitId,
|
int32 SplitId,
|
||||||
FOnDTFluxRequestResponse OnCompleted,
|
FOnDTFluxTrackedRequestResponse OnCompleted,
|
||||||
FOnDTFluxRequestTimeout OnTimeout,
|
FOnDTFluxTrackedRequestTimeout OnTimeout,
|
||||||
|
TOptional<FOnDTFluxRequestResponseError> OnError,
|
||||||
float TimeoutSeconds)
|
float TimeoutSeconds)
|
||||||
{
|
{
|
||||||
FGuid RequestId = SendTrackedRequest(RequestType, ContestId, StageId, SplitId, TimeoutSeconds);
|
FGuid RequestId = SendTrackedRequest(RequestType, ContestId, StageId, SplitId, TimeoutSeconds);
|
||||||
@ -98,11 +99,16 @@ FGuid UDTFluxNetworkSubsystem::SendTrackedRequestWithCallback(
|
|||||||
{
|
{
|
||||||
PendingTimeoutCallbacks.Add(RequestId, OnTimeout);
|
PendingTimeoutCallbacks.Add(RequestId, OnTimeout);
|
||||||
}
|
}
|
||||||
|
if (OnError.IsSet() && OnError.GetValue().IsBound())
|
||||||
|
{
|
||||||
|
PendingErrorCallbacks.Add(RequestId, OnError.GetValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return RequestId;
|
return RequestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool UDTFluxNetworkSubsystem::GetTrackedRequest(const FGuid& RequestId, FDTFluxQueuedRequest& OutRequest) const
|
bool UDTFluxNetworkSubsystem::GetTrackedRequest(const FGuid& RequestId, FDTFluxQueuedRequest& OutRequest) const
|
||||||
{
|
{
|
||||||
if (!QueueManager)
|
if (!QueueManager)
|
||||||
@ -155,7 +161,8 @@ bool UDTFluxNetworkSubsystem::IsRequestPending(EDTFluxRequestType RequestType, i
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return QueueManager->IsRequestPending(RequestType, ContestId, StageId, SplitId);
|
FGuid OutRequestId;
|
||||||
|
return QueueManager->IsRequestPending(OutRequestId, RequestType, ContestId, StageId, SplitId);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 UDTFluxNetworkSubsystem::GetPendingRequestCount() const
|
int32 UDTFluxNetworkSubsystem::GetPendingRequestCount() const
|
||||||
@ -583,8 +590,6 @@ void UDTFluxNetworkSubsystem::Parse(FDTFluxServerResponse& Response)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//TODO reforge API to keep track of Requests
|
|
||||||
void UDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& MessageString)
|
void UDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& MessageString)
|
||||||
{
|
{
|
||||||
// UE_LOG(logDTFluxNetwork, Warning, TEXT("Client %s :\nMessage Received : %s"), *WsClient->GetAddress(), *MessageString);
|
// UE_LOG(logDTFluxNetwork, Warning, TEXT("Client %s :\nMessage Received : %s"), *WsClient->GetAddress(), *MessageString);
|
||||||
@ -600,8 +605,11 @@ void UDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& M
|
|||||||
UE_LOG(logDTFluxNetwork, Warning, TEXT("Not a push message"));
|
UE_LOG(logDTFluxNetwork, Warning, TEXT("Not a push message"));
|
||||||
// Legacy
|
// Legacy
|
||||||
Parse(Response);
|
Parse(Response);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// // if we are here we have a tracked Message
|
||||||
|
// QueueManager->MarkRequestAsResponded()
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent)
|
void UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent)
|
||||||
@ -609,6 +617,29 @@ void UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem(const FStrin
|
|||||||
UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws %s :\nMessage Sent: %s"), *WsClient->GetAddress(), *MessageSent);
|
UE_LOG(logDTFluxNetwork, Warning, TEXT("Ws %s :\nMessage Sent: %s"), *WsClient->GetAddress(), *MessageSent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UDTFluxNetworkSubsystem::CleanRequestCallbacks(const FGuid& RequestId)
|
||||||
|
{
|
||||||
|
bool bCbSuppressSuccess = false;
|
||||||
|
bool bErrorCbSuppressSuccess = false;
|
||||||
|
bool bTimeoutCbSuppressSuccess = false;
|
||||||
|
if (PendingCallbacks.Contains(RequestId))
|
||||||
|
{
|
||||||
|
PendingCallbacks.Remove(RequestId);
|
||||||
|
bCbSuppressSuccess = true;
|
||||||
|
}
|
||||||
|
if (PendingTimeoutCallbacks.Contains(RequestId))
|
||||||
|
{
|
||||||
|
PendingTimeoutCallbacks.Remove(RequestId);
|
||||||
|
bTimeoutCbSuppressSuccess = true;
|
||||||
|
}
|
||||||
|
if (PendingTimeoutCallbacks.Contains(RequestId))
|
||||||
|
{
|
||||||
|
PendingTimeoutCallbacks.Remove(RequestId);
|
||||||
|
bErrorCbSuppressSuccess = true;
|
||||||
|
}
|
||||||
|
return bCbSuppressSuccess && bErrorCbSuppressSuccess && bTimeoutCbSuppressSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
void UDTFluxNetworkSubsystem::OnRequestTimedOut_Internal(const FDTFluxQueuedRequest& TimedOutRequest)
|
void UDTFluxNetworkSubsystem::OnRequestTimedOut_Internal(const FDTFluxQueuedRequest& TimedOutRequest)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxNetwork, Warning, TEXT("Request %s timed out: Type=%d, Contest=%d, Stage=%d, Split=%d"),
|
UE_LOG(logDTFluxNetwork, Warning, TEXT("Request %s timed out: Type=%d, Contest=%d, Stage=%d, Split=%d"),
|
||||||
@ -619,7 +650,7 @@ void UDTFluxNetworkSubsystem::OnRequestTimedOut_Internal(const FDTFluxQueuedRequ
|
|||||||
TimedOutRequest.SplitId);
|
TimedOutRequest.SplitId);
|
||||||
|
|
||||||
// Appeler le callback de timeout si présent
|
// Appeler le callback de timeout si présent
|
||||||
if (FOnDTFluxRequestTimeout* TimeoutCallback = PendingTimeoutCallbacks.Find(TimedOutRequest.RequestId))
|
if (FOnDTFluxTrackedRequestTimeout* TimeoutCallback = PendingTimeoutCallbacks.Find(TimedOutRequest.RequestId))
|
||||||
{
|
{
|
||||||
if (TimeoutCallback->IsBound())
|
if (TimeoutCallback->IsBound())
|
||||||
{
|
{
|
||||||
@ -635,32 +666,28 @@ void UDTFluxNetworkSubsystem::OnRequestTimedOut_Internal(const FDTFluxQueuedRequ
|
|||||||
OnTrackedRequestFailed.Broadcast(TimedOutRequest.RequestId, TimedOutRequest.RequestType, TEXT("Request timeout"));
|
OnTrackedRequestFailed.Broadcast(TimedOutRequest.RequestId, TimedOutRequest.RequestType, TEXT("Request timeout"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UDTFluxNetworkSubsystem::TryMatchResponseToQueuedRequest(const FDTFluxServerResponse& Response)
|
bool UDTFluxNetworkSubsystem::TryMatchResponseToQueuedRequest(FDTFluxServerResponse& Response)
|
||||||
{
|
{
|
||||||
if (!QueueManager)
|
if (!QueueManager)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Essayer de trouver une requête correspondante
|
FGuid FoundRequestId;
|
||||||
// Note: Cette méthode nécessiterait une modification de UDTFluxQueuedManager pour supporter le matching par type et paramètres
|
if (QueueManager->IsRequestPending(FoundRequestId, Response.GetResponseType(), Response.ContestID, Response.StageID,
|
||||||
|
|
||||||
// Pour l'instant, on utilise une approche simple : chercher la première requête du bon type
|
|
||||||
// Vous devrez probablement modifier UDTFluxQueuedManager pour ajouter une méthode comme FindMatchingRequest()
|
|
||||||
|
|
||||||
|
|
||||||
// Implémentation temporaire : on assume qu'il n'y a qu'une requête de chaque type en cours
|
|
||||||
if (QueueManager->IsRequestPending(Response.GetResponseType(), Response.ContestID, Response.StageID,
|
|
||||||
Response.SplitID))
|
Response.SplitID))
|
||||||
{
|
{
|
||||||
// Marquer comme répondu - vous devrez adapter cette méthode selon votre logique de matching
|
|
||||||
// Pour l'instant, on va faire un workaround simple
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Log,
|
UE_LOG(logDTFluxNetwork, Log,
|
||||||
TEXT("Matched response to queued request: Type=%s, Contest=%d, Stage=%d, Split=%d"),
|
TEXT("Matched response to queued request: Type=%s, Contest=%d, Stage=%d, Split=%d"),
|
||||||
*UEnum::GetValueAsString(Response.GetResponseType()), Response.ContestID, Response.StageID,
|
*UEnum::GetValueAsString(Response.GetResponseType()), Response.ContestID, Response.StageID,
|
||||||
Response.SplitID);
|
Response.SplitID);
|
||||||
|
if (PendingCallbacks.Contains(FoundRequestId))
|
||||||
return true;
|
{
|
||||||
|
FOnDTFluxTrackedRequestResponse* SuccessCallback = PendingCallbacks.Find(FoundRequestId);
|
||||||
|
SuccessCallback->ExecuteIfBound(FoundRequestId, Response);
|
||||||
|
//Suppress Callback;
|
||||||
|
return CleanRequestCallbacks(FoundRequestId);
|
||||||
|
}
|
||||||
|
return QueueManager->MarkRequestAsResponded(FoundRequestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -676,13 +703,26 @@ void UDTFluxNetworkSubsystem::CompleteTrackedRequest(const FGuid& RequestId, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Appeler le callback de succès si présent
|
// Appeler le callback de succès si présent
|
||||||
if (FOnDTFluxRequestResponse* SuccessCallback = PendingCallbacks.Find(RequestId))
|
if (FOnDTFluxTrackedRequestResponse* SuccessCallback = PendingCallbacks.Find(RequestId))
|
||||||
{
|
{
|
||||||
if (SuccessCallback->IsBound())
|
if (SuccessCallback->IsBound())
|
||||||
{
|
{
|
||||||
SuccessCallback->Execute(RequestId, ResponseData);
|
EDTFluxResponseStatus ResponseStatus;
|
||||||
}
|
FDTFluxServerResponse Response(ResponseData, ResponseStatus);
|
||||||
|
if (ResponseStatus == EDTFluxResponseStatus::Success)
|
||||||
|
{
|
||||||
|
SuccessCallback->Execute(RequestId, Response);
|
||||||
|
QueueManager->MarkRequestAsResponded(RequestId);
|
||||||
PendingCallbacks.Remove(RequestId);
|
PendingCallbacks.Remove(RequestId);
|
||||||
|
PendingTimeoutCallbacks.Remove(RequestId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QueueManager->MarkRequestAsError(RequestId);
|
||||||
|
// Fail
|
||||||
|
// FailTrackedRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nettoyer le callback de timeout
|
// Nettoyer le callback de timeout
|
||||||
@ -698,11 +738,11 @@ void UDTFluxNetworkSubsystem::FailTrackedRequest(const FGuid& RequestId, const F
|
|||||||
EDTFluxRequestType RequestType)
|
EDTFluxRequestType RequestType)
|
||||||
{
|
{
|
||||||
// Appeler le callback d'erreur si présent
|
// Appeler le callback d'erreur si présent
|
||||||
if (FOnDTFluxRequestTimeout* ErrorCallback = PendingTimeoutCallbacks.Find(RequestId))
|
if (FOnDTFluxTrackedRequestTimeout* ErrorCallback = PendingTimeoutCallbacks.Find(RequestId))
|
||||||
{
|
{
|
||||||
if (ErrorCallback->IsBound())
|
if (ErrorCallback->IsBound())
|
||||||
{
|
{
|
||||||
ErrorCallback->Execute(RequestId, ErrorMessage);
|
ErrorCallback->ExecuteIfBound(RequestId, ErrorMessage);
|
||||||
}
|
}
|
||||||
PendingTimeoutCallbacks.Remove(RequestId);
|
PendingTimeoutCallbacks.Remove(RequestId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,107 +11,6 @@
|
|||||||
#include "Types/Enum/DTFluxCoreEnum.h"
|
#include "Types/Enum/DTFluxCoreEnum.h"
|
||||||
#include "DTFluxQueuedManager.generated.h"
|
#include "DTFluxQueuedManager.generated.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Structure représentant une requête en file d'attente avec ses métadonnées
|
|
||||||
*/
|
|
||||||
USTRUCT(BlueprintType)
|
|
||||||
struct FDTFluxQueuedRequest : public FDTFluxRequestBase
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
|
|
||||||
/** L'identifiant unique de la requête */
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
|
||||||
FGuid RequestId;
|
|
||||||
|
|
||||||
/** L'heure à laquelle la requête a été envoyée */
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
|
||||||
FDateTime CreatedAt;
|
|
||||||
|
|
||||||
/** Le type de requête */
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
|
||||||
EDTFluxApiDataType RequestType = EDTFluxRequestType::None;
|
|
||||||
|
|
||||||
/** Identifiant de la compétition (ContestId) */
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
|
||||||
int32 ContestId = -1;
|
|
||||||
|
|
||||||
/** Identifiant de l'étape (StageId) */
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
|
||||||
int32 StageId = -1;
|
|
||||||
|
|
||||||
/** Identifiant du split (SplitId) */
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
|
||||||
int32 SplitId = -1;
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
|
||||||
FString RawResponse = "";
|
|
||||||
|
|
||||||
/** Délai maximum avant que la requête soit considérée comme expirée (en secondes) */
|
|
||||||
UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request")
|
|
||||||
float TimeoutSeconds = 2.0f;
|
|
||||||
|
|
||||||
/** Determine si la requête peut être mise en cache */
|
|
||||||
UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request")
|
|
||||||
bool bIsCacheable = false;
|
|
||||||
|
|
||||||
/** Validité du cache si bIsCacheable est mis à true après reception de la réponse (en secondes) */
|
|
||||||
UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request")
|
|
||||||
float CachedValidity = 50.0f;
|
|
||||||
|
|
||||||
/** Indicateur si la requête a reçu une réponse */
|
|
||||||
UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request")
|
|
||||||
bool bHasReceivedResponse = false;
|
|
||||||
|
|
||||||
/** Constructeur par défaut */
|
|
||||||
FDTFluxQueuedRequest()
|
|
||||||
{
|
|
||||||
RequestId = FGuid::NewGuid();
|
|
||||||
CreatedAt = FDateTime::Now();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Constructeur avec paramètres */
|
|
||||||
FDTFluxQueuedRequest(EDTFluxRequestType InRequestType, int32 InContestId = -1, int32 InStageId = -1,
|
|
||||||
int32 InSplitId = -1)
|
|
||||||
: RequestType(InRequestType)
|
|
||||||
, ContestId(InContestId)
|
|
||||||
, StageId(InStageId)
|
|
||||||
, SplitId(InSplitId)
|
|
||||||
{
|
|
||||||
RequestId = FGuid::NewGuid();
|
|
||||||
CreatedAt = FDateTime::Now();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const FDTFluxQueuedRequest& Left) const
|
|
||||||
{
|
|
||||||
return RequestId == Left.RequestId;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const FDTFluxQueuedRequest& Left) const
|
|
||||||
{
|
|
||||||
return RequestId != Left.RequestId;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const FString Serialize() const;
|
|
||||||
|
|
||||||
/** Vérifie si la requête a expiré */
|
|
||||||
bool HasTimedOut() const
|
|
||||||
{
|
|
||||||
return (FDateTime::Now() - CreatedAt).GetTotalSeconds() > TimeoutSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Vérifie si cette requête correspond aux paramètres spécifiés */
|
|
||||||
bool Matches(EDTFluxRequestType InRequestType, int32 InContestId = -1, int32 InStageId = -1,
|
|
||||||
int32 InSplitId = -1) const
|
|
||||||
{
|
|
||||||
return RequestType == InRequestType &&
|
|
||||||
(InContestId == -1 || ContestId == InContestId) &&
|
|
||||||
(InStageId == -1 || StageId == InStageId) &&
|
|
||||||
(InSplitId == -1 || SplitId == InSplitId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestTimedOut, const FDTFluxQueuedRequest&, TimedOutRequest);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestTimedOut, const FDTFluxQueuedRequest&, TimedOutRequest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,9 +29,11 @@ public:
|
|||||||
void Initialize();
|
void Initialize();
|
||||||
FGuid QueueRequest(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1,
|
FGuid QueueRequest(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1,
|
||||||
const FString& RawMessage = "");
|
const FString& RawMessage = "");
|
||||||
|
bool MarkRequestAsError(const FGuid& TargetRequestGuid);
|
||||||
bool MarkRequestAsResponded(const FGuid& TargetRequestGuid);
|
bool MarkRequestAsResponded(const FGuid& TargetRequestGuid);
|
||||||
bool MarkRequestAsResponded(const FDTFluxQueuedRequest& TargetRequest);
|
bool MarkRequestAsResponded(const FDTFluxQueuedRequest& TargetRequest);
|
||||||
bool IsRequestPending(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1);
|
bool IsRequestPending(FGuid& OutRequestId, EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
||||||
|
int32 SplitId = -1);
|
||||||
FDTFluxQueuedRequest* GetRequestPending(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
FDTFluxQueuedRequest* GetRequestPending(EDTFluxRequestType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
||||||
int32 SplitId = -1);
|
int32 SplitId = -1);
|
||||||
const FDTFluxQueuedRequest* GetRequest(const FGuid& SearchedGuid);
|
const FDTFluxQueuedRequest* GetRequest(const FGuid& SearchedGuid);
|
||||||
@ -142,7 +43,6 @@ public:
|
|||||||
void ClearAllRequests();
|
void ClearAllRequests();
|
||||||
// bool TryProcessResponse(const FDTFluxServerResponse& Response);
|
// bool TryProcessResponse(const FDTFluxServerResponse& Response);
|
||||||
|
|
||||||
|
|
||||||
// Interface FTickableGameObject
|
// Interface FTickableGameObject
|
||||||
virtual void Tick(float DeltaTime) override;
|
virtual void Tick(float DeltaTime) override;
|
||||||
virtual bool IsTickable() const override;
|
virtual bool IsTickable() const override;
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
|
#include "Types/Enum/DTFluxCoreEnum.h"
|
||||||
#include "UObject/Object.h"
|
#include "UObject/Object.h"
|
||||||
#include "DTFluxRequestStructs.generated.h"
|
#include "DTFluxRequestStructs.generated.h"
|
||||||
|
|
||||||
@ -139,3 +140,104 @@ public:
|
|||||||
SplitID = InSplitId;
|
SplitID = InSplitId;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure représentant une requête en file d'attente avec ses métadonnées
|
||||||
|
*/
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FDTFluxQueuedRequest : public FDTFluxRequestBase
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** L'identifiant unique de la requête */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
||||||
|
FGuid RequestId;
|
||||||
|
|
||||||
|
/** L'heure à laquelle la requête a été envoyée */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
||||||
|
FDateTime CreatedAt;
|
||||||
|
|
||||||
|
/** Le type de requête */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
||||||
|
EDTFluxApiDataType RequestType = EDTFluxRequestType::None;
|
||||||
|
|
||||||
|
/** Identifiant de la compétition (ContestId) */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
||||||
|
int32 ContestId = -1;
|
||||||
|
|
||||||
|
/** Identifiant de l'étape (StageId) */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
||||||
|
int32 StageId = -1;
|
||||||
|
|
||||||
|
/** Identifiant du split (SplitId) */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
||||||
|
int32 SplitId = -1;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Request")
|
||||||
|
FString RawResponse = "";
|
||||||
|
|
||||||
|
/** Délai maximum avant que la requête soit considérée comme expirée (en secondes) */
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request")
|
||||||
|
float TimeoutSeconds = 2.0f;
|
||||||
|
|
||||||
|
/** Determine si la requête peut être mise en cache */
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request")
|
||||||
|
bool bIsCacheable = false;
|
||||||
|
|
||||||
|
/** Validité du cache si bIsCacheable est mis à true après reception de la réponse (en secondes) */
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request")
|
||||||
|
float CachedValidity = 50.0f;
|
||||||
|
|
||||||
|
/** Indicateur si la requête a reçu une réponse */
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category = "DTFlux|Request")
|
||||||
|
bool bHasReceivedResponse = false;
|
||||||
|
|
||||||
|
/** Constructeur par défaut */
|
||||||
|
FDTFluxQueuedRequest()
|
||||||
|
{
|
||||||
|
RequestId = FGuid::NewGuid();
|
||||||
|
CreatedAt = FDateTime::Now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructeur avec paramètres */
|
||||||
|
FDTFluxQueuedRequest(EDTFluxRequestType InRequestType, int32 InContestId = -1, int32 InStageId = -1,
|
||||||
|
int32 InSplitId = -1)
|
||||||
|
: RequestType(InRequestType)
|
||||||
|
, ContestId(InContestId)
|
||||||
|
, StageId(InStageId)
|
||||||
|
, SplitId(InSplitId)
|
||||||
|
{
|
||||||
|
RequestId = FGuid::NewGuid();
|
||||||
|
CreatedAt = FDateTime::Now();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const FDTFluxQueuedRequest& Left) const
|
||||||
|
{
|
||||||
|
return RequestId == Left.RequestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const FDTFluxQueuedRequest& Left) const
|
||||||
|
{
|
||||||
|
return RequestId != Left.RequestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const FString Serialize() const;
|
||||||
|
|
||||||
|
/** Vérifie si la requête a expiré */
|
||||||
|
bool HasTimedOut() const
|
||||||
|
{
|
||||||
|
return (FDateTime::Now() - CreatedAt).GetTotalSeconds() > TimeoutSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Vérifie si cette requête correspond aux paramètres spécifiés */
|
||||||
|
bool Matches(EDTFluxRequestType InRequestType, int32 InContestId = -1, int32 InStageId = -1,
|
||||||
|
int32 InSplitId = -1) const
|
||||||
|
{
|
||||||
|
return RequestType == InRequestType &&
|
||||||
|
(InContestId == -1 || ContestId == InContestId) &&
|
||||||
|
(InStageId == -1 || StageId == InStageId) &&
|
||||||
|
(InSplitId == -1 || SplitId == InSplitId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@ -117,4 +117,27 @@ private:
|
|||||||
bool ParseJsonObject(TSharedPtr<FJsonObject>& OutJsonObject) const;
|
bool ParseJsonObject(TSharedPtr<FJsonObject>& OutJsonObject) const;
|
||||||
bool ValidateResponseType(const FString& ExpectedType) const;
|
bool ValidateResponseType(const FString& ExpectedType) const;
|
||||||
EDTFluxResponseStatus InitializeFromJson(const FString& JsonMessage, bool bLogErrors);
|
EDTFluxResponseStatus InitializeFromJson(const FString& JsonMessage, bool bLogErrors);
|
||||||
|
|
||||||
|
static FString GetJsonType(const EJson Type)
|
||||||
|
{
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case EJson::None:
|
||||||
|
return TEXT("None");
|
||||||
|
case EJson::Null:
|
||||||
|
return TEXT("Null");
|
||||||
|
case EJson::String:
|
||||||
|
return TEXT("String");
|
||||||
|
case EJson::Number:
|
||||||
|
return TEXT("Number");
|
||||||
|
case EJson::Boolean:
|
||||||
|
return TEXT("Boolean");
|
||||||
|
case EJson::Array:
|
||||||
|
return TEXT("Array");
|
||||||
|
case EJson::Object:
|
||||||
|
return TEXT("Object");
|
||||||
|
default:
|
||||||
|
return TEXT("Unknown");
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -23,8 +23,9 @@ typedef TSharedPtr<FDTFluxHttpClient> FDTFluxHttpClientSP;
|
|||||||
|
|
||||||
|
|
||||||
// Delegates pour les requêtes avec callback
|
// Delegates pour les requêtes avec callback
|
||||||
DECLARE_DELEGATE_TwoParams(FOnDTFluxRequestResponse, const FGuid&, const FString&);
|
DECLARE_DELEGATE_TwoParams(FOnDTFluxRequestResponseError, const FGuid&, const FString&);
|
||||||
DECLARE_DELEGATE_TwoParams(FOnDTFluxRequestTimeout, const FGuid&, const FString&);
|
DECLARE_DELEGATE_TwoParams(FOnDTFluxTrackedRequestResponse, const FGuid&, FDTFluxServerResponse&);
|
||||||
|
DECLARE_DELEGATE_TwoParams(FOnDTFluxTrackedRequestTimeout, const FGuid&, const FString& /*ErrorMessage*/);
|
||||||
// Delegates Blueprint pour les requêtes avec tracking
|
// Delegates Blueprint pour les requêtes avec tracking
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestCompleted, const FGuid&, RequestId,
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestCompleted, const FGuid&, RequestId,
|
||||||
EDTFluxApiDataType, RequestType, const FString&, ResponseData);
|
EDTFluxApiDataType, RequestType, const FString&, ResponseData);
|
||||||
@ -128,9 +129,14 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
||||||
FGuid SendTrackedRequest(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
FGuid SendTrackedRequest(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1,
|
||||||
int32 SplitId = -1, float TimeoutSeconds = 30.0f);
|
int32 SplitId = -1, float TimeoutSeconds = 30.0f);
|
||||||
|
|
||||||
FGuid SendTrackedRequestWithCallback(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId, int32 SplitId,
|
FGuid SendTrackedRequestWithCallback(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId, int32 SplitId,
|
||||||
FOnDTFluxRequestResponse OnCompleted, FOnDTFluxRequestTimeout OnTimeout,
|
FOnDTFluxTrackedRequestResponse OnCompleted,
|
||||||
|
FOnDTFluxTrackedRequestTimeout OnTimeout,
|
||||||
|
TOptional<FOnDTFluxRequestResponseError> OnError = TOptional<
|
||||||
|
FOnDTFluxRequestResponseError>(),
|
||||||
float TimeoutSeconds = 30.0f);
|
float TimeoutSeconds = 30.0f);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Tracked Requests")
|
||||||
bool GetTrackedRequest(const FGuid& RequestId, FDTFluxQueuedRequest& OutRequest) const;
|
bool GetTrackedRequest(const FGuid& RequestId, FDTFluxQueuedRequest& OutRequest) const;
|
||||||
const FDTFluxQueuedRequest* GetTrackedRequestPtr(const FGuid& RequestId) const;
|
const FDTFluxQueuedRequest* GetTrackedRequestPtr(const FGuid& RequestId) const;
|
||||||
@ -175,8 +181,9 @@ private:
|
|||||||
UDTFluxQueuedManager* QueueManager;
|
UDTFluxQueuedManager* QueueManager;
|
||||||
|
|
||||||
// === MAPPING DES CALLBACKS C++ ===
|
// === MAPPING DES CALLBACKS C++ ===
|
||||||
TMap<FGuid, FOnDTFluxRequestResponse> PendingCallbacks;
|
TMap<FGuid, FOnDTFluxTrackedRequestResponse> PendingCallbacks;
|
||||||
TMap<FGuid, FOnDTFluxRequestTimeout> PendingTimeoutCallbacks;
|
TMap<FGuid, FOnDTFluxTrackedRequestTimeout> PendingTimeoutCallbacks;
|
||||||
|
TMap<FGuid, FOnDTFluxRequestResponseError> PendingErrorCallbacks;
|
||||||
|
|
||||||
// === CLIENTS RÉSEAU ===
|
// === CLIENTS RÉSEAU ===
|
||||||
FDTFluxWebSocketClientSP WsClient = nullptr;
|
FDTFluxWebSocketClientSP WsClient = nullptr;
|
||||||
@ -221,11 +228,12 @@ private:
|
|||||||
void Parse(FDTFluxServerResponse& Response);
|
void Parse(FDTFluxServerResponse& Response);
|
||||||
void OnWebSocketMessageEvent_Subsystem(const FString& MessageString);
|
void OnWebSocketMessageEvent_Subsystem(const FString& MessageString);
|
||||||
void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent);
|
void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent);
|
||||||
|
bool CleanRequestCallbacks(const FGuid& RequestId);
|
||||||
|
|
||||||
// === GESTION DES REQUÊTES TRACKÉES ===
|
// === GESTION DES REQUÊTES TRACKÉES ===
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void OnRequestTimedOut_Internal(const FDTFluxQueuedRequest& TimedOutRequest);
|
void OnRequestTimedOut_Internal(const FDTFluxQueuedRequest& TimedOutRequest);
|
||||||
bool TryMatchResponseToQueuedRequest(const FDTFluxServerResponse& Response);
|
bool TryMatchResponseToQueuedRequest(FDTFluxServerResponse& Response);
|
||||||
void CompleteTrackedRequest(const FGuid& RequestId, const FString& ResponseData, EDTFluxRequestType RequestType);
|
void CompleteTrackedRequest(const FGuid& RequestId, const FString& ResponseData, EDTFluxRequestType RequestType);
|
||||||
void FailTrackedRequest(const FGuid& RequestId, const FString& ErrorMessage, EDTFluxRequestType RequestType);
|
void FailTrackedRequest(const FGuid& RequestId, const FString& ErrorMessage, EDTFluxRequestType RequestType);
|
||||||
void SendQueuedRequest(const FDTFluxQueuedRequest& QueuedRequest);
|
void SendQueuedRequest(const FDTFluxQueuedRequest& QueuedRequest);
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
using UnrealBuildTool;
|
|
||||||
|
|
||||||
public class DTFluxPursuitSystem : ModuleRules
|
|
||||||
{
|
|
||||||
public DTFluxPursuitSystem(ReadOnlyTargetRules Target) : base(Target)
|
|
||||||
{
|
|
||||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
|
||||||
|
|
||||||
PublicDependencyModuleNames.AddRange(
|
|
||||||
new string[]
|
|
||||||
{
|
|
||||||
"Core"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
PrivateDependencyModuleNames.AddRange(
|
|
||||||
new string[]
|
|
||||||
{
|
|
||||||
"CoreUObject",
|
|
||||||
"Engine",
|
|
||||||
"Slate",
|
|
||||||
"SlateCore",
|
|
||||||
"DTFluxCore",
|
|
||||||
"DTFluxCoreSubsystem"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
#include "DTFluxPursuitSystemModule.h"
|
|
||||||
|
|
||||||
#define LOCTEXT_NAMESPACE "FDTFluxPursuitSystemModule"
|
|
||||||
|
|
||||||
DEFINE_LOG_CATEGORY(logDTFluxPursuitSystem);
|
|
||||||
|
|
||||||
void FDTFluxPursuitSystem::StartupModule()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxPursuitSystem::ShutdownModule()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef LOCTEXT_NAMESPACE
|
|
||||||
|
|
||||||
IMPLEMENT_MODULE(FDTFluxPursuitSystem, DTFluxPursuitSystem)
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
|
||||||
#include "Modules/ModuleManager.h"
|
|
||||||
|
|
||||||
DECLARE_LOG_CATEGORY_EXTERN(logDTFluxPursuitSystem, All, All)
|
|
||||||
|
|
||||||
class DTFLUXPURSUITSYSTEM_API FDTFluxPursuitSystem : public IModuleInterface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void StartupModule() override;
|
|
||||||
virtual void ShutdownModule() override;
|
|
||||||
};
|
|
||||||
34
Source/DTFluxRaceResult/DTFluxRaceResult.Build.cs
Normal file
34
Source/DTFluxRaceResult/DTFluxRaceResult.Build.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using UnrealBuildTool;
|
||||||
|
|
||||||
|
public class DTFluxRaceResult : ModuleRules
|
||||||
|
{
|
||||||
|
public DTFluxRaceResult(ReadOnlyTargetRules Target) : base(Target)
|
||||||
|
{
|
||||||
|
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||||
|
|
||||||
|
PublicDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"Core"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
PrivateDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"CoreUObject",
|
||||||
|
"Engine",
|
||||||
|
"Slate",
|
||||||
|
"SlateCore",
|
||||||
|
"DTFluxProjectSettings",
|
||||||
|
"UMG",
|
||||||
|
"WebBrowser",
|
||||||
|
"Projects",
|
||||||
|
"ToolMenus",
|
||||||
|
"HTTP",
|
||||||
|
"JsonUtilities",
|
||||||
|
"Json"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
161
Source/DTFluxRaceResult/Private/DTFluxRaceResultModule.cpp
Normal file
161
Source/DTFluxRaceResult/Private/DTFluxRaceResultModule.cpp
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
#include "DTFluxRaceResultModule.h"
|
||||||
|
|
||||||
|
#include "LevelEditor.h"
|
||||||
|
#include "Widget/SDTFluxRaceResultWidget.h"
|
||||||
|
#include "Widget/Style/DTFluxRaceResultStyle.h"
|
||||||
|
|
||||||
|
#define LOCTEXT_NAMESPACE "FDTFluxRaceResultModule"
|
||||||
|
|
||||||
|
DEFINE_LOG_CATEGORY(logDTFluxRaceResult)
|
||||||
|
|
||||||
|
FName DTFLUXRACERESULT_API FDTFluxRaceResult::RaceResultTabId = "DTFluxRaceResult";
|
||||||
|
FText DTFLUXRACERESULT_API FDTFluxRaceResult::RaceResultTabDisplayName = FText::FromString(TEXT("DTFlux RaceResult"));
|
||||||
|
|
||||||
|
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::StartupModule()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Warning, TEXT("DTFluxRaceResult Module Started"))
|
||||||
|
FDTFluxRaceResultStyle::RegisterStyle();
|
||||||
|
InitMenuExtension();
|
||||||
|
RegisterRaceResultTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma region MenuExtension
|
||||||
|
|
||||||
|
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::InitMenuExtension()
|
||||||
|
{
|
||||||
|
UToolMenus::RegisterStartupCallback(
|
||||||
|
FSimpleMulticastDelegate::FDelegate::CreateRaw(this,
|
||||||
|
&FDTFluxRaceResult::RegisterMenuExtensions));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::RegisterMenuExtensions()
|
||||||
|
{
|
||||||
|
|
||||||
|
// Étendre le menu enregistré
|
||||||
|
if (UToolMenu* DTFluxMenu = UToolMenus::Get()->ExtendMenu("DTFlux.MainMenu"))
|
||||||
|
{
|
||||||
|
FToolMenuSection& ToolsSection = DTFluxMenu->FindOrAddSection("Tools");
|
||||||
|
ToolsSection.AddMenuEntry(
|
||||||
|
"DTFluxRaceResult",
|
||||||
|
FText::FromString("RaceResult"),
|
||||||
|
FText::FromString("Launch DTFlux RaceResult Control Panel"),
|
||||||
|
FSlateIcon(FDTFluxRaceResultStyle::GetStyleSetName(), "LevelEditor.Tab.IconRaceResult"),
|
||||||
|
FUIAction(FExecuteAction::CreateRaw(this, &FDTFluxRaceResult::OnButtonClicked))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxRaceResult::CreateSubmenu(UToolMenu* Menu)
|
||||||
|
{
|
||||||
|
// Section 2 : Tools
|
||||||
|
FToolMenuSection& ToolsSection = Menu->FindOrAddSection("Tools");
|
||||||
|
ToolsSection.Label = FText::FromString("Tools");
|
||||||
|
ToolsSection.AddMenuEntry(
|
||||||
|
"DTFluxRaceResult",
|
||||||
|
FText::FromString("RaceResult"),
|
||||||
|
FText::FromString("Launch Race Result WebAdmin Console"),
|
||||||
|
FSlateIcon(FDTFluxRaceResultStyle::GetStyleSetName(), "LevelEditor.Tab.IconRaceResult"),
|
||||||
|
// Adaptez selon votre icône
|
||||||
|
FUIAction(FExecuteAction::CreateRaw(this, &FDTFluxRaceResult::OnButtonClicked))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::OnButtonClicked()
|
||||||
|
{
|
||||||
|
FGlobalTabmanager::Get()->TryInvokeTab(RaceResultTabId);
|
||||||
|
UE_LOG(LogTemp, Log, TEXT("Race Result Launched"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion EditorTab
|
||||||
|
|
||||||
|
#pragma region
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::RegisterRaceResultTab()
|
||||||
|
{
|
||||||
|
FTabSpawnerEntry& SpawnerEntry =
|
||||||
|
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(
|
||||||
|
RaceResultTabId,
|
||||||
|
FOnSpawnTab::CreateRaw(this, &FDTFluxRaceResult::OnSpawnTab)
|
||||||
|
)
|
||||||
|
.SetDisplayName(RaceResultTabDisplayName)
|
||||||
|
.SetTooltipText(FText::FromString(TEXT("Race Result Control Panel")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SDockTab> DTFLUXRACERESULT_API FDTFluxRaceResult::OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
SNew(
|
||||||
|
SDockTab
|
||||||
|
)
|
||||||
|
.TabRole(ETabRole::NomadTab)
|
||||||
|
// .ShouldAutosize(true)
|
||||||
|
[
|
||||||
|
SNew(SDTFluxRaceResultWidget)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
void DTFLUXRACERESULT_API FDTFluxRaceResult::ShutdownModule()
|
||||||
|
{
|
||||||
|
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(RaceResultTabId);
|
||||||
|
FDTFluxRaceResultStyle::UnregisterStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// // Dans votre code de debug
|
||||||
|
// void DTFLUXRACERESULT_API FDTFluxRaceResult::DebugMenus()
|
||||||
|
// {
|
||||||
|
// UToolMenus* ToolMenus = UToolMenus::Get();
|
||||||
|
// if (ToolMenus)
|
||||||
|
// {
|
||||||
|
// TArray<FName> MenuNames;
|
||||||
|
// ToolMenus->GetAllMenuNames(MenuNames);
|
||||||
|
//
|
||||||
|
// UE_LOG(logDTFluxRaceResult, Warning, TEXT("=== ALL AVAILABLE MENUS ==="));
|
||||||
|
// for (const FName& MenuName : MenuNames)
|
||||||
|
// {
|
||||||
|
// UE_LOG(logDTFluxRaceResult, Warning, TEXT("Menu: %s"), *MenuName.ToString());
|
||||||
|
//
|
||||||
|
// // Obtenir les sections de chaque menu
|
||||||
|
// UToolMenu* Menu = ToolMenus->FindMenu(MenuName);
|
||||||
|
// if (Menu)
|
||||||
|
// {
|
||||||
|
// for (const FToolMenuSection& Section : Menu->Sections)
|
||||||
|
// {
|
||||||
|
// UE_LOG(logDTFluxRaceResult, Warning, TEXT(" Section: %s"), *Section.Name.ToString());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// UE_LOG(logDTFluxRaceResult, Warning, TEXT("=== END MENU LIST ==="));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// void DTFLUXRACERESULT_API FDTFluxRaceResult::AddMenu(FMenuBarBuilder& MenuBarBuilder)
|
||||||
|
// {
|
||||||
|
// MenuBarBuilder.AddPullDownMenu(
|
||||||
|
// FText::FromString("DTFlux"),
|
||||||
|
// FText::FromString("DTFlux API Tools"),
|
||||||
|
// FNewMenuDelegate::CreateRaw(this, &FDTFluxRaceResult::FillMenu)
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void DTFLUXRACERESULT_API FDTFluxRaceResult::FillMenu(FMenuBuilder& MenuBuilder)
|
||||||
|
// {
|
||||||
|
// MenuBuilder.AddMenuEntry(
|
||||||
|
// FText::FromString("RaceResult ControlPanel"),
|
||||||
|
// FText::FromString("Launch RaceResult Control Panel"),
|
||||||
|
// FSlateIcon(FDTFluxRaceResultStyle::GetStyleSetName(), "LevelEditor.Tab.IconRaceResult"),
|
||||||
|
// FExecuteAction::CreateRaw(this, &FDTFluxRaceResult::OnButtonClicked)
|
||||||
|
// );
|
||||||
|
// MenuBuilder.EndSection();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
#undef LOCTEXT_NAMESPACE
|
||||||
|
|
||||||
|
IMPLEMENT_MODULE(FDTFluxRaceResult, DTFluxRaceResult)
|
||||||
@ -0,0 +1,283 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Widget/SDTFluxRaceResultWidget.h"
|
||||||
|
|
||||||
|
#include "DTFluxRaceResultModule.h"
|
||||||
|
#include "HttpModule.h"
|
||||||
|
#include "IWebBrowserCookieManager.h"
|
||||||
|
#include "IWebBrowserWindow.h"
|
||||||
|
#include "SlateOptMacros.h"
|
||||||
|
#include "WebBrowserModule.h"
|
||||||
|
#include "Interfaces/IHttpRequest.h"
|
||||||
|
#include "Interfaces/IHttpResponse.h"
|
||||||
|
|
||||||
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::Construct(const FArguments& InArgs)
|
||||||
|
{
|
||||||
|
|
||||||
|
FWebBrowserInitSettings browserInitSettings = FWebBrowserInitSettings();
|
||||||
|
|
||||||
|
IWebBrowserModule::Get().CustomInitialize(browserInitSettings);
|
||||||
|
WindowSettings.InitialURL = TEXT("about:blank");
|
||||||
|
WindowSettings.BrowserFrameRate = 25;
|
||||||
|
|
||||||
|
if (IWebBrowserModule::IsAvailable() && IWebBrowserModule::Get().IsWebModuleAvailable())
|
||||||
|
{
|
||||||
|
IWebBrowserSingleton* WebBrowserSingleton = IWebBrowserModule::Get().GetSingleton();
|
||||||
|
Browser = WebBrowserSingleton->CreateBrowserWindow(WindowSettings);
|
||||||
|
// Browser->OnLoadUrl().BindRaw(this, &SDTFluxRaceResultWidget::OnLoadOverride);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ChildSlot
|
||||||
|
[
|
||||||
|
SNew(SBox)
|
||||||
|
.Padding(5.0f)
|
||||||
|
[
|
||||||
|
SNew(SBorder)
|
||||||
|
.BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder"))
|
||||||
|
[
|
||||||
|
SNew(SVerticalBox)
|
||||||
|
+ SVerticalBox::Slot()
|
||||||
|
[
|
||||||
|
SAssignNew(WebBrowser, SWebBrowser, Browser)
|
||||||
|
.ShowControls(true)
|
||||||
|
.SupportsTransparency(true)
|
||||||
|
.OnUrlChanged(this, &SDTFluxRaceResultWidget::OnUrlChanged)
|
||||||
|
.OnBeforeNavigation(this, &SDTFluxRaceResultWidget::OnBeforeNavigation)
|
||||||
|
.OnLoadCompleted(FSimpleDelegate::CreateRaw(this, &SDTFluxRaceResultWidget::OnLoadCompleted))
|
||||||
|
.OnLoadError(FSimpleDelegate::CreateRaw(this, &SDTFluxRaceResultWidget::OnLoadError))
|
||||||
|
.OnLoadStarted(FSimpleDelegate::CreateRaw(this, &SDTFluxRaceResultWidget::OnLoadStarted))
|
||||||
|
.ShowErrorMessage(true)
|
||||||
|
.ShowAddressBar(true)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnCookieSet(bool bSuccess)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::LoadContentViaHTTP()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Loading initial content via HTTP: %s"), *RaceResultUrl);
|
||||||
|
LoadSpecificURL(RaceResultUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::LoadSpecificURL(const FString& Url)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Loading via HTTP: %s"), *Url);
|
||||||
|
|
||||||
|
// Créer la requête HTTP
|
||||||
|
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
|
||||||
|
|
||||||
|
// Ajouter l'authentification Basic pour TOUTES les requêtes
|
||||||
|
FString Credentials = Username + TEXT(":") + Password;
|
||||||
|
FString EncodedCredentials = FBase64::Encode(Credentials);
|
||||||
|
|
||||||
|
Request->SetURL(Url);
|
||||||
|
Request->SetVerb("GET");
|
||||||
|
Request->SetHeader("Authorization", "Basic " + EncodedCredentials);
|
||||||
|
Request->SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
|
||||||
|
Request->SetHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
|
||||||
|
Request->SetHeader("Accept-Language", "en-US,en;q=0.5");
|
||||||
|
Request->SetHeader("Accept-Encoding", "gzip, deflate");
|
||||||
|
Request->SetHeader("Connection", "keep-alive");
|
||||||
|
|
||||||
|
Request->OnProcessRequestComplete().BindRaw(this, &SDTFluxRaceResultWidget::OnHTTPContentLoaded);
|
||||||
|
Request->ProcessRequest();
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("HTTP request sent with Basic Auth"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnHTTPContentLoaded(TSharedPtr<IHttpRequest> Request,
|
||||||
|
TSharedPtr<IHttpResponse> HttpResponse, bool bWasSuccessful)
|
||||||
|
{
|
||||||
|
if (bWasSuccessful && HttpResponse.IsValid())
|
||||||
|
{
|
||||||
|
int32 ResponseCode = HttpResponse->GetResponseCode();
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("HTTP Response Code: %d"), ResponseCode);
|
||||||
|
|
||||||
|
if (ResponseCode == 200)
|
||||||
|
{
|
||||||
|
FString Content = HttpResponse->GetContentAsString();
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Content loaded successfully, size: %d characters"), Content.Len());
|
||||||
|
|
||||||
|
// Traiter le contenu HTML
|
||||||
|
FString ProcessedContent = ProcessHTMLContent(Content, Request->GetURL());
|
||||||
|
|
||||||
|
// Charger le contenu dans le browser via LoadString
|
||||||
|
if (Browser.IsValid())
|
||||||
|
{
|
||||||
|
Browser->LoadString(ProcessedContent, Request->GetURL());
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Content loaded into browser"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ResponseCode == 401)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Error, TEXT("Authentication failed - 401 Unauthorized. Check your credentials."));
|
||||||
|
}
|
||||||
|
else if (ResponseCode == 403)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Error, TEXT("Access forbidden - 403 Forbidden"));
|
||||||
|
}
|
||||||
|
else if (ResponseCode == 404)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Error, TEXT("Page not found - 404 Not Found"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Error, TEXT("HTTP request failed with code: %d"), ResponseCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Error, TEXT("HTTP request failed completely"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnLoadOverride()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnUrlChanged(const FText& NewUrl)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("URL changed to: %s"), *NewUrl.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
FString SDTFluxRaceResultWidget::ProcessHTMLContent(const FString& Content, const FString& BaseUrl)
|
||||||
|
{
|
||||||
|
FString ProcessedContent = Content;
|
||||||
|
|
||||||
|
// Extraire le domaine de base
|
||||||
|
FString BaseDomain = ExtractDomain(BaseUrl);
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Processing HTML content, base domain: %s"), *BaseDomain);
|
||||||
|
|
||||||
|
// Convertir tous les liens relatifs en liens absolus
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("src=\"/"), *FString::Printf(TEXT("src=\"%s/"), *BaseDomain));
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("href=\"/"), *FString::Printf(TEXT("href=\"%s/"), *BaseDomain));
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("src='/"), *FString::Printf(TEXT("src='%s/"), *BaseDomain));
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("href='/"), *FString::Printf(TEXT("href='%s/"), *BaseDomain));
|
||||||
|
|
||||||
|
// Gérer les liens relatifs avec ./
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("src=\"./"), *FString::Printf(TEXT("src=\"%s/"), *BaseDomain));
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("href=\"./"), *FString::Printf(TEXT("href=\"%s/"), *BaseDomain));
|
||||||
|
|
||||||
|
// Ajouter une balise base pour aider avec les liens relatifs restants
|
||||||
|
FString BaseTag = FString::Printf(TEXT("<base href=\"%s/\">"), *BaseDomain);
|
||||||
|
if (ProcessedContent.Contains(TEXT("<head>")))
|
||||||
|
{
|
||||||
|
FString HeadReplacement = TEXT("<head>") + BaseTag;
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("<head>"), *HeadReplacement);
|
||||||
|
}
|
||||||
|
else if (ProcessedContent.Contains(TEXT("<HEAD>")))
|
||||||
|
{
|
||||||
|
FString HeadReplacement = TEXT("<HEAD>") + BaseTag;
|
||||||
|
ProcessedContent = ProcessedContent.Replace(TEXT("<HEAD>"), *HeadReplacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("HTML content processed"));
|
||||||
|
return ProcessedContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FString SDTFluxRaceResultWidget::ExtractDomain(const FString& Url)
|
||||||
|
{
|
||||||
|
FString Domain = Url;
|
||||||
|
|
||||||
|
// Enlever le protocole
|
||||||
|
if (Url.StartsWith(TEXT("https://")))
|
||||||
|
{
|
||||||
|
Domain = Url.Mid(8);
|
||||||
|
}
|
||||||
|
else if (Url.StartsWith(TEXT("http://")))
|
||||||
|
{
|
||||||
|
Domain = Url.Mid(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trouver le premier slash (début du path)
|
||||||
|
int32 SlashIndex;
|
||||||
|
if (Domain.FindChar(TEXT('/'), SlashIndex))
|
||||||
|
{
|
||||||
|
Domain = Url.Left(Url.Len() - (Domain.Len() - SlashIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enlever le port si présent
|
||||||
|
FString DomainPart = Domain;
|
||||||
|
if (Domain.StartsWith(TEXT("https://")))
|
||||||
|
{
|
||||||
|
DomainPart = Domain.Mid(8);
|
||||||
|
}
|
||||||
|
else if (Domain.StartsWith(TEXT("http://")))
|
||||||
|
{
|
||||||
|
DomainPart = Domain.Mid(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 ColonIndex;
|
||||||
|
if (DomainPart.FindChar(TEXT(':'), ColonIndex))
|
||||||
|
{
|
||||||
|
Domain = Domain.Left(Domain.Len() - (DomainPart.Len() - ColonIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDTFluxRaceResultWidget::OnBeforeNavigation(const FString& Url, const FWebNavigationRequest& Request)
|
||||||
|
{
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Loading via HTTP: %s"), *Url);
|
||||||
|
|
||||||
|
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HTTPRequest = FHttpModule::Get().CreateRequest();
|
||||||
|
|
||||||
|
// Authentification pour toutes les requêtes
|
||||||
|
FString Credentials = Username + TEXT(":") + Password;
|
||||||
|
FString EncodedCredentials = FBase64::Encode(Credentials);
|
||||||
|
|
||||||
|
HTTPRequest->SetURL(RaceResultUrl);
|
||||||
|
HTTPRequest->SetVerb("GET");
|
||||||
|
HTTPRequest->SetHeader("Authorization", "Basic " + EncodedCredentials);
|
||||||
|
HTTPRequest->SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
|
||||||
|
HTTPRequest->SetHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||||
|
|
||||||
|
HTTPRequest->OnProcessRequestComplete().BindRaw(this, &SDTFluxRaceResultWidget::OnHTTPContentLoaded);
|
||||||
|
HTTPRequest->ProcessRequest();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnLoadCompleted()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Load Completed"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnLoadStarted()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Load Started"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnLoadError()
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxRaceResult, Log, TEXT("Load Error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::OnBeforeResourceLoad(FString Url, FString ResourceType, FContextRequestHeaders& AdditionalHeaders, const bool AllowUserCredentials)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDTFluxRaceResultWidget::SetupBasicAuth()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
|
#include "Widget/Style/DTFluxRaceResultStyle.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "Interfaces/IPluginManager.h"
|
||||||
|
#include "Styling/SlateStyleRegistry.h"
|
||||||
|
#include "Styling/SlateStyleMacros.h"
|
||||||
|
|
||||||
|
#define RootToContentDir Style->RootToContentDir
|
||||||
|
|
||||||
|
TSharedPtr<ISlateStyle> FDTFluxRaceResultStyle::StyleSet = nullptr;
|
||||||
|
|
||||||
|
void FDTFluxRaceResultStyle::RegisterStyle()
|
||||||
|
{
|
||||||
|
if(StyleSet.IsValid()) return;
|
||||||
|
|
||||||
|
StyleSet = Create();
|
||||||
|
FSlateStyleRegistry::RegisterSlateStyle(*StyleSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxRaceResultStyle::UnregisterStyle()
|
||||||
|
{
|
||||||
|
if(StyleSet.IsValid())
|
||||||
|
{
|
||||||
|
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet);
|
||||||
|
ensure(StyleSet.IsUnique());
|
||||||
|
StyleSet.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxRaceResultStyle::ReloadTextures()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedPtr<ISlateStyle> FDTFluxRaceResultStyle::Create()
|
||||||
|
{
|
||||||
|
TSharedPtr<FSlateStyleSet> Style = MakeShareable(new FSlateStyleSet("DTFluxRaceResultStyle"));
|
||||||
|
Style->SetContentRoot(IPluginManager::Get().FindPlugin("DTFluxAPI")->GetBaseDir()/TEXT("Resources"));
|
||||||
|
|
||||||
|
Style->Set("LevelEditor.Tab.IconRaceResult", new IMAGE_BRUSH_SVG("DTFluxRaceResult16x16", FVector2d(16)) );
|
||||||
|
return Style;
|
||||||
|
}
|
||||||
|
|
||||||
33
Source/DTFluxRaceResult/Public/DTFluxRaceResultModule.h
Normal file
33
Source/DTFluxRaceResult/Public/DTFluxRaceResultModule.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Modules/ModuleManager.h"
|
||||||
|
|
||||||
|
DTFLUXRACERESULT_API DECLARE_LOG_CATEGORY_EXTERN(logDTFluxRaceResult, All, All);
|
||||||
|
|
||||||
|
class DTFLUXRACERESULT_API FDTFluxRaceResult : public IModuleInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void StartupModule() override;
|
||||||
|
virtual void ShutdownModule() override;
|
||||||
|
|
||||||
|
#pragma region MenuExtention
|
||||||
|
void RegisterMenuExtensions();
|
||||||
|
void InitMenuExtension();
|
||||||
|
void CreateSubmenu(UToolMenu* Menu);
|
||||||
|
|
||||||
|
// void AddMenu(FMenuBarBuilder& MenuBarBuilder);
|
||||||
|
// void FillMenu(FMenuBuilder& MenuBuilder);
|
||||||
|
void OnButtonClicked();
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region EditorTab
|
||||||
|
void RegisterRaceResultTab();
|
||||||
|
TSharedRef<SDockTab> OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs);
|
||||||
|
private:
|
||||||
|
// static void DebugMenus();
|
||||||
|
static FName RaceResultTabId;
|
||||||
|
static FText RaceResultTabDisplayName;
|
||||||
|
TSharedPtr<class SDTFluxRaceResultWidget> RaceResultWidget;
|
||||||
|
#pragma endregion
|
||||||
|
};
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "SWebBrowser.h"
|
||||||
|
#include "Widgets/SCompoundWidget.h"
|
||||||
|
|
||||||
|
class IHttpResponse;
|
||||||
|
class IHttpRequest;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DTFLUXRACERESULT_API SDTFluxRaceResultWidget : public SCompoundWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SLATE_BEGIN_ARGS(SDTFluxRaceResultWidget)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SLATE_END_ARGS()
|
||||||
|
|
||||||
|
/** Constructs this widget with InArgs */
|
||||||
|
void Construct(const FArguments& InArgs);
|
||||||
|
void OnCookieSet(bool bSuccess);
|
||||||
|
void LoadSpecificURL(const FString& Url);
|
||||||
|
void LoadContentViaHTTP();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TSharedPtr<SWebBrowser> WebBrowser;
|
||||||
|
TSharedPtr<IWebBrowserWindow> Browser;
|
||||||
|
TSharedPtr<IWebBrowserAdapter> BrowserAdapter;
|
||||||
|
FCreateBrowserWindowSettings WindowSettings;
|
||||||
|
|
||||||
|
void OnUrlChanged(const FText& NewUrl);
|
||||||
|
FString ProcessHTMLContent(const FString& Content, const FString& BaseUrl);
|
||||||
|
FString ExtractDomain(const FString& Url);
|
||||||
|
void OnHTTPContentLoaded(TSharedPtr<IHttpRequest> Request, TSharedPtr<IHttpResponse> HttpResponse, bool bWasSuccessful);
|
||||||
|
bool OnBeforeNavigation(const FString& Url, const FWebNavigationRequest& Request);
|
||||||
|
void OnLoadCompleted();
|
||||||
|
void OnLoadStarted();
|
||||||
|
void OnLoadError();
|
||||||
|
|
||||||
|
void OnLoadOverride();
|
||||||
|
|
||||||
|
void OnBeforeResourceLoad(FString Url, FString ResourceType, FContextRequestHeaders& AdditionalHeaders, const bool AllowUserCredentials);
|
||||||
|
|
||||||
|
FString RaceResultUrl = "https://raceresult.tds-france.com";
|
||||||
|
FString Username = "sporkrono";
|
||||||
|
FString Password = "Notre 3ème décennie d'action pour le climat";
|
||||||
|
|
||||||
|
|
||||||
|
void SetupBasicAuth();
|
||||||
|
};
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Styling/ISlateStyle.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DTFLUXRACERESULT_API FDTFluxRaceResultStyle
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void RegisterStyle();
|
||||||
|
static void UnregisterStyle();
|
||||||
|
|
||||||
|
static void ReloadTextures();
|
||||||
|
|
||||||
|
static const ISlateStyle& Get()
|
||||||
|
{
|
||||||
|
return *StyleSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const FName& GetStyleSetName()
|
||||||
|
{
|
||||||
|
return StyleSet->GetStyleSetName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static TSharedPtr<ISlateStyle> Create();
|
||||||
|
static TSharedPtr<ISlateStyle> StyleSet;
|
||||||
|
};
|
||||||
@ -20,7 +20,8 @@ public class DTFluxUtilities : ModuleRules
|
|||||||
"Engine",
|
"Engine",
|
||||||
"Slate",
|
"Slate",
|
||||||
"SlateCore",
|
"SlateCore",
|
||||||
"DTFluxCore"
|
"DTFluxCore",
|
||||||
|
"DTFluxCoreSubsystem",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,17 @@
|
|||||||
|
|
||||||
#include "FTDFluxUtils.h"
|
#include "FTDFluxUtils.h"
|
||||||
|
|
||||||
FText UFTDFluxUtils::GetFormatedName(FDTFluxParticipant& Participant, const int MaxChar, const FString OverFlowChar)
|
#include "DTFluxCoreSubsystem.h"
|
||||||
|
|
||||||
|
FText UFTDFluxUtils::GetFormatedName(const int& Bib, const int MaxChar, const FString OverFlowChar)
|
||||||
|
{
|
||||||
|
UDTFluxCoreSubsystem* CoreSubsystem = GEngine->GetEngineSubsystem<UDTFluxCoreSubsystem>();
|
||||||
|
const FDTFluxParticipant OutParticipant = CoreSubsystem->GetParticipant(Bib);
|
||||||
|
return OutParticipant.GetFormattedNameText(MaxChar, OverFlowChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
FText UFTDFluxUtils::GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar,
|
||||||
|
const FString OverFlowChar)
|
||||||
{
|
{
|
||||||
return Participant.GetFormattedNameText(MaxChar, OverFlowChar);
|
return Participant.GetFormattedNameText(MaxChar, OverFlowChar);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,9 @@ class DTFLUXUTILITIES_API UFTDFluxUtils : public UBlueprintFunctionLibrary
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
||||||
static FText GetFormatedName(FDTFluxParticipant& Participant, const int MaxChar = 10,
|
static FText GetFormatedName(const int& Bib, const int MaxChar = 10,
|
||||||
|
const FString OverFlowChar = "...");
|
||||||
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
||||||
|
static FText GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar = 10,
|
||||||
const FString OverFlowChar = "...");
|
const FString OverFlowChar = "...");
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user