445 lines
12 KiB
C++
445 lines
12 KiB
C++
// ================================================================================================
|
|
// DTFluxRequestManager.h - Gestionnaire C++ optimisé avec cache, timeout et retry
|
|
// ================================================================================================
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Tickable.h"
|
|
#include "HAL/CriticalSection.h"
|
|
#include "Struct/DTFluxServerResponseStruct.h"
|
|
#include "Types/Enum/DTFluxCoreEnum.h"
|
|
#include "DTFluxQueuedManager.generated.h"
|
|
|
|
class FDTFluxAsyncParser;
|
|
|
|
|
|
// ================================================================================================
|
|
// ENUMS ET STRUCTURES POUR LES REQUÊTES
|
|
// ================================================================================================
|
|
|
|
UENUM(BlueprintType)
|
|
enum class EDTFluxRequestState : uint8
|
|
{
|
|
Pending UMETA(DisplayName = "Pending"),
|
|
Sent UMETA(DisplayName = "Sent"),
|
|
Completed UMETA(DisplayName = "Completed"),
|
|
Failed UMETA(DisplayName = "Failed"),
|
|
TimedOut UMETA(DisplayName = "TimedOut"),
|
|
Cached UMETA(DisplayName = "Cached"),
|
|
Retrying UMETA(DisplayName = "Retrying")
|
|
};
|
|
|
|
USTRUCT(BlueprintType)
|
|
struct DTFLUXNETWORK_API FDTFluxRequestConfig
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
|
float TimeoutSeconds = 5.0f;
|
|
|
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
|
int32 MaxRetries = 3;
|
|
|
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
|
float RetryBackoffMultiplier = 1.5f;
|
|
|
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
|
bool bEnableCache = true;
|
|
|
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
|
float CacheValiditySeconds = 60.0f;
|
|
};
|
|
|
|
USTRUCT(BlueprintType)
|
|
struct DTFLUXNETWORK_API FDTFluxTrackedRequest
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
// === IDENTIFICATION ===
|
|
UPROPERTY(BlueprintReadOnly)
|
|
FGuid RequestId = FGuid::NewGuid();
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
EDTFluxApiDataType RequestType = EDTFluxApiDataType::None;
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
int32 ContestId = -1;
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
int32 StageId = -1;
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
int32 SplitId = -1;
|
|
|
|
// === ÉTAT ET TIMING ===
|
|
UPROPERTY(BlueprintReadOnly)
|
|
EDTFluxRequestState State = EDTFluxRequestState::Pending;
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
FDateTime CreatedAt = FDateTime::Now();
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
FDateTime SentAt = FDateTime::MinValue();
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
FDateTime CompletedAt = FDateTime::MinValue();
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
FDateTime LastAttemptTime = FDateTime::Now();
|
|
|
|
// === CONFIGURATION ===
|
|
FDTFluxRequestConfig Config;
|
|
|
|
// === RETRY LOGIC ===
|
|
UPROPERTY(BlueprintReadOnly)
|
|
int32 CurrentRetries = 0;
|
|
|
|
// === DONNÉES DE RÉPONSE ===
|
|
UPROPERTY(BlueprintReadOnly)
|
|
FString RawResponseData;
|
|
|
|
// Réponse parsée (lazy loading)
|
|
mutable TOptional<TSharedPtr<FDTFluxServerResponse>> ParsedResponse;
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
bool bIsResponseParsed = false;
|
|
|
|
UPROPERTY(BlueprintReadOnly)
|
|
FString LastErrorMessage;
|
|
|
|
// === MÉTHODES UTILITAIRES ===
|
|
|
|
bool HasTimedOut() const;
|
|
bool CanRetry() const;
|
|
bool IsCacheValid() const;
|
|
float GetRetryDelay() const;
|
|
bool Matches(EDTFluxApiDataType InType, int32 InContestId = -1, int32 InStageId = -1, int32 InSplitId = -1) const;
|
|
FString GetCacheKey() const;
|
|
void SetRawResponse(const FString& RawData);
|
|
FString Serialize() const;
|
|
};
|
|
|
|
// ================================================================================================
|
|
// DELEGATES POUR LES CALLBACKS
|
|
// ================================================================================================
|
|
|
|
DECLARE_DELEGATE_OneParam(FOnDTFluxRequestSuccess, const FDTFluxTrackedRequest&);
|
|
DECLARE_DELEGATE_TwoParams(FOnDTFluxRequestError, const FDTFluxTrackedRequest&, const FString& /*ErrorMessage*/);
|
|
|
|
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnRequestStateChangedNative, const FGuid& /*RequestId*/,
|
|
EDTFluxRequestState& /*NewState*/);
|
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnRequestCompletedNative, const FDTFluxTrackedRequest& /*CompletedRequest*/);
|
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnRequestFailedNative, const FDTFluxTrackedRequest& /*FailedRequest*/);
|
|
|
|
// ================================================================================================
|
|
// REQUEST MANAGER - Classe C++ principale avec SmartPointers
|
|
// ================================================================================================
|
|
|
|
/**
|
|
* Gestionnaire de requêtes trackées avec cache, timeout, retry et parsing asynchrone
|
|
* Implémentation C++ pure avec SmartPointers pour des performances optimales
|
|
*/
|
|
class DTFLUXNETWORK_API FDTFluxQueuedRequestManager : public FTickableGameObject
|
|
{
|
|
public:
|
|
FDTFluxQueuedRequestManager();
|
|
virtual ~FDTFluxQueuedRequestManager();
|
|
|
|
// === LIFECYCLE ===
|
|
|
|
/**
|
|
* Initialiser le gestionnaire de requêtes
|
|
* @param DefaultConfig Configuration par défaut pour les nouvelles requêtes
|
|
*/
|
|
void Initialize(const FDTFluxRequestConfig& DefaultConfig = FDTFluxRequestConfig());
|
|
|
|
/**
|
|
* Arrêter le gestionnaire et nettoyer toutes les ressources
|
|
*/
|
|
void Shutdown();
|
|
|
|
/**
|
|
* Vérifier si le gestionnaire est initialisé
|
|
*/
|
|
bool IsInitialized() const { return bIsInitialized.load(); }
|
|
|
|
// === CRÉATION DE REQUÊTES ===
|
|
|
|
/**
|
|
* Créer une nouvelle requête trackée
|
|
* @param RequestType Type de requête (ContestRanking, StageRanking, etc.)
|
|
* @param ContestId ID du contest (-1 si non applicable)
|
|
* @param StageId ID du stage (-1 si non applicable)
|
|
* @param SplitId ID du split (-1 si non applicable)
|
|
* @param CustomConfig Configuration spécifique pour cette requête
|
|
* @return GUID de la requête créée
|
|
*/
|
|
FGuid CreateTrackedRequest(
|
|
EDTFluxApiDataType RequestType,
|
|
int32 ContestId = -1,
|
|
int32 StageId = -1,
|
|
int32 SplitId = -1,
|
|
const FDTFluxRequestConfig& CustomConfig = FDTFluxRequestConfig()
|
|
);
|
|
|
|
/**
|
|
* Créer une requête trackée avec callbacks C++
|
|
* @param RequestType Type de requête
|
|
* @param ContestId ID du contest
|
|
* @param StageId ID du stage
|
|
* @param SplitId ID du split
|
|
* @param OnSuccess Callback appelé en cas de succès
|
|
* @param OnError Callback appelé en cas d'erreur
|
|
* @param CustomConfig Configuration spécifique
|
|
* @return GUID de la requête créée
|
|
*/
|
|
FGuid CreateTrackedRequestWithCallbacks(
|
|
EDTFluxApiDataType RequestType,
|
|
int32 ContestId,
|
|
int32 StageId,
|
|
int32 SplitId,
|
|
FOnDTFluxRequestSuccess OnSuccess,
|
|
FOnDTFluxRequestError OnError,
|
|
const FDTFluxRequestConfig& CustomConfig = FDTFluxRequestConfig()
|
|
);
|
|
|
|
// === GESTION DES REQUÊTES ===
|
|
|
|
/**
|
|
* Marquer une requête comme envoyée
|
|
*/
|
|
bool MarkRequestAsSent(const FGuid& RequestId);
|
|
|
|
/**
|
|
* Compléter une requête avec la réponse reçue
|
|
* @param RequestId ID de la requête
|
|
* @param RawResponseData Données JSON brutes de la réponse
|
|
* @param bUseAsyncParsing Utiliser le parsing asynchrone (recommandé)
|
|
*/
|
|
bool CompleteRequest(const FGuid& RequestId, const FString& RawResponseData, bool bUseAsyncParsing = true);
|
|
|
|
/**
|
|
* Marquer une requête comme échouée
|
|
*/
|
|
bool FailRequest(const FGuid& RequestId, const FString& ErrorMessage);
|
|
|
|
/**
|
|
* Relancer une requête (si retry possible)
|
|
*/
|
|
bool RetryRequest(const FGuid& RequestId);
|
|
|
|
// === RECHERCHE ET CACHE ===
|
|
|
|
/**
|
|
* Chercher une requête en attente correspondant aux critères
|
|
*/
|
|
bool FindPendingRequest(
|
|
FGuid& OutRequestId,
|
|
EDTFluxApiDataType RequestType,
|
|
int32 ContestId = -1,
|
|
int32 StageId = -1,
|
|
int32 SplitId = -1
|
|
) const;
|
|
|
|
/**
|
|
* Récupérer une réponse depuis le cache (données brutes)
|
|
*/
|
|
bool GetFromCache(
|
|
EDTFluxApiDataType RequestType,
|
|
FString& OutRawResponse,
|
|
int32 ContestId = -1,
|
|
int32 StageId = -1,
|
|
int32 SplitId = -1
|
|
) const;
|
|
|
|
/**
|
|
* Récupérer une réponse parsée depuis le cache
|
|
*/
|
|
bool GetParsedFromCache(
|
|
EDTFluxApiDataType RequestType,
|
|
TSharedPtr<FDTFluxServerResponse>& OutResponse,
|
|
int32 ContestId = -1,
|
|
int32 StageId = -1,
|
|
int32 SplitId = -1
|
|
) const;
|
|
|
|
// === ACCESSEURS ===
|
|
|
|
/**
|
|
* Récupérer une requête par son ID
|
|
*/
|
|
bool GetRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const;
|
|
|
|
/**
|
|
* Récupérer un pointeur vers une requête (plus efficace)
|
|
*/
|
|
const FDTFluxTrackedRequest* GetRequestPtr(const FGuid& RequestId) const;
|
|
|
|
/**
|
|
* Récupérer toutes les requêtes dans un état donné
|
|
*/
|
|
TArray<FDTFluxTrackedRequest> GetRequestsByState(EDTFluxRequestState State) const;
|
|
|
|
/**
|
|
* Compter les requêtes dans un état donné
|
|
*/
|
|
int32 GetRequestCount(EDTFluxRequestState State = EDTFluxRequestState::Pending) const;
|
|
|
|
// === STATISTIQUES ===
|
|
|
|
/**
|
|
* Statistiques complètes du gestionnaire de requêtes
|
|
*/
|
|
struct FRequestStatistics
|
|
{
|
|
int32 Pending = 0;
|
|
int32 Cached = 0;
|
|
int32 Completed = 0;
|
|
int32 Failed = 0;
|
|
int32 TotalRequests = 0;
|
|
int32 CacheHits = 0;
|
|
int32 CacheMisses = 0;
|
|
float HitRate = 0.0f;
|
|
};
|
|
|
|
FRequestStatistics GetStatistics() const;
|
|
|
|
// === NETTOYAGE ===
|
|
|
|
/**
|
|
* Nettoyer les entrées de cache expirées
|
|
* @return Nombre d'entrées supprimées
|
|
*/
|
|
int32 CleanupExpiredCache();
|
|
|
|
/**
|
|
* Nettoyer les requêtes terminées anciennes
|
|
* @param OlderThanSeconds Supprimer les requêtes plus anciennes que ce délai
|
|
* @return Nombre de requêtes supprimées
|
|
*/
|
|
int32 CleanupCompletedRequests(float OlderThanSeconds = 300.0f);
|
|
|
|
/**
|
|
* Vider toutes les requêtes et le cache
|
|
*/
|
|
void ClearAllRequests();
|
|
|
|
// === EVENTS ===
|
|
|
|
FOnRequestStateChangedNative OnRequestStateChanged;
|
|
FOnRequestCompletedNative OnRequestCompleted;
|
|
FOnRequestFailedNative OnRequestFailed;
|
|
|
|
// === INTERFACE TICKABLE ===
|
|
|
|
virtual void Tick(float DeltaTime) override;
|
|
virtual bool IsTickable() const override { return true; };
|
|
|
|
virtual TStatId GetStatId() const override
|
|
{
|
|
RETURN_QUICK_DECLARE_CYCLE_STAT(FDTFluxQueuedRequestManager, STATGROUP_Tickables);
|
|
};
|
|
virtual bool IsTickableWhenPaused() const override { return true; }
|
|
virtual bool IsTickableInEditor() const override { return true; }
|
|
|
|
// === ACCESSEUR POUR LE PARSER (debug/stats) ===
|
|
const FDTFluxAsyncParser* GetAsyncParser() const { return AsyncParser.Get(); }
|
|
|
|
private:
|
|
// === CONFIGURATION ===
|
|
FDTFluxRequestConfig DefaultConfig;
|
|
std::atomic<bool> bIsInitialized{false};
|
|
|
|
// === TIMING POUR LE TICK ===
|
|
float TimeSinceLastTimeoutCheck = 0.0f;
|
|
float TimeSinceLastCacheCleanup = 0.0f;
|
|
float TimeSinceLastRetryCheck = 0.0f;
|
|
|
|
static constexpr float TimeoutCheckInterval = 1.0f;
|
|
static constexpr float CacheCleanupInterval = 30.0f;
|
|
static constexpr float RetryCheckInterval = 0.5f;
|
|
|
|
// === STOCKAGE THREAD-SAFE ===
|
|
mutable FCriticalSection RequestsLock;
|
|
TMap<FGuid, TSharedPtr<FDTFluxTrackedRequest>> AllRequests;
|
|
TMap<FString, FGuid> CacheKeyToRequestId;
|
|
|
|
// === CALLBACKS C++ ===
|
|
mutable FCriticalSection CallbacksLock;
|
|
TMap<FGuid, FOnDTFluxRequestSuccess> SuccessCallbacks;
|
|
TMap<FGuid, FOnDTFluxRequestError> ErrorCallbacks;
|
|
|
|
// === MÉTRIQUES ===
|
|
mutable FCriticalSection MetricsLock;
|
|
mutable int32 TotalRequests = 0;
|
|
mutable int32 CacheHits = 0;
|
|
mutable int32 CacheMisses = 0;
|
|
|
|
|
|
// === PARSER ASYNCHRONE ===
|
|
TUniquePtr<FDTFluxAsyncParser> AsyncParser;
|
|
|
|
// === MÉTHODES PRIVÉES ===
|
|
|
|
/**
|
|
* Changer l'état d'une requête et notifier les observers
|
|
*/
|
|
void ChangeRequestState(TSharedPtr<FDTFluxTrackedRequest> Request, EDTFluxRequestState NewState);
|
|
|
|
/**
|
|
* Traiter les requêtes en timeout (appelé périodiquement)
|
|
*/
|
|
void ProcessTimeouts();
|
|
|
|
/**
|
|
* Traiter les requêtes à relancer (appelé périodiquement)
|
|
*/
|
|
void ProcessRetries();
|
|
|
|
/**
|
|
* Nettoyer le cache périodiquement
|
|
*/
|
|
void ProcessCacheCleanup();
|
|
|
|
/**
|
|
* Déclencher les callbacks pour une requête
|
|
*/
|
|
void TriggerCallbacks(const FDTFluxTrackedRequest& Request);
|
|
|
|
/**
|
|
* Nettoyer les callbacks d'une requête
|
|
*/
|
|
void CleanupCallbacks(const FGuid& RequestId);
|
|
|
|
/**
|
|
* Enregistrer un hit cache dans les métriques
|
|
*/
|
|
void RecordCacheHit() const;
|
|
|
|
/**
|
|
* Enregistrer un miss cache dans les métriques
|
|
*/
|
|
void RecordCacheMiss() const;
|
|
|
|
|
|
// === CALLBACKS POUR LE PARSING ASYNCHRONE ===
|
|
|
|
/**
|
|
* Callback appelé quand le parsing asynchrone réussit
|
|
*/
|
|
void OnParsingCompleted(const FGuid& RequestId, TSharedPtr<FDTFluxServerResponse> ParsedResponse, bool bSuccess);
|
|
|
|
/**
|
|
* Callback appelé quand le parsing asynchrone échoue
|
|
*/
|
|
void OnParsingFailed(const FGuid& RequestId, const FString& ErrorMessage);
|
|
|
|
// === UTILITAIRES STATIQUES ===
|
|
|
|
/**
|
|
* Générer une clé de cache unique pour une requête
|
|
*/
|
|
static FString GenerateCacheKey(EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId, int32 SplitId);
|
|
};
|