Files
2025-05-18 13:04:45 +08:00

289 lines
7.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "WebAPILiquidJSProcess.h"
#include "Misc/AsyncTaskNotification.h"
#include "SocketSubsystem.h"
#include "WebAPILiquidJSLog.h"
#include "WebAPILiquidJSSettings.h"
#include "Async/Async.h"
#include "GenericPlatform/GenericPlatformFile.h"
#include "HAL/PlatformFileManager.h"
#include "Interfaces/IPluginManager.h"
#define LOCTEXT_NAMESPACE "WebAPILiquidJS"
FWebAPILiquidJSProcess::FWebAPILiquidJSProcess()
: Status(EStatus::Stopped)
, bForceBuildWebApp(false)
{
}
bool FWebAPILiquidJSProcess::TryStart()
{
Shutdown();
Status = EStatus::Launching;
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
const FString WebApp = IPluginManager::Get().FindPlugin("WebAPI")->GetBaseDir() / TEXT("WebAPIGeneratorApp");
Root = PlatformFile.ConvertToAbsolutePathForExternalAppForRead(*WebApp);
if (!PlatformFile.DirectoryExists(*Root))
{
UE_LOG(LogWebAPILiquidJS, Warning, TEXT("WebAPIGeneratorApp folder doesn't exist (%s)"), *Root);
return false;
}
FAsyncTaskNotificationConfig NotificationConfig;
NotificationConfig.bKeepOpenOnFailure = true;
NotificationConfig.TitleText = LOCTEXT("WebAPILiquidJS_Launch", "Launching WebAPILiquidJS");
#if !NO_LOGGING
NotificationConfig.LogCategory = &LogWebAPILiquidJS;
#endif
NotificationConfig.bIsHeadless = false;
TaskNotification = MakeUnique<FAsyncTaskNotification>(NotificationConfig);
Thread = FRunnableThread::Create(this, TEXT("FWebAPILiquidJSProcess"), 8 * 1024, TPri_BelowNormal);
return true;
}
void FWebAPILiquidJSProcess::Shutdown()
{
Status = EStatus::Stopped;
SetExternalLoggerEnabled(false);
if (Process.IsValid())
{
FPlatformProcess::TerminateProc(Process, true);
Process.Reset();
}
if (Thread)
{
Thread->Kill(true);
delete Thread;
Thread = nullptr;
}
}
FWebAPILiquidJSProcess::EStatus FWebAPILiquidJSProcess::GetStatus() const
{
return Status.load();
}
void FWebAPILiquidJSProcess::SetExternalLoggerEnabled(bool bEnableExternalLog) const
{
if (bEnableExternalLog)
{
const UWebAPILiquidJSSettings* Settings = GetDefault<UWebAPILiquidJSSettings>();
const TSharedRef<FInternetAddr> Address = ISocketSubsystem::Get()->CreateInternetAddr();
bool bIsValidIp = false;
Address->SetIp(TEXT("127.0.0.1"), bIsValidIp);
Address->SetPort(Settings->Port + 2);
}
}
uint32 FWebAPILiquidJSProcess::Run()
{
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
#if PLATFORM_WINDOWS
const FString StartScript = Root / TEXT("Start.bat");
#elif PLATFORM_MAC
const FString StartScript = Root / TEXT("Start.command");
#else
const FString StartScript = Root / TEXT("Start.sh");
#endif
const FText ErrorTitle = LOCTEXT("WebAPILiquidJS_ErrorTitle", "Failed to Launch the WebAPILiquidJS process");
if (!PlatformFile.FileExists(*StartScript))
{
TaskNotification->SetComplete(
ErrorTitle,
LOCTEXT("WebAPILiquidJS_FilesMissing", "Missing files in WebAPIGeneratorApp folder"),
false
);
Status = EStatus::Error;
return 0;
}
const UWebAPILiquidJSSettings* Settings = GetDefault<UWebAPILiquidJSSettings>();
FString Args = FString::Printf(TEXT("--port %d --uews %d --uehttp %d --monitor "),
Settings->Port,
Settings->WebSocketServerPort,
Settings->HttpServerPort);
if (Settings->bForceWebAppBuildAtStartup || bForceBuildWebApp)
{
Args.Append(TEXT("--build "));
}
if (Settings->bWebAppLogRequestDuration)
{
Args.Append(TEXT("--log "));
}
void* ReadPipe = nullptr;
void* WritePipe = nullptr;
if (!FPlatformProcess::CreatePipe(ReadPipe, WritePipe))
{
TaskNotification->SetComplete(
ErrorTitle,
LOCTEXT("WebAPILiquidJS_PipeFailed", "Failed to create Pipes for the WebAPILiquidJS process"),
false
);
Status = EStatus::Error;
return 0;
}
check(ReadPipe);
check(WritePipe);
Process = FPlatformProcess::CreateProc(
*StartScript, /* Path to start script */
*Args, /* Arguments with port numbers */
false, /* bLaunchDetached */
true, /* bLaunchHidden */
true, /* bLaunchReallyHidden */
nullptr, /* OutProcessID */
0, /* PriorityModifier */
*Root, /* OptionalWorkingDirectory */
WritePipe, /* PipeWriteChild */
ReadPipe /* PipeReadChild */
);
if (!Process.IsValid())
{
FPlatformProcess::ClosePipe(ReadPipe, WritePipe);
TaskNotification->SetComplete(
ErrorTitle,
LOCTEXT("WebAPILiquidJS_LaunchFailed", "Failed to start WebAPILiquidJS process"),
false
);
Status = EStatus::Error;
return 0;
}
bool bLoadDone = false;
auto LogReadPipe = [&](void* InReadPipe)
{
const FString ProcessOutput = FPlatformProcess::ReadPipe(InReadPipe);
if (ProcessOutput.Len() > 0)
{
TArray<FString> Lines;
ProcessOutput.ParseIntoArray(Lines, TEXT("\n"), false);
for (const FString& Line : Lines)
{
if (Line.Len() == 0)
{
continue;
}
if (!bLoadDone)
{
if (Line.StartsWith(TEXT("ERROR: ")))
{
bLoadDone = true;
const FString ErrorMessage = Line.Mid(7);
TaskNotification->SetComplete(ErrorTitle, FText::FromString(ErrorMessage), false);
Status = EStatus::Error;
}
else if (Line.StartsWith(TEXT("DONE: ")))
{
bLoadDone = true;
const FString CompleteMessage = Line.Mid(6);
TaskNotification->SetComplete(
LOCTEXT("WebAPILiquidJS_SuccessTitle", "WebAPILiquidJS is running"),
FText::FromString(CompleteMessage),
true);
Status = EStatus::Running;
if (Settings->bWebAppLogRequestDuration)
{
SetExternalLoggerEnabled(true);
}
}
else
{
TaskNotification->SetProgressText(FText::FromString(Line));
}
}
else
{
UE_LOG(LogWebAPILiquidJS, Log, TEXT("%s"), *Line);
}
}
}
return ProcessOutput.Len();
};
UE_LOG(LogWebAPILiquidJS, Log, TEXT("WebApp started, initial launch will take longer as it will be building the WebApp"));
while (FPlatformProcess::IsProcRunning(Process))
{
const int32 BytesRead = LogReadPipe(ReadPipe);
if (!BytesRead)
{
FPlatformProcess::Sleep(0.2);
}
}
// One last ReadPipe log for anything that the while loop didn't catch.
LogReadPipe(ReadPipe);
if (!bLoadDone)
{
TaskNotification->SetComplete(
ErrorTitle,
LOCTEXT("WebAPILiquidJS_LaunchFailed2", "WebApp exited"),
false
);
}
else
{
UE_LOG(LogWebAPILiquidJS, Log, TEXT("WebApp exited"));
}
EStatus PreStopStatus = Status;
Status = EStatus::Stopped;
FPlatformProcess::ClosePipe(ReadPipe, WritePipe);
Process.Reset();
// If haven't already, try again with --build flag
if(PreStopStatus == EStatus::Error && !bForceBuildWebApp && !Settings->bForceWebAppBuildAtStartup)
{
bForceBuildWebApp = true;
Async(EAsyncExecution::TaskGraphMainThread, [this]()
{
FAsyncTaskNotificationConfig NotificationConfig;
NotificationConfig.TitleText = LOCTEXT("WebAPILiquidJS_Launch", "Launching WebAPILiquidJS");
#if !NO_LOGGING
NotificationConfig.LogCategory = &LogWebAPILiquidJS;
#endif
NotificationConfig.bIsHeadless = false;
TaskNotification.Reset();
TaskNotification = MakeUnique<FAsyncTaskNotification>(NotificationConfig);
}).Wait();
return Run();
}
return 0;
}
#undef LOCTEXT_NAMESPACE