Fixing Various Bugs On Delegates

This commit is contained in:
2025-07-11 19:04:37 +02:00
parent 73413e44b4
commit f1f300a351
12 changed files with 2765 additions and 1207 deletions

View File

@ -0,0 +1,317 @@
#include "DTFluxAsyncParser.h"
#include "DTFluxNetworkModule.h"
#include "Struct/DTFluxServerResponseStruct.h"
#include "Async/AsyncWork.h"
// ================================================================================================
// IMPLÉMENTATION DE LA TÂCHE DE PARSING
// ================================================================================================
DECLARE_STATS_GROUP(TEXT("DTFlux"), STATGROUP_DTFlux, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("DTFlux Parsing Task"), STAT_FDTFluxParsingTask, STATGROUP_DTFlux);
DECLARE_CYCLE_STAT(TEXT("DTFlux Parsing Task DoWork"), STAT_FDTFluxParsingTask_DoWork, STATGROUP_DTFlux);
FDTFluxParsingTask::FDTFluxParsingTask(
const FGuid& InRequestId,
const FString& InRawJsonData,
FOnParsingCompleted InOnCompleted,
FOnParsingFailed InOnFailed
)
: RequestId(InRequestId)
, RawJsonData(InRawJsonData)
, OnCompleted(InOnCompleted)
, OnFailed(InOnFailed)
, StartTime(FPlatformTime::Seconds())
{
}
void FDTFluxParsingTask::DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
{
SCOPE_CYCLE_COUNTER(STAT_FDTFluxParsingTask_DoWork);
UE_LOG(logDTFluxNetwork, VeryVerbose, TEXT("Starting async parsing for request %s"), *RequestId.ToString());
TSharedPtr<FDTFluxServerResponse> ParsedResponse;
bool bParsingSuccess = false;
FString ErrorMessage;
try
{
// === PARSING SUR LE THREAD WORKER ===
EDTFluxResponseStatus Status;
ParsedResponse = MakeShared<FDTFluxServerResponse>(RawJsonData, Status, false); // Pas de logs sur worker thread
if (Status == EDTFluxResponseStatus::Success)
{
bParsingSuccess = true;
UE_LOG(logDTFluxNetwork, VeryVerbose, TEXT("Async parsing successful for request %s"),
*RequestId.ToString());
}
else
{
ErrorMessage = FString::Printf(TEXT("Parsing failed with status: %s"),
*UEnum::GetValueAsString(Status));
UE_LOG(logDTFluxNetwork, Warning, TEXT("Async parsing failed for request %s: %s"),
*RequestId.ToString(), *ErrorMessage);
}
}
catch (const std::exception& e)
{
ErrorMessage = FString::Printf(TEXT("Exception during parsing: %s"), ANSI_TO_TCHAR(e.what()));
UE_LOG(logDTFluxNetwork, Error, TEXT("Exception during async parsing for request %s: %s"),
*RequestId.ToString(), *ErrorMessage);
}
catch (...)
{
ErrorMessage = TEXT("Unknown exception during parsing");
UE_LOG(logDTFluxNetwork, Error, TEXT("Unknown exception during async parsing for request %s"),
*RequestId.ToString());
}
const float ParsingTime = (FPlatformTime::Seconds() - StartTime) * 1000.0f; // En millisecondes
// === PROGRAMMER LA CALLBACK SUR LE MAIN THREAD ===
FFunctionGraphTask::CreateAndDispatchWhenReady(
[this, ParsedResponse, bParsingSuccess, ErrorMessage, ParsingTime]()
{
// Cette lambda s'exécute sur le main thread
if (bParsingSuccess && ParsedResponse.IsValid())
{
OnCompleted.ExecuteIfBound(RequestId, ParsedResponse, true);
}
else
{
OnFailed.ExecuteIfBound(RequestId, ErrorMessage);
}
},
TStatId(),
nullptr,
ENamedThreads::GameThread // Forcer l'exécution sur le main thread
);
}
// ================================================================================================
// IMPLÉMENTATION DU PARSER ASYNCHRONE
// ================================================================================================
FDTFluxAsyncParser::FDTFluxAsyncParser()
{
UE_LOG(logDTFluxNetwork, Log, TEXT("AsyncParser initialized"));
}
FDTFluxAsyncParser::~FDTFluxAsyncParser()
{
CancelAllParsing();
UE_LOG(logDTFluxNetwork, Log, TEXT("AsyncParser destroyed"));
}
void FDTFluxAsyncParser::ParseResponseAsync(
const FGuid& RequestId,
const FString& RawJsonData,
FOnParsingCompleted OnCompleted,
FOnParsingFailed OnFailed)
{
if (RawJsonData.IsEmpty())
{
OnFailed.ExecuteIfBound(RequestId, TEXT("Empty JSON data"));
return;
}
// Créer la tâche de parsing
FGraphEventRef Task = FFunctionGraphTask::CreateAndDispatchWhenReady(
[RequestId, RawJsonData, OnCompleted, OnFailed]()
{
// Ce code s'exécute sur le worker thread
const double StartTime = FPlatformTime::Seconds();
TSharedPtr<FDTFluxServerResponse> ParsedResponse;
bool bParsingSuccess = false;
FString ErrorMessage;
try
{
EDTFluxResponseStatus Status;
ParsedResponse = MakeShared<FDTFluxServerResponse>(RawJsonData, Status, false);
if (Status == EDTFluxResponseStatus::Success)
{
bParsingSuccess = true;
}
else
{
ErrorMessage = FString::Printf(TEXT("Parsing failed with status: %s"),
*UEnum::GetValueAsString(Status));
}
}
catch (const std::exception& e)
{
ErrorMessage = FString::Printf(TEXT("Exception during parsing: %s"), ANSI_TO_TCHAR(e.what()));
}
catch (...)
{
ErrorMessage = TEXT("Unknown exception during parsing");
}
const float ParsingTime = (FPlatformTime::Seconds() - StartTime) * 1000.0f;
FFunctionGraphTask::CreateAndDispatchWhenReady(
[RequestId, ParsedResponse, bParsingSuccess, ErrorMessage, OnCompleted, OnFailed]()
{
// Cette lambda s'exécute sur le main thread
if (bParsingSuccess && ParsedResponse.IsValid())
{
OnCompleted.ExecuteIfBound(RequestId, ParsedResponse, true);
}
else
{
OnFailed.ExecuteIfBound(RequestId, ErrorMessage);
}
},
TStatId(),
nullptr,
ENamedThreads::GameThread // Forcer main thread
);
},
TStatId(),
nullptr,
ENamedThreads::AnyBackgroundThreadNormalTask
);
// Tracker la tâche
{
FScopeLock Lock(&TasksLock);
ActiveTasks.Add(Task);
}
UE_LOG(logDTFluxNetwork, Verbose, TEXT("Queued async parsing task for request %s"), *RequestId.ToString());
}
TSharedPtr<FDTFluxServerResponse> FDTFluxAsyncParser::ParseResponseSync(
const FString& RawJsonData,
float TimeoutSeconds)
{
if (RawJsonData.IsEmpty())
{
return nullptr;
}
// Variables pour la synchronisation
TSharedPtr<FDTFluxServerResponse> Result;
std::atomic<bool> bCompleted{false};
// Lancer le parsing async avec callback sync
FOnParsingCompleted OnCompleted = FOnParsingCompleted::CreateLambda(
[&Result, &bCompleted](const FGuid& RequestId, TSharedPtr<FDTFluxServerResponse> ParsedResponse, bool bSuccess)
{
if (bSuccess)
{
Result = ParsedResponse;
}
bCompleted.store(true);
}
);
FOnParsingFailed OnFailed = FOnParsingFailed::CreateLambda(
[&bCompleted](const FGuid& RequestId, const FString& ErrorMessage)
{
UE_LOG(logDTFluxNetwork, Warning, TEXT("Sync parsing failed: %s"), *ErrorMessage);
bCompleted.store(true);
}
);
FGuid TempId = FGuid::NewGuid();
ParseResponseAsync(TempId, RawJsonData, OnCompleted, OnFailed);
// Attendre avec timeout
const double StartTime = FPlatformTime::Seconds();
while (!bCompleted.load() && (FPlatformTime::Seconds() - StartTime) < TimeoutSeconds)
{
FPlatformProcess::Sleep(0.001f); // 1ms
}
return Result;
}
void FDTFluxAsyncParser::CancelAllParsing()
{
FScopeLock Lock(&TasksLock);
for (const FGraphEventRef& Task : ActiveTasks)
{
// Note: On ne peut pas vraiment "cancel" une tâche TaskGraph en cours,
// mais on peut marquer qu'on ne veut plus les résultats
}
ActiveTasks.Empty();
UE_LOG(logDTFluxNetwork, Log, TEXT("Cancelled all pending parsing tasks"));
}
FDTFluxAsyncParser::FParsingStats FDTFluxAsyncParser::GetStats() const
{
FScopeLock StatsLock_Local(&StatsLock);
FScopeLock TasksLock_Local(&TasksLock);
FParsingStats Stats;
Stats.TasksInProgress = ActiveTasks.Num();
Stats.TasksCompleted = TasksCompletedCount;
Stats.TasksFailed = TasksFailedCount;
if (ParsingTimes.Num() > 0)
{
float Sum = 0.0f;
for (float Time : ParsingTimes)
{
Sum += Time;
}
Stats.AverageParsingTimeMs = Sum / ParsingTimes.Num();
}
return Stats;
}
void FDTFluxAsyncParser::ResetStats()
{
FScopeLock Lock(&StatsLock);
TasksCompletedCount = 0;
TasksFailedCount = 0;
ParsingTimes.Empty();
}
void FDTFluxAsyncParser::OnTaskCompleted(bool bSuccess, float ParsingTimeMs)
{
FScopeLock Lock(&StatsLock);
if (bSuccess)
{
TasksCompletedCount++;
}
else
{
TasksFailedCount++;
}
ParsingTimes.Add(ParsingTimeMs);
// Garder seulement les 100 derniers temps pour la moyenne
if (ParsingTimes.Num() > 100)
{
ParsingTimes.RemoveAt(0);
}
}
void FDTFluxAsyncParser::CleanupCompletedTasks()
{
FScopeLock Lock(&TasksLock);
for (auto It = ActiveTasks.CreateIterator(); It; ++It)
{
const FGraphEventRef& Task = *It;
if (Task.IsValid() && Task->IsComplete())
{
It.RemoveCurrent(); // Supprime l'élément actuel de manière sécurisée
}
}
}