2025-07-11 19:04:37 +02:00
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
// DTFluxNetworkSubsystem.cpp - Implémentation complète du subsystem avec compatibilité
|
|
|
|
|
|
// ================================================================================================
|
2025-06-29 19:04:36 +02:00
|
|
|
|
|
|
|
|
|
|
#include "Subsystems/DTFluxNetworkSubsystem.h"
|
2025-07-11 19:04:37 +02:00
|
|
|
|
#include "DTFluxQueuedManager.h"
|
2025-06-29 19:04:36 +02:00
|
|
|
|
#include "DTFluxNetworkModule.h"
|
|
|
|
|
|
#include "DTFluxNetworkSettings.h"
|
|
|
|
|
|
#include "JsonObjectConverter.h"
|
|
|
|
|
|
#include "Clients/DTFluxWebSocketClient.h"
|
|
|
|
|
|
#include "Struct/DTFluxServerResponseStruct.h"
|
|
|
|
|
|
#include "Struct/DTFluxRequestStructs.h"
|
2025-07-03 17:28:51 +02:00
|
|
|
|
#include "Types/Objects/UDTFluxParticipantFactory.h"
|
2025-06-29 19:04:36 +02:00
|
|
|
|
|
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;
|
|
|
|
|
|
DefaultConfig.bEnableCache = true;
|
|
|
|
|
|
DefaultConfig.CacheValiditySeconds = 60.0f;
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
// ================================================================================================
|
2025-06-29 19:04:36 +02:00
|
|
|
|
|
2025-07-03 17:28:51 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::Connect()
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
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());
|
|
|
|
|
|
}
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void UDTFluxNetworkSubsystem::Disconnect()
|
|
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (WsClient.IsValid())
|
|
|
|
|
|
{
|
|
|
|
|
|
WsClient->Disconnect();
|
|
|
|
|
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Disconnecting from WebSocket"));
|
|
|
|
|
|
}
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void UDTFluxNetworkSubsystem::Reconnect()
|
|
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Reconnecting WebSocket..."));
|
2025-07-03 17:28:51 +02:00
|
|
|
|
ReconnectWs(FName("Ws_Client_0"));
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
// REQUÊTES TRACKÉES (NOUVEAU SYSTÈME OPTIMISÉ)
|
|
|
|
|
|
// ================================================================================================
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
|
|
|
|
|
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-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (!RequestManager.IsValid())
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Error, TEXT("RequestManager not available"));
|
2025-07-08 16:50:31 +02:00
|
|
|
|
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.bEnableCache = bEnableCache;
|
|
|
|
|
|
CustomConfig.CacheValiditySeconds = 60.0f;
|
|
|
|
|
|
CustomConfig.RetryBackoffMultiplier = 1.5f;
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
FGuid RequestId = RequestManager->CreateTrackedRequest(RequestType, ContestId, StageId, SplitId, CustomConfig);
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (RequestId.IsValid())
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// Récupérer la requête pour l'envoyer
|
|
|
|
|
|
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);
|
|
|
|
|
|
SendQueuedRequest(*Request);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return RequestId;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
FGuid UDTFluxNetworkSubsystem::SendTrackedRequestWithCallbacks(
|
2025-07-08 16:50:31 +02:00
|
|
|
|
EDTFluxApiDataType RequestType,
|
|
|
|
|
|
int32 ContestId,
|
|
|
|
|
|
int32 StageId,
|
|
|
|
|
|
int32 SplitId,
|
2025-07-11 19:04:37 +02:00
|
|
|
|
FOnDTFluxRequestSuccess& OnSuccess,
|
|
|
|
|
|
FOnDTFluxRequestError& OnError,
|
|
|
|
|
|
float TimeoutSeconds,
|
|
|
|
|
|
int32 MaxRetries,
|
|
|
|
|
|
bool bEnableCache)
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
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.bEnableCache = bEnableCache;
|
|
|
|
|
|
CustomConfig.CacheValiditySeconds = 60.0f;
|
|
|
|
|
|
CustomConfig.RetryBackoffMultiplier = 1.5f;
|
|
|
|
|
|
|
|
|
|
|
|
FGuid RequestId = RequestManager->CreateTrackedRequestWithCallbacks(
|
|
|
|
|
|
RequestType, ContestId, StageId, SplitId, OnSuccess, OnError, CustomConfig);
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
|
|
|
|
|
if (RequestId.IsValid())
|
|
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (const FDTFluxTrackedRequest* Request = RequestManager->GetRequestPtr(RequestId))
|
2025-07-09 03:27:23 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (Request->State != EDTFluxRequestState::Cached)
|
|
|
|
|
|
{
|
|
|
|
|
|
RequestManager->MarkRequestAsSent(RequestId);
|
|
|
|
|
|
SendQueuedRequest(*Request);
|
|
|
|
|
|
}
|
2025-07-09 03:27:23 +02:00
|
|
|
|
}
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return RequestId;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
// ACCESSEURS BLUEPRINT POUR LES REQUÊTES TRACKÉES
|
|
|
|
|
|
// ================================================================================================
|
2025-07-09 03:27:23 +02:00
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
bool UDTFluxNetworkSubsystem::GetTrackedRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (RequestManager.IsValid())
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
return RequestManager->GetRequest(RequestId, OutRequest);
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool UDTFluxNetworkSubsystem::HasRequestReceivedResponse(const FGuid& RequestId) const
|
|
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
FDTFluxTrackedRequest Request;
|
2025-07-08 16:50:31 +02:00
|
|
|
|
if (GetTrackedRequest(RequestId, Request))
|
|
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
return Request.State == EDTFluxRequestState::Completed ||
|
|
|
|
|
|
Request.State == EDTFluxRequestState::Cached ||
|
|
|
|
|
|
!Request.RawResponseData.IsEmpty();
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FString UDTFluxNetworkSubsystem::GetRequestResponseData(const FGuid& RequestId) const
|
|
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
FDTFluxTrackedRequest Request;
|
2025-07-08 16:50:31 +02:00
|
|
|
|
if (GetTrackedRequest(RequestId, Request))
|
|
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
return Request.RawResponseData;
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
return FString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
bool UDTFluxNetworkSubsystem::IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId,
|
2025-07-08 16:50:31 +02:00
|
|
|
|
int32 SplitId) const
|
|
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (RequestManager.IsValid())
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
FGuid OutRequestId;
|
|
|
|
|
|
return RequestManager->FindPendingRequest(OutRequestId, RequestType, ContestId, StageId, SplitId);
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
2025-07-11 19:04:37 +02:00
|
|
|
|
return false;
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int32 UDTFluxNetworkSubsystem::GetPendingRequestCount() const
|
|
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (RequestManager.IsValid())
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
return RequestManager->GetRequestCount(EDTFluxRequestState::Pending) +
|
|
|
|
|
|
RequestManager->GetRequestCount(EDTFluxRequestState::Sent);
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
2025-07-11 19:04:37 +02:00
|
|
|
|
return 0;
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::GetRequestStatistics(int32& OutPending, int32& OutCached, int32& OutCompleted,
|
|
|
|
|
|
int32& OutFailed, float& OutHitRate) const
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (RequestManager.IsValid())
|
|
|
|
|
|
{
|
|
|
|
|
|
FDTFluxQueuedRequestManager::FRequestStatistics Stats = RequestManager->GetStatistics();
|
|
|
|
|
|
OutPending = Stats.Pending;
|
|
|
|
|
|
OutCached = Stats.Cached;
|
|
|
|
|
|
OutCompleted = Stats.Completed;
|
|
|
|
|
|
OutFailed = Stats.Failed;
|
|
|
|
|
|
OutHitRate = Stats.HitRate;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
OutPending = OutCached = OutCompleted = OutFailed = 0;
|
|
|
|
|
|
OutHitRate = 0.0f;
|
|
|
|
|
|
}
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
// REQUÊTES LEGACY (COMPATIBILITÉ TOTALE)
|
|
|
|
|
|
// ================================================================================================
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::SendRequest(const EDTFluxApiDataType RequestType, int InContestId, int InStageId,
|
2025-07-03 17:28:51 +02:00
|
|
|
|
int InSplitId)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
|
|
|
|
|
FString Message;
|
|
|
|
|
|
switch (RequestType)
|
|
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
case EDTFluxApiDataType::ContestRanking:
|
2025-06-29 19:04:36 +02:00
|
|
|
|
FJsonObjectConverter::UStructToJsonObjectString(FDTFluxContestRankingRequest(InContestId), Message);
|
|
|
|
|
|
break;
|
2025-07-11 19:04:37 +02:00
|
|
|
|
case EDTFluxApiDataType::StageRanking:
|
2025-06-29 19:04:36 +02:00
|
|
|
|
FJsonObjectConverter::UStructToJsonObjectString(FDTFluxStageRankingRequest(InContestId, InStageId), Message);
|
|
|
|
|
|
break;
|
2025-07-11 19:04:37 +02:00
|
|
|
|
case EDTFluxApiDataType::SplitRanking:
|
2025-07-08 16:50:31 +02:00
|
|
|
|
FJsonObjectConverter::UStructToJsonObjectString(FDTFluxSplitRankingRequest(InContestId, InStageId, InSplitId),
|
|
|
|
|
|
Message);
|
2025-06-29 19:04:36 +02:00
|
|
|
|
break;
|
2025-07-11 19:04:37 +02:00
|
|
|
|
case EDTFluxApiDataType::TeamList:
|
2025-06-29 19:04:36 +02:00
|
|
|
|
FJsonObjectConverter::UStructToJsonObjectString(FDTFluxTeamListRequest(), Message);
|
|
|
|
|
|
break;
|
2025-07-11 19:04:37 +02:00
|
|
|
|
case EDTFluxApiDataType::RaceData:
|
2025-06-29 19:04:36 +02:00
|
|
|
|
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));
|
2025-06-29 19:04:36 +02:00
|
|
|
|
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);
|
2025-06-29 19:04:36 +02:00
|
|
|
|
SendMessage(Message);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-03 17:28:51 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::SendMessage(const FString& Message)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Sending Message: %s"), *Message);
|
2025-07-03 17:28:51 +02:00
|
|
|
|
|
2025-07-08 16:50:31 +02:00
|
|
|
|
if (WsClient.IsValid() && WsClient->CanSend())
|
2025-07-03 17:28:51 +02:00
|
|
|
|
{
|
|
|
|
|
|
WsClient->Send(Message);
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Message sent successfully"));
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Error, TEXT("WebSocket not connected. Connect before sending requests."));
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
// GESTION DES ÉVÉNEMENTS WEBSOCKET
|
|
|
|
|
|
// ================================================================================================
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::UnregisterWebSocketEvents()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!WsClient.IsValid()) return;
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
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-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::OnWebSocketConnected_Subsystem()
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
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-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::OnWebSocketConnectionError_Subsystem(const FString& Error)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
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-06-29 19:04:36 +02:00
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (WsSettings.bShouldAutoReconnectOnError)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
WsClient->Reconnect();
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::OnWebSocketClosedEvent_Subsystem(int32 StatusCode, const FString& Reason, bool bWasClean)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
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-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::OnWebSocketMessageEvent_Subsystem(const FString& MessageString)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, VeryVerbose, TEXT("Received WebSocket message: %s"), *MessageString);
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
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-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, VeryVerbose, TEXT("WebSocket message sent: %s"), *MessageSent);
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
bool UDTFluxNetworkSubsystem::TryMatchResponseToQueuedRequest(const FString& MessageString)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (!RequestManager.IsValid())
|
|
|
|
|
|
return false;
|
2025-06-29 19:04:36 +02:00
|
|
|
|
|
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-06-29 19:04:36 +02:00
|
|
|
|
{
|
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-06-29 19:04:36 +02:00
|
|
|
|
}
|
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-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// Utiliser le parsing asynchrone pour les réponses volumineuses
|
|
|
|
|
|
bool bUseAsyncParsing = ShouldUseAsyncParsing(MessageString);
|
|
|
|
|
|
|
|
|
|
|
|
RequestManager->CompleteRequest(FoundRequestId, MessageString, bUseAsyncParsing);
|
|
|
|
|
|
|
|
|
|
|
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Matched response to tracked request %s (async=%s)"),
|
|
|
|
|
|
*FoundRequestId.ToString(), bUseAsyncParsing ? TEXT("true") : TEXT("false"));
|
|
|
|
|
|
return true;
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|
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-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
ProcessParsedResponse(ParsedResponse);
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|
2025-07-11 19:04:37 +02:00
|
|
|
|
else
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse legacy response: %s"),
|
|
|
|
|
|
*ParsedResponse->GetErrorMessage());
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|
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-06-29 19:04:36 +02:00
|
|
|
|
{
|
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-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
// CALLBACKS POUR LE REQUEST MANAGER
|
|
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
|
|
|
|
|
|
void UDTFluxNetworkSubsystem::OnRequestCompleted_Internal(const FDTFluxTrackedRequest& CompletedRequest)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// Broadcaster l'événement Blueprint
|
|
|
|
|
|
OnTrackedRequestCompleted.Broadcast(
|
|
|
|
|
|
CompletedRequest.RequestId,
|
|
|
|
|
|
CompletedRequest.RequestType,
|
|
|
|
|
|
CompletedRequest.RawResponseData
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Tracked request completed: %s"), *CompletedRequest.RequestId.ToString());
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::OnRequestFailed_Internal(const FDTFluxTrackedRequest& FailedRequest)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
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-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
// CONFIGURATION DYNAMIQUE
|
|
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
|
|
|
|
|
|
void UDTFluxNetworkSubsystem::WsSettingsChanged(const FDTFluxWsSettings& NewWsSettings)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
bool bNeedsReload = WsSettings != NewWsSettings;
|
|
|
|
|
|
WsSettings = NewWsSettings;
|
|
|
|
|
|
|
|
|
|
|
|
if (bNeedsReload || WsSettings.bShouldConnectAtStartup)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Warning, TEXT("WebSocket settings changed, reconnecting..."));
|
|
|
|
|
|
ReconnectWs(FName("Ws_Client_0"));
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::ReconnectWs(const FName WsClientId)
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
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-03 17:28:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
// MÉTHODES DE PARSING LEGACY (COMPATIBILITÉ TOTALE)
|
|
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
|
2025-07-08 16:50:31 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::ParseTeamListResponse(FDTFluxServerResponse& Response)
|
|
|
|
|
|
{
|
|
|
|
|
|
FDTFluxTeamListDefinition TeamListDefinition;
|
|
|
|
|
|
Response.ParseTeamListResponse(TeamListDefinition);
|
2025-07-11 19:04:37 +02:00
|
|
|
|
|
|
|
|
|
|
if (Response.GetParsingStatus() == EDTFluxResponseStatus::Success)
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
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());
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void UDTFluxNetworkSubsystem::ParseRaceData(FDTFluxServerResponse& Response)
|
2025-07-03 17:28:51 +02:00
|
|
|
|
{
|
2025-07-08 16:50:31 +02:00
|
|
|
|
FDTFluxRaceData RaceData;
|
|
|
|
|
|
Response.ParseRaceData(RaceData);
|
2025-07-11 19:04:37 +02:00
|
|
|
|
|
|
|
|
|
|
if (Response.GetParsingStatus() == EDTFluxResponseStatus::Success)
|
2025-07-03 17:28:51 +02:00
|
|
|
|
{
|
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());
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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-03 17:28:51 +02:00
|
|
|
|
{
|
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());
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void UDTFluxNetworkSubsystem::ParseStageRankingResponse(FDTFluxServerResponse& Response)
|
|
|
|
|
|
{
|
|
|
|
|
|
FDTFluxStageRankings StageRankings;
|
|
|
|
|
|
Response.ParseStageRankingResponse(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-03 17:28:51 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse stage ranking: %s"), *Response.GetErrorMessage());
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-03 17:28:51 +02:00
|
|
|
|
|
2025-07-08 16:50:31 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::ParseSplitRankingResponse(FDTFluxServerResponse& Response)
|
|
|
|
|
|
{
|
|
|
|
|
|
FDTFluxSplitRankings SplitRankings;
|
|
|
|
|
|
Response.ParseSplitRankingResponse(SplitRankings);
|
2025-07-11 19:04:37 +02:00
|
|
|
|
|
|
|
|
|
|
if (Response.GetParsingStatus() == EDTFluxResponseStatus::Success)
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
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());
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
2025-07-03 17:28:51 +02:00
|
|
|
|
|
2025-07-08 16:50:31 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::ParseStatusUpdateResponse(FDTFluxServerResponse& Response)
|
|
|
|
|
|
{
|
|
|
|
|
|
FDTFluxTeamStatusUpdate StatusUpdate;
|
|
|
|
|
|
Response.ParseStatusUpdateResponse(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-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse status update: %s"), *Response.GetErrorMessage());
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-08 16:50:31 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::ParseSplitSensorResponse(FDTFluxServerResponse& Response)
|
2025-07-03 17:28:51 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
TArray<FDTFluxSplitSensorInfo> SplitSensorInfos;
|
2025-07-08 16:50:31 +02:00
|
|
|
|
Response.ParseSplitSensorResponse(SplitSensorInfos);
|
2025-07-11 19:04:37 +02:00
|
|
|
|
|
|
|
|
|
|
if (Response.GetParsingStatus() == EDTFluxResponseStatus::Success)
|
2025-07-03 17:28:51 +02:00
|
|
|
|
{
|
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-08 16:50:31 +02:00
|
|
|
|
}
|
2025-07-11 19:04:37 +02:00
|
|
|
|
else
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to parse split sensor: %s"), *Response.GetErrorMessage());
|
2025-07-08 16:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2025-07-08 16:50:31 +02:00
|
|
|
|
EDTFluxResponseStatus ResponseStatus = EDTFluxResponseStatus::UnknownError;
|
2025-07-11 19:04:37 +02:00
|
|
|
|
|
2025-07-08 16:50:31 +02:00
|
|
|
|
if (DTFluxDataTypeUtils::IsPushOnly(Response.GetResponseType()))
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (Response.GetResponseType())
|
2025-07-03 17:28:51 +02:00
|
|
|
|
{
|
2025-07-08 16:50:31 +02:00
|
|
|
|
case EDTFluxApiDataType::SplitSensor:
|
2025-07-03 17:28:51 +02:00
|
|
|
|
{
|
2025-07-08 16:50:31 +02:00
|
|
|
|
TArray<FDTFluxSplitSensorInfo> SplitSensorInfos;
|
|
|
|
|
|
if (Response.ParseSplitSensorResponse(SplitSensorInfos))
|
|
|
|
|
|
{
|
|
|
|
|
|
for (const auto& SplitSensorInfo : SplitSensorInfos)
|
|
|
|
|
|
{
|
|
|
|
|
|
OnSplitSensorReceived.ExecuteIfBound(SplitSensorInfo);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ResponseStatus = Response.GetParsingStatus();
|
|
|
|
|
|
break;
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
2025-07-08 16:50:31 +02:00
|
|
|
|
case EDTFluxApiDataType::StatusUpdate:
|
2025-07-03 17:28:51 +02:00
|
|
|
|
{
|
2025-07-08 16:50:31 +02:00
|
|
|
|
FDTFluxTeamStatusUpdate StatusUpdate;
|
|
|
|
|
|
if (Response.ParseStatusUpdateResponse(StatusUpdate))
|
|
|
|
|
|
{
|
|
|
|
|
|
OnTeamStatusUpdateReceived.ExecuteIfBound(StatusUpdate);
|
|
|
|
|
|
}
|
|
|
|
|
|
ResponseStatus = Response.GetParsingStatus();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case EDTFluxApiDataType::TeamUpdate:
|
|
|
|
|
|
{
|
|
|
|
|
|
FDTFluxTeamListDefinition TeamUpdateList;
|
|
|
|
|
|
if (Response.ParseTeamUpdateResponse(TeamUpdateList))
|
|
|
|
|
|
{
|
|
|
|
|
|
OnTeamUpdateReceived.ExecuteIfBound(TeamUpdateList);
|
|
|
|
|
|
}
|
|
|
|
|
|
ResponseStatus = Response.GetParsingStatus();
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
|
|
|
{
|
|
|
|
|
|
ResponseStatus = EDTFluxResponseStatus::UnknownError;
|
|
|
|
|
|
break;
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Log, TEXT("Processed push message: %s"),
|
2025-07-08 16:50:31 +02:00
|
|
|
|
*UEnum::GetValueAsString(Response.GetResponseType()));
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
return ResponseStatus;
|
2025-07-09 03:27:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// ================================================================================================
|
|
|
|
|
|
// UTILITAIRES
|
|
|
|
|
|
// ================================================================================================
|
2025-07-03 17:28:51 +02:00
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
FString UDTFluxNetworkSubsystem::ConstructWsAddress(const FString& Address, const FString& Path, const int& Port)
|
2025-07-03 17:28:51 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
FString NewAddress;
|
|
|
|
|
|
if (!Address.Contains("ws://") && !Address.Contains("wss://"))
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
NewAddress += FString("ws://");
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
2025-07-11 19:04:37 +02:00
|
|
|
|
NewAddress += Address + FString(":") + FString::FromInt(Port) + Path;
|
|
|
|
|
|
return NewAddress;
|
2025-07-03 17:28:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
void UDTFluxNetworkSubsystem::SendQueuedRequest(const FDTFluxTrackedRequest& QueuedRequest)
|
2025-07-03 17:28:51 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
FString Message = QueuedRequest.Serialize();
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (Message.IsEmpty())
|
2025-07-08 16:50:31 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
UE_LOG(logDTFluxNetwork, Error, TEXT("Failed to serialize request %s"), *QueuedRequest.RequestId.ToString());
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
if (RequestManager.IsValid())
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
RequestManager->FailRequest(QueuedRequest.RequestId, TEXT("Serialization failed"));
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|
2025-07-08 16:50:31 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// Dirty trick to fix Case (compatibilité avec l'ancienne API)
|
2025-07-08 16:50:31 +02:00
|
|
|
|
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);
|
2025-07-08 16:50:31 +02:00
|
|
|
|
|
|
|
|
|
|
SendMessage(Message);
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 19:04:37 +02:00
|
|
|
|
bool UDTFluxNetworkSubsystem::ShouldUseAsyncParsing(const FString& JsonData) const
|
2025-06-29 19:04:36 +02:00
|
|
|
|
{
|
2025-07-11 19:04:37 +02:00
|
|
|
|
// Critères pour décider du parsing asynchrone :
|
|
|
|
|
|
// - Taille des données (> 1KB par défaut)
|
|
|
|
|
|
// - Charge actuelle du système
|
|
|
|
|
|
// - Type de données (certains types sont plus complexes à parser)
|
|
|
|
|
|
|
|
|
|
|
|
const int32 AsyncThreshold = 1024; // 1KB
|
|
|
|
|
|
return JsonData.Len() > AsyncThreshold;
|
2025-06-29 19:04:36 +02:00
|
|
|
|
}
|