Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Source/Edgegap/Private/APIToken/APITokenSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ struct FAPITokenSettings
FString APIToken;
};

#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2
#if defined(UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2) && UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2
#include "CoreMinimal.h"
#endif
2 changes: 1 addition & 1 deletion Source/Edgegap/Private/EdgegapSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,6 @@ class UEdgegapSettings : public UDeveloperSettings
bool bIsTokenVerified = true;
};

#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2
#if defined(UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2) && UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2
#include "CoreMinimal.h"
#endif
51 changes: 32 additions & 19 deletions Source/Edgegap/Private/EdgegapSettingsDetails.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,9 @@ void FEdgegapSettingsDetails::PackageProject(const FName IniPlatformName)
// this may delete UProjectPackagingSettings , don't hold it across this call
FEdgegapSettingsDetails::SaveAll();

// Re-get PackagingSettings after SaveAll() as it may have been invalidated
UProjectPackagingSettings* PackagingSettingsAfterSave = GetMutableDefault<UProjectPackagingSettings>();

// basic BuildCookRun params we always want
FString BuildCookRunParams = FString::Printf(TEXT("-nop4 -utf8output %s -cook "), GetUATCompilationFlags());

Expand All @@ -1047,8 +1050,17 @@ void FEdgegapSettingsDetails::PackageProject(const FName IniPlatformName)
BuildCookRunParams += FString::Printf(TEXT(" -project=\"%s\""), *ProjectPath);
}

// Get build target info - in UE 5.7, use PackagingSettings instead of PlatformsSettings
const FTargetInfo* BuildTargetInfo = PackagingSettingsAfterSave->GetBuildTargetInfo();
bool bIsProjectBuildTarget = false;
const FTargetInfo* BuildTargetInfo = PlatformsSettings->GetBuildTargetInfoForPlatform(IniPlatformName, bIsProjectBuildTarget);

// Check if this is a project build target (not engine target)
if (BuildTargetInfo)
{
FString ProjectDir = FPaths::GetPath(FPaths::GetProjectFilePath());
FString TargetPath = BuildTargetInfo->Path;
bIsProjectBuildTarget = FPaths::IsUnderDirectory(TargetPath, ProjectDir);
}

// Only add the -Target=... argument for code projects. Content projects will return UnrealGame/UnrealClient/UnrealServer here, but
// may need a temporary target generated to enable/disable plugins. Specifying -Target in these cases will cause packaging to fail,
Expand Down Expand Up @@ -1078,7 +1090,7 @@ void FEdgegapSettingsDetails::PackageProject(const FName IniPlatformName)
}

// optional settings
if (PackagingSettings->bSkipEditorContent)
if (PackagingSettingsAfterSave->bSkipEditorContent)
{
BuildCookRunParams += TEXT(" -SkipCookingEditorContent");
}
Expand Down Expand Up @@ -1126,72 +1138,73 @@ void FEdgegapSettingsDetails::PackageProject(const FName IniPlatformName)
BuildCookRunParams += TEXT(" -stage -archive -package");

const ITargetPlatform* TargetPlatform = GetTargetPlatformManager()->FindTargetPlatform(PlatformInfo->Name);
if (ShouldBuildProject(PackagingSettings, TargetPlatform))
if (ShouldBuildProject(PackagingSettingsAfterSave, TargetPlatform))
{
BuildCookRunParams += TEXT(" -build");
}

if (PackagingSettings->FullRebuild)
if (PackagingSettingsAfterSave->FullRebuild)
{
BuildCookRunParams += TEXT(" -clean");
}

if (PackagingSettings->bCompressed)
if (PackagingSettingsAfterSave->bCompressed)
{
BuildCookRunParams += TEXT(" -compressed");
}

if (PackagingSettings->bUseIoStore)
if (PackagingSettingsAfterSave->bUseIoStore)
{
BuildCookRunParams += TEXT(" -iostore");

// Pak file(s) must be used when using container file(s)
PackagingSettings->UsePakFile = true;
PackagingSettingsAfterSave->UsePakFile = true;
}

if (PackagingSettings->UsePakFile)
if (PackagingSettingsAfterSave->UsePakFile)
{
BuildCookRunParams += TEXT(" -pak");
}

if (PackagingSettings->IncludePrerequisites)
if (PackagingSettingsAfterSave->IncludePrerequisites)
{
BuildCookRunParams += TEXT(" -prereqs");
}

if (!PackagingSettings->ApplocalPrerequisitesDirectory.Path.IsEmpty())
if (!PackagingSettingsAfterSave->ApplocalPrerequisitesDirectory.Path.IsEmpty())
{
BuildCookRunParams += FString::Printf(TEXT(" -applocaldirectory=\"%s\""), *(PackagingSettings->ApplocalPrerequisitesDirectory.Path));
BuildCookRunParams += FString::Printf(TEXT(" -applocaldirectory=\"%s\""), *(PackagingSettingsAfterSave->ApplocalPrerequisitesDirectory.Path));
}
else if (PackagingSettings->IncludeAppLocalPrerequisites)
else if (PackagingSettingsAfterSave->IncludeAppLocalPrerequisites)
{
BuildCookRunParams += TEXT(" -applocaldirectory=\"$(EngineDir)/Binaries/ThirdParty/AppLocalDependencies\"");
}

BuildCookRunParams += FString::Printf(TEXT(" -archivedirectory=\"%s\""), *PlatformsSettings->StagingDirectory.Path);

if (PackagingSettings->ForDistribution)
if (PackagingSettingsAfterSave->ForDistribution)
{
BuildCookRunParams += TEXT(" -distribution");
}

if (PackagingSettings->bGenerateChunks)
if (PackagingSettingsAfterSave->bGenerateChunks)
{
BuildCookRunParams += TEXT(" -manifests");
}

// Whether to include the crash reporter.
if (PackagingSettings->IncludeCrashReporter && PlatformInfo->DataDrivenPlatformInfo->bCanUseCrashReporter)
if (PackagingSettingsAfterSave->IncludeCrashReporter && PlatformInfo->DataDrivenPlatformInfo->bCanUseCrashReporter)
{
BuildCookRunParams += TEXT(" -CrashReporter");
}

if (PackagingSettings->bBuildHttpChunkInstallData)
if (PackagingSettingsAfterSave->bBuildHttpChunkInstallData)
{
BuildCookRunParams += FString::Printf(TEXT(" -manifests -createchunkinstall -chunkinstalldirectory=\"%s\" -chunkinstallversion=%s"), *(PackagingSettings->HttpChunkInstallDataDirectory.Path), *(PackagingSettings->HttpChunkInstallDataVersion));
BuildCookRunParams += FString::Printf(TEXT(" -manifests -createchunkinstall -chunkinstalldirectory=\"%s\" -chunkinstallversion=%s"), *(PackagingSettingsAfterSave->HttpChunkInstallDataDirectory.Path), *(PackagingSettingsAfterSave->HttpChunkInstallDataVersion));
}

EProjectPackagingBuildConfigurations BuildConfig = PlatformsSettings->GetBuildConfigurationForPlatform(IniPlatformName);
// Get build configuration - in UE 5.7, use PackagingSettings instead of PlatformsSettings
EProjectPackagingBuildConfigurations BuildConfig = PackagingSettingsAfterSave->BuildConfiguration;
UEdgegapSettings* EdgegapSettings = GetMutableDefault<UEdgegapSettings>();
if(EdgegapSettings)
{
Expand All @@ -1205,7 +1218,7 @@ void FEdgegapSettingsDetails::PackageProject(const FName IniPlatformName)

BuildCookRunParams += FString::Printf(TEXT(" -server -noclient -serverconfig=%s"), LexToString(ConfigurationInfo.Configuration));

if (ConfigurationInfo.Configuration == EBuildConfiguration::Shipping && !PackagingSettings->IncludeDebugFiles)
if (ConfigurationInfo.Configuration == EBuildConfiguration::Shipping && !PackagingSettingsAfterSave->IncludeDebugFiles)
{
BuildCookRunParams += TEXT(" -nodebuginfo");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@ void UEdgegapServerShutdownSubsystem::Initialize(FSubsystemCollectionBase& Colle
CachedRequestID = FPlatformMisc::GetEnvironmentVariable(TEXT("ARBITRIUM_REQUEST_ID"));
}

bIsEdgegapEnvironment = !CachedRequestID.IsEmpty();
// Get production self-stop URL and token (preferred method for production)
CachedDeleteURL = FPlatformMisc::GetEnvironmentVariable(TEXT("ARBITRIUM_DELETE_URL"));
CachedDeleteToken = FPlatformMisc::GetEnvironmentVariable(TEXT("ARBITRIUM_DELETE_TOKEN"));

bIsEdgegapEnvironment = !CachedDeleteURL.IsEmpty() && !CachedDeleteToken.IsEmpty();

if (bIsEdgegapEnvironment)
{
UE_LOG(LogTemp, Log, TEXT("EdgegapServerShutdown: Detected Edgegap environment. Request ID: %s"), *CachedRequestID);
UE_LOG(LogTemp, Log, TEXT("EdgegapServerShutdown: Detected Edgegap environment with production self-stop endpoint"));

// Setup shutdown hooks
SetupShutdownHooks();
Expand All @@ -52,7 +56,7 @@ void UEdgegapServerShutdownSubsystem::Initialize(FSubsystemCollectionBase& Colle
}
else
{
UE_LOG(LogTemp, Log, TEXT("EdgegapServerShutdown: Not running on Edgegap (no request ID found)"));
UE_LOG(LogTemp, Log, TEXT("EdgegapServerShutdown: Not running on Edgegap (no ARBITRIUM_DELETE_URL found)"));
}
}

Expand Down Expand Up @@ -91,15 +95,25 @@ void UEdgegapServerShutdownSubsystem::CallSelfStopAPI(bool bWaitForResponse)
return;
}

if (!bIsEdgegapEnvironment || CachedRequestID.IsEmpty())
if (!bIsEdgegapEnvironment)
{
UE_LOG(LogTemp, Warning, TEXT("EdgegapServerShutdown: Cannot call self-stop API - not running on Edgegap or no request ID"));
UE_LOG(LogTemp, Warning, TEXT("EdgegapServerShutdown: Cannot call self-stop API - not running on Edgegap"));
OnServerShutdown.Broadcast(false, TEXT("Not running on Edgegap environment"));
return;
}

bShutdownInitiated = true;
StopDeploymentInternal(CachedRequestID);

// Use production self-stop method (ARBITRIUM_DELETE_URL) - required for production
if (!CachedDeleteURL.IsEmpty() && !CachedDeleteToken.IsEmpty())
{
StopDeploymentInternal(CachedDeleteURL, CachedDeleteToken);
}
else
{
UE_LOG(LogTemp, Error, TEXT("EdgegapServerShutdown: Production self-stop method not available - missing ARBITRIUM_DELETE_URL or ARBITRIUM_DELETE_TOKEN"));
OnServerShutdown.Broadcast(false, TEXT("Production self-stop method not available - missing ARBITRIUM_DELETE_URL or ARBITRIUM_DELETE_TOKEN"));
}

if (bWaitForResponse)
{
Expand All @@ -118,8 +132,9 @@ void UEdgegapServerShutdownSubsystem::CallSelfStopAPI(bool bWaitForResponse)
}
}

void UEdgegapServerShutdownSubsystem::StopDeploymentInternal(const FString& RequestID)
void UEdgegapServerShutdownSubsystem::StopDeploymentInternal(const FString& DeleteURL, const FString& DeleteToken)
{
// Production self-stop endpoint (preferred method)
FHttpModule* Http = &FHttpModule::Get();
if (!Http)
{
Expand All @@ -128,26 +143,16 @@ void UEdgegapServerShutdownSubsystem::StopDeploymentInternal(const FString& Requ
return;
}

FString AuthorizationKey = UEGIKBlueprintFunctionLibrary::GetAuthorizationKey();
if (AuthorizationKey.IsEmpty())
{
UE_LOG(LogTemp, Error, TEXT("EdgegapServerShutdown: Authorization key not found"));
OnServerShutdown.Broadcast(false, TEXT("Authorization key not configured"));
return;
}

FString URL = FString::Printf(TEXT("https://api.edgegap.com/v1/stop/%s"), *RequestID);

ShutdownRequest = Http->CreateRequest();
ShutdownRequest->SetURL(URL);
ShutdownRequest->SetURL(DeleteURL);
ShutdownRequest->SetVerb("DELETE");
ShutdownRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
ShutdownRequest->SetHeader(TEXT("Authorization"), *AuthorizationKey);
ShutdownRequest->SetHeader(TEXT("Authorization"), *DeleteToken);
ShutdownRequest->SetTimeout(5); // 5 second timeout

ShutdownRequest->OnProcessRequestComplete().BindUObject(this, &UEdgegapServerShutdownSubsystem::OnShutdownResponseReceived);

UE_LOG(LogTemp, Log, TEXT("EdgegapServerShutdown: Calling self-stop API for request ID: %s"), *RequestID);
UE_LOG(LogTemp, Log, TEXT("EdgegapServerShutdown: Calling production self-stop API"));

if (!ShutdownRequest->ProcessRequest())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class EDGEGAPINTEGRATIONKIT_API UEdgegapServerShutdownSubsystem : public UGameIn

/**
* Check if the server is running in Edgegap environment
* @return True if EDGEGAP_REQUEST_ID environment variable is set
* @return True if ARBITRIUM_DELETE_URL and ARBITRIUM_DELETE_TOKEN environment variables are set
*/
UFUNCTION(BlueprintPure, Category = "Edgegap Integration Kit | Server")
bool IsRunningOnEdgegap() const;
Expand All @@ -53,7 +53,13 @@ class EDGEGAPINTEGRATIONKIT_API UEdgegapServerShutdownSubsystem : public UGameIn
/** The deployment request ID from environment */
FString CachedRequestID;

/** Whether we're running on Edgegap (have request ID) */
/** Production self-stop URL from ARBITRIUM_DELETE_URL environment variable */
FString CachedDeleteURL;

/** Production self-stop token from ARBITRIUM_DELETE_TOKEN environment variable */
FString CachedDeleteToken;

/** Whether we're running on Edgegap (have request ID or delete URL) */
bool bIsEdgegapEnvironment;

/** Flag to prevent multiple shutdown calls */
Expand All @@ -65,8 +71,8 @@ class EDGEGAPINTEGRATIONKIT_API UEdgegapServerShutdownSubsystem : public UGameIn
/** Callback for shutdown API response */
void OnShutdownResponseReceived(TSharedPtr<IHttpRequest> HttpRequest, TSharedPtr<IHttpResponse> HttpResponse, bool bArg);

/** Internal method to stop deployment */
void StopDeploymentInternal(const FString& RequestID);
/** Internal method to stop deployment using production self-stop endpoint */
void StopDeploymentInternal(const FString& DeleteURL, const FString& DeleteToken);

/** Setup crash handlers */
void SetupCrashHandlers();
Expand Down
37 changes: 12 additions & 25 deletions StartServer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,33 @@ env
# Function to call Edgegap self-stop API
call_stop_api() {
local EXIT_CODE=$1
# Try multiple possible environment variable names for request ID
local REQUEST_ID="${EG_REQUEST_ID:-${EDGEGAP_REQUEST_ID:-${EDGE_REQUEST_ID:-${ARBITRIUM_REQUEST_ID}}}}"

# API key may not be available as env var (typically stored in config)
# Try environment first, then common config locations
local API_KEY="${EG_API_KEY:-${EDGEGAP_API_KEY:-${EDGE_API_KEY}}}"
# Use production self-stop endpoint (ARBITRIUM_DELETE_URL) - required for production
local DELETE_URL="${ARBITRIUM_DELETE_URL}"
local DELETE_TOKEN="${ARBITRIUM_DELETE_TOKEN}"

# If API key not in env, try reading from config file (if available)
if [ -z "$API_KEY" ] && [ -f "${UE_PROJECT_DIR}/Saved/Config/LinuxServer/DefaultEngine.ini" ]; then
API_KEY=$(grep -m 1 "^AuthorizationKey=" "${UE_PROJECT_DIR}/Saved/Config/LinuxServer/DefaultEngine.ini" 2>/dev/null | cut -d'=' -f2 | tr -d ' ')
fi

if [ -z "$REQUEST_ID" ]; then
echo "No Edgegap request ID found in environment variables (checked: EG_REQUEST_ID, EDGEGAP_REQUEST_ID, EDGE_REQUEST_ID, ARBITRIUM_REQUEST_ID)"
echo "Note: The C++ subsystem may still call the self-stop API if it has access to the request ID"
exit $EXIT_CODE
fi

if [ -z "$API_KEY" ]; then
echo "No Edgegap API key found (checked env vars and config file)"
echo "Note: The C++ subsystem may still call the self-stop API if it has access to the API key from config"
if [ -z "$DELETE_URL" ] || [ -z "$DELETE_TOKEN" ]; then
echo "Error: Production self-stop method not available - missing ARBITRIUM_DELETE_URL or ARBITRIUM_DELETE_TOKEN"
echo "Note: The C++ subsystem may still call the self-stop API if it has access to the environment variables"
exit $EXIT_CODE
fi

echo "Calling Edgegap self-stop API for request ID: $REQUEST_ID"
# Production method - use deployment-specific URL and token
echo "Calling Edgegap production self-stop API"

# Call the self-stop API
RESPONSE=$(curl -s -w "\n%{http_code}" -X DELETE \
-H "Content-Type: application/json" \
-H "Authorization: $API_KEY" \
"https://api.edgegap.com/v1/stop/$REQUEST_ID" \
-H "Authorization: $DELETE_TOKEN" \
"$DELETE_URL" \
--max-time 5)

HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | sed '$d')

if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
echo "Successfully called self-stop API (HTTP $HTTP_CODE)"
echo "Successfully called production self-stop API (HTTP $HTTP_CODE)"
else
echo "Self-stop API call returned HTTP $HTTP_CODE: $BODY"
echo "Production self-stop API call returned HTTP $HTTP_CODE: $BODY"
fi

exit $EXIT_CODE
Expand Down