// ================================================================================================ // 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; 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); 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); UCLASS(Blueprintable) class DTFLUXNETWORK_API UDTFluxNetworkSubsystem : public UEngineSubsystem { GENERATED_BODY() public: UPROPERTY(BlueprintReadOnly, Category = "DTFlux|Network") EDTFluxConnectionStatus WsStatus = EDTFluxConnectionStatus::Unset; /** * 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(); /** * 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 ); UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") bool GetTrackedRequest(const FGuid& RequestId, FDTFluxTrackedRequest& OutRequest) const; UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") bool HasRequestReceivedResponse(const FGuid& RequestId) const; UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") FString GetRequestResponseData(const FGuid& RequestId) const; UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") bool IsRequestPending(EDTFluxApiDataType RequestType, int32 ContestId = -1, int32 StageId = -1, int32 SplitId = -1) const; UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") int32 GetPendingRequestCount() const; UFUNCTION(BlueprintCallable, Category = "DTFlux|Tracked Requests") void GetRequestStatistics(int32& OutPending, int32& OutCompleted, int32& OutFailed) const; UFUNCTION(BlueprintCallable, Category = "DTFlux|Legacy") void SendRequest(const EDTFluxApiDataType RequestType, int InContestId = -1, int InStageId = -1, int InSplitId = -1); UFUNCTION(BlueprintCallable, Category = "DTFlux|Network") void SendMessage(const FString& Message); UPROPERTY(BlueprintAssignable, Category = "DTFlux|Network") FOnWebSocketConnected OnWebSocketConnected; UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests") FOnDTFluxTrackedRequestCompleted OnTrackedRequestCompleted; UPROPERTY(BlueprintAssignable, Category = "DTFlux|Tracked Requests") FOnDTFluxTrackedRequestFailed OnTrackedRequestFailed; FOnRaceDataReceived OnRaceDataReceived; FOnTeamListReceived OnTeamListReceived; FOnStageRankingReceived OnStageRankingReceived; FOnSplitRankingReceived OnSplitRankingReceived; FOnContestRankingReceived OnContestRankingReceived; FOnSplitSensorReceived OnSplitSensorReceived; FOnTeamUpdateReceived OnTeamUpdateReceived; FOnTeamStatusUpdateReceived OnTeamStatusUpdateReceived; 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; } TSharedPtr GetRequestManager() const { return RequestManager; } protected: virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; private: FDTFluxWsSettings WsSettings; FDTFluxWebSocketClientSP WsClient = nullptr; TSharedPtr RequestManager; 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); FDelegateHandle OnWsConnectedEventDelegateHandle; FDelegateHandle OnWsConnectionErrorEventDelegateHandle; FDelegateHandle OnWsClosedEventDelegateHandle; FDelegateHandle OnWsMessageEventDelegateHandle; FDelegateHandle OnWsMessageSentEventDelegateHandle; /** * 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); /** * 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); /** * Callback appelé quand les paramètres WebSocket changent */ UFUNCTION() void WsSettingsChanged(const FDTFluxWsSettings& NewWsSettings); /** * Reconnecter le client WebSocket */ void ReconnectWs(const FName WsClientId); /** * 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; };