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 2855265 on 2016/02/03 by Max.Chen Sequencer: Release track editors when destroying sequencer #jira UE-26423 Change 2855247 on 2016/02/03 by Max.Chen PlacementMode: Null factory check in constructor to fix cooking. #codereview andrew.rodham #rb andrew.rodham #jira UE-26412 ChangeChange 2855116 on 2016/02/03 by Michael.Noland [AUTOMERGE] PS4: Added a log statement when the gap between SubmitDone calls exceeds 2 seconds and removed a duplicate call to set the LastSubmitDoneTime PS4: Fixed a bogus log statement when PS4_GNM_SLOW_FRAME_DEBUGGING=1 Merging CL#2854751and 2852176 by way of 2855100 #tests Tested on PS4 with PS4_GNM_SLOW_FRAME_DEBUGGING=1 #rb dave.ratti #lockdown andrew.grant -------- Integrated using branch //Orion/Main_to_//Orion/Dev-General of change#2855109 by Michael.Noland on 2016/02/03 20:59:51. Change 2855109 on 2016/02/03 by Michael.Noland PS4: Added a log statement when the gap between SubmitDone calls exceeds 2 seconds and removed a duplicate call to set the LastSubmitDoneTime PS4: Fixed a bogus log statement when PS4_GNM_SLOW_FRAME_DEBUGGING=1 Merging CL#2854751and 2852176 by way of 2855100 #tests Tested on PS4 with PS4_GNM_SLOW_FRAME_DEBUGGING=1 #rb dave.ratti #lockdown andrew.grant Change 2855100 on 2016/02/03 by Michael.Noland PS4: Added a log statement when the gap between SubmitDone calls exceeds 2 seconds and removed a duplicate call to set the LastSubmitDoneTime PS4: Fixed a bogus log statement when PS4_GNM_SLOW_FRAME_DEBUGGING=1 Merging CL#2854751and 2852176 using //Orion/Release-Next_to_//Orion/Release-Live #tests Tested on PS4 with PS4_GNM_SLOW_FRAME_DEBUGGING=1 #rb dave.ratti #lockdown andrew.grant ChangeChangeChangeChangeChange 2854825 on 2016/02/03 by Zabir.Hoque Harden MaterialParameterCollection from ending up with duplicate parameter names of GUIDs. #Tests: Ran debug editor, create materail param collection with >500 elements. Still only ~18ms. Used param in shader. #RB: Daniel.Wright #CodeReview: Daniel.Wright, Gil.Gribb, Rolando.Caloca, Marcus.Wassmer Change 2854788 on 2016/02/03 by Josh.Markiewicz #UE4 - JsonObjectConverter changes - added the ability for a UStruct to emit json as a string if type traits are setup with ExportTextItem / ImportTextItem - allows the UStruct to convert to json as something other than FJsonValueObject -- things like FColor, FDateTime but they are already handled differently - checked for possible change in existing behavior, no classes currently use type traits for this that aren't handled special already - FUniqueNetIdRepl can now convert to/from json as a string #rb david.nikdel #codereview ben.zeigler, sam.zamani, david.nikdel, paul.moore #tests various online tests connecting to servers, etc Change2854751on 2016/02/03 by Michael.Noland PS4: Fixed a bogus log statement when PS4_GNM_SLOW_FRAME_DEBUGGING=1 #rb dave.ratti #lockdown andrew.grant #tests Tested on PS4 with PS4_GNM_SLOW_FRAME_DEBUGGING=1 ChangeChange 2854712 on 2016/02/03 by Josh.Markiewicz #UE4 - added some json compatibility features to FUniqueNetIdRepl struct - ImportTextItem - To/FromJson #rb david.nikdel #codereview none #tests various online features, additional unit tests added to class Change 2854696 on 2016/02/03 by Dmitry.Rekman Making Memprofiler usable (by MichaelN). #rb Zak.Middleton (who I got the shelved CL # from) #codereview Michael.Noland, Zak.Middleton, Bob.Tellez #tests Used Memprofiler on a number of captures. ChangeChange 2854536 on 2016/02/03 by John.Pollard Add event groups as users to replay, so we can quickly find replays with certain events types in them #rb RyanG #tests Replays and events Merging using OrionDevGeneral->ReleaseCandidate Change 2854526 on 2016/02/03 by John.Pollard Add support for getting replay id #rb RyanG #tests Replays Merging using OrionDevGeneral->ReleaseCandidate Change 2854522 on 2016/02/03 by John.Pollard Support setting string values in perf counters through the perf counters helper class. #rb none #tests Client/Server match [CL 2856676 by Andrew Grant in Main branch]
307 lines
10 KiB
C++
307 lines
10 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"
|
|
|
|
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 201601280
|
|
|
|
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& EventName, 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& EventName, const TArray<FAnalyticsEventAttribute>& InAttributes)
|
|
{
|
|
if (APIKey.IsEmpty() || APIServer.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// add attributes common to each QoS event first
|
|
TArray<FAnalyticsEventAttribute> Attributes;
|
|
Attributes.Add(FAnalyticsEventAttribute(TEXT("QoSRevision"), QOS_EVENTS_REVISION));
|
|
Attributes.Add(FAnalyticsEventAttribute(TEXT("Role"), GetApplicationRole()));
|
|
Attributes.Add(FAnalyticsEventAttribute(TEXT("SystemId"), FPlatformMisc::GetOperatingSystemId()));
|
|
Attributes.Add(FAnalyticsEventAttribute(TEXT("InstanceId"), InstanceId.ToString()));
|
|
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)
|
|
{
|
|
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());
|
|
}
|
|
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());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|