You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden ========================== MAJOR FEATURES + CHANGES ========================== Change 2861045 on 2016/02/09 by Marcus.Wassmer Fix debug editor crash from async compute creating commands when it shouldn't. #rb none #test debug editor Change 2861030 on 2016/02/09 by Michael.Noland Engine: Added support for debugging safe zones (visualization on any platform and simulation on platforms that don't natively provide safe zone information) r.DebugSafeZone.Mode controls the debug visualization overlay (0..2, default 0) - 0: Do not display the safe zone overlay - 1: Display the overlay for the title safe zone - 2: Display the overlay for the action safe zone r.DebugSafeZone.OverlayAlpha controls how opaque the debug visualization overlay is (0..1, default 0.3) On platforms that don't natively support safe zones, you can simulate a safe zone for quick/easy testing in the editor: - r.DebugSafeZone.TitleRatio controls the title safe zone margins returned in FDisplayMetrics - r.DebugActionZone.ActionRatio controls the action safe zone margins returned in FDisplayMetrics - These both range from 0..1, and default to 1 indicating 100% of the display is safe. A typical test value would be 0.9 #codereview josh.adams #rb marcus.wassmer #tests Tested on Win64 uncooked and PS4 cooked (front-end and game) Change 2860923 on 2016/02/09 by Andrew.Grant Fix client warning about HTTPChunkInstaller module not existing #rb none #tests ran Win64 client Change 2860852 on 2016/02/09 by Daniel.Wright Fixed crash enabling capsule direct shadows in BP #rb Nick.Penwarden #tests Editor Change 2860842 on 2016/02/09 by Marcus.Wassmer MallocLeakDetection proxy #rb Steve.Robb #test PS4/PC testing all commands. Change 2860744 on 2016/02/09 by Josh.Markiewicz #UE4 - fixed possible crash when refresh auth with invalid response #rb sam.zamani #tests login flow #codereview justin.sargent, joe.wilcox, pter.knepley, ben.zeigler Change 2860739 on 2016/02/09 by Laurent.Delayen Sync Markers - Reset SyncGroups every frame. - ::GetSyncGroupPosition() makes sure there is a valid MarkerSyncContext. => Fixes SyncGroup returning 'valid' positions for TransitionLeaders that were not in between sync markers. #rb martin.wilson #codereview lina.halper #tests new riftmage and kurohane networked in PIE Change 2860736 on 2016/02/09 by Daniel.Lamb Fixed issue with iterative cook on the fly invalidating cooked content all the time. #rb Marcus.Wassmer #test Cook on the fly iterative ps4 Change 2860598 on 2016/02/09 by Joe.Graf Simple log category change to match existing log messages in LoadMap #rb: n/a #test: loading, cooking, game Change 2860559 on 2016/02/09 by Zak.Middleton #orion - Add flag to AIController to control whether it copies the Pawn rotation to ControlRotation if there is no focus point. #rb Lukasz.Furman #tests PIE ded server AI with lanes Change 2860462 on 2016/02/09 by Marc.Audy Build system improvements * Added details to Empty manifest file save error * Removed redundent pseudo-dependencies from -showdependency output * Monolithic Kinds now a set and branch hacker can specify kind not to build #rb Ben.Marsh #tests Preflight Change 2860434 on 2016/02/09 by David.Ratti NaN checks: -Targeting mode checks in orion code -Changed the AActor::SetTransform NaN check so that the logging is included in the NaN ensure (rather than getting cut off from the log afterwards). #rb FrankG #tests golden path vs bots Change 2860390 on 2016/02/09 by Michael.Trepka Adjust 3D rendering resolution so that it stays approximately the same when user switches display modes #rb none #tests Tested editor build on PC Change 2860364 on 2016/02/09 by Justin.Sargent Removed unused editor-only functions causing compiler errors when compiling the game. #rb keli #tests none Change 2860242 on 2016/02/09 by Justin.Sargent Made a number of DialogueWave quality of life improvements to the editor and specifically SoundCue editing. New right-click option on SoundWaves to create a DialogueWave [CL 2863630 by Andrew Grant in Main branch]
329 lines
11 KiB
C++
329 lines
11 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "QoSReporterPrivatePCH.h"
|
|
|
|
#include "Core.h"
|
|
#include "Json.h"
|
|
#include "Analytics.h"
|
|
#include "Http.h"
|
|
#include "EngineVersion.h"
|
|
#include "QoSReporter.h"
|
|
|
|
#if USE_SERVER_PERF_COUNTERS
|
|
#include "PerfCountersHelpers.h"
|
|
#endif
|
|
|
|
DEFINE_LOG_CATEGORY(LogQoSReporter);
|
|
|
|
IMPLEMENT_MODULE(FQoSReporterModule, QoSReporter);
|
|
|
|
// helps to version QoS events (date*10 to allow for 10 revisions per day)
|
|
#define QOS_EVENTS_REVISION 201602080
|
|
|
|
FString FQoSReporterModule::Config::GetDefaultAppVersion()
|
|
{
|
|
return FString::Printf(TEXT("UE4-CL-%d"), FEngineVersion::Current().GetChangelist());
|
|
}
|
|
|
|
FString FQoSReporterModule::Config::GetDefaultAppEnvironment()
|
|
{
|
|
return FAnalytics::ToString(FAnalytics::Get().GetBuildType());
|
|
}
|
|
|
|
class FAnalyticsProviderQoSReporter : public IAnalyticsProvider
|
|
{
|
|
public:
|
|
FAnalyticsProviderQoSReporter(const FQoSReporterModule::Config& ConfigValues);
|
|
|
|
/** This provider does not have a concept of sessions */
|
|
virtual bool StartSession(const TArray<FAnalyticsEventAttribute>& Attributes) override { return true; };
|
|
/** This provider does not have a concept of sessions */
|
|
virtual void EndSession() override {};
|
|
/** This provider is not supposed to send many events, and due to nature of QoS we don't want to cache them */
|
|
virtual void FlushEvents() override {};
|
|
|
|
/** This provider is not using user IDs */
|
|
virtual void SetUserID(const FString& InUserID) override {};
|
|
/** This provider is not using user IDs, but we're (ab)using this API to return InstanceId */
|
|
virtual FString GetUserID() const override { return InstanceId.ToString(); };
|
|
|
|
/** This provider does not have a concept of sessions */
|
|
virtual FString GetSessionID() const override { checkf(false, TEXT("FAnalyticsProviderQoSReporter is not session based")); return TEXT("UnknownSessionId"); };
|
|
/** This provider does not have a concept of sessions */
|
|
virtual bool SetSessionID(const FString& InSessionID) override { return false; };
|
|
|
|
/** We're (ab)using this API to set DeploymentName */
|
|
virtual void SetLocation(const FString& InLocation) override { DeploymentName = InLocation; };
|
|
|
|
virtual void RecordEvent(const FString& InEventName, const TArray<FAnalyticsEventAttribute>& Attributes) override;
|
|
virtual ~FAnalyticsProviderQoSReporter();
|
|
|
|
FString GetAPIKey() const { return APIKey; }
|
|
|
|
private:
|
|
|
|
/** API key (also known as "upload type" on data router) */
|
|
FString APIKey;
|
|
/** API Server to use (also known as "endpoint"). */
|
|
FString APIServer;
|
|
/** The AppVersion to use. */
|
|
FString AppVersion;
|
|
/** The AppEnvironment to use. */
|
|
FString AppEnvironment;
|
|
/** The upload type to use. */
|
|
FString UploadType;
|
|
|
|
/** Unique identifier for this QoS reporter instance (only changed on module initialization) */
|
|
FGuid InstanceId;
|
|
/** Deployment name (if empty, it won't be sent). */
|
|
FString DeploymentName;
|
|
|
|
/**
|
|
* Delegate called when an event Http request completes
|
|
*/
|
|
void EventRequestComplete(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded);
|
|
|
|
/**
|
|
* Returns application role (server, client)
|
|
*/
|
|
static FString GetApplicationRole();
|
|
};
|
|
|
|
void FQoSReporterModule::StartupModule()
|
|
{
|
|
// FQoSReporter::Initialize() is expected to be called by game code with proper config
|
|
}
|
|
|
|
void FQoSReporterModule::ShutdownModule()
|
|
{
|
|
FQoSReporter::Shutdown();
|
|
}
|
|
|
|
TSharedPtr<IAnalyticsProvider> FQoSReporterModule::CreateAnalyticsProvider(const FAnalytics::FProviderConfigurationDelegate& GetConfigValue) const
|
|
{
|
|
if (GetConfigValue.IsBound())
|
|
{
|
|
Config ConfigValues;
|
|
ConfigValues.APIServer = GetConfigValue.Execute(Config::GetKeyNameForAPIServer(), true);
|
|
ConfigValues.APIKey = GetConfigValue.Execute(Config::GetKeyNameForAPIKey(), false);
|
|
ConfigValues.AppVersion = GetConfigValue.Execute(Config::GetKeyNameForAppVersion(), false);
|
|
ConfigValues.AppEnvironment = GetConfigValue.Execute(Config::GetKeyNameForAppEnvironment(), false);
|
|
ConfigValues.UploadType = GetConfigValue.Execute(Config::GetKeyNameForUploadType(), false);
|
|
return CreateAnalyticsProvider(ConfigValues);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogQoSReporter, Warning, TEXT("CreateAnalyticsProvider called with an unbound delegate"));
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
TSharedPtr<IAnalyticsProvider> FQoSReporterModule::CreateAnalyticsProvider(const Config& ConfigValues) const
|
|
{
|
|
return TSharedPtr<IAnalyticsProvider>(new FAnalyticsProviderQoSReporter(ConfigValues));
|
|
}
|
|
|
|
/**
|
|
* Perform any initialization.
|
|
*/
|
|
FAnalyticsProviderQoSReporter::FAnalyticsProviderQoSReporter(const FQoSReporterModule::Config& ConfigValues)
|
|
{
|
|
UE_LOG(LogQoSReporter, Verbose, TEXT("Initializing QoS Reporter"));
|
|
|
|
APIKey = ConfigValues.APIKey;
|
|
if (APIKey.IsEmpty())
|
|
{
|
|
UE_LOG(LogQoSReporter, Error, TEXT("QoS API key is not configured, no QoS metrics will be reported."));
|
|
}
|
|
|
|
APIServer = ConfigValues.APIServer;
|
|
if (APIServer.IsEmpty())
|
|
{
|
|
UE_LOG(LogQoSReporter, Error, TEXT("QoS API server is not configured, no QoS metrics will be reported."));
|
|
}
|
|
|
|
AppVersion = ConfigValues.AppVersion;
|
|
if (AppVersion.IsEmpty())
|
|
{
|
|
AppVersion = ConfigValues.GetDefaultAppVersion();
|
|
}
|
|
|
|
AppEnvironment = ConfigValues.AppEnvironment;
|
|
if (AppEnvironment.IsEmpty())
|
|
{
|
|
AppEnvironment = ConfigValues.GetDefaultAppEnvironment();
|
|
}
|
|
|
|
UploadType = ConfigValues.UploadType;
|
|
if (UploadType.IsEmpty())
|
|
{
|
|
UploadType = ConfigValues.GetDefaultUploadType();
|
|
}
|
|
|
|
// add a unique id
|
|
InstanceId = FGuid::NewGuid();
|
|
FPlatformMisc::CreateGuid(InstanceId);
|
|
|
|
UE_LOG(LogQoSReporter, Log, TEXT("QoSReporter initialized (Guid = '%s')"), *InstanceId.ToString());
|
|
UE_LOG(LogQoSReporter, Log, TEXT("APIKey = '%s'. APIServer = '%s'. AppVersion = '%s'. AppEnvironment = '%s'"), *APIKey, *APIServer, *AppVersion, *AppEnvironment);
|
|
}
|
|
|
|
FAnalyticsProviderQoSReporter::~FAnalyticsProviderQoSReporter()
|
|
{
|
|
UE_LOG(LogQoSReporter, Verbose, TEXT("Destroying QoS Reporter"));
|
|
EndSession();
|
|
}
|
|
|
|
void FAnalyticsProviderQoSReporter::RecordEvent(const FString& InEventName, const TArray<FAnalyticsEventAttribute>& InAttributes)
|
|
{
|
|
if (APIKey.IsEmpty() || APIServer.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// for data router, it is actually preferable to have different events than attach more attributes, so
|
|
// append application role to the event name instead
|
|
FString EventName = InEventName;
|
|
EventName += TEXT(".");
|
|
EventName += GetApplicationRole();
|
|
|
|
// add attributes common to each QoS event first
|
|
TArray<FAnalyticsEventAttribute> Attributes;
|
|
Attributes.Add(FAnalyticsEventAttribute(TEXT("QoSRevision"), QOS_EVENTS_REVISION));
|
|
Attributes.Add(FAnalyticsEventAttribute(TEXT("SystemId"), FPlatformMisc::GetOperatingSystemId()));
|
|
Attributes.Add(FAnalyticsEventAttribute(TEXT("InstanceId"), InstanceId.ToString()));
|
|
Attributes.Add(FAnalyticsEventAttribute(TEXT("Platform"), FString(FPlatformProperties::PlatformName())));
|
|
if (LIKELY(DeploymentName.Len() > 0))
|
|
{
|
|
Attributes.Add(FAnalyticsEventAttribute(TEXT("Deployment"), DeploymentName));
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogQoSReporter, Warning, TEXT("QoSReporter was not configured for any deployment; metrics will be likely discarded."));
|
|
}
|
|
|
|
// append the rest
|
|
Attributes += InAttributes;
|
|
|
|
// encode params as Json
|
|
if (ensure(FModuleManager::Get().IsModuleLoaded("HTTP")))
|
|
{
|
|
FString Payload;
|
|
|
|
FDateTime CurrentTime = FDateTime::UtcNow();
|
|
|
|
TSharedRef< TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR> > > JsonWriter = TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR> >::Create(&Payload);
|
|
JsonWriter->WriteObjectStart();
|
|
JsonWriter->WriteArrayStart(TEXT("Events"));
|
|
|
|
// write just a single event
|
|
JsonWriter->WriteObjectStart();
|
|
JsonWriter->WriteValue(TEXT("EventName"), EventName);
|
|
|
|
if (Attributes.Num() > 0)
|
|
{
|
|
// optional attributes for this event
|
|
for (int32 AttrIdx = 0; AttrIdx < Attributes.Num(); AttrIdx++)
|
|
{
|
|
const FAnalyticsEventAttribute& Attr = Attributes[AttrIdx];
|
|
JsonWriter->WriteValue(Attr.AttrName, Attr.AttrValue);
|
|
}
|
|
}
|
|
JsonWriter->WriteObjectEnd();
|
|
|
|
JsonWriter->WriteArrayEnd();
|
|
JsonWriter->WriteObjectEnd();
|
|
JsonWriter->Close();
|
|
|
|
FString URLPath = FString::Printf(TEXT("?AppID=%s&AppVersion=%s&AppEnvironment=%s&UploadType=%s"),
|
|
*FGenericPlatformHttp::UrlEncode(APIKey),
|
|
*FGenericPlatformHttp::UrlEncode(AppVersion),
|
|
*FGenericPlatformHttp::UrlEncode(AppEnvironment),
|
|
*FGenericPlatformHttp::UrlEncode(UploadType)
|
|
);
|
|
|
|
// Recreate the URLPath for logging because we do not want to escape the parameters when logging.
|
|
// We cannot simply UrlEncode the entire Path after logging it because UrlEncode(Params) != UrlEncode(Param1) & UrlEncode(Param2) ...
|
|
UE_LOG(LogQoSReporter, VeryVerbose, TEXT("[%s] QoS URL:%s?AppID=%s&AppVersion=%s&AppEnvironment=%s&UploadType=%s. Payload:%s"),
|
|
*APIKey,
|
|
*APIServer,
|
|
*APIKey,
|
|
*AppVersion,
|
|
*AppEnvironment,
|
|
*UploadType,
|
|
*Payload);
|
|
|
|
// Create/send Http request for an event
|
|
TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
|
|
HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json; charset=utf-8"));
|
|
|
|
HttpRequest->SetURL(APIServer + URLPath);
|
|
HttpRequest->SetVerb(TEXT("POST"));
|
|
HttpRequest->SetContentAsString(Payload);
|
|
HttpRequest->OnProcessRequestComplete().BindRaw(this, &FAnalyticsProviderQoSReporter::EventRequestComplete);
|
|
HttpRequest->ProcessRequest();
|
|
|
|
}
|
|
}
|
|
|
|
void FAnalyticsProviderQoSReporter::EventRequestComplete(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded)
|
|
{
|
|
bool bCountTowardsFailedAttempts = true;
|
|
|
|
if (bSucceeded && HttpResponse.IsValid())
|
|
{
|
|
// normal operation is silent, but any problems are reported as warnings
|
|
if (EHttpResponseCodes::IsOk(HttpResponse->GetResponseCode()))
|
|
{
|
|
UE_LOG(LogQoSReporter, VeryVerbose, TEXT("QoS response for [%s]. Code: %d. Payload: %s"),
|
|
*HttpRequest->GetURL(), HttpResponse->GetResponseCode(), *HttpResponse->GetContentAsString());
|
|
|
|
bCountTowardsFailedAttempts = false;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogQoSReporter, Warning, TEXT("Bad QoS response for [%s] - code: %d. Payload: %s"),
|
|
*HttpRequest->GetURL(), HttpResponse->GetResponseCode(), *HttpResponse->GetContentAsString());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if we cannot report QoS metrics this is pretty bad; report at least a warning
|
|
UE_LOG(LogQoSReporter, Warning, TEXT("QoS response for [%s]. No response"), *HttpRequest->GetURL());
|
|
}
|
|
|
|
if (bCountTowardsFailedAttempts)
|
|
{
|
|
// FIXME: should use retrial with exponential backoff here
|
|
#if USE_SERVER_PERF_COUNTERS
|
|
PerfCountersIncrement(TEXT("FailedQoSRequests"), 0, IPerfCounters::Flags::Transient);
|
|
#endif // USE_SERVER_PERF_COUNTERS
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns application role (server, client)
|
|
*/
|
|
FString FAnalyticsProviderQoSReporter::GetApplicationRole()
|
|
{
|
|
if (IsRunningDedicatedServer())
|
|
{
|
|
static FString DedicatedServer(TEXT("DedicatedServer"));
|
|
return DedicatedServer;
|
|
}
|
|
else if (IsRunningClientOnly())
|
|
{
|
|
static FString ClientOnly(TEXT("ClientOnly"));
|
|
return ClientOnly;
|
|
}
|
|
else if (IsRunningGame())
|
|
{
|
|
static FString StandaloneGame(TEXT("StandaloneGame"));
|
|
return StandaloneGame;
|
|
}
|
|
|
|
static FString Editor(TEXT("Editor"));
|
|
return Editor;
|
|
}
|
|
|