merging latest development
Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
@@ -4,13 +4,15 @@
|
||||
<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">
|
||||
|
||||
<Packet Name="EditorServerReadyForLevelData" Desc="A packet the editor-server will send on startup once it's ready to receive all the current level data from the Editor."/>
|
||||
|
||||
<Packet Name="EditorServerLevelData" Desc="A packet that initializes the editor-server with level data from the editor. The packet includes data required for loading the current level on the server (entities and asset data).">
|
||||
<Member Type="bool" Name="lastUpdate" Init="false"/>
|
||||
|
||||
<!--16379 is 16384 (max TCP packet size) - 1 byte (bool lastUpdate) - 4 bytes (serialization overhead for ByteBuffer) -->
|
||||
<Member Type="AzNetworking::ByteBuffer<16379>" Name="assetData"/>
|
||||
</Packet>
|
||||
|
||||
<Packet Name="EditorServerReady" Desc="A response packet the local server should send when ready for traffic"/>
|
||||
<Packet Name="EditorServerReady" Desc="A response packet the editor-server should send after getting the editor's level data when it's ready to begin the actual game-mode network simulation."/>
|
||||
</PacketGroup>
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace Multiplayer
|
||||
void NetworkHierarchyChildComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent"));
|
||||
provided.push_back(AZ_CRC_CE("MultiplayerInputDriver"));
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace Multiplayer
|
||||
void NetworkHierarchyRootComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent"));
|
||||
provided.push_back(AZ_CRC_CE("MultiplayerInputDriver"));
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/INetworkSpawnableLibrary.h>
|
||||
#include <Multiplayer/MultiplayerConstants.h>
|
||||
#include <Multiplayer/MultiplayerEditorServerBus.h>
|
||||
#include <Editor/MultiplayerEditorConnection.h>
|
||||
#include <Source/AutoGen/AutoComponentTypes.h>
|
||||
|
||||
@@ -17,7 +18,6 @@
|
||||
#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>
|
||||
|
||||
@@ -26,6 +26,7 @@ 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.");
|
||||
AZ_CVAR(uint16_t, editorsv_port, DefaultServerEditorPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that the multiplayer editor gem will bind to for traffic.");
|
||||
|
||||
MultiplayerEditorConnection::MultiplayerEditorConnection()
|
||||
: m_byteStream(&m_buffer)
|
||||
@@ -33,23 +34,42 @@ namespace Multiplayer
|
||||
m_networkEditorInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(
|
||||
AZ::Name(MpEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this);
|
||||
m_networkEditorInterface->SetTimeoutMs(AZ::TimeMs{ 0 }); // Disable timeouts on this network interface
|
||||
if (editorsv_isDedicated)
|
||||
ActivateDedicatedEditorServer();
|
||||
}
|
||||
|
||||
void MultiplayerEditorConnection::ActivateDedicatedEditorServer() const
|
||||
{
|
||||
if (m_isActivated || !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);
|
||||
return;
|
||||
}
|
||||
m_isActivated = true;
|
||||
|
||||
AZ_Assert(m_networkEditorInterface, "MP Editor Network Interface was unregistered before Editor Server could start listening.")
|
||||
|
||||
// Check if there's already an Editor out there waiting to connect
|
||||
const ConnectionId editorServerToEditorConnectionId =
|
||||
m_networkEditorInterface->Connect(IpAddress(LocalHost.data(), editorsv_port, ProtocolType::Tcp));
|
||||
|
||||
// If there wasn't an Editor waiting for this server to start, then assume this is an editor-server launched by hand... listen
|
||||
// and wait for the editor to request a connection
|
||||
if (editorServerToEditorConnectionId == InvalidConnectionId)
|
||||
{
|
||||
m_networkEditorInterface->Listen(editorsv_port);
|
||||
AZ_Printf("MultiplayerEditorConnection", "Editor-server activation did not find an editor in game-mode willing to connect; we'll instead wait and listen for an editor trying to connect to us.")
|
||||
}
|
||||
else
|
||||
{
|
||||
m_networkEditorInterface->SendReliablePacket(editorServerToEditorConnectionId, MultiplayerEditorPackets::EditorServerReadyForLevelData());
|
||||
AZ_Printf("MultiplayerEditorConnection", "Editor-server activation has found and connected to the editor.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool MultiplayerEditorConnection::HandleRequest
|
||||
(
|
||||
[[maybe_unused]] AzNetworking::IConnection* connection,
|
||||
[[maybe_unused]] const IPacketHeader& packetHeader,
|
||||
[[maybe_unused]] MultiplayerEditorPackets::EditorServerInit& packet
|
||||
[[maybe_unused]] MultiplayerEditorPackets::EditorServerLevelData& packet
|
||||
)
|
||||
{
|
||||
// Editor Server Init is intended for non-release targets
|
||||
@@ -76,7 +96,7 @@ namespace Multiplayer
|
||||
AZ::Data::AssetData* assetDatum = AZ::Utils::LoadObjectFromStream<AZ::Data::AssetData>(m_byteStream, nullptr);
|
||||
if (!assetDatum)
|
||||
{
|
||||
AZLOG_ERROR("EditorServerInit packet contains no asset data. Asset: %s", assetHint.c_str());
|
||||
AZLOG_ERROR("EditorServerLevelData packet contains no asset data. Asset: %s", assetHint.c_str())
|
||||
return false;
|
||||
}
|
||||
assetSize = m_byteStream.GetCurPos() - assetSize;
|
||||
@@ -105,26 +125,38 @@ namespace Multiplayer
|
||||
|
||||
// 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());
|
||||
const auto console = AZ::Interface<AZ::IConsole>::Get();
|
||||
console->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)
|
||||
uint16_t sv_port = DefaultServerPort;
|
||||
if (console->GetCvarValue("sv_port", sv_port) != AZ::GetValueResult::Success)
|
||||
{
|
||||
console->GetCvarValue("sv_port", serverPort);
|
||||
AZ_Assert(false,
|
||||
"MultiplayerEditorConnection::HandleRequest for EditorServerLevelData failed! Could not find the sv_port cvar; we won't be able to listen on the correct port for incoming network messages! Please update this code to use a valid cvar!")
|
||||
}
|
||||
networkInterface->Listen(serverPort);
|
||||
|
||||
networkInterface->Listen(sv_port);
|
||||
|
||||
AZLOG_INFO("Editor Server completed asset receive, responding to Editor...");
|
||||
AZLOG_INFO("Editor Server completed receiving the editor's level assets, responding to Editor...");
|
||||
return connection->SendReliablePacket(MultiplayerEditorPackets::EditorServerReady());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MultiplayerEditorConnection::HandleRequest(
|
||||
[[maybe_unused]] AzNetworking::IConnection* connection,
|
||||
[[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader,
|
||||
[[maybe_unused]] MultiplayerEditorPackets::EditorServerReadyForLevelData& packet)
|
||||
{
|
||||
MultiplayerEditorServerRequestBus::Broadcast(&MultiplayerEditorServerRequestBus::Events::SendEditorServerLevelDataPacket, connection);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MultiplayerEditorConnection::HandleRequest
|
||||
(
|
||||
[[maybe_unused]] AzNetworking::IConnection* connection,
|
||||
@@ -132,22 +164,34 @@ namespace Multiplayer
|
||||
[[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);
|
||||
// Receiving this packet means Editor sync is done, disconnect
|
||||
connection->Disconnect(AzNetworking::DisconnectReason::TerminatedByClient, AzNetworking::TerminationEndpoint::Local);
|
||||
const auto console = AZ::Interface<AZ::IConsole>::Get();
|
||||
AZ::CVarFixedString editorsv_serveraddr = AZ::CVarFixedString(LocalHost);
|
||||
uint16_t sv_port = DefaultServerEditorPort;
|
||||
|
||||
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("sv_port", remotePort) != AZ::GetValueResult::ConsoleVarNotFound)
|
||||
{
|
||||
// Connect the Editor to the editor server for Multiplayer simulation
|
||||
AZ::Interface<IMultiplayer>::Get()->Connect(remoteAddress.c_str(), remotePort);
|
||||
}
|
||||
}
|
||||
if (console->GetCvarValue("sv_port", sv_port) != AZ::GetValueResult::Success)
|
||||
{
|
||||
AZ_Assert(false,
|
||||
"MultiplayerEditorConnection::HandleRequest for EditorServerReady failed! Could not find the sv_port cvar; we may not be able to "
|
||||
"connect to the correct port for incoming network messages! Please update this code to use a valid cvar!")
|
||||
}
|
||||
|
||||
if (console->GetCvarValue("editorsv_serveraddr", editorsv_serveraddr) != AZ::GetValueResult::Success)
|
||||
{
|
||||
AZ_Assert(false,
|
||||
"MultiplayerEditorConnection::HandleRequest for EditorServerReady failed! Could not find the editorsv_serveraddr cvar; we may not be able to "
|
||||
"connect to the correct port for incoming network messages! Please update this code to use a valid cvar!")
|
||||
}
|
||||
|
||||
// Connect the Editor to the editor server for Multiplayer simulation
|
||||
if (AZ::Interface<IMultiplayer>::Get()->Connect(editorsv_serveraddr.c_str(), sv_port))
|
||||
{
|
||||
AZ_Printf("MultiplayerEditorConnection", "Editor-server ready. Editor has successfully connected to the editor-server's network simulation.")
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("MultiplayerEditorConnection", false, "MultiplayerEditorConnection::HandleRequest for EditorServerReady failed! Connecting to the editor-server's network simulation failed.")
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -171,26 +215,5 @@ namespace Multiplayer
|
||||
{
|
||||
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)
|
||||
{
|
||||
bool editorLaunch = false;
|
||||
if (auto console = AZ::Interface<AZ::IConsole>::Get(); console)
|
||||
{
|
||||
console->GetCvarValue("editorsv_launch", editorLaunch);
|
||||
}
|
||||
|
||||
if (editorsv_isDedicated && editorLaunch && m_networkEditorInterface->GetConnectionSet().GetConnectionCount() == 1)
|
||||
{
|
||||
if (m_networkEditorInterface->GetPort() != 0)
|
||||
{
|
||||
m_networkEditorInterface->StopListening();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
#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>
|
||||
@@ -33,7 +31,8 @@ namespace Multiplayer
|
||||
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::EditorServerReadyForLevelData& packet);
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerLevelData& packet);
|
||||
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerEditorPackets::EditorServerReady& packet);
|
||||
|
||||
//! IConnectionListener interface
|
||||
@@ -41,14 +40,16 @@ namespace Multiplayer
|
||||
AzNetworking::ConnectResult ValidateConnect(const AzNetworking::IpAddress& remoteAddress, const AzNetworking::IPacketHeader& packetHeader, AzNetworking::ISerializer& serializer) override;
|
||||
void OnConnect(AzNetworking::IConnection* connection) override;
|
||||
AzNetworking::PacketDispatchResult 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;
|
||||
void OnPacketLost([[maybe_unused]]AzNetworking::IConnection* connection, [[maybe_unused]]AzNetworking::PacketId packetId) override {}
|
||||
void OnDisconnect([[maybe_unused]]AzNetworking::IConnection* connection, [[maybe_unused]]AzNetworking::DisconnectReason reason, [[maybe_unused]]AzNetworking::TerminationEndpoint endpoint) override {}
|
||||
//! @}
|
||||
|
||||
private:
|
||||
void ActivateDedicatedEditorServer() const;
|
||||
|
||||
AzNetworking::INetworkInterface* m_networkEditorInterface = nullptr;
|
||||
AZStd::vector<uint8_t> m_buffer;
|
||||
AZ::IO::ByteContainerStream<AZStd::vector<uint8_t>> m_byteStream;
|
||||
mutable bool m_isActivated = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Multiplayer
|
||||
m_descriptors.end(),
|
||||
{
|
||||
MultiplayerEditorSystemComponent::CreateDescriptor(),
|
||||
PythonEditorFuncs::CreateDescriptor()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <Multiplayer/MultiplayerConstants.h>
|
||||
|
||||
#include <MultiplayerSystemComponent.h>
|
||||
#include <PythonEditorEventsBus.h>
|
||||
#include <Editor/MultiplayerEditorSystemComponent.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
|
||||
@@ -23,6 +24,7 @@
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzNetworking/Framework/INetworking.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <Atom/RPI.Public/RPISystemInterface.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -35,8 +37,47 @@ namespace Multiplayer
|
||||
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");
|
||||
AZ_CVAR(AZ::CVarFixedString, editorsv_rhi_override, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"Override the default rendering hardware interface (rhi) when launching the Editor server. For example, you may be running an Editor using 'dx12', but want to launch a headless server using 'null'. If empty the server will launch using the same rhi as the Editor.");
|
||||
AZ_CVAR_EXTERNED(uint16_t, editorsv_port);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void PyEnterGameMode()
|
||||
{
|
||||
editorsv_enabled = true;
|
||||
editorsv_launch = true;
|
||||
AzToolsFramework::EditorLayerPythonRequestBus::Broadcast(&AzToolsFramework::EditorLayerPythonRequestBus::Events::EnterGameMode);
|
||||
}
|
||||
|
||||
bool PyIsInGameMode()
|
||||
{
|
||||
// If the network entity manager is tracking at least 1 entity then the editor has connected and the autonomous player exists and is being replicated.
|
||||
if (const INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get())
|
||||
{
|
||||
return networkEntityManager->GetEntityCount() > 0;
|
||||
}
|
||||
|
||||
AZ_Warning("MultiplayerEditorSystemComponent", false, "PyIsInGameMode returning false; NetworkEntityManager has not been created yet.")
|
||||
return false;
|
||||
}
|
||||
|
||||
void PythonEditorFuncs::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
// This will create static python methods in the 'azlmbr.multiplayer' module
|
||||
// Note: The methods will be prefixed with the class name, PythonEditorFuncs
|
||||
// Example Hydra Python: azlmbr.multiplayer.PythonEditorFuncs_enter_game_mode()
|
||||
behaviorContext->Class<PythonEditorFuncs>()
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Attribute(AZ::Script::Attributes::Module, "multiplayer")
|
||||
->Method("enter_game_mode", PyEnterGameMode, nullptr, "Enters the editor game mode and launches/connects to the server launcher.")
|
||||
->Method("is_in_game_mode", PyIsInGameMode, nullptr, "Queries if it's in the game mode and the server has finished connecting and the default network player has spawned.")
|
||||
;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerEditorSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
@@ -44,6 +85,18 @@ namespace Multiplayer
|
||||
serializeContext->Class<MultiplayerEditorSystemComponent, AZ::Component>()
|
||||
->Version(1);
|
||||
}
|
||||
|
||||
// Reflect Python Editor Functions
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
// This will add the MultiplayerPythonEditorBus into the 'azlmbr.multiplayer' module
|
||||
behaviorContext->EBus<MultiplayerEditorLayerPythonRequestBus>("MultiplayerPythonEditorBus")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Attribute(AZ::Script::Attributes::Module, "multiplayer")
|
||||
->Event("EnterGameMode", &MultiplayerEditorLayerPythonRequestBus::Events::EnterGameMode)
|
||||
->Event("IsInGameMode", &MultiplayerEditorLayerPythonRequestBus::Events::IsInGameMode)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerEditorSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
@@ -71,6 +124,7 @@ namespace Multiplayer
|
||||
{
|
||||
AzFramework::GameEntityContextEventBus::Handler::BusConnect();
|
||||
AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
|
||||
MultiplayerEditorServerRequestBus::Handler::BusConnect();
|
||||
AZ::Interface<IMultiplayer>::Get()->AddServerAcceptanceReceivedHandler(m_serverAcceptanceReceivedHandler);
|
||||
}
|
||||
|
||||
@@ -78,6 +132,7 @@ namespace Multiplayer
|
||||
{
|
||||
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
|
||||
AzFramework::GameEntityContextEventBus::Handler::BusDisconnect();
|
||||
MultiplayerEditorServerRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void MultiplayerEditorSystemComponent::NotifyRegisterViews()
|
||||
@@ -107,8 +162,8 @@ namespace Multiplayer
|
||||
m_serverProcess->TerminateProcess(0);
|
||||
m_serverProcess = nullptr;
|
||||
}
|
||||
INetworkInterface* editorNetworkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName));
|
||||
if (editorNetworkInterface)
|
||||
|
||||
if (INetworkInterface* editorNetworkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)))
|
||||
{
|
||||
editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByClient);
|
||||
}
|
||||
@@ -145,11 +200,21 @@ namespace Multiplayer
|
||||
|
||||
// Start the configured server if it's available
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
|
||||
// Open the server launcher using the same rhi as the editor (or launch with the override rhi)
|
||||
AZ::Name server_rhi = AZ::RPI::RPISystemInterface::Get()->GetRenderApiName();
|
||||
if (!static_cast<AZ::CVarFixedString>(editorsv_rhi_override).empty())
|
||||
{
|
||||
server_rhi = static_cast<AZ::CVarFixedString>(editorsv_rhi_override);
|
||||
}
|
||||
|
||||
processLaunchInfo.m_commandlineParameters = AZStd::string::format(
|
||||
R"("%s" --project-path "%s" --editorsv_isDedicated true --sv_defaultPlayerSpawnAsset "%s")",
|
||||
R"("%s" --project-path "%s" --editorsv_isDedicated true --sv_defaultPlayerSpawnAsset "%s" --rhi "%s")",
|
||||
serverPath.c_str(),
|
||||
AZ::Utils::GetProjectPath().c_str(),
|
||||
static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str());
|
||||
static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str(),
|
||||
server_rhi.GetCStr()
|
||||
);
|
||||
processLaunchInfo.m_showWindow = true;
|
||||
processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_NORMAL;
|
||||
|
||||
@@ -157,6 +222,10 @@ namespace Multiplayer
|
||||
AzFramework::ProcessWatcher* outProcess = AzFramework::ProcessWatcher::LaunchProcess(
|
||||
processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE);
|
||||
|
||||
AZ_Error(
|
||||
"MultiplayerEditor", processLaunchInfo.m_launchResult != AzFramework::ProcessLauncher::ProcessLaunchResult::PLR_MissingFile,
|
||||
"LaunchEditorServer failed! The ServerLauncher binary is missing! (%s) Please build server launcher.", serverPath.c_str())
|
||||
|
||||
return outProcess;
|
||||
}
|
||||
|
||||
@@ -191,53 +260,49 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
const AZ::CVarFixedString remoteAddress = editorsv_serveraddr;
|
||||
if (editorsv_launch && LocalHost == remoteAddress)
|
||||
if (editorsv_launch)
|
||||
{
|
||||
if (LocalHost != remoteAddress)
|
||||
{
|
||||
AZ_Warning(
|
||||
"MultiplayerEditor", false,
|
||||
"Launching EditorServer skipped because incompatible cvars. editorsv_launch=true, meaning you want to launch an editor-server on this machine, but the editorsv_serveraddr is %s instead of the local address (127.0.0.1). "
|
||||
"Please either set editorsv_launch=false and keep the remote editor-server, or set editorsv_launch=true and editorsv_serveraddr=127.0.0.1.",
|
||||
remoteAddress.c_str())
|
||||
return;
|
||||
}
|
||||
|
||||
// Begin listening for MPEditor packets before we launch the editor-server.
|
||||
// The editor-server will send us (the editor) an "EditorServerReadyForLevelData" packet to let us know it's ready to receive data.
|
||||
INetworkInterface* editorNetworkInterface =
|
||||
AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName));
|
||||
AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect.");
|
||||
editorNetworkInterface->Listen(editorsv_port);
|
||||
|
||||
// Launch the editor-server
|
||||
m_serverProcess = LaunchEditorServer();
|
||||
}
|
||||
|
||||
// Spawnable library needs to be rebuilt since now we have newly registered in-memory spawnable assets
|
||||
AZ::Interface<INetworkSpawnableLibrary>::Get()->BuildSpawnablesList();
|
||||
|
||||
// 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)
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
// Editorsv_launch=false, so we're expecting an editor-server already exists.
|
||||
// Connect to the editor-server and then send the EditorServerLevelData packet.
|
||||
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));
|
||||
|
||||
// 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;
|
||||
auto& outBuffer = packet.ModifyAssetData();
|
||||
|
||||
// Size the packet's buffer appropriately
|
||||
size_t readSize = outBuffer.GetCapacity();
|
||||
size_t byteStreamSize = byteStream.GetLength() - byteStream.GetCurPos();
|
||||
if (byteStreamSize < readSize)
|
||||
if (m_editorConnId == AzNetworking::InvalidConnectionId)
|
||||
{
|
||||
readSize = byteStreamSize;
|
||||
AZ_Warning(
|
||||
"MultiplayerEditor", false,
|
||||
"Editor multiplayer game-mode failed! Could not connect to an editor-server. editorsv_launch is false so we're assuming you're running your own editor-server at editorsv_serveraddr(%s) on editorsv_port(%i). "
|
||||
"Either set editorsv_launch=true so the editor launches an editor-server for you, or launch your own editor-server by hand before entering game-mode. Remember editor-servers must use editorsv_isDedicated=true.",
|
||||
remoteAddress.c_str(),
|
||||
static_cast<uint16_t>(editorsv_port))
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
SendEditorServerLevelDataPacket(editorNetworkInterface->GetConnectionSet().GetConnection(m_editorConnId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,4 +318,75 @@ namespace Multiplayer
|
||||
// but since we're in Editor, we're already in the level.
|
||||
AZ::Interface<IMultiplayer>::Get()->SendReadyForEntityUpdates(true);
|
||||
}
|
||||
|
||||
void MultiplayerEditorSystemComponent::SendEditorServerLevelDataPacket(AzNetworking::IConnection* connection)
|
||||
{
|
||||
const auto prefabEditorEntityOwnershipInterface = AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
|
||||
if (!prefabEditorEntityOwnershipInterface)
|
||||
{
|
||||
AZ_Error("MultiplayerEditor", prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface unavailable")
|
||||
return;
|
||||
}
|
||||
|
||||
AZ_Printf("MultiplayerEditor", "Editor is sending the editor-server the level data packet.")
|
||||
|
||||
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();
|
||||
auto 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());
|
||||
}
|
||||
|
||||
// Spawnable library needs to be rebuilt since now we have newly registered in-memory spawnable assets
|
||||
AZ::Interface<INetworkSpawnableLibrary>::Get()->BuildSpawnablesList();
|
||||
|
||||
// Read the buffer into EditorServerLevelData packets until we've flushed the whole thing
|
||||
byteStream.Seek(0, AZ::IO::GenericStream::SeekMode::ST_SEEK_BEGIN);
|
||||
|
||||
while (byteStream.GetCurPos() < byteStream.GetLength())
|
||||
{
|
||||
MultiplayerEditorPackets::EditorServerLevelData editorServerLevelDataPacket;
|
||||
auto& outBuffer = editorServerLevelDataPacket.ModifyAssetData();
|
||||
|
||||
// Size the packet's buffer appropriately
|
||||
size_t readSize = outBuffer.GetCapacity();
|
||||
const 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())
|
||||
{
|
||||
editorServerLevelDataPacket.SetLastUpdate(true);
|
||||
}
|
||||
|
||||
connection->SendReliablePacket(editorServerLevelDataPacket);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerEditorSystemComponent::EnterGameMode()
|
||||
{
|
||||
PyEnterGameMode();
|
||||
}
|
||||
|
||||
bool MultiplayerEditorSystemComponent::IsInGameMode()
|
||||
{
|
||||
return PyIsInGameMode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
|
||||
#include <Multiplayer/MultiplayerEditorServerBus.h>
|
||||
#include <Multiplayer/Editor/MultiplayerPythonEditorEventsBus.h>
|
||||
#include <IEditor.h>
|
||||
|
||||
#include <Editor/MultiplayerEditorConnection.h>
|
||||
@@ -29,12 +30,28 @@ namespace AzNetworking
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
//! A component to reflect scriptable commands for the Editor
|
||||
class PythonEditorFuncs : public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(PythonEditorFuncs, "{22AEEA59-94E6-4033-B67D-7C8FBB84DF0D}")
|
||||
|
||||
SANDBOX_API static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// AZ::Component ...
|
||||
void Activate() override {}
|
||||
void Deactivate() override {}
|
||||
};
|
||||
|
||||
|
||||
//! Multiplayer system component wraps the bridging logic between the game and transport layer.
|
||||
class MultiplayerEditorSystemComponent final
|
||||
: public AZ::Component
|
||||
, public MultiplayerEditorLayerPythonRequestBus::Handler
|
||||
, private AzFramework::GameEntityContextEventBus::Handler
|
||||
, private AzToolsFramework::EditorEvents::Bus::Handler
|
||||
, private IEditorNotifyListener
|
||||
, private MultiplayerEditorServerRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(MultiplayerEditorSystemComponent, "{9F335CC0-5574-4AD3-A2D8-2FAEF356946C}");
|
||||
@@ -61,6 +78,12 @@ namespace Multiplayer
|
||||
void NotifyRegisterViews() override;
|
||||
//! @}
|
||||
|
||||
//! MultiplayerEditorLayerPythonRequestBus::Handler overrides.
|
||||
//! @{
|
||||
void EnterGameMode() override;
|
||||
bool IsInGameMode() override;
|
||||
//! @}
|
||||
|
||||
private:
|
||||
//! EditorEvents::Handler overrides
|
||||
//! @{
|
||||
@@ -73,6 +96,11 @@ namespace Multiplayer
|
||||
void OnGameEntitiesReset() override;
|
||||
//! @}
|
||||
|
||||
//! MultiplayerEditorServerRequestBus::Handler
|
||||
//! @{
|
||||
void SendEditorServerLevelDataPacket(AzNetworking::IConnection* connection) override;
|
||||
//! @}
|
||||
|
||||
IEditor* m_editor = nullptr;
|
||||
AzFramework::ProcessWatcher* m_serverProcess = nullptr;
|
||||
AzNetworking::ConnectionId m_editorConnId;
|
||||
|
||||
@@ -720,11 +720,6 @@ namespace Multiplayer
|
||||
|
||||
void MultiplayerSystemComponent::OnConnect(AzNetworking::IConnection* connection)
|
||||
{
|
||||
MultiplayerAgentDatum datum;
|
||||
datum.m_id = connection->GetConnectionId();
|
||||
datum.m_isInvited = false;
|
||||
datum.m_agentType = MultiplayerAgentType::Client;
|
||||
|
||||
AZStd::string providerTicket;
|
||||
if (connection->GetConnectionRole() == ConnectionRole::Connector)
|
||||
{
|
||||
@@ -738,7 +733,12 @@ namespace Multiplayer
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG_INFO("New incoming connection from remote address: %s", connection->GetRemoteAddress().GetString().c_str());
|
||||
AZLOG_INFO("New incoming connection from remote address: %s", connection->GetRemoteAddress().GetString().c_str())
|
||||
|
||||
MultiplayerAgentDatum datum;
|
||||
datum.m_id = connection->GetConnectionId();
|
||||
datum.m_isInvited = false;
|
||||
datum.m_agentType = MultiplayerAgentType::Client;
|
||||
m_connectionAcquiredEvent.Signal(datum);
|
||||
}
|
||||
|
||||
@@ -772,15 +772,14 @@ namespace Multiplayer
|
||||
AZLOG_INFO("%s from remote address %s due to %s", endpointString, connection->GetRemoteAddress().GetString().c_str(), reasonString.c_str());
|
||||
|
||||
// The client is disconnecting
|
||||
if (GetAgentType() == MultiplayerAgentType::Client)
|
||||
if (m_agentType == MultiplayerAgentType::Client)
|
||||
{
|
||||
AZ_Assert(connection->GetConnectionRole() == ConnectionRole::Connector, "Client connection role should only ever be Connector");
|
||||
m_clientDisconnectedEvent.Signal();
|
||||
}
|
||||
|
||||
// Signal to session management that a user has left the server
|
||||
if (m_agentType == MultiplayerAgentType::DedicatedServer || m_agentType == MultiplayerAgentType::ClientServer)
|
||||
else if (m_agentType == MultiplayerAgentType::DedicatedServer || m_agentType == MultiplayerAgentType::ClientServer)
|
||||
{
|
||||
// Signal to session management that a user has left the server
|
||||
if (AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get() != nullptr &&
|
||||
connection->GetConnectionRole() == ConnectionRole::Acceptor)
|
||||
{
|
||||
@@ -1133,7 +1132,10 @@ namespace Multiplayer
|
||||
return m_networkEntityManager.GetNetworkEntityTracker()->Get(node->second);
|
||||
}
|
||||
|
||||
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str()));
|
||||
// make sure the player prefab path is lowercase (how it's stored in the cache folder)
|
||||
auto sv_defaultPlayerSpawnAssetLowerCase = static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset);
|
||||
AZStd::to_lower(sv_defaultPlayerSpawnAssetLowerCase.begin(), sv_defaultPlayerSpawnAssetLowerCase.end());
|
||||
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAssetLowerCase).c_str()));
|
||||
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate);
|
||||
|
||||
for (NetworkEntityHandle subEntity : entityList)
|
||||
|
||||
@@ -31,8 +31,10 @@ namespace Multiplayer
|
||||
mpTools->SetDidProcessNetworkPrefabs(false);
|
||||
}
|
||||
|
||||
context.ListPrefabs([&context](AZStd::string_view prefabName, PrefabDom& prefab) {
|
||||
ProcessPrefab(context, prefabName, prefab);
|
||||
AZ::DataStream::StreamType serializationFormat = GetAzSerializationFormat();
|
||||
|
||||
context.ListPrefabs([&context, serializationFormat](AZStd::string_view prefabName, PrefabDom& prefab) {
|
||||
ProcessPrefab(context, prefabName, prefab, serializationFormat);
|
||||
});
|
||||
|
||||
if (mpTools && !context.GetProcessedObjects().empty())
|
||||
@@ -45,7 +47,15 @@ namespace Multiplayer
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
serializeContext->Class<NetworkPrefabProcessor, PrefabProcessor>()->Version(2);
|
||||
serializeContext->Enum<SerializationFormats>()
|
||||
->Value("Binary", SerializationFormats::Binary)
|
||||
->Value("Text", SerializationFormats::Text)
|
||||
;
|
||||
|
||||
serializeContext->Class<NetworkPrefabProcessor, PrefabProcessor>()
|
||||
->Version(3)
|
||||
->Field("SerializationFormat", &NetworkPrefabProcessor::m_serializationFormat)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +103,7 @@ namespace Multiplayer
|
||||
});
|
||||
}
|
||||
|
||||
void NetworkPrefabProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab)
|
||||
void NetworkPrefabProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab, AZ::DataStream::StreamType serializationFormat)
|
||||
{
|
||||
using namespace AzToolsFramework::Prefab;
|
||||
|
||||
@@ -107,10 +117,10 @@ namespace Multiplayer
|
||||
AZStd::string uniqueName = prefabName;
|
||||
uniqueName += ".network.spawnable";
|
||||
|
||||
auto serializer = [](AZStd::vector<uint8_t>& output, const ProcessedObjectStore& object) -> bool {
|
||||
auto serializer = [serializationFormat](AZStd::vector<uint8_t>& output, const ProcessedObjectStore& object) -> bool {
|
||||
AZ::IO::ByteContainerStream stream(&output);
|
||||
auto& asset = object.GetAsset();
|
||||
return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::ST_BINARY, &asset, asset.GetType());
|
||||
return AZ::Utils::SaveObjectToStream(stream, serializationFormat, &asset, asset.GetType());
|
||||
};
|
||||
|
||||
auto&& [object, networkSpawnable] =
|
||||
@@ -178,4 +188,14 @@ namespace Multiplayer
|
||||
|
||||
context.GetProcessedObjects().push_back(AZStd::move(object));
|
||||
}
|
||||
|
||||
AZ::DataStream::StreamType NetworkPrefabProcessor::GetAzSerializationFormat() const
|
||||
{
|
||||
if (m_serializationFormat == SerializationFormats::Text)
|
||||
{
|
||||
return AZ::DataStream::StreamType::ST_JSON;
|
||||
}
|
||||
|
||||
return AZ::DataStream::StreamType::ST_BINARY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessor.h>
|
||||
#include <AzCore/Serialization/ObjectStream.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab::PrefabConversionUtils
|
||||
{
|
||||
@@ -33,7 +34,23 @@ namespace Multiplayer
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//! The format the network spawnables are going to be stored in.
|
||||
enum class SerializationFormats
|
||||
{
|
||||
Binary, //!< Binary is generally preferable for performance.
|
||||
Text //!< Store in text format which is usually slower but helps with debugging.
|
||||
};
|
||||
|
||||
AZ::DataStream::StreamType GetAzSerializationFormat() const;
|
||||
|
||||
protected:
|
||||
static void ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab);
|
||||
static void ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab, AZ::DataStream::StreamType serializationFormat);
|
||||
|
||||
SerializationFormats m_serializationFormat = SerializationFormats::Binary;
|
||||
};
|
||||
}
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
AZ_TYPE_INFO_SPECIALIZE(Multiplayer::NetworkPrefabProcessor::SerializationFormats, "{F69B49EB-9D67-4D9C-99E7-DFA35D4ACCD2}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user