Compare commits

...

6 Commits

11 changed files with 268 additions and 104 deletions

View File

@ -18,6 +18,15 @@ void UDTFluxModelAsset::AddContest(const FDTFluxContest& Contest)
{ {
FinishedStagesCache.Add(FDTFluxStageKey(Contest.ContestId, Stage.StageId), Stage.IsFinished()); FinishedStagesCache.Add(FDTFluxStageKey(Contest.ContestId, Stage.StageId), Stage.IsFinished());
} }
TArray<FDTFluxSplit> Splits = Contest.Splits;
Splits.Sort([](const FDTFluxSplit& A, const FDTFluxSplit& B)
{
return A.SplitId < B.SplitId;
});
// last and Penultimate split cache for contest
LastSplitIdCache.Add(Contest.ContestId, Splits.Pop().SplitId);
PenultimateSplitIdCache.Add(Contest.ContestId, Splits.Pop().SplitId);
} }
bool UDTFluxModelAsset::GetContestById(const int InContestId, FDTFluxContest& OutContest) bool UDTFluxModelAsset::GetContestById(const int InContestId, FDTFluxContest& OutContest)
@ -151,6 +160,12 @@ bool UDTFluxModelAsset::IsStageFinished(FDTFluxStageKey StageKey)
return false; return false;
} }
void UDTFluxModelAsset::CacheSplitSensorInfo(const FDTFluxSplitSensorKey SplitSensorKey,
const FDTFluxSplitSensorInfo& SplitSensorInfo)
{
SplitSensorInfoCache.Add(SplitSensorKey, SplitSensorInfo);
}
bool UDTFluxModelAsset::CheckStageIsFinished(FDTFluxStageKey StageKey) bool UDTFluxModelAsset::CheckStageIsFinished(FDTFluxStageKey StageKey)
{ {

View File

@ -44,6 +44,15 @@ public:
UPROPERTY(BlueprintReadOnly, EditAnywhere) UPROPERTY(BlueprintReadOnly, EditAnywhere)
TMap<FDTFluxSplitKey, FDTFluxSplitRankings> SplitRankings; TMap<FDTFluxSplitKey, FDTFluxSplitRankings> SplitRankings;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
TMap<FDTFluxSplitSensorKey, FDTFluxSplitSensorInfo> SplitSensorInfoCache;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
TMap<int /*ContestId*/, int /*SplitId*/> LastSplitIdCache;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
TMap<int/*ContestId*/, int /*Penultimate*/>PenultimateSplitIdCache;
UFUNCTION(BlueprintCallable, CallInEditor, Category="DTFlux|ModelAsset") UFUNCTION(BlueprintCallable, CallInEditor, Category="DTFlux|ModelAsset")
void AddContest(const FDTFluxContest& Contest); void AddContest(const FDTFluxContest& Contest);
@ -92,6 +101,8 @@ public:
UFUNCTION() UFUNCTION()
bool IsStageFinished(FDTFluxStageKey StageKey); bool IsStageFinished(FDTFluxStageKey StageKey);
UFUNCTION()
void CacheSplitSensorInfo(const FDTFluxSplitSensorKey SplitSensorKey, const FDTFluxSplitSensorInfo& SplitSensorInfo);
private: private:
UPROPERTY() UPROPERTY()

View File

@ -109,3 +109,61 @@ struct DTFLUXCORE_API FDTFluxSplitKey : public FDTFluxCompositeKey
); );
} }
}; };
USTRUCT(BlueprintType)
struct DTFLUXCORE_API FDTFluxSplitSensorKey : public FDTFluxCompositeKey
{
GENERATED_BODY()
public:
FDTFluxSplitSensorKey() = default;
FDTFluxSplitSensorKey(const int InContestId, const int InStageId, const int InSplitId, const int InBib) :
ContestId(InContestId),
StageId(InStageId),
SplitId(InSplitId),
Bib(InBib){};
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model")
int ContestId = 0;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model")
int StageId = 0;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model")
int SplitId = 0;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="DTFlux|Model")
int Bib = 0;
friend uint32 GetTypeHash(const FDTFluxSplitSensorKey& Key)
{
return HashCombine(
GetTypeHash(Key.ContestId),
GetTypeHash(Key.StageId),
GetTypeHash(Key.SplitId),
GetTypeHash(Key.Bib)
);
}
bool operator==(const FDTFluxSplitSensorKey& Other) const
{
return ContestId == Other.ContestId && StageId == Other.StageId
&& SplitId == Other.SplitId && Bib == Other.Bib;
}
FString GetDisplayName() const
{
return FString::Printf(TEXT("Contest%i | Stage%i | Split%i | Bib%i"), ContestId, StageId, SplitId, Bib);
}
FText GetTooltipText() const
{
return FText::Format(INVTEXT("Contest{0}|Stage{1}|Split{2}"),
FText::AsNumber(ContestId),
FText::AsNumber(StageId),
FText::AsNumber(SplitId),
FText::AsNumber(Bib)
);
}
};

View File

@ -17,7 +17,17 @@ struct FDTFluxSplitSensorInfo
public: public:
FDTFluxSplitSensorInfo() = default; FDTFluxSplitSensorInfo() = default;
FDTFluxSplitSensorInfo(const FString InSplitName):
Bib(-1),
ContestId(-1),
StageId(-1),
SplitId(-1),
Time(""),
Gap("-"),
Rank(-1),
SplitName(InSplitName)
{
};
UPROPERTY(BlueprintReadOnly, VisibleAnywhere) UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
int Bib = -1; int Bib = -1;
UPROPERTY(BlueprintReadOnly, VisibleAnywhere) UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
@ -32,4 +42,22 @@ public:
FString Gap = "-"; FString Gap = "-";
UPROPERTY(BlueprintReadOnly, VisibleAnywhere) UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
int Rank = -1; int Rank = -1;
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
FString SplitName = "";
};
USTRUCT(BlueprintType)
struct FDTFluxSplitHistory
{
GENERATED_BODY()
public:
FDTFluxSplitHistory() = default;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
FDTFluxParticipant Participant = FDTFluxParticipant();
UPROPERTY(BlueprintReadOnly, EditAnywhere)
TArray<FDTFluxSplitSensorInfo> SplitSensors = TArray<FDTFluxSplitSensorInfo>();
}; };

View File

@ -224,13 +224,38 @@ bool UDTFluxCoreSubsystem::IsContestRankingSealed(int ContestId)
return false; return false;
} }
EDTFluxFinisherType UDTFluxCoreSubsystem::GetSplitSensorType(const FDTFluxSplitSensorInfo& SplitSensorInfo)
{
if (DataStorage != nullptr)
{
if (DataStorage->LastSplitIdCache.Contains(SplitSensorInfo.ContestId))
{
int LastSplitIdForContest = DataStorage->LastSplitIdCache[SplitSensorInfo.ContestId];
if (LastSplitIdForContest == SplitSensorInfo.SplitId)
{
if (SplitSensorInfo.Rank == 1 )
{
return EDTFluxFinisherType::Winner;
}
return EDTFluxFinisherType::Finish;
}
return EDTFluxFinisherType::None;
}
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("LastSplitIdCache not found for ContestId %i"), SplitSensorInfo.ContestId);
}
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage not available"));
return EDTFluxFinisherType::None;
}
void UDTFluxCoreSubsystem::ProcessRaceData(const FDTFluxRaceData& RaceDataDefinition) void UDTFluxCoreSubsystem::ProcessRaceData(const FDTFluxRaceData& RaceDataDefinition)
{ {
if (RaceDataDefinition.Datas.Num() > 0) if (RaceDataDefinition.Datas.Num() > 0)
{ {
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Receiving RaceDataDefinition [%s]"), // UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Receiving RaceDataDefinition [%s]"),
*RaceDataDefinition.Datas[0].Name); // *RaceDataDefinition.Datas[0].Name);
if (DataStorage != nullptr) if (DataStorage != nullptr)
{ {
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage Name %s"), *DataStorage->EventName); UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DataStorage Name %s"), *DataStorage->EventName);
@ -307,17 +332,49 @@ void UDTFluxCoreSubsystem::ProcessTeamUpdate(const FDTFluxTeamListDefinition& Te
void UDTFluxCoreSubsystem::ProcessSplitSensor(const FDTFluxSplitSensorInfo& SplitSensorInfo) void UDTFluxCoreSubsystem::ProcessSplitSensor(const FDTFluxSplitSensorInfo& SplitSensorInfo)
{ {
FDTFluxContest Contest;
FDTFluxStageKey StageKey(SplitSensorInfo.ContestId, SplitSensorInfo.StageId);
FDTFluxStage Stage;
DataStorage->GetStage(StageKey, Stage);
FDTFluxParticipant Participant;
DataStorage->GetParticipantByBib(SplitSensorInfo.Bib, Participant);
DataStorage->GetContestById(SplitSensorInfo.ContestId, Contest); if (DataStorage != nullptr)
UE_LOG(logDTFluxCoreSubsystem, Log, TEXT("%s|%s Split %i Sensor for Participant [Bib] %i [FullName] %s"), {
*Contest.Name, *Stage.Name, FString DebugString = FString::Printf(TEXT("Received SplitSensorInfo for Bib %i"), SplitSensorInfo.Bib);
SplitSensorInfo.SplitId, SplitSensorInfo.Bib, *Participant.GetFormattedName()); DebugString += FString::Printf(TEXT("ContestId[%i] StageId[%i] SplitId[%i] Time[%s], Gap[%s] Rank[%i]"),
SplitSensorInfo.ContestId, SplitSensorInfo.StageId, SplitSensorInfo.SplitId, *SplitSensorInfo.Time,
*SplitSensorInfo.Gap, SplitSensorInfo.Rank);
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Received SplitSensorInfo :\n%s"), *DebugString)
// Gestion Cache Split Sensor
FDTFluxSplitSensorKey SplitSensorKey(SplitSensorInfo.ContestId, SplitSensorInfo.StageId, SplitSensorInfo.SplitId, -1);
FDTFluxSplitSensorInfo NewSplitSensorInfo = SplitSensorInfo;
if (DataStorage->SplitSensorInfoCache.Contains(SplitSensorKey))
{
NewSplitSensorInfo.SplitName = DataStorage->SplitSensorInfoCache[SplitSensorKey].SplitName;
}
SplitSensorKey.Bib = SplitSensorInfo.Bib;
DataStorage->SplitSensorInfoCache.Add(SplitSensorKey, NewSplitSensorInfo);
// Update Current currentSplit
FDTFluxParticipant Participant;
if (DataStorage->Participants.Contains(SplitSensorInfo.Bib))
{
DataStorage->Participants[SplitSensorInfo.Bib].CurrentSplit = SplitSensorInfo.SplitId;
}
// Gestion Finnish Status
switch (GetSplitSensorType(SplitSensorInfo))
{
case EDTFluxFinisherType::Winner:
{
OnWinner.Broadcast(SplitSensorInfo);
break;
}
case EDTFluxFinisherType::Finish :
{
OnFinisher.Broadcast(SplitSensorInfo);
break;
}
default:
{
OnSplitSensor.Broadcast(SplitSensorInfo);
break;
}
}
}
} }
void UDTFluxCoreSubsystem::SendRequest(const FString& Message) void UDTFluxCoreSubsystem::SendRequest(const FString& Message)
@ -328,13 +385,51 @@ void UDTFluxCoreSubsystem::SendRequest(const FString& Message)
} }
} }
void UDTFluxCoreSubsystem::InitParticipantTracking(const int Bib, const int ContestId, const int StageId)
{
FDTFluxContest Contest;
if (GetContestForId(ContestId, Contest))
{
// get all splits
TArray<FDTFluxSplitSensorInfo> SplitSensorInfos;
FDTFluxSplitSensorKey SplitSensorKey;
SplitSensorKey.ContestId = ContestId;
SplitSensorKey.StageId = StageId;
SplitSensorKey.Bib = Bib;
for (auto Split : Contest.Splits)
{
SplitSensorKey.SplitId = Split.SplitId;
if (DataStorage->SplitSensorInfoCache.Contains(SplitSensorKey))
{
SplitSensorInfos.Add(DataStorage->SplitSensorInfoCache[SplitSensorKey]);
}
else
{
SplitSensorInfos.Add(FDTFluxSplitSensorInfo(Split.Name));
}
}
FDTFluxSplitHistory History;
History.SplitSensors = SplitSensorInfos;
OnParticipantTrackingReady.Broadcast(History);
}
FDTFluxSplitHistory SplitHistory;
if (GetParticipant(Bib, SplitHistory.Participant))
{
}
FString Text = "sqfhds";
FName Key = FName(Text);
}
FGuid UDTFluxCoreSubsystem::InitContestRankingsDisplay(const int ContestId) FGuid UDTFluxCoreSubsystem::InitContestRankingsDisplay(const int ContestId)
{ {
if (NetworkSubsystem) if (NetworkSubsystem)
{ {
if (DataStorage) if (DataStorage)
{ {
// no need to request StageRankings; // no need to request ContestRankings;
if (IsContestRankingSealed(ContestId)) if (IsContestRankingSealed(ContestId))
{ {
const FGuid DisplayRequestId = FGuid::NewGuid(); const FGuid DisplayRequestId = FGuid::NewGuid();
@ -349,12 +444,14 @@ FGuid UDTFluxCoreSubsystem::InitContestRankingsDisplay(const int ContestId)
FDTFluxContestRankings Rankings = FDTFluxContestRankings(); FDTFluxContestRankings Rankings = FDTFluxContestRankings();
if (Request.ParsedResponse.IsSet()) if (Request.ParsedResponse.IsSet())
{ {
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Request IsSet()"));
TSharedPtr<FDTFluxServerResponse> ResponsePtr = Request.ParsedResponse.GetValue(); TSharedPtr<FDTFluxServerResponse> ResponsePtr = Request.ParsedResponse.GetValue();
ResponsePtr->ParseContestRanking(Rankings); ResponsePtr->ParseContestRanking(Rankings);
this->DataStorage->AddContestRanking(Rankings); this->DataStorage->AddContestRanking(Rankings);
this->OnContestRankingDisplayReady.Broadcast(Request.RequestId, true); this->OnContestRankingDisplayReady.Broadcast(Request.RequestId, true);
return; return;
} }
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("Request IsSet(FALSE)"));
this->OnStageRankingDisplayReady.Broadcast(Request.RequestId, false); this->OnStageRankingDisplayReady.Broadcast(Request.RequestId, false);
}); });
FOnDTFluxRequestError OnError = FOnDTFluxRequestError::CreateLambda( FOnDTFluxRequestError OnError = FOnDTFluxRequestError::CreateLambda(
@ -367,7 +464,7 @@ FGuid UDTFluxCoreSubsystem::InitContestRankingsDisplay(const int ContestId)
return DisplayRequestId; return DisplayRequestId;
} }
} }
UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DTFluxDatastorage unavailable ...")); UE_LOG(logDTFluxCoreSubsystem, Error, TEXT("DTFluxDataStorage unavailable ..."));
OnContestRankingDisplayReady.Broadcast(FGuid(), false); OnContestRankingDisplayReady.Broadcast(FGuid(), false);
return FGuid(); return FGuid();
} }
@ -540,6 +637,7 @@ bool UDTFluxCoreSubsystem::GetContestRankings(const int ContestId,
} }
if (NetworkSubsystem) if (NetworkSubsystem)
{ {
UE_LOG(logDTFluxCoreSubsystem, Warning, TEXT("Requesting ContestRankings for ContestId %i"), ContestId);
TArray<int> TackedContestIds = {ContestId}; TArray<int> TackedContestIds = {ContestId};
TrackedRequestContestRankings(TackedContestIds); TrackedRequestContestRankings(TackedContestIds);
return false; return false;

View File

@ -80,24 +80,37 @@ public:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSplitSensor, FDTFluxSplitSensorInfo, SplitSensorInfo); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSplitSensor, FDTFluxSplitSensorInfo, SplitSensorInfo);
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem") UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
FOnSplitSensor OnSplitSensor; FOnSplitSensor OnSplitSensor;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFinisher, FDTFluxSplitSensorInfo, SplitSensorInfo); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFinisher, FDTFluxSplitSensorInfo, SplitSensorInfo);
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem") UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
FOnFinisher OnFinisher; FOnFinisher OnFinisher;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPreFinish, FDTFluxSplitSensorInfo, SplitSensorInfo);
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
FOnPreFinish OnPreFinish;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWinner, FDTFluxSplitSensorInfo, SplitSensorInfo); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWinner, FDTFluxSplitSensorInfo, SplitSensorInfo);
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem") UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
FOnWinner OnWinner; FOnWinner OnWinner;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnParticipantTrackingReady, FDTFluxSplitHistory, SplitHistory);
UPROPERTY(BlueprintAssignable, Category="DTFlux|Core Subsystem")
FOnParticipantTrackingReady OnParticipantTrackingReady;
//TODO : this must be a ProjectSetting //TODO : this must be a ProjectSetting
UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem") UPROPERTY(BlueprintReadOnly, Category="DTFlux|Core Subsystem")
bool bShouldKeepRankings = true; bool bShouldKeepRankings = true;
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
void InitParticipantTracking(const int Bib, const int ContestId, const int StageId);
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem") UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
FGuid InitContestRankingsDisplay(const int ContestIds); FGuid InitContestRankingsDisplay(const int ContestIds);
@ -108,6 +121,7 @@ public:
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem") UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
FGuid InitSplitRankingsDisplay(const int ContestId, const int StageId, const int SplitId); FGuid InitSplitRankingsDisplay(const int ContestId, const int StageId, const int SplitId);
UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem") UFUNCTION(BlueprintCallable, Category="DTFlux|Core Subsystem")
bool GetStageRankingForBib(const int ContestId, const int StageId, const int Bib, bool GetStageRankingForBib(const int ContestId, const int StageId, const int Bib,
@ -196,9 +210,11 @@ private:
void SendRequest(const FString& Message); void SendRequest(const FString& Message);
UFUNCTION() UFUNCTION()
void RegisterDelegates(); void RegisterDelegates();
UFUNCTION() UFUNCTION()
bool IsStageRankingSealed(FDTFluxStageKey StageKey); bool IsStageRankingSealed(FDTFluxStageKey StageKey);
UFUNCTION() UFUNCTION()
bool IsContestRankingSealed(int ContestId); bool IsContestRankingSealed(int ContestId);
EDTFluxFinisherType GetSplitSensorType(const FDTFluxSplitSensorInfo& SplitSensorInfo);
}; };

View File

@ -29,6 +29,7 @@ public:
UFUNCTION(Blueprintable, Category="DTFlux|Core Subsystem|Tools") UFUNCTION(Blueprintable, Category="DTFlux|Core Subsystem|Tools")
static float ConvertTimeStringToSeconds(const FString& TimeString); static float ConvertTimeStringToSeconds(const FString& TimeString);
UFUNCTION(Blueprintable, Category="DTFlux|Core Subsystem|Tools") UFUNCTION(Blueprintable, Category="DTFlux|Core Subsystem|Tools")
static bool CompareTimeString(const FString& A_TimeStr, const FString& B_TimeStr, bool bAscendant = true); static bool CompareTimeString(const FString& A_TimeStr, const FString& B_TimeStr, bool bAscendant = true);

View File

@ -363,8 +363,8 @@ bool FDTFluxServerResponse::ParseContestRanking(FDTFluxContestRankings& OutConte
OutContestRankings.Rankings.Add(Ranking); OutContestRankings.Rankings.Add(Ranking);
} }
UE_LOG(logDTFluxNetwork, Log, TEXT("Successfully parsed contest ranking for Contest %d with %d entries"), // UE_LOG(logDTFluxNetwork, Log, TEXT("Successfully parsed contest ranking for Contest %d with %d entries"),
OutContestRankings.ContestId, OutContestRankings.Rankings.Num()); // OutContestRankings.ContestId, OutContestRankings.Rankings.Num());
ParsingStatus = EDTFluxResponseStatus::Success; ParsingStatus = EDTFluxResponseStatus::Success;
return true; return true;
} }
@ -475,12 +475,14 @@ bool FDTFluxServerResponse::ParseSplitSensor(TArray<FDTFluxSplitSensorInfo>& Out
NewSplitSensorInfo.StageId = SplitSensorInfoResponse.StageID; NewSplitSensorInfo.StageId = SplitSensorInfoResponse.StageID;
NewSplitSensorInfo.SplitId = SplitSensorInfoResponse.SplitID; NewSplitSensorInfo.SplitId = SplitSensorInfoResponse.SplitID;
NewSplitSensorInfo.Time = SplitSensorInfoResponse.Time; NewSplitSensorInfo.Time = SplitSensorInfoResponse.Time;
NewSplitSensorInfo.Gap = SplitSensorInfoResponse.Gap;
NewSplitSensorInfo.Rank = SplitSensorInfoResponse.Rank;
OutSplitSensorInfos.Add(NewSplitSensorInfo); OutSplitSensorInfos.Add(NewSplitSensorInfo);
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Split sensor info for bib %d in Contest %d, Stage %d, Split %d"), UE_LOG(logDTFluxNetwork, Verbose, TEXT("Split sensor info for bib %d in Contest %d, Stage %d, Split %d Rank [%i] Gap [%s] Time [%s]"),
NewSplitSensorInfo.Bib, NewSplitSensorInfo.ContestId, NewSplitSensorInfo.StageId, NewSplitSensorInfo.Bib, NewSplitSensorInfo.ContestId, NewSplitSensorInfo.StageId,
NewSplitSensorInfo.SplitId); NewSplitSensorInfo.SplitId, NewSplitSensorInfo.Rank, *NewSplitSensorInfo.Gap,*NewSplitSensorInfo.Time);
} }
UE_LOG(logDTFluxNetwork, Log, TEXT("Successfully parsed %d split sensor entries"), OutSplitSensorInfos.Num()); UE_LOG(logDTFluxNetwork, Log, TEXT("Successfully parsed %d split sensor entries"), OutSplitSensorInfos.Num());

View File

@ -554,9 +554,6 @@ void UDTFluxNetworkSubsystem::ReconnectWs(const FName WsClientId)
} }
} }
// ================================================================================================
// MÉTHODES DE PARSING LEGACY (COMPATIBILITÉ TOTALE)
// ================================================================================================
void UDTFluxNetworkSubsystem::ParseTeamListResponse(FDTFluxServerResponse& Response) void UDTFluxNetworkSubsystem::ParseTeamListResponse(FDTFluxServerResponse& Response)
{ {

View File

@ -14,17 +14,21 @@ struct DTFLUXNETWORK_API FDTFluxSplitSensorItemResponse
public: public:
UPROPERTY() UPROPERTY()
int Bib; int Bib = -1;
UPROPERTY() UPROPERTY()
FString Type = "split-sensor-item"; FString Type = "split-sensor-item";
UPROPERTY() UPROPERTY()
int ContestID; int ContestID =-1;
UPROPERTY() UPROPERTY()
int StageID; int StageID =-1;
UPROPERTY() UPROPERTY()
int SplitID; int SplitID = -1;
UPROPERTY() UPROPERTY()
FString Time = "-"; FString Time = "-";
UPROPERTY()
int Rank = -1;
UPROPERTY()
FString Gap = "";
}; };

View File

@ -17,20 +17,12 @@ class FDTFluxQueuedRequestManager;
typedef TSharedPtr<FDTFluxWebSocketClient> FDTFluxWebSocketClientSP; typedef TSharedPtr<FDTFluxWebSocketClient> FDTFluxWebSocketClientSP;
// ================================================================================================
// DELEGATES BLUEPRINT POUR LES REQUÊTES TRACKÉES
// ================================================================================================
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestCompleted, const FGuid&, RequestId, DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestCompleted, const FGuid&, RequestId,
EDTFluxApiDataType, RequestType, const FString&, ResponseData); EDTFluxApiDataType, RequestType, const FString&, ResponseData);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestFailed, const FGuid&, RequestId, DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestFailed, const FGuid&, RequestId,
EDTFluxApiDataType, RequestType, const FString&, ErrorMessage); EDTFluxApiDataType, RequestType, const FString&, ErrorMessage);
// ================================================================================================
// DELEGATES LEGACY POUR LA COMPATIBILITÉ
// ================================================================================================
DECLARE_DELEGATE_OneParam(FOnRaceDataReceived, const FDTFluxRaceData& /*RaceDataDefinition*/); DECLARE_DELEGATE_OneParam(FOnRaceDataReceived, const FDTFluxRaceData& /*RaceDataDefinition*/);
DECLARE_DELEGATE_OneParam(FOnTeamListReceived, const FDTFluxTeamListDefinition& /*TeamListDefinition*/); DECLARE_DELEGATE_OneParam(FOnTeamListReceived, const FDTFluxTeamListDefinition& /*TeamListDefinition*/);
DECLARE_DELEGATE_OneParam(FOnStageRankingReceived, const FDTFluxStageRankings& /*StageRankings*/); DECLARE_DELEGATE_OneParam(FOnStageRankingReceived, const FDTFluxStageRankings& /*StageRankings*/);
@ -42,25 +34,16 @@ DECLARE_DELEGATE_OneParam(FOnTeamStatusUpdateReceived, const FDTFluxTeamStatusUp
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWebSocketConnected); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWebSocketConnected);
// ================================================================================================
// NETWORK SUBSYSTEM - Interface UObject avec compatibilité Blueprint
// ================================================================================================
/**
* Subsystem réseau DTFlux avec support complet des requêtes trackées et compatibilité legacy
* Combine l'efficacité du RequestManager C++ avec l'interface Blueprint UObject
*/
UCLASS(Blueprintable) UCLASS(Blueprintable)
class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
// === ÉTAT DE CONNEXION ===
UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Network") UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Network")
EDTFluxConnectionStatus WsStatus = EDTFluxConnectionStatus::Unset; EDTFluxConnectionStatus WsStatus = EDTFluxConnectionStatus::Unset;
// === CONNEXION WEBSOCKET (Legacy) ===
/** /**
* Se connecter au serveur WebSocket * Se connecter au serveur WebSocket
@ -80,8 +63,6 @@ public:
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network") UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
void Reconnect(); void Reconnect();
// === REQUÊTES TRACKÉES (Nouveau système optimisé) ===
/** /**
* Envoyer une requête trackée avec cache, timeout et retry * Envoyer une requête trackée avec cache, timeout et retry
* @param RequestType Type de requête (ContestRanking, StageRanking, etc.) * @param RequestType Type de requête (ContestRanking, StageRanking, etc.)
@ -127,83 +108,50 @@ public:
float TimeoutSeconds = 5.0f, float TimeoutSeconds = 5.0f,
int32 MaxRetries = 3 int32 MaxRetries = 3
); );
// === ACCESSEURS BLUEPRINT POUR LES REQUÊTES TRACKÉES ===
/**
* Récupérer une requête trackée par son ID
*/
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
bool GetTrackedRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const; bool GetTrackedRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const;
/**
* Vérifier si une requête a reçu une réponse
*/
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
bool HasRequestReceivedResponse(const FGuid& RequestId) const; bool HasRequestReceivedResponse(const FGuid& RequestId) const;
/**
* Récupérer les données de réponse d'une requête
*/
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
FString GetRequestResponseData(const FGuid& RequestId) const; FString GetRequestResponseData(const FGuid& RequestId) const;
/**
* Vérifier si une requête similaire est en attente
*/
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
bool IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1, bool IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1,
int32 SplitId = -1) const; int32 SplitId = -1) const;
/**
* Compter le nombre de requêtes en attente
*/
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
int32 GetPendingRequestCount() const; int32 GetPendingRequestCount() const;
/**
* Récupérer les statistiques du gestionnaire de requêtes
*/
UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests")
void GetRequestStatistics(int32& OutPending, int32& OutCompleted, int32& OutFailed) const; void GetRequestStatistics(int32& OutPending, int32& OutCompleted, int32& OutFailed) const;
// === REQUÊTES LEGACY (Compatibilité totale) ===
/**
* Envoyer une requête en mode legacy (pour compatibilité)
*/
UFUNCTION(BlueprintCallable, Category = "DTFlux|Legacy") UFUNCTION(BlueprintCallable, Category = "DTFlux|Legacy")
void SendRequest(const EDTFluxApiDataType RequestType, int InContestId = -1, int InStageId = -1, void SendRequest(const EDTFluxApiDataType RequestType, int InContestId = -1, int InStageId = -1,
int InSplitId = -1); int InSplitId = -1);
/**
* Envoyer un message brut via WebSocket
*/
UFUNCTION(BlueprintCallable, Category = "DTFlux|Network") UFUNCTION(BlueprintCallable, Category = "DTFlux|Network")
void SendMessage(const FString& Message); void SendMessage(const FString& Message);
// === EVENTS BLUEPRINT ===
/**
* Event déclenché lors de la connexion WebSocket
*/
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Network") UPROPERTY(BlueprintAssignable, Category = "DTFlux|Network")
FOnWebSocketConnected OnWebSocketConnected; FOnWebSocketConnected OnWebSocketConnected;
/**
* Event déclenché quand une requête trackée se termine avec succès
*/
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests") UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests")
FOnDTFluxTrackedRequestCompleted OnTrackedRequestCompleted; FOnDTFluxTrackedRequestCompleted OnTrackedRequestCompleted;
/**
* Event déclenché quand une requête trackée échoue
*/
UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests") UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests")
FOnDTFluxTrackedRequestFailed OnTrackedRequestFailed; FOnDTFluxTrackedRequestFailed OnTrackedRequestFailed;
// === DELEGATES LEGACY (Compatibilité totale) ===
FOnRaceDataReceived OnRaceDataReceived; FOnRaceDataReceived OnRaceDataReceived;
FOnTeamListReceived OnTeamListReceived; FOnTeamListReceived OnTeamListReceived;
FOnStageRankingReceived OnStageRankingReceived; FOnStageRankingReceived OnStageRankingReceived;
@ -213,7 +161,6 @@ public:
FOnTeamUpdateReceived OnTeamUpdateReceived; FOnTeamUpdateReceived OnTeamUpdateReceived;
FOnTeamStatusUpdateReceived OnTeamStatusUpdateReceived; FOnTeamStatusUpdateReceived OnTeamStatusUpdateReceived;
// Accesseurs pour la compatibilité legacy
FOnRaceDataReceived& OnReceivedRaceData() { return OnRaceDataReceived; } FOnRaceDataReceived& OnReceivedRaceData() { return OnRaceDataReceived; }
FOnTeamListReceived& OnReceivedTeamList() { return OnTeamListReceived; } FOnTeamListReceived& OnReceivedTeamList() { return OnTeamListReceived; }
FOnStageRankingReceived& OnReceivedStageRanking() { return OnStageRankingReceived; } FOnStageRankingReceived& OnReceivedStageRanking() { return OnStageRankingReceived; }
@ -223,29 +170,21 @@ public:
FOnTeamUpdateReceived& OnReceivedTeamUpdate() { return OnTeamUpdateReceived; } FOnTeamUpdateReceived& OnReceivedTeamUpdate() { return OnTeamUpdateReceived; }
FOnTeamStatusUpdateReceived& OnReceivedTeamStatusUpdate() { return OnTeamStatusUpdateReceived; } FOnTeamStatusUpdateReceived& OnReceivedTeamStatusUpdate() { return OnTeamStatusUpdateReceived; }
// === ACCESSEUR PUBLIC POUR LE REQUEST MANAGER ===
/**
* Accéder au gestionnaire de requêtes (pour usage avancé)
*/
TSharedPtr<FDTFluxQueuedRequestManager> GetRequestManager() const { return RequestManager; } TSharedPtr<FDTFluxQueuedRequestManager> GetRequestManager() const { return RequestManager; }
protected: protected:
// === LIFECYCLE DU SUBSYSTEM ===
virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override; virtual void Deinitialize() override;
private: private:
// === CONFIGURATION ===
FDTFluxWsSettings WsSettings; FDTFluxWsSettings WsSettings;
// === CLIENTS RÉSEAU ===
FDTFluxWebSocketClientSP WsClient = nullptr; FDTFluxWebSocketClientSP WsClient = nullptr;
// === REQUEST MANAGER C++ ===
TSharedPtr<FDTFluxQueuedRequestManager> RequestManager; TSharedPtr<FDTFluxQueuedRequestManager> RequestManager;
// === GESTION DES ÉVÉNEMENTS WEBSOCKET ===
void RegisterWebSocketEvents(); void RegisterWebSocketEvents();
void UnregisterWebSocketEvents() const; void UnregisterWebSocketEvents() const;
void OnWebSocketConnected_Subsystem(); void OnWebSocketConnected_Subsystem();
@ -254,14 +193,12 @@ private:
void OnWebSocketMessageEvent_Subsystem(const FString& MessageString); void OnWebSocketMessageEvent_Subsystem(const FString& MessageString);
void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent); void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent);
// Handles pour les événements WebSocket
FDelegateHandle OnWsConnectedEventDelegateHandle; FDelegateHandle OnWsConnectedEventDelegateHandle;
FDelegateHandle OnWsConnectionErrorEventDelegateHandle; FDelegateHandle OnWsConnectionErrorEventDelegateHandle;
FDelegateHandle OnWsClosedEventDelegateHandle; FDelegateHandle OnWsClosedEventDelegateHandle;
FDelegateHandle OnWsMessageEventDelegateHandle; FDelegateHandle OnWsMessageEventDelegateHandle;
FDelegateHandle OnWsMessageSentEventDelegateHandle; FDelegateHandle OnWsMessageSentEventDelegateHandle;
// === PARSING ET TRAITEMENT DES RÉPONSES ===
/** /**
* Essayer de matcher une réponse à une requête trackée * Essayer de matcher une réponse à une requête trackée
@ -292,7 +229,6 @@ private:
void ParseSplitSensorResponse(FDTFluxServerResponse& Response); void ParseSplitSensorResponse(FDTFluxServerResponse& Response);
EDTFluxResponseStatus ProcessPushMessage(FDTFluxServerResponse& Response); EDTFluxResponseStatus ProcessPushMessage(FDTFluxServerResponse& Response);
// === CALLBACKS POUR LE REQUEST MANAGER ===
/** /**
* Callback appelé quand une requête trackée se termine * Callback appelé quand une requête trackée se termine
@ -304,7 +240,6 @@ private:
*/ */
void OnRequestFailed_Internal(const FDTFluxTrackedRequest& FailedRequest); void OnRequestFailed_Internal(const FDTFluxTrackedRequest& FailedRequest);
// === CONFIGURATION DYNAMIQUE ===
/** /**
* Callback appelé quand les paramètres WebSocket changent * Callback appelé quand les paramètres WebSocket changent
@ -317,7 +252,6 @@ private:
*/ */
void ReconnectWs(const FName WsClientId); void ReconnectWs(const FName WsClientId);
// === UTILITAIRES ===
/** /**
* Construire une adresse WebSocket complète * Construire une adresse WebSocket complète