340 lines
14 KiB
C++
340 lines
14 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Settings/LevelEditorPlayNetworkEmulationSettings.h"
|
|
|
|
#include "IDetailChildrenBuilder.h"
|
|
#include "IDetailPropertyRow.h"
|
|
#include "DetailWidgetRow.h"
|
|
#include "DetailLayoutBuilder.h"
|
|
#include "Engine/NetDriver.h"
|
|
#include "Engine/NetworkSettings.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
|
|
namespace NetworkEmulationSettingsHelper
|
|
{
|
|
const FString& GetCustomProfileName()
|
|
{
|
|
static const FString CustomProfileName = TEXT("Custom");
|
|
return CustomProfileName;
|
|
}
|
|
|
|
void ConvertNetDriverSettingsToLevelEditorSettings(const FPacketSimulationSettings& NetDriverSettings, FNetworkEmulationPacketSettings& OutgoingTrafficPieSettings, FNetworkEmulationPacketSettings& IncomingTrafficPieSettings)
|
|
{
|
|
if (NetDriverSettings.PktLag > 0)
|
|
{
|
|
OutgoingTrafficPieSettings.MinLatency = FMath::Max(NetDriverSettings.PktLag - NetDriverSettings.PktLagVariance, 0);
|
|
OutgoingTrafficPieSettings.MaxLatency = FMath::Max(OutgoingTrafficPieSettings.MinLatency, NetDriverSettings.PktLag + NetDriverSettings.PktLagVariance);
|
|
;
|
|
}
|
|
else if (NetDriverSettings.PktLagMin > 0 || NetDriverSettings.PktLagMax > 0)
|
|
{
|
|
OutgoingTrafficPieSettings.MinLatency = NetDriverSettings.PktLagMin;
|
|
OutgoingTrafficPieSettings.MaxLatency = NetDriverSettings.PktLagMax;
|
|
}
|
|
|
|
OutgoingTrafficPieSettings.PacketLossPercentage = NetDriverSettings.PktLoss;
|
|
|
|
IncomingTrafficPieSettings.MinLatency = NetDriverSettings.PktIncomingLagMin;
|
|
IncomingTrafficPieSettings.MaxLatency = NetDriverSettings.PktIncomingLagMax;
|
|
IncomingTrafficPieSettings.PacketLossPercentage = NetDriverSettings.PktIncomingLoss;
|
|
}
|
|
|
|
FNetworkEmulationPacketSettings* GetPacketSettingsFromHandle(const TSharedPtr<IPropertyHandle>& PropertyHandle)
|
|
{
|
|
void* ValueData(nullptr);
|
|
FPropertyAccess::Result Result = PropertyHandle->GetValueData(ValueData);
|
|
if (Result != FPropertyAccess::Success)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return (FNetworkEmulationPacketSettings*)ValueData;
|
|
}
|
|
} // namespace NetworkEmulationSettingsHelper
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
// FLevelEditorPlayNetworkEmulationSettingsDetail
|
|
|
|
TSharedRef<IPropertyTypeCustomization> FLevelEditorPlayNetworkEmulationSettingsDetail::MakeInstance()
|
|
{
|
|
return MakeShareable(new FLevelEditorPlayNetworkEmulationSettingsDetail());
|
|
}
|
|
|
|
void FLevelEditorPlayNetworkEmulationSettingsDetail::CustomizeHeader(TSharedRef<IPropertyHandle> StructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
|
|
{
|
|
IsNetworkEmulationEnabledHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FLevelEditorPlayNetworkEmulationSettings, bIsNetworkEmulationEnabled));
|
|
|
|
HeaderRow.NameContent()
|
|
[IsNetworkEmulationEnabledHandle->CreatePropertyNameWidget()
|
|
|
|
]
|
|
.ValueContent()
|
|
[IsNetworkEmulationEnabledHandle->CreatePropertyValueWidget()];
|
|
}
|
|
|
|
void FLevelEditorPlayNetworkEmulationSettingsDetail::CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
|
|
{
|
|
// Grab the widget handles
|
|
NetworkEmulationSettingsHandle = StructPropertyHandle;
|
|
CurrentProfileHandle = NetworkEmulationSettingsHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FLevelEditorPlayNetworkEmulationSettings, CurrentProfile));
|
|
OutPacketsHandle = NetworkEmulationSettingsHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FLevelEditorPlayNetworkEmulationSettings, OutPackets));
|
|
InPacketsHandle = NetworkEmulationSettingsHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FLevelEditorPlayNetworkEmulationSettings, InPackets));
|
|
|
|
StructBuilder.AddProperty(NetworkEmulationSettingsHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FLevelEditorPlayNetworkEmulationSettings, EmulationTarget)).ToSharedRef())
|
|
.IsEnabled(TAttribute<bool>(this, &FLevelEditorPlayNetworkEmulationSettingsDetail::HandleAreSettingsEnabled));
|
|
|
|
StructBuilder.AddCustomRow(NSLOCTEXT("FLevelEditorPlayNetworkEmulationSettings", "NetworkEmulationProfileName", "Network Emulation Profile"))
|
|
.NameContent()
|
|
[SNew(STextBlock)
|
|
.Text(NSLOCTEXT("FLevelEditorPlayNetworkEmulationSettings", "NetworkEmulationProfileName", "Network Emulation Profile"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
|
|
]
|
|
.ValueContent()
|
|
[SNew(SComboButton)
|
|
.OnGetMenuContent(this, &FLevelEditorPlayNetworkEmulationSettingsDetail::OnGetProfilesMenuContent)
|
|
.ButtonContent()
|
|
[SNew(STextBlock)
|
|
.Text(this, &FLevelEditorPlayNetworkEmulationSettingsDetail::GetSelectedNetworkEmulationProfile)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())]]
|
|
.IsEnabled(TAttribute<bool>(this, &FLevelEditorPlayNetworkEmulationSettingsDetail::HandleAreSettingsEnabled));
|
|
|
|
StructBuilder.AddProperty(InPacketsHandle.ToSharedRef())
|
|
.IsEnabled(TAttribute<bool>(this, &FLevelEditorPlayNetworkEmulationSettingsDetail::HandleAreSettingsEnabled));
|
|
|
|
StructBuilder.AddProperty(OutPacketsHandle.ToSharedRef())
|
|
.IsEnabled(TAttribute<bool>(this, &FLevelEditorPlayNetworkEmulationSettingsDetail::HandleAreSettingsEnabled));
|
|
}
|
|
|
|
bool FLevelEditorPlayNetworkEmulationSettingsDetail::HandleAreSettingsEnabled() const
|
|
{
|
|
if (IsNetworkEmulationEnabledHandle.IsValid())
|
|
{
|
|
bool bIsEnabled(false);
|
|
IsNetworkEmulationEnabledHandle->GetValue(bIsEnabled);
|
|
return bIsEnabled;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
TSharedRef<SWidget> FLevelEditorPlayNetworkEmulationSettingsDetail::OnGetProfilesMenuContent() const
|
|
{
|
|
FMenuBuilder MenuBuilder(true, nullptr);
|
|
|
|
if (const UNetworkSettings* NetworkSettings = GetDefault<UNetworkSettings>())
|
|
{
|
|
// Add preconfigured profiles
|
|
for (int32 Index = 0; Index < NetworkSettings->NetworkEmulationProfiles.Num(); Index++)
|
|
{
|
|
const FNetworkEmulationProfileDescription& ProfileDescription = NetworkSettings->NetworkEmulationProfiles[Index];
|
|
|
|
MenuBuilder.AddMenuEntry(
|
|
FText::FromString(ProfileDescription.ProfileName),
|
|
FText::FromString(ProfileDescription.ToolTip),
|
|
FSlateIcon(),
|
|
FUIAction(
|
|
FExecuteAction::CreateSP(this, &FLevelEditorPlayNetworkEmulationSettingsDetail::OnEmulationProfileChanged, Index),
|
|
FCanExecuteAction()));
|
|
}
|
|
|
|
// Add the custom entry
|
|
MenuBuilder.AddMenuEntry(
|
|
NSLOCTEXT("FLevelEditorPlayNetworkEmulationSettings", "NetworkEmulationCustomProfile", "Custom"),
|
|
NSLOCTEXT("FLevelEditorPlayNetworkEmulationSettings", "NetworkEmulationCustomProfileToolTip", "Customizable profile"),
|
|
FSlateIcon(),
|
|
FUIAction(
|
|
FExecuteAction::CreateSP(this, &FLevelEditorPlayNetworkEmulationSettingsDetail::OnEmulationProfileChanged, -1),
|
|
FCanExecuteAction()));
|
|
}
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
void FLevelEditorPlayNetworkEmulationSettingsDetail::OnEmulationProfileChanged(int32 Index) const
|
|
{
|
|
FString Selection;
|
|
if (const UNetworkSettings* NetworkSettings = GetDefault<UNetworkSettings>())
|
|
{
|
|
if (NetworkSettings->NetworkEmulationProfiles.IsValidIndex(Index))
|
|
{
|
|
Selection = NetworkSettings->NetworkEmulationProfiles[Index].ProfileName;
|
|
}
|
|
else
|
|
{
|
|
Selection = NetworkEmulationSettingsHelper::GetCustomProfileName();
|
|
}
|
|
}
|
|
|
|
// Get the current settings
|
|
FNetworkEmulationPacketSettings* OutSettings = NetworkEmulationSettingsHelper::GetPacketSettingsFromHandle(OutPacketsHandle);
|
|
FNetworkEmulationPacketSettings* InSettings = NetworkEmulationSettingsHelper::GetPacketSettingsFromHandle(InPacketsHandle);
|
|
|
|
// Reload the settings when switching to an official profile
|
|
if (Selection != NetworkEmulationSettingsHelper::GetCustomProfileName())
|
|
{
|
|
FPacketSimulationSettings NetDriverSettings;
|
|
NetDriverSettings.LoadEmulationProfile(*Selection);
|
|
|
|
FNetworkEmulationPacketSettings OutgoingTrafficPIESettings;
|
|
FNetworkEmulationPacketSettings IncomingTrafficPIESettings;
|
|
NetworkEmulationSettingsHelper::ConvertNetDriverSettingsToLevelEditorSettings(NetDriverSettings, OutgoingTrafficPIESettings, IncomingTrafficPIESettings);
|
|
|
|
*OutSettings = OutgoingTrafficPIESettings;
|
|
*InSettings = IncomingTrafficPIESettings;
|
|
}
|
|
|
|
CurrentProfileHandle->SetValue(Selection);
|
|
}
|
|
|
|
FText FLevelEditorPlayNetworkEmulationSettingsDetail::GetSelectedNetworkEmulationProfile() const
|
|
{
|
|
FString SelectedProfile;
|
|
CurrentProfileHandle->GetValue(SelectedProfile);
|
|
|
|
return FText::FromString(SelectedProfile);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
// FLevelEditorPlayNetworkEmulationSettings
|
|
|
|
bool FLevelEditorPlayNetworkEmulationSettings::IsCustomProfile() const
|
|
{
|
|
return CurrentProfile == NetworkEmulationSettingsHelper::GetCustomProfileName();
|
|
}
|
|
|
|
bool FLevelEditorPlayNetworkEmulationSettings::IsEmulationEnabledForTarget(NetworkEmulationTarget CurrentTarget) const
|
|
{
|
|
// Settings are applied to everyone
|
|
if (EmulationTarget == NetworkEmulationTarget::Any)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Otherwise check if target matches the wanted setting
|
|
return EmulationTarget == CurrentTarget;
|
|
}
|
|
|
|
void FLevelEditorPlayNetworkEmulationSettings::ConvertToNetDriverSettings(FPacketSimulationSettings& OutNetDriverSettings) const
|
|
{
|
|
OutNetDriverSettings.ResetSettings();
|
|
|
|
bool bProfileFound(false);
|
|
|
|
if (!IsCustomProfile())
|
|
{
|
|
// Load hardcoded profiles from the config since they might have advanced settings not found in PIE settings
|
|
bProfileFound = OutNetDriverSettings.LoadEmulationProfile(*CurrentProfile);
|
|
}
|
|
|
|
if (IsCustomProfile() || !bProfileFound)
|
|
{
|
|
// For custom set the settings manually from the PIE variables
|
|
OutNetDriverSettings.PktLagMin = OutPackets.MinLatency;
|
|
OutNetDriverSettings.PktLagMax = OutPackets.MaxLatency;
|
|
OutNetDriverSettings.PktLoss = OutPackets.PacketLossPercentage;
|
|
OutNetDriverSettings.PktIncomingLagMin = InPackets.MinLatency;
|
|
OutNetDriverSettings.PktIncomingLagMax = InPackets.MaxLatency;
|
|
OutNetDriverSettings.PktIncomingLoss = InPackets.PacketLossPercentage;
|
|
}
|
|
}
|
|
|
|
FString FLevelEditorPlayNetworkEmulationSettings::BuildPacketSettingsForCmdLine() const
|
|
{
|
|
// Empty string when disabled
|
|
if (!bIsNetworkEmulationEnabled)
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
FString CmdLine;
|
|
|
|
if (IsCustomProfile())
|
|
{
|
|
// Set each setting manually for user-edited profiles
|
|
CmdLine += FString::Printf(TEXT(" -PktLagMin=%d"), OutPackets.MinLatency);
|
|
CmdLine += FString::Printf(TEXT(" -PktLagMax=%d"), OutPackets.MaxLatency);
|
|
CmdLine += FString::Printf(TEXT(" -PktLoss=%d"), OutPackets.PacketLossPercentage);
|
|
|
|
CmdLine += FString::Printf(TEXT(" -PktIncomingLagMin=%d"), InPackets.MinLatency);
|
|
CmdLine += FString::Printf(TEXT(" -PktIncomingLagMax=%d"), InPackets.MaxLatency);
|
|
CmdLine += FString::Printf(TEXT(" -PktIncomingLoss=%d"), InPackets.PacketLossPercentage);
|
|
}
|
|
else
|
|
{
|
|
CmdLine += FString::Printf(TEXT(" -PktEmulationProfile=%s"), *CurrentProfile);
|
|
}
|
|
|
|
return CmdLine;
|
|
}
|
|
|
|
FString FLevelEditorPlayNetworkEmulationSettings::BuildPacketSettingsForURL() const
|
|
{
|
|
// Empty string when disabled
|
|
if (!bIsNetworkEmulationEnabled)
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
FString CmdLine;
|
|
|
|
if (IsCustomProfile())
|
|
{
|
|
// Set each setting manually for user-edited profiles
|
|
CmdLine += FString::Printf(TEXT("?PktLagMin=%d"), OutPackets.MinLatency);
|
|
CmdLine += FString::Printf(TEXT("?PktLagMax=%d"), OutPackets.MaxLatency);
|
|
CmdLine += FString::Printf(TEXT("?PktLoss=%d"), OutPackets.PacketLossPercentage);
|
|
|
|
CmdLine += FString::Printf(TEXT("?PktIncomingLagMin=%d"), InPackets.MinLatency);
|
|
CmdLine += FString::Printf(TEXT("?PktIncomingLagMax=%d"), InPackets.MaxLatency);
|
|
CmdLine += FString::Printf(TEXT("?PktIncomingLoss=%d"), InPackets.PacketLossPercentage);
|
|
}
|
|
else
|
|
{
|
|
CmdLine += FString::Printf(TEXT("?PktEmulationProfile=%s"), *CurrentProfile);
|
|
}
|
|
|
|
return CmdLine;
|
|
}
|
|
|
|
void FLevelEditorPlayNetworkEmulationSettings::OnPostInitProperties()
|
|
{
|
|
if (CurrentProfile.IsEmpty())
|
|
{
|
|
CurrentProfile = NetworkEmulationSettingsHelper::GetCustomProfileName();
|
|
}
|
|
else if (!IsCustomProfile())
|
|
{
|
|
// For official profiles reload the settings from the config in case some values got changed
|
|
FPacketSimulationSettings NetDriverSettings;
|
|
|
|
bool bProfileFound = NetDriverSettings.LoadEmulationProfile(*CurrentProfile);
|
|
|
|
if (bProfileFound)
|
|
{
|
|
NetworkEmulationSettingsHelper::ConvertNetDriverSettingsToLevelEditorSettings(NetDriverSettings, OutPackets, InPackets);
|
|
}
|
|
else
|
|
{
|
|
CurrentProfile = NetworkEmulationSettingsHelper::GetCustomProfileName();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FLevelEditorPlayNetworkEmulationSettings::OnPostEditChange(struct FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
if (PropertyChangedEvent.Property)
|
|
{
|
|
if (GET_MEMBER_NAME_CHECKED(FLevelEditorPlayNetworkEmulationSettings, CurrentProfile) == PropertyChangedEvent.Property->GetFName() ||
|
|
GET_MEMBER_NAME_CHECKED(FLevelEditorPlayNetworkEmulationSettings, bIsNetworkEmulationEnabled) == PropertyChangedEvent.Property->GetFName() ||
|
|
GET_MEMBER_NAME_CHECKED(FLevelEditorPlayNetworkEmulationSettings, EmulationTarget) == PropertyChangedEvent.Property->GetFName())
|
|
{
|
|
// Don't consider the settings dirty when these properties change
|
|
return;
|
|
}
|
|
|
|
// Set profile to Custom when the user dirties any other property
|
|
CurrentProfile = NetworkEmulationSettingsHelper::GetCustomProfileName();
|
|
}
|
|
}
|