Merge branch 'development' into TIF/Runtime

monroegm-disable-blank-issue-2
John 5 years ago
commit 22093f46fc

@ -493,6 +493,29 @@ namespace AzQtComponents
}
}
break;
case CE_MenuItem:
{
const QMenu* menu = qobject_cast<const QMenu*>(widget);
QAction* action = menu->activeAction();
if (action)
{
QMenu* subMenu = action->menu();
if (subMenu)
{
QVariant noHover = subMenu->property("noHover");
if (noHover.isValid() && noHover.toBool())
{
// First draw as standard to get the correct hover background for the complete control.
QProxyStyle::drawControl(element, option, painter, widget);
// Now draw the icon as non-hovered so control behaves as designed.
QStyleOptionMenuItem myOpt = *qstyleoption_cast<const QStyleOptionMenuItem*>(option);
myOpt.state &= ~QStyle::State_Selected;
return QProxyStyle::drawControl(element, &myOpt, painter, widget);
}
}
}
}
break;
}
return QProxyStyle::drawControl(element, option, painter, widget);

@ -1,4 +1,4 @@
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.0708 0.87087L11.0708 0.87074C10.4286 0.371024 9.61107 0.13517 8.79265 0.213464C7.97422 0.291757 7.21961 0.678004 6.6897 1.28985L5.76756 2.36366C5.63378 2.51944 5.56909 2.72055 5.58771 2.92273C5.60634 3.12492 5.70675 3.31163 5.86686 3.44178C6.02698 3.57194 6.23368 3.63488 6.44149 3.61676C6.6493 3.59864 6.8412 3.50094 6.97498 3.34516L7.89716 2.27122C8.03184 2.11488 8.19736 1.98639 8.38395 1.89334C8.57053 1.8003 8.7744 1.74459 8.98349 1.7295C9.19258 1.71442 9.40266 1.74027 9.60131 1.80552C9.79996 1.87078 9.98315 1.97411 10.1401 2.10942C10.4422 2.38256 10.6244 2.75851 10.6488 3.15904C10.6732 3.55957 10.5379 3.95383 10.2711 4.25978L8.35381 6.49309L8.33616 6.51209C8.23375 6.62767 8.11374 6.72729 7.98031 6.80749C7.67189 6.99364 7.30661 7.06983 6.94686 7.02305C6.58711 6.97626 6.25522 6.8094 6.00789 6.55097C5.86556 6.40288 5.66865 6.31578 5.46039 6.30879C5.25213 6.30179 5.04952 6.37546 4.89703 6.51365C4.74453 6.65183 4.65462 6.84323 4.64701 7.04584C4.6394 7.24845 4.71472 7.44572 4.85645 7.59436C5.21222 7.96578 5.65746 8.24515 6.15198 8.40726C6.6465 8.56937 7.17473 8.60911 7.68898 8.52289C7.84983 8.49539 8.00828 8.45599 8.16298 8.40505C8.70939 8.22568 9.19408 7.90261 9.56335 7.47165L11.4758 5.24457C11.7478 4.92529 11.9523 4.55683 12.0773 4.16038C12.2024 3.76394 12.2457 3.34735 12.2046 2.93457C12.1671 2.53429 12.0475 2.1454 11.8527 1.79092C11.658 1.43644 11.3921 1.12358 11.0708 0.87087Z" fill="white"/>
<path d="M5.40958 9.14055L4.58546 10.1003C4.32342 10.4101 3.94867 10.6097 3.53919 10.6576C3.12972 10.7054 2.71706 10.5977 2.3871 10.357C2.22234 10.2309 2.08525 10.0738 1.98392 9.89526C1.8826 9.71668 1.81911 9.52014 1.79719 9.31727C1.77528 9.1144 1.79539 8.90932 1.85634 8.71414C1.91729 8.51896 2.01784 8.33765 2.15204 8.18093L4.10231 5.90975L4.11666 5.89415C4.21908 5.77861 4.3391 5.67903 4.47254 5.59887C4.75216 5.42921 5.07976 5.34988 5.4085 5.37222C5.73725 5.39457 6.05033 5.51745 6.30299 5.72329C6.3635 5.77246 6.42015 5.82595 6.47246 5.88333C6.54738 5.9658 6.63971 6.03156 6.74316 6.07611C6.84661 6.12066 6.95872 6.14295 7.07184 6.14146C7.18361 6.13992 7.29372 6.11492 7.39465 6.06817C7.49558 6.02143 7.58495 5.95403 7.65666 5.8706L7.66607 5.85958C7.78641 5.72083 7.85132 5.54454 7.8489 5.36301C7.84647 5.18148 7.77687 5.00689 7.65286 4.87124C7.35341 4.54132 6.98422 4.27825 6.57057 4.10003C6.15692 3.92182 5.70858 3.83266 5.25623 3.83866C4.80388 3.84467 4.35821 3.94569 3.94971 4.13482C3.54122 4.32395 3.17954 4.59672 2.88945 4.93446L0.944626 7.19943C0.420065 7.81564 0.163643 8.60683 0.230025 9.40433C0.296406 10.2018 0.68034 10.9426 1.29998 11.4686C1.61272 11.7312 1.97644 11.9301 2.3696 12.0535C2.76277 12.1769 3.17738 12.2223 3.5889 12.187C3.687 12.1793 3.78434 12.1672 3.88093 12.1507C4.62877 12.0232 5.30663 11.6437 5.79562 11.0786L6.617 10.1221C6.75077 9.96628 6.81547 9.76517 6.79684 9.56299C6.77822 9.3608 6.6778 9.17409 6.51769 9.04394C6.35758 8.91378 6.15088 8.85084 5.94306 8.86896C5.73525 8.88708 5.54335 8.98477 5.40957 9.14055H5.40958Z" fill="white"/>
<path d="M11.0708 0.87087L11.0708 0.87074C10.4286 0.371024 9.61107 0.13517 8.79265 0.213464C7.97422 0.291757 7.21961 0.678004 6.6897 1.28985L5.76756 2.36366C5.63378 2.51944 5.56909 2.72055 5.58771 2.92273C5.60634 3.12492 5.70675 3.31163 5.86686 3.44178C6.02698 3.57194 6.23368 3.63488 6.44149 3.61676C6.6493 3.59864 6.8412 3.50094 6.97498 3.34516L7.89716 2.27122C8.03184 2.11488 8.19736 1.98639 8.38395 1.89334C8.57053 1.8003 8.7744 1.74459 8.98349 1.7295C9.19258 1.71442 9.40266 1.74027 9.60131 1.80552C9.79996 1.87078 9.98315 1.97411 10.1401 2.10942C10.4422 2.38256 10.6244 2.75851 10.6488 3.15904C10.6732 3.55957 10.5379 3.95383 10.2711 4.25978L8.35381 6.49309L8.33616 6.51209C8.23375 6.62767 8.11374 6.72729 7.98031 6.80749C7.67189 6.99364 7.30661 7.06983 6.94686 7.02305C6.58711 6.97626 6.25522 6.8094 6.00789 6.55097C5.86556 6.40288 5.66865 6.31578 5.46039 6.30879C5.25213 6.30179 5.04952 6.37546 4.89703 6.51365C4.74453 6.65183 4.65462 6.84323 4.64701 7.04584C4.6394 7.24845 4.71472 7.44572 4.85645 7.59436C5.21222 7.96578 5.65746 8.24515 6.15198 8.40726C6.6465 8.56937 7.17473 8.60911 7.68898 8.52289C7.84983 8.49539 8.00828 8.45599 8.16298 8.40505C8.70939 8.22568 9.19408 7.90261 9.56335 7.47165L11.4758 5.24457C11.7478 4.92529 11.9523 4.55683 12.0773 4.16038C12.2024 3.76394 12.2457 3.34735 12.2046 2.93457C12.1671 2.53429 12.0475 2.1454 11.8527 1.79092C11.658 1.43644 11.3921 1.12358 11.0708 0.87087Z" fill="#3F3F3F"/>
<path d="M5.40958 9.14055L4.58546 10.1003C4.32342 10.4101 3.94867 10.6097 3.53919 10.6576C3.12972 10.7054 2.71706 10.5977 2.3871 10.357C2.22234 10.2309 2.08525 10.0738 1.98392 9.89526C1.8826 9.71668 1.81911 9.52014 1.79719 9.31727C1.77528 9.1144 1.79539 8.90932 1.85634 8.71414C1.91729 8.51896 2.01784 8.33765 2.15204 8.18093L4.10231 5.90975L4.11666 5.89415C4.21908 5.77861 4.3391 5.67903 4.47254 5.59887C4.75216 5.42921 5.07976 5.34988 5.4085 5.37222C5.73725 5.39457 6.05033 5.51745 6.30299 5.72329C6.3635 5.77246 6.42015 5.82595 6.47246 5.88333C6.54738 5.9658 6.63971 6.03156 6.74316 6.07611C6.84661 6.12066 6.95872 6.14295 7.07184 6.14146C7.18361 6.13992 7.29372 6.11492 7.39465 6.06817C7.49558 6.02143 7.58495 5.95403 7.65666 5.8706L7.66607 5.85958C7.78641 5.72083 7.85132 5.54454 7.8489 5.36301C7.84647 5.18148 7.77687 5.00689 7.65286 4.87124C7.35341 4.54132 6.98422 4.27825 6.57057 4.10003C6.15692 3.92182 5.70858 3.83266 5.25623 3.83866C4.80388 3.84467 4.35821 3.94569 3.94971 4.13482C3.54122 4.32395 3.17954 4.59672 2.88945 4.93446L0.944626 7.19943C0.420065 7.81564 0.163643 8.60683 0.230025 9.40433C0.296406 10.2018 0.68034 10.9426 1.29998 11.4686C1.61272 11.7312 1.97644 11.9301 2.3696 12.0535C2.76277 12.1769 3.17738 12.2223 3.5889 12.187C3.687 12.1793 3.78434 12.1672 3.88093 12.1507C4.62877 12.0232 5.30663 11.6437 5.79562 11.0786L6.617 10.1221C6.75077 9.96628 6.81547 9.76517 6.79684 9.56299C6.77822 9.3608 6.6778 9.17409 6.51769 9.04394C6.35758 8.91378 6.15088 8.85084 5.94306 8.86896C5.73525 8.88708 5.54335 8.98477 5.40957 9.14055H5.40958Z" fill="#3F3F3F"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

@ -46,6 +46,7 @@ namespace AWSCore
void InitializeAWSDocActions();
void InitializeAWSGlobalDocsSubMenu();
void InitializeAWSFeatureGemActions();
void AddSpaceForIcon(QMenu* menu);
// AWSCoreEditorRequestBus interface implementation
void SetAWSClientAuthEnabled() override;

@ -35,6 +35,9 @@
namespace AWSCore
{
static constexpr int IconSize = 16;
AWSCoreEditorMenu::AWSCoreEditorMenu(const QString& text)
: QMenu(text)
, m_resourceMappingToolWatcher(nullptr)
@ -43,6 +46,7 @@ namespace AWSCore
InitializeResourceMappingToolAction();
this->addSeparator();
InitializeAWSFeatureGemActions();
AddSpaceForIcon(this);
AWSCoreEditorRequestBus::Handler::BusConnect();
}
@ -136,6 +140,8 @@ namespace AWSCore
globalDocsMenu->addAction(AddExternalLinkAction(AWSAndScriptCanvasActionText, AWSAndScriptCanvasUrl, ":/Notifications/link.svg"));
globalDocsMenu->addAction(AddExternalLinkAction(AWSAndComponentsActionText, AWSAndComponentsUrl, ":/Notifications/link.svg"));
globalDocsMenu->addAction(AddExternalLinkAction(CallAWSResourcesActionText, CallAWSResourcesUrl, ":/Notifications/link.svg"));
AddSpaceForIcon(globalDocsMenu);
}
void AWSCoreEditorMenu::InitializeAWSFeatureGemActions()
@ -170,6 +176,8 @@ namespace AWSCore
AWSClientAuthPlatformSpecificActionText, AWSClientAuthPlatformSpecificUrl, ":/Notifications/link.svg"));
subMenu->addAction(AddExternalLinkAction(
AWSClientAuthAPIReferenceActionText, AWSClientAuthAPIReferenceUrl, ":/Notifications/link.svg"));
AddSpaceForIcon(subMenu);
}
void AWSCoreEditorMenu::SetAWSMetricsEnabled()
@ -197,7 +205,9 @@ namespace AWSCore
[configFilePath](){
QDesktopServices::openUrl(QUrl::fromLocalFile(configFilePath.c_str()));
});
subMenu->addAction(settingsAction);
AddSpaceForIcon(subMenu);
}
QMenu* AWSCoreEditorMenu::SetAWSFeatureSubMenu(const AZStd::string& menuText)
@ -209,6 +219,7 @@ namespace AWSCore
{
QMenu* subMenu = new QMenu(QObject::tr(menuText.c_str()));
subMenu->setIcon(QIcon(QString(":/Notifications/checkmark.svg")));
subMenu->setProperty("noHover", true);
this->insertMenu(*itr, subMenu);
this->removeAction(*itr);
return subMenu;
@ -216,4 +227,11 @@ namespace AWSCore
}
return nullptr;
}
void AWSCoreEditorMenu::AddSpaceForIcon(QMenu *menu)
{
QSize size = menu->sizeHint();
size.setWidth(size.width() + IconSize);
menu->setFixedSize(size);
}
} // namespace AWSCore

@ -0,0 +1,62 @@
#!/usr/bin/env python
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
from genericpath import isdir
from argparse import ArgumentParser
import json
from pathlib import Path
import os
# this allows us to add additional data if necessary, e.g. frame_test_timestamps.json
is_timestamp_file = lambda file: file.name.startswith('frame') and file.name.endswith('_timestamps.json')
ns_to_ms = lambda time: time / 1e6
def main(logs_dir):
count = 0
total = 0
maximum = 0
print(f'Analyzing frame timestamp logs in {logs_dir}')
# go through files in alphabetical order (remove sorted() if not necessary)
for file in sorted(logs_dir.iterdir(), key=lambda file: len(file.name)):
if file.is_dir() or not is_timestamp_file(file):
continue
data = json.loads(file.read_text())
entries = data['ClassData']['timestampEntries']
timestamps = [entry['timestampResultInNanoseconds'] for entry in entries]
frame_time = sum(timestamps)
frame_name = file.name.split('_')[0]
print(f'- Total time for frame {frame_name}: {ns_to_ms(frame_time)}ms')
maximum = max(maximum, frame_time)
total += frame_time
count += 1
if count < 1:
print(f'No logs were found in {base_dir}')
exit(1)
print(f'Avg. time across {count} frames: {ns_to_ms(total / count)}ms')
print(f'Max frame time: {ns_to_ms(maximum)}ms')
if __name__ == '__main__':
parser = ArgumentParser(description='Gathers statistics from a group of pass timestamp logs')
parser.add_argument('path', help='Path to the directory containing the pass timestamp logs')
args = parser.parse_args()
base_dir = Path(args.path)
if not base_dir.exists():
raise FileNotFoundError('Invalid path provided')
main(base_dir)

@ -50,6 +50,8 @@ namespace AZ
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<OutputDeviceTransformType>("OutputDeviceTransformType");
behaviorContext->Class<AcesParameterOverrides>("AcesParameterOverrides")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "render")
@ -58,6 +60,16 @@ namespace AZ
->Method("LoadPreset", &AcesParameterOverrides::LoadPreset)
->Property("overrideDefaults", BehaviorValueProperty(&AcesParameterOverrides::m_overrideDefaults))
->Property("preset", BehaviorValueProperty(&AcesParameterOverrides::m_preset))
->Enum<aznumeric_cast<int>(OutputDeviceTransformType::NumOutputDeviceTransformTypes)>(
"OutputDeviceTransformType_NumOutputDeviceTransformTypes")
->Enum<aznumeric_cast<int>(OutputDeviceTransformType::OutputDeviceTransformType_48Nits)>(
"OutputDeviceTransformType_48Nits")
->Enum<aznumeric_cast<int>(OutputDeviceTransformType::OutputDeviceTransformType_1000Nits)>(
"OutputDeviceTransformType_1000Nits")
->Enum<aznumeric_cast<int>(OutputDeviceTransformType::OutputDeviceTransformType_2000Nits)>(
"OutputDeviceTransformType_2000Nits")
->Enum<aznumeric_cast<int>(OutputDeviceTransformType::OutputDeviceTransformType_4000Nits)>(
"OutputDeviceTransformType_4000Nits")
->Property("alterSurround", BehaviorValueProperty(&AcesParameterOverrides::m_alterSurround))
->Property("applyDesaturation", BehaviorValueProperty(&AcesParameterOverrides::m_applyDesaturation))
->Property("applyCATD60toD65", BehaviorValueProperty(&AcesParameterOverrides::m_applyCATD60toD65))

@ -179,7 +179,10 @@ namespace Multiplayer
AZ::TickBus::Handler::BusConnect();
AzFramework::SessionNotificationBus::Handler::BusConnect();
m_networkInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(AZ::Name(MPNetworkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this);
m_consoleCommandHandler.Connect(AZ::Interface<AZ::IConsole>::Get()->GetConsoleCommandInvokedEvent());
if (AZ::Interface<AZ::IConsole>::Get())
{
m_consoleCommandHandler.Connect(AZ::Interface<AZ::IConsole>::Get()->GetConsoleCommandInvokedEvent());
}
AZ::Interface<IMultiplayer>::Register(this);
AZ::Interface<AzFramework::ISessionHandlingClientRequests>::Register(this);
@ -191,6 +194,8 @@ namespace Multiplayer
{
AZ::Interface<AzFramework::ISessionHandlingClientRequests>::Unregister(this);
AZ::Interface<IMultiplayer>::Unregister(this);
m_consoleCommandHandler.Disconnect();
AZ::Interface<INetworking>::Get()->DestroyNetworkInterface(AZ::Name(MPNetworkInterfaceName));
AzFramework::SessionNotificationBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
}
@ -199,19 +204,10 @@ namespace Multiplayer
{
AZ::Interface<IMultiplayer>::Get()->InitializeMultiplayer(MultiplayerAgentType::Client);
m_pendingConnectionTickets.push(config.m_playerSessionId);
AZStd::string hostname = config.m_dnsName.empty() ? config.m_ipAddress : config.m_dnsName;
const IpAddress ipAddress(hostname.c_str(), config.m_port, m_networkInterface->GetType());
ConnectionId connectionId = m_networkInterface->Connect(ipAddress);
AzNetworking::IConnection* connection = m_networkInterface->GetConnectionSet().GetConnection(connectionId);
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
{
connection->SetUserData(new ClientToServerConnectionData(connection, *this, config.m_playerSessionId));
}
else
{
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->SetProviderTicket(config.m_playerSessionId);
}
m_networkInterface->Connect(ipAddress);
return true;
}
@ -584,15 +580,16 @@ namespace Multiplayer
datum.m_isInvited = false;
datum.m_agentType = MultiplayerAgentType::Client;
AZStd::string providerTicket;
if (connection->GetConnectionRole() == ConnectionRole::Connector)
{
AZLOG_INFO("New outgoing connection to remote address: %s", connection->GetRemoteAddress().GetString().c_str());
AZ::CVarFixedString providerTicket;
if (connection->GetUserData() != nullptr)
if (!m_pendingConnectionTickets.empty())
{
providerTicket = reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetProviderTicket();
providerTicket = m_pendingConnectionTickets.front();
m_pendingConnectionTickets.pop();
}
connection->SendReliablePacket(MultiplayerPackets::Connect(0, providerTicket));
connection->SendReliablePacket(MultiplayerPackets::Connect(0, providerTicket.c_str()));
}
else
{
@ -622,7 +619,11 @@ namespace Multiplayer
{
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
{
connection->SetUserData(new ClientToServerConnectionData(connection, *this));
connection->SetUserData(new ClientToServerConnectionData(connection, *this, providerTicket));
}
else
{
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->SetProviderTicket(providerTicket);
}
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<NullReplicationWindow>();
@ -646,14 +647,10 @@ namespace Multiplayer
AZStd::string reasonString = ToString(reason);
AZLOG_INFO("%s due to %s from remote address: %s", endpointString, reasonString.c_str(), connection->GetRemoteAddress().GetString().c_str());
if (connection->GetConnectionRole() == ConnectionRole::Acceptor)
{
// The authority is shutting down its connection
m_shutdownEvent.Signal(m_networkInterface);
}
else if (GetAgentType() == MultiplayerAgentType::Client && connection->GetConnectionRole() == ConnectionRole::Connector)
// The client is disconnecting
if (GetAgentType() == MultiplayerAgentType::Client)
{
// The client is disconnecting
AZ_Assert(connection->GetConnectionRole() == ConnectionRole::Connector, "Client connection role should only ever be Connector");
m_clientDisconnectedEvent.Signal();
}
@ -669,7 +666,7 @@ namespace Multiplayer
if (m_agentType == MultiplayerAgentType::DedicatedServer || m_agentType == MultiplayerAgentType::ClientServer)
{
if (AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get() != nullptr &&
connection->GetConnectionRole() == ConnectionRole::Connector)
connection->GetConnectionRole() == ConnectionRole::Acceptor)
{
AzFramework::PlayerConnectionConfig config;
config.m_playerConnectionId = aznumeric_cast<uint32_t>(connection->GetConnectionId());
@ -680,12 +677,15 @@ namespace Multiplayer
// Signal to session management when there are no remaining players in a dedicated server for potential cleanup
// We avoid this for client server as the host itself is a user
if (m_agentType == MultiplayerAgentType::DedicatedServer && connection->GetConnectionRole() == ConnectionRole::Connector)
if (m_agentType == MultiplayerAgentType::DedicatedServer && connection->GetConnectionRole() == ConnectionRole::Acceptor)
{
if (AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get() != nullptr
&& m_networkInterface->GetConnectionSet().GetConnectionCount() == 0)
if (m_networkInterface->GetConnectionSet().GetConnectionCount() == 0)
{
AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get()->HandleDestroySession();
m_shutdownEvent.Signal(m_networkInterface);
if (AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get() != nullptr)
{
AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get()->HandleDestroySession();
}
}
}
}

@ -146,6 +146,8 @@ namespace Multiplayer
ConnectionAcquiredEvent m_connAcquiredEvent;
ClientDisconnectedEvent m_clientDisconnectedEvent;
AZStd::queue<AZStd::string> m_pendingConnectionTickets;
AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 };
HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId;

@ -35,14 +35,16 @@ namespace UnitTest
m_initHandler = Multiplayer::SessionInitEvent::Handler([this](AzNetworking::INetworkInterface* value) { TestInitEvent(value); });
m_mpComponent->AddSessionInitHandler(m_initHandler);
m_shutdownHandler = Multiplayer::SessionInitEvent::Handler([this](AzNetworking::INetworkInterface* value) { TestShutdownEvent(value); });
m_shutdownHandler = Multiplayer::SessionShutdownEvent::Handler([this](AzNetworking::INetworkInterface* value) { TestShutdownEvent(value); });
m_mpComponent->AddSessionShutdownHandler(m_shutdownHandler);
m_connAcquiredHandler = Multiplayer::ConnectionAcquiredEvent::Handler([this](Multiplayer::MultiplayerAgentDatum value) { TestConnectionAcquiredEvent(value); });
m_mpComponent->AddConnectionAcquiredHandler(m_connAcquiredHandler);
m_mpComponent->Activate();
}
void TearDown() override
{
m_mpComponent->Deactivate();
delete m_mpComponent;
delete m_netComponent;
AZ::NameDictionary::Destroy();
@ -86,6 +88,7 @@ namespace UnitTest
TEST_F(MultiplayerSystemTests, TestShutdownEvent)
{
m_mpComponent->InitializeMultiplayer(Multiplayer::MultiplayerAgentType::DedicatedServer);
IMultiplayerConnectionMock connMock1 = IMultiplayerConnectionMock(AzNetworking::ConnectionId(), AzNetworking::IpAddress(), AzNetworking::ConnectionRole::Acceptor);
IMultiplayerConnectionMock connMock2 = IMultiplayerConnectionMock(AzNetworking::ConnectionId(), AzNetworking::IpAddress(), AzNetworking::ConnectionRole::Connector);
m_mpComponent->OnDisconnect(&connMock1, AzNetworking::DisconnectReason::None, AzNetworking::TerminationEndpoint::Local);

@ -189,7 +189,8 @@
"CMAKE_NATIVE_BUILD_ARGS": "/m /nologo",
"CTEST_OPTIONS": "-L \"(SUITE_smoke_REQUIRES_gpu|SUITE_main_REQUIRES_gpu)\" -T Test",
"TEST_METRICS": "True",
"TEST_RESULTS": "True"
"TEST_RESULTS": "True",
"TEST_SCREENSHOTS": "True"
}
},
"asset_profile_vs2019": {

@ -17,12 +17,16 @@ python upload_to_s3.py --base_dir %WORKSPACE% --file_regex "(.*zip$|.*MD5$)" --b
Use profile to upload all .zip and .MD5 files in %WORKSPACE% folder to bucket ly-packages-mainline:
python upload_to_s3.py --base_dir %WORKSPACE% --profile profile --file_regex "(.*zip$|.*MD5$)" --bucket ly-packages-mainline
Another example usage for uploading all .png and .ppm files inside base_dir and only subdirectories within base_dir:
python upload_to_s3.py --base_dir %WORKSPACE%/path/to/files --file_regex "(.*png$|.*ppm$)" --bucket screenshot-test-bucket --search_subdirectories True --key_prefix Test
'''
import os
import re
import json
import time
import boto3
from optparse import OptionParser
@ -34,6 +38,8 @@ def parse_args():
parser.add_option("--profile", dest="profile", default=None, help="The name of a profile to use. If not given, then the default profile is used.")
parser.add_option("--bucket", dest="bucket", default=None, help="S3 bucket the files are uploaded to.")
parser.add_option("--key_prefix", dest="key_prefix", default='', help="Object key prefix.")
parser.add_option("--search_subdirectories", dest="search_subdirectories", action='store_true',
help="Toggle for searching for files in subdirectories beneath base_dir, defaults to False")
'''
ExtraArgs used to call s3.upload_file(), should be in json format. extra_args key must be one of: ACL, CacheControl, ContentDisposition, ContentEncoding, ContentLanguage, ContentType, Expires,
GrantFullControl, GrantRead, GrantReadACP, GrantWriteACP, Metadata, RequestPayer, ServerSideEncryption, StorageClass,
@ -62,48 +68,82 @@ def get_client(service_name, profile_name):
return client
def get_files_to_upload(base_dir, regex):
def get_files_to_upload(base_dir, regex, search_subdirectories):
"""
Uses a regex expression pattern to return a list of file paths for files to upload to the s3 bucket.
:param base_dir: path for the base directory, if using search_subdirectories=True ensure this is the parent.
:param regex: pattern to use for regex searching, ex. "(.*zip$|.*MD5$)"
:param search_subdirectories: boolean False for only getting files in base_dir, True to get all files in base_dir
and any subdirectory inside base_dir, defaults to False from the parse_args() function.
:return: a list of string file paths for files to upload to the s3 bucket matching the regex expression.
"""
# Get all file names in base directory
files = [x for x in os.listdir(base_dir) if os.path.isfile(os.path.join(base_dir, x))]
# strip the surround quotes, if they exist
files = [os.path.join(base_dir, x) for x in os.listdir(base_dir) if os.path.isfile(os.path.join(base_dir, x))]
if search_subdirectories: # Get all file names in base directory and any subdirectories.
for subdirectory in os.walk(base_dir):
# Example output for subdirectory:
# ('C:\path\to\base_dir\', ['Subfolder1', 'Subfolder2'], ['file1', 'file2'])
subdirectory_file_path = subdirectory[0]
subdirectory_files = subdirectory[2]
if subdirectory_files:
subdirectory_file_paths = _build_file_paths(subdirectory_file_path, subdirectory_files)
files.extend(subdirectory_file_paths)
try:
regex = json.loads(regex)
regex = json.loads(regex) # strip the surround quotes, if they exist
except:
print(f'WARNING: failed to call json.loads() for regex: "{regex}"')
pass
# Get all file names matching the regular expression, those file will be uploaded to S3
files_to_upload = [x for x in files if re.match(regex, x)]
return files_to_upload
regex_files_to_upload = [x for x in files if re.match(regex, x)]
return regex_files_to_upload
def s3_upload_file(client, base_dir, file, bucket, key_prefix=None, extra_args=None, max_retry=1):
print(('Uploading file {} to bucket {}.'.format(file, bucket)))
key = file if key_prefix is None else '{}/{}'.format(key_prefix, file)
def s3_upload_file(client, file, bucket, key_prefix=None, extra_args=None, max_retry=1):
key = file if key_prefix is None else f'{key_prefix}/{file}'
error_message = None
for x in range(max_retry):
try:
client.upload_file(
os.path.join(base_dir, file), bucket, key,
ExtraArgs=extra_args
)
print('Upload succeeded')
client.upload_file(file, bucket, key, ExtraArgs=extra_args)
return True
except Exception as err:
print(('exception while uploading: {}'.format(err)))
print('Retrying upload...')
print('Upload failed')
time.sleep(0.1) # Sleep for 100 milliseconds between retries.
error_message = err
print(f'Upload failed - Exception while uploading: {error_message}')
return False
def _build_file_paths(path_to_files, files_in_path):
"""
Given a path containing files, returns a list of strings representing complete paths to each file.
:param path_to_files: path to the location storing the files to create string paths for
:param files_in_path: list of files that are inside the path_to_files path string
:return: list of fully parsed file path strings from path_to_files path.
"""
parsed_file_paths = []
for file_in_path in files_in_path:
complete_file_path = os.path.join(path_to_files, file_in_path)
if os.path.isfile(complete_file_path):
parsed_file_paths.append(complete_file_path)
return parsed_file_paths
if __name__ == "__main__":
options = parse_args()
client = get_client('s3', options.profile)
files_to_upload = get_files_to_upload(options.base_dir, options.file_regex)
files_to_upload = get_files_to_upload(options.base_dir, options.file_regex, options.search_subdirectories)
extra_args = json.loads(options.extra_args) if options.extra_args else None
print(('Uploading {} files to bucket {}.'.format(len(files_to_upload), options.bucket)))
failure = []
success = []
for file in files_to_upload:
if not s3_upload_file(client, options.base_dir, file, options.bucket, options.key_prefix, extra_args, 2):
if not s3_upload_file(client, file, options.bucket, options.key_prefix, extra_args, 2):
failure.append(file)
else:
success.append(file)

Loading…
Cancel
Save