From 2f4120cdfbbd736d18fdc5f3187a94f6640ed28b Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 3 May 2021 13:07:11 -0700 Subject: [PATCH 01/21] Update Ctrl+G logic to account for prefab processing status and timing --- .../PrefabEditorEntityOwnershipInterface.h | 2 + .../PrefabEditorEntityOwnershipService.cpp | 5 ++ .../PrefabEditorEntityOwnershipService.h | 2 + Gems/Multiplayer/Code/CMakeLists.txt | 1 + .../Code/Include/IMultiplayerTools.h | 39 ++++++++++++ .../MultiplayerEditorSystemComponent.cpp | 63 ++++++++++++------- .../Editor/MultiplayerEditorSystemComponent.h | 12 +++- .../Code/Source/MultiplayerToolsModule.cpp | 35 ++++------- .../Code/Source/MultiplayerToolsModule.h | 27 ++++++++ .../Pipeline/NetworkPrefabProcessor.cpp | 3 + .../Code/multiplayer_tools_files.cmake | 1 + 11 files changed, 142 insertions(+), 48 deletions(-) create mode 100644 Gems/Multiplayer/Code/Include/IMultiplayerTools.h diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h index 19c236f509..4476876b59 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h @@ -46,6 +46,8 @@ namespace AzToolsFramework virtual Prefab::InstanceOptionalReference GetRootPrefabInstance() = 0; + virtual const AZStd::vector>& GetPlayInEditorAssetData() = 0; + virtual bool LoadFromStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) = 0; virtual bool SaveToStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp index 81233069a9..e81d6bf08e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp @@ -321,6 +321,11 @@ namespace AzToolsFramework return *m_rootInstance; } + const AZStd::vector>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData() + { + return m_playInEditorData.m_assets; + } + void PrefabEditorEntityOwnershipService::OnEntityRemoved(AZ::EntityId entityId) { AzFramework::SliceEntityRequestBus::MultiHandler::BusDisconnect(entityId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h index 9c483e61c5..cf62220e67 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h @@ -195,6 +195,8 @@ namespace AzToolsFramework AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) override; Prefab::InstanceOptionalReference GetRootPrefabInstance() override; + + const AZStd::vector>& GetPlayInEditorAssetData() override; ////////////////////////////////////////////////////////////////////////// void OnEntityRemoved(AZ::EntityId entityId); diff --git a/Gems/Multiplayer/Code/CMakeLists.txt b/Gems/Multiplayer/Code/CMakeLists.txt index 4eeee15c47..46f56ef315 100644 --- a/Gems/Multiplayer/Code/CMakeLists.txt +++ b/Gems/Multiplayer/Code/CMakeLists.txt @@ -119,6 +119,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) BUILD_DEPENDENCIES PRIVATE Gem::Multiplayer.Editor.Static + Gem::Multiplayer.Tools ) endif() diff --git a/Gems/Multiplayer/Code/Include/IMultiplayerTools.h b/Gems/Multiplayer/Code/Include/IMultiplayerTools.h new file mode 100644 index 0000000000..c621808f7a --- /dev/null +++ b/Gems/Multiplayer/Code/Include/IMultiplayerTools.h @@ -0,0 +1,39 @@ +/* +* 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. +* +*/ + +#pragma once + +#include + +namespace Multiplayer +{ + //! IMultiplayer provides insight into the Multiplayer session and its Agents + class IMultiplayerTools + { + public: + // NetworkPrefabProcessor is the only class that should be setting process network prefab status + friend class NetworkPrefabProcessor; + + AZ_RTTI(IMultiplayerTools, "{E8A80EAB-29CB-4E3B-A0B2-FFCB37060FB0}"); + + virtual ~IMultiplayerTools() = default; + + //! Returns if network prefab processing has created currently active or pending spawnables + //! @return If network prefab processing has created currently active or pending spawnables + virtual bool DidProcessNetworkPrefabs() = 0; + + private: + //! Sets if network prefab processing has created currently active or pending spawnables + //! @param didProcessNetPrefabs if network prefab processing has created currently active or pending spawnables + virtual void SetDidProcessNetworkPrefabs(bool didProcessNetPrefabs) = 0; + }; +} diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index aec3e7870f..0850b858a2 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -10,12 +10,14 @@ * */ +#include #include #include #include #include #include #include +#include namespace Multiplayer { @@ -57,12 +59,14 @@ namespace Multiplayer void MultiplayerEditorSystemComponent::Activate() { + AzFramework::GameEntityContextEventBus::Handler::BusConnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); } void MultiplayerEditorSystemComponent::Deactivate() { AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); + AzFramework::GameEntityContextEventBus::Handler::BusDisconnect(); } void MultiplayerEditorSystemComponent::NotifyRegisterViews() @@ -77,11 +81,42 @@ namespace Multiplayer { switch (event) { - case eNotify_OnBeginGameMode: - { + case eNotify_OnQuit: + AZ_Warning("Multiplayer Editor", m_editor != nullptr, "Multiplayer Editor received On Quit without an Editor pointer."); + if (m_editor) + { + m_editor->UnregisterNotifyListener(this); + m_editor = nullptr; + } + [[fallthrough]]; + case eNotify_OnEndGameMode: + AZ::TickBus::Handler::BusDisconnect(); + // Kill the configured server if it's active + if (m_serverProcess) + { + m_serverProcess->TerminateProcess(0); + m_serverProcess = nullptr; + } + break; + } + } + + void MultiplayerEditorSystemComponent::OnGameEntitiesStarted() + { + // BeginGameMode and Prefab Processing have completed at this point + IMultiplayerTools* mpTools = AZ::Interface::Get(); + if (editorsv_enabled && mpTools != nullptr && mpTools->DidProcessNetworkPrefabs()) + { AZ::TickBus::Handler::BusConnect(); - if (editorsv_enabled) + auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); + if (!prefabEditorEntityOwnershipInterface) + { + AZ_Error("MultiplayerEditor", prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface unavailable"); + } + const AZStd::vector>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData(); + + if (assetData.size() > 0) { // Assemble the server's path AZ::CVarFixedString serverProcess = editorsv_process; @@ -111,33 +146,13 @@ namespace Multiplayer // Start the configured server if it's available AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - processLaunchInfo.m_commandlineParameters = - AZStd::string::format("\"%s\"", serverPath.c_str()); + processLaunchInfo.m_commandlineParameters = AZStd::string::format("\"%s\"", serverPath.c_str()); processLaunchInfo.m_showWindow = true; processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_NORMAL; m_serverProcess = AzFramework::ProcessWatcher::LaunchProcess( processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE); } - break; - } - case eNotify_OnQuit: - AZ_Warning("Multiplayer Editor", m_editor != nullptr, "Multiplayer Editor received On Quit without an Editor pointer."); - if (m_editor) - { - m_editor->UnregisterNotifyListener(this); - m_editor = nullptr; - } - [[fallthrough]]; - case eNotify_OnEndGameMode: - AZ::TickBus::Handler::BusDisconnect(); - // Kill the configured server if it's active - if (m_serverProcess) - { - m_serverProcess->TerminateProcess(0); - m_serverProcess = nullptr; - } - break; } } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h index 8c18a2e57a..31ecdf83a3 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -34,6 +35,7 @@ namespace Multiplayer class MultiplayerEditorSystemComponent final : public AZ::Component , private AZ::TickBus::Handler + , private AzFramework::GameEntityContextEventBus::Handler , private AzToolsFramework::EditorEvents::Bus::Handler , private IEditorNotifyListener { @@ -66,8 +68,16 @@ namespace Multiplayer void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; int GetTickOrder() override; //! @} - //! + + //! EditorEvents::Handler overrides + //! @{ void OnEditorNotifyEvent(EEditorNotifyEvent event) override; + //! @} + + //! GameEntityContextEventBus::Handler overrides + //! @{ + void OnGameEntitiesStarted() override; + //! @} IEditor* m_editor = nullptr; AzFramework::ProcessWatcher* m_serverProcess = nullptr; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp index 5a223d6214..a5df3c1dc5 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp @@ -18,32 +18,21 @@ namespace Multiplayer { - //! Multiplayer Tools system component provides serialize context reflection for tools-only systems. - class MultiplayerToolsSystemComponent final - : public AZ::Component + + void MultiplayerToolsSystemComponent::Reflect(AZ::ReflectContext* context) { - public: - AZ_COMPONENT(MultiplayerToolsSystemComponent, "{65AF5342-0ECE-423B-B646-AF55A122F72B}"); + NetworkPrefabProcessor::Reflect(context); + } - static void Reflect(AZ::ReflectContext* context) - { - NetworkPrefabProcessor::Reflect(context); - } + bool MultiplayerToolsSystemComponent::DidProcessNetworkPrefabs() + { + return m_didProcessNetPrefabs; + } - MultiplayerToolsSystemComponent() = default; - ~MultiplayerToolsSystemComponent() override = default; - - /// AZ::Component overrides. - void Activate() override - { - - } - - void Deactivate() override - { - - } - }; + void MultiplayerToolsSystemComponent::SetDidProcessNetworkPrefabs(bool didProcessNetPrefabs) + { + m_didProcessNetPrefabs = didProcessNetPrefabs; + } MultiplayerToolsModule::MultiplayerToolsModule() : AZ::Module() diff --git a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.h b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.h index 823bd63a1d..82d0415c5a 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.h @@ -12,10 +12,37 @@ #pragma once +#include #include +#include namespace Multiplayer { + class MultiplayerToolsSystemComponent final + : public AZ::Component + , public IMultiplayerTools + { + public: + AZ_COMPONENT(MultiplayerToolsSystemComponent, "{65AF5342-0ECE-423B-B646-AF55A122F72B}"); + + static void Reflect(AZ::ReflectContext* context); + + MultiplayerToolsSystemComponent() = default; + ~MultiplayerToolsSystemComponent() override = default; + + /// AZ::Component overrides. + void Activate() override {}; + + void Deactivate() override {}; + + bool DidProcessNetworkPrefabs() override; + + private: + void SetDidProcessNetworkPrefabs(bool didProcessNetPrefabs) override; + + bool m_didProcessNetPrefabs = false; + }; + class MultiplayerToolsModule : public AZ::Module { diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index 2006272135..8528b3d564 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,8 @@ namespace Multiplayer void NetworkPrefabProcessor::Process(PrefabProcessorContext& context) { + IMultiplayerTools* mpTools = AZ::Interface::Get(); + mpTools->SetDidProcessNetworkPrefabs(false); context.ListPrefabs([&context](AZStd::string_view prefabName, PrefabDom& prefab) { ProcessPrefab(context, prefabName, prefab); }); diff --git a/Gems/Multiplayer/Code/multiplayer_tools_files.cmake b/Gems/Multiplayer/Code/multiplayer_tools_files.cmake index 1be02fd999..12f12479ba 100644 --- a/Gems/Multiplayer/Code/multiplayer_tools_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_tools_files.cmake @@ -10,6 +10,7 @@ # set(FILES + Include/IMultiplayerTools.h Source/Multiplayer_precompiled.cpp Source/Multiplayer_precompiled.h Source/Pipeline/NetworkPrefabProcessor.cpp From aa51233536a55816324da9d41fff6afe4e3970c9 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 6 May 2021 16:18:13 -0700 Subject: [PATCH 02/21] Add Asset serialization for Ctrl+G and related net interfaces --- .../AzNetworking/TcpTransport/TcpSocket.cpp | 4 +- .../AutoGen/Multiplayer.AutoPackets.xml | 6 ++ .../MultiplayerEditorSystemComponent.cpp | 93 +++++++++++++++++-- .../Editor/MultiplayerEditorSystemComponent.h | 2 + .../Source/MultiplayerSystemComponent.cpp | 21 +++++ .../Code/Source/MultiplayerSystemComponent.h | 4 +- .../Code/Source/MultiplayerToolsModule.cpp | 10 ++ .../Code/Source/MultiplayerToolsModule.h | 5 +- 8 files changed, 132 insertions(+), 13 deletions(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp index 78020cb09d..e8c30d0816 100644 --- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp @@ -116,8 +116,8 @@ namespace AzNetworking int32_t TcpSocket::Receive(uint8_t* outData, uint32_t size) const { - AZ_Assert(size > 0, "Invalid data size for send"); - AZ_Assert(outData != nullptr, "NULL data pointer passed to send"); + AZ_Assert(size > 0, "Invalid data size for receive"); + AZ_Assert(outData != nullptr, "NULL data pointer passed to receive"); if (!IsOpen()) { return SocketOpResultErrorNotOpen; diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index 5de466899c..21faefa880 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -57,4 +57,10 @@ + + + + + + diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 0850b858a2..15f00bdf80 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -10,23 +10,32 @@ * */ +#include #include +#include +#include #include #include +#include #include #include #include #include +#include #include namespace Multiplayer { + static const AZStd::string_view s_networkInterfaceName("MultiplayerEditorServerInterface"); + using namespace AzNetworking; AZ_CVAR(bool, editorsv_enabled, false, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether Editor launching a local server to connect to is supported"); AZ_CVAR(AZ::CVarFixedString, editorsv_process, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The server executable that should be run. Empty to use the current project's ServerLauncher"); + AZ_CVAR(AZ::CVarFixedString, sv_serveraddr, "127.0.0.1", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the server to connect to"); + AZ_CVAR(uint16_t, sv_port, 30091, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that the multiplayer editor gem will bind to for traffic"); void MultiplayerEditorSystemComponent::Reflect(AZ::ReflectContext* context) { @@ -61,6 +70,16 @@ namespace Multiplayer { AzFramework::GameEntityContextEventBus::Handler::BusConnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); + + // Setup a network interface handled by MultiplayerSystemComponent + if (m_editorNetworkInterface == nullptr) + { + AZ::Entity* systemEntity = this->GetEntity(); + MultiplayerSystemComponent* mpSysComponent = systemEntity->FindComponent(); + + m_editorNetworkInterface = AZ::Interface::Get()->CreateNetworkInterface( + AZ::Name(s_networkInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *mpSysComponent); + } } void MultiplayerEditorSystemComponent::Deactivate() @@ -97,25 +116,52 @@ namespace Multiplayer m_serverProcess->TerminateProcess(0); m_serverProcess = nullptr; } + if (m_editorNetworkInterface) + { + // Disconnect the interface, connection management will clean it up + m_editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByUser); + m_editorConnId = AzNetworking::InvalidConnectionId; + } break; } } void MultiplayerEditorSystemComponent::OnGameEntitiesStarted() { + auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); + if (!prefabEditorEntityOwnershipInterface) + { + AZ_Error("MultiplayerEditor", prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface unavailable"); + } + const AZStd::vector>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData(); + + AZStd::vector buffer; + AZ::IO::ByteContainerStream byteStream(&buffer); + + // Serialize Asset information and AssetData into a potentially large buffer + for (auto asset : assetData) + { + AZ::Data::AssetId assetId = asset.GetId(); + AZ::Data::AssetType assetType = asset.GetType(); + const AZStd::string& assetHint = asset.GetHint(); + AZ::IO::SizeType assetHintSize = assetHint.size(); + AZ::Data::AssetLoadBehavior assetLoadBehavior = asset.GetAutoLoadBehavior(); + + byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId)); + byteStream.Write(sizeof(AZ::Data::AssetType), reinterpret_cast(&assetType)); + byteStream.Write(sizeof(assetHintSize), reinterpret_cast(&assetHintSize)); + byteStream.Write(assetHint.size(), assetHint.c_str()); + byteStream.Write(sizeof(AZ::Data::AssetLoadBehavior), reinterpret_cast(&assetLoadBehavior)); + + AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType()); + } + // BeginGameMode and Prefab Processing have completed at this point IMultiplayerTools* mpTools = AZ::Interface::Get(); if (editorsv_enabled && mpTools != nullptr && mpTools->DidProcessNetworkPrefabs()) { AZ::TickBus::Handler::BusConnect(); - auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); - if (!prefabEditorEntityOwnershipInterface) - { - AZ_Error("MultiplayerEditor", prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface unavailable"); - } - const AZStd::vector>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData(); - if (assetData.size() > 0) { // Assemble the server's path @@ -154,6 +200,39 @@ namespace Multiplayer processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE); } } + + // Now that the server has launched, attempt to connect the NetworkInterface + const AZ::CVarFixedString remoteAddress = sv_serveraddr; + m_editorConnId = m_editorNetworkInterface->Connect( + AzNetworking::IpAddress(remoteAddress.c_str(), sv_port, AzNetworking::ProtocolType::Tcp)); + + // Read the buffer into EditorServerInit packets until we've flushed the whole thing + byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); + + while (byteStream.GetCurPos() < byteStream.GetLength()) + { + MultiplayerPackets::EditorServerInit packet; + AzNetworking::TcpPacketEncodingBuffer& outBuffer = packet.ModifyAssetData(); + + // Size the packet's buffer appropriately + size_t readSize = TcpPacketEncodingBuffer::GetCapacity(); + size_t byteStreamSize = byteStream.GetLength() - byteStream.GetCurPos(); + if (byteStreamSize < readSize) + { + readSize = byteStreamSize; + } + + outBuffer.Resize(readSize); + byteStream.Read(readSize, outBuffer.GetBuffer()); + + // If we've run out of buffer, mark that we're done + if (byteStream.GetCurPos() == byteStream.GetLength()) + { + packet.SetLastUpdate(true); + } + m_editorNetworkInterface->SendReliablePacket(m_editorConnId, packet); + } + } void MultiplayerEditorSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h index 31ecdf83a3..d43d8747b9 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h @@ -81,5 +81,7 @@ namespace Multiplayer IEditor* m_editor = nullptr; AzFramework::ProcessWatcher* m_serverProcess = nullptr; + AzNetworking::ConnectionId m_editorConnId; + AzNetworking::INetworkInterface* m_editorNetworkInterface = nullptr; }; } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 03c661ab03..3c1e142d96 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -57,7 +57,9 @@ namespace Multiplayer using namespace AzNetworking; static const AZStd::string_view s_networkInterfaceName("MultiplayerNetworkInterface"); + static const AZStd::string_view s_networkEditorInterfaceName("MultiplayerEditorNetworkInterface"); static constexpr uint16_t DefaultServerPort = 30090; + static constexpr uint16_t DefaultServerEditorPort = 30091; AZ_CVAR(uint16_t, cl_clientport, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port to bind to for game traffic when connecting to a remote host, a value of 0 will select any available port"); AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, "127.0.0.1", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the remote server or host to connect to"); @@ -397,6 +399,19 @@ namespace Multiplayer return false; } + bool MultiplayerSystemComponent::HandleRequest + ( + [[maybe_unused]] AzNetworking::IConnection* connection, + [[maybe_unused]] const IPacketHeader& packetHeader, + [[maybe_unused]] MultiplayerPackets::EditorServerInit& packet + ) + { +#if !defined(_RELEASE) + // Support Editor Server Init for all non-release targets +#endif + return true; + } + ConnectResult MultiplayerSystemComponent::ValidateConnect ( [[maybe_unused]] const IpAddress& remoteAddress, @@ -492,6 +507,12 @@ namespace Multiplayer { if (multiplayerType == MultiplayerAgentType::ClientServer || multiplayerType == MultiplayerAgentType::DedicatedServer) { +#if !defined(_RELEASE) + m_networkEditorInterface = AZ::Interface::Get()->CreateNetworkInterface( + AZ::Name(s_networkEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this); + m_networkEditorInterface->Listen(DefaultServerEditorPort); +#endif + m_initEvent.Signal(m_networkInterface); const AZ::Aabb worldBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-16384.0f), AZ::Vector3(16384.0f)); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index f25e530b61..e77fa129b0 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -72,7 +72,8 @@ namespace Multiplayer bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::NotifyClientMigration& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityMigration& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet); - + bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EditorServerInit& packet); + //! IConnectionListener interface //! @{ AzNetworking::ConnectResult ValidateConnect(const AzNetworking::IpAddress& remoteAddress, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; @@ -109,6 +110,7 @@ namespace Multiplayer AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session"); AzNetworking::INetworkInterface* m_networkInterface = nullptr; + AzNetworking::INetworkInterface* m_networkEditorInterface = nullptr; AZ::ConsoleCommandInvokedEvent::Handler m_consoleCommandHandler; AZ::ThreadSafeDeque m_cvarCommands; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp index a5df3c1dc5..ae91999a0c 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp @@ -24,6 +24,16 @@ namespace Multiplayer NetworkPrefabProcessor::Reflect(context); } + void MultiplayerToolsSystemComponent::Activate() + { + AZ::Interface::Register(this); + } + + void MultiplayerToolsSystemComponent::Deactivate() + { + AZ::Interface::Unregister(this); + } + bool MultiplayerToolsSystemComponent::DidProcessNetworkPrefabs() { return m_didProcessNetPrefabs; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.h b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.h index 82d0415c5a..181a971150 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.h @@ -31,9 +31,8 @@ namespace Multiplayer ~MultiplayerToolsSystemComponent() override = default; /// AZ::Component overrides. - void Activate() override {}; - - void Deactivate() override {}; + void Activate() override; + void Deactivate() override; bool DidProcessNetworkPrefabs() override; From 8a39f9f1b474640b4b525423b0ec3fa9c32c187e Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 13 May 2021 15:31:37 -0700 Subject: [PATCH 03/21] Streamline MP Ctrl+G logic via MultiplayerEditorConnection --- .../AzNetworking/TcpTransport/TcpSocket.cpp | 2 +- .../UdpTransport/UdpNetworkInterface.cpp | 2 +- .../AzNetworking/UdpTransport/UdpSocket.cpp | 2 +- Gems/Multiplayer/Code/CMakeLists.txt | 25 +-- .../AutoGen/Multiplayer.AutoPackets.xml | 7 +- .../AutoGen/MultiplayerEditor.AutoPackets.xml | 14 ++ .../Editor/MultiplayerEditorConnection.cpp | 162 ++++++++++++++ .../Editor/MultiplayerEditorConnection.h | 55 +++++ .../Editor/MultiplayerEditorDispatcher.cpp | 21 -- .../Editor/MultiplayerEditorDispatcher.h | 36 ---- .../{ => Editor}/MultiplayerEditorGem.cpp | 2 +- .../{ => Editor}/MultiplayerEditorGem.h | 0 .../MultiplayerEditorSystemComponent.cpp | 199 ++++++++---------- .../Editor/MultiplayerEditorSystemComponent.h | 13 +- .../Source/MultiplayerSystemComponent.cpp | 42 ++-- .../Code/Source/MultiplayerSystemComponent.h | 8 +- .../Pipeline/NetworkPrefabProcessor.cpp | 4 + .../Code/multiplayer_editor_files.cmake | 2 - .../multiplayer_editor_shared_files.cmake | 4 +- Gems/Multiplayer/Code/multiplayer_files.cmake | 3 + 20 files changed, 359 insertions(+), 244 deletions(-) create mode 100644 Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml create mode 100644 Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp create mode 100644 Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h delete mode 100644 Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorDispatcher.cpp delete mode 100644 Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorDispatcher.h rename Gems/Multiplayer/Code/Source/{ => Editor}/MultiplayerEditorGem.cpp (97%) rename Gems/Multiplayer/Code/Source/{ => Editor}/MultiplayerEditorGem.h (100%) diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp index e8c30d0816..70000dc9a8 100644 --- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocket.cpp @@ -176,7 +176,7 @@ namespace AzNetworking if (::bind(aznumeric_cast(m_socketFd), (const sockaddr*)&hints, sizeof(hints)) != 0) { const int32_t error = GetLastNetworkError(); - AZLOG_ERROR("Failed to bind socket (%d:%s)", error, GetNetworkErrorDesc(error)); + AZLOG_ERROR("Failed to bind TCP socket to port %u (%d:%s)", uint32_t(port), error, GetNetworkErrorDesc(error)); return false; } diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp index 870545e6c8..5676d48150 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp @@ -162,7 +162,7 @@ namespace AzNetworking const UdpReaderThread::ReceivedPackets* packets = m_readerThread.GetReceivedPackets(m_socket.get()); if (packets == nullptr) { - AZ_Assert(false, "nullptr was retrieved for the received packet buffer, check that the socket has been registered with the reader thread"); + // Socket is not yet registered with the reader thread and is likely still pending, try again later return; } diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp index e642c87623..cbb5f8e6c0 100644 --- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpSocket.cpp @@ -82,7 +82,7 @@ namespace AzNetworking if (::bind(static_cast(m_socketFd), (const sockaddr *)&hints, sizeof(hints)) != 0) { const int32_t error = GetLastNetworkError(); - AZLOG_ERROR("Failed to bind socket to port %u (%d:%s)", uint32_t(port), error, GetNetworkErrorDesc(error)); + AZLOG_ERROR("Failed to bind UDP socket to port %u (%d:%s)", uint32_t(port), error, GetNetworkErrorDesc(error)); return false; } } diff --git a/Gems/Multiplayer/Code/CMakeLists.txt b/Gems/Multiplayer/Code/CMakeLists.txt index 46f56ef315..3f0c2936b7 100644 --- a/Gems/Multiplayer/Code/CMakeLists.txt +++ b/Gems/Multiplayer/Code/CMakeLists.txt @@ -77,12 +77,12 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AzToolsFramework Gem::Multiplayer.Static ) - + ly_add_target( - NAME Multiplayer.Editor.Static STATIC + NAME Multiplayer.Editor GEM_MODULE NAMESPACE Gem FILES_CMAKE - multiplayer_editor_files.cmake + multiplayer_editor_shared_files.cmake COMPILE_DEFINITIONS PUBLIC MULTIPLAYER_EDITOR @@ -94,7 +94,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) PUBLIC Include BUILD_DEPENDENCIES - PUBLIC + PRIVATE Legacy::CryCommon Legacy::Editor.Headers AZ::AzCore @@ -102,23 +102,6 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AzNetworking AZ::AzToolsFramework Gem::Multiplayer.Static - ) - - ly_add_target( - NAME Multiplayer.Editor GEM_MODULE - NAMESPACE Gem - FILES_CMAKE - multiplayer_editor_shared_files.cmake - INCLUDE_DIRECTORIES - PRIVATE - . - Source - ${pal_source_dir} - PUBLIC - Include - BUILD_DEPENDENCIES - PRIVATE - Gem::Multiplayer.Editor.Static Gem::Multiplayer.Tools ) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index 84a7207f0c..633218c215 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -59,10 +59,5 @@ - - - - - - + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml new file mode 100644 index 0000000000..986860e822 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp new file mode 100644 index 0000000000..dcf9c134da --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -0,0 +1,162 @@ +/* + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + using namespace AzNetworking; + + static const AZStd::string_view s_networkInterfaceName("MultiplayerNetworkInterface"); + static const AZStd::string_view s_networkEditorInterfaceName("MultiplayerEditorNetworkInterface"); + static constexpr AZStd::string_view DefaultEditorIp = "127.0.0.1"; + static constexpr uint16_t DefaultServerPort = 30090; + static constexpr uint16_t DefaultServerEditorPort = 30091; + + static AZStd::vector buffer; + static AZ::IO::ByteContainerStream> s_byteStream(&buffer); + + AZ_CVAR(bool, editorsv_isDedicated, false, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether to init as a server expecting data from an Editor. Do not modify unless you're sure of what you're doing."); + + MultiplayerEditorConnection::MultiplayerEditorConnection() + { + m_networkEditorInterface = AZ::Interface::Get()->CreateNetworkInterface( + AZ::Name(s_networkEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this); + if (editorsv_isDedicated) + { + m_networkEditorInterface->Listen(DefaultServerEditorPort); + } + } + + bool MultiplayerEditorConnection::HandleRequest + ( + [[maybe_unused]] AzNetworking::IConnection* connection, + [[maybe_unused]] const IPacketHeader& packetHeader, + [[maybe_unused]] MultiplayerEditorPackets::EditorServerInit& packet + ) + { + // Editor Server Init is intended for non-release targets + if (!packet.GetLastUpdate()) + { + // More packets are expected, flush this to the buffer + s_byteStream.Write(TcpPacketEncodingBuffer::GetCapacity(), reinterpret_cast(packet.ModifyAssetData().GetBuffer())); + } + else + { + // This is the last expected packet, flush it to the buffer + s_byteStream.Write(packet.GetAssetData().GetSize(), reinterpret_cast(packet.ModifyAssetData().GetBuffer())); + + // Read all assets out of the buffer + s_byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); + AZStd::vector> assetData; + while (s_byteStream.GetCurPos() < s_byteStream.GetLength()) + { + AZ::Data::AssetLoadBehavior assetLoadBehavior; + s_byteStream.Read(sizeof(AZ::Data::AssetLoadBehavior), reinterpret_cast(&assetLoadBehavior)); + + AZ::Data::AssetData* assetDatum = AZ::Utils::LoadObjectFromStream(s_byteStream, nullptr); + AZ::Data::Asset asset = AZ::Data::Asset(assetDatum, assetLoadBehavior); + + /* + // Register Asset to AssetManager + */ + + assetData.push_back(asset); + } + + // Now that we've deserialized, clear the byte stream + s_byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); + s_byteStream.Truncate(); + + /* + // Hand-off our resultant assets + */ + + AZLOG_INFO("Editor Server completed asset receive, responding to Editor..."); + if (connection->SendReliablePacket(MultiplayerEditorPackets::EditorServerReady())) + { + // Setup the normal multiplayer connection + AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::DedicatedServer); + INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName)); + networkInterface->Listen(DefaultServerPort); + + return true; + } + else + { + return false; + } + } + + return true; + } + + bool MultiplayerEditorConnection::HandleRequest + ( + [[maybe_unused]] AzNetworking::IConnection* connection, + [[maybe_unused]] const IPacketHeader& packetHeader, + [[maybe_unused]] MultiplayerEditorPackets::EditorServerReady& packet + ) + { + if (connection->GetConnectionRole() == ConnectionRole::Connector) + { + // Receiving this packet means Editor sync is done, disconnect + connection->Disconnect(AzNetworking::DisconnectReason::TerminatedByClient, AzNetworking::TerminationEndpoint::Local); + + // Connect the Editor to the local server for Multiplayer simulation + AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); + INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName)); + const IpAddress ipAddress(DefaultEditorIp.data(), DefaultServerEditorPort, networkInterface->GetType()); + networkInterface->Connect(ipAddress); + } + return true; + } + + ConnectResult MultiplayerEditorConnection::ValidateConnect + ( + [[maybe_unused]] const IpAddress& remoteAddress, + [[maybe_unused]] const IPacketHeader& packetHeader, + [[maybe_unused]] ISerializer& serializer + ) + { + return ConnectResult::Accepted; + } + + void MultiplayerEditorConnection::OnConnect([[maybe_unused]] AzNetworking::IConnection* connection) + { + ; + } + + bool MultiplayerEditorConnection::OnPacketReceived(AzNetworking::IConnection* connection, const IPacketHeader& packetHeader, ISerializer& serializer) + { + return MultiplayerEditorPackets::DispatchPacket(connection, packetHeader, serializer, *this); + } + + void MultiplayerEditorConnection::OnPacketLost([[maybe_unused]] IConnection* connection, [[maybe_unused]] PacketId packetId) + { + ; + } + + void MultiplayerEditorConnection::OnDisconnect([[maybe_unused]] AzNetworking::IConnection* connection, [[maybe_unused]] DisconnectReason reason, [[maybe_unused]] TerminationEndpoint endpoint) + { + ; + } +} diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h new file mode 100644 index 0000000000..3621e3aee6 --- /dev/null +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h @@ -0,0 +1,55 @@ +/* +* 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. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AzNetworking +{ + class INetworkInterface; +} + +namespace Multiplayer +{ + //! MultiplayerEditorConnection is a connection listener to synchronize the Editor and a local server it launches + class MultiplayerEditorConnection final + : public AzNetworking::IConnectionListener + { + public: + MultiplayerEditorConnection(); + ~MultiplayerEditorConnection() = default; + + bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerInit& packet); + bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerReady& packet); + + //! IConnectionListener interface + //! @{ + AzNetworking::ConnectResult ValidateConnect(const AzNetworking::IpAddress& remoteAddress, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; + void OnConnect(AzNetworking::IConnection* connection) override; + bool OnPacketReceived(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; + void OnPacketLost(AzNetworking::IConnection* connection, AzNetworking::PacketId packetId) override; + void OnDisconnect(AzNetworking::IConnection* connection, AzNetworking::DisconnectReason reason, AzNetworking::TerminationEndpoint endpoint) override; + //! @} + + private: + + AzNetworking::INetworkInterface* m_networkEditorInterface = nullptr; + }; +} diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorDispatcher.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorDispatcher.cpp deleted file mode 100644 index 470aa61cd0..0000000000 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorDispatcher.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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. - * - */ - -#include - -namespace Multiplayer -{ - MultiplayerEditorDispatcher::MultiplayerEditorDispatcher() - { - ; - } -} diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorDispatcher.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorDispatcher.h deleted file mode 100644 index c1058dc8a0..0000000000 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorDispatcher.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -* 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. -* -*/ - -#pragma once - -#include - -#include -#include -#include -#include - -#include - - -namespace Multiplayer -{ - //! MultiplayerEditorDispatcher is responsible for dispatching delta from the Editor to an Editor launched local server - class MultiplayerEditorDispatcher final - { - public: - MultiplayerEditorDispatcher(); - ~MultiplayerEditorDispatcher() = default; - - private: - }; -} diff --git a/Gems/Multiplayer/Code/Source/MultiplayerEditorGem.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorGem.cpp similarity index 97% rename from Gems/Multiplayer/Code/Source/MultiplayerEditorGem.cpp rename to Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorGem.cpp index 97c5e4d105..ae38ef1d6d 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerEditorGem.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorGem.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include diff --git a/Gems/Multiplayer/Code/Source/MultiplayerEditorGem.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorGem.h similarity index 100% rename from Gems/Multiplayer/Code/Source/MultiplayerEditorGem.h rename to Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorGem.h diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 15f00bdf80..4c3e8200a1 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -26,16 +26,16 @@ namespace Multiplayer { - static const AZStd::string_view s_networkInterfaceName("MultiplayerEditorServerInterface"); + static const AZStd::string_view s_networkEditorInterfaceName("MultiplayerEditorNetworkInterface"); using namespace AzNetworking; - AZ_CVAR(bool, editorsv_enabled, false, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, + AZ_CVAR(bool, editorsv_enabled, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether Editor launching a local server to connect to is supported"); AZ_CVAR(AZ::CVarFixedString, editorsv_process, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The server executable that should be run. Empty to use the current project's ServerLauncher"); - AZ_CVAR(AZ::CVarFixedString, sv_serveraddr, "127.0.0.1", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the server to connect to"); - AZ_CVAR(uint16_t, sv_port, 30091, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that the multiplayer editor gem will bind to for traffic"); + AZ_CVAR(AZ::CVarFixedString, editorsv_serveraddr, "127.0.0.1", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the server to connect to"); + AZ_CVAR(uint16_t, editorsv_port, 30091, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that the multiplayer editor gem will bind to for traffic"); void MultiplayerEditorSystemComponent::Reflect(AZ::ReflectContext* context) { @@ -70,16 +70,6 @@ namespace Multiplayer { AzFramework::GameEntityContextEventBus::Handler::BusConnect(); AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); - - // Setup a network interface handled by MultiplayerSystemComponent - if (m_editorNetworkInterface == nullptr) - { - AZ::Entity* systemEntity = this->GetEntity(); - MultiplayerSystemComponent* mpSysComponent = systemEntity->FindComponent(); - - m_editorNetworkInterface = AZ::Interface::Get()->CreateNetworkInterface( - AZ::Name(s_networkInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *mpSysComponent); - } } void MultiplayerEditorSystemComponent::Deactivate() @@ -109,18 +99,16 @@ namespace Multiplayer } [[fallthrough]]; case eNotify_OnEndGameMode: - AZ::TickBus::Handler::BusDisconnect(); // Kill the configured server if it's active if (m_serverProcess) { m_serverProcess->TerminateProcess(0); m_serverProcess = nullptr; } - if (m_editorNetworkInterface) + INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkEditorInterfaceName)); + if (editorNetworkInterface) { - // Disconnect the interface, connection management will clean it up - m_editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByUser); - m_editorConnId = AzNetworking::InvalidConnectionId; + editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByClient); } break; } @@ -133,116 +121,95 @@ namespace Multiplayer { AZ_Error("MultiplayerEditor", prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface unavailable"); } - const AZStd::vector>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData(); - - AZStd::vector buffer; - AZ::IO::ByteContainerStream byteStream(&buffer); - - // Serialize Asset information and AssetData into a potentially large buffer - for (auto asset : assetData) - { - AZ::Data::AssetId assetId = asset.GetId(); - AZ::Data::AssetType assetType = asset.GetType(); - const AZStd::string& assetHint = asset.GetHint(); - AZ::IO::SizeType assetHintSize = assetHint.size(); - AZ::Data::AssetLoadBehavior assetLoadBehavior = asset.GetAutoLoadBehavior(); - - byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId)); - byteStream.Write(sizeof(AZ::Data::AssetType), reinterpret_cast(&assetType)); - byteStream.Write(sizeof(assetHintSize), reinterpret_cast(&assetHintSize)); - byteStream.Write(assetHint.size(), assetHint.c_str()); - byteStream.Write(sizeof(AZ::Data::AssetLoadBehavior), reinterpret_cast(&assetLoadBehavior)); - - AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType()); - } // BeginGameMode and Prefab Processing have completed at this point IMultiplayerTools* mpTools = AZ::Interface::Get(); if (editorsv_enabled && mpTools != nullptr && mpTools->DidProcessNetworkPrefabs()) { - AZ::TickBus::Handler::BusConnect(); - - if (assetData.size() > 0) - { - // Assemble the server's path - AZ::CVarFixedString serverProcess = editorsv_process; - if (serverProcess.empty()) - { - // If enabled but no process name is supplied, try this project's ServerLauncher - serverProcess = AZ::Utils::GetProjectName() + ".ServerLauncher"; - } - - AZ::IO::FixedMaxPathString serverPath = AZ::Utils::GetExecutableDirectory(); - if (!serverProcess.contains(AZ_TRAIT_OS_PATH_SEPARATOR)) - { - // If only the process name is specified, append that as well - serverPath.append(AZ_TRAIT_OS_PATH_SEPARATOR + serverProcess); - } - else - { - // If any path was already specified, then simply assign - serverPath = serverProcess; - } - - if (!serverProcess.ends_with(AZ_TRAIT_OS_EXECUTABLE_EXTENSION)) - { - // Add this platform's exe extension if it's not specified - serverPath.append(AZ_TRAIT_OS_EXECUTABLE_EXTENSION); - } - - // Start the configured server if it's available - AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - processLaunchInfo.m_commandlineParameters = AZStd::string::format("\"%s\"", serverPath.c_str()); - processLaunchInfo.m_showWindow = true; - processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_NORMAL; - - m_serverProcess = AzFramework::ProcessWatcher::LaunchProcess( - processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE); - } - } - - // Now that the server has launched, attempt to connect the NetworkInterface - const AZ::CVarFixedString remoteAddress = sv_serveraddr; - m_editorConnId = m_editorNetworkInterface->Connect( - AzNetworking::IpAddress(remoteAddress.c_str(), sv_port, AzNetworking::ProtocolType::Tcp)); - - // Read the buffer into EditorServerInit packets until we've flushed the whole thing - byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); + const AZStd::vector>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData(); - while (byteStream.GetCurPos() < byteStream.GetLength()) - { - MultiplayerPackets::EditorServerInit packet; - AzNetworking::TcpPacketEncodingBuffer& outBuffer = packet.ModifyAssetData(); + AZStd::vector buffer; + AZ::IO::ByteContainerStream byteStream(&buffer); - // Size the packet's buffer appropriately - size_t readSize = TcpPacketEncodingBuffer::GetCapacity(); - size_t byteStreamSize = byteStream.GetLength() - byteStream.GetCurPos(); - if (byteStreamSize < readSize) + // Serialize Asset information and AssetData into a potentially large buffer + for (auto asset : assetData) { - readSize = byteStreamSize; + AZ::Data::AssetLoadBehavior assetLoadBehavior = asset.GetAutoLoadBehavior(); + byteStream.Write(sizeof(AZ::Data::AssetLoadBehavior), reinterpret_cast(&assetLoadBehavior)); + + AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType()); } - outBuffer.Resize(readSize); - byteStream.Read(readSize, outBuffer.GetBuffer()); - - // If we've run out of buffer, mark that we're done - if (byteStream.GetCurPos() == byteStream.GetLength()) + // Assemble the server's path + AZ::CVarFixedString serverProcess = editorsv_process; + if (serverProcess.empty()) { - packet.SetLastUpdate(true); + // If enabled but no process name is supplied, try this project's ServerLauncher + serverProcess = AZ::Utils::GetProjectName() + ".ServerLauncher"; + } + + AZ::IO::FixedMaxPathString serverPath = AZ::Utils::GetExecutableDirectory(); + if (!serverProcess.contains(AZ_TRAIT_OS_PATH_SEPARATOR)) + { + // If only the process name is specified, append that as well + serverPath.append(AZ_TRAIT_OS_PATH_SEPARATOR + serverProcess); + } + else + { + // If any path was already specified, then simply assign + serverPath = serverProcess; + } + + if (!serverProcess.ends_with(AZ_TRAIT_OS_EXECUTABLE_EXTENSION)) + { + // Add this platform's exe extension if it's not specified + serverPath.append(AZ_TRAIT_OS_EXECUTABLE_EXTENSION); + } + + // Start the configured server if it's available + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_commandlineParameters = AZStd::string::format("\"%s\" --editorsv_isDedicated true", serverPath.c_str()); + processLaunchInfo.m_showWindow = true; + processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_NORMAL; + + // Launch the Server and give it a few seconds to boot up + m_serverProcess = AzFramework::ProcessWatcher::LaunchProcess( + processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE); + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(15000)); + + // Now that the server has launched, attempt to connect the NetworkInterface + const AZ::CVarFixedString remoteAddress = editorsv_serveraddr; + INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkEditorInterfaceName)); + m_editorConnId = editorNetworkInterface->Connect( + AzNetworking::IpAddress(remoteAddress.c_str(), editorsv_port, AzNetworking::ProtocolType::Tcp)); + + // Read the buffer into EditorServerInit packets until we've flushed the whole thing + byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); + + while (byteStream.GetCurPos() < byteStream.GetLength()) + { + MultiplayerEditorPackets::EditorServerInit packet; + AzNetworking::TcpPacketEncodingBuffer& outBuffer = packet.ModifyAssetData(); + + // Size the packet's buffer appropriately + size_t readSize = TcpPacketEncodingBuffer::GetCapacity(); + size_t byteStreamSize = byteStream.GetLength() - byteStream.GetCurPos(); + if (byteStreamSize < readSize) + { + readSize = byteStreamSize; + } + + outBuffer.Resize(readSize); + byteStream.Read(readSize, outBuffer.GetBuffer()); + + // If we've run out of buffer, mark that we're done + if (byteStream.GetCurPos() == byteStream.GetLength()) + { + packet.SetLastUpdate(true); + } + editorNetworkInterface->SendReliablePacket(m_editorConnId, packet); } - m_editorNetworkInterface->SendReliablePacket(m_editorConnId, packet); } } - - void MultiplayerEditorSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) - { - - } - - int MultiplayerEditorSystemComponent::GetTickOrder() - { - // Tick immediately after the network system component - return AZ::TICK_PLACEMENT + 1; - } } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h index d43d8747b9..569092e981 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h @@ -14,6 +14,8 @@ #include +#include + #include #include #include @@ -34,7 +36,6 @@ namespace Multiplayer //! Multiplayer system component wraps the bridging logic between the game and transport layer. class MultiplayerEditorSystemComponent final : public AZ::Component - , private AZ::TickBus::Handler , private AzFramework::GameEntityContextEventBus::Handler , private AzToolsFramework::EditorEvents::Bus::Handler , private IEditorNotifyListener @@ -61,14 +62,7 @@ namespace Multiplayer void NotifyRegisterViews() override; //! @} - private: - - //! AZ::TickBus::Handler overrides. - //! @{ - void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; - int GetTickOrder() override; - //! @} - + private: //! EditorEvents::Handler overrides //! @{ void OnEditorNotifyEvent(EEditorNotifyEvent event) override; @@ -82,6 +76,5 @@ namespace Multiplayer IEditor* m_editor = nullptr; AzFramework::ProcessWatcher* m_serverProcess = nullptr; AzNetworking::ConnectionId m_editorConnId; - AzNetworking::INetworkInterface* m_editorNetworkInterface = nullptr; }; } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 5dfdd86607..e277b394ff 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include namespace AZ::ConsoleTypeHelpers { @@ -60,6 +63,8 @@ namespace Multiplayer static const AZStd::string_view s_networkEditorInterfaceName("MultiplayerEditorNetworkInterface"); static constexpr uint16_t DefaultServerPort = 30090; static constexpr uint16_t DefaultServerEditorPort = 30091; + //static AZStd::vector buffer; + //static AZ::IO::ByteContainerStream> s_byteStream(&buffer); AZ_CVAR(uint16_t, cl_clientport, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port to bind to for game traffic when connecting to a remote host, a value of 0 will select any available port"); AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, "127.0.0.1", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the remote server or host to connect to"); @@ -107,7 +112,7 @@ namespace Multiplayer AZ::ConsoleInvokedFrom invokedFrom ) { OnConsoleCommandInvoked(command, args, flags, invokedFrom); }) { - ; + } void MultiplayerSystemComponent::Activate() @@ -401,19 +406,6 @@ namespace Multiplayer return false; } - bool MultiplayerSystemComponent::HandleRequest - ( - [[maybe_unused]] AzNetworking::IConnection* connection, - [[maybe_unused]] const IPacketHeader& packetHeader, - [[maybe_unused]] MultiplayerPackets::EditorServerInit& packet - ) - { -#if !defined(_RELEASE) - // Support Editor Server Init for all non-release targets -#endif - return true; - } - ConnectResult MultiplayerSystemComponent::ValidateConnect ( [[maybe_unused]] const IpAddress& remoteAddress, @@ -447,13 +439,19 @@ namespace Multiplayer // TODO: This needs to be set to the players autonomous proxy ------------v NetworkEntityHandle controlledEntity = GetNetworkEntityTracker()->Get(NetEntityId{ 0 }); - if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so + if (controlledEntity.GetEntity() != nullptr) { - connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity)); - } + if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so + { + connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity)); + } - AZStd::unique_ptr window = AZStd::make_unique(controlledEntity, connection); - reinterpret_cast(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window)); + AZStd::unique_ptr window = + AZStd::make_unique(controlledEntity, connection); + reinterpret_cast(connection->GetUserData()) + ->GetReplicationManager() + .SetReplicationWindow(AZStd::move(window)); + } } else { @@ -509,12 +507,6 @@ namespace Multiplayer { if (multiplayerType == MultiplayerAgentType::ClientServer || multiplayerType == MultiplayerAgentType::DedicatedServer) { -#if !defined(_RELEASE) - m_networkEditorInterface = AZ::Interface::Get()->CreateNetworkInterface( - AZ::Name(s_networkEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this); - m_networkEditorInterface->Listen(DefaultServerEditorPort); -#endif - m_initEvent.Signal(m_networkInterface); const AZ::Aabb worldBounds = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-16384.0f), AZ::Vector3(16384.0f)); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index af5791e5aa..c49e5367e6 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -16,10 +16,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -72,7 +74,7 @@ namespace Multiplayer bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::NotifyClientMigration& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityMigration& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet); - bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EditorServerInit& packet); + //bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EditorServerInit& packet); //! IConnectionListener interface //! @{ @@ -125,5 +127,9 @@ namespace Multiplayer AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 }; HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId; + +#if !defined(_RELEASE) + MultiplayerEditorConnection m_editorConnectionListener; +#endif }; } diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index c2b0c07976..bc6e5710fc 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -35,6 +35,10 @@ namespace Multiplayer context.ListPrefabs([&context](AZStd::string_view prefabName, PrefabDom& prefab) { ProcessPrefab(context, prefabName, prefab); }); + if (context.GetProcessedObjects().size() > 0) + { + mpTools->SetDidProcessNetworkPrefabs(true); + } } void NetworkPrefabProcessor::Reflect(AZ::ReflectContext* context) diff --git a/Gems/Multiplayer/Code/multiplayer_editor_files.cmake b/Gems/Multiplayer/Code/multiplayer_editor_files.cmake index ce3e3227e0..5714be5dfb 100644 --- a/Gems/Multiplayer/Code/multiplayer_editor_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_editor_files.cmake @@ -10,6 +10,4 @@ # set(FILES - Source/Editor/MultiplayerEditorDispatcher.cpp - Source/Editor/MultiplayerEditorDispatcher.h ) diff --git a/Gems/Multiplayer/Code/multiplayer_editor_shared_files.cmake b/Gems/Multiplayer/Code/multiplayer_editor_shared_files.cmake index 2d5611d4b6..3fb76061b8 100644 --- a/Gems/Multiplayer/Code/multiplayer_editor_shared_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_editor_shared_files.cmake @@ -12,8 +12,8 @@ set(FILES Source/MultiplayerGem.cpp Source/MultiplayerGem.h - Source/MultiplayerEditorGem.cpp - Source/MultiplayerEditorGem.h + Source/Editor/MultiplayerEditorGem.cpp + Source/Editor/MultiplayerEditorGem.h Source/Editor/MultiplayerEditorSystemComponent.cpp Source/Editor/MultiplayerEditorSystemComponent.h ) diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index 1f4e57ae43..b8fd842426 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -33,6 +33,7 @@ set(FILES Source/AutoGen/AutoComponentTypes_Source.jinja Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml Source/AutoGen/Multiplayer.AutoPackets.xml + Source/AutoGen/MultiplayerEditor.AutoPackets.xml Source/AutoGen/NetworkTransformComponent.AutoComponent.xml Source/Components/LocalPredictionPlayerInputComponent.cpp Source/Components/LocalPredictionPlayerInputComponent.h @@ -52,6 +53,8 @@ set(FILES Source/ConnectionData/ServerToClientConnectionData.cpp Source/ConnectionData/ServerToClientConnectionData.h Source/ConnectionData/ServerToClientConnectionData.inl + Source/Editor/MultiplayerEditorConnection.cpp + Source/Editor/MultiplayerEditorConnection.h Source/EntityDomains/FullOwnershipEntityDomain.cpp Source/EntityDomains/FullOwnershipEntityDomain.h Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp From 467caa61759b12426ba8bbd969b4c89aed21c5a1 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 13 May 2021 15:42:21 -0700 Subject: [PATCH 04/21] Some whitespace and comment cleanup --- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 2 +- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index e277b394ff..867a9d3c2e 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -112,7 +112,7 @@ namespace Multiplayer AZ::ConsoleInvokedFrom invokedFrom ) { OnConsoleCommandInvoked(command, args, flags, invokedFrom); }) { - + ; } void MultiplayerSystemComponent::Activate() diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index c49e5367e6..eba6813169 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -74,8 +74,7 @@ namespace Multiplayer bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::NotifyClientMigration& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityMigration& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet); - //bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EditorServerInit& packet); - + //! IConnectionListener interface //! @{ AzNetworking::ConnectResult ValidateConnect(const AzNetworking::IpAddress& remoteAddress, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override; From 74ea093f71096c124ec25ca39630f5abbea2bdde Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 13 May 2021 15:43:07 -0700 Subject: [PATCH 05/21] More comment cleanup --- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 867a9d3c2e..f920e7ed74 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -63,8 +63,6 @@ namespace Multiplayer static const AZStd::string_view s_networkEditorInterfaceName("MultiplayerEditorNetworkInterface"); static constexpr uint16_t DefaultServerPort = 30090; static constexpr uint16_t DefaultServerEditorPort = 30091; - //static AZStd::vector buffer; - //static AZ::IO::ByteContainerStream> s_byteStream(&buffer); AZ_CVAR(uint16_t, cl_clientport, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port to bind to for game traffic when connecting to a remote host, a value of 0 will select any available port"); AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, "127.0.0.1", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the remote server or host to connect to"); From 1915b97c16e7837bf325e60b58f608b04f5428f5 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Sat, 15 May 2021 12:58:30 -0700 Subject: [PATCH 06/21] Cleanup server launch, misc. MP consts, and register Editor Spawnable assets server side --- .../AzCore/AzCore/Asset/AssetCommon.h | 13 ++ .../PrefabEditorEntityOwnershipInterface.h | 2 + .../Code/Include/MultiplayerConstants.h | 32 +++++ .../Editor/MultiplayerEditorConnection.cpp | 87 +++++++++---- .../MultiplayerEditorSystemComponent.cpp | 114 +++++++++++------- .../Source/MultiplayerSystemComponent.cpp | 16 +-- .../Pipeline/NetworkPrefabProcessor.cpp | 9 +- Gems/Multiplayer/Code/multiplayer_files.cmake | 1 + 8 files changed, 191 insertions(+), 83 deletions(-) create mode 100644 Gems/Multiplayer/Code/Include/MultiplayerConstants.h diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h b/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h index c8a245af68..ff1cd13023 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h @@ -308,6 +308,8 @@ namespace AZ Asset(AssetLoadBehavior loadBehavior = AssetLoadBehavior::Default); /// Create an asset from a valid asset data (created asset), might not be loaded or currently loading. Asset(AssetData* assetData, AssetLoadBehavior loadBehavior); + /// Create an asset from a valid asset data (created asset) and set the asset id for both, might not be loaded or currently loading. + Asset(const AZ::Data::AssetId& id, AssetData* assetData, AssetLoadBehavior loadBehavior); /// Initialize asset pointer with id, type, and hint. No data construction will occur until QueueLoad is called. Asset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type, const AZStd::string& hint = AZStd::string()); @@ -788,6 +790,17 @@ namespace AZ SetData(assetData); } + //========================================================================= + template + Asset::Asset(const AssetId& id, AssetData* assetData, AssetLoadBehavior loadBehavior) + : m_assetId(id) + , m_assetType(azrtti_typeid()) + , m_loadBehavior(loadBehavior) + { + assetData->m_assetId = id; + SetData(assetData); + } + //========================================================================= template Asset::Asset(const AssetId& id, const AZ::Data::AssetType& type, const AZStd::string& hint) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h index 4476876b59..2afabd16a9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h @@ -46,6 +46,8 @@ namespace AzToolsFramework virtual Prefab::InstanceOptionalReference GetRootPrefabInstance() = 0; + //! Get all Assets generated by Prefab processing when entering Play-In Editor mode (Ctrl+G) + //! /return The vector of Assets generated by Prefab processing virtual const AZStd::vector>& GetPlayInEditorAssetData() = 0; virtual bool LoadFromStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) = 0; diff --git a/Gems/Multiplayer/Code/Include/MultiplayerConstants.h b/Gems/Multiplayer/Code/Include/MultiplayerConstants.h new file mode 100644 index 0000000000..892691177a --- /dev/null +++ b/Gems/Multiplayer/Code/Include/MultiplayerConstants.h @@ -0,0 +1,32 @@ +/* +* 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. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + static constexpr AZStd::string_view MPNetworkInterfaceName("MultiplayerNetworkInterface"); + static constexpr AZStd::string_view MPEditorInterfaceName("MultiplayerEditorNetworkInterface"); + + static constexpr AZStd::string_view LocalHost("127.0.0.1"); + static constexpr uint16_t DefaultServerPort = 30090; + static constexpr uint16_t DefaultServerEditorPort = 30091; + +} + diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index dcf9c134da..c97c66544e 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -11,26 +11,22 @@ */ #include +#include #include #include #include #include -#include +#include #include #include #include #include +#include namespace Multiplayer { using namespace AzNetworking; - static const AZStd::string_view s_networkInterfaceName("MultiplayerNetworkInterface"); - static const AZStd::string_view s_networkEditorInterfaceName("MultiplayerEditorNetworkInterface"); - static constexpr AZStd::string_view DefaultEditorIp = "127.0.0.1"; - static constexpr uint16_t DefaultServerPort = 30090; - static constexpr uint16_t DefaultServerEditorPort = 30091; - static AZStd::vector buffer; static AZ::IO::ByteContainerStream> s_byteStream(&buffer); @@ -39,10 +35,16 @@ namespace Multiplayer MultiplayerEditorConnection::MultiplayerEditorConnection() { m_networkEditorInterface = AZ::Interface::Get()->CreateNetworkInterface( - AZ::Name(s_networkEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this); + AZ::Name(MPEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this); if (editorsv_isDedicated) { - m_networkEditorInterface->Listen(DefaultServerEditorPort); + uint16_t editorServerPort = DefaultServerEditorPort; + if (auto console = AZ::Interface::Get(); console) + { + console->GetCvarValue("editorsv_port", editorServerPort); + } + AZ_Assert(m_networkEditorInterface, "MP Editor Network Interface was unregistered before Editor Server could start listening."); + m_networkEditorInterface->Listen(editorServerPort); } } @@ -69,34 +71,56 @@ namespace Multiplayer AZStd::vector> assetData; while (s_byteStream.GetCurPos() < s_byteStream.GetLength()) { + AZ::Data::AssetId assetId; AZ::Data::AssetLoadBehavior assetLoadBehavior; + uint32_t hintSize; + AZStd::string assetHint; + s_byteStream.Read(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId)); s_byteStream.Read(sizeof(AZ::Data::AssetLoadBehavior), reinterpret_cast(&assetLoadBehavior)); + s_byteStream.Read(sizeof(uint32_t), reinterpret_cast(&hintSize)); + assetHint.resize(hintSize); + s_byteStream.Read(hintSize, assetHint.data()); + + size_t assetSize = s_byteStream.GetCurPos(); + AZ::Data::AssetData* assetDatum = AZ::Utils::LoadObjectFromStream(s_byteStream, nullptr); + assetSize = s_byteStream.GetCurPos() - assetSize; + AZ::Data::Asset asset = AZ::Data::Asset(assetId, assetDatum, assetLoadBehavior); + asset.SetHint(assetHint); + + AZ::Data::AssetInfo assetInfo; + assetInfo.m_assetId = asset.GetId(); + assetInfo.m_assetType = asset.GetType(); + assetInfo.m_relativePath = asset.GetHint(); + assetInfo.m_sizeBytes = assetSize; - AZ::Data::AssetData* assetDatum = AZ::Utils::LoadObjectFromStream(s_byteStream, nullptr); - AZ::Data::Asset asset = AZ::Data::Asset(assetDatum, assetLoadBehavior); - - /* // Register Asset to AssetManager - */ - - assetData.push_back(asset); + AZ::Data::AssetManager::Instance().AssignAssetData(asset); + AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::RegisterAsset, asset.GetId(), assetInfo); + + assetData.push_back(asset); } // Now that we've deserialized, clear the byte stream s_byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); s_byteStream.Truncate(); - /* - // Hand-off our resultant assets - */ + // Load the level via the root spawnable tha was registered + AZ::CVarFixedString loadLevelString = "LoadLevel Root.spawnable"; + AZ::Interface::Get()->PerformCommand(loadLevelString.c_str()); AZLOG_INFO("Editor Server completed asset receive, responding to Editor..."); if (connection->SendReliablePacket(MultiplayerEditorPackets::EditorServerReady())) { // Setup the normal multiplayer connection AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::DedicatedServer); - INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName)); - networkInterface->Listen(DefaultServerPort); + INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName)); + + uint16_t serverPort = DefaultServerPort; + if (auto console = AZ::Interface::Get(); console) + { + console->GetCvarValue("sv_port", serverPort); + } + networkInterface->Listen(serverPort); return true; } @@ -121,11 +145,22 @@ namespace Multiplayer // Receiving this packet means Editor sync is done, disconnect connection->Disconnect(AzNetworking::DisconnectReason::TerminatedByClient, AzNetworking::TerminationEndpoint::Local); - // Connect the Editor to the local server for Multiplayer simulation - AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); - INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName)); - const IpAddress ipAddress(DefaultEditorIp.data(), DefaultServerEditorPort, networkInterface->GetType()); - networkInterface->Connect(ipAddress); + if (auto console = AZ::Interface::Get(); console) + { + AZ::CVarFixedString remoteAddress; + uint16_t remotePort; + if (console->GetCvarValue("editorsv_serveraddr", remoteAddress) != AZ::GetValueResult::ConsoleVarNotFound && + console->GetCvarValue("editorsv_port", remotePort) != AZ::GetValueResult::ConsoleVarNotFound) + { + // Connect the Editor to the editor server for Multiplayer simulation + AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); + INetworkInterface* networkInterface = + AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName)); + + const IpAddress ipAddress(remoteAddress.c_str(), remotePort, networkInterface->GetType()); + networkInterface->Connect(ipAddress); + } + } } return true; } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 4c3e8200a1..89cb816695 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -26,16 +27,16 @@ namespace Multiplayer { - static const AZStd::string_view s_networkEditorInterfaceName("MultiplayerEditorNetworkInterface"); - using namespace AzNetworking; AZ_CVAR(bool, editorsv_enabled, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether Editor launching a local server to connect to is supported"); + AZ_CVAR(bool, editorsv_launch, false, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, + "Whether Editor should launch a server when the server address is localhost"); AZ_CVAR(AZ::CVarFixedString, editorsv_process, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The server executable that should be run. Empty to use the current project's ServerLauncher"); - AZ_CVAR(AZ::CVarFixedString, editorsv_serveraddr, "127.0.0.1", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the server to connect to"); - AZ_CVAR(uint16_t, editorsv_port, 30091, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that the multiplayer editor gem will bind to for traffic"); + AZ_CVAR(AZ::CVarFixedString, editorsv_serveraddr, LocalHost.data(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the server to connect to"); + AZ_CVAR(uint16_t, editorsv_port, DefaultServerEditorPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that the multiplayer editor gem will bind to for traffic"); void MultiplayerEditorSystemComponent::Reflect(AZ::ReflectContext* context) { @@ -105,7 +106,7 @@ namespace Multiplayer m_serverProcess->TerminateProcess(0); m_serverProcess = nullptr; } - INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkEditorInterfaceName)); + INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPEditorInterfaceName)); if (editorNetworkInterface) { editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByClient); @@ -114,6 +115,46 @@ namespace Multiplayer } } + void LaunchEditorServer(AzFramework::ProcessWatcher* outProcess) + { + // Assemble the server's path + AZ::CVarFixedString serverProcess = editorsv_process; + if (serverProcess.empty()) + { + // If enabled but no process name is supplied, try this project's ServerLauncher + serverProcess = AZ::Utils::GetProjectName() + ".ServerLauncher"; + } + + AZ::IO::FixedMaxPathString serverPath = AZ::Utils::GetExecutableDirectory(); + if (!serverProcess.contains(AZ_TRAIT_OS_PATH_SEPARATOR)) + { + // If only the process name is specified, append that as well + serverPath.append(AZ_TRAIT_OS_PATH_SEPARATOR + serverProcess); + } + else + { + // If any path was already specified, then simply assign + serverPath = serverProcess; + } + + if (!serverProcess.ends_with(AZ_TRAIT_OS_EXECUTABLE_EXTENSION)) + { + // Add this platform's exe extension if it's not specified + serverPath.append(AZ_TRAIT_OS_EXECUTABLE_EXTENSION); + } + + // Start the configured server if it's available + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_commandlineParameters = AZStd::string::format("\"%s\" --editorsv_isDedicated true", serverPath.c_str()); + processLaunchInfo.m_showWindow = true; + processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_NORMAL; + + // Launch the Server and give it a few seconds to boot up + outProcess = AzFramework::ProcessWatcher::LaunchProcess( + processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE); + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(15000)); + } + void MultiplayerEditorSystemComponent::OnGameEntitiesStarted() { auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); @@ -134,55 +175,38 @@ namespace Multiplayer // Serialize Asset information and AssetData into a potentially large buffer for (auto asset : assetData) { + AZ::Data::AssetId assetId = asset.GetId(); AZ::Data::AssetLoadBehavior assetLoadBehavior = asset.GetAutoLoadBehavior(); + AZStd::string assetHint = asset.GetHint(); + uint32_t hintSize = aznumeric_cast(assetHint.size()); + + byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId)); byteStream.Write(sizeof(AZ::Data::AssetLoadBehavior), reinterpret_cast(&assetLoadBehavior)); - + byteStream.Write(sizeof(uint32_t), reinterpret_cast(&hintSize)); + byteStream.Write(assetHint.size(), assetHint.data()); AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType()); } - // Assemble the server's path - AZ::CVarFixedString serverProcess = editorsv_process; - if (serverProcess.empty()) - { - // If enabled but no process name is supplied, try this project's ServerLauncher - serverProcess = AZ::Utils::GetProjectName() + ".ServerLauncher"; - } - - AZ::IO::FixedMaxPathString serverPath = AZ::Utils::GetExecutableDirectory(); - if (!serverProcess.contains(AZ_TRAIT_OS_PATH_SEPARATOR)) - { - // If only the process name is specified, append that as well - serverPath.append(AZ_TRAIT_OS_PATH_SEPARATOR + serverProcess); - } - else - { - // If any path was already specified, then simply assign - serverPath = serverProcess; - } - - if (!serverProcess.ends_with(AZ_TRAIT_OS_EXECUTABLE_EXTENSION)) - { - // Add this platform's exe extension if it's not specified - serverPath.append(AZ_TRAIT_OS_EXECUTABLE_EXTENSION); - } - - // Start the configured server if it's available - AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; - processLaunchInfo.m_commandlineParameters = AZStd::string::format("\"%s\" --editorsv_isDedicated true", serverPath.c_str()); - processLaunchInfo.m_showWindow = true; - processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_NORMAL; - - // Launch the Server and give it a few seconds to boot up - m_serverProcess = AzFramework::ProcessWatcher::LaunchProcess( - processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE); - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(15000)); - - // Now that the server has launched, attempt to connect the NetworkInterface const AZ::CVarFixedString remoteAddress = editorsv_serveraddr; - INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkEditorInterfaceName)); + if (editorsv_launch && LocalHost.compare(remoteAddress.c_str()) == 0) + { + LaunchEditorServer(m_serverProcess); + } + + // Now that the server has launched, attempt to connect the NetworkInterface + INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPEditorInterfaceName)); + AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect."); m_editorConnId = editorNetworkInterface->Connect( AzNetworking::IpAddress(remoteAddress.c_str(), editorsv_port, AzNetworking::ProtocolType::Tcp)); + if (m_editorConnId == AzNetworking::InvalidConnectionId) + { + AZ_Warning( + "MultiplayerEditor", false, + "Could not connect to server targeted by Editor. If using a local server, check that it's built and editorsv_launch is true."); + return; + } + // Read the buffer into EditorServerInit packets until we've flushed the whole thing byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index f920e7ed74..a0a343f320 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -10,6 +10,7 @@ * */ +#include #include #include #include @@ -59,13 +60,8 @@ namespace Multiplayer { using namespace AzNetworking; - static const AZStd::string_view s_networkInterfaceName("MultiplayerNetworkInterface"); - static const AZStd::string_view s_networkEditorInterfaceName("MultiplayerEditorNetworkInterface"); - static constexpr uint16_t DefaultServerPort = 30090; - static constexpr uint16_t DefaultServerEditorPort = 30091; - AZ_CVAR(uint16_t, cl_clientport, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port to bind to for game traffic when connecting to a remote host, a value of 0 will select any available port"); - AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, "127.0.0.1", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the remote server or host to connect to"); + AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, LocalHost.data(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the remote server or host to connect to"); AZ_CVAR(AZ::CVarFixedString, cl_serverpassword, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Optional server password"); AZ_CVAR(uint16_t, cl_serverport, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port of the remote host to connect to for game traffic"); AZ_CVAR(uint16_t, sv_port, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that this multiplayer gem will bind to for game traffic"); @@ -116,7 +112,7 @@ namespace Multiplayer void MultiplayerSystemComponent::Activate() { AZ::TickBus::Handler::BusConnect(); - m_networkInterface = AZ::Interface::Get()->CreateNetworkInterface(AZ::Name(s_networkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this); + m_networkInterface = AZ::Interface::Get()->CreateNetworkInterface(AZ::Name(MPNetworkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this); m_consoleCommandHandler.Connect(AZ::Interface::Get()->GetConsoleCommandInvokedEvent()); AZ::Interface::Register(this); @@ -635,7 +631,7 @@ namespace Multiplayer { Multiplayer::MultiplayerAgentType serverType = sv_isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer; AZ::Interface::Get()->InitializeMultiplayer(serverType); - INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName)); + INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName)); networkInterface->Listen(sv_port); } AZ_CONSOLEFREEFUNC(host, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection as a host for other clients to connect to"); @@ -643,7 +639,7 @@ namespace Multiplayer void connect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); - INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName)); + INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName)); if (arguments.size() < 1) { @@ -673,7 +669,7 @@ namespace Multiplayer void disconnect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Uninitialized); - INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName)); + INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName)); auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); }; networkInterface->GetConnectionSet().VisitConnections(visitor); } diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index bc6e5710fc..137bd27794 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -31,11 +31,16 @@ namespace Multiplayer void NetworkPrefabProcessor::Process(PrefabProcessorContext& context) { IMultiplayerTools* mpTools = AZ::Interface::Get(); - mpTools->SetDidProcessNetworkPrefabs(false); + if (mpTools) + { + mpTools->SetDidProcessNetworkPrefabs(false); + } + context.ListPrefabs([&context](AZStd::string_view prefabName, PrefabDom& prefab) { ProcessPrefab(context, prefabName, prefab); }); - if (context.GetProcessedObjects().size() > 0) + + if (mpTools && context.GetProcessedObjects().size() > 0) { mpTools->SetDidProcessNetworkPrefabs(true); } diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index b8fd842426..ecde01e2d7 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -17,6 +17,7 @@ set(FILES Include/INetworkEntityManager.h Include/INetworkTime.h Include/IReplicationWindow.h + Include/MultiplayerConstants.h Include/MultiplayerStats.cpp Include/MultiplayerStats.h Include/MultiplayerTypes.h From 0e53c775162bd46ffd89245b1792e6bb98919a6d Mon Sep 17 00:00:00 2001 From: puvvadar Date: Sat, 15 May 2021 13:47:03 -0700 Subject: [PATCH 07/21] Fix some include paths --- .../Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml | 4 ++-- .../Code/Source/Editor/MultiplayerEditorConnection.cpp | 4 ++-- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml index 986860e822..9904eef83c 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml @@ -2,8 +2,8 @@ - - + + diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index c97c66544e..545e17eff6 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -10,8 +10,8 @@ * */ -#include -#include +#include +#include #include #include #include diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index c8bdd80a39..fe90d68696 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -10,7 +10,7 @@ * */ -#include +#include #include #include #include From df68ab6c568a632b9591570454f5c8062a35448f Mon Sep 17 00:00:00 2001 From: puvvadar Date: Sun, 16 May 2021 15:30:00 -0700 Subject: [PATCH 08/21] Update headers in editor auto packets --- .../Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml index 9904eef83c..8f55ecd2b8 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/MultiplayerEditor.AutoPackets.xml @@ -3,7 +3,7 @@ - + From f94d0c99e72738d4add4b10e053e403bf67bba47 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 17 May 2021 09:58:58 -0700 Subject: [PATCH 09/21] Cleanup connection order slightly --- .../Editor/MultiplayerEditorConnection.cpp | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index 545e17eff6..731c865137 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -93,7 +93,7 @@ namespace Multiplayer assetInfo.m_relativePath = asset.GetHint(); assetInfo.m_sizeBytes = assetSize; - // Register Asset to AssetManager + // Register Asset to AssetManager AZ::Data::AssetManager::Instance().AssignAssetData(asset); AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::RegisterAsset, asset.GetId(), assetInfo); @@ -108,26 +108,19 @@ namespace Multiplayer AZ::CVarFixedString loadLevelString = "LoadLevel Root.spawnable"; AZ::Interface::Get()->PerformCommand(loadLevelString.c_str()); + // Setup the normal multiplayer connection + AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::DedicatedServer); + INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName)); + + uint16_t serverPort = DefaultServerPort; + if (auto console = AZ::Interface::Get(); console) + { + console->GetCvarValue("sv_port", serverPort); + } + networkInterface->Listen(serverPort); + AZLOG_INFO("Editor Server completed asset receive, responding to Editor..."); - if (connection->SendReliablePacket(MultiplayerEditorPackets::EditorServerReady())) - { - // Setup the normal multiplayer connection - AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::DedicatedServer); - INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName)); - - uint16_t serverPort = DefaultServerPort; - if (auto console = AZ::Interface::Get(); console) - { - console->GetCvarValue("sv_port", serverPort); - } - networkInterface->Listen(serverPort); - - return true; - } - else - { - return false; - } + return connection->SendReliablePacket(MultiplayerEditorPackets::EditorServerReady()); } return true; From 83a56ce71cdb62e0a815b3a0f8a1c1419f1c5221 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 17 May 2021 12:53:15 -0700 Subject: [PATCH 10/21] Cleanup typo --- .../Code/Source/Editor/MultiplayerEditorConnection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index 731c865137..e5f7ed4a68 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -104,7 +104,7 @@ namespace Multiplayer s_byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); s_byteStream.Truncate(); - // Load the level via the root spawnable tha was registered + // Load the level via the root spawnable that was registered AZ::CVarFixedString loadLevelString = "LoadLevel Root.spawnable"; AZ::Interface::Get()->PerformCommand(loadLevelString.c_str()); From 6f3f5268d830736872a3ccdc31f03182b2cb91d0 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 17 May 2021 14:06:20 -0700 Subject: [PATCH 11/21] Fix build error on unity builds --- .../Entity/PrefabEditorEntityOwnershipInterface.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h index 2afabd16a9..8412361657 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h @@ -12,6 +12,7 @@ #pragma once +#include #include #include #include From bb851943c8bb20d65ae81c43aeb0638b7414c369 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 17 May 2021 15:16:35 -0700 Subject: [PATCH 12/21] Update unit test to account for missing dependency --- Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp index 2e0b9c0759..6ace4db592 100644 --- a/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp +++ b/Gems/Multiplayer/Code/Tests/MultiplayerSystemTests.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ namespace UnitTest SetupAllocator(); AZ::NameDictionary::Create(); m_spawnableComponent = new AzFramework::SpawnableSystemComponent(); + m_netComponent = new AzNetworking::NetworkingSystemComponent(); m_mpComponent = new Multiplayer::MultiplayerSystemComponent(); m_initHandler = Multiplayer::SessionInitEvent::Handler([this](AzNetworking::INetworkInterface* value) { TestInitEvent(value); }); @@ -43,6 +45,7 @@ namespace UnitTest void TearDown() override { delete m_mpComponent; + delete m_netComponent; delete m_spawnableComponent; AZ::NameDictionary::Destroy(); TeardownAllocator(); @@ -71,6 +74,7 @@ namespace UnitTest Multiplayer::SessionShutdownEvent::Handler m_shutdownHandler; Multiplayer::ConnectionAcquiredEvent::Handler m_connAcquiredHandler; + AzNetworking::NetworkingSystemComponent* m_netComponent = nullptr; Multiplayer::MultiplayerSystemComponent* m_mpComponent = nullptr; AzFramework::SpawnableSystemComponent* m_spawnableComponent = nullptr; }; From 0d9b55bff25e26d86b7b904412432bfec92acf24 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 17 May 2021 16:48:05 -0700 Subject: [PATCH 13/21] Move static buffer to member to prevent potential memory issues --- .../Editor/MultiplayerEditorConnection.cpp | 30 +++++++++---------- .../Editor/MultiplayerEditorConnection.h | 2 ++ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index e5f7ed4a68..2fdd29c542 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -27,12 +27,10 @@ namespace Multiplayer { using namespace AzNetworking; - static AZStd::vector buffer; - static AZ::IO::ByteContainerStream> s_byteStream(&buffer); - AZ_CVAR(bool, editorsv_isDedicated, false, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether to init as a server expecting data from an Editor. Do not modify unless you're sure of what you're doing."); MultiplayerEditorConnection::MultiplayerEditorConnection() + : m_byteStream(&m_buffer) { m_networkEditorInterface = AZ::Interface::Get()->CreateNetworkInterface( AZ::Name(MPEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this); @@ -59,31 +57,31 @@ namespace Multiplayer if (!packet.GetLastUpdate()) { // More packets are expected, flush this to the buffer - s_byteStream.Write(TcpPacketEncodingBuffer::GetCapacity(), reinterpret_cast(packet.ModifyAssetData().GetBuffer())); + m_byteStream.Write(TcpPacketEncodingBuffer::GetCapacity(), reinterpret_cast(packet.ModifyAssetData().GetBuffer())); } else { // This is the last expected packet, flush it to the buffer - s_byteStream.Write(packet.GetAssetData().GetSize(), reinterpret_cast(packet.ModifyAssetData().GetBuffer())); + m_byteStream.Write(packet.GetAssetData().GetSize(), reinterpret_cast(packet.ModifyAssetData().GetBuffer())); // Read all assets out of the buffer - s_byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); + m_byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); AZStd::vector> assetData; - while (s_byteStream.GetCurPos() < s_byteStream.GetLength()) + while (m_byteStream.GetCurPos() < m_byteStream.GetLength()) { AZ::Data::AssetId assetId; AZ::Data::AssetLoadBehavior assetLoadBehavior; uint32_t hintSize; AZStd::string assetHint; - s_byteStream.Read(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId)); - s_byteStream.Read(sizeof(AZ::Data::AssetLoadBehavior), reinterpret_cast(&assetLoadBehavior)); - s_byteStream.Read(sizeof(uint32_t), reinterpret_cast(&hintSize)); + m_byteStream.Read(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId)); + m_byteStream.Read(sizeof(AZ::Data::AssetLoadBehavior), reinterpret_cast(&assetLoadBehavior)); + m_byteStream.Read(sizeof(uint32_t), reinterpret_cast(&hintSize)); assetHint.resize(hintSize); - s_byteStream.Read(hintSize, assetHint.data()); + m_byteStream.Read(hintSize, assetHint.data()); - size_t assetSize = s_byteStream.GetCurPos(); - AZ::Data::AssetData* assetDatum = AZ::Utils::LoadObjectFromStream(s_byteStream, nullptr); - assetSize = s_byteStream.GetCurPos() - assetSize; + size_t assetSize = m_byteStream.GetCurPos(); + AZ::Data::AssetData* assetDatum = AZ::Utils::LoadObjectFromStream(m_byteStream, nullptr); + assetSize = m_byteStream.GetCurPos() - assetSize; AZ::Data::Asset asset = AZ::Data::Asset(assetId, assetDatum, assetLoadBehavior); asset.SetHint(assetHint); @@ -101,8 +99,8 @@ namespace Multiplayer } // Now that we've deserialized, clear the byte stream - s_byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); - s_byteStream.Truncate(); + m_byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN); + m_byteStream.Truncate(); // Load the level via the root spawnable that was registered AZ::CVarFixedString loadLevelString = "LoadLevel Root.spawnable"; diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h index 3621e3aee6..d559651080 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h @@ -51,5 +51,7 @@ namespace Multiplayer private: AzNetworking::INetworkInterface* m_networkEditorInterface = nullptr; + AZStd::vector m_buffer; + AZ::IO::ByteContainerStream> m_byteStream; }; } From 93e267345fb2fb8954ec090e25296dc7c625c98e Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 17 May 2021 18:46:21 -0700 Subject: [PATCH 14/21] Address string/mem feedback plus some misc cleanup --- .../Multiplayer/MultiplayerConstants.h | 10 ++--- .../Editor/MultiplayerEditorConnection.cpp | 7 ++-- .../Source/Editor/MultiplayerEditorGem.cpp | 12 +++--- .../MultiplayerEditorSystemComponent.cpp | 38 ++++++++----------- .../Editor/MultiplayerEditorSystemComponent.h | 4 +- .../Source/MultiplayerSystemComponent.cpp | 20 +++++----- .../Pipeline/NetworkPrefabProcessor.cpp | 12 +++--- 7 files changed, 49 insertions(+), 54 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerConstants.h b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerConstants.h index 892691177a..b82fab91be 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerConstants.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerConstants.h @@ -21,12 +21,12 @@ namespace Multiplayer { - static constexpr AZStd::string_view MPNetworkInterfaceName("MultiplayerNetworkInterface"); - static constexpr AZStd::string_view MPEditorInterfaceName("MultiplayerEditorNetworkInterface"); + constexpr AZStd::string_view MPNetworkInterfaceName("MultiplayerNetworkInterface"); + constexpr AZStd::string_view MPEditorInterfaceName("MultiplayerEditorNetworkInterface"); - static constexpr AZStd::string_view LocalHost("127.0.0.1"); - static constexpr uint16_t DefaultServerPort = 30090; - static constexpr uint16_t DefaultServerEditorPort = 30091; + constexpr AZStd::string_view LocalHost("127.0.0.1"); + constexpr uint16_t DefaultServerPort = 30090; + constexpr uint16_t DefaultServerEditorPort = 30091; } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index 2fdd29c542..f88a314ea5 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -12,16 +12,17 @@ #include #include -#include +#include #include -#include -#include + #include #include #include #include #include #include +#include +#include namespace Multiplayer { diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorGem.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorGem.cpp index ae38ef1d6d..2fe0aabe5f 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorGem.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorGem.cpp @@ -10,13 +10,13 @@ * */ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include -#include +#include namespace Multiplayer { diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index d837b15b05..159f3b944b 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -13,13 +13,15 @@ #include #include #include + +#include +#include #include -#include -#include -#include + #include #include #include +#include #include #include #include @@ -31,11 +33,11 @@ namespace Multiplayer AZ_CVAR(bool, editorsv_enabled, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether Editor launching a local server to connect to is supported"); - AZ_CVAR(bool, editorsv_launch, false, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, + AZ_CVAR(bool, editorsv_launch, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether Editor should launch a server when the server address is localhost"); AZ_CVAR(AZ::CVarFixedString, editorsv_process, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The server executable that should be run. Empty to use the current project's ServerLauncher"); - AZ_CVAR(AZ::CVarFixedString, editorsv_serveraddr, LocalHost.data(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the server to connect to"); + AZ_CVAR(AZ::CVarFixedString, editorsv_serveraddr, AZ::CVarFixedString(LocalHost), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the server to connect to"); AZ_CVAR(uint16_t, editorsv_port, DefaultServerEditorPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that the multiplayer editor gem will bind to for traffic"); void MultiplayerEditorSystemComponent::Reflect(AZ::ReflectContext* context) @@ -115,34 +117,24 @@ namespace Multiplayer } } - void LaunchEditorServer(AzFramework::ProcessWatcher* outProcess) + AzFramework::ProcessWatcher* LaunchEditorServer() { // Assemble the server's path AZ::CVarFixedString serverProcess = editorsv_process; + AZ::IO::FixedMaxPath serverPath; if (serverProcess.empty()) { // If enabled but no process name is supplied, try this project's ServerLauncher serverProcess = AZ::Utils::GetProjectName() + ".ServerLauncher"; - } - AZ::IO::FixedMaxPathString serverPath = AZ::Utils::GetExecutableDirectory(); - if (!serverProcess.contains(AZ_TRAIT_OS_PATH_SEPARATOR)) - { - // If only the process name is specified, append that as well - serverPath.append(AZ_TRAIT_OS_PATH_SEPARATOR + serverProcess); + serverPath = AZ::Utils::GetExecutableDirectory(); + serverPath /= serverProcess + AZ_TRAIT_OS_EXECUTABLE_EXTENSION; } else { - // If any path was already specified, then simply assign serverPath = serverProcess; } - if (!serverProcess.ends_with(AZ_TRAIT_OS_EXECUTABLE_EXTENSION)) - { - // Add this platform's exe extension if it's not specified - serverPath.append(AZ_TRAIT_OS_EXECUTABLE_EXTENSION); - } - // Start the configured server if it's available AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; processLaunchInfo.m_commandlineParameters = AZStd::string::format("\"%s\" --editorsv_isDedicated true", serverPath.c_str()); @@ -150,9 +142,11 @@ namespace Multiplayer processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_NORMAL; // Launch the Server and give it a few seconds to boot up - outProcess = AzFramework::ProcessWatcher::LaunchProcess( + AzFramework::ProcessWatcher* outProcess = AzFramework::ProcessWatcher::LaunchProcess( processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE); AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(15000)); + + return outProcess; } void MultiplayerEditorSystemComponent::OnGameEntitiesStarted() @@ -188,9 +182,9 @@ namespace Multiplayer } const AZ::CVarFixedString remoteAddress = editorsv_serveraddr; - if (editorsv_launch && LocalHost.compare(remoteAddress.c_str()) == 0) + if (editorsv_launch && LocalHost == remoteAddress) { - LaunchEditorServer(m_serverProcess); + m_serverProcess = LaunchEditorServer(); } // Now that the server has launched, attempt to connect the NetworkInterface diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h index 569092e981..81b138c675 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.h @@ -14,18 +14,16 @@ #include -#include +#include #include #include #include #include - #include #include #include - namespace AzNetworking { class INetworkInterface; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index c6a8458eae..aa6fe6e72c 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -11,15 +11,16 @@ */ #include -#include -#include -#include -#include -#include -#include -#include #include -#include + +#include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include namespace AZ::ConsoleTypeHelpers { @@ -63,7 +65,7 @@ namespace Multiplayer using namespace AzNetworking; AZ_CVAR(uint16_t, cl_clientport, 0, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port to bind to for game traffic when connecting to a remote host, a value of 0 will select any available port"); - AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, LocalHost.data(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the remote server or host to connect to"); + AZ_CVAR(AZ::CVarFixedString, cl_serveraddr, AZ::CVarFixedString(LocalHost), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the remote server or host to connect to"); AZ_CVAR(AZ::CVarFixedString, cl_serverpassword, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Optional server password"); AZ_CVAR(uint16_t, cl_serverport, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port of the remote host to connect to for game traffic"); AZ_CVAR(uint16_t, sv_port, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that this multiplayer gem will bind to for game traffic"); diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index 8bea44e893..bd6899aad6 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -10,7 +10,11 @@ * */ -#include +#include +#include +#include +#include +#include #include #include @@ -18,10 +22,6 @@ #include #include #include -#include -#include -#include -#include namespace Multiplayer { @@ -40,7 +40,7 @@ namespace Multiplayer ProcessPrefab(context, prefabName, prefab); }); - if (mpTools && context.GetProcessedObjects().size() > 0) + if (mpTools && !context.GetProcessedObjects().empty()) { mpTools->SetDidProcessNetworkPrefabs(true); } From cf4e04ba573aad88417671a1424f3896cac96b6b Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 17 May 2021 18:52:40 -0700 Subject: [PATCH 15/21] Cleanup a few more headers --- .../Code/Source/Editor/MultiplayerEditorConnection.h | 3 ++- Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h index d559651080..d803a60744 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h @@ -12,6 +12,8 @@ #pragma once +#include + #include #include #include @@ -19,7 +21,6 @@ #include #include #include -#include namespace AzNetworking { diff --git a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp index ae91999a0c..3646dd52a4 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp @@ -10,9 +10,10 @@ * */ -#include -#include +#include +#include #include + #include #include From 920f85981de33e1ebf2bae5d548b3a6df41d1a6a Mon Sep 17 00:00:00 2001 From: puvvadar Date: Mon, 17 May 2021 18:55:00 -0700 Subject: [PATCH 16/21] Add another missed header file --- .../Code/Source/MultiplayerSystemComponent.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index a48363abe4..cac64db89b 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -12,6 +12,12 @@ #pragma once +#include +#include +#include +#include +#include + #include #include #include @@ -20,11 +26,6 @@ #include #include #include -#include -#include -#include -#include -#include namespace AzNetworking { From 19316e422b7c42cce7b3ac6d52c5fbb9e9dc68a2 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 18 May 2021 10:46:18 -0700 Subject: [PATCH 17/21] Check launch process exists before waiting for 15 seconds --- .../Code/Source/Editor/MultiplayerEditorSystemComponent.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 159f3b944b..a3d2551dd8 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -126,7 +126,6 @@ namespace Multiplayer { // If enabled but no process name is supplied, try this project's ServerLauncher serverProcess = AZ::Utils::GetProjectName() + ".ServerLauncher"; - serverPath = AZ::Utils::GetExecutableDirectory(); serverPath /= serverProcess + AZ_TRAIT_OS_EXECUTABLE_EXTENSION; } @@ -144,7 +143,10 @@ namespace Multiplayer // Launch the Server and give it a few seconds to boot up AzFramework::ProcessWatcher* outProcess = AzFramework::ProcessWatcher::LaunchProcess( processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE); - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(15000)); + if (outProcess) + { + AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(15000)); + } return outProcess; } From e03645f8161cfcb67400176e23cc3870c1aa872b Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 18 May 2021 10:48:39 -0700 Subject: [PATCH 18/21] Disable editorsv launch by default --- .../Code/Source/Editor/MultiplayerEditorSystemComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index a3d2551dd8..3c361dab2a 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -31,7 +31,7 @@ namespace Multiplayer { using namespace AzNetworking; - AZ_CVAR(bool, editorsv_enabled, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, + AZ_CVAR(bool, editorsv_enabled, false, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether Editor launching a local server to connect to is supported"); AZ_CVAR(bool, editorsv_launch, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether Editor should launch a server when the server address is localhost"); From e2ade654fb30d12bdb1564f04f8700e3652532d8 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 18 May 2021 13:52:28 -0700 Subject: [PATCH 19/21] Address misc feedback --- .../Components/LocalPredictionPlayerInputComponent.cpp | 4 ++-- .../Code/Source/Editor/MultiplayerEditorConnection.cpp | 6 ++---- .../Code/Source/Editor/MultiplayerEditorSystemComponent.cpp | 4 +--- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index 10a0d17e73..612601883c 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -23,7 +23,7 @@ namespace Multiplayer { AZ_CVAR(AZ::TimeMs, cl_InputRateMs, AZ::TimeMs{ 33 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Rate at which to sample and process client inputs"); AZ_CVAR(AZ::TimeMs, cl_MaxRewindHistoryMs, AZ::TimeMs{ 2000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of milliseconds to keep for server correction rewind and replay"); -#ifndef _RELEASE +#ifndef AZ_RELEASE_BUILD AZ_CVAR(float, cl_DebugHackTimeMultiplier, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Scalar value used to simulate clock hacking cheats for validating bank time system and anticheat"); #endif @@ -477,7 +477,7 @@ namespace Multiplayer const double inputRate = static_cast(static_cast(cl_InputRateMs)) / 1000.0; const double maxRewindHistory = static_cast(static_cast(cl_MaxRewindHistoryMs)) / 1000.0; -#ifndef _RELEASE +#ifndef AZ_RELEASE_BUILD m_moveAccumulator += deltaTime * cl_DebugHackTimeMultiplier; #else m_moveAccumulator += deltaTime; diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index f88a314ea5..f684e1f12f 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -71,11 +71,9 @@ namespace Multiplayer while (m_byteStream.GetCurPos() < m_byteStream.GetLength()) { AZ::Data::AssetId assetId; - AZ::Data::AssetLoadBehavior assetLoadBehavior; uint32_t hintSize; AZStd::string assetHint; m_byteStream.Read(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId)); - m_byteStream.Read(sizeof(AZ::Data::AssetLoadBehavior), reinterpret_cast(&assetLoadBehavior)); m_byteStream.Read(sizeof(uint32_t), reinterpret_cast(&hintSize)); assetHint.resize(hintSize); m_byteStream.Read(hintSize, assetHint.data()); @@ -83,7 +81,7 @@ namespace Multiplayer size_t assetSize = m_byteStream.GetCurPos(); AZ::Data::AssetData* assetDatum = AZ::Utils::LoadObjectFromStream(m_byteStream, nullptr); assetSize = m_byteStream.GetCurPos() - assetSize; - AZ::Data::Asset asset = AZ::Data::Asset(assetId, assetDatum, assetLoadBehavior); + AZ::Data::Asset asset = AZ::Data::Asset(assetId, assetDatum, AZ::Data::AssetLoadBehavior::NoLoad); asset.SetHint(assetHint); AZ::Data::AssetInfo assetInfo; @@ -104,7 +102,7 @@ namespace Multiplayer m_byteStream.Truncate(); // Load the level via the root spawnable that was registered - AZ::CVarFixedString loadLevelString = "LoadLevel Root.spawnable"; + const AZ::CVarFixedString loadLevelString = "LoadLevel Root.spawnable"; AZ::Interface::Get()->PerformCommand(loadLevelString.c_str()); // Setup the normal multiplayer connection diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 3c361dab2a..0a95f96ff7 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -169,15 +169,13 @@ namespace Multiplayer AZ::IO::ByteContainerStream byteStream(&buffer); // Serialize Asset information and AssetData into a potentially large buffer - for (auto asset : assetData) + for (auto& asset : assetData) { AZ::Data::AssetId assetId = asset.GetId(); - AZ::Data::AssetLoadBehavior assetLoadBehavior = asset.GetAutoLoadBehavior(); AZStd::string assetHint = asset.GetHint(); uint32_t hintSize = aznumeric_cast(assetHint.size()); byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast(&assetId)); - byteStream.Write(sizeof(AZ::Data::AssetLoadBehavior), reinterpret_cast(&assetLoadBehavior)); byteStream.Write(sizeof(uint32_t), reinterpret_cast(&hintSize)); byteStream.Write(assetHint.size(), assetHint.data()); AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType()); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index cac64db89b..1410a82a3c 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -129,7 +129,7 @@ namespace Multiplayer AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 }; HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId; -#if !defined(_RELEASE) +#if !defined(AZ_RELEASE_BUILD) MultiplayerEditorConnection m_editorConnectionListener; #endif }; From 55ecd8517d3b8769199b36edf000f69a356cf4fd Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 18 May 2021 14:57:27 -0700 Subject: [PATCH 20/21] Add assert to new Asset constructor to declare intent and safeguard ID overwrite --- Code/Framework/AzCore/AzCore/Asset/AssetCommon.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h b/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h index 3666eae2d4..11f4531124 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h +++ b/Code/Framework/AzCore/AzCore/Asset/AssetCommon.h @@ -796,6 +796,7 @@ namespace AZ , m_assetType(azrtti_typeid()) , m_loadBehavior(loadBehavior) { + AZ_Assert(!assetData->m_assetId.IsValid(), "Asset data already has an ID set."); assetData->m_assetId = id; SetData(assetData); } From 350e5a0cd24c634bc7cd78036300937569c56333 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 18 May 2021 15:22:22 -0700 Subject: [PATCH 21/21] Update to const auto& --- .../Code/Source/Editor/MultiplayerEditorSystemComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 0a95f96ff7..829fe7e495 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -169,7 +169,7 @@ namespace Multiplayer AZ::IO::ByteContainerStream byteStream(&buffer); // Serialize Asset information and AssetData into a potentially large buffer - for (auto& asset : assetData) + for (const auto& asset : assetData) { AZ::Data::AssetId assetId = asset.GetId(); AZStd::string assetHint = asset.GetHint();