Compare commits
6 Commits
d92ca63ea4
...
6a88fec83a
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a88fec83a | |||
| 700a7120f0 | |||
| f1d583926b | |||
| a08bcd98a4 | |||
| d7a189f905 | |||
| 0d851b7298 |
@ -100,6 +100,20 @@ void FDTFluxModelAssetCustomization::CustomizeDetailsWithoutRawDataAsset(IDetail
|
|||||||
|
|
||||||
void FDTFluxModelAssetCustomization::CustomizeDetailsWithRawDataAccess(IDetailLayoutBuilder& DetailBuilder)
|
void FDTFluxModelAssetCustomization::CustomizeDetailsWithRawDataAccess(IDetailLayoutBuilder& DetailBuilder)
|
||||||
{
|
{
|
||||||
|
// Edit object
|
||||||
|
TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
|
||||||
|
DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
|
||||||
|
|
||||||
|
if (ObjectsBeingCustomized.Num() > 0)
|
||||||
|
{
|
||||||
|
ModelAsset = Cast<UDTFluxModelAsset>(ObjectsBeingCustomized[0].Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ModelAsset.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Error, TEXT("No valid DTFluxModelAsset found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
// ===== WIDGET PRINCIPAL =====
|
// ===== WIDGET PRINCIPAL =====
|
||||||
IDetailCategoryBuilder& MainCategory = DetailBuilder.EditCategory(
|
IDetailCategoryBuilder& MainCategory = DetailBuilder.EditCategory(
|
||||||
"DTFlux Model Explorer",
|
"DTFlux Model Explorer",
|
||||||
|
|||||||
@ -1,3 +1,67 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
||||||
|
|
||||||
|
bool FDTFluxContest::IsFinished() const
|
||||||
|
{
|
||||||
|
return EndTime <= FDateTime::Now();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxContest::UpdateEndTime()
|
||||||
|
{
|
||||||
|
TArray<FDTFluxStage> TempStages = Stages;
|
||||||
|
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
||||||
|
{
|
||||||
|
return A.EndTime < B.EndTime;
|
||||||
|
});
|
||||||
|
EndTime = TempStages.Last().EndTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FDTFluxContest::GetLastStageId()
|
||||||
|
{
|
||||||
|
if (LastStageId <= 0)
|
||||||
|
{
|
||||||
|
UpdateLastStageId();
|
||||||
|
}
|
||||||
|
return LastStageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxContest::UpdateLastStageId()
|
||||||
|
{
|
||||||
|
TArray<FDTFluxStage> TempStages = Stages;
|
||||||
|
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
||||||
|
{
|
||||||
|
return A.StageId < B.StageId;
|
||||||
|
});
|
||||||
|
LastStageId = TempStages.Last().StageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
FDTFluxStage& FDTFluxContest::GetLastStage() const
|
||||||
|
{
|
||||||
|
TArray<FDTFluxStage> TempStages = Stages;
|
||||||
|
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
||||||
|
{
|
||||||
|
return A.StageId < B.StageId;
|
||||||
|
});
|
||||||
|
return TempStages.Last();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FDTFluxContest::GetStage(const int StageID, FDTFluxStage& OutStage) const
|
||||||
|
{
|
||||||
|
if (Stages.Num() == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const FDTFluxStage& Stage : Stages)
|
||||||
|
{
|
||||||
|
if (Stage.StageId == StageID)
|
||||||
|
{
|
||||||
|
OutStage = Stage;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,56 +1,212 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
#include "Types/Struct/DTFluxTeamListStruct.h"
|
#include "Types/Struct/DTFluxTeamListStruct.h"
|
||||||
|
|
||||||
#include "DTFluxCoreModule.h"
|
#include "DTFluxCoreModule.h"
|
||||||
|
#include "Dom/JsonObject.h"
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
// FDTFluxPerson Implementation
|
||||||
|
// ===================================
|
||||||
|
|
||||||
|
bool FDTFluxPerson::operator==(const FDTFluxPerson& Right) const
|
||||||
|
{
|
||||||
|
return GetNormalizedString() == Right.GetNormalizedString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FDTFluxPerson::operator!=(const FDTFluxPerson& Right) const
|
||||||
|
{
|
||||||
|
return !(*this == Right);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FDTFluxPerson::operator==(const int Length) const
|
||||||
|
{
|
||||||
|
return GetNormalizedString().Len() == Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FDTFluxPerson::operator!=(const int Length) const
|
||||||
|
{
|
||||||
|
return !(*this == Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString FDTFluxPerson::GetNormalizedString() const
|
||||||
|
{
|
||||||
|
return FirstName.ToLower() + LastName.ToLower() + Gender.ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FDTFluxPerson::IsValid() const
|
||||||
|
{
|
||||||
|
return !FirstName.TrimStartAndEnd().IsEmpty() &&
|
||||||
|
!LastName.TrimStartAndEnd().IsEmpty() &&
|
||||||
|
!Gender.TrimStartAndEnd().IsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
FDTFluxParticipant::FDTFluxParticipant()
|
||||||
|
: Bib(-1)
|
||||||
|
, ContestId(-1)
|
||||||
|
, Elite(false)
|
||||||
|
, Status(static_cast<EDTFluxParticipantStatusType>(0))
|
||||||
|
, bIsMassStartParticipant(false)
|
||||||
|
, CurrentSplit(-1)
|
||||||
|
{
|
||||||
|
Teammate.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
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")))
|
||||||
|
, CurrentSplit(-1)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCore, Log, TEXT("Creating participant from JSON - Bib: %d, Contest: %d"), Bib, ContestId);
|
||||||
|
|
||||||
|
for (uint8 Index = 1; Index <= 10; Index++)
|
||||||
|
{
|
||||||
|
FString FirstNameKey = Index == 1 ? TEXT("firstName") : FString::Printf(TEXT("firstName%d"), Index);
|
||||||
|
FString LastNameKey = Index == 1 ? TEXT("lastName") : FString::Printf(TEXT("lastName%d"), Index);
|
||||||
|
FString GenderKey = Index == 1 ? TEXT("gender") : FString::Printf(TEXT("gender%d"), Index);
|
||||||
|
|
||||||
|
// Vérifie si au moins un des champs existe
|
||||||
|
if (!JsonObject->HasField(FirstNameKey) && !JsonObject->HasField(LastNameKey) && !JsonObject->HasField(GenderKey))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FString FirstName = JsonObject->GetStringField(FirstNameKey);
|
||||||
|
const FString LastName = JsonObject->GetStringField(LastNameKey);
|
||||||
|
const FString Gender = JsonObject->GetStringField(GenderKey);
|
||||||
|
|
||||||
|
if (FirstName.TrimStartAndEnd().IsEmpty() && LastName.TrimStartAndEnd().IsEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
FDTFluxPerson Person;
|
||||||
|
Person.FirstName = FirstName.TrimStartAndEnd();
|
||||||
|
Person.LastName = LastName.TrimStartAndEnd();
|
||||||
|
Person.Gender = Gender.TrimStartAndEnd();
|
||||||
|
|
||||||
|
if (Person.IsValid())
|
||||||
|
{
|
||||||
|
Teammate.Add(Person);
|
||||||
|
UE_LOG(logDTFluxCore, Verbose, TEXT("Added person %d: %s %s (%s)"),
|
||||||
|
Index, *Person.FirstName, *Person.LastName, *Person.Gender);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCore, Warning, TEXT("Invalid person data at index %d: '%s' '%s' '%s'"),
|
||||||
|
Index, *FirstName, *LastName, *Gender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxCore, Log, TEXT("Participant created with %d teammates"), Teammate.Num());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FDTFluxParticipant::IsDefault() const
|
||||||
|
{
|
||||||
|
return Bib == -1
|
||||||
|
&& ContestId == -1
|
||||||
|
&& Category.IsEmpty()
|
||||||
|
&& Club.IsEmpty()
|
||||||
|
&& !Elite
|
||||||
|
&& Status == static_cast<EDTFluxParticipantStatusType>(0)
|
||||||
|
&& Team.IsEmpty()
|
||||||
|
&& !bIsMassStartParticipant
|
||||||
|
&& CurrentSplit == -1
|
||||||
|
&& Teammate.IsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
void FDTFluxParticipant::AddTeammate(const FDTFluxPerson& Person)
|
void FDTFluxParticipant::AddTeammate(const FDTFluxPerson& Person)
|
||||||
{
|
{
|
||||||
Teammate.Add(Person);
|
if (Person.IsValid())
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxParticipant::AddTeammate(const FString LastName, const FString FirstName, const FString Gender)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString OverflowChars) const
|
|
||||||
{
|
|
||||||
{
|
{
|
||||||
|
Teammate.Add(Person);
|
||||||
|
UE_LOG(logDTFluxCore, Verbose, TEXT("Added teammate: %s %s"), *Person.FirstName, *Person.LastName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCore, Warning, TEXT("Cannot add invalid teammate: %s %s"), *Person.FirstName, *Person.LastName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FDTFluxParticipant::AddTeammate(const FString& LastName, const FString& FirstName, const FString& Gender)
|
||||||
|
{
|
||||||
|
FDTFluxPerson Person;
|
||||||
|
Person.FirstName = FirstName.TrimStartAndEnd();
|
||||||
|
Person.LastName = LastName.TrimStartAndEnd();
|
||||||
|
Person.Gender = Gender.TrimStartAndEnd();
|
||||||
|
|
||||||
|
AddTeammate(Person);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FDTFluxParticipant::GetTeammateNum() const
|
||||||
|
{
|
||||||
|
return Teammate.Num();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FDTFluxParticipant::IsTeam() const
|
||||||
|
{
|
||||||
|
return Teammate.Num() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TArray<FDTFluxPerson>& FDTFluxParticipant::GetTeammate() const
|
||||||
|
{
|
||||||
|
return Teammate;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString& Separator, const FString& OverflowChar) const
|
||||||
|
{
|
||||||
if (MaxChar <= 0)
|
if (MaxChar <= 0)
|
||||||
{
|
{
|
||||||
return "";
|
return TEXT("");
|
||||||
}
|
}
|
||||||
|
|
||||||
FString FirstName;
|
FString FirstName;
|
||||||
FString LastName;
|
FString LastName;
|
||||||
|
|
||||||
if (IsTeam())
|
if (IsTeam())
|
||||||
|
{
|
||||||
|
if (!Team.IsEmpty())
|
||||||
{
|
{
|
||||||
LastName = Team;
|
LastName = Team;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
TArray<FString> Names;
|
||||||
|
for (const FDTFluxPerson& Person : Teammate)
|
||||||
|
{
|
||||||
|
Names.Add(Person.LastName);
|
||||||
|
}
|
||||||
|
LastName = FString::Join(Names, TEXT("/"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Teammate.Num() > 0)
|
||||||
{
|
{
|
||||||
FirstName = Teammate[0].FirstName;
|
FirstName = Teammate[0].FirstName;
|
||||||
LastName = Teammate[0].LastName;
|
LastName = Teammate[0].LastName;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LastName = TEXT("Unknown");
|
||||||
|
}
|
||||||
FString Initial;
|
FString Initial;
|
||||||
if (!FirstName.IsEmpty())
|
if (!FirstName.IsEmpty())
|
||||||
{
|
{
|
||||||
Initial = FirstName.Left(1).ToUpper() + " ";
|
Initial = FirstName.Left(1).ToUpper() + Separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
FString FormattedLastName = LastName.ToUpper();
|
FString FormattedLastName = LastName.ToUpper();
|
||||||
|
|
||||||
FString FullName = Initial + FormattedLastName;
|
FString FullName = Initial + FormattedLastName;
|
||||||
UE_LOG(logDTFluxCore, Error, TEXT("FullName for Bib %i is %s"), Bib, *FullName);
|
|
||||||
|
|
||||||
if (FullName.Len() <= MaxChar)
|
if (FullName.Len() <= MaxChar)
|
||||||
{
|
{
|
||||||
return FullName;
|
return FullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int32 OverflowLength = OverflowChars.Len();
|
const int32 OverflowLength = OverflowChar.Len();
|
||||||
|
|
||||||
if (OverflowLength > MaxChar)
|
if (OverflowLength > MaxChar)
|
||||||
{
|
{
|
||||||
@ -69,7 +225,7 @@ FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString Ov
|
|||||||
return FullName.Left(MaxChar);
|
return FullName.Left(MaxChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
FString TruncatedName = Initial + FormattedLastName.Left(AvailableForLastName) + OverflowChars;
|
FString TruncatedName = Initial + FormattedLastName.Left(AvailableForLastName) + OverflowChar;
|
||||||
|
|
||||||
if (TruncatedName.Len() > MaxChar)
|
if (TruncatedName.Len() > MaxChar)
|
||||||
{
|
{
|
||||||
@ -77,70 +233,73 @@ FString FDTFluxParticipant::GetFormattedName(const int MaxChar, const FString Ov
|
|||||||
}
|
}
|
||||||
|
|
||||||
return TruncatedName;
|
return TruncatedName;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FString FDTFluxParticipant::GetConcatFormattedName(const int MaxChar, const FString OverflowChar) const
|
FString FDTFluxParticipant::GetConcatFormattedName(const int MaxChar, const FString& Separator,
|
||||||
|
const FString& OverflowChar, const FString& BibSeparator) const
|
||||||
{
|
{
|
||||||
FString BibText = FString::FromInt(Bib) + " ";
|
FString BibText = FString::FromInt(Bib) + BibSeparator;
|
||||||
FString FormattedName = GetFormattedName(MaxChar - BibText.Len(), OverflowChar);
|
int32 RemainingChars = MaxChar - BibText.Len();
|
||||||
|
|
||||||
|
if (RemainingChars <= 0)
|
||||||
|
{
|
||||||
|
return BibText.Left(MaxChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString FormattedName = GetFormattedName(RemainingChars, Separator, OverflowChar);
|
||||||
return BibText + FormattedName;
|
return BibText + FormattedName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructeur privé depuis JSON
|
FText FDTFluxParticipant::GetFormattedNameText(const int MaxChar, const FString& Separator, const FString& OverflowChar) const
|
||||||
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)
|
|
||||||
, CurrentSplit(-1)
|
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCore, Error, TEXT("Ctor with JSON Object"))
|
return FText::FromString(GetFormattedName(MaxChar, Separator, OverflowChar));
|
||||||
for (uint8 Index = 1; ; Index++)
|
}
|
||||||
{
|
|
||||||
FString FirstNameKey = Index == 1 ? "firstName" : FString::Printf(TEXT("firstName%i"), Index);
|
FText FDTFluxParticipant::GetConcatFormattedNameText(const int MaxChar, const FString& Separator,
|
||||||
FString LastNameKey = Index == 1 ? "lastName" : FString::Printf(TEXT("lastName%i"), Index);
|
const FString& OverflowChar, const FString& BibSeparator) const
|
||||||
FString GenderKey = Index == 1 ? "gender" : FString::Printf(TEXT("gender%i"), Index);
|
{
|
||||||
// max 10 Persons
|
return FText::FromString(GetConcatFormattedName(MaxChar, Separator, OverflowChar, BibSeparator));
|
||||||
if (Index >= 10)
|
}
|
||||||
{
|
|
||||||
break;
|
FString FDTFluxParticipant::GetFormattedName(const FDTFluxParticipant& Participant, const int MaxChar,
|
||||||
}
|
const FString& Separator, const FString& OverflowChar)
|
||||||
if (!JsonObject->HasField(FirstNameKey) && !JsonObject->HasField(LastNameKey)
|
{
|
||||||
&& !JsonObject->HasField(GenderKey))
|
return Participant.GetFormattedName(MaxChar, Separator, OverflowChar);
|
||||||
{
|
}
|
||||||
UE_LOG(logDTFluxCore, Error, TEXT("No Corresponding Field!!!"))
|
|
||||||
break;
|
FString FDTFluxParticipant::GetConcatFormattedName(const FDTFluxParticipant& Participant, const int MaxChar,
|
||||||
}
|
const FString& Separator, const FString& OverflowChar,
|
||||||
const FString FirstName = JsonObject->GetStringField(FirstNameKey);
|
const FString& BibSeparator)
|
||||||
const FString LastName = JsonObject->GetStringField(LastNameKey);
|
{
|
||||||
const FString Gender = JsonObject->GetStringField(GenderKey);
|
return Participant.GetConcatFormattedName(MaxChar, Separator, OverflowChar, BibSeparator);
|
||||||
if (FirstName.IsEmpty() && LastName.IsEmpty())
|
}
|
||||||
continue;
|
|
||||||
FDTFluxPerson Person;
|
FText FDTFluxParticipant::GetFormattedNameText(const FDTFluxParticipant& Participant, const int MaxChar,
|
||||||
Person.FirstName = FirstName;
|
const FString& Separator, const FString& OverflowChar)
|
||||||
Person.LastName = LastName;
|
{
|
||||||
Person.Gender = Gender;
|
return Participant.GetFormattedNameText(MaxChar, Separator, OverflowChar);
|
||||||
Teammate.Add(Person);
|
}
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxCore, Error, TEXT("Ctor with JSON Object Teammate is now %i long"), Teammate.Num());
|
FText FDTFluxParticipant::GetConcatFormattedNameText(const FDTFluxParticipant& Participant, const int MaxChar,
|
||||||
|
const FString& Separator, const FString& OverflowChar,
|
||||||
|
const FString& BibSeparator)
|
||||||
|
{
|
||||||
|
return Participant.GetConcatFormattedNameText(MaxChar, Separator, OverflowChar, BibSeparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
FDTFluxParticipant FDTFluxParticipant::CreateFromJson(const TSharedPtr<FJsonObject>& JsonObject)
|
FDTFluxParticipant FDTFluxParticipant::CreateFromJson(const TSharedPtr<FJsonObject>& JsonObject)
|
||||||
{
|
{
|
||||||
|
if (!JsonObject.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCore, Error, TEXT("Cannot create participant from invalid JSON object"));
|
||||||
|
return FDTFluxParticipant();
|
||||||
|
}
|
||||||
|
|
||||||
return FDTFluxParticipant(JsonObject);
|
return FDTFluxParticipant(JsonObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FDTFluxParticipant::GetTeammateNum() const
|
FDTFluxTeamStatusUpdate::FDTFluxTeamStatusUpdate(const int InBib, const int InStatus)
|
||||||
|
: Bib(InBib)
|
||||||
|
, Status(static_cast<EDTFluxParticipantStatusType>(InStatus))
|
||||||
{
|
{
|
||||||
return Teammate.Num();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FDTFluxParticipant::IsTeam() const
|
|
||||||
{
|
|
||||||
return Teammate.Num() < 1;
|
|
||||||
}
|
}
|
||||||
@ -89,68 +89,6 @@ public:
|
|||||||
bool GetStage(const int StageID, FDTFluxStage& OutStage) const;
|
bool GetStage(const int StageID, FDTFluxStage& OutStage) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool FDTFluxContest::IsFinished() const
|
|
||||||
{
|
|
||||||
return EndTime <= FDateTime::Now();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void FDTFluxContest::UpdateEndTime()
|
|
||||||
{
|
|
||||||
TArray<FDTFluxStage> TempStages = Stages;
|
|
||||||
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
|
||||||
{
|
|
||||||
return A.EndTime < B.EndTime;
|
|
||||||
});
|
|
||||||
EndTime = TempStages.Last().EndTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int FDTFluxContest::GetLastStageId()
|
|
||||||
{
|
|
||||||
if (LastStageId <= 0)
|
|
||||||
{
|
|
||||||
UpdateLastStageId();
|
|
||||||
}
|
|
||||||
return LastStageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void FDTFluxContest::UpdateLastStageId()
|
|
||||||
{
|
|
||||||
TArray<FDTFluxStage> TempStages = Stages;
|
|
||||||
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
|
||||||
{
|
|
||||||
return A.StageId < B.StageId;
|
|
||||||
});
|
|
||||||
LastStageId = TempStages.Last().StageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline FDTFluxStage& FDTFluxContest::GetLastStage() const
|
|
||||||
{
|
|
||||||
TArray<FDTFluxStage> TempStages = Stages;
|
|
||||||
TempStages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
|
|
||||||
{
|
|
||||||
return A.StageId < B.StageId;
|
|
||||||
});
|
|
||||||
return TempStages.Last();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool FDTFluxContest::GetStage(const int StageID, FDTFluxStage& OutStage) const
|
|
||||||
{
|
|
||||||
if (Stages.Num() == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const FDTFluxStage& Stage : Stages)
|
|
||||||
{
|
|
||||||
if (Stage.StageId == StageID)
|
|
||||||
{
|
|
||||||
OutStage = Stage;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
USTRUCT()
|
USTRUCT()
|
||||||
struct DTFLUXCORE_API FDTFluxRaceData
|
struct DTFLUXCORE_API FDTFluxRaceData
|
||||||
{
|
{
|
||||||
|
|||||||
@ -165,6 +165,7 @@ struct FDTFluxStageRankings : public FDTFluxDetailedRankings
|
|||||||
Ranking.TimeStart.ParseIntoArray(Exploded, TEXT(":"), true);
|
Ranking.TimeStart.ParseIntoArray(Exploded, TEXT(":"), true);
|
||||||
if (Exploded.Num() == 3)
|
if (Exploded.Num() == 3)
|
||||||
{
|
{
|
||||||
|
//TODO: Pas sur que ce soit super de le mettre à ce jour ???
|
||||||
FDateTime Now = FDateTime::Now();
|
FDateTime Now = FDateTime::Now();
|
||||||
RankingStartTime = FDateTime(Now.GetYear(), Now.GetMonth(), Now.GetDay(),
|
RankingStartTime = FDateTime(Now.GetYear(), Now.GetMonth(), Now.GetDay(),
|
||||||
FCString::Atoi(*Exploded[0]), FCString::Atoi(*Exploded[1]),
|
FCString::Atoi(*Exploded[0]), FCString::Atoi(*Exploded[1]),
|
||||||
|
|||||||
@ -7,6 +7,10 @@
|
|||||||
#include "Types/Enum/DTFluxModelEnums.h"
|
#include "Types/Enum/DTFluxModelEnums.h"
|
||||||
#include "DTFluxTeamListStruct.generated.h"
|
#include "DTFluxTeamListStruct.generated.h"
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
class UDTFluxModelAsset;
|
||||||
|
class UDTFluxParticipantFactory;
|
||||||
|
|
||||||
USTRUCT()
|
USTRUCT()
|
||||||
struct DTFLUXCORE_API FDTFluxTeamListItemDefinition
|
struct DTFLUXCORE_API FDTFluxTeamListItemDefinition
|
||||||
{
|
{
|
||||||
@ -15,76 +19,78 @@ struct DTFLUXCORE_API FDTFluxTeamListItemDefinition
|
|||||||
public:
|
public:
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Type = "team-list-item";
|
FString Type = "team-list-item";
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int ContestId;
|
int ContestId = 0;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int Bib;
|
int Bib = 0;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString FirstName;
|
FString FirstName;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString LastName;
|
FString LastName;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString FirstName2 = "";
|
FString FirstName2 = "";
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString LastName2 = "";
|
FString LastName2 = "";
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Team = "";
|
FString Team = "";
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Gender;
|
FString Gender;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Gender2;
|
FString Gender2;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
bool Elite;
|
bool Elite = false;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Category;
|
FString Category;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int Status;
|
int Status = 0;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString Club;
|
FString Club;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
||||||
struct DTFLUXCORE_API FDTFluxPerson
|
struct DTFLUXCORE_API FDTFluxPerson
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
||||||
FString FirstName;
|
FString FirstName;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
||||||
FString LastName;
|
FString LastName;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
||||||
FString Gender;
|
FString Gender;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
||||||
FString FunctionLine1 = TEXT("");
|
FString FunctionLine1 = TEXT("");
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
||||||
FString FunctionLine2 = TEXT("");
|
FString FunctionLine2 = TEXT("");
|
||||||
|
|
||||||
bool operator==(const FDTFluxPerson& Right) const
|
bool operator==(const FDTFluxPerson& Right) const;
|
||||||
{
|
bool operator!=(const FDTFluxPerson& Right) const;
|
||||||
return FirstName.ToLower() + LastName.ToLower() + Gender.ToLower()
|
bool operator==(const int Length) const;
|
||||||
== Right.FirstName.ToLower() + Right.LastName.ToLower() + Right.Gender.ToLower();
|
bool operator!=(const int Length) const;
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const int Length) const
|
FString GetNormalizedString() const;
|
||||||
{
|
|
||||||
return (FirstName.ToLower() + LastName.ToLower() + Gender.ToLower()).Len() == Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const int Length) const
|
bool IsValid() const;
|
||||||
{
|
|
||||||
return !(*this == Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const FDTFluxPerson& Right) const
|
|
||||||
{
|
|
||||||
return FirstName.ToLower() + LastName.ToLower() + Gender.ToLower()
|
|
||||||
!= Right.FirstName.ToLower() + Right.LastName.ToLower() + Right.Gender.ToLower();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
USTRUCT(BlueprintType, Category="DTFlux|Model")
|
||||||
struct DTFLUXCORE_API FDTFluxParticipant
|
struct DTFLUXCORE_API FDTFluxParticipant
|
||||||
{
|
{
|
||||||
@ -94,117 +100,98 @@ struct DTFLUXCORE_API FDTFluxParticipant
|
|||||||
friend class UDTFluxParticipantFactory;
|
friend class UDTFluxParticipantFactory;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructeur public par défaut requis par Unreal
|
FDTFluxParticipant();
|
||||||
FDTFluxParticipant()
|
|
||||||
: Bib(-1)
|
|
||||||
, ContestId(-1)
|
|
||||||
, Elite(false)
|
|
||||||
, Status(static_cast<EDTFluxParticipantStatusType>(0))
|
|
||||||
, bIsMassStartParticipant(false)
|
|
||||||
, CurrentSplit(-1)
|
|
||||||
{
|
|
||||||
Teammate.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", EditAnywhere)
|
||||||
/**
|
|
||||||
* Vérifie si le participant est dans son état par défaut (non initialisé)
|
|
||||||
* @return True si tous les champs sont à leur valeur par défaut
|
|
||||||
*/
|
|
||||||
bool IsDefault() const
|
|
||||||
{
|
|
||||||
return Bib == -1
|
|
||||||
&& ContestId == -1
|
|
||||||
&& Category.IsEmpty()
|
|
||||||
&& Club.IsEmpty()
|
|
||||||
&& !Elite
|
|
||||||
&& Status == static_cast<EDTFluxParticipantStatusType>(0)
|
|
||||||
&& Team.IsEmpty()
|
|
||||||
&& !bIsMassStartParticipant
|
|
||||||
&& CurrentSplit == -1
|
|
||||||
&& Teammate.IsEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|model", EditAnywhere)
|
|
||||||
int Bib = -1;
|
int Bib = -1;
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|model", EditAnywhere)
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", EditAnywhere)
|
||||||
int ContestId = -1;
|
int ContestId = -1;
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|model", EditAnywhere)
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Model", EditAnywhere)
|
||||||
FString Category;
|
FString Category;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
||||||
FString Club;
|
FString Club;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
|
||||||
bool Elite;
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
bool Elite = false;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
||||||
EDTFluxParticipantStatusType Status;
|
EDTFluxParticipantStatusType Status;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
||||||
FString Team;
|
FString Team;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model", EditAnywhere)
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model", EditAnywhere)
|
||||||
bool bIsMassStartParticipant = false;
|
bool bIsMassStartParticipant = false;
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
|
||||||
int CurrentSplit = -1;
|
int CurrentSplit = -1;
|
||||||
|
|
||||||
// void Dump() const;
|
bool IsDefault() const;
|
||||||
|
|
||||||
void AddTeammate(const FDTFluxPerson& Person);
|
void AddTeammate(const FDTFluxPerson& Person);
|
||||||
void AddTeammate(const FString LastName, const FString FirstName, const FString Gender);
|
void AddTeammate(const FString& LastName, const FString& FirstName, const FString& Gender);
|
||||||
|
|
||||||
FText GetFormattedNameText(const int MaxChar = 15, const FString OverflowChar = FString("...")) const
|
int GetTeammateNum() const;
|
||||||
{
|
|
||||||
return FText::FromString(GetFormattedName(MaxChar, OverflowChar));
|
|
||||||
};
|
|
||||||
|
|
||||||
FText GetConcatFormattedNameText(const int MaxChar = 20, const FString OverflowChar = FString("...")) const
|
bool IsTeam() const;
|
||||||
{
|
|
||||||
return FText::FromString(GetConcatFormattedName(MaxChar, OverflowChar));
|
|
||||||
};
|
|
||||||
FString GetFormattedName(const int MaxChar = 15, const FString OverflowChar = FString("...")) const;
|
|
||||||
FString GetConcatFormattedName(const int MaxChar = 20, const FString OverflowChar = FString("...")) const;
|
|
||||||
|
|
||||||
static FString GetFormattedName(const FDTFluxParticipant& Participant, const int MaxChar = 15,
|
|
||||||
const FString OverflowChar = FString("..."))
|
|
||||||
{
|
|
||||||
return Participant.GetFormattedName(MaxChar, OverflowChar);
|
|
||||||
};
|
|
||||||
|
|
||||||
static FString GetConcatFormattedName(const FDTFluxParticipant& Participant, const int MaxChar = 15,
|
const TArray<FDTFluxPerson>& GetTeammate() const;
|
||||||
const FString OverflowChar = FString("..."))
|
|
||||||
{
|
|
||||||
return Participant.GetConcatFormattedName(MaxChar, OverflowChar);
|
|
||||||
};
|
|
||||||
|
|
||||||
static FText GetFormattedNameText(const FDTFluxParticipant& Participant, const int MaxChar = 15,
|
FString GetFormattedName(const int MaxChar = 15,
|
||||||
const FString OverflowChar = FString("..."))
|
const FString& Separator = FString(". "),
|
||||||
{
|
const FString& OverflowChar = FString("...")) const;
|
||||||
return Participant.GetFormattedNameText();
|
|
||||||
};
|
|
||||||
|
|
||||||
static FText GetConcatFormattedNameText(const FDTFluxParticipant& Participant, const int MaxChar = 15,
|
FString GetConcatFormattedName(const int MaxChar = 20,
|
||||||
const FString OverflowChar = FString("..."))
|
const FString& Separator = FString(". "),
|
||||||
{
|
const FString& OverflowChar = FString("..."),
|
||||||
return Participant.GetConcatFormattedNameText();
|
const FString& BibSeparator = FString(". ")) const;
|
||||||
};
|
|
||||||
const TArray<FDTFluxPerson> GetTeammate() const { return Teammate; }
|
|
||||||
|
|
||||||
private:
|
FText GetFormattedNameText(const int MaxChar = 15,
|
||||||
// --- Constructeur privé ---
|
const FString& Separator = FString(". "),
|
||||||
explicit FDTFluxParticipant(const TSharedPtr<FJsonObject>& JsonObject);
|
const FString& OverflowChar = FString("...")) const;
|
||||||
|
|
||||||
|
FText GetConcatFormattedNameText(const int MaxChar = 20,
|
||||||
|
const FString& Separator = FString(". "),
|
||||||
|
const FString& OverflowChar = FString("..."),
|
||||||
|
const FString& BibSeparator = FString(". ")) const;
|
||||||
|
|
||||||
|
static FString GetFormattedName(const FDTFluxParticipant& Participant,
|
||||||
|
const int MaxChar = 15,
|
||||||
|
const FString& Separator = FString(". "),
|
||||||
|
const FString& OverflowChar = FString("..."));
|
||||||
|
|
||||||
|
static FString GetConcatFormattedName(const FDTFluxParticipant& Participant,
|
||||||
|
const int MaxChar = 15,
|
||||||
|
const FString& Separator = FString(". "),
|
||||||
|
const FString& OverflowChar = FString("..."),
|
||||||
|
const FString& BibSeparator = FString(". "));
|
||||||
|
|
||||||
|
static FText GetFormattedNameText(const FDTFluxParticipant& Participant,
|
||||||
|
const int MaxChar = 15,
|
||||||
|
const FString& Separator = FString(". "),
|
||||||
|
const FString& OverflowChar = FString("..."));
|
||||||
|
|
||||||
|
static FText GetConcatFormattedNameText(const FDTFluxParticipant& Participant,
|
||||||
|
const int MaxChar = 15,
|
||||||
|
const FString& Separator = FString(". "),
|
||||||
|
const FString& OverflowChar = FString("..."),
|
||||||
|
const FString& BibSeparator = FString(". "));
|
||||||
|
|
||||||
|
static FDTFluxParticipant CreateFromJson(const TSharedPtr<FJsonObject>& JsonObject);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UPROPERTY(Category="DTFlux|model", VisibleAnywhere)
|
UPROPERTY(Category="DTFlux|Model", VisibleAnywhere)
|
||||||
TArray<FDTFluxPerson> Teammate;
|
TArray<FDTFluxPerson> Teammate;
|
||||||
// Méthode publique pour construire à partir d'un JSON (utilisée par la factory)
|
|
||||||
static FDTFluxParticipant CreateFromJson(const TSharedPtr<FJsonObject>& JsonObject);
|
private:
|
||||||
int GetTeammateNum() const;
|
explicit FDTFluxParticipant(const TSharedPtr<FJsonObject>& JsonObject);
|
||||||
bool IsTeam() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @struct FDTFluxTeamListDefinition
|
|
||||||
* Struct representing the Participant List definition
|
|
||||||
* Used to exchange data between Objects in the system
|
|
||||||
*/
|
|
||||||
USTRUCT(BlueprintType)
|
USTRUCT(BlueprintType)
|
||||||
struct DTFLUXCORE_API FDTFluxTeamListDefinition
|
struct DTFLUXCORE_API FDTFluxTeamListDefinition
|
||||||
{
|
{
|
||||||
@ -212,27 +199,22 @@ struct DTFLUXCORE_API FDTFluxTeamListDefinition
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
// ReSharper disable once IdentifierTypo
|
|
||||||
TArray<FDTFluxParticipant> Participants;
|
TArray<FDTFluxParticipant> Participants;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
USTRUCT(BlueprintType)
|
||||||
struct FDTFluxTeamStatusUpdate
|
struct DTFLUXCORE_API FDTFluxTeamStatusUpdate
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FDTFluxTeamStatusUpdate() = default;
|
FDTFluxTeamStatusUpdate() = default;
|
||||||
|
FDTFluxTeamStatusUpdate(const int InBib, const int InStatus);
|
||||||
FDTFluxTeamStatusUpdate(const int InBib, const int InStatus)
|
|
||||||
: Bib(InBib)
|
|
||||||
, Status(static_cast<EDTFluxParticipantStatusType>(InStatus))
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant")
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant")
|
||||||
int Bib = -1;
|
int Bib = -1;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant")
|
UPROPERTY(BlueprintReadWrite, Category="DTFlux|Participant")
|
||||||
EDTFluxParticipantStatusType Status = EDTFluxParticipantStatusType::Unknown;
|
EDTFluxParticipantStatusType Status = EDTFluxParticipantStatusType::Unknown;
|
||||||
};
|
};
|
||||||
@ -62,6 +62,7 @@ void UDTFluxCoreSubsystem::ProcessTrackedResponse(FDTFluxServerResponse& InRespo
|
|||||||
FDTFluxContestRankings Rankings;
|
FDTFluxContestRankings Rankings;
|
||||||
if (InResponse.ParseContestRanking(Rankings))
|
if (InResponse.ParseContestRanking(Rankings))
|
||||||
{
|
{
|
||||||
|
OnContestRankings.Broadcast(Rankings.ContestId, Rankings);
|
||||||
ProcessContestRanking(Rankings);
|
ProcessContestRanking(Rankings);
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestRanking added for Contest %s"),
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestRanking added for Contest %s"),
|
||||||
*Rankings.ContestName);
|
*Rankings.ContestName);
|
||||||
@ -77,6 +78,8 @@ void UDTFluxCoreSubsystem::ProcessTrackedResponse(FDTFluxServerResponse& InRespo
|
|||||||
FDTFluxStageRankings Rankings;
|
FDTFluxStageRankings Rankings;
|
||||||
if (InResponse.ParseStageRanking(Rankings))
|
if (InResponse.ParseStageRanking(Rankings))
|
||||||
{
|
{
|
||||||
|
FDTFluxStageKey StageKey(Rankings.ContestId, Rankings.StageId);
|
||||||
|
OnStageRankings.Broadcast(StageKey, Rankings);
|
||||||
ProcessStageRanking(Rankings);
|
ProcessStageRanking(Rankings);
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestRanking added for Stage %i of Contest %i"),
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("ContestRanking added for Stage %i of Contest %i"),
|
||||||
Rankings.StageId, Rankings.ContestId);
|
Rankings.StageId, Rankings.ContestId);
|
||||||
@ -92,6 +95,8 @@ void UDTFluxCoreSubsystem::ProcessTrackedResponse(FDTFluxServerResponse& InRespo
|
|||||||
FDTFluxSplitRankings Rankings;
|
FDTFluxSplitRankings Rankings;
|
||||||
if (InResponse.ParseSplitRanking(Rankings))
|
if (InResponse.ParseSplitRanking(Rankings))
|
||||||
{
|
{
|
||||||
|
FDTFluxSplitKey SplitKey(Rankings.ContestId, Rankings.StageId, Rankings.SplitId);
|
||||||
|
OnSplitRankings.Broadcast(SplitKey, Rankings);
|
||||||
ProcessSplitRanking(Rankings);
|
ProcessSplitRanking(Rankings);
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Warning,
|
UE_LOG(logDTFluxCoreSubsystem, Warning,
|
||||||
TEXT("ContestRanking added for Split %i of Stage %i of Contest %i"),
|
TEXT("ContestRanking added for Split %i of Stage %i of Contest %i"),
|
||||||
@ -275,7 +280,6 @@ void UDTFluxCoreSubsystem::ProcessSplitSensor(const FDTFluxSplitSensorInfo& Spli
|
|||||||
SplitSensorInfo.SplitId, SplitSensorInfo.Bib, *Participant.GetFormattedName());
|
SplitSensorInfo.SplitId, SplitSensorInfo.Bib, *Participant.GetFormattedName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UDTFluxCoreSubsystem::SendRequest(const FString& Message)
|
void UDTFluxCoreSubsystem::SendRequest(const FString& Message)
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
@ -360,7 +364,7 @@ bool UDTFluxCoreSubsystem::GetSplitRankingsWithKey(const FDTFluxSplitKey SplitKe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestContestRankings(const TArray<int> ForContests)
|
TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestContestRankings(const TArray<int> ForContests, bool bEnableCache)
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
{
|
{
|
||||||
@ -386,7 +390,7 @@ TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestContestRankings(const TArray<i
|
|||||||
for (auto ContestId : ForContests)
|
for (auto ContestId : ForContests)
|
||||||
{
|
{
|
||||||
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::ContestRanking,
|
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::ContestRanking,
|
||||||
ContestId, -1, -1, OnSuccess, OnError);
|
ContestId, -1, -1, OnSuccess, OnError, bEnableCache);
|
||||||
RequestIds.Add(ContestRequest);
|
RequestIds.Add(ContestRequest);
|
||||||
}
|
}
|
||||||
return RequestIds;
|
return RequestIds;
|
||||||
@ -394,7 +398,8 @@ TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestContestRankings(const TArray<i
|
|||||||
return TArray<FGuid>();
|
return TArray<FGuid>();
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestStageRankings(const TArray<FDTFluxStageKey> ForStages)
|
TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestStageRankings(const TArray<FDTFluxStageKey> ForStages,
|
||||||
|
bool bEnableCache)
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
{
|
{
|
||||||
@ -420,7 +425,7 @@ TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestStageRankings(const TArray<FDT
|
|||||||
for (auto StageKey : ForStages)
|
for (auto StageKey : ForStages)
|
||||||
{
|
{
|
||||||
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::StageRanking,
|
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::StageRanking,
|
||||||
StageKey.ContestId, StageKey.StageId, -1, OnSuccess, OnError);
|
StageKey.ContestId, StageKey.StageId, -1, OnSuccess, OnError, bEnableCache);
|
||||||
RequestIds.Add(ContestRequest);
|
RequestIds.Add(ContestRequest);
|
||||||
}
|
}
|
||||||
return RequestIds;
|
return RequestIds;
|
||||||
@ -428,7 +433,8 @@ TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestStageRankings(const TArray<FDT
|
|||||||
return TArray<FGuid>();
|
return TArray<FGuid>();
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestSplitRankings(const TArray<FDTFluxSplitKey> ForSplits)
|
TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestSplitRankings(const TArray<FDTFluxSplitKey> ForSplits,
|
||||||
|
bool bEnableCache)
|
||||||
{
|
{
|
||||||
if (NetworkSubsystem)
|
if (NetworkSubsystem)
|
||||||
{
|
{
|
||||||
@ -454,7 +460,7 @@ TArray<FGuid> UDTFluxCoreSubsystem::TrackedRequestSplitRankings(const TArray<FDT
|
|||||||
for (auto SplitKey : ForSplits)
|
for (auto SplitKey : ForSplits)
|
||||||
{
|
{
|
||||||
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::SplitRanking,
|
FGuid ContestRequest = NetworkSubsystem->SendTrackedRequestWithCallbacks(EDTFluxApiDataType::SplitRanking,
|
||||||
SplitKey.ContestId, SplitKey.StageId, SplitKey.SplitId, OnSuccess, OnError);
|
SplitKey.ContestId, SplitKey.StageId, SplitKey.SplitId, OnSuccess, OnError, bEnableCache);
|
||||||
RequestIds.Add(ContestRequest);
|
RequestIds.Add(ContestRequest);
|
||||||
}
|
}
|
||||||
return RequestIds;
|
return RequestIds;
|
||||||
|
|||||||
@ -45,61 +45,136 @@ void UDTFluxPursuitManager::SetPursuitInfoIsMassStart(FDTFluxPursuitGroup NextFo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UDTFluxPursuitManager::DebugFocusNext(const TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext)
|
||||||
|
{
|
||||||
|
FString FocusBibs;
|
||||||
|
for (const auto& Pursuit : OutPursuitFocusNext)
|
||||||
|
{
|
||||||
|
FocusBibs += FString::Printf(TEXT("%d "), Pursuit.Bib);
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus Bibs: %s"), *FocusBibs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UDTFluxPursuitManager::DebugOutPoursuitNext(const TArray<FDTFluxPursuitInfo>& OutPursuitNext)
|
||||||
|
{
|
||||||
|
FString NextBibs;
|
||||||
|
for (int32 i = 0; i < OutPursuitNext.Num(); i++)
|
||||||
|
{
|
||||||
|
NextBibs += FString::Printf(TEXT("%d "), OutPursuitNext[i].Bib);
|
||||||
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next Bibs: %s"), *NextBibs);
|
||||||
|
}
|
||||||
|
|
||||||
void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext,
|
void UDTFluxPursuitManager::GetPursuit(TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext,
|
||||||
TArray<FDTFluxPursuitInfo>& OutPursuitNext, bool& BIsFocusTruncate,
|
TArray<FDTFluxPursuitInfo>& OutPursuitNext, bool& BIsFocusTruncate,
|
||||||
const int MaxSimultaneousPursuit)
|
const int MaxSimultaneousPursuit)
|
||||||
{
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== GetPursuit CALLED ==="));
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("MaxSimultaneousPursuit: %d"), MaxSimultaneousPursuit);
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Available groups: %d"), GroupedPursuit.Num());
|
||||||
|
|
||||||
|
// Validation
|
||||||
if (MaxSimultaneousPursuit <= 0)
|
if (MaxSimultaneousPursuit <= 0)
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("MaxSimultaneousPursuit must be > 0"));
|
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Invalid MaxSimultaneousPursuit: %d"), MaxSimultaneousPursuit);
|
||||||
OutPursuitFocusNext = TArray<FDTFluxPursuitInfo>();
|
|
||||||
OutPursuitNext = TArray<FDTFluxPursuitInfo>();
|
|
||||||
BIsFocusTruncate = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (bIsSequenceDone && MaxSimultaneousPursuit <= 0)
|
|
||||||
{
|
|
||||||
OutPursuitFocusNext = TArray<FDTFluxPursuitInfo>();
|
|
||||||
OutPursuitNext = TArray<FDTFluxPursuitInfo>();
|
|
||||||
BIsFocusTruncate = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OutPursuitFocusNext.Reset();
|
OutPursuitFocusNext.Reset();
|
||||||
OutPursuitNext.Reset();
|
OutPursuitNext.Reset();
|
||||||
if (!GroupedPursuit.IsEmpty())
|
BIsFocusTruncate = false;
|
||||||
{
|
return;
|
||||||
FDTFluxPursuitGroup NextFocusGroup = GroupedPursuit[0];
|
|
||||||
GroupedPursuit.RemoveAt(0);
|
|
||||||
SetPursuitInfoIsMassStart(NextFocusGroup);
|
|
||||||
OutPursuitFocusNext = NextFocusGroup.PursuitGroup;
|
|
||||||
bFocusIsTruncate = NextFocusGroup.PursuitGroup.Num() > 1;
|
|
||||||
for (int RemainingPursuitNum = MaxSimultaneousPursuit - 1; RemainingPursuitNum != 0;)
|
|
||||||
{
|
|
||||||
if (!GroupedPursuit.IsEmpty())
|
|
||||||
{
|
|
||||||
FDTFluxPursuitGroup NextGroup = GroupedPursuit[0];
|
|
||||||
SetPursuitInfoIsMassStart(NextGroup);
|
|
||||||
if (NextGroup.PursuitGroup.Num() >= RemainingPursuitNum)
|
|
||||||
{
|
|
||||||
// extract the number we need
|
|
||||||
for (int i = 0; i < RemainingPursuitNum; i++)
|
|
||||||
{
|
|
||||||
FDTFluxPursuitInfo Pursuit = NextGroup.PursuitGroup[0];
|
|
||||||
OutPursuitNext.Add(Pursuit);
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
if (bIsSequenceDone || GroupedPursuit.IsEmpty())
|
||||||
else
|
|
||||||
{
|
{
|
||||||
OutPursuitNext.Append(NextGroup.PursuitGroup);
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("No groups available or sequence completed"));
|
||||||
RemainingPursuitNum -= NextGroup.PursuitGroup.Num();
|
OutPursuitFocusNext.Reset();
|
||||||
|
OutPursuitNext.Reset();
|
||||||
|
BIsFocusTruncate = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutPursuitFocusNext.Reset();
|
||||||
|
OutPursuitNext.Reset();
|
||||||
|
|
||||||
|
// === ÉTAPE 1: FOCUS = PREMIER GROUPE (et le supprimer) ===
|
||||||
|
FDTFluxPursuitGroup FocusGroup = GroupedPursuit[0]; // Copie du premier groupe
|
||||||
|
GroupedPursuit.RemoveAt(0); // ✅ SUPPRIMER le premier groupe
|
||||||
|
|
||||||
|
SetPursuitInfoIsMassStart(FocusGroup);
|
||||||
|
OutPursuitFocusNext = FocusGroup.PursuitGroup;
|
||||||
|
BIsFocusTruncate = FocusGroup.PursuitGroup.Num() > 1;
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning,
|
||||||
|
TEXT("Focus Group: StartTime=%s, Participants=%d"),
|
||||||
|
*FocusGroup.StartTimeGlobal.ToString(),
|
||||||
|
FocusGroup.PursuitGroup.Num());
|
||||||
|
|
||||||
|
// === ÉTAPE 2: NEXT = GROUPES SUIVANTS (SANS les supprimer) ===
|
||||||
|
int32 TargetNextCount = MaxSimultaneousPursuit - 1; // -1 pour le focus
|
||||||
|
int32 AddedNextCount = 0;
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Target Next Count: %d"), TargetNextCount);
|
||||||
|
|
||||||
|
// ✅ PARCOURIR les groupes restants SANS les modifier
|
||||||
|
for (int32 GroupIndex = 0;
|
||||||
|
GroupIndex < GroupedPursuit.Num() && AddedNextCount < TargetNextCount;
|
||||||
|
GroupIndex++)
|
||||||
|
{
|
||||||
|
FDTFluxPursuitGroup& NextGroup = GroupedPursuit[GroupIndex]; // Référence (pour SetPursuitInfoIsMassStart)
|
||||||
|
|
||||||
|
if (NextGroup.PursuitGroup.Num() == 0)
|
||||||
|
{
|
||||||
|
continue; // Groupe vide
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 AvailableInGroup = NextGroup.PursuitGroup.Num();
|
||||||
|
int32 NeededFromGroup = FMath::Min(TargetNextCount - AddedNextCount, AvailableInGroup);
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning,
|
||||||
|
TEXT("Processing Next Group %d: StartTime=%s, Available=%d, Taking=%d"),
|
||||||
|
GroupIndex,
|
||||||
|
*NextGroup.StartTimeGlobal.ToString(),
|
||||||
|
AvailableInGroup,
|
||||||
|
NeededFromGroup);
|
||||||
|
|
||||||
|
// ✅ COPIER les participants nécessaires (SANS les supprimer du groupe)
|
||||||
|
for (int32 ParticipantIndex = 0; ParticipantIndex < NeededFromGroup; ParticipantIndex++)
|
||||||
|
{
|
||||||
|
FDTFluxPursuitInfo NextParticipant = NextGroup.PursuitGroup[ParticipantIndex]; // Copie
|
||||||
|
|
||||||
|
// Appliquer MassStart
|
||||||
|
NextParticipant.bIsMassStart = NextParticipant.StartTime >= MassStartTime;
|
||||||
|
|
||||||
|
OutPursuitNext.Add(NextParticipant);
|
||||||
|
AddedNextCount++;
|
||||||
|
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, VeryVerbose,
|
||||||
|
TEXT("Added to Next: Bib %d from Group %d"),
|
||||||
|
NextParticipant.Bib, GroupIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// === LOGS DE RÉSUMÉ ===
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("=== PURSUIT RESULTS ==="));
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Focus: %d participants"), OutPursuitFocusNext.Num());
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Next: %d participants"), OutPursuitNext.Num());
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Remaining groups for future: %d"), GroupedPursuit.Num());
|
||||||
|
|
||||||
|
if (OutPursuitFocusNext.Num() > 0)
|
||||||
{
|
{
|
||||||
break;
|
DebugFocusNext(OutPursuitFocusNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log détaillé des Next (limité pour éviter spam)
|
||||||
|
if (OutPursuitNext.Num() > 0)
|
||||||
|
{
|
||||||
|
DebugOutPoursuitNext(OutPursuitNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vérifier si la séquence est terminée
|
||||||
|
if (GroupedPursuit.IsEmpty())
|
||||||
|
{
|
||||||
|
bIsSequenceDone = true;
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Pursuit sequence will be completed after this round"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +194,7 @@ bool UDTFluxPursuitManager::BindRankings()
|
|||||||
{
|
{
|
||||||
if (!bIsRankingBounded)
|
if (!bIsRankingBounded)
|
||||||
{
|
{
|
||||||
CoreSubsystem->OnRequestedStageRankings.AddDynamic(this, &UDTFluxPursuitManager::OnRankingsReceived);
|
CoreSubsystem->OnStageRankings.AddDynamic(this, &UDTFluxPursuitManager::OnRankingsReceived);
|
||||||
bIsRankingBounded = true;
|
bIsRankingBounded = true;
|
||||||
}
|
}
|
||||||
return bIsRankingBounded;
|
return bIsRankingBounded;
|
||||||
@ -134,7 +209,7 @@ void UDTFluxPursuitManager::UnbindRankings()
|
|||||||
{
|
{
|
||||||
if (bIsRankingBounded)
|
if (bIsRankingBounded)
|
||||||
{
|
{
|
||||||
CoreSubsystem->OnRequestedStageRankings.RemoveDynamic(this, &UDTFluxPursuitManager::OnRankingsReceived);
|
CoreSubsystem->OnStageRankings.RemoveDynamic(this, &UDTFluxPursuitManager::OnRankingsReceived);
|
||||||
bIsRankingBounded = false;
|
bIsRankingBounded = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -178,18 +253,30 @@ bool UDTFluxPursuitManager::LaunchPursuitSequence()
|
|||||||
AllPursuits.Add(PursuitInfo);
|
AllPursuits.Add(PursuitInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("AllPursuits.Num() = %i"), AllPursuits.Num());
|
||||||
for (auto& Pursuit : AllPursuits)
|
for (auto& Pursuit : AllPursuits)
|
||||||
{
|
{
|
||||||
if (TempGroups.Contains(Pursuit.StartTime))
|
if (TempGroups.Contains(Pursuit.StartTime))
|
||||||
{
|
{
|
||||||
TempGroups[Pursuit.StartTime].PursuitGroup.Add(Pursuit);
|
FDTFluxPursuitGroup& Group = TempGroups[Pursuit.StartTime];
|
||||||
|
Group.PursuitGroup.Add(Pursuit);
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning,
|
||||||
|
TEXT("Adding [%i] To PursuitGroup starting At %s, PursuitGroup.Num() %i"),
|
||||||
|
Pursuit.Bib, *Pursuit.StartTime.ToString(), Group.PursuitGroup.Num());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FDTFluxPursuitGroup Group;
|
FDTFluxPursuitGroup NewGroup;
|
||||||
Group.StartTimeGlobal = Pursuit.StartTime;
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("New Group starting At %s, Adding Bib [%i]"),
|
||||||
Group.PursuitGroup.Add(Pursuit);
|
*Pursuit.StartTime.ToString(), Pursuit.Bib);
|
||||||
TempGroups.Add(Pursuit.StartTime, Group);
|
NewGroup.StartTimeGlobal = Pursuit.StartTime;
|
||||||
|
NewGroup.PursuitGroup.Add(Pursuit);
|
||||||
|
TempGroups.Add(Pursuit.StartTime, NewGroup);
|
||||||
|
for (const auto& Group : TempGroups)
|
||||||
|
{
|
||||||
|
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Group.StartTime = %s, Group.PursuitGroup.Num() = %i"),
|
||||||
|
*Group.Key.ToString(), Group.Value.PursuitGroup.Num());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TempGroups.KeySort([](const FDateTime& A, const FDateTime& B)
|
TempGroups.KeySort([](const FDateTime& A, const FDateTime& B)
|
||||||
@ -199,18 +286,18 @@ bool UDTFluxPursuitManager::LaunchPursuitSequence()
|
|||||||
TMap<FDateTime, int> StartTimeFrequency;
|
TMap<FDateTime, int> StartTimeFrequency;
|
||||||
int32 MaxFrequency = 0;
|
int32 MaxFrequency = 0;
|
||||||
GroupedPursuit.Reserve(TempGroups.Num());
|
GroupedPursuit.Reserve(TempGroups.Num());
|
||||||
|
// parcours du TMap
|
||||||
for (const auto& Pair : TempGroups)
|
for (const auto& Pair : TempGroups)
|
||||||
{
|
{
|
||||||
if (Pair.Value.StartTimeGlobal != FDateTime::MinValue() && Pair.Value.StartTimeGlobal != FDateTime::MaxValue())
|
if (Pair.Value.StartTimeGlobal != FDateTime::MinValue() && Pair.Value.StartTimeGlobal != FDateTime::MaxValue())
|
||||||
{
|
{
|
||||||
StartTimeFrequency.FindOrAdd(Pair.Value.StartTimeGlobal)++;
|
// récuperation de la ref de la valeur actuel de la fréquence dans la TMap Freq
|
||||||
const FDateTime& PropertyValue = Pair.Value.StartTimeGlobal; // Votre propriété
|
int& CurrentFreq = StartTimeFrequency.FindOrAdd(Pair.Value.StartTimeGlobal, 0);
|
||||||
int32& Count = StartTimeFrequency.FindOrAdd(PropertyValue, 0);
|
CurrentFreq = Pair.Value.PursuitGroup.Num();
|
||||||
Count++;
|
if (CurrentFreq > MaxFrequency)
|
||||||
if (Count > MaxFrequency)
|
|
||||||
{
|
{
|
||||||
MaxFrequency = Count;
|
MaxFrequency = CurrentFreq;
|
||||||
MassStartTime = PropertyValue;
|
MassStartTime = Pair.Value.StartTimeGlobal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GroupedPursuit.Add(Pair.Value);
|
GroupedPursuit.Add(Pair.Value);
|
||||||
@ -226,7 +313,7 @@ bool UDTFluxPursuitManager::LaunchPursuitSequence()
|
|||||||
bool bIsFocusTruncate = false;
|
bool bIsFocusTruncate = false;
|
||||||
|
|
||||||
GetPursuit(FocusPursuits, NextPursuits, bIsFocusTruncate);
|
GetPursuit(FocusPursuits, NextPursuits, bIsFocusTruncate);
|
||||||
FPursuitStaterData PursuitData = FPursuitStaterData(FocusPursuits, NextPursuits, MassStartTime, bIsFocusTruncate);
|
FPursuitStarterData PursuitData = FPursuitStarterData(FocusPursuits, NextPursuits, MassStartTime, bIsFocusTruncate);
|
||||||
CoreSubsystem->OnPursuitSequenceReady.Broadcast(PursuitData);
|
OnPursuitSequenceReady.Broadcast(PursuitData);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,34 +17,6 @@ class UDTFluxModelAsset;
|
|||||||
class UDTFluxPursuitManager;
|
class UDTFluxPursuitManager;
|
||||||
struct FDTFluxServerResponse;
|
struct FDTFluxServerResponse;
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
|
||||||
struct FPursuitStaterData
|
|
||||||
{
|
|
||||||
GENERATED_BODY()
|
|
||||||
|
|
||||||
public:
|
|
||||||
FPursuitStaterData() = default;
|
|
||||||
|
|
||||||
FPursuitStaterData(const TArray<FDTFluxPursuitInfo>& InPursuitFocusNext,
|
|
||||||
const TArray<FDTFluxPursuitInfo>& InPursuitNext, const FDateTime& InMassStartTime,
|
|
||||||
const bool InIsFocusTruncate)
|
|
||||||
: PursuitFocusNext(InPursuitFocusNext), PursuitNext(InPursuitNext), MassStartTime(InMassStartTime),
|
|
||||||
bIsFocusTruncate(InIsFocusTruncate)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
|
||||||
TArray<FDTFluxPursuitInfo> PursuitFocusNext = TArray<FDTFluxPursuitInfo>();
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
|
||||||
TArray<FDTFluxPursuitInfo> PursuitNext = TArray<FDTFluxPursuitInfo>();
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
|
||||||
FDateTime MassStartTime = FDateTime::MinValue();
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
|
||||||
bool bIsFocusTruncate = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPursuitSequenceReady, const FPursuitStaterData, PursuitInfoSequenceItem);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -55,18 +27,24 @@ class DTFLUXCORESUBSYSTEM_API UDTFluxCoreSubsystem : public UEngineSubsystem
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSplitRankings, FDTFluxSplitRankings, SplitRankings);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSplitRankings, FDTFluxSplitKey, SplitKey, FDTFluxSplitRankings,
|
||||||
|
SplitRankings);
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnSplitRankings OnSplitRankings;
|
FOnSplitRankings OnSplitRankings;
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStageRankings, FDTFluxStageRankings, StageRankings);
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnStageRankings, FDTFluxStageKey, StageKey, FDTFluxStageRankings,
|
||||||
|
StageRankings);
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnStageRankings OnStageRankings;
|
FOnStageRankings OnStageRankings;
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnContestRankings, FDTFluxContestRankings, ContestRankings);
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnContestRankings, const int, ContestId, FDTFluxContestRankings,
|
||||||
|
ContestRankings);
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnContestRankings OnContestRankings;
|
FOnContestRankings OnContestRankings;
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTeamList);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTeamList);
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
@ -76,14 +54,8 @@ public:
|
|||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
FOnTeamStatusUpdate OnTeamStatusUpdate;
|
FOnTeamStatusUpdate OnTeamStatusUpdate;
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnRequestedStageRankings, const FDTFluxStageKey, StageKey,
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem")
|
||||||
const FDTFluxStageRankings, StageRankings);
|
UDTFluxPursuitManager* PursuitManager = nullptr;
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
|
||||||
FOnRequestedStageRankings OnRequestedStageRankings;
|
|
||||||
|
|
||||||
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
|
||||||
FOnPursuitSequenceReady OnPursuitSequenceReady;
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
bool GetContestRankings(const int ContestId, FDTFluxContestRankings& OutContestRankings);
|
bool GetContestRankings(const int ContestId, FDTFluxContestRankings& OutContestRankings);
|
||||||
@ -104,16 +76,14 @@ public:
|
|||||||
const bool bShouldUseCached = true);
|
const bool bShouldUseCached = true);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
TArray<FGuid> TrackedRequestContestRankings(const TArray<int> ForContests);
|
TArray<FGuid> TrackedRequestContestRankings(const TArray<int> ForContests, bool bEnableCache = true);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
TArray<FGuid> TrackedRequestStageRankings(const TArray<FDTFluxStageKey> ForStages);
|
TArray<FGuid> TrackedRequestStageRankings(const TArray<FDTFluxStageKey> ForStages, bool bEnableCache = true);
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
TArray<FGuid> TrackedRequestSplitRankings(const TArray<FDTFluxSplitKey> ForSplits);
|
TArray<FGuid> TrackedRequestSplitRankings(const TArray<FDTFluxSplitKey> ForSplits, bool bEnableCache = true);
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem")
|
|
||||||
UDTFluxPursuitManager* PursuitManager = nullptr;
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
|
||||||
const FDTFluxParticipant GetParticipant(int InBib);
|
const FDTFluxParticipant GetParticipant(int InBib);
|
||||||
|
|||||||
@ -10,44 +10,31 @@
|
|||||||
|
|
||||||
class UDTFluxCoreSubsystem;
|
class UDTFluxCoreSubsystem;
|
||||||
|
|
||||||
USTRUCT()
|
|
||||||
struct FRequestData
|
USTRUCT(BlueprintType)
|
||||||
|
struct FPursuitStarterData
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
UPROPERTY()
|
public:
|
||||||
TArray<FGuid> RequestIds;
|
FPursuitStarterData() = default;
|
||||||
UPROPERTY()
|
|
||||||
TMap<FGuid, FDTFluxStageRankings> StageRankings;
|
|
||||||
UPROPERTY()
|
|
||||||
int ContestId;
|
|
||||||
|
|
||||||
UPROPERTY()
|
FPursuitStarterData(const TArray<FDTFluxPursuitInfo>& InPursuitFocusNext,
|
||||||
bool bIsReady = false;
|
const TArray<FDTFluxPursuitInfo>& InPursuitNext, const FDateTime& InMassStartTime,
|
||||||
|
const bool InIsFocusTruncate)
|
||||||
|
: PursuitFocusNext(InPursuitFocusNext), PursuitNext(InPursuitNext), MassStartTime(InMassStartTime),
|
||||||
FRequestData() = default;
|
bIsFocusTruncate(InIsFocusTruncate)
|
||||||
|
|
||||||
FRequestData(const TArray<FGuid>& InRequestIds, const TMap<FGuid, FDTFluxStageRankings>& InStageRankings)
|
|
||||||
: RequestIds(InRequestIds), StageRankings(InStageRankings), ContestId(-1)
|
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
||||||
*
|
TArray<FDTFluxPursuitInfo> PursuitFocusNext = TArray<FDTFluxPursuitInfo>();
|
||||||
* @param RequestId
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
||||||
* @param InRankings
|
TArray<FDTFluxPursuitInfo> PursuitNext = TArray<FDTFluxPursuitInfo>();
|
||||||
* @return True if all needed requests have responses
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
||||||
*/
|
FDateTime MassStartTime = FDateTime::MinValue();
|
||||||
bool IsWaitingFor(const FGuid& RequestId, const FDTFluxStageRankings& InRankings)
|
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Pursuit")
|
||||||
{
|
bool bIsFocusTruncate = false;
|
||||||
if (!StageRankings.Contains(RequestId))
|
|
||||||
{
|
|
||||||
StageRankings.Add(RequestId, InRankings);
|
|
||||||
}
|
|
||||||
bIsReady = StageRankings.Num() <= RequestIds.Num();
|
|
||||||
return bIsReady;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
USTRUCT()
|
USTRUCT()
|
||||||
@ -55,6 +42,7 @@ struct FDTFluxPursuitGroup
|
|||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TArray<FDTFluxPursuitInfo> PursuitGroup = TArray<FDTFluxPursuitInfo>();
|
TArray<FDTFluxPursuitInfo> PursuitGroup = TArray<FDTFluxPursuitInfo>();
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
@ -65,7 +53,6 @@ struct FDTFluxPursuitGroup
|
|||||||
bool bIsFocus = false;
|
bool bIsFocus = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -78,14 +65,18 @@ public:
|
|||||||
UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer);
|
UDTFluxPursuitManager(const FObjectInitializer& ObjectInitializer);
|
||||||
|
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPursuitSequenceReady, const FPursuitStarterData, PursuitData);
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
|
||||||
|
FOnPursuitSequenceReady OnPursuitSequenceReady;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||||
bool bFocusIsTruncate = false;
|
bool bFocusIsTruncate = false;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int PursuitMaxSimultaneousPursuit = 7;
|
int PursuitMaxSimultaneousPursuit = 7;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="DTFlux|Pursuit",
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="DTFlux|Pursuit")
|
||||||
meta=(ClampMin="1", ClampMax="60", UIMin="0", UIMax="60"))
|
|
||||||
int MassStartDelay = 10;
|
int MassStartDelay = 10;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
@ -117,6 +108,10 @@ public:
|
|||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void OnRankingsReceived(const FDTFluxStageKey NewStageKey, const FDTFluxStageRankings NewStageRankings);
|
void OnRankingsReceived(const FDTFluxStageKey NewStageKey, const FDTFluxStageRankings NewStageRankings);
|
||||||
|
|
||||||
|
|
||||||
|
void DebugFocusNext(const TArray<FDTFluxPursuitInfo>& OutPursuitFocusNext);
|
||||||
|
void DebugOutPoursuitNext(const TArray<FDTFluxPursuitInfo>& OutPursuitNext);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TMap<FDTFluxStageKey, bool> PendingStageRanking;
|
TMap<FDTFluxStageKey, bool> PendingStageRanking;
|
||||||
TArray<FDTFluxStageRankings> AllRankings;
|
TArray<FDTFluxStageRankings> AllRankings;
|
||||||
|
|||||||
@ -23,11 +23,6 @@ bool FDTFluxTrackedRequest::CanRetry() const
|
|||||||
(State == EDTFluxRequestState::Failed || State == EDTFluxRequestState::TimedOut);
|
(State == EDTFluxRequestState::Failed || State == EDTFluxRequestState::TimedOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxTrackedRequest::IsCacheValid() const
|
|
||||||
{
|
|
||||||
if (State != EDTFluxRequestState::Cached) return false;
|
|
||||||
return (FDateTime::Now() - CompletedAt).GetTotalSeconds() < Config.CacheValiditySeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
float FDTFluxTrackedRequest::GetRetryDelay() const
|
float FDTFluxTrackedRequest::GetRetryDelay() const
|
||||||
{
|
{
|
||||||
@ -40,11 +35,6 @@ bool FDTFluxTrackedRequest::Matches(EDTFluxApiDataType InType, int32 InContestId
|
|||||||
return RequestType == InType && ContestId == InContestId && StageId == InStageId && SplitId == InSplitId;
|
return RequestType == InType && ContestId == InContestId && StageId == InStageId && SplitId == InSplitId;
|
||||||
}
|
}
|
||||||
|
|
||||||
FString FDTFluxTrackedRequest::GetCacheKey() const
|
|
||||||
{
|
|
||||||
return FString::Printf(TEXT("%s_%d_%d_%d"),
|
|
||||||
*UEnum::GetValueAsString(RequestType), ContestId, StageId, SplitId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxTrackedRequest::SetRawResponse(const FString& RawData)
|
void FDTFluxTrackedRequest::SetRawResponse(const FString& RawData)
|
||||||
{
|
{
|
||||||
@ -118,8 +108,8 @@ void FDTFluxQueuedRequestManager::Initialize(const FDTFluxRequestConfig& InDefau
|
|||||||
DefaultConfig = InDefaultConfig;
|
DefaultConfig = InDefaultConfig;
|
||||||
bIsInitialized.store(true);
|
bIsInitialized.store(true);
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("RequestManager initialized with timeout=%.1fs, cache=%.1fs"),
|
UE_LOG(logDTFluxNetwork, Log, TEXT("RequestManager initialized with timeout=%.1fs"),
|
||||||
DefaultConfig.TimeoutSeconds, DefaultConfig.CacheValiditySeconds);
|
DefaultConfig.TimeoutSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::Shutdown()
|
void FDTFluxQueuedRequestManager::Shutdown()
|
||||||
@ -134,7 +124,6 @@ void FDTFluxQueuedRequestManager::Shutdown()
|
|||||||
FScopeLock CallbacksLock_Local(&CallbacksLock);
|
FScopeLock CallbacksLock_Local(&CallbacksLock);
|
||||||
|
|
||||||
AllRequests.Empty();
|
AllRequests.Empty();
|
||||||
CacheKeyToRequestId.Empty();
|
|
||||||
SuccessCallbacks.Empty();
|
SuccessCallbacks.Empty();
|
||||||
ErrorCallbacks.Empty();
|
ErrorCallbacks.Empty();
|
||||||
}
|
}
|
||||||
@ -154,37 +143,7 @@ FGuid FDTFluxQueuedRequestManager::CreateTrackedRequest(
|
|||||||
UE_LOG(logDTFluxNetwork, Error, TEXT("RequestManager not initialized"));
|
UE_LOG(logDTFluxNetwork, Error, TEXT("RequestManager not initialized"));
|
||||||
return FGuid();
|
return FGuid();
|
||||||
}
|
}
|
||||||
|
// Create new request
|
||||||
// Vérifier le cache d'abord
|
|
||||||
FString CachedResponse;
|
|
||||||
if (CustomConfig.bEnableCache && GetFromCache(RequestType, CachedResponse, ContestId, StageId, SplitId))
|
|
||||||
{
|
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("Request served from cache: Type=%s"),
|
|
||||||
*UEnum::GetValueAsString(RequestType));
|
|
||||||
|
|
||||||
// Créer une "fausse" requête pour représenter le hit cache
|
|
||||||
auto CachedRequest = MakeShared<FDTFluxTrackedRequest>();
|
|
||||||
CachedRequest->RequestType = RequestType;
|
|
||||||
CachedRequest->ContestId = ContestId;
|
|
||||||
CachedRequest->StageId = StageId;
|
|
||||||
CachedRequest->SplitId = SplitId;
|
|
||||||
CachedRequest->Config = CustomConfig.bEnableCache ? CustomConfig : DefaultConfig;
|
|
||||||
CachedRequest->State = EDTFluxRequestState::Cached;
|
|
||||||
CachedRequest->RawResponseData = CachedResponse;
|
|
||||||
CachedRequest->CompletedAt = FDateTime::Now();
|
|
||||||
|
|
||||||
FGuid CacheRequestId = CachedRequest->RequestId;
|
|
||||||
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
AllRequests.Add(CacheRequestId, CachedRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
RecordCacheHit();
|
|
||||||
return CacheRequestId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Créer une nouvelle requête
|
|
||||||
auto NewRequest = MakeShared<FDTFluxTrackedRequest>();
|
auto NewRequest = MakeShared<FDTFluxTrackedRequest>();
|
||||||
NewRequest->RequestType = RequestType;
|
NewRequest->RequestType = RequestType;
|
||||||
NewRequest->ContestId = ContestId;
|
NewRequest->ContestId = ContestId;
|
||||||
@ -199,12 +158,8 @@ FGuid FDTFluxQueuedRequestManager::CreateTrackedRequest(
|
|||||||
AllRequests.Add(RequestId, NewRequest);
|
AllRequests.Add(RequestId, NewRequest);
|
||||||
TotalRequests++;
|
TotalRequests++;
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordCacheMiss();
|
|
||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("Created tracked request %s: Type=%s, Contest=%d, Stage=%d, Split=%d"),
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Created tracked request %s: Type=%s, Contest=%d, Stage=%d, Split=%d"),
|
||||||
*RequestId.ToString(), *UEnum::GetValueAsString(RequestType), ContestId, StageId, SplitId);
|
*RequestId.ToString(), *UEnum::GetValueAsString(RequestType), ContestId, StageId, SplitId);
|
||||||
|
|
||||||
return RequestId;
|
return RequestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +212,6 @@ bool FDTFluxQueuedRequestManager::CompleteRequest(const FGuid& RequestId, const
|
|||||||
{
|
{
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("FDTFluxQueuedRequestManager::CompleteRequest() %s"), *RequestId.ToString());
|
UE_LOG(logDTFluxNetwork, Log, TEXT("FDTFluxQueuedRequestManager::CompleteRequest() %s"), *RequestId.ToString());
|
||||||
TSharedPtr<FDTFluxTrackedRequest> Request;
|
TSharedPtr<FDTFluxTrackedRequest> Request;
|
||||||
|
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&RequestsLock);
|
FScopeLock Lock(&RequestsLock);
|
||||||
if (TSharedPtr<FDTFluxTrackedRequest>* RequestPtr = AllRequests.Find(RequestId))
|
if (TSharedPtr<FDTFluxTrackedRequest>* RequestPtr = AllRequests.Find(RequestId))
|
||||||
@ -265,19 +219,18 @@ bool FDTFluxQueuedRequestManager::CompleteRequest(const FGuid& RequestId, const
|
|||||||
Request = *RequestPtr;
|
Request = *RequestPtr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Request.IsValid())
|
if (!Request.IsValid())
|
||||||
{
|
{
|
||||||
UE_LOG(logDTFluxNetwork, Warning, TEXT("Request %s not found"), *RequestId.ToString());
|
UE_LOG(logDTFluxNetwork, Warning, TEXT("Request %s not found"), *RequestId.ToString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stocker la réponse brute
|
// Store RawResponse
|
||||||
Request->SetRawResponse(RawResponseData);
|
Request->SetRawResponse(RawResponseData);
|
||||||
Request->CompletedAt = FDateTime::Now();
|
Request->CompletedAt = FDateTime::Now();
|
||||||
UE_LOG(logDTFluxNetwork, Log, TEXT("Request %s completed at %s"), *RequestId.ToString(),
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Request %s completed at %s"), *RequestId.ToString(),
|
||||||
*Request->CompletedAt.ToString());
|
*Request->CompletedAt.ToString());
|
||||||
// Décider du parsing selon les callbacks et la configuration
|
// Decide to parse based upon config
|
||||||
bool bHasCallbacks = false;
|
bool bHasCallbacks = false;
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&CallbacksLock);
|
FScopeLock Lock(&CallbacksLock);
|
||||||
@ -292,7 +245,7 @@ bool FDTFluxQueuedRequestManager::CompleteRequest(const FGuid& RequestId, const
|
|||||||
bHasCallbacks ? TEXT("true") : TEXT("false"), bUseAsyncParsing ? TEXT("true") : TEXT("false"),
|
bHasCallbacks ? TEXT("true") : TEXT("false"), bUseAsyncParsing ? TEXT("true") : TEXT("false"),
|
||||||
RawResponseData.IsEmpty() ? TEXT("true") : TEXT("false"));
|
RawResponseData.IsEmpty() ? TEXT("true") : TEXT("false"));
|
||||||
|
|
||||||
// Parsing asynchrone pour les callbacks
|
// Async parsing for Cb
|
||||||
FOnParsingCompleted OnCompleted = FOnParsingCompleted::CreateRaw(
|
FOnParsingCompleted OnCompleted = FOnParsingCompleted::CreateRaw(
|
||||||
this, &FDTFluxQueuedRequestManager::OnParsingCompleted
|
this, &FDTFluxQueuedRequestManager::OnParsingCompleted
|
||||||
);
|
);
|
||||||
@ -300,9 +253,8 @@ bool FDTFluxQueuedRequestManager::CompleteRequest(const FGuid& RequestId, const
|
|||||||
FOnParsingFailed OnFailed = FOnParsingFailed::CreateRaw(
|
FOnParsingFailed OnFailed = FOnParsingFailed::CreateRaw(
|
||||||
this, &FDTFluxQueuedRequestManager::OnParsingFailed
|
this, &FDTFluxQueuedRequestManager::OnParsingFailed
|
||||||
);
|
);
|
||||||
|
// Maybe send to parser in another place
|
||||||
AsyncParser->ParseResponseAsync(RequestId, RawResponseData, OnCompleted, OnFailed);
|
AsyncParser->ParseResponseAsync(RequestId, RawResponseData, OnCompleted, OnFailed);
|
||||||
// CleanupCallbacks(RequestId);
|
|
||||||
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Started async parsing for request %s"), *RequestId.ToString());
|
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Started async parsing for request %s"), *RequestId.ToString());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -310,26 +262,21 @@ bool FDTFluxQueuedRequestManager::CompleteRequest(const FGuid& RequestId, const
|
|||||||
{
|
{
|
||||||
UE_LOG(logDTFluxNetwork, Warning, TEXT("request %s completed without sync"), *RequestId.ToString());
|
UE_LOG(logDTFluxNetwork, Warning, TEXT("request %s completed without sync"), *RequestId.ToString());
|
||||||
// Compléter immédiatement sans parsing ou avec parsing sync
|
// Compléter immédiatement sans parsing ou avec parsing sync
|
||||||
EDTFluxRequestState NewState = Request->Config.bEnableCache
|
EDTFluxRequestState NewState = EDTFluxRequestState::Completed;
|
||||||
? EDTFluxRequestState::Cached
|
|
||||||
: EDTFluxRequestState::Completed;
|
|
||||||
|
|
||||||
ChangeRequestState(Request, NewState);
|
ChangeRequestState(Request, NewState);
|
||||||
|
|
||||||
if (Request->Config.bEnableCache)
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
CacheKeyToRequestId.Add(Request->GetCacheKey(), RequestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Déclencher les callbacks avec les données brutes
|
// Déclencher les callbacks avec les données brutes
|
||||||
TriggerCallbacks(*Request);
|
TriggerCallbacks(*Request);
|
||||||
CleanupCallbacks(RequestId);
|
CleanupCallbacks(RequestId);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo Check protocol errors ???
|
||||||
|
* @param RequestId
|
||||||
|
* @param ErrorMessage
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
bool FDTFluxQueuedRequestManager::FailRequest(const FGuid& RequestId, const FString& ErrorMessage)
|
bool FDTFluxQueuedRequestManager::FailRequest(const FGuid& RequestId, const FString& ErrorMessage)
|
||||||
{
|
{
|
||||||
TSharedPtr<FDTFluxTrackedRequest> Request;
|
TSharedPtr<FDTFluxTrackedRequest> Request;
|
||||||
@ -350,7 +297,6 @@ bool FDTFluxQueuedRequestManager::FailRequest(const FGuid& RequestId, const FStr
|
|||||||
Request->LastErrorMessage = ErrorMessage;
|
Request->LastErrorMessage = ErrorMessage;
|
||||||
ChangeRequestState(Request, EDTFluxRequestState::Failed);
|
ChangeRequestState(Request, EDTFluxRequestState::Failed);
|
||||||
|
|
||||||
// Déclencher les callbacks d'erreur
|
|
||||||
TriggerCallbacks(*Request);
|
TriggerCallbacks(*Request);
|
||||||
CleanupCallbacks(RequestId);
|
CleanupCallbacks(RequestId);
|
||||||
|
|
||||||
@ -407,76 +353,6 @@ bool FDTFluxQueuedRequestManager::FindPendingRequest(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FDTFluxQueuedRequestManager::GetFromCache(
|
|
||||||
EDTFluxApiDataType RequestType,
|
|
||||||
FString& OutRawResponse,
|
|
||||||
int32 ContestId,
|
|
||||||
int32 StageId,
|
|
||||||
int32 SplitId) const
|
|
||||||
{
|
|
||||||
FString CacheKey = GenerateCacheKey(RequestType, ContestId, StageId, SplitId);
|
|
||||||
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
|
|
||||||
if (const FGuid* RequestId = CacheKeyToRequestId.Find(CacheKey))
|
|
||||||
{
|
|
||||||
if (const TSharedPtr<FDTFluxTrackedRequest>* RequestPtr = AllRequests.Find(*RequestId))
|
|
||||||
{
|
|
||||||
const TSharedPtr<FDTFluxTrackedRequest>& CachedRequest = *RequestPtr;
|
|
||||||
|
|
||||||
if (CachedRequest->IsCacheValid() && !CachedRequest->RawResponseData.IsEmpty())
|
|
||||||
{
|
|
||||||
OutRawResponse = CachedRequest->RawResponseData;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FDTFluxQueuedRequestManager::GetParsedFromCache(
|
|
||||||
EDTFluxApiDataType RequestType,
|
|
||||||
TSharedPtr<FDTFluxServerResponse>& OutResponse,
|
|
||||||
int32 ContestId,
|
|
||||||
int32 StageId,
|
|
||||||
int32 SplitId) const
|
|
||||||
{
|
|
||||||
FString CacheKey = GenerateCacheKey(RequestType, ContestId, StageId, SplitId);
|
|
||||||
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
|
|
||||||
if (const FGuid* RequestId = CacheKeyToRequestId.Find(CacheKey))
|
|
||||||
{
|
|
||||||
if (const TSharedPtr<FDTFluxTrackedRequest>* RequestPtr = AllRequests.Find(*RequestId))
|
|
||||||
{
|
|
||||||
const TSharedPtr<FDTFluxTrackedRequest>& CachedRequest = *RequestPtr;
|
|
||||||
|
|
||||||
if (CachedRequest->IsCacheValid())
|
|
||||||
{
|
|
||||||
// Parsing lazy si nécessaire
|
|
||||||
if (!CachedRequest->ParsedResponse.IsSet() && !CachedRequest->RawResponseData.IsEmpty())
|
|
||||||
{
|
|
||||||
OutResponse = AsyncParser->ParseResponseSync(CachedRequest->RawResponseData, 1.0f);
|
|
||||||
if (OutResponse.IsValid())
|
|
||||||
{
|
|
||||||
CachedRequest->ParsedResponse = OutResponse;
|
|
||||||
const_cast<FDTFluxTrackedRequest*>(CachedRequest.Get())->bIsResponseParsed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (CachedRequest->ParsedResponse.IsSet())
|
|
||||||
{
|
|
||||||
OutResponse = CachedRequest->ParsedResponse.GetValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
return OutResponse.IsValid();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// === ACCESSEURS ===
|
// === ACCESSEURS ===
|
||||||
|
|
||||||
bool FDTFluxQueuedRequestManager::GetRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const
|
bool FDTFluxQueuedRequestManager::GetRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const
|
||||||
@ -554,9 +430,6 @@ FDTFluxQueuedRequestManager::FRequestStatistics FDTFluxQueuedRequestManager::Get
|
|||||||
case EDTFluxRequestState::Retrying:
|
case EDTFluxRequestState::Retrying:
|
||||||
Stats.Pending++;
|
Stats.Pending++;
|
||||||
break;
|
break;
|
||||||
case EDTFluxRequestState::Cached:
|
|
||||||
Stats.Cached++;
|
|
||||||
break;
|
|
||||||
case EDTFluxRequestState::Completed:
|
case EDTFluxRequestState::Completed:
|
||||||
Stats.Completed++;
|
Stats.Completed++;
|
||||||
break;
|
break;
|
||||||
@ -568,46 +441,10 @@ FDTFluxQueuedRequestManager::FRequestStatistics FDTFluxQueuedRequestManager::Get
|
|||||||
}
|
}
|
||||||
|
|
||||||
Stats.TotalRequests = TotalRequests;
|
Stats.TotalRequests = TotalRequests;
|
||||||
Stats.CacheHits = CacheHits;
|
|
||||||
Stats.CacheMisses = CacheMisses;
|
|
||||||
|
|
||||||
if (Stats.TotalRequests > 0)
|
|
||||||
{
|
|
||||||
Stats.HitRate = ((float)Stats.CacheHits / (float)Stats.TotalRequests) * 100.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Stats;
|
return Stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
// === NETTOYAGE ===
|
// === NETTOYAGE ===
|
||||||
|
|
||||||
int32 FDTFluxQueuedRequestManager::CleanupExpiredCache()
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
|
|
||||||
TArray<FGuid> ExpiredRequests;
|
|
||||||
|
|
||||||
for (const auto& [RequestId, Request] : AllRequests)
|
|
||||||
{
|
|
||||||
if (Request->State == EDTFluxRequestState::Cached && !Request->IsCacheValid())
|
|
||||||
{
|
|
||||||
ExpiredRequests.Add(RequestId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const FGuid& RequestId : ExpiredRequests)
|
|
||||||
{
|
|
||||||
if (TSharedPtr<FDTFluxTrackedRequest>* RequestPtr = AllRequests.Find(RequestId))
|
|
||||||
{
|
|
||||||
const TSharedPtr<FDTFluxTrackedRequest>& Request = *RequestPtr;
|
|
||||||
CacheKeyToRequestId.Remove(Request->GetCacheKey());
|
|
||||||
AllRequests.Remove(RequestId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExpiredRequests.Num();
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 FDTFluxQueuedRequestManager::CleanupCompletedRequests(float OlderThanSeconds)
|
int32 FDTFluxQueuedRequestManager::CleanupCompletedRequests(float OlderThanSeconds)
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&RequestsLock);
|
FScopeLock Lock(&RequestsLock);
|
||||||
@ -638,7 +475,6 @@ void FDTFluxQueuedRequestManager::ClearAllRequests()
|
|||||||
FScopeLock CallbacksLock_Local(&CallbacksLock);
|
FScopeLock CallbacksLock_Local(&CallbacksLock);
|
||||||
|
|
||||||
AllRequests.Empty();
|
AllRequests.Empty();
|
||||||
CacheKeyToRequestId.Empty();
|
|
||||||
SuccessCallbacks.Empty();
|
SuccessCallbacks.Empty();
|
||||||
ErrorCallbacks.Empty();
|
ErrorCallbacks.Empty();
|
||||||
|
|
||||||
@ -652,7 +488,6 @@ void FDTFluxQueuedRequestManager::Tick(float DeltaTime)
|
|||||||
|
|
||||||
// Mise à jour des timers
|
// Mise à jour des timers
|
||||||
TimeSinceLastTimeoutCheck += DeltaTime;
|
TimeSinceLastTimeoutCheck += DeltaTime;
|
||||||
TimeSinceLastCacheCleanup += DeltaTime;
|
|
||||||
TimeSinceLastRetryCheck += DeltaTime;
|
TimeSinceLastRetryCheck += DeltaTime;
|
||||||
|
|
||||||
// Vérifier les timeouts
|
// Vérifier les timeouts
|
||||||
@ -668,13 +503,6 @@ void FDTFluxQueuedRequestManager::Tick(float DeltaTime)
|
|||||||
ProcessRetries();
|
ProcessRetries();
|
||||||
TimeSinceLastRetryCheck = 0.0f;
|
TimeSinceLastRetryCheck = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nettoyage du cache
|
|
||||||
if (TimeSinceLastCacheCleanup >= CacheCleanupInterval)
|
|
||||||
{
|
|
||||||
ProcessCacheCleanup();
|
|
||||||
TimeSinceLastCacheCleanup = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::ChangeRequestState(TSharedPtr<FDTFluxTrackedRequest> Request,
|
void FDTFluxQueuedRequestManager::ChangeRequestState(TSharedPtr<FDTFluxTrackedRequest> Request,
|
||||||
@ -756,17 +584,11 @@ void FDTFluxQueuedRequestManager::ProcessRetries()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::ProcessCacheCleanup()
|
|
||||||
{
|
|
||||||
CleanupExpiredCache();
|
|
||||||
CleanupCompletedRequests(600.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::TriggerCallbacks(const FDTFluxTrackedRequest& Request)
|
void FDTFluxQueuedRequestManager::TriggerCallbacks(const FDTFluxTrackedRequest& Request)
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&CallbacksLock);
|
FScopeLock Lock(&CallbacksLock);
|
||||||
|
|
||||||
if (Request.State == EDTFluxRequestState::Completed || Request.State == EDTFluxRequestState::Cached)
|
if (Request.State == EDTFluxRequestState::Completed)
|
||||||
{
|
{
|
||||||
// Success Cb
|
// Success Cb
|
||||||
const FOnDTFluxRequestSuccess* SuccessCallback = SuccessCallbacks.Find(Request.RequestId);
|
const FOnDTFluxRequestSuccess* SuccessCallback = SuccessCallbacks.Find(Request.RequestId);
|
||||||
@ -793,18 +615,6 @@ void FDTFluxQueuedRequestManager::CleanupCallbacks(const FGuid& RequestId)
|
|||||||
ErrorCallbacks.Remove(RequestId);
|
ErrorCallbacks.Remove(RequestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::RecordCacheHit() const
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&MetricsLock);
|
|
||||||
CacheHits++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::RecordCacheMiss() const
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&MetricsLock);
|
|
||||||
CacheMisses++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FDTFluxQueuedRequestManager::OnParsingCompleted(const FGuid& RequestId,
|
void FDTFluxQueuedRequestManager::OnParsingCompleted(const FGuid& RequestId,
|
||||||
TSharedPtr<FDTFluxServerResponse> ParsedResponse, bool bSuccess)
|
TSharedPtr<FDTFluxServerResponse> ParsedResponse, bool bSuccess)
|
||||||
{
|
{
|
||||||
@ -833,16 +643,9 @@ void FDTFluxQueuedRequestManager::OnParsingCompleted(const FGuid& RequestId,
|
|||||||
{
|
{
|
||||||
Request->ParsedResponse = ParsedResponse;
|
Request->ParsedResponse = ParsedResponse;
|
||||||
Request->bIsResponseParsed = true;
|
Request->bIsResponseParsed = true;
|
||||||
EDTFluxRequestState NewState = Request->Config.bEnableCache
|
EDTFluxRequestState NewState = EDTFluxRequestState::Completed;
|
||||||
? EDTFluxRequestState::Cached
|
|
||||||
: EDTFluxRequestState::Completed;
|
|
||||||
|
|
||||||
ChangeRequestState(Request, NewState);
|
ChangeRequestState(Request, NewState);
|
||||||
if (Request->Config.bEnableCache)
|
|
||||||
{
|
|
||||||
FScopeLock Lock(&RequestsLock);
|
|
||||||
CacheKeyToRequestId.Add(Request->GetCacheKey(), RequestId);
|
|
||||||
}
|
|
||||||
UE_LOG(logDTFluxNetwork, Log,
|
UE_LOG(logDTFluxNetwork, Log,
|
||||||
TEXT("DTFluxQueuedRequestManager: Async parsing completed for request %s"),
|
TEXT("DTFluxQueuedRequestManager: Async parsing completed for request %s"),
|
||||||
*RequestId.ToString());
|
*RequestId.ToString());
|
||||||
@ -854,7 +657,6 @@ void FDTFluxQueuedRequestManager::OnParsingCompleted(const FGuid& RequestId,
|
|||||||
|
|
||||||
UE_LOG(logDTFluxNetwork, Error, TEXT("Async parsing failed for request %s"), *RequestId.ToString());
|
UE_LOG(logDTFluxNetwork, Error, TEXT("Async parsing failed for request %s"), *RequestId.ToString());
|
||||||
}
|
}
|
||||||
// ✅ FIX: Déclencher les callbacks maintenant !
|
|
||||||
TriggerCallbacks(*Request);
|
TriggerCallbacks(*Request);
|
||||||
CleanupCallbacks(RequestId);
|
CleanupCallbacks(RequestId);
|
||||||
}
|
}
|
||||||
@ -866,13 +668,3 @@ void FDTFluxQueuedRequestManager::OnParsingFailed(const FGuid& RequestId, const
|
|||||||
*ErrorMessage);
|
*ErrorMessage);
|
||||||
FailRequest(RequestId, FString::Printf(TEXT("Parsing failed: %s"), *ErrorMessage));
|
FailRequest(RequestId, FString::Printf(TEXT("Parsing failed: %s"), *ErrorMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
FString FDTFluxQueuedRequestManager::GenerateCacheKey(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId,
|
|
||||||
int32 SplitId)
|
|
||||||
{
|
|
||||||
return FString::Printf(TEXT("%s_%d_%d_%d"),
|
|
||||||
*UEnum::GetValueAsString(RequestType),
|
|
||||||
ContestId,
|
|
||||||
StageId,
|
|
||||||
SplitId);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -36,8 +36,6 @@ void UDTFluxNetworkSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|||||||
DefaultConfig.TimeoutSeconds = 5.0f;
|
DefaultConfig.TimeoutSeconds = 5.0f;
|
||||||
DefaultConfig.MaxRetries = 3;
|
DefaultConfig.MaxRetries = 3;
|
||||||
DefaultConfig.RetryBackoffMultiplier = 1.5f;
|
DefaultConfig.RetryBackoffMultiplier = 1.5f;
|
||||||
DefaultConfig.bEnableCache = true;
|
|
||||||
DefaultConfig.CacheValiditySeconds = 60.0f;
|
|
||||||
|
|
||||||
RequestManager->Initialize(DefaultConfig);
|
RequestManager->Initialize(DefaultConfig);
|
||||||
|
|
||||||
@ -143,8 +141,6 @@ FGuid UDTFluxNetworkSubsystem::SendTrackedRequest(
|
|||||||
FDTFluxRequestConfig CustomConfig;
|
FDTFluxRequestConfig CustomConfig;
|
||||||
CustomConfig.TimeoutSeconds = TimeoutSeconds;
|
CustomConfig.TimeoutSeconds = TimeoutSeconds;
|
||||||
CustomConfig.MaxRetries = MaxRetries;
|
CustomConfig.MaxRetries = MaxRetries;
|
||||||
CustomConfig.bEnableCache = bEnableCache;
|
|
||||||
CustomConfig.CacheValiditySeconds = 60.0f;
|
|
||||||
CustomConfig.RetryBackoffMultiplier = 1.5f;
|
CustomConfig.RetryBackoffMultiplier = 1.5f;
|
||||||
|
|
||||||
FGuid RequestId = RequestManager->CreateTrackedRequest(RequestType, ContestId, StageId, SplitId, CustomConfig);
|
FGuid RequestId = RequestManager->CreateTrackedRequest(RequestType, ContestId, StageId, SplitId, CustomConfig);
|
||||||
@ -153,15 +149,11 @@ FGuid UDTFluxNetworkSubsystem::SendTrackedRequest(
|
|||||||
{
|
{
|
||||||
// Récupérer la requête pour l'envoyer
|
// Récupérer la requête pour l'envoyer
|
||||||
if (const FDTFluxTrackedRequest* Request = RequestManager->GetRequestPtr(RequestId))
|
if (const FDTFluxTrackedRequest* Request = RequestManager->GetRequestPtr(RequestId))
|
||||||
{
|
|
||||||
// Si la requête est déjà en cache, pas besoin d'envoyer
|
|
||||||
if (Request->State != EDTFluxRequestState::Cached)
|
|
||||||
{
|
{
|
||||||
RequestManager->MarkRequestAsSent(RequestId);
|
RequestManager->MarkRequestAsSent(RequestId);
|
||||||
SendQueuedRequest(*Request);
|
SendQueuedRequest(*Request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return RequestId;
|
return RequestId;
|
||||||
}
|
}
|
||||||
@ -174,8 +166,8 @@ FGuid UDTFluxNetworkSubsystem::SendTrackedRequestWithCallbacks(
|
|||||||
FOnDTFluxRequestSuccess& OnSuccess,
|
FOnDTFluxRequestSuccess& OnSuccess,
|
||||||
FOnDTFluxRequestError& OnError,
|
FOnDTFluxRequestError& OnError,
|
||||||
float TimeoutSeconds,
|
float TimeoutSeconds,
|
||||||
int32 MaxRetries,
|
int32 MaxRetries
|
||||||
bool bEnableCache)
|
)
|
||||||
{
|
{
|
||||||
if (!RequestManager.IsValid())
|
if (!RequestManager.IsValid())
|
||||||
{
|
{
|
||||||
@ -186,8 +178,6 @@ FGuid UDTFluxNetworkSubsystem::SendTrackedRequestWithCallbacks(
|
|||||||
FDTFluxRequestConfig CustomConfig;
|
FDTFluxRequestConfig CustomConfig;
|
||||||
CustomConfig.TimeoutSeconds = TimeoutSeconds;
|
CustomConfig.TimeoutSeconds = TimeoutSeconds;
|
||||||
CustomConfig.MaxRetries = MaxRetries;
|
CustomConfig.MaxRetries = MaxRetries;
|
||||||
CustomConfig.bEnableCache = bEnableCache;
|
|
||||||
CustomConfig.CacheValiditySeconds = 60.0f;
|
|
||||||
CustomConfig.RetryBackoffMultiplier = 1.5f;
|
CustomConfig.RetryBackoffMultiplier = 1.5f;
|
||||||
|
|
||||||
FGuid RequestId = RequestManager->CreateTrackedRequestWithCallbacks(
|
FGuid RequestId = RequestManager->CreateTrackedRequestWithCallbacks(
|
||||||
@ -196,14 +186,11 @@ FGuid UDTFluxNetworkSubsystem::SendTrackedRequestWithCallbacks(
|
|||||||
if (RequestId.IsValid())
|
if (RequestId.IsValid())
|
||||||
{
|
{
|
||||||
if (const FDTFluxTrackedRequest* Request = RequestManager->GetRequestPtr(RequestId))
|
if (const FDTFluxTrackedRequest* Request = RequestManager->GetRequestPtr(RequestId))
|
||||||
{
|
|
||||||
if (Request->State != EDTFluxRequestState::Cached)
|
|
||||||
{
|
{
|
||||||
RequestManager->MarkRequestAsSent(RequestId);
|
RequestManager->MarkRequestAsSent(RequestId);
|
||||||
SendQueuedRequest(*Request);
|
SendQueuedRequest(*Request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return RequestId;
|
return RequestId;
|
||||||
}
|
}
|
||||||
@ -227,7 +214,6 @@ bool UDTFluxNetworkSubsystem::HasRequestReceivedResponse(const FGuid& RequestId)
|
|||||||
if (GetTrackedRequest(RequestId, Request))
|
if (GetTrackedRequest(RequestId, Request))
|
||||||
{
|
{
|
||||||
return Request.State == EDTFluxRequestState::Completed ||
|
return Request.State == EDTFluxRequestState::Completed ||
|
||||||
Request.State == EDTFluxRequestState::Cached ||
|
|
||||||
!Request.RawResponseData.IsEmpty();
|
!Request.RawResponseData.IsEmpty();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -264,22 +250,19 @@ int32 UDTFluxNetworkSubsystem::GetPendingRequestCount() const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxNetworkSubsystem::GetRequestStatistics(int32& OutPending, int32& OutCached, int32& OutCompleted,
|
void UDTFluxNetworkSubsystem::GetRequestStatistics(int32& OutPending, int32& OutCompleted,
|
||||||
int32& OutFailed, float& OutHitRate) const
|
int32& OutFailed) const
|
||||||
{
|
{
|
||||||
if (RequestManager.IsValid())
|
if (RequestManager.IsValid())
|
||||||
{
|
{
|
||||||
FDTFluxQueuedRequestManager::FRequestStatistics Stats = RequestManager->GetStatistics();
|
FDTFluxQueuedRequestManager::FRequestStatistics Stats = RequestManager->GetStatistics();
|
||||||
OutPending = Stats.Pending;
|
OutPending = Stats.Pending;
|
||||||
OutCached = Stats.Cached;
|
|
||||||
OutCompleted = Stats.Completed;
|
OutCompleted = Stats.Completed;
|
||||||
OutFailed = Stats.Failed;
|
OutFailed = Stats.Failed;
|
||||||
OutHitRate = Stats.HitRate;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OutPending = OutCached = OutCompleted = OutFailed = 0;
|
OutPending = OutCompleted = OutFailed = 0;
|
||||||
OutHitRate = 0.0f;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +345,7 @@ void UDTFluxNetworkSubsystem::RegisterWebSocketEvents()
|
|||||||
&UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem);
|
&UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDTFluxNetworkSubsystem::UnregisterWebSocketEvents()
|
void UDTFluxNetworkSubsystem::UnregisterWebSocketEvents() const
|
||||||
{
|
{
|
||||||
if (!WsClient.IsValid()) return;
|
if (!WsClient.IsValid()) return;
|
||||||
|
|
||||||
@ -794,11 +777,10 @@ void UDTFluxNetworkSubsystem::SendQueuedRequest(const FDTFluxTrackedRequest& Que
|
|||||||
|
|
||||||
bool UDTFluxNetworkSubsystem::ShouldUseAsyncParsing(const FString& JsonData) const
|
bool UDTFluxNetworkSubsystem::ShouldUseAsyncParsing(const FString& JsonData) const
|
||||||
{
|
{
|
||||||
// Critères pour décider du parsing asynchrone :
|
// Critère pour décider du parsing asynchrone :
|
||||||
// - Taille des données (> 1KB par défaut)
|
// - Taille des données (> 1KB par défaut)
|
||||||
// - Charge actuelle du système
|
// Pour le moment uniquement taille
|
||||||
// - Type de données (certains types sont plus complexes à parser)
|
|
||||||
|
|
||||||
const int32 AsyncThreshold = 1024; // 1KB
|
constexpr int32 AsyncThreshold = 1024; // 1KB
|
||||||
return JsonData.Len() > AsyncThreshold;
|
return JsonData.Len() > AsyncThreshold;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,6 @@ enum class EDTFluxRequestState : uint8
|
|||||||
Completed UMETA(DisplayName = "Completed"),
|
Completed UMETA(DisplayName = "Completed"),
|
||||||
Failed UMETA(DisplayName = "Failed"),
|
Failed UMETA(DisplayName = "Failed"),
|
||||||
TimedOut UMETA(DisplayName = "TimedOut"),
|
TimedOut UMETA(DisplayName = "TimedOut"),
|
||||||
Cached UMETA(DisplayName = "Cached"),
|
|
||||||
Retrying UMETA(DisplayName = "Retrying")
|
Retrying UMETA(DisplayName = "Retrying")
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,12 +42,6 @@ struct DTFLUXNETWORK_API FDTFluxRequestConfig
|
|||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||||
float RetryBackoffMultiplier = 1.5f;
|
float RetryBackoffMultiplier = 1.5f;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
|
||||||
bool bEnableCache = true;
|
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
|
||||||
float CacheValiditySeconds = 60.0f;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
USTRUCT(BlueprintType)
|
||||||
@ -112,10 +105,8 @@ struct DTFLUXNETWORK_API FDTFluxTrackedRequest
|
|||||||
|
|
||||||
bool HasTimedOut() const;
|
bool HasTimedOut() const;
|
||||||
bool CanRetry() const;
|
bool CanRetry() const;
|
||||||
bool IsCacheValid() const;
|
|
||||||
float GetRetryDelay() const;
|
float GetRetryDelay() const;
|
||||||
bool Matches(EDTFluxApiDataType InType, int32 InContestId = -1, int32 InStageId = -1, int32 InSplitId = -1) const;
|
bool Matches(EDTFluxApiDataType InType, int32 InContestId = -1, int32 InStageId = -1, int32 InSplitId = -1) const;
|
||||||
FString GetCacheKey() const;
|
|
||||||
void SetRawResponse(const FString& RawData);
|
void SetRawResponse(const FString& RawData);
|
||||||
FString Serialize() const;
|
FString Serialize() const;
|
||||||
};
|
};
|
||||||
@ -137,7 +128,7 @@ DECLARE_MULTICAST_DELEGATE_OneParam(FOnRequestFailedNative, const FDTFluxTracked
|
|||||||
// ================================================================================================
|
// ================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gestionnaire de requêtes trackées avec cache, timeout, retry et parsing asynchrone
|
* Gestionnaire de requêtes trackées timeout, retry et parsing asynchrone
|
||||||
* Implémentation C++ pure avec SmartPointers pour des performances optimales
|
* Implémentation C++ pure avec SmartPointers pour des performances optimales
|
||||||
*/
|
*/
|
||||||
class DTFLUXNETWORK_API FDTFluxQueuedRequestManager : public FTickableGameObject
|
class DTFLUXNETWORK_API FDTFluxQueuedRequestManager : public FTickableGameObject
|
||||||
@ -229,7 +220,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool RetryRequest(const FGuid& RequestId);
|
bool RetryRequest(const FGuid& RequestId);
|
||||||
|
|
||||||
// === RECHERCHE ET CACHE ===
|
// === RECHERCHE ===
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chercher une requête en attente correspondant aux critères
|
* Chercher une requête en attente correspondant aux critères
|
||||||
@ -242,28 +233,6 @@ public:
|
|||||||
int32 SplitId = -1
|
int32 SplitId = -1
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupérer une réponse depuis le cache (données brutes)
|
|
||||||
*/
|
|
||||||
bool GetFromCache(
|
|
||||||
EDTFluxApiDataType RequestType,
|
|
||||||
FString& OutRawResponse,
|
|
||||||
int32 ContestId = -1,
|
|
||||||
int32 StageId = -1,
|
|
||||||
int32 SplitId = -1
|
|
||||||
) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupérer une réponse parsée depuis le cache
|
|
||||||
*/
|
|
||||||
bool GetParsedFromCache(
|
|
||||||
EDTFluxApiDataType RequestType,
|
|
||||||
TSharedPtr<FDTFluxServerResponse>& OutResponse,
|
|
||||||
int32 ContestId = -1,
|
|
||||||
int32 StageId = -1,
|
|
||||||
int32 SplitId = -1
|
|
||||||
) const;
|
|
||||||
|
|
||||||
// === ACCESSEURS ===
|
// === ACCESSEURS ===
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -294,25 +263,15 @@ public:
|
|||||||
struct FRequestStatistics
|
struct FRequestStatistics
|
||||||
{
|
{
|
||||||
int32 Pending = 0;
|
int32 Pending = 0;
|
||||||
int32 Cached = 0;
|
|
||||||
int32 Completed = 0;
|
int32 Completed = 0;
|
||||||
int32 Failed = 0;
|
int32 Failed = 0;
|
||||||
int32 TotalRequests = 0;
|
int32 TotalRequests = 0;
|
||||||
int32 CacheHits = 0;
|
|
||||||
int32 CacheMisses = 0;
|
|
||||||
float HitRate = 0.0f;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FRequestStatistics GetStatistics() const;
|
FRequestStatistics GetStatistics() const;
|
||||||
|
|
||||||
// === NETTOYAGE ===
|
// === NETTOYAGE ===
|
||||||
|
|
||||||
/**
|
|
||||||
* Nettoyer les entrées de cache expirées
|
|
||||||
* @return Nombre d'entrées supprimées
|
|
||||||
*/
|
|
||||||
int32 CleanupExpiredCache();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nettoyer les requêtes terminées anciennes
|
* Nettoyer les requêtes terminées anciennes
|
||||||
* @param OlderThanSeconds Supprimer les requêtes plus anciennes que ce délai
|
* @param OlderThanSeconds Supprimer les requêtes plus anciennes que ce délai
|
||||||
@ -321,7 +280,7 @@ public:
|
|||||||
int32 CleanupCompletedRequests(float OlderThanSeconds = 300.0f);
|
int32 CleanupCompletedRequests(float OlderThanSeconds = 300.0f);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vider toutes les requêtes et le cache
|
* Vider toutes les requêtes
|
||||||
*/
|
*/
|
||||||
void ClearAllRequests();
|
void ClearAllRequests();
|
||||||
|
|
||||||
@ -353,17 +312,14 @@ private:
|
|||||||
|
|
||||||
// === TIMING POUR LE TICK ===
|
// === TIMING POUR LE TICK ===
|
||||||
float TimeSinceLastTimeoutCheck = 0.0f;
|
float TimeSinceLastTimeoutCheck = 0.0f;
|
||||||
float TimeSinceLastCacheCleanup = 0.0f;
|
|
||||||
float TimeSinceLastRetryCheck = 0.0f;
|
float TimeSinceLastRetryCheck = 0.0f;
|
||||||
|
|
||||||
static constexpr float TimeoutCheckInterval = 1.0f;
|
static constexpr float TimeoutCheckInterval = 1.0f;
|
||||||
static constexpr float CacheCleanupInterval = 30.0f;
|
|
||||||
static constexpr float RetryCheckInterval = 0.5f;
|
static constexpr float RetryCheckInterval = 0.5f;
|
||||||
|
|
||||||
// === STOCKAGE THREAD-SAFE ===
|
// === STOCKAGE THREAD-SAFE ===
|
||||||
mutable FCriticalSection RequestsLock;
|
mutable FCriticalSection RequestsLock;
|
||||||
TMap<FGuid, TSharedPtr<FDTFluxTrackedRequest>> AllRequests;
|
TMap<FGuid, TSharedPtr<FDTFluxTrackedRequest>> AllRequests;
|
||||||
TMap<FString, FGuid> CacheKeyToRequestId;
|
|
||||||
|
|
||||||
// === CALLBACKS C++ ===
|
// === CALLBACKS C++ ===
|
||||||
mutable FCriticalSection CallbacksLock;
|
mutable FCriticalSection CallbacksLock;
|
||||||
@ -373,8 +329,6 @@ private:
|
|||||||
// === MÉTRIQUES ===
|
// === MÉTRIQUES ===
|
||||||
mutable FCriticalSection MetricsLock;
|
mutable FCriticalSection MetricsLock;
|
||||||
mutable int32 TotalRequests = 0;
|
mutable int32 TotalRequests = 0;
|
||||||
mutable int32 CacheHits = 0;
|
|
||||||
mutable int32 CacheMisses = 0;
|
|
||||||
|
|
||||||
|
|
||||||
// === PARSER ASYNCHRONE ===
|
// === PARSER ASYNCHRONE ===
|
||||||
@ -397,11 +351,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
void ProcessRetries();
|
void ProcessRetries();
|
||||||
|
|
||||||
/**
|
|
||||||
* Nettoyer le cache périodiquement
|
|
||||||
*/
|
|
||||||
void ProcessCacheCleanup();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Déclencher les callbacks pour une requête
|
* Déclencher les callbacks pour une requête
|
||||||
*/
|
*/
|
||||||
@ -412,17 +361,6 @@ private:
|
|||||||
*/
|
*/
|
||||||
void CleanupCallbacks(const FGuid& RequestId);
|
void CleanupCallbacks(const FGuid& RequestId);
|
||||||
|
|
||||||
/**
|
|
||||||
* Enregistrer un hit cache dans les métriques
|
|
||||||
*/
|
|
||||||
void RecordCacheHit() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enregistrer un miss cache dans les métriques
|
|
||||||
*/
|
|
||||||
void RecordCacheMiss() const;
|
|
||||||
|
|
||||||
|
|
||||||
// === CALLBACKS POUR LE PARSING ASYNCHRONE ===
|
// === CALLBACKS POUR LE PARSING ASYNCHRONE ===
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -434,11 +372,4 @@ private:
|
|||||||
* Callback appelé quand le parsing asynchrone échoue
|
* Callback appelé quand le parsing asynchrone échoue
|
||||||
*/
|
*/
|
||||||
void OnParsingFailed(const FGuid& RequestId, const FString& ErrorMessage);
|
void OnParsingFailed(const FGuid& RequestId, const FString& ErrorMessage);
|
||||||
|
|
||||||
// === UTILITAIRES STATIQUES ===
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Générer une clé de cache unique pour une requête
|
|
||||||
*/
|
|
||||||
static FString GenerateCacheKey(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId, int32 SplitId);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -125,8 +125,7 @@ public:
|
|||||||
FOnDTFluxRequestSuccess& OnSuccess,
|
FOnDTFluxRequestSuccess& OnSuccess,
|
||||||
FOnDTFluxRequestError& OnError,
|
FOnDTFluxRequestError& OnError,
|
||||||
float TimeoutSeconds = 5.0f,
|
float TimeoutSeconds = 5.0f,
|
||||||
int32 MaxRetries = 3,
|
int32 MaxRetries = 3
|
||||||
bool bEnableCache = true
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// === ACCESSEURS BLUEPRINT POUR LES REQUÊTES TRACKÉES ===
|
// === ACCESSEURS BLUEPRINT POUR LES REQUÊTES TRACKÉES ===
|
||||||
@ -166,8 +165,7 @@ public:
|
|||||||
* Récupérer les statistiques du gestionnaire de requêtes
|
* Récupérer les statistiques du gestionnaire de requêtes
|
||||||
*/
|
*/
|
||||||
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
|
||||||
void GetRequestStatistics(int32& OutPending, int32& OutCached, int32& OutCompleted, int32& OutFailed,
|
void GetRequestStatistics(int32& OutPending, int32& OutCompleted, int32& OutFailed) const;
|
||||||
float& OutHitRate) const;
|
|
||||||
|
|
||||||
// === REQUÊTES LEGACY (Compatibilité totale) ===
|
// === REQUÊTES LEGACY (Compatibilité totale) ===
|
||||||
|
|
||||||
@ -249,7 +247,7 @@ private:
|
|||||||
|
|
||||||
// === GESTION DES ÉVÉNEMENTS WEBSOCKET ===
|
// === GESTION DES ÉVÉNEMENTS WEBSOCKET ===
|
||||||
void RegisterWebSocketEvents();
|
void RegisterWebSocketEvents();
|
||||||
void UnregisterWebSocketEvents();
|
void UnregisterWebSocketEvents() const;
|
||||||
void OnWebSocketConnected_Subsystem();
|
void OnWebSocketConnected_Subsystem();
|
||||||
void OnWebSocketConnectionError_Subsystem(const FString& Error);
|
void OnWebSocketConnectionError_Subsystem(const FString& Error);
|
||||||
void OnWebSocketClosedEvent_Subsystem(int32 StatusCode, const FString& Reason, bool bWasClean);
|
void OnWebSocketClosedEvent_Subsystem(int32 StatusCode, const FString& Reason, bool bWasClean);
|
||||||
|
|||||||
@ -5,15 +5,17 @@
|
|||||||
|
|
||||||
#include "DTFluxCoreSubsystem.h"
|
#include "DTFluxCoreSubsystem.h"
|
||||||
|
|
||||||
FText UFTDFluxUtils::GetFormatedName(const int& Bib, const int MaxChar, const FString OverFlowChar)
|
FText UFTDFluxUtils::GetFormatedName(const int& Bib, const int MaxChar, const FString Separator,
|
||||||
|
const FString OverFlowChar)
|
||||||
{
|
{
|
||||||
UDTFluxCoreSubsystem* CoreSubsystem = GEngine->GetEngineSubsystem<UDTFluxCoreSubsystem>();
|
UDTFluxCoreSubsystem* CoreSubsystem = GEngine->GetEngineSubsystem<UDTFluxCoreSubsystem>();
|
||||||
const FDTFluxParticipant OutParticipant = CoreSubsystem->GetParticipant(Bib);
|
const FDTFluxParticipant OutParticipant = CoreSubsystem->GetParticipant(Bib);
|
||||||
return OutParticipant.GetFormattedNameText(MaxChar, OverFlowChar);
|
return OutParticipant.GetFormattedNameText(MaxChar, Separator, OverFlowChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
FText UFTDFluxUtils::GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar,
|
FText UFTDFluxUtils::GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar,
|
||||||
|
const FString Separator,
|
||||||
const FString OverFlowChar)
|
const FString OverFlowChar)
|
||||||
{
|
{
|
||||||
return Participant.GetFormattedNameText(MaxChar, OverFlowChar);
|
return Participant.GetFormattedNameText(MaxChar, Separator, OverFlowChar);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,9 +17,10 @@ class DTFLUXUTILITIES_API UFTDFluxUtils : public UBlueprintFunctionLibrary
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
||||||
static FText GetFormatedName(const int& Bib, const int MaxChar = 10,
|
static FText GetFormatedName(const int& Bib, const int MaxChar = 10, const FString Separator = ".",
|
||||||
const FString OverFlowChar = "...");
|
const FString OverFlowChar = "...");
|
||||||
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
UFUNCTION(BlueprintCallable, Category="DTFlux|Utils", Meta=(Keywords="name, concat, participant"))
|
||||||
static FText GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar = 10,
|
static FText GetParticipantFormatedName(FDTFluxParticipant& Participant, const int MaxChar = 10,
|
||||||
|
const FString Separator = ".",
|
||||||
const FString OverFlowChar = "...");
|
const FString OverFlowChar = "...");
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user