Files
DTFluxAPI/Source/DTFluxNetwork/Private/Subsystems/DTFluxNetworkSubsystem.cpp

787 lines
26 KiB
C++
Raw Normal View History

2025-07-11 19:04:37 +02:00
// ================================================================================================
// DTFluxNetworkSubsystem.cpp - Implémentation complète du subsystem avec compatibilité
// ================================================================================================
#include "Subsystems/DTFluxNetworkSubsystem.h"
2025-07-11 19:04:37 +02:00
#include "DTFluxQueuedManager.h"
#include "DTFluxNetworkModule.h"
#include "DTFluxNetworkSettings.h"
#include "JsonObjectConverter.h"
#include "Clients/DTFluxWebSocketClient.h"
#include "Struct/DTFluxServerResponseStruct.h"
#include "Struct/DTFluxRequestStructs.h"
#include "Types/Objects/UDTFluxParticipantFactory.h"
2025-07-11 19:04:37 +02:00
// ================================================================================================
// LIFECYCLE DU SUBSYSTEM
// ================================================================================================
void UDTFluxNetworkSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
UE_LOG(logDTFluxNetwork, Log, TEXT("Initializing DTFluxNetworkSubsystem..."));
// === CHARGER LES PARAMÈTRES ===
UDTFluxNetworkSettings* NetworkSettings = GetMutableDefault<UDTFluxNetworkSettings>();
UDTFluxNetworkSettings::GetWebSocketSettings(NetworkSettings, WsSettings);
// === CRÉER LES CLIENTS RÉSEAU ===
WsClient = MakeShareable<FDTFluxWebSocketClient>(new FDTFluxWebSocketClient());
// === CRÉER LE GESTIONNAIRE DE REQUÊTES OPTIMISÉ ===
RequestManager = MakeShared<FDTFluxQueuedRequestManager>();
FDTFluxRequestConfig DefaultConfig;
DefaultConfig.TimeoutSeconds = 5.0f;
DefaultConfig.MaxRetries = 3;
DefaultConfig.RetryBackoffMultiplier = 1.5f;
RequestManager->Initialize(DefaultConfig);
// === CONNECTER LES ÉVÉNEMENTS ===
RegisterWebSocketEvents();
// Connecter les événements du RequestManager aux delegates Blueprint
RequestManager->OnRequestCompleted.AddUObject(this, &UDTFluxNetworkSubsystem::OnRequestCompleted_Internal);
RequestManager->OnRequestFailed.AddUObject(this, &UDTFluxNetworkSubsystem::OnRequestFailed_Internal);
#if WITH_EDITOR
// Écouter les changements de configuration en éditeur
NetworkSettings->OnDTFluxWebSocketSettingsChanged.AddUFunction(this, FName("WsSettingsChanged"));
#endif
// === CONNEXION AUTOMATIQUE ===
if (WsSettings.bShouldConnectAtStartup)
{
WsClient->SetAddress(ConstructWsAddress(WsSettings.Address, WsSettings.Path, WsSettings.Port));
WsClient->Connect();
}
UE_LOG(logDTFluxNetwork, Log,
TEXT("DTFluxNetworkSubsystem initialized successfully with optimized RequestManager"));
}
void UDTFluxNetworkSubsystem::Deinitialize()
{
UE_LOG(logDTFluxNetwork, Log, TEXT("Deinitializing DTFluxNetworkSubsystem..."));
// === NETTOYER LE REQUEST MANAGER ===
if (RequestManager.IsValid())
{
RequestManager->OnRequestCompleted.RemoveAll(this);
RequestManager->OnRequestFailed.RemoveAll(this);
RequestManager->Shutdown();
RequestManager.Reset();
}
// === DÉCONNECTER LES CLIENTS ===
UnregisterWebSocketEvents();
if (WsClient.IsValid())
{
WsClient->Disconnect();
WsClient.Reset();
}
Super::Deinitialize();
UE_LOG(logDTFluxNetwork, Log, TEXT("DTFluxNetworkSubsystem deinitialized"));
}
// ================================================================================================
// CONNEXION WEBSOCKET
// ================================================================================================
void UDTFluxNetworkSubsystem::Connect()
{
2025-07-11 19:04:37 +02:00
if (WsClient.IsValid())
{
WsClient->SetAddress(ConstructWsAddress(WsSettings.Address, WsSettings.Path, WsSettings.Port));
WsClient->Connect();
UE_LOG(logDTFluxNetwork, Log, TEXT("Connecting to WebSocket: %s"), *WsClient->GetAddress());
}
}
void UDTFluxNetworkSubsystem::Disconnect()
{
2025-07-11 19:04:37 +02:00
if (WsClient.IsValid())
{
WsClient->Disconnect();
UE_LOG(logDTFluxNetwork, Log, TEXT("Disconnecting from WebSocket"));
}
}
void UDTFluxNetworkSubsystem::Reconnect()
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Log, TEXT("Reconnecting WebSocket..."));
ReconnectWs(FName("Ws_Client_0"));
}
2025-07-11 19:04:37 +02:00
// ================================================================================================
// REQUÊTES TRACKÉES (NOUVEAU SYSTÈME OPTIMISÉ)
// ================================================================================================
FGuid UDTFluxNetworkSubsystem::SendTrackedRequest(
EDTFluxApiDataType RequestType,
int32 ContestId,
int32 StageId,
int32 SplitId,
2025-07-11 19:04:37 +02:00
float TimeoutSeconds,
int32 MaxRetries,
bool bEnableCache)
{
2025-07-11 19:04:37 +02:00
if (!RequestManager.IsValid())
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Error, TEXT("RequestManager not available"));
return FGuid();
}
2025-07-11 19:04:37 +02:00
// Configuration personnalisée pour cette requête
FDTFluxRequestConfig CustomConfig;
CustomConfig.TimeoutSeconds = TimeoutSeconds;
CustomConfig.MaxRetries = MaxRetries;
CustomConfig.RetryBackoffMultiplier = 1.5f;
2025-07-11 19:04:37 +02:00
FGuid RequestId = RequestManager->CreateTrackedRequest(RequestType, ContestId, StageId, SplitId, CustomConfig);
2025-07-11 19:04:37 +02:00
if (RequestId.IsValid())
{
2025-07-11 19:04:37 +02:00
// Récupérer la requête pour l'envoyer
if (const FDTFluxTrackedRequest* Request = RequestManager->GetRequestPtr(RequestId))
{
RequestManager->MarkRequestAsSent(RequestId);
SendQueuedRequest(*Request);
2025-07-11 19:04:37 +02:00
}
}
return RequestId;
}
2025-07-11 19:04:37 +02:00
FGuid UDTFluxNetworkSubsystem::SendTrackedRequestWithCallbacks(
EDTFluxApiDataType RequestType,
int32 ContestId,
int32 StageId,
int32 SplitId,
2025-07-11 19:04:37 +02:00
FOnDTFluxRequestSuccess& OnSuccess,
FOnDTFluxRequestError& OnError,
float TimeoutSeconds,
int32 MaxRetries
)
{
2025-07-11 19:04:37 +02:00
if (!RequestManager.IsValid())
{
UE_LOG(logDTFluxNetwork, Error, TEXT("RequestManager not available"));
return FGuid();
}
FDTFluxRequestConfig CustomConfig;
CustomConfig.TimeoutSeconds = TimeoutSeconds;
CustomConfig.MaxRetries = MaxRetries;
CustomConfig.RetryBackoffMultiplier = 1.5f;
FGuid RequestId = RequestManager->CreateTrackedRequestWithCallbacks(
RequestType, ContestId, StageId, SplitId, OnSuccess, OnError, CustomConfig);
if (RequestId.IsValid())
{
2025-07-11 19:04:37 +02:00
if (const FDTFluxTrackedRequest* Request = RequestManager->GetRequestPtr(RequestId))
{
RequestManager->MarkRequestAsSent(RequestId);
SendQueuedRequest(*Request);
}
}
return RequestId;
}
2025-07-11 19:04:37 +02:00
// ================================================================================================
// ACCESSEURS BLUEPRINT POUR LES REQUÊTES TRACKÉES
// ================================================================================================
2025-07-11 19:04:37 +02:00
bool UDTFluxNetworkSubsystem::GetTrackedRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const
{
2025-07-11 19:04:37 +02:00
if (RequestManager.IsValid())
{
2025-07-11 19:04:37 +02:00
return RequestManager->GetRequest(RequestId, OutRequest);
}
return false;
}
bool UDTFluxNetworkSubsystem::HasRequestReceivedResponse(const FGuid& RequestId) const
{
2025-07-11 19:04:37 +02:00
FDTFluxTrackedRequest Request;
if (GetTrackedRequest(RequestId, Request))
{
2025-07-11 19:04:37 +02:00
return Request.State == EDTFluxRequestState::Completed ||
!Request.RawResponseData.IsEmpty();
}
return false;
}
FString UDTFluxNetworkSubsystem::GetRequestResponseData(const FGuid& RequestId) const
{
2025-07-11 19:04:37 +02:00
FDTFluxTrackedRequest Request;
if (GetTrackedRequest(RequestId, Request))
{
2025-07-11 19:04:37 +02:00
return Request.RawResponseData;
}
return FString();
}
2025-07-11 19:04:37 +02:00
bool UDTFluxNetworkSubsystem::IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId,
int32 SplitId) const
{
2025-07-11 19:04:37 +02:00
if (RequestManager.IsValid())
{
2025-07-11 19:04:37 +02:00
FGuid OutRequestId;
return RequestManager->FindPendingRequest(OutRequestId, RequestType, ContestId, StageId, SplitId);
}
2025-07-11 19:04:37 +02:00
return false;
}
int32 UDTFluxNetworkSubsystem::GetPendingRequestCount() const
{
2025-07-11 19:04:37 +02:00
if (RequestManager.IsValid())
{
2025-07-11 19:04:37 +02:00
return RequestManager->GetRequestCount(EDTFluxRequestState::Pending) +
RequestManager->GetRequestCount(EDTFluxRequestState::Sent);
}
2025-07-11 19:04:37 +02:00
return 0;
}
void UDTFluxNetworkSubsystem::GetRequestStatistics(int32& OutPending, int32& OutCompleted,
int32& OutFailed) const
{
2025-07-11 19:04:37 +02:00
if (RequestManager.IsValid())
{
FDTFluxQueuedRequestManager::FRequestStatistics Stats = RequestManager->GetStatistics();
OutPending = Stats.Pending;
OutCompleted = Stats.Completed;
OutFailed = Stats.Failed;
}
else
{
OutPending = OutCompleted = OutFailed = 0;
2025-07-11 19:04:37 +02:00
}
}
2025-07-11 19:04:37 +02:00
// ================================================================================================
// REQUÊTES LEGACY (COMPATIBILITÉ TOTALE)
// ================================================================================================
2025-07-11 19:04:37 +02:00
void UDTFluxNetworkSubsystem::SendRequest(const EDTFluxApiDataType RequestType, int InContestId, int InStageId,
int InSplitId)
{
FString Message;
switch (RequestType)
{
2025-07-11 19:04:37 +02:00
case EDTFluxApiDataType::ContestRanking:
FJsonObjectConverter::UStructToJsonObjectString(FDTFluxContestRankingRequest(InContestId), Message);
break;
2025-07-11 19:04:37 +02:00
case EDTFluxApiDataType::StageRanking:
FJsonObjectConverter::UStructToJsonObjectString(FDTFluxStageRankingRequest(InContestId, InStageId), Message);
break;
2025-07-11 19:04:37 +02:00
case EDTFluxApiDataType::SplitRanking:
FJsonObjectConverter::UStructToJsonObjectString(FDTFluxSplitRankingRequest(InContestId, InStageId, InSplitId),
Message);
break;
2025-07-11 19:04:37 +02:00
case EDTFluxApiDataType::TeamList:
FJsonObjectConverter::UStructToJsonObjectString(FDTFluxTeamListRequest(), Message);
break;
2025-07-11 19:04:37 +02:00
case EDTFluxApiDataType::RaceData:
FJsonObjectConverter::UStructToJsonObjectString(FDTFluxRaceDataRequest(), Message);
break;
default:
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Warning, TEXT("Unknown request type: %s"), *UEnum::GetValueAsString(RequestType));
return;
}
2025-07-11 19:04:37 +02:00
// Dirty trick to fix Case (compatibilité avec l'ancienne API)
Message = Message.Replace(TEXT("Id"), TEXT("ID"), ESearchCase::CaseSensitive);
UE_LOG(logDTFluxNetwork, Warning, TEXT("Sending Legacy Request %s"), *Message);
SendMessage(Message);
}
void UDTFluxNetworkSubsystem::SendMessage(const FString& Message)
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Sending Message: %s"), *Message);
if (WsClient.IsValid() && WsClient->CanSend())
{
WsClient->Send(Message);
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Log, TEXT("Message sent successfully"));
}
else
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Error, TEXT("WebSocket not connected. Connect before sending requests."));
}
}
2025-07-11 19:04:37 +02:00
// ================================================================================================
// GESTION DES ÉVÉNEMENTS WEBSOCKET
// ================================================================================================
2025-07-11 19:04:37 +02:00
void UDTFluxNetworkSubsystem::RegisterWebSocketEvents()
{
if (!WsClient.IsValid()) return;
OnWsConnectedEventDelegateHandle = WsClient->RegisterConnectedEvent()
.AddUObject(
this, &UDTFluxNetworkSubsystem::OnWebSocketConnected_Subsystem);
OnWsConnectionErrorEventDelegateHandle = WsClient->RegisterConnectionError()
.AddUObject(
this,
&UDTFluxNetworkSubsystem::OnWebSocketConnectionError_Subsystem);
OnWsClosedEventDelegateHandle = WsClient->RegisterClosedEvent()
.AddUObject(
this, &UDTFluxNetworkSubsystem::OnWebSocketClosedEvent_Subsystem);
OnWsMessageEventDelegateHandle = WsClient->RegisterMessageEvent()
.AddUObject(
this, &UDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem);
OnWsMessageSentEventDelegateHandle = WsClient->RegisterMessageSentEvent()
.AddUObject(
this,
&UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem);
}
void UDTFluxNetworkSubsystem::UnregisterWebSocketEvents() const
2025-07-11 19:04:37 +02:00
{
if (!WsClient.IsValid()) return;
2025-07-11 19:04:37 +02:00
if (OnWsConnectedEventDelegateHandle.IsValid())
WsClient->UnregisterConnectedEvent().Remove(OnWsConnectedEventDelegateHandle);
if (OnWsConnectionErrorEventDelegateHandle.IsValid())
WsClient->UnregisterConnectionError().Remove(OnWsConnectionErrorEventDelegateHandle);
if (OnWsClosedEventDelegateHandle.IsValid())
WsClient->UnregisterClosedEvent().Remove(OnWsClosedEventDelegateHandle);
if (OnWsMessageEventDelegateHandle.IsValid())
WsClient->UnregisterMessageEvent().Remove(OnWsMessageEventDelegateHandle);
if (OnWsMessageSentEventDelegateHandle.IsValid())
WsClient->UnregisterMessageSentEvent().Remove(OnWsMessageSentEventDelegateHandle);
}
2025-07-11 19:04:37 +02:00
void UDTFluxNetworkSubsystem::OnWebSocketConnected_Subsystem()
{
2025-07-11 19:04:37 +02:00
WsStatus = EDTFluxConnectionStatus::Connected;
OnWebSocketConnected.Broadcast();
UE_LOG(logDTFluxNetwork, Warning, TEXT("WebSocket connected to %s"), *WsClient->GetAddress());
}
2025-07-11 19:04:37 +02:00
void UDTFluxNetworkSubsystem::OnWebSocketConnectionError_Subsystem(const FString& Error)
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Warning, TEXT("WebSocket error with %s: %s"), *WsClient->GetAddress(), *Error);
WsStatus = EDTFluxConnectionStatus::Error;
2025-07-11 19:04:37 +02:00
if (WsSettings.bShouldAutoReconnectOnError)
{
2025-07-11 19:04:37 +02:00
WsClient->Reconnect();
}
}
2025-07-11 19:04:37 +02:00
void UDTFluxNetworkSubsystem::OnWebSocketClosedEvent_Subsystem(int32 StatusCode, const FString& Reason, bool bWasClean)
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Warning, TEXT("WebSocket closed: Reason=%s, StatusCode=%d, Clean=%s"),
*Reason, StatusCode, bWasClean ? TEXT("True") : TEXT("False"));
WsStatus = EDTFluxConnectionStatus::Closed;
}
2025-07-11 19:04:37 +02:00
void UDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& MessageString)
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, VeryVerbose, TEXT("Received WebSocket message: %s"), *MessageString);
2025-07-11 19:04:37 +02:00
// Essayer d'abord de matcher avec une requête trackée
if (!TryMatchResponseToQueuedRequest(MessageString))
{
// Si pas de match, traiter en mode legacy
ProcessLegacyResponse(MessageString);
}
}
2025-07-11 19:04:37 +02:00
void UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent)
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, VeryVerbose, TEXT("WebSocket message sent: %s"), *MessageSent);
}
2025-07-11 19:04:37 +02:00
bool UDTFluxNetworkSubsystem::TryMatchResponseToQueuedRequest(const FString& MessageString)
{
2025-07-11 19:04:37 +02:00
if (!RequestManager.IsValid())
return false;
2025-07-11 19:04:37 +02:00
// Parser rapidement pour identifier le type de réponse
EDTFluxResponseStatus Status;
FDTFluxServerResponse QuickResponse(MessageString, Status, false);
if (Status != EDTFluxResponseStatus::Success)
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Warning, TEXT("Failed to parse response for matching: %s"),
*QuickResponse.GetErrorMessage());
return false;
}
2025-07-11 19:04:37 +02:00
// Chercher une requête correspondante
FGuid FoundRequestId;
if (RequestManager->FindPendingRequest(FoundRequestId, QuickResponse.GetResponseType(),
QuickResponse.ContestID, QuickResponse.StageID, QuickResponse.SplitID))
{
2025-07-11 19:04:37 +02:00
// Utiliser le parsing asynchrone pour les réponses volumineuses
bool bUseAsyncParsing = ShouldUseAsyncParsing(MessageString);
if (RequestManager->CompleteRequest(FoundRequestId, MessageString, bUseAsyncParsing))
{
}
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Log, TEXT("Matched response to tracked request %s (async=%s)"),
*FoundRequestId.ToString(), bUseAsyncParsing ? TEXT("true") : TEXT("false"));
return true;
}
2025-07-11 19:04:37 +02:00
return false;
}
void UDTFluxNetworkSubsystem::ProcessLegacyResponse(const FString& MessageString)
{
UE_LOG(logDTFluxNetwork, Log, TEXT("Processing legacy response"));
// Parsing synchrone pour le mode legacy (pour simplicité)
EDTFluxResponseStatus Status;
TSharedPtr<FDTFluxServerResponse> ParsedResponse = MakeShared<FDTFluxServerResponse>(MessageString, Status);
if (Status == EDTFluxResponseStatus::Success)
{
2025-07-11 19:04:37 +02:00
ProcessParsedResponse(ParsedResponse);
}
2025-07-11 19:04:37 +02:00
else
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse legacy response: %s"),
*ParsedResponse->GetErrorMessage());
}
2025-07-11 19:04:37 +02:00
}
void UDTFluxNetworkSubsystem::ProcessParsedResponse(TSharedPtr<FDTFluxServerResponse> ParsedResponse)
{
if (!ParsedResponse.IsValid())
return;
// Traiter les messages push (temps réel) d'abord
if (ProcessPushMessage(*ParsedResponse) == EDTFluxResponseStatus::Success)
{
return; // Message push traité avec succès
}
// Traiter les réponses classiques selon le type
switch (ParsedResponse->GetResponseType())
{
2025-07-11 19:04:37 +02:00
case EDTFluxApiDataType::RaceData:
ParseRaceData(*ParsedResponse);
break;
case EDTFluxApiDataType::TeamList:
ParseTeamListResponse(*ParsedResponse);
break;
case EDTFluxApiDataType::ContestRanking:
ParseContestRanking(*ParsedResponse);
break;
case EDTFluxApiDataType::StageRanking:
ParseStageRankingResponse(*ParsedResponse);
break;
case EDTFluxApiDataType::SplitRanking:
ParseSplitRankingResponse(*ParsedResponse);
break;
default:
UE_LOG(logDTFluxNetwork, Warning, TEXT("Unknown response type in legacy processing: %s"),
*UEnum::GetValueAsString(ParsedResponse->GetResponseType()));
break;
}
}
2025-07-11 19:04:37 +02:00
// ================================================================================================
// CALLBACKS POUR LE REQUEST MANAGER
// ================================================================================================
void UDTFluxNetworkSubsystem::OnRequestCompleted_Internal(const FDTFluxTrackedRequest& CompletedRequest)
{
2025-07-11 19:04:37 +02:00
// Broadcaster l'événement Blueprint
OnTrackedRequestCompleted.Broadcast(
CompletedRequest.RequestId,
CompletedRequest.RequestType,
CompletedRequest.RawResponseData
);
if (CompletedRequest.ParsedResponse.IsSet())
{
UE_LOG(logDTFluxNetwork, Log, TEXT("Tracked About to process : %s"), *CompletedRequest.RequestId.ToString());
TSharedPtr<FDTFluxServerResponse> Response = CompletedRequest.ParsedResponse.GetValue();
ProcessParsedResponse(Response);
}
}
2025-07-11 19:04:37 +02:00
void UDTFluxNetworkSubsystem::OnRequestFailed_Internal(const FDTFluxTrackedRequest& FailedRequest)
{
2025-07-11 19:04:37 +02:00
// Broadcaster l'événement Blueprint
OnTrackedRequestFailed.Broadcast(
FailedRequest.RequestId,
FailedRequest.RequestType,
FailedRequest.LastErrorMessage
);
UE_LOG(logDTFluxNetwork, Warning, TEXT("Tracked request failed: %s - %s"),
*FailedRequest.RequestId.ToString(), *FailedRequest.LastErrorMessage);
}
2025-07-11 19:04:37 +02:00
// ================================================================================================
// CONFIGURATION DYNAMIQUE
// ================================================================================================
void UDTFluxNetworkSubsystem::WsSettingsChanged(const FDTFluxWsSettings& NewWsSettings)
{
2025-07-11 19:04:37 +02:00
bool bNeedsReload = WsSettings != NewWsSettings;
WsSettings = NewWsSettings;
if (bNeedsReload || WsSettings.bShouldConnectAtStartup)
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Warning, TEXT("WebSocket settings changed, reconnecting..."));
ReconnectWs(FName("Ws_Client_0"));
}
}
2025-07-11 19:04:37 +02:00
void UDTFluxNetworkSubsystem::ReconnectWs(const FName WsClientId)
{
2025-07-11 19:04:37 +02:00
if (WsClient.IsValid())
{
FString NewAddress = ConstructWsAddress(WsSettings.Address, WsSettings.Path, WsSettings.Port);
WsClient->SetAddress(NewAddress);
WsClient->Reconnect();
UE_LOG(logDTFluxNetwork, Log, TEXT("Reconnecting to: %s"), *NewAddress);
}
}
2025-07-11 19:04:37 +02:00
// ================================================================================================
// MÉTHODES DE PARSING LEGACY (COMPATIBILITÉ TOTALE)
// ================================================================================================
void UDTFluxNetworkSubsystem::ParseTeamListResponse(FDTFluxServerResponse& Response)
{
FDTFluxTeamListDefinition TeamListDefinition;
Response.ParseTeamList(TeamListDefinition);
2025-07-11 19:04:37 +02:00
if (Response.GetParsingStatus() == EDTFluxResponseStatus::Success)
{
2025-07-11 19:04:37 +02:00
OnTeamListReceived.ExecuteIfBound(TeamListDefinition);
UE_LOG(logDTFluxNetwork, Log, TEXT("Team list parsed: %d participants"), TeamListDefinition.Participants.Num());
}
else
{
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse team list: %s"), *Response.GetErrorMessage());
}
}
void UDTFluxNetworkSubsystem::ParseRaceData(FDTFluxServerResponse& Response)
{
FDTFluxRaceData RaceData;
Response.ParseRaceData(RaceData);
2025-07-11 19:04:37 +02:00
if (Response.GetParsingStatus() == EDTFluxResponseStatus::Success)
{
2025-07-11 19:04:37 +02:00
OnRaceDataReceived.ExecuteIfBound(RaceData);
UE_LOG(logDTFluxNetwork, Log, TEXT("Race data parsed: %d contests"), RaceData.Datas.Num());
}
else
{
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse race data: %s"), *Response.GetErrorMessage());
}
}
void UDTFluxNetworkSubsystem::ParseContestRanking(FDTFluxServerResponse& Response)
{
FDTFluxContestRankings ContestRankings;
Response.ParseContestRanking(ContestRankings);
2025-07-11 19:04:37 +02:00
if (Response.GetParsingStatus() == EDTFluxResponseStatus::Success)
{
2025-07-11 19:04:37 +02:00
OnContestRankingReceived.ExecuteIfBound(ContestRankings);
UE_LOG(logDTFluxNetwork, Log, TEXT("Contest ranking parsed for Contest %d: %d entries"),
ContestRankings.ContestId, ContestRankings.Rankings.Num());
}
else
{
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse contest ranking: %s"), *Response.GetErrorMessage());
}
}
void UDTFluxNetworkSubsystem::ParseStageRankingResponse(FDTFluxServerResponse& Response)
{
FDTFluxStageRankings StageRankings;
Response.ParseStageRanking(StageRankings);
2025-07-11 19:04:37 +02:00
if (Response.GetParsingStatus() == EDTFluxResponseStatus::Success)
{
OnStageRankingReceived.ExecuteIfBound(StageRankings);
UE_LOG(logDTFluxNetwork, Log, TEXT("Stage ranking parsed for Contest %d, Stage %d: %d entries"),
StageRankings.ContestId, StageRankings.StageId, StageRankings.Rankings.Num());
}
else
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse stage ranking: %s"), *Response.GetErrorMessage());
}
}
void UDTFluxNetworkSubsystem::ParseSplitRankingResponse(FDTFluxServerResponse& Response)
{
FDTFluxSplitRankings SplitRankings;
Response.ParseSplitRanking(SplitRankings);
2025-07-11 19:04:37 +02:00
if (Response.GetParsingStatus() == EDTFluxResponseStatus::Success)
{
2025-07-11 19:04:37 +02:00
OnSplitRankingReceived.ExecuteIfBound(SplitRankings);
UE_LOG(logDTFluxNetwork, Log, TEXT("Split ranking parsed for Contest %d, Stage %d, Split %d: %d entries"),
SplitRankings.ContestId, SplitRankings.StageId, SplitRankings.SplitId, SplitRankings.Rankings.Num());
}
else
{
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse split ranking: %s"), *Response.GetErrorMessage());
}
}
void UDTFluxNetworkSubsystem::ParseStatusUpdateResponse(FDTFluxServerResponse& Response)
{
FDTFluxTeamStatusUpdate StatusUpdate;
Response.ParseStatusUpdate(StatusUpdate);
2025-07-11 19:04:37 +02:00
if (Response.GetParsingStatus() == EDTFluxResponseStatus::Success)
{
OnTeamStatusUpdateReceived.ExecuteIfBound(StatusUpdate);
UE_LOG(logDTFluxNetwork, Log, TEXT("Status update parsed for Bib %d: %s"),
StatusUpdate.Bib, *UEnum::GetValueAsString(StatusUpdate.Status));
}
else
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse status update: %s"), *Response.GetErrorMessage());
}
}
void UDTFluxNetworkSubsystem::ParseSplitSensorResponse(FDTFluxServerResponse& Response)
{
2025-07-11 19:04:37 +02:00
TArray<FDTFluxSplitSensorInfo> SplitSensorInfos;
Response.ParseSplitSensor(SplitSensorInfos);
2025-07-11 19:04:37 +02:00
if (Response.GetParsingStatus() == EDTFluxResponseStatus::Success)
{
2025-07-11 19:04:37 +02:00
for (const auto& SplitSensorInfo : SplitSensorInfos)
{
OnSplitSensorReceived.ExecuteIfBound(SplitSensorInfo);
UE_LOG(logDTFluxNetwork, VeryVerbose,
TEXT("Split sensor parsed for Bib %d on Contest %d, Stage %d, Split %d"),
SplitSensorInfo.Bib, SplitSensorInfo.ContestId, SplitSensorInfo.StageId, SplitSensorInfo.SplitId);
}
UE_LOG(logDTFluxNetwork, Log, TEXT("Split sensor data parsed: %d entries"), SplitSensorInfos.Num());
}
2025-07-11 19:04:37 +02:00
else
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse split sensor: %s"), *Response.GetErrorMessage());
}
}
EDTFluxResponseStatus UDTFluxNetworkSubsystem::ProcessPushMessage(FDTFluxServerResponse& Response)
{
2025-07-11 19:04:37 +02:00
// Traiter les messages push (temps réel) qui ne correspondent pas à des requêtes
EDTFluxResponseStatus ResponseStatus = EDTFluxResponseStatus::UnknownError;
2025-07-11 19:04:37 +02:00
if (DTFluxDataTypeUtils::IsPushOnly(Response.GetResponseType()))
{
switch (Response.GetResponseType())
{
case EDTFluxApiDataType::SplitSensor:
{
TArray<FDTFluxSplitSensorInfo> SplitSensorInfos;
if (Response.ParseSplitSensor(SplitSensorInfos))
{
for (const auto& SplitSensorInfo : SplitSensorInfos)
{
OnSplitSensorReceived.ExecuteIfBound(SplitSensorInfo);
}
}
ResponseStatus = Response.GetParsingStatus();
break;
}
case EDTFluxApiDataType::StatusUpdate:
{
FDTFluxTeamStatusUpdate StatusUpdate;
if (Response.ParseStatusUpdate(StatusUpdate))
{
OnTeamStatusUpdateReceived.ExecuteIfBound(StatusUpdate);
}
ResponseStatus = Response.GetParsingStatus();
break;
}
case EDTFluxApiDataType::TeamUpdate:
{
FDTFluxTeamListDefinition TeamUpdateList;
if (Response.ParseTeamUpdate(TeamUpdateList))
{
OnTeamUpdateReceived.ExecuteIfBound(TeamUpdateList);
}
ResponseStatus = Response.GetParsingStatus();
break;
}
default:
{
ResponseStatus = EDTFluxResponseStatus::UnknownError;
break;
}
}
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Log, TEXT("Processed push message: %s"),
*UEnum::GetValueAsString(Response.GetResponseType()));
}
2025-07-11 19:04:37 +02:00
return ResponseStatus;
}
2025-07-11 19:04:37 +02:00
// ================================================================================================
// UTILITAIRES
// ================================================================================================
2025-07-11 19:04:37 +02:00
FString UDTFluxNetworkSubsystem::ConstructWsAddress(const FString& Address, const FString& Path, const int& Port)
{
2025-07-11 19:04:37 +02:00
FString NewAddress;
if (!Address.Contains("ws://") && !Address.Contains("wss://"))
{
2025-07-11 19:04:37 +02:00
NewAddress += FString("ws://");
}
2025-07-11 19:04:37 +02:00
NewAddress += Address + FString(":") + FString::FromInt(Port) + Path;
return NewAddress;
}
2025-07-11 19:04:37 +02:00
void UDTFluxNetworkSubsystem::SendQueuedRequest(const FDTFluxTrackedRequest& QueuedRequest)
{
2025-07-11 19:04:37 +02:00
FString Message = QueuedRequest.Serialize();
2025-07-11 19:04:37 +02:00
if (Message.IsEmpty())
{
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to serialize request %s"), *QueuedRequest.RequestId.ToString());
2025-07-11 19:04:37 +02:00
if (RequestManager.IsValid())
{
2025-07-11 19:04:37 +02:00
RequestManager->FailRequest(QueuedRequest.RequestId, TEXT("Serialization failed"));
}
return;
}
2025-07-11 19:04:37 +02:00
// Dirty trick to fix Case (compatibilité avec l'ancienne API)
Message = Message.Replace(TEXT("Id"), TEXT("ID"), ESearchCase::CaseSensitive);
2025-07-11 19:04:37 +02:00
UE_LOG(logDTFluxNetwork, Log, TEXT("Sending tracked request %s: %s"),
*QueuedRequest.RequestId.ToString(), *Message);
SendMessage(Message);
}
2025-07-11 19:04:37 +02:00
bool UDTFluxNetworkSubsystem::ShouldUseAsyncParsing(const FString& JsonData) const
{
// Critère pour décider du parsing asynchrone :
2025-07-11 19:04:37 +02:00
// - Taille des données (> 1KB par défaut)
// Pour le moment uniquement taille
2025-07-11 19:04:37 +02:00
constexpr int32 AsyncThreshold = 1024; // 1KB
2025-07-11 19:04:37 +02:00
return JsonData.Len() > AsyncThreshold;
}