Merge pull request #522 from aws-lumberyard-dev/mp_editor_pipeline
Integrating network prefab processing with Multiplayer.Editor to enable server launch on Ctrl+G via an Editor specific network connection
This commit is contained in:
@@ -307,6 +307,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());
|
||||
|
||||
@@ -787,6 +789,18 @@ namespace AZ
|
||||
SetData(assetData);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
template<class T>
|
||||
Asset<T>::Asset(const AssetId& id, AssetData* assetData, AssetLoadBehavior loadBehavior)
|
||||
: m_assetId(id)
|
||||
, m_assetType(azrtti_typeid<T>())
|
||||
, m_loadBehavior(loadBehavior)
|
||||
{
|
||||
AZ_Assert(!assetData->m_assetId.IsValid(), "Asset data already has an ID set.");
|
||||
assetData->m_assetId = id;
|
||||
SetData(assetData);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
template<class T>
|
||||
Asset<T>::Asset(const AssetId& id, const AZ::Data::AssetType& type, const AZStd::string& hint)
|
||||
|
||||
@@ -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;
|
||||
@@ -176,7 +176,7 @@ namespace AzNetworking
|
||||
if (::bind(aznumeric_cast<int32_t>(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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace AzNetworking
|
||||
if (::bind(static_cast<int32_t>(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;
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -12,6 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/IO/GenericStreams.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
@@ -46,6 +47,10 @@ 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<AZ::Data::Asset<AZ::Data::AssetData>>& 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;
|
||||
|
||||
|
||||
+5
@@ -314,6 +314,11 @@ namespace AzToolsFramework
|
||||
return *m_rootInstance;
|
||||
}
|
||||
|
||||
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData()
|
||||
{
|
||||
return m_playInEditorData.m_assets;
|
||||
}
|
||||
|
||||
void PrefabEditorEntityOwnershipService::OnEntityRemoved(AZ::EntityId entityId)
|
||||
{
|
||||
AzFramework::SliceEntityRequestBus::MultiHandler::BusDisconnect(entityId);
|
||||
|
||||
+2
@@ -195,6 +195,8 @@ namespace AzToolsFramework
|
||||
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) override;
|
||||
|
||||
Prefab::InstanceOptionalReference GetRootPrefabInstance() override;
|
||||
|
||||
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void OnEntityRemoved(AZ::EntityId entityId);
|
||||
|
||||
@@ -96,12 +96,12 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
PRIVATE
|
||||
Gem::Multiplayer.Tools.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
|
||||
@@ -113,7 +113,7 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
PUBLIC
|
||||
Include
|
||||
BUILD_DEPENDENCIES
|
||||
PUBLIC
|
||||
PRIVATE
|
||||
Legacy::CryCommon
|
||||
Legacy::Editor.Headers
|
||||
AZ::AzCore
|
||||
@@ -121,23 +121,7 @@ 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
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -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 <AzCore/RTTI/RTTI.h>
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
@@ -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 <AzCore/EBus/Event.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/RTTI/TypeSafeIntegral.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
#include <AzNetworking/Serialization/ISerializer.h>
|
||||
#include <AzNetworking/ConnectionLayer/ConnectionEnums.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
constexpr AZStd::string_view MPNetworkInterfaceName("MultiplayerNetworkInterface");
|
||||
constexpr AZStd::string_view MPEditorInterfaceName("MultiplayerEditorNetworkInterface");
|
||||
|
||||
constexpr AZStd::string_view LocalHost("127.0.0.1");
|
||||
constexpr uint16_t DefaultServerPort = 30090;
|
||||
constexpr uint16_t DefaultServerEditorPort = 30091;
|
||||
|
||||
}
|
||||
|
||||
@@ -59,4 +59,5 @@
|
||||
<Member Type="Multiplayer::PrefabEntityId" Name="prefabEntityId" Init="" />
|
||||
<Member Type="AzNetworking::PacketEncodingBuffer" Name="propertyUpdateData" Init="" SuppressFromInitializerList="true" />
|
||||
</Packet>
|
||||
|
||||
</PacketGroup>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<PacketGroup Name="MultiplayerEditorPackets" PacketStart="CorePackets::PacketType::MAX">
|
||||
<Include File="AzNetworking/AutoGen/CorePackets.AutoPackets.h" />
|
||||
<Include File="Multiplayer/MultiplayerTypes.h" />
|
||||
<Include File="Multiplayer/NetworkTime/INetworkTime.h" />
|
||||
|
||||
<Packet Name="EditorServerInit" Desc="A packet that initializes a local server launched from the editor">
|
||||
<Member Type="bool" Name="lastUpdate" Init="false"/>
|
||||
<Member Type="AzNetworking::TcpPacketEncodingBuffer" Name="assetData"/>
|
||||
</Packet>
|
||||
|
||||
<Packet Name="EditorServerReady" Desc="A response packet the local server should send when ready for traffic"/>
|
||||
</PacketGroup>
|
||||
@@ -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<double>(static_cast<AZ::TimeMs>(cl_InputRateMs)) / 1000.0;
|
||||
const double maxRewindHistory = static_cast<double>(static_cast<AZ::TimeMs>(cl_MaxRewindHistoryMs)) / 1000.0;
|
||||
|
||||
#ifndef _RELEASE
|
||||
#ifndef AZ_RELEASE_BUILD
|
||||
m_moveAccumulator += deltaTime * cl_DebugHackTimeMultiplier;
|
||||
#else
|
||||
m_moveAccumulator += deltaTime;
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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 <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/MultiplayerConstants.h>
|
||||
#include <Editor/MultiplayerEditorConnection.h>
|
||||
#include <Source/AutoGen/AutoComponentTypes.h>
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/Utils.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
#include <AzNetworking/Framework/INetworking.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
using namespace AzNetworking;
|
||||
|
||||
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<INetworking>::Get()->CreateNetworkInterface(
|
||||
AZ::Name(MPEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this);
|
||||
if (editorsv_isDedicated)
|
||||
{
|
||||
uint16_t editorServerPort = DefaultServerEditorPort;
|
||||
if (auto console = AZ::Interface<AZ::IConsole>::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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
m_byteStream.Write(TcpPacketEncodingBuffer::GetCapacity(), reinterpret_cast<void*>(packet.ModifyAssetData().GetBuffer()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is the last expected packet, flush it to the buffer
|
||||
m_byteStream.Write(packet.GetAssetData().GetSize(), reinterpret_cast<void*>(packet.ModifyAssetData().GetBuffer()));
|
||||
|
||||
// Read all assets out of the buffer
|
||||
m_byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN);
|
||||
AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> assetData;
|
||||
while (m_byteStream.GetCurPos() < m_byteStream.GetLength())
|
||||
{
|
||||
AZ::Data::AssetId assetId;
|
||||
uint32_t hintSize;
|
||||
AZStd::string assetHint;
|
||||
m_byteStream.Read(sizeof(AZ::Data::AssetId), reinterpret_cast<void*>(&assetId));
|
||||
m_byteStream.Read(sizeof(uint32_t), reinterpret_cast<void*>(&hintSize));
|
||||
assetHint.resize(hintSize);
|
||||
m_byteStream.Read(hintSize, assetHint.data());
|
||||
|
||||
size_t assetSize = m_byteStream.GetCurPos();
|
||||
AZ::Data::AssetData* assetDatum = AZ::Utils::LoadObjectFromStream<AZ::Data::AssetData>(m_byteStream, nullptr);
|
||||
assetSize = m_byteStream.GetCurPos() - assetSize;
|
||||
AZ::Data::Asset<AZ::Data::AssetData> asset = AZ::Data::Asset<AZ::Data::AssetData>(assetId, assetDatum, AZ::Data::AssetLoadBehavior::NoLoad);
|
||||
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;
|
||||
|
||||
// Register Asset to AssetManager
|
||||
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
|
||||
m_byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN);
|
||||
m_byteStream.Truncate();
|
||||
|
||||
// Load the level via the root spawnable that was registered
|
||||
const AZ::CVarFixedString loadLevelString = "LoadLevel Root.spawnable";
|
||||
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(loadLevelString.c_str());
|
||||
|
||||
// Setup the normal multiplayer connection
|
||||
AZ::Interface<IMultiplayer>::Get()->InitializeMultiplayer(MultiplayerAgentType::DedicatedServer);
|
||||
INetworkInterface* networkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName));
|
||||
|
||||
uint16_t serverPort = DefaultServerPort;
|
||||
if (auto console = AZ::Interface<AZ::IConsole>::Get(); console)
|
||||
{
|
||||
console->GetCvarValue("sv_port", serverPort);
|
||||
}
|
||||
networkInterface->Listen(serverPort);
|
||||
|
||||
AZLOG_INFO("Editor Server completed asset receive, responding to Editor...");
|
||||
return connection->SendReliablePacket(MultiplayerEditorPackets::EditorServerReady());
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (auto console = AZ::Interface<AZ::IConsole>::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<IMultiplayer>::Get()->InitializeMultiplayer(MultiplayerAgentType::Client);
|
||||
INetworkInterface* networkInterface =
|
||||
AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName));
|
||||
|
||||
const IpAddress ipAddress(remoteAddress.c_str(), remotePort, 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)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 <Source/AutoGen/MultiplayerEditor.AutoPacketDispatcher.h>
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzCore/IO/ByteContainerStream.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnectionListener.h>
|
||||
|
||||
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;
|
||||
AZStd::vector<uint8_t> m_buffer;
|
||||
AZ::IO::ByteContainerStream<AZStd::vector<uint8_t>> m_byteStream;
|
||||
};
|
||||
}
|
||||
@@ -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 <Source/Editor/MultiplayerEditorDispatcher.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
MultiplayerEditorDispatcher::MultiplayerEditorDispatcher()
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -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 <IEditor.h>
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
|
||||
|
||||
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:
|
||||
};
|
||||
}
|
||||
+6
-6
@@ -10,13 +10,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/Multiplayer_precompiled.h>
|
||||
#include <Source/MultiplayerGem.h>
|
||||
#include <Source/MultiplayerSystemComponent.h>
|
||||
#include <Source/MultiplayerEditorGem.h>
|
||||
#include <AzNetworking/Framework/NetworkingSystemComponent.h>
|
||||
#include <Multiplayer_precompiled.h>
|
||||
#include <MultiplayerGem.h>
|
||||
#include <MultiplayerSystemComponent.h>
|
||||
#include <Editor/MultiplayerEditorGem.h>
|
||||
#include <Editor/MultiplayerEditorSystemComponent.h>
|
||||
|
||||
#include <Source/Editor/MultiplayerEditorSystemComponent.h>
|
||||
#include <AzNetworking/Framework/NetworkingSystemComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -10,12 +10,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/Editor/MultiplayerEditorSystemComponent.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/IMultiplayerTools.h>
|
||||
#include <Multiplayer/MultiplayerConstants.h>
|
||||
|
||||
#include <MultiplayerSystemComponent.h>
|
||||
#include <Editor/MultiplayerEditorSystemComponent.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzNetworking/Framework/INetworking.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -23,8 +33,12 @@ namespace Multiplayer
|
||||
|
||||
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");
|
||||
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, 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)
|
||||
{
|
||||
@@ -57,12 +71,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,50 +93,6 @@ namespace Multiplayer
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case eNotify_OnBeginGameMode:
|
||||
{
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
|
||||
if (editorsv_enabled)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eNotify_OnQuit:
|
||||
AZ_Warning("Multiplayer Editor", m_editor != nullptr, "Multiplayer Editor received On Quit without an Editor pointer.");
|
||||
if (m_editor)
|
||||
@@ -130,25 +102,132 @@ 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;
|
||||
}
|
||||
INetworkInterface* editorNetworkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MPEditorInterfaceName));
|
||||
if (editorNetworkInterface)
|
||||
{
|
||||
editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByClient);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerEditorSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
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";
|
||||
serverPath = AZ::Utils::GetExecutableDirectory();
|
||||
serverPath /= serverProcess + AZ_TRAIT_OS_EXECUTABLE_EXTENSION;
|
||||
}
|
||||
else
|
||||
{
|
||||
serverPath = serverProcess;
|
||||
}
|
||||
|
||||
// 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
|
||||
AzFramework::ProcessWatcher* outProcess = AzFramework::ProcessWatcher::LaunchProcess(
|
||||
processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE);
|
||||
if (outProcess)
|
||||
{
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(15000));
|
||||
}
|
||||
|
||||
return outProcess;
|
||||
}
|
||||
|
||||
int MultiplayerEditorSystemComponent::GetTickOrder()
|
||||
void MultiplayerEditorSystemComponent::OnGameEntitiesStarted()
|
||||
{
|
||||
// Tick immediately after the network system component
|
||||
return AZ::TICK_PLACEMENT + 1;
|
||||
auto prefabEditorEntityOwnershipInterface = AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
|
||||
if (!prefabEditorEntityOwnershipInterface)
|
||||
{
|
||||
AZ_Error("MultiplayerEditor", prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface unavailable");
|
||||
}
|
||||
|
||||
// BeginGameMode and Prefab Processing have completed at this point
|
||||
IMultiplayerTools* mpTools = AZ::Interface<IMultiplayerTools>::Get();
|
||||
if (editorsv_enabled && mpTools != nullptr && mpTools->DidProcessNetworkPrefabs())
|
||||
{
|
||||
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData();
|
||||
|
||||
AZStd::vector<uint8_t> buffer;
|
||||
AZ::IO::ByteContainerStream byteStream(&buffer);
|
||||
|
||||
// Serialize Asset information and AssetData into a potentially large buffer
|
||||
for (const auto& asset : assetData)
|
||||
{
|
||||
AZ::Data::AssetId assetId = asset.GetId();
|
||||
AZStd::string assetHint = asset.GetHint();
|
||||
uint32_t hintSize = aznumeric_cast<uint32_t>(assetHint.size());
|
||||
|
||||
byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast<void*>(&assetId));
|
||||
byteStream.Write(sizeof(uint32_t), reinterpret_cast<void*>(&hintSize));
|
||||
byteStream.Write(assetHint.size(), assetHint.data());
|
||||
AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType());
|
||||
}
|
||||
|
||||
const AZ::CVarFixedString remoteAddress = editorsv_serveraddr;
|
||||
if (editorsv_launch && LocalHost == remoteAddress)
|
||||
{
|
||||
m_serverProcess = LaunchEditorServer();
|
||||
}
|
||||
|
||||
// Now that the server has launched, attempt to connect the NetworkInterface
|
||||
INetworkInterface* editorNetworkInterface = AZ::Interface<INetworking>::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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,15 +14,16 @@
|
||||
|
||||
#include <IEditor.h>
|
||||
|
||||
#include <Editor/MultiplayerEditorConnection.h>
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
|
||||
#include <AzFramework/Entity/GameEntityContextBus.h>
|
||||
#include <AzFramework/Process/ProcessWatcher.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
class INetworkInterface;
|
||||
@@ -33,7 +34,7 @@ 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
|
||||
{
|
||||
@@ -59,17 +60,19 @@ namespace Multiplayer
|
||||
void NotifyRegisterViews() override;
|
||||
//! @}
|
||||
|
||||
private:
|
||||
|
||||
//! AZ::TickBus::Handler overrides.
|
||||
private:
|
||||
//! EditorEvents::Handler overrides
|
||||
//! @{
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
int GetTickOrder() override;
|
||||
//! @}
|
||||
//!
|
||||
void OnEditorNotifyEvent(EEditorNotifyEvent event) override;
|
||||
//! @}
|
||||
|
||||
//! GameEntityContextEventBus::Handler overrides
|
||||
//! @{
|
||||
void OnGameEntitiesStarted() override;
|
||||
//! @}
|
||||
|
||||
IEditor* m_editor = nullptr;
|
||||
AzFramework::ProcessWatcher* m_serverProcess = nullptr;
|
||||
AzNetworking::ConnectionId m_editorConnId;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,22 +10,27 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/MultiplayerSystemComponent.h>
|
||||
#include <Source/AutoGen/AutoComponentTypes.h>
|
||||
#include <Source/ConnectionData/ClientToServerConnectionData.h>
|
||||
#include <Source/ConnectionData/ServerToClientConnectionData.h>
|
||||
#include <Source/ReplicationWindows/NullReplicationWindow.h>
|
||||
#include <Source/ReplicationWindows/ServerToClientReplicationWindow.h>
|
||||
#include <Source/EntityDomains/FullOwnershipEntityDomain.h>
|
||||
#include <Multiplayer/MultiplayerConstants.h>
|
||||
#include <Multiplayer/Components/MultiplayerComponent.h>
|
||||
#include <AzNetworking/Framework/INetworking.h>
|
||||
|
||||
#include <MultiplayerSystemComponent.h>
|
||||
#include <ConnectionData/ClientToServerConnectionData.h>
|
||||
#include <ConnectionData/ServerToClientConnectionData.h>
|
||||
#include <EntityDomains/FullOwnershipEntityDomain.h>
|
||||
#include <ReplicationWindows/NullReplicationWindow.h>
|
||||
#include <ReplicationWindows/ServerToClientReplicationWindow.h>
|
||||
#include <Source/AutoGen/AutoComponentTypes.h>
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/Utils.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
#include <AzNetworking/Framework/INetworking.h>
|
||||
|
||||
namespace AZ::ConsoleTypeHelpers
|
||||
{
|
||||
@@ -59,11 +64,8 @@ namespace Multiplayer
|
||||
{
|
||||
using namespace AzNetworking;
|
||||
|
||||
static const AZStd::string_view s_networkInterfaceName("MultiplayerNetworkInterface");
|
||||
static constexpr uint16_t DefaultServerPort = 30090;
|
||||
|
||||
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, 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");
|
||||
@@ -140,7 +142,7 @@ namespace Multiplayer
|
||||
void MultiplayerSystemComponent::Activate()
|
||||
{
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
m_networkInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(AZ::Name(s_networkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this);
|
||||
m_networkInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(AZ::Name(MPNetworkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this);
|
||||
m_consoleCommandHandler.Connect(AZ::Interface<AZ::IConsole>::Get()->GetConsoleCommandInvokedEvent());
|
||||
AZ::Interface<IMultiplayer>::Register(this);
|
||||
|
||||
@@ -664,7 +666,7 @@ namespace Multiplayer
|
||||
{
|
||||
Multiplayer::MultiplayerAgentType serverType = sv_isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer;
|
||||
AZ::Interface<IMultiplayer>::Get()->InitializeMultiplayer(serverType);
|
||||
INetworkInterface* networkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName));
|
||||
INetworkInterface* networkInterface = AZ::Interface<INetworking>::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");
|
||||
@@ -672,7 +674,7 @@ namespace Multiplayer
|
||||
void connect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
AZ::Interface<IMultiplayer>::Get()->InitializeMultiplayer(MultiplayerAgentType::Client);
|
||||
INetworkInterface* networkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName));
|
||||
INetworkInterface* networkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName));
|
||||
|
||||
if (arguments.size() < 1)
|
||||
{
|
||||
@@ -702,7 +704,7 @@ namespace Multiplayer
|
||||
void disconnect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
AZ::Interface<IMultiplayer>::Get()->InitializeMultiplayer(MultiplayerAgentType::Uninitialized);
|
||||
INetworkInterface* networkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(s_networkInterfaceName));
|
||||
INetworkInterface* networkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName));
|
||||
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); };
|
||||
networkInterface->GetConnectionSet().VisitConnections(visitor);
|
||||
}
|
||||
|
||||
@@ -12,17 +12,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Editor/MultiplayerEditorConnection.h>
|
||||
#include <NetworkTime/NetworkTime.h>
|
||||
#include <NetworkEntity/NetworkEntityManager.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPacketDispatcher.h>
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzCore/IO/ByteContainerStream.h>
|
||||
#include <AzCore/Threading/ThreadSafeDeque.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnectionListener.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Source/NetworkTime/NetworkTime.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityManager.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPacketDispatcher.h>
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
@@ -72,7 +75,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);
|
||||
|
||||
|
||||
//! IConnectionListener interface
|
||||
//! @{
|
||||
AzNetworking::ConnectResult ValidateConnect(const AzNetworking::IpAddress& remoteAddress, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override;
|
||||
@@ -109,6 +112,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<AZStd::string> m_cvarCommands;
|
||||
|
||||
@@ -124,5 +128,9 @@ namespace Multiplayer
|
||||
|
||||
AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 };
|
||||
HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId;
|
||||
|
||||
#if !defined(AZ_RELEASE_BUILD)
|
||||
MultiplayerEditorConnection m_editorConnectionListener;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,40 +10,40 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/Multiplayer_precompiled.h>
|
||||
#include <Source/MultiplayerToolsModule.h>
|
||||
#include <Multiplayer_precompiled.h>
|
||||
#include <MultiplayerToolsModule.h>
|
||||
#include <Pipeline/NetworkPrefabProcessor.h>
|
||||
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <Prefab/Instance/InstanceSerializer.h>
|
||||
|
||||
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);
|
||||
}
|
||||
void MultiplayerToolsSystemComponent::Activate()
|
||||
{
|
||||
AZ::Interface<IMultiplayerTools>::Register(this);
|
||||
}
|
||||
|
||||
MultiplayerToolsSystemComponent() = default;
|
||||
~MultiplayerToolsSystemComponent() override = default;
|
||||
void MultiplayerToolsSystemComponent::Deactivate()
|
||||
{
|
||||
AZ::Interface<IMultiplayerTools>::Unregister(this);
|
||||
}
|
||||
|
||||
/// AZ::Component overrides.
|
||||
void Activate() override
|
||||
{
|
||||
bool MultiplayerToolsSystemComponent::DidProcessNetworkPrefabs()
|
||||
{
|
||||
return m_didProcessNetPrefabs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Deactivate() override
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
void MultiplayerToolsSystemComponent::SetDidProcessNetworkPrefabs(bool didProcessNetPrefabs)
|
||||
{
|
||||
m_didProcessNetPrefabs = didProcessNetPrefabs;
|
||||
}
|
||||
|
||||
MultiplayerToolsModule::MultiplayerToolsModule()
|
||||
: AZ::Module()
|
||||
|
||||
@@ -12,10 +12,36 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Module/Module.h>
|
||||
#include <Multiplayer/IMultiplayerTools.h>
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
@@ -10,7 +10,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Source/Pipeline/NetworkPrefabProcessor.h>
|
||||
#include <Multiplayer/IMultiplayerTools.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Pipeline/NetBindMarkerComponent.h>
|
||||
#include <Pipeline/NetworkPrefabProcessor.h>
|
||||
#include <Pipeline/NetworkSpawnableHolderComponent.h>
|
||||
|
||||
#include <AzCore/Serialization/Utils.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
@@ -18,9 +22,6 @@
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <Prefab/Spawnable/SpawnableUtils.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Source/Pipeline/NetBindMarkerComponent.h>
|
||||
#include <Source/Pipeline/NetworkSpawnableHolderComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -29,9 +30,20 @@ namespace Multiplayer
|
||||
|
||||
void NetworkPrefabProcessor::Process(PrefabProcessorContext& context)
|
||||
{
|
||||
IMultiplayerTools* mpTools = AZ::Interface<IMultiplayerTools>::Get();
|
||||
if (mpTools)
|
||||
{
|
||||
mpTools->SetDidProcessNetworkPrefabs(false);
|
||||
}
|
||||
|
||||
context.ListPrefabs([&context](AZStd::string_view prefabName, PrefabDom& prefab) {
|
||||
ProcessPrefab(context, prefabName, prefab);
|
||||
});
|
||||
|
||||
if (mpTools && !context.GetProcessedObjects().empty())
|
||||
{
|
||||
mpTools->SetDidProcessNetworkPrefabs(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkPrefabProcessor::Reflect(AZ::ReflectContext* context)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <AzCore/Name/NameDictionary.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzFramework/Spawnable/SpawnableSystemComponent.h>
|
||||
#include <AzNetworking/Framework/NetworkingSystemComponent.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <MultiplayerSystemComponent.h>
|
||||
#include <IMultiplayerConnectionMock.h>
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -1,15 +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.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Source/Editor/MultiplayerEditorDispatcher.cpp
|
||||
Source/Editor/MultiplayerEditorDispatcher.h
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
set(FILES
|
||||
Include/Multiplayer/IMultiplayer.h
|
||||
Include/Multiplayer/IMultiplayerTools.h
|
||||
Include/Multiplayer/MultiplayerConstants.h
|
||||
Include/Multiplayer/MultiplayerStats.h
|
||||
Include/Multiplayer/MultiplayerTypes.h
|
||||
Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h
|
||||
@@ -46,6 +48,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/MultiplayerComponent.cpp
|
||||
@@ -59,6 +62,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
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Include/Multiplayer/IMultiplayerTools.h
|
||||
Source/Multiplayer_precompiled.cpp
|
||||
Source/Multiplayer_precompiled.h
|
||||
Source/Pipeline/NetworkPrefabProcessor.cpp
|
||||
|
||||
Reference in New Issue
Block a user