// ================================================================================================ // DTFluxNetworkSubsystem.h - Interface UObject avec compatibilité Blueprint // ================================================================================================ #pragma once #include "CoreMinimal.h" #include "Subsystems/EngineSubsystem.h" #include "Types/DTFluxNetworkSettingsTypes.h" #include "Types/Enum/DTFluxCoreEnum.h" #include "DTFluxQueuedManager.h" #include "DTFluxNetworkSubsystem.generated.h" // Forward declarations class FDTFluxWebSocketClient; class FDTFluxQueuedRequestManager; typedef TSharedPtr FDTFluxWebSocketClientSP; // ================================================================================================ // DELEGATES BLUEPRINT POUR LES REQUÊTES TRACKÉES // ================================================================================================ DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestCompleted, const FGuid&, RequestId, EDTFluxApiDataType, RequestType, const FString&, ResponseData); DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDTFluxTrackedRequestFailed, const FGuid&, RequestId, EDTFluxApiDataType, RequestType, const FString&, ErrorMessage); // ================================================================================================ // DELEGATES LEGACY POUR LA COMPATIBILITÉ // ================================================================================================ DECLARE_DELEGATE_OneParam(FOnRaceDataReceived, const FDTFluxRaceData& /*RaceDataDefinition*/); DECLARE_DELEGATE_OneParam(FOnTeamListReceived, const FDTFluxTeamListDefinition& /*TeamListDefinition*/); DECLARE_DELEGATE_OneParam(FOnStageRankingReceived, const FDTFluxStageRankings& /*StageRankings*/); DECLARE_DELEGATE_OneParam(FOnSplitRankingReceived, const FDTFluxSplitRankings& /*SplitRankings*/); DECLARE_DELEGATE_OneParam(FOnContestRankingReceived, const FDTFluxContestRankings& /*ContestRankings*/); DECLARE_DELEGATE_OneParam(FOnSplitSensorReceived, const FDTFluxSplitSensorInfo& /*SplitSensorInfo*/); DECLARE_DELEGATE_OneParam(FOnTeamUpdateReceived, const FDTFluxTeamListDefinition& /*ParticipantToUpdate*/); DECLARE_DELEGATE_OneParam(FOnTeamStatusUpdateReceived, const FDTFluxTeamStatusUpdate& /*TeamToUpdate*/); 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) class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem { GENERATED_BODY() public: // === ÉTAT DE CONNEXION === UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Network") EDTFluxConnectionStatus WsStatus = EDTFluxConnectionStatus::Unset; // === CONNEXION WEBSOCKET (Legacy) === /** * Se connecter au serveur WebSocket */ UFUNCTION(BlueprintCallable, Category = "DTFlux|Network") void Connect(); /** * Se déconnecter du serveur WebSocket */ UFUNCTION(BlueprintCallable, Category = "DTFlux|Network") void Disconnect(); /** * Reconnecter au serveur WebSocket */ UFUNCTION(BlueprintCallable, Category = "DTFlux|Network") void Reconnect(); // === REQUÊTES TRACKÉES (Nouveau système optimisé) === /** * Envoyer une requête trackée avec cache, timeout et retry * @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 TimeoutSeconds Timeout en secondes * @param MaxRetries Nombre maximum de tentatives * @param bEnableCache Activer le cache pour cette requête * @return GUID de la requête pour le suivi */ UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") FGuid SendTrackedRequest( EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1, float TimeoutSeconds = 5.0f, int32 MaxRetries = 3, bool bEnableCache = true ); /** * Envoyer une requête trackée avec callbacks C++ (non Blueprint) * @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 TimeoutSeconds Timeout en secondes * @param MaxRetries Nombre maximum de tentatives * @param bEnableCache Activer le cache * @return GUID de la requête */ FGuid SendTrackedRequestWithCallbacks( EDTFluxApiDataType RequestType, int32 ContestId, int32 StageId, int32 SplitId, FOnDTFluxRequestSuccess& OnSuccess, FOnDTFluxRequestError& OnError, float TimeoutSeconds = 5.0f, 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") 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") 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") FString GetRequestResponseData(const FGuid& RequestId) const; /** * Vérifier si une requête similaire est en attente */ UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") bool IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1) const; /** * Compter le nombre de requêtes en attente */ UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") int32 GetPendingRequestCount() const; /** * Récupérer les statistiques du gestionnaire de requêtes */ UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") 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") void SendRequest(const EDTFluxApiDataType RequestType, int InContestId = -1, int InStageId = -1, int InSplitId = -1); /** * Envoyer un message brut via WebSocket */ UFUNCTION(BlueprintCallable, Category = "DTFlux|Network") void SendMessage(const FString& Message); // === EVENTS BLUEPRINT === /** * Event déclenché lors de la connexion WebSocket */ UPROPERTY(BlueprintAssignable, Category = "DTFlux|Network") FOnWebSocketConnected OnWebSocketConnected; /** * Event déclenché quand une requête trackée se termine avec succès */ UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests") FOnDTFluxTrackedRequestCompleted OnTrackedRequestCompleted; /** * Event déclenché quand une requête trackée échoue */ UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests") FOnDTFluxTrackedRequestFailed OnTrackedRequestFailed; // === DELEGATES LEGACY (Compatibilité totale) === FOnRaceDataReceived OnRaceDataReceived; FOnTeamListReceived OnTeamListReceived; FOnStageRankingReceived OnStageRankingReceived; FOnSplitRankingReceived OnSplitRankingReceived; FOnContestRankingReceived OnContestRankingReceived; FOnSplitSensorReceived OnSplitSensorReceived; FOnTeamUpdateReceived OnTeamUpdateReceived; FOnTeamStatusUpdateReceived OnTeamStatusUpdateReceived; // Accesseurs pour la compatibilité legacy FOnRaceDataReceived& OnReceivedRaceData() { return OnRaceDataReceived; } FOnTeamListReceived& OnReceivedTeamList() { return OnTeamListReceived; } FOnStageRankingReceived& OnReceivedStageRanking() { return OnStageRankingReceived; } FOnSplitRankingReceived& OnReceivedSplitRanking() { return OnSplitRankingReceived; } FOnContestRankingReceived& OnReceivedContestRanking() { return OnContestRankingReceived; } FOnSplitSensorReceived& OnReceivedSplitSensor() { return OnSplitSensorReceived; } FOnTeamUpdateReceived& OnReceivedTeamUpdate() { return OnTeamUpdateReceived; } FOnTeamStatusUpdateReceived& OnReceivedTeamStatusUpdate() { return OnTeamStatusUpdateReceived; } // === ACCESSEUR PUBLIC POUR LE REQUEST MANAGER === /** * Accéder au gestionnaire de requêtes (pour usage avancé) */ TSharedPtr GetRequestManager() const { return RequestManager; } protected: // === LIFECYCLE DU SUBSYSTEM === virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; private: // === CONFIGURATION === FDTFluxWsSettings WsSettings; // === CLIENTS RÉSEAU === FDTFluxWebSocketClientSP WsClient = nullptr; // === REQUEST MANAGER C++ === TSharedPtr RequestManager; // === GESTION DES ÉVÉNEMENTS WEBSOCKET === void RegisterWebSocketEvents(); void UnregisterWebSocketEvents() const; void OnWebSocketConnected_Subsystem(); void OnWebSocketConnectionError_Subsystem(const FString& Error); void OnWebSocketClosedEvent_Subsystem(int32 StatusCode, const FString& Reason, bool bWasClean); void OnWebSocketMessageEvent_Subsystem(const FString& MessageString); void OnWebSocketMessageSentEvent_Subsystem(const FString& MessageSent); // Handles pour les événements WebSocket FDelegateHandle OnWsConnectedEventDelegateHandle; FDelegateHandle OnWsConnectionErrorEventDelegateHandle; FDelegateHandle OnWsClosedEventDelegateHandle; FDelegateHandle OnWsMessageEventDelegateHandle; FDelegateHandle OnWsMessageSentEventDelegateHandle; // === PARSING ET TRAITEMENT DES RÉPONSES === /** * Essayer de matcher une réponse à une requête trackée * @param MessageString Message JSON reçu * @return true si la réponse correspond à une requête trackée */ bool TryMatchResponseToQueuedRequest(const FString& MessageString); /** * Traiter une réponse en mode legacy * @param MessageString Message JSON à traiter */ void ProcessLegacyResponse(const FString& MessageString); /** * Traiter une réponse déjà parsée * @param ParsedResponse Réponse parsée à traiter */ void ProcessParsedResponse(TSharedPtr ParsedResponse); // === MÉTHODES DE PARSING LEGACY (pour compatibilité) === void ParseTeamListResponse(FDTFluxServerResponse& Response); void ParseRaceData(FDTFluxServerResponse& Response); void ParseContestRanking(FDTFluxServerResponse& Response); void ParseStageRankingResponse(FDTFluxServerResponse& Response); void ParseSplitRankingResponse(FDTFluxServerResponse& Response); void ParseStatusUpdateResponse(FDTFluxServerResponse& Response); void ParseSplitSensorResponse(FDTFluxServerResponse& Response); EDTFluxResponseStatus ProcessPushMessage(FDTFluxServerResponse& Response); // === CALLBACKS POUR LE REQUEST MANAGER === /** * Callback appelé quand une requête trackée se termine */ void OnRequestCompleted_Internal(const FDTFluxTrackedRequest& CompletedRequest); /** * Callback appelé quand une requête trackée échoue */ void OnRequestFailed_Internal(const FDTFluxTrackedRequest& FailedRequest); // === CONFIGURATION DYNAMIQUE === /** * Callback appelé quand les paramètres WebSocket changent */ UFUNCTION() void WsSettingsChanged(const FDTFluxWsSettings& NewWsSettings); /** * Reconnecter le client WebSocket */ void ReconnectWs(const FName WsClientId); // === UTILITAIRES === /** * Construire une adresse WebSocket complète */ static FString ConstructWsAddress(const FString& Address, const FString& Path, const int& Port); /** * Envoyer une requête trackée via le réseau */ void SendQueuedRequest(const FDTFluxTrackedRequest& QueuedRequest); /** * Déterminer si on doit utiliser le parsing asynchrone */ bool ShouldUseAsyncParsing(const FString& JsonData) const; };