diff --git a/Gems/Multiplayer/Code/Include/IMultiplayer.h b/Gems/Multiplayer/Code/Include/IMultiplayer.h
index 94744dbb54..0f28bef403 100644
--- a/Gems/Multiplayer/Code/Include/IMultiplayer.h
+++ b/Gems/Multiplayer/Code/Include/IMultiplayer.h
@@ -92,6 +92,10 @@ namespace Multiplayer
//! @return the stats object bound to this multiplayer instance
MultiplayerStats& GetStats() { return m_stats; }
+ //! Sends a packet telling if entity update messages can be sent
+ //! @param readyForEntityUpdates Ready for entity updates or not
+ virtual void SendReadyForEntityUpdates(bool readyForEntityUpdates) = 0;
+
private:
MultiplayerStats m_stats;
};
diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml
index e5549e90d6..758ea27ad7 100644
--- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml
+++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml
@@ -14,6 +14,10 @@
+
+
+
+
diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h
index 02b045e63f..596bdc88d9 100644
--- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h
+++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h
@@ -36,7 +36,8 @@ namespace Multiplayer
void Update(AZ::TimeMs serverGameTimeMs) override;
//! @}
- bool CanSendUpdates();
+ bool CanSendUpdates() const;
+ void SetCanSendUpdates(bool canSendUpdates);
NetworkEntityHandle GetPrimaryPlayerEntity();
const NetworkEntityHandle& GetPrimaryPlayerEntity() const;
@@ -51,7 +52,7 @@ namespace Multiplayer
EntityStopEvent::Handler m_controlledEntityRemovedHandler;
EntityMigrationEvent::Handler m_controlledEntityMigrationHandler;
AzNetworking::IConnection* m_connection = nullptr;
- bool m_canSendUpdates = true;
+ bool m_canSendUpdates = false;
};
}
diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl
index 07bfb51536..0a4215a363 100644
--- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl
+++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.inl
@@ -12,11 +12,17 @@
namespace Multiplayer
{
- inline bool ServerToClientConnectionData::CanSendUpdates()
+ inline bool ServerToClientConnectionData::CanSendUpdates() const
{
return m_canSendUpdates;
}
+ inline void ServerToClientConnectionData::SetCanSendUpdates(bool canSendUpdates)
+ {
+ m_canSendUpdates = canSendUpdates;
+ }
+
+
inline NetworkEntityHandle ServerToClientConnectionData::GetPrimaryPlayerEntity()
{
return m_controlledEntity;
diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp
index e40e9e9d66..49a0f63b63 100644
--- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp
+++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp
@@ -381,6 +381,19 @@ namespace Multiplayer
return false;
}
+ bool MultiplayerSystemComponent::HandleRequest( AzNetworking::IConnection* connection,
+ [[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet)
+ {
+ auto* connectionData = reinterpret_cast(connection->GetUserData());
+ if (connectionData)
+ {
+ connectionData->SetCanSendUpdates(packet.GetReadyForEntityUpdates());
+ return true;
+ }
+
+ return false;
+ }
+
ConnectResult MultiplayerSystemComponent::ValidateConnect
(
[[maybe_unused]] const IpAddress& remoteAddress,
@@ -503,6 +516,15 @@ namespace Multiplayer
handler.Connect(m_shutdownEvent);
}
+ void MultiplayerSystemComponent::SendReadyForEntityUpdates(bool readyForEntityUpdates)
+ {
+ IConnectionSet& connectionSet = m_networkInterface->GetConnectionSet();
+ connectionSet.VisitConnections([readyForEntityUpdates](IConnection& connection)
+ {
+ connection.SendReliablePacket(MultiplayerPackets::ReadyForEntityUpdates(readyForEntityUpdates));
+ });
+ }
+
void MultiplayerSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
const MultiplayerStats& stats = GetStats();
diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h
index 1e10f9841e..8695dea7dd 100644
--- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h
+++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h
@@ -71,6 +71,7 @@ namespace Multiplayer
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ClientMigration& packet);
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
//! @{
@@ -88,6 +89,7 @@ namespace Multiplayer
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
+ void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
//! @}
//! Console commands.
diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp
index 2eb93d706d..131e7be3f1 100644
--- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp
+++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp
@@ -11,19 +11,19 @@
*/
#include
-#include
-#include
-#include
+
+#include
#include
#include
+#include
#include
#include
+#include
#include
#include
-#include
#include
#include
-#include
+#include
namespace Multiplayer
{
@@ -462,7 +462,9 @@ namespace Multiplayer
m_rootSpawnableAsset = netSpawnableAsset;
- const auto agentType = AZ::Interface::Get()->GetAgentType();
+ auto* iMultiplayer = AZ::Interface::Get();
+
+ const auto agentType = iMultiplayer->GetAgentType();
const bool spawnImmediately =
(agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer);
@@ -470,6 +472,12 @@ namespace Multiplayer
{
CreateEntitiesImmediate(*netSpawnable, NetEntityRole::Authority);
}
+ else
+ {
+ // If we don't spawn net entities immediately (i.e. it is a client),
+ // tell the server/host it can start sending updates that will instantiate entities.
+ iMultiplayer->SendReadyForEntityUpdates(true);
+ }
}
void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation)