diff --git a/Code/Framework/AzNetworking/AzNetworking/AutoGen/CorePackets.AutoPackets.xml b/Code/Framework/AzNetworking/AzNetworking/AutoGen/CorePackets.AutoPackets.xml
index 8ce3e5ad86..ae025b67e3 100644
--- a/Code/Framework/AzNetworking/AzNetworking/AutoGen/CorePackets.AutoPackets.xml
+++ b/Code/Framework/AzNetworking/AzNetworking/AutoGen/CorePackets.AutoPackets.xml
@@ -13,7 +13,9 @@
-
+
+
+
diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp
index b0f316cf50..eb07efe80f 100644
--- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.cpp
@@ -122,10 +122,4 @@ namespace AzNetworking
m_timeoutItemMap.erase(itemTimeoutId);
}
}
-
- void TimeoutQueue::UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts)
- {
- TimeoutHandler handler([&timeoutHandler](TimeoutQueue::TimeoutItem& item) { return timeoutHandler.HandleTimeout(item); });
- UpdateTimeouts(handler, maxTimeouts);
- }
}
diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h
index 63417ea36f..097e45c960 100644
--- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h
+++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/TimeoutQueue.h
@@ -23,8 +23,6 @@ namespace AzNetworking
Delete
};
- class ITimeoutHandler;
-
//! @class TimeoutQueue
//! @brief class for managing timeout items.
class TimeoutQueue
@@ -70,11 +68,6 @@ namespace AzNetworking
using TimeoutHandler = AZStd::function;
void UpdateTimeouts(const TimeoutHandler& timeoutHandler, int32_t maxTimeouts = -1);
- //! Updates timeouts for all items, invokes timeout handlers if required.
- //! @param timeoutHandler listener instance to call back on for timeouts
- //! @param maxTimeouts the maximum number of timeouts to process before breaking iteration
- void UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts = -1);
-
private:
struct TimeoutQueueItem
@@ -94,19 +87,6 @@ namespace AzNetworking
TimeoutItemMap m_timeoutItemMap;
TimeoutItemQueue m_timeoutItemQueue;
};
-
- //! @class ITimeoutHandler
- //! @brief interface class for managing timeout items.
- class ITimeoutHandler
- {
- public:
- virtual ~ITimeoutHandler() = default;
-
- //! Handler callback for timed out items.
- //! @param item containing registered timeout details
- //! @return ETimeoutResult for whether to re-register or discard the timeout params
- virtual TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) = 0;
- };
}
#include
diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp
index 6d7358a425..7537232a27 100644
--- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.cpp
@@ -27,13 +27,11 @@ namespace AzNetworking
ConnectionId connectionId,
const IpAddress& remoteAddress,
TcpNetworkInterface& networkInterface,
- TcpSocket& socket,
- TimeoutId timeoutId
+ TcpSocket& socket
)
: IConnection(connectionId, remoteAddress)
, m_networkInterface(networkInterface)
, m_socket(socket.CloneAndTakeOwnership())
- , m_timeoutId(timeoutId)
, m_state(m_socket->IsOpen() ? ConnectionState::Connecting : ConnectionState::Disconnected)
, m_connectionRole(ConnectionRole::Acceptor)
, m_registeredSocketFd(InvalidSocketFd)
@@ -163,13 +161,6 @@ namespace AzNetworking
break;
}
- TimeoutQueue::TimeoutItem* timeoutItem = m_networkInterface.m_connectionTimeoutQueue.RetrieveItem(GetTimeoutId());
- if (timeoutItem == nullptr)
- {
- return true;
- }
- timeoutItem->UpdateTimeoutTime(startTimeMs);
-
NetworkOutputSerializer serializer(buffer.GetBuffer(), static_cast(buffer.GetSize()));
if (m_state == ConnectionState::Connecting)
{
diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h
index b769aea086..3d74f3f336 100644
--- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h
+++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.h
@@ -38,14 +38,12 @@ namespace AzNetworking
//! @param remoteAddress IP address of the remote endpoint
//! @param networkInterface TcpNetworkInterface that owns this connection instance
//! @param socket TCP socket to take ownership of and use for sending and receiving data
- //! @param timeoutId timeout identifier of this connection instance
TcpConnection
(
ConnectionId connectionId,
const IpAddress& remoteAddress,
TcpNetworkInterface& networkInterface,
- TcpSocket& socket,
- TimeoutId timeoutId
+ TcpSocket& socket
);
//! Construct a new socket with optional encryption, used when initiating a new connection
@@ -69,14 +67,6 @@ namespace AzNetworking
//! @return the TcpSocket bound to this TcpConnection
TcpSocket* GetTcpSocket() const;
- //! Sets the timeout identifier for this TcpConnection.
- //! @param timeoutId the timeout identifier to use for this TcpConnection
- void SetTimeoutId(TimeoutId timeoutId);
-
- //! Returns the timeout identifier for this TcpConnection.
- //! @return the timeout identifier for this TcpConnection
- TimeoutId GetTimeoutId() const;
-
//! Returns true if this connection instance is in an open state, and is capable of actively sending and receiving packets.
//! @return boolean true if this connection instance is in an open state
bool IsOpen() const;
@@ -142,7 +132,6 @@ namespace AzNetworking
AZStd::unique_ptr m_socket;
AZStd::unique_ptr m_compressor;
- TimeoutId m_timeoutId;
PacketId m_lastSentPacketId = InvalidPacketId;
ConnectionState m_state = ConnectionState::Disconnected;
ConnectionRole m_connectionRole = ConnectionRole::Connector;
diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.inl b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.inl
index ecd1e5e908..5b5f38774e 100644
--- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.inl
+++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpConnection.inl
@@ -15,16 +15,6 @@ namespace AzNetworking
return m_socket.get();
}
- inline void TcpConnection::SetTimeoutId(TimeoutId timeoutId)
- {
- m_timeoutId = timeoutId;
- }
-
- inline TimeoutId TcpConnection::GetTimeoutId() const
- {
- return m_timeoutId;
- }
-
inline bool TcpConnection::IsOpen() const
{
return m_socket->IsOpen();
diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp
index 1ccff7be50..0278856ce9 100644
--- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.cpp
@@ -21,16 +21,11 @@ namespace AzNetworking
static const bool net_TcpUseEncryption = false;
#endif
- AZ_CVAR(bool, net_TcpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Tcp connections");
- AZ_CVAR(AZ::TimeMs, net_TcpHeartbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Tcp connection heartbeat frequency");
- AZ_CVAR(AZ::TimeMs, net_TcpDefaultTimeoutMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Tcp connection");
-
TcpNetworkInterface::TcpNetworkInterface(AZ::Name name, IConnectionListener& connectionListener, TrustZone trustZone, TcpListenThread& listenThread)
: m_name(name)
, m_trustZone(trustZone)
, m_connectionListener(connectionListener)
, m_listenThread(listenThread)
- , m_timeoutMs(net_TcpDefaultTimeoutMs)
{
;
}
@@ -98,8 +93,6 @@ namespace AzNetworking
}
AZLOG_INFO("Adding new socket %d", static_cast(tcpSocket->GetSocketFd()));
- const TimeoutId newTimeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast(tcpSocket->GetSocketFd()), net_TcpHeartbeatTimeMs);
- connection->SetTimeoutId(newTimeoutId);
connection->SendReliablePacket(CorePackets::InitiateConnectionPacket());
m_connectionListener.OnConnect(connection.get());
m_connectionSet.AddConnection(AZStd::move(connection));
@@ -110,12 +103,6 @@ namespace AzNetworking
{
const AZ::TimeMs startTimeMs = AZ::GetElapsedTimeMs();
- // Time out any stale connections
- {
- ConnectionTimeoutFunctor functor(*this);
- m_connectionTimeoutQueue.UpdateTimeouts(functor);
- }
-
AcceptNewConnections();
auto readCallback = [this, startTimeMs](SocketFd socketFd) { HandleConnectionRecv(socketFd, startTimeMs); };
@@ -258,8 +245,7 @@ namespace AzNetworking
return;
}
AZLOG(NET_TcpTraffic, "Adding new socket %d", static_cast(tcpSocket.GetSocketFd()));
- const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast(tcpSocket.GetSocketFd()), m_timeoutMs);
- AZStd::unique_ptr connection = AZStd::make_unique(connectionId, remoteAddress, *this, tcpSocket, timeoutId);
+ AZStd::unique_ptr connection = AZStd::make_unique(connectionId, remoteAddress, *this, tcpSocket);
AZ_Assert(connection->GetConnectionRole() == ConnectionRole::Acceptor, "Invalid role for connection");
GetConnectionListener().OnConnect(connection.get());
m_connectionSet.AddConnection(AZStd::move(connection));
@@ -286,7 +272,6 @@ namespace AzNetworking
m_pendingRemoves.resize_no_construct(0);
}
-
TcpNetworkInterface::PendingConnection::PendingConnection(SocketFd socketFd, uint32_t remoteIpAddress, uint16_t remotePort, uint16_t listenPort)
: m_socketFd(socketFd)
, m_remoteIpAddress(remoteIpAddress)
@@ -295,34 +280,4 @@ namespace AzNetworking
{
;
}
-
- TcpNetworkInterface::ConnectionTimeoutFunctor::ConnectionTimeoutFunctor(TcpNetworkInterface& networkInterface)
- : m_networkInterface(networkInterface)
- {
- ;
- }
-
- TimeoutResult TcpNetworkInterface::ConnectionTimeoutFunctor::HandleTimeout(TimeoutQueue::TimeoutItem& item)
- {
- const SocketFd socketFd = static_cast(item.m_userData);
- TcpConnection* tcpConnection = m_networkInterface.m_connectionSet.GetConnection(socketFd);
-
- if (tcpConnection == nullptr)
- {
- // We've already deleted this connection
- return TimeoutResult::Delete;
- }
-
- if (tcpConnection->GetConnectionRole() == ConnectionRole::Connector)
- {
- tcpConnection->SendReliablePacket(CorePackets::HeartbeatPacket());
- }
- else if (net_TcpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 }))
- {
- tcpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
- return TimeoutResult::Delete;
- }
-
- return TimeoutResult::Refresh;
- }
}
diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h
index d8f5d1b62b..8d45e847a8 100644
--- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h
+++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpNetworkInterface.h
@@ -137,16 +137,6 @@ namespace AzNetworking
AZ_DISABLE_COPY_MOVE(TcpNetworkInterface);
- struct ConnectionTimeoutFunctor final
- : public ITimeoutHandler
- {
- ConnectionTimeoutFunctor(TcpNetworkInterface& networkInterface);
- TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override;
- private:
- AZ_DISABLE_COPY_MOVE(ConnectionTimeoutFunctor);
- TcpNetworkInterface& m_networkInterface;
- };
-
struct PendingRemove
{
SocketFd m_socketFd;
@@ -162,7 +152,6 @@ namespace AzNetworking
TcpSocketManager m_tcpSocketManager;
AZ::ThreadSafeDeque m_pendingConnections;
AZStd::vector m_pendingRemoves;
- TimeoutQueue m_connectionTimeoutQueue;
TcpListenThread& m_listenThread;
friend class TcpConnection; // For access to private RequestDisconnect() method
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp
index 7b451865c4..452992f971 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.cpp
@@ -79,7 +79,8 @@ namespace AzNetworking
AZLOG(NET_Acks, "Unacked packet count exceeded, sending client heartbeat (curr %u : max %u)", m_unackedPacketCount, static_cast(net_UdpMaxUnackedPacketCount));
// This simply times out unreliable chunks that haven't completed within our timeout delay
m_fragmentQueue.Update();
- SendUnreliablePacket(CorePackets::HeartbeatPacket());
+ // This heartbeat is sent to minimize the time the remote endpoint spends waiting for ack vector replication, we don't require a response
+ SendUnreliablePacket(CorePackets::HeartbeatPacket(false));
}
}
@@ -289,7 +290,11 @@ namespace AzNetworking
{
return PacketDispatchResult::Failure;
}
- // Do nothing, we've already processed our ack packets
+ if (packet.GetRequestResponse())
+ {
+ // We're replying to a heartbeat request, we don't want a response
+ SendUnreliablePacket(CorePackets::HeartbeatPacket(false));
+ }
return PacketDispatchResult::Success;
}
break;
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h
index 199a5a8347..ddd75c9946 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpConnection.h
@@ -136,12 +136,12 @@ namespace AzNetworking
AZ_DISABLE_COPY_MOVE(UdpConnection);
UdpNetworkInterface& m_networkInterface;
- UdpPacketTracker m_packetTracker;
- UdpReliableQueue m_reliableQueue;
- UdpFragmentQueue m_fragmentQueue;
- ConnectionState m_state = ConnectionState::Disconnected;
- ConnectionRole m_connectionRole = ConnectionRole::Connector;
- DtlsEndpoint m_dtlsEndpoint;
+ UdpPacketTracker m_packetTracker;
+ UdpReliableQueue m_reliableQueue;
+ UdpFragmentQueue m_fragmentQueue;
+ ConnectionState m_state = ConnectionState::Disconnected;
+ ConnectionRole m_connectionRole = ConnectionRole::Connector;
+ DtlsEndpoint m_dtlsEndpoint;
AZ::TimeMs m_lastSentPacketMs;
uint32_t m_unackedPacketCount = 0;
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp
index fa4ee78a92..0c710f5a14 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.cpp
@@ -20,7 +20,13 @@ namespace AzNetworking
void UdpFragmentQueue::Update()
{
- m_timeoutQueue.UpdateTimeouts(*this);
+ m_timeoutQueue.UpdateTimeouts([this](TimeoutQueue::TimeoutItem& item)
+ {
+ const SequenceId fragmentSequence = static_cast(item.m_userData & 0xFF);
+ AZLOG(NET_FragmentQueue, "Timing out unreliable fragmented packet %u", static_cast(fragmentSequence));
+ m_packetFragments.erase(fragmentSequence);
+ return TimeoutResult::Delete;
+ });
}
void UdpFragmentQueue::Reset()
@@ -163,12 +169,4 @@ namespace AzNetworking
return handledPacket;
}
-
- TimeoutResult UdpFragmentQueue::HandleTimeout(TimeoutQueue::TimeoutItem& item)
- {
- const SequenceId fragmentSequence = static_cast(item.m_userData & 0xFF);
- AZLOG(NET_FragmentQueue, "Timing out unreliable fragmented packet %u", static_cast(fragmentSequence));
- m_packetFragments.erase(fragmentSequence);
- return TimeoutResult::Delete;
- }
}
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h
index 9c929d63e8..5efa767283 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpFragmentQueue.h
@@ -26,7 +26,6 @@ namespace AzNetworking
//! @class UdpFragmentQueue
//! @brief Class for reconstructing packet chunks into the original unsegmented packet.
class UdpFragmentQueue
- : public ITimeoutHandler
{
public:
@@ -51,11 +50,6 @@ namespace AzNetworking
private:
- //! Handler callback for timed out items.
- //! @param item containing registered timeout details
- //! @return ETimeoutResult for whether to re-register or discard the timeout params
- TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override;
-
TimeoutQueue m_timeoutQueue;
SequenceGenerator m_sequenceGenerator;
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp
index b0e64f93e3..b810c7a347 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.cpp
@@ -31,7 +31,7 @@ namespace AzNetworking
AZ_CVAR(bool, net_UdpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Udp connections");
AZ_CVAR(AZ::TimeMs, net_UdpPacketTimeSliceMs, AZ::TimeMs{ 8 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The number of milliseconds to allow for packet processing");
- AZ_CVAR(AZ::TimeMs, net_UdpHeartbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Udp connection heartbeat frequency");
+ AZ_CVAR(uint32_t, net_UdpUnackedHeartbeats, 5, nullptr, AZ::ConsoleFunctorFlags::Null, "The number of heartbeats to attempt to send to keep a connection alive before giving up");
AZ_CVAR(AZ::TimeMs, net_UdpDefaultTimeoutMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Udp connection");
AZ_CVAR(AZ::TimeMs, net_MinPacketTimeoutMs, AZ::TimeMs{ 200 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Minimum time to wait before timing out an unacked packet");
AZ_CVAR(int32_t, net_MaxTimeoutsPerFrame, 1000, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of packet timeouts to allow to process in a single frame");
@@ -139,7 +139,8 @@ namespace AzNetworking
}
const ConnectionId connectionId = m_connectionSet.GetNextConnectionId();
- const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast(connectionId), m_timeoutMs);
+ const AZ::TimeMs timeoutTimeMs = m_timeoutMs / static_cast(static_cast(net_UdpUnackedHeartbeats));
+ const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast(connectionId), timeoutTimeMs);
AZStd::unique_ptr connection = AZStd::make_unique(connectionId, remoteAddress, *this, ConnectionRole::Connector);
UdpPacketEncodingBuffer dtlsData;
@@ -277,6 +278,7 @@ namespace AzNetworking
}
timeoutItem->UpdateTimeoutTime(startTimeMs);
+ connection->m_timeoutCounter = 0;
PacketDispatchResult handledPacket = PacketDispatchResult::Failure;
if (header.GetPacketType() < aznumeric_cast(CorePackets::PacketType::MAX))
@@ -319,16 +321,10 @@ namespace AzNetworking
const AZ::TimeMs receiveTimeMs = AZ::GetElapsedTimeMs() - startTimeMs;
// Time out any stale client connections
- {
- ConnectionTimeoutFunctor functor(*this);
- m_connectionTimeoutQueue.UpdateTimeouts(functor);
- }
+ m_connectionTimeoutQueue.UpdateTimeouts([this](TimeoutQueue::TimeoutItem& item) { return HandleConnectionTimeout(item); });
// Time out any packets that haven't been acked within our timeout window
- {
- PacketTimeoutFunctor functor(*this);
- m_packetTimeoutQueue.UpdateTimeouts(functor, static_cast(net_MaxTimeoutsPerFrame));
- }
+ m_packetTimeoutQueue.UpdateTimeouts([this](TimeoutQueue::TimeoutItem& item) { return HandlePacketTimeout(item); }, static_cast(net_MaxTimeoutsPerFrame));
// Delete any connections we've disconnected
for (RemovedConnection& removedConnection : m_removedConnections)
@@ -709,21 +705,14 @@ namespace AzNetworking
{
// Packets involved in handshake are InitiateConnection, ConnectionHandshake and FragmentedPackets of ConnectionHandshake
return packetType == aznumeric_cast(CorePackets::PacketType::InitiateConnectionPacket) ||
- packetType == aznumeric_cast(CorePackets::PacketType::ConnectionHandshakePacket) ||
- (packetType == aznumeric_cast(CorePackets::PacketType::FragmentedPacket) && endpoint.IsConnecting());
+ packetType == aznumeric_cast(CorePackets::PacketType::ConnectionHandshakePacket) ||
+ (packetType == aznumeric_cast(CorePackets::PacketType::FragmentedPacket) && endpoint.IsConnecting());
}
-
- UdpNetworkInterface::ConnectionTimeoutFunctor::ConnectionTimeoutFunctor(UdpNetworkInterface& networkInterface)
- : m_networkInterface(networkInterface)
- {
- ;
- }
-
- TimeoutResult UdpNetworkInterface::ConnectionTimeoutFunctor::HandleTimeout(TimeoutQueue::TimeoutItem& item)
+ TimeoutResult UdpNetworkInterface::HandleConnectionTimeout(TimeoutQueue::TimeoutItem& item)
{
const ConnectionId connectionId = ConnectionId(aznumeric_cast(item.m_userData));
- UdpConnection* udpConnection = static_cast(m_networkInterface.m_connectionSet.GetConnection(connectionId));
+ UdpConnection* udpConnection = static_cast(m_connectionSet.GetConnection(connectionId));
if (udpConnection == nullptr)
{
@@ -731,22 +720,23 @@ namespace AzNetworking
return TimeoutResult::Delete;
}
- if (udpConnection->GetConnectionState() == ConnectionState::Connecting)
+ if ((udpConnection->GetConnectionState() == ConnectionState::Connecting)
+ && udpConnection->GetDtlsEndpoint().IsConnecting())
{
- if (udpConnection->GetDtlsEndpoint().IsConnecting())
- {
- // DTLS prefers we resend data lost over the wire with fresh SSL IDs so account for that here
- UdpPacketEncodingBuffer dtlsData;
- udpConnection->ProcessHandshakeData(dtlsData);
- return TimeoutResult::Refresh;
- }
+ // DTLS prefers we resend data lost over the wire with fresh SSL IDs so account for that here
+ UdpPacketEncodingBuffer dtlsData;
+ udpConnection->ProcessHandshakeData(dtlsData);
+ return TimeoutResult::Refresh;
}
- if (udpConnection->GetConnectionRole() == ConnectionRole::Connector)
+ if ((udpConnection->GetConnectionRole() == ConnectionRole::Connector)
+ && (udpConnection->m_timeoutCounter < net_UdpUnackedHeartbeats))
{
- udpConnection->SendUnreliablePacket(CorePackets::HeartbeatPacket());
+ // Set the request response flag to true since we want a response to keep the connection alive
+ udpConnection->SendUnreliablePacket(CorePackets::HeartbeatPacket(true));
+ ++udpConnection->m_timeoutCounter;
}
- else if (net_UdpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 }))
+ else if (net_UdpTimeoutConnections && (GetTimeoutMs() > AZ::TimeMs{ 0 }))
{
udpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
return TimeoutResult::Delete;
@@ -755,19 +745,13 @@ namespace AzNetworking
return TimeoutResult::Refresh;
}
- UdpNetworkInterface::PacketTimeoutFunctor::PacketTimeoutFunctor(UdpNetworkInterface& networkInterface)
- : m_networkInterface(networkInterface)
- {
- ;
- }
-
- TimeoutResult UdpNetworkInterface::PacketTimeoutFunctor::HandleTimeout(TimeoutQueue::TimeoutItem& item)
+ TimeoutResult UdpNetworkInterface::HandlePacketTimeout(TimeoutQueue::TimeoutItem& item)
{
ConnectionId connectionId;
PacketId packetId;
ReliabilityType reliability;
DecodeTimeoutId(item.m_userData, connectionId, packetId, reliability);
- UdpConnection* connection = static_cast(m_networkInterface.m_connectionSet.GetConnection(connectionId));
+ UdpConnection* connection = static_cast(m_connectionSet.GetConnection(connectionId));
if (connection == nullptr)
{
@@ -782,16 +766,14 @@ namespace AzNetworking
case PacketTimeoutResult::Acked:
// Packet was already acked, just discard this timeout entry
return TimeoutResult::Delete;
-
case PacketTimeoutResult::Pending:
// Packet timed out before we received any info about it's sequence from the remote endpoint
// The connection latency may have increased, and our Rtt metrics may still be adjusting..
// Just throw it back into the timeout queue
return TimeoutResult::Refresh;
-
case PacketTimeoutResult::Lost:
// Packet timed out and was not acked, so we consider it lost
- m_networkInterface.m_connectionListener.OnPacketLost(connection, packetId);
+ m_connectionListener.OnPacketLost(connection, packetId);
break;
}
diff --git a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h
index 8f827c74c4..e6abeded0d 100644
--- a/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h
+++ b/Code/Framework/AzNetworking/AzNetworking/UdpTransport/UdpNetworkInterface.h
@@ -149,34 +149,24 @@ namespace AzNetworking
//! @param endpoint whether the disconnection was initiated locally or remotely
void RequestDisconnect(UdpConnection* connection, DisconnectReason reason, TerminationEndpoint endpoint);
- //! Internal helper to check if a packet's type is for connection handshake
+ //! Internal helper to check if a packet's type is for connection handshake.
//! @param endpoint DTLS endpoint participating in the handshake
//! @param packetType type of the packet
//! @return if the packet is for handshake
bool IsHandshakePacket(const DtlsEndpoint& endpoint, AzNetworking::PacketType packetType) const;
+ //! Internal helper to manage connection timeout behaviour.
+ //! @param item the timeout item corresponding to the timed out connection
+ //! @return whether to delete or persist the timeout item
+ TimeoutResult HandleConnectionTimeout(TimeoutQueue::TimeoutItem& item);
+
+ //! Internal helper to manage packet timeout behaviour.
+ //! @param item the timeout item corresponding to the timed out packet
+ //! @return whether to delete or persist the timeout item
+ TimeoutResult HandlePacketTimeout(TimeoutQueue::TimeoutItem& item);
+
AZ_DISABLE_COPY_MOVE(UdpNetworkInterface);
- struct ConnectionTimeoutFunctor final
- : public ITimeoutHandler
- {
- ConnectionTimeoutFunctor(UdpNetworkInterface& networkInterface);
- TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override;
- private:
- AZ_DISABLE_COPY_MOVE(ConnectionTimeoutFunctor);
- UdpNetworkInterface& m_networkInterface;
- };
-
- struct PacketTimeoutFunctor final
- : public ITimeoutHandler
- {
- PacketTimeoutFunctor(UdpNetworkInterface& networkInterface);
- TimeoutResult HandleTimeout(TimeoutQueue::TimeoutItem& item) override;
- private:
- AZ_DISABLE_COPY_MOVE(PacketTimeoutFunctor);
- UdpNetworkInterface& m_networkInterface;
- };
-
AZ::Name m_name;
TrustZone m_trustZone;
uint16_t m_port = 0;
diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h b/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h
index fc41a9b4d0..b650f9e1d9 100644
--- a/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h
+++ b/Gems/Multiplayer/Code/Include/Multiplayer/EntityDomains/IEntityDomain.h
@@ -17,8 +17,6 @@ namespace Multiplayer
class IEntityDomain
{
public:
- using EntitiesNotInDomain = AZStd::unordered_set;
-
virtual ~IEntityDomain() = default;
//! For domains that operate on a region of space, this sets the area the domain is responsible for.
@@ -34,12 +32,10 @@ namespace Multiplayer
//! @return false if this entity should not belong to the entity manger, true if it could be owned by the entity manager
virtual bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const = 0;
- //! Enable Entity Domain Exit Tracking for entities on the host.
- //! @param ownedEntitySet the set of entities to activate tracking for
- virtual void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) = 0;
-
- //! Return the set of netbound entities not included in this domain.
- virtual const EntitiesNotInDomain& RetrieveEntitiesNotInDomain() const = 0;
+ //! This method will be invoked whenever we unexpectedly lose the authoritative entity replicator for an entity.
+ //! This gives our entity domain a chance to determine whether or not it should assume authority in this instance.
+ //! @param entityHandle the network entity handle of the entity that has lost its authoritative replicator
+ virtual void HandleLossOfAuthoritativeReplicator(const ConstNetworkEntityHandle& entityHandle) = 0;
//! Debug draw to visualize host entity domains.
virtual void DebugDraw() const = 0;
diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h
index 10346ad777..74935d5746 100644
--- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h
+++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h
@@ -150,7 +150,6 @@ namespace Multiplayer
void ClearRemovedReplicators();
class OrphanedEntityRpcs
- : public AzNetworking::ITimeoutHandler
{
public:
OrphanedEntityRpcs(EntityReplicationManager& replicationManager);
@@ -159,7 +158,6 @@ namespace Multiplayer
void AddOrphanedRpc(NetEntityId entityId, NetworkEntityRpcMessage& entityRpcMessage);
AZStd::size_t Size() const { return m_entityRpcMap.size(); }
private:
- AzNetworking::TimeoutResult HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item) override;
struct OrphanedRpcs
{
OrphanedRpcs() = default;
diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h
index 43915127ed..8c16176736 100644
--- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h
+++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/INetworkEntityManager.h
@@ -26,6 +26,7 @@ namespace Multiplayer
using EntityExitDomainEvent = AZ::Event;
using ControllersActivatedEvent = AZ::Event;
using ControllersDeactivatedEvent = AZ::Event;
+ using NetEntityIdSet = AZStd::unordered_set;
//! @class INetworkEntityManager
//! @brief The interface for managing all networked entities.
@@ -34,18 +35,17 @@ namespace Multiplayer
public:
AZ_RTTI(INetworkEntityManager, "{109759DE-9492-439C-A0B1-AE46E6FD029C}");
- using OwnedEntitySet = AZStd::unordered_set;
using EntityList = AZStd::vector;
virtual ~INetworkEntityManager() = default;
- //! Configures the NetworkEntityManager to operate as an authoritative host.
- //! @param hostId the hostId of this NetworkEntityManager
+ //! Configures the NetworkEntityManager.
+ //! @param hostId the hostId of this NetworkEntityManager (invalid for clients)
//! @param entityDomain the entity domain used to determine which entities this manager has authority over
virtual void Initialize(const HostId& hostId, AZStd::unique_ptr entityDomain) = 0;
- //! Returns whether or not the network entity manager has been initialized to host.
- //! @return boolean true if this network entity manager has been intialized to host
+ //! Returns whether or not the network entity manager has been initialized.
+ //! @return boolean true if this network entity manager has been intialized
virtual bool IsInitialized() const = 0;
//! Returns the entity domain associated with this network entity manager, this will be nullptr on clients.
@@ -181,6 +181,19 @@ namespace Multiplayer
//! @param entityRpcMessage the local rpc message to handle
virtual void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) = 0;
+ //! Handles a set of entities transitioning between entity domains.
+ //! @param entitiesNotInDomain the set of entities that are no longer contained within our entity domain
+ virtual void HandleEntitiesExitDomain(const NetEntityIdSet& entitiesNotInDomain) = 0;
+
+ //! Forcibly assumes authoritative control over the given entity.
+ //! This should only be used in the event of the unexpected loss of the previous authority, any other usage could corrupt the simulation.
+ //! @param entityHandle the entity to forcibly assume authoritative control over
+ virtual void ForceAssumeAuthority(const ConstNetworkEntityHandle& entityHandle) = 0;
+
+ //! Overrides the default timeout time used during entity migrations.
+ //! @param timeoutTimeMs the timeout time to use in milliseconds
+ virtual void SetMigrateTimeoutTimeMs(AZ::TimeMs timeoutTimeMs) = 0;
+
//! Visualization of network entity manager state.
virtual void DebugDraw() const = 0;
};
diff --git a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp
index cc71000d33..ceb9412408 100644
--- a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp
+++ b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp
@@ -317,7 +317,7 @@ namespace Multiplayer
return false;
}
- bool NetBindComponent::HandlePropertyChangeMessage([[maybe_unused]] AzNetworking::ISerializer& serializer, [[maybe_unused]] bool notifyChanges)
+ bool NetBindComponent::HandlePropertyChangeMessage(AzNetworking::ISerializer& serializer, bool notifyChanges)
{
const NetEntityRole netEntityRole = m_netEntityRole;
ReplicationRecord replicationRecord(netEntityRole);
@@ -492,7 +492,7 @@ namespace Multiplayer
void NetBindComponent::FillTotalReplicationRecord(ReplicationRecord& replicationRecord) const
{
replicationRecord.Append(m_totalRecord);
- // if we have any outstanding changes yet to be logged, grab those as well
+ // If we have any outstanding changes yet to be logged, grab those as well
if (m_currentRecord.HasChanges())
{
replicationRecord.Append(m_currentRecord);
diff --git a/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.cpp b/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.cpp
index 9d53990fb4..59e2afc638 100644
--- a/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.cpp
+++ b/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.cpp
@@ -26,14 +26,9 @@ namespace Multiplayer
return true;
}
- void FullOwnershipEntityDomain::ActivateTracking([[maybe_unused]] const INetworkEntityManager::OwnedEntitySet& ownedEntitySet)
+ void FullOwnershipEntityDomain::HandleLossOfAuthoritativeReplicator([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle)
{
- ;
- }
-
- const IEntityDomain::EntitiesNotInDomain& FullOwnershipEntityDomain::RetrieveEntitiesNotInDomain() const
- {
- return m_entitiesNotInDomain;
+ AZ_Assert(false, "FullOwnershipEntityDomain has authoritative control over all entities, something unexpected has happened");
}
void FullOwnershipEntityDomain::DebugDraw() const
diff --git a/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.h b/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.h
index ae80c16ab8..203d9579ea 100644
--- a/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.h
+++ b/Gems/Multiplayer/Code/Source/EntityDomains/FullOwnershipEntityDomain.h
@@ -24,12 +24,8 @@ namespace Multiplayer
void SetAabb(const AZ::Aabb& aabb) override;
const AZ::Aabb& GetAabb() const override;
bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const override;
- void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) override;
- const EntitiesNotInDomain& RetrieveEntitiesNotInDomain() const override;
+ void HandleLossOfAuthoritativeReplicator(const ConstNetworkEntityHandle& entityHandle) override;
void DebugDraw() const override;
//! @}
-
- private:
- EntitiesNotInDomain m_entitiesNotInDomain;
};
}
diff --git a/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.cpp b/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.cpp
new file mode 100644
index 0000000000..21d6abcb5a
--- /dev/null
+++ b/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+#include
+#include
+
+namespace Multiplayer
+{
+ void NullEntityDomain::SetAabb([[maybe_unused]] const AZ::Aabb& aabb)
+ {
+ ; // Do nothing, by definition we own everything
+ }
+
+ const AZ::Aabb& NullEntityDomain::GetAabb() const
+ {
+ static AZ::Aabb nullAabb = AZ::Aabb::CreateNull();
+ return nullAabb;
+ }
+
+ bool NullEntityDomain::IsInDomain([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const
+ {
+ return false;
+ }
+
+ void NullEntityDomain::HandleLossOfAuthoritativeReplicator([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle)
+ {
+ AZLOG_ERROR("Timed out entity id %llu during migration, marking for removal", aznumeric_cast(entityHandle.GetNetEntityId()));
+ GetNetworkEntityManager()->MarkForRemoval(entityHandle);
+ }
+
+ void NullEntityDomain::DebugDraw() const
+ {
+ ;
+ }
+}
diff --git a/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.h b/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.h
new file mode 100644
index 0000000000..247d82b366
--- /dev/null
+++ b/Gems/Multiplayer/Code/Source/EntityDomains/NullEntityDomain.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+
+namespace Multiplayer
+{
+ class NullEntityDomain
+ : public IEntityDomain
+ {
+ public:
+ NullEntityDomain() = default;
+ NullEntityDomain(const NullEntityDomain& rhs) = default;
+
+ //! IEntityDomain overrides.
+ //! @{
+ void SetAabb(const AZ::Aabb& aabb) override;
+ const AZ::Aabb& GetAabb() const override;
+ bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const override;
+ void HandleLossOfAuthoritativeReplicator(const ConstNetworkEntityHandle& entityHandle) override;
+ void DebugDraw() const override;
+ //! @}
+ };
+}
diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp
index 6df5e4611f..503a8128b0 100644
--- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp
+++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -831,18 +832,21 @@ namespace Multiplayer
if (multiplayerType == MultiplayerAgentType::ClientServer || multiplayerType == MultiplayerAgentType::DedicatedServer)
{
m_spawnNetboundEntities = true;
- m_initEvent.Signal(m_networkInterface);
-
+ m_initEvent.Signal(m_networkInterface); //< Note! This might initialize our network entity manager for us
if (!m_networkEntityManager.IsInitialized())
{
- // Set up a full ownership domain if we didn't construct a domain during the initialize event
const AZ::CVarFixedString serverAddr = cl_serveraddr;
const uint16_t serverPort = cl_serverport;
const AzNetworking::ProtocolType serverProtocol = sv_protocol;
const AzNetworking::IpAddress hostId = AzNetworking::IpAddress(serverAddr.c_str(), serverPort, serverProtocol);
+ // Set up a full ownership domain if we didn't construct a domain during the initialize event
m_networkEntityManager.Initialize(hostId, AZStd::make_unique());
}
}
+ else if (multiplayerType == MultiplayerAgentType::Client)
+ {
+ m_networkEntityManager.Initialize(AzNetworking::IpAddress(), AZStd::make_unique());
+ }
}
m_agentType = multiplayerType;
@@ -1106,7 +1110,6 @@ namespace Multiplayer
void MultiplayerSystemComponent::OnAutonomousEntityReplicatorCreated()
{
m_autonomousEntityReplicatorCreatedHandler.Disconnect();
- //m_networkEntityManager.GetNetworkEntityAuthorityTracker()->ResetTimeoutTime(AZ::TimeMs{ 2000 });
m_clientMigrationEndEvent.Signal();
}
diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h
index 87d084d5bc..53707523bb 100644
--- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h
+++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h
@@ -155,7 +155,6 @@ namespace Multiplayer
AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session");
AzNetworking::INetworkInterface* m_networkInterface = nullptr;
- AzNetworking::INetworkInterface* m_networkEditorInterface = nullptr;
AZ::ConsoleCommandInvokedEvent::Handler m_consoleCommandHandler;
AZ::ThreadSafeDeque m_cvarCommands;
diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp
index fa099842c5..583671d1f3 100644
--- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp
+++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp
@@ -912,24 +912,22 @@ namespace Multiplayer
;
}
- AzNetworking::TimeoutResult EntityReplicationManager::OrphanedEntityRpcs::HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item)
- {
- NetEntityId timedOutEntityId = aznumeric_cast(item.m_userData);
- auto entityRpcsIter = m_entityRpcMap.find(timedOutEntityId);
- if (entityRpcsIter != m_entityRpcMap.end())
- {
- for (NetworkEntityRpcMessage& rpcMessage : entityRpcsIter->second.m_rpcMessages)
- {
- m_replicationManager.DispatchOrphanedRpc(rpcMessage, nullptr);
- }
- m_entityRpcMap.erase(entityRpcsIter);
- }
- return AzNetworking::TimeoutResult::Delete;
- }
-
void EntityReplicationManager::OrphanedEntityRpcs::Update()
{
- m_timeoutQueue.UpdateTimeouts(*this);
+ m_timeoutQueue.UpdateTimeouts([this](AzNetworking::TimeoutQueue::TimeoutItem& item)
+ {
+ NetEntityId timedOutEntityId = aznumeric_cast(item.m_userData);
+ auto entityRpcsIter = m_entityRpcMap.find(timedOutEntityId);
+ if (entityRpcsIter != m_entityRpcMap.end())
+ {
+ for (NetworkEntityRpcMessage& rpcMessage : entityRpcsIter->second.m_rpcMessages)
+ {
+ m_replicationManager.DispatchOrphanedRpc(rpcMessage, nullptr);
+ }
+ m_entityRpcMap.erase(entityRpcsIter);
+ }
+ return AzNetworking::TimeoutResult::Delete;
+ });
}
bool EntityReplicationManager::OrphanedEntityRpcs::DispatchOrphanedRpcs(EntityReplicator& entityReplicator)
diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp
index b3f87ea9ab..b401b85a27 100644
--- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp
+++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.cpp
@@ -9,61 +9,53 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
namespace Multiplayer
{
- AZ_CVAR(AZ::TimeMs, net_EntityMigrationTimeoutMs, AZ::TimeMs{ 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time to wait for a new authority to attach to an entity before we delete the entity");
+ AZ_CVAR(AZ::TimeMs, net_DefaultEntityMigrationTimeoutMs, AZ::TimeMs{ 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time to wait for a new authority to attach to an entity before we delete the entity");
NetworkEntityAuthorityTracker::NetworkEntityAuthorityTracker(INetworkEntityManager& networkEntityManager)
: m_networkEntityManager(networkEntityManager)
+ , m_timeoutTimeMs(net_DefaultEntityMigrationTimeoutMs)
{
;
}
+ void NetworkEntityAuthorityTracker::SetTimeoutTimeMs(AZ::TimeMs timeoutTimeMs)
+ {
+ m_timeoutTimeMs = timeoutTimeMs;
+ }
+
bool NetworkEntityAuthorityTracker::AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& newOwner)
{
bool ret = false;
- auto timeoutData = m_timeoutDataMap.find(entityHandle.GetNetEntityId());
- if (timeoutData != m_timeoutDataMap.end())
+ auto timeoutData = m_timedOutNetEntityIds.find(entityHandle.GetNetEntityId());
+ if (timeoutData != m_timedOutNetEntityIds.end())
{
AZLOG
(
NET_AuthTracker,
- "AuthTracker: Removing timeout for networkEntityId %llu from %s, new owner is %s",
+ "AuthTracker: Removing timeout for networkEntityId %llu, new owner is %s",
aznumeric_cast(entityHandle.GetNetEntityId()),
- timeoutData->second.m_previousOwner.GetString().c_str(),
newOwner.GetString().c_str()
);
- m_timeoutDataMap.erase(timeoutData);
+ m_timedOutNetEntityIds.erase(timeoutData);
ret = true;
}
- auto iter = m_entityAuthorityMap.find(entityHandle.GetNetEntityId());
- if (iter != m_entityAuthorityMap.end())
- {
- AZLOG
- (
- NET_AuthTracker,
- "AuthTracker: Assigning networkEntityId %llu from %s to %s",
- aznumeric_cast(entityHandle.GetNetEntityId()),
- iter->second.back().GetString().c_str(),
- newOwner.GetString().c_str()
- );
- }
- else
- {
- AZLOG
- (
- NET_AuthTracker,
- "AuthTracker: Assigning networkEntityId %llu to %s",
- aznumeric_cast(entityHandle.GetNetEntityId()),
- newOwner.GetString().c_str()
- );
- }
+ AZLOG
+ (
+ NET_AuthTracker,
+ "AuthTracker: Assigning networkEntityId %llu to %s",
+ aznumeric_cast(entityHandle.GetNetEntityId()),
+ newOwner.GetString().c_str()
+ );
m_entityAuthorityMap[entityHandle.GetNetEntityId()].push_back(newOwner);
return ret;
@@ -103,14 +95,35 @@ namespace Multiplayer
{
AZ_Assert
(
- (m_timeoutDataMap.find(entityHandle.GetNetEntityId()) == m_timeoutDataMap.end()) ||
- (m_timeoutDataMap[entityHandle.GetNetEntityId()].m_previousOwner == previousOwner),
+ m_timedOutNetEntityIds.find(entityHandle.GetNetEntityId()) == m_timedOutNetEntityIds.end(),
"Trying to add something twice to the timeout map, this is unexpected"
);
- m_timeoutQueue.RegisterItem(aznumeric_cast(entityHandle.GetNetEntityId()), net_EntityMigrationTimeoutMs);
- TimeoutData& timeoutData = m_timeoutDataMap[entityHandle.GetNetEntityId()];
- timeoutData.m_entityHandle = entityHandle;
- timeoutData.m_previousOwner = previousOwner;
+ m_timedOutNetEntityIds.insert(entityHandle.GetNetEntityId());
+ AZ::Interface::Get()->AddCallback([this, netEntityId = entityHandle.GetNetEntityId()]
+ {
+ auto timeoutData = m_timedOutNetEntityIds.find(netEntityId);
+ if (timeoutData != m_timedOutNetEntityIds.end())
+ {
+ m_timedOutNetEntityIds.erase(timeoutData);
+ ConstNetworkEntityHandle entityHandle = m_networkEntityManager.GetEntity(netEntityId);
+ if (auto entity = entityHandle.GetEntity())
+ {
+ NetEntityRole networkRole = NetEntityRole::InvalidRole;
+ NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
+ if (netBindComponent != nullptr)
+ {
+ networkRole = netBindComponent->GetNetEntityRole();
+ }
+ if (networkRole != NetEntityRole::Authority)
+ {
+ m_networkEntityManager.GetEntityDomain()->HandleLossOfAuthoritativeReplicator(entityHandle);
+ }
+ }
+ }
+ },
+ AZ::Name("Entity authority removal functor"),
+ m_timeoutTimeMs
+ );
}
else
{
@@ -127,18 +140,6 @@ namespace Multiplayer
}
HostId NetworkEntityAuthorityTracker::GetEntityAuthorityManager(ConstNetworkEntityHandle entityHandle) const
- {
- HostId hostId = GetEntityAuthorityManagerInternal(entityHandle);
- AZ_Assert(hostId != InvalidHostId, "Unable to determine manager for entity");
- return hostId;
- }
-
- bool NetworkEntityAuthorityTracker::DoesEntityHaveOwner(ConstNetworkEntityHandle entityHandle) const
- {
- return InvalidHostId != GetEntityAuthorityManagerInternal(entityHandle);
- }
-
- HostId NetworkEntityAuthorityTracker::GetEntityAuthorityManagerInternal(ConstNetworkEntityHandle entityHandle) const
{
if (auto localEnt = entityHandle.GetEntity())
{
@@ -167,52 +168,8 @@ namespace Multiplayer
return InvalidHostId;
}
- NetworkEntityAuthorityTracker::TimeoutData::TimeoutData(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner)
- : m_entityHandle(entityHandle)
- , m_previousOwner(previousOwner)
+ bool NetworkEntityAuthorityTracker::DoesEntityHaveOwner(ConstNetworkEntityHandle entityHandle) const
{
- ;
- }
-
- NetworkEntityAuthorityTracker::NetworkEntityTimeoutFunctor::NetworkEntityTimeoutFunctor
- (
- NetworkEntityAuthorityTracker& networkEntityAuthorityTracker,
- INetworkEntityManager& networkEntityManager
- )
- : m_networkEntityAuthorityTracker(networkEntityAuthorityTracker)
- , m_networkEntityManager(networkEntityManager)
- {
- ;
- }
-
- AzNetworking::TimeoutResult NetworkEntityAuthorityTracker::NetworkEntityTimeoutFunctor::HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item)
- {
- const NetEntityId netEntityId = aznumeric_cast(item.m_userData);
- auto timeoutData = m_networkEntityAuthorityTracker.m_timeoutDataMap.find(netEntityId);
- if (timeoutData != m_networkEntityAuthorityTracker.m_timeoutDataMap.end())
- {
- m_networkEntityAuthorityTracker.m_timeoutDataMap.erase(timeoutData);
- ConstNetworkEntityHandle entityHandle = m_networkEntityManager.GetEntity(netEntityId);
- if (auto entity = entityHandle.GetEntity())
- {
- NetEntityRole networkRole = NetEntityRole::InvalidRole;
- NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
- if (netBindComponent != nullptr)
- {
- networkRole = netBindComponent->GetNetEntityRole();
- }
- if (networkRole != NetEntityRole::Authority)
- {
- AZLOG_ERROR
- (
- "Timed out entity id %llu during migration previous owner %s, removing it",
- aznumeric_cast(entityHandle.GetNetEntityId()),
- timeoutData->second.m_previousOwner.GetString().c_str()
- );
- m_networkEntityManager.MarkForRemoval(entityHandle);
- }
- }
- }
- return AzNetworking::TimeoutResult::Delete;
+ return InvalidHostId != GetEntityAuthorityManager(entityHandle);
}
}
diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h
index 0f4ff5665a..ae9aac04ea 100644
--- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h
+++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityAuthorityTracker.h
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
namespace Multiplayer
{
@@ -23,43 +24,21 @@ namespace Multiplayer
public:
NetworkEntityAuthorityTracker(INetworkEntityManager& networkEntityManager);
+ void SetTimeoutTimeMs(AZ::TimeMs timeoutTimeMs);
bool DoesEntityHaveOwner(ConstNetworkEntityHandle entityHandle) const;
bool AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& newOwner);
void RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner);
HostId GetEntityAuthorityManager(ConstNetworkEntityHandle entityHandle) const;
private:
-
- HostId GetEntityAuthorityManagerInternal(ConstNetworkEntityHandle entityHandle) const;
-
NetworkEntityAuthorityTracker& operator= (const NetworkEntityAuthorityTracker&) = delete;
- struct TimeoutData final
- {
- TimeoutData() = default;
- TimeoutData(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner);
- ConstNetworkEntityHandle m_entityHandle;
- HostId m_previousOwner = InvalidHostId;
- };
-
- struct NetworkEntityTimeoutFunctor final
- : public AzNetworking::ITimeoutHandler
- {
- NetworkEntityTimeoutFunctor(NetworkEntityAuthorityTracker& networkEntityAuthorityTracker, INetworkEntityManager& m_networkEntityManager);
- AzNetworking::TimeoutResult HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item) override;
- private:
- AZ_DISABLE_COPY_MOVE(NetworkEntityTimeoutFunctor);
- NetworkEntityAuthorityTracker& m_networkEntityAuthorityTracker;
- INetworkEntityManager& m_networkEntityManager;
- };
-
- using TimeoutDataMap = AZStd::unordered_map;
using EntityAuthorityMap = AZStd::unordered_map>;
- TimeoutDataMap m_timeoutDataMap;
+ NetEntityIdSet m_timedOutNetEntityIds;
EntityAuthorityMap m_entityAuthorityMap;
INetworkEntityManager& m_networkEntityManager;
- AzNetworking::TimeoutQueue m_timeoutQueue;
+
+ AZ::TimeMs m_timeoutTimeMs = AZ::TimeMs{ 0 };
};
}
-
diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp
index c7582af83f..b178ebeb02 100644
--- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp
+++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp
@@ -28,12 +28,10 @@
namespace Multiplayer
{
AZ_CVAR(bool, net_DebugCheckNetworkEntityManager, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables extra debug checks inside the NetworkEntityManager");
- AZ_CVAR(AZ::TimeMs, net_EntityDomainUpdateMs, AZ::TimeMs{ 500 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Frequency for updating the entity domain in ms");
NetworkEntityManager::NetworkEntityManager()
: m_networkEntityAuthorityTracker(*this)
, m_removeEntitiesEvent([this] { RemoveEntities(); }, AZ::Name("NetworkEntityManager remove entities event"))
- , m_updateEntityDomainEvent([this] { UpdateEntityDomain(); }, AZ::Name("NetworkEntityManager update entity domain event"))
{
AZ::Interface::Register(this);
AzFramework::RootSpawnableNotificationBus::Handler::BusConnect();
@@ -63,8 +61,6 @@ namespace Multiplayer
}
m_entityDomain = AZStd::move(entityDomain);
- m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true);
- m_entityDomain->ActivateTracking(m_ownedEntities);
}
bool NetworkEntityManager::IsInitialized() const
@@ -231,6 +227,74 @@ namespace Multiplayer
m_localDeferredRpcMessages.emplace_back(AZStd::move(message));
}
+ void NetworkEntityManager::HandleEntitiesExitDomain(const NetEntityIdSet& entitiesNotInDomain)
+ {
+ for (NetEntityId exitingId : entitiesNotInDomain)
+ {
+ bool safeToExit = true;
+ NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(exitingId);
+
+ // We need special handling for the NetworkHierarchy as well, since related entities need to be migrated together
+ NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController();
+ NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController();
+
+ // Find the root entity
+ AZ::Entity* hierarchyRootEntity = nullptr;
+ if (hierarchyRootController)
+ {
+ hierarchyRootEntity = hierarchyRootController->GetParent().GetHierarchicalRoot();
+ }
+ else if (hierarchyChildController)
+ {
+ hierarchyRootEntity = hierarchyChildController->GetParent().GetHierarchicalRoot();
+ }
+
+ if (hierarchyRootEntity)
+ {
+ NetEntityId rootNetId = GetNetEntityIdById(hierarchyRootEntity->GetId());
+ ConstNetworkEntityHandle rootEntityHandle = GetEntity(rootNetId);
+
+ // Check if the root entity is still tracked by this authority
+ if (rootEntityHandle.Exists() && rootEntityHandle.GetNetBindComponent()->HasController())
+ {
+ safeToExit = false;
+ }
+ }
+
+ // Validate that we aren't already planning to remove this entity
+ if (safeToExit)
+ {
+ for (auto remoteEntityId : m_removeList)
+ {
+ if (remoteEntityId == remoteEntityId)
+ {
+ safeToExit = false;
+ }
+ }
+ }
+
+ if (safeToExit)
+ {
+ // Tell all the attached replicators for this entity that it's exited the domain
+ m_entityExitDomainEvent.Signal(entityHandle);
+ }
+ }
+ }
+
+ void NetworkEntityManager::ForceAssumeAuthority(const ConstNetworkEntityHandle& entityHandle)
+ {
+ NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
+ if (netBindComponent != nullptr)
+ {
+ netBindComponent->ConstructControllers();
+ }
+ }
+
+ void NetworkEntityManager::SetMigrateTimeoutTimeMs(AZ::TimeMs timeoutTimeMs)
+ {
+ m_networkEntityAuthorityTracker.SetTimeoutTimeMs(timeoutTimeMs);
+ }
+
void NetworkEntityManager::DebugDraw() const
{
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
@@ -243,7 +307,7 @@ namespace Multiplayer
NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity);
AZ::Aabb entityBounds = AZ::Interface::Get()->GetEntityWorldBoundsUnion(entity->GetId());
entityBounds.Expand(AZ::Vector3(0.01f));
- if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority)
+ if ((netBindComponent != nullptr) && netBindComponent->GetNetEntityRole() == NetEntityRole::Authority)
{
debugDisplay->SetColor(AZ::Colors::Black);
debugDisplay->SetAlpha(0.5f);
@@ -277,77 +341,11 @@ namespace Multiplayer
m_localDeferredRpcMessages.clear();
}
- void NetworkEntityManager::UpdateEntityDomain()
- {
- if (m_entityDomain == nullptr)
- {
- return;
- }
-
- const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain = m_entityDomain->RetrieveEntitiesNotInDomain();
- for (NetEntityId exitingId : entitiesNotInDomain)
- {
- OnEntityExitDomain(exitingId);
- }
- }
-
- void NetworkEntityManager::OnEntityExitDomain(NetEntityId entityId)
- {
- bool safeToExit = true;
- NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(entityId);
-
- // We also need special handling for the NetworkHierarchy as well, since related entities need to be migrated together
- NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController();
- NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController();
-
- // Find the root entity
- AZ::Entity* hierarchyRootEntity = nullptr;
- if (hierarchyRootController)
- {
- hierarchyRootEntity = hierarchyRootController->GetParent().GetHierarchicalRoot();
- }
- else if (hierarchyChildController)
- {
- hierarchyRootEntity = hierarchyChildController->GetParent().GetHierarchicalRoot();
- }
-
- if (hierarchyRootEntity)
- {
- NetEntityId rootNetId = GetNetEntityIdById(hierarchyRootEntity->GetId());
- ConstNetworkEntityHandle rootEntityHandle = GetEntity(rootNetId);
-
- // Check if the root entity is still tracked by this authority
- if (rootEntityHandle.Exists() && rootEntityHandle.GetNetBindComponent()->HasController())
- {
- safeToExit = false;
- }
- }
-
- // Validate that we aren't already planning to remove this entity
- if (safeToExit)
- {
- for (auto remoteEntityId : m_removeList)
- {
- if (remoteEntityId == remoteEntityId)
- {
- safeToExit = false;
- }
- }
- }
-
- if (safeToExit)
- {
- m_entityExitDomainEvent.Signal(entityHandle);
- }
- }
-
void NetworkEntityManager::Reset()
{
m_multiplayerComponentRegistry.Reset();
m_removeList.clear();
m_entityDomain = nullptr;
- m_updateEntityDomainEvent.RemoveFromQueue();
- m_ownedEntities.clear();
m_entityExitDomainEvent.DisconnectAllHandlers();
m_onEntityMarkedDirty.DisconnectAllHandlers();
m_onEntityNotifyChanges.DisconnectAllHandlers();
diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h
index 133c35dce0..7c6fdd94f9 100644
--- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h
+++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h
@@ -79,12 +79,13 @@ namespace Multiplayer
void NotifyControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
void NotifyControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) override;
+ void HandleEntitiesExitDomain(const NetEntityIdSet& entitiesNotInDomain) override;
+ void ForceAssumeAuthority(const ConstNetworkEntityHandle& entityHandle) override;
+ void SetMigrateTimeoutTimeMs(AZ::TimeMs timeoutTimeMs) override;
void DebugDraw() const override;
//! @}
void DispatchLocalDeferredRpcMessages();
- void UpdateEntityDomain();
- void OnEntityExitDomain(NetEntityId entityId);
//! RootSpawnableNotificationBus
//! @{
@@ -106,9 +107,6 @@ namespace Multiplayer
AZ::ScheduledEvent m_removeEntitiesEvent;
AZStd::vector m_removeList;
AZStd::unique_ptr m_entityDomain;
- AZ::ScheduledEvent m_updateEntityDomainEvent;
-
- OwnedEntitySet m_ownedEntities;
EntityExitDomainEvent m_entityExitDomainEvent;
AZ::Event<> m_onEntityMarkedDirty;
diff --git a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h
index 3c3d77e011..3e4a33290a 100644
--- a/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h
+++ b/Gems/Multiplayer/Code/Tests/CommonBenchmarkSetup.h
@@ -212,7 +212,7 @@ namespace Multiplayer
}
};
- class BenchmarkNetworkEntityManager : public Multiplayer::INetworkEntityManager
+ class BenchmarkNetworkEntityManager : public MockNetworkEntityManager
{
public:
BenchmarkNetworkEntityManager() : m_authorityTracker(*this) {}
@@ -221,55 +221,6 @@ namespace Multiplayer
NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override { return &m_authorityTracker; }
MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() override { return &m_multiplayerComponentRegistry; }
const HostId& GetHostId() const override { return m_hostId; }
- EntityList CreateEntitiesImmediate(
- [[maybe_unused]] const PrefabEntityId& prefabEntryId,
- [[maybe_unused]] NetEntityRole netEntityRole,
- [[maybe_unused]] const AZ::Transform& transform,
- [[maybe_unused]] AutoActivate autoActivate) override {
- return {};
- }
- EntityList CreateEntitiesImmediate(
- [[maybe_unused]] const PrefabEntityId& prefabEntryId,
- [[maybe_unused]] NetEntityId netEntityId,
- [[maybe_unused]] NetEntityRole netEntityRole,
- [[maybe_unused]] AutoActivate autoActivate,
- [[maybe_unused]] const AZ::Transform& transform) override {
- return {};
- }
- void SetupNetEntity(
- [[maybe_unused]] AZ::Entity* netEntity,
- [[maybe_unused]] PrefabEntityId prefabEntityId,
- [[maybe_unused]] NetEntityRole netEntityRole) override {}
- uint32_t GetEntityCount() const override { return {}; }
- void MarkForRemoval(
- [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {}
- bool IsMarkedForRemoval(
- [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const override {
- return {};
- }
- void ClearEntityFromRemovalList(
- [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {}
- void ClearAllEntities() override {}
- void AddEntityMarkedDirtyHandler(
- [[maybe_unused]] AZ::Event<>::Handler& entityMarkedDirtyHandle) override {}
- void AddEntityNotifyChangesHandler(
- [[maybe_unused]] AZ::Event<>::Handler& entityNotifyChangesHandle) override {}
- void AddEntityExitDomainHandler(
- [[maybe_unused]] EntityExitDomainEvent::Handler& entityExitDomainHandler) override {}
- void AddControllersActivatedHandler(
- [[maybe_unused]] ControllersActivatedEvent::Handler& controllersActivatedHandler) override {}
- void AddControllersDeactivatedHandler(
- [[maybe_unused]] ControllersDeactivatedEvent::Handler& controllersDeactivatedHandler) override {}
- void NotifyEntitiesDirtied() override {}
- void NotifyEntitiesChanged() override {}
- void NotifyControllersActivated(
- [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
- [[maybe_unused]] EntityIsMigrating entityIsMigrating) override {}
- void NotifyControllersDeactivated(
- [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
- [[maybe_unused]] EntityIsMigrating entityIsMigrating) override {}
- void HandleLocalRpcMessage(
- [[maybe_unused]] NetworkEntityRpcMessage& message) override {}
mutable AZStd::map m_networkEntityMap;
@@ -298,18 +249,6 @@ namespace Multiplayer
return InvalidNetEntityId;
}
- [[nodiscard]] AZStd::unique_ptr RequestNetSpawnableInstantiation(
- [[maybe_unused]] const AZ::Data::Asset& netSpawnable,
- [[maybe_unused]] const AZ::Transform& transform) override
- {
- return {};
- }
-
- void Initialize([[maybe_unused]] const HostId& hostId, [[maybe_unused]] AZStd::unique_ptr entityDomain) override {}
- bool IsInitialized() const override { return true; }
- IEntityDomain* GetEntityDomain() const override { return nullptr; }
- void DebugDraw() const override {}
-
NetworkEntityTracker m_tracker;
NetworkEntityAuthorityTracker m_authorityTracker;
MultiplayerComponentRegistry m_multiplayerComponentRegistry;
diff --git a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h
index 249837b484..1b64efc5e2 100644
--- a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h
+++ b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -119,6 +120,8 @@ namespace Multiplayer
m_mockTime = AZStd::make_unique>();
AZ::Interface::Register(m_mockTime.get());
+ m_eventScheduler = AZStd::make_unique();
+
m_mockNetworkTime = AZStd::make_unique>();
AZ::Interface::Register(m_mockNetworkTime.get());
@@ -170,6 +173,7 @@ namespace Multiplayer
AZ::Interface::Unregister(m_mockMultiplayer.get());
AZ::Interface::Unregister(m_mockComponentApplicationRequests.get());
+ m_eventScheduler.reset();
m_mockTime.reset();
m_mockNetworkEntityManager.reset();
@@ -204,6 +208,7 @@ namespace Multiplayer
AZStd::unique_ptr> m_mockMultiplayer;
AZStd::unique_ptr m_mockNetworkEntityManager;
AZStd::unique_ptr> m_mockTime;
+ AZStd::unique_ptr m_eventScheduler;
AZStd::unique_ptr> m_mockNetworkTime;
AZStd::unique_ptr> m_mockConnection;
diff --git a/Gems/Multiplayer/Code/Tests/MockInterfaces.h b/Gems/Multiplayer/Code/Tests/MockInterfaces.h
index 8cebf280b9..f5207d94c8 100644
--- a/Gems/Multiplayer/Code/Tests/MockInterfaces.h
+++ b/Gems/Multiplayer/Code/Tests/MockInterfaces.h
@@ -87,6 +87,9 @@ namespace UnitTest
MOCK_METHOD2(NotifyControllersActivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating));
MOCK_METHOD2(NotifyControllersDeactivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating));
MOCK_METHOD1(HandleLocalRpcMessage, void(Multiplayer::NetworkEntityRpcMessage&));
+ MOCK_METHOD1(HandleEntitiesExitDomain, void(const Multiplayer::NetEntityIdSet&));
+ MOCK_METHOD1(ForceAssumeAuthority, void(const Multiplayer::ConstNetworkEntityHandle&));
+ MOCK_METHOD1(SetMigrateTimeoutTimeMs, void(AZ::TimeMs));
MOCK_CONST_METHOD0(DebugDraw, void());
};
diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake
index 1376083443..7c5f3a946b 100644
--- a/Gems/Multiplayer/Code/multiplayer_files.cmake
+++ b/Gems/Multiplayer/Code/multiplayer_files.cmake
@@ -95,6 +95,8 @@ set(FILES
Source/Editor/MultiplayerEditorConnection.h
Source/EntityDomains/FullOwnershipEntityDomain.cpp
Source/EntityDomains/FullOwnershipEntityDomain.h
+ Source/EntityDomains/NullEntityDomain.cpp
+ Source/EntityDomains/NullEntityDomain.h
Source/MultiplayerStats.cpp
Source/MultiplayerSystemComponent.cpp
Source/MultiplayerSystemComponent.h