Merge pull request #5276 from aws-lumberyard-dev/TimeoutAndDisconnectImprovements
Timeout and disconnect improvements
This commit is contained in:
@@ -13,7 +13,9 @@
|
||||
<Member Type="AzNetworking::DisconnectReason" Name="disconnectReason" Init="AzNetworking::DisconnectReason::None" />
|
||||
</Packet>
|
||||
|
||||
<Packet Name="HeartbeatPacket" Desc="This packet is used to keep an established connection alive" />
|
||||
<Packet Name="HeartbeatPacket" Desc="This packet is used to keep an established connection alive">
|
||||
<Member Type="bool" Name="requestResponse" Init="false" />
|
||||
</Packet>
|
||||
|
||||
<Packet Name="FragmentedPacket" Desc="This packet is used to segment a packet that exceeds a connections MTU">
|
||||
<Member Type="AzNetworking::SequenceId" Name="unfragmentedSequence" Init="AzNetworking::InvalidSequenceId" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<TimeoutResult(TimeoutQueue::TimeoutItem&)>;
|
||||
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 <AzNetworking/DataStructures/TimeoutQueue.inl>
|
||||
|
||||
@@ -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<uint32_t>(buffer.GetSize()));
|
||||
if (m_state == ConnectionState::Connecting)
|
||||
{
|
||||
|
||||
@@ -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<TcpSocket> m_socket;
|
||||
AZStd::unique_ptr<ICompressor> m_compressor;
|
||||
|
||||
TimeoutId m_timeoutId;
|
||||
PacketId m_lastSentPacketId = InvalidPacketId;
|
||||
ConnectionState m_state = ConnectionState::Disconnected;
|
||||
ConnectionRole m_connectionRole = ConnectionRole::Connector;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<int32_t>(tcpSocket->GetSocketFd()));
|
||||
const TimeoutId newTimeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast<uint64_t>(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<int32_t>(tcpSocket.GetSocketFd()));
|
||||
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast<uint64_t>(tcpSocket.GetSocketFd()), m_timeoutMs);
|
||||
AZStd::unique_ptr<TcpConnection> connection = AZStd::make_unique<TcpConnection>(connectionId, remoteAddress, *this, tcpSocket, timeoutId);
|
||||
AZStd::unique_ptr<TcpConnection> connection = AZStd::make_unique<TcpConnection>(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<SocketFd>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PendingConnection> m_pendingConnections;
|
||||
AZStd::vector<PendingRemove> m_pendingRemoves;
|
||||
TimeoutQueue m_connectionTimeoutQueue;
|
||||
TcpListenThread& m_listenThread;
|
||||
|
||||
friend class TcpConnection; // For access to private RequestDisconnect() method
|
||||
|
||||
@@ -79,7 +79,8 @@ namespace AzNetworking
|
||||
AZLOG(NET_Acks, "Unacked packet count exceeded, sending client heartbeat (curr %u : max %u)", m_unackedPacketCount, static_cast<uint32_t>(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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<SequenceId>(item.m_userData & 0xFF);
|
||||
AZLOG(NET_FragmentQueue, "Timing out unreliable fragmented packet %u", static_cast<uint32_t>(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<SequenceId>(item.m_userData & 0xFF);
|
||||
AZLOG(NET_FragmentQueue, "Timing out unreliable fragmented packet %u", static_cast<uint32_t>(fragmentSequence));
|
||||
m_packetFragments.erase(fragmentSequence);
|
||||
return TimeoutResult::Delete;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<uint64_t>(connectionId), m_timeoutMs);
|
||||
const AZ::TimeMs timeoutTimeMs = m_timeoutMs / static_cast<AZ::TimeMs>(static_cast<int32_t>(net_UdpUnackedHeartbeats));
|
||||
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(connectionId), timeoutTimeMs);
|
||||
|
||||
AZStd::unique_ptr<UdpConnection> connection = AZStd::make_unique<UdpConnection>(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<PacketType>(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<int32_t>(net_MaxTimeoutsPerFrame));
|
||||
}
|
||||
m_packetTimeoutQueue.UpdateTimeouts([this](TimeoutQueue::TimeoutItem& item) { return HandlePacketTimeout(item); }, static_cast<int32_t>(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<PacketType>(CorePackets::PacketType::InitiateConnectionPacket) ||
|
||||
packetType == aznumeric_cast<PacketType>(CorePackets::PacketType::ConnectionHandshakePacket) ||
|
||||
(packetType == aznumeric_cast<PacketType>(CorePackets::PacketType::FragmentedPacket) && endpoint.IsConnecting());
|
||||
packetType == aznumeric_cast<PacketType>(CorePackets::PacketType::ConnectionHandshakePacket) ||
|
||||
(packetType == aznumeric_cast<PacketType>(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<uint32_t>(item.m_userData));
|
||||
UdpConnection* udpConnection = static_cast<UdpConnection*>(m_networkInterface.m_connectionSet.GetConnection(connectionId));
|
||||
UdpConnection* udpConnection = static_cast<UdpConnection*>(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<UdpConnection*>(m_networkInterface.m_connectionSet.GetConnection(connectionId));
|
||||
UdpConnection* connection = static_cast<UdpConnection*>(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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -17,8 +17,6 @@ namespace Multiplayer
|
||||
class IEntityDomain
|
||||
{
|
||||
public:
|
||||
using EntitiesNotInDomain = AZStd::unordered_set<NetEntityId>;
|
||||
|
||||
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;
|
||||
|
||||
-2
@@ -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;
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Multiplayer
|
||||
using EntityExitDomainEvent = AZ::Event<const ConstNetworkEntityHandle&>;
|
||||
using ControllersActivatedEvent = AZ::Event<const ConstNetworkEntityHandle&, EntityIsMigrating>;
|
||||
using ControllersDeactivatedEvent = AZ::Event<const ConstNetworkEntityHandle&, EntityIsMigrating>;
|
||||
using NetEntityIdSet = AZStd::unordered_set<NetEntityId>;
|
||||
|
||||
//! @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<ConstNetworkEntityHandle>;
|
||||
using EntityList = AZStd::vector<NetworkEntityHandle>;
|
||||
|
||||
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<IEntityDomain> 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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 <Source/EntityDomains/NullEntityDomain.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
|
||||
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<AZ::u64>(entityHandle.GetNetEntityId()));
|
||||
GetNetworkEntityManager()->MarkForRemoval(entityHandle);
|
||||
}
|
||||
|
||||
void NullEntityDomain::DebugDraw() const
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -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 <Multiplayer/EntityDomains/IEntityDomain.h>
|
||||
|
||||
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;
|
||||
//! @}
|
||||
};
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <ConnectionData/ClientToServerConnectionData.h>
|
||||
#include <ConnectionData/ServerToClientConnectionData.h>
|
||||
#include <EntityDomains/FullOwnershipEntityDomain.h>
|
||||
#include <EntityDomains/NullEntityDomain.h>
|
||||
#include <ReplicationWindows/NullReplicationWindow.h>
|
||||
#include <ReplicationWindows/ServerToClientReplicationWindow.h>
|
||||
#include <Source/AutoGen/AutoComponentTypes.h>
|
||||
@@ -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<FullOwnershipEntityDomain>());
|
||||
}
|
||||
}
|
||||
else if (multiplayerType == MultiplayerAgentType::Client)
|
||||
{
|
||||
m_networkEntityManager.Initialize(AzNetworking::IpAddress(), AZStd::make_unique<NullEntityDomain>());
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<AZStd::string> m_cvarCommands;
|
||||
|
||||
|
||||
+14
-16
@@ -912,24 +912,22 @@ namespace Multiplayer
|
||||
;
|
||||
}
|
||||
|
||||
AzNetworking::TimeoutResult EntityReplicationManager::OrphanedEntityRpcs::HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item)
|
||||
{
|
||||
NetEntityId timedOutEntityId = aznumeric_cast<NetEntityId>(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<NetEntityId>(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)
|
||||
|
||||
@@ -9,61 +9,53 @@
|
||||
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
|
||||
#include <Multiplayer/EntityDomains/IEntityDomain.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzCore/EBus/IEventScheduler.h>
|
||||
#include <AzNetworking/Utilities/NetworkCommon.h>
|
||||
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
|
||||
|
||||
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<AZ::u64>(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<AZ::u64>(entityHandle.GetNetEntityId()),
|
||||
iter->second.back().GetString().c_str(),
|
||||
newOwner.GetString().c_str()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG
|
||||
(
|
||||
NET_AuthTracker,
|
||||
"AuthTracker: Assigning networkEntityId %llu to %s",
|
||||
aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()),
|
||||
newOwner.GetString().c_str()
|
||||
);
|
||||
}
|
||||
AZLOG
|
||||
(
|
||||
NET_AuthTracker,
|
||||
"AuthTracker: Assigning networkEntityId %llu to %s",
|
||||
aznumeric_cast<AZ::u64>(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<uint64_t>(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<AZ::IEventScheduler>::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<NetEntityId>(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<AZ::u64>(entityHandle.GetNetEntityId()),
|
||||
timeoutData->second.m_previousOwner.GetString().c_str()
|
||||
);
|
||||
m_networkEntityManager.MarkForRemoval(entityHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
return AzNetworking::TimeoutResult::Delete;
|
||||
return InvalidHostId != GetEntityAuthorityManager(entityHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzNetworking/DataStructures/TimeoutQueue.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityTracker.h>
|
||||
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
|
||||
|
||||
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<NetEntityId, TimeoutData>;
|
||||
using EntityAuthorityMap = AZStd::unordered_map<NetEntityId, AZStd::vector<HostId>>;
|
||||
|
||||
TimeoutDataMap m_timeoutDataMap;
|
||||
NetEntityIdSet m_timedOutNetEntityIds;
|
||||
EntityAuthorityMap m_entityAuthorityMap;
|
||||
INetworkEntityManager& m_networkEntityManager;
|
||||
AzNetworking::TimeoutQueue m_timeoutQueue;
|
||||
|
||||
AZ::TimeMs m_timeoutTimeMs = AZ::TimeMs{ 0 };
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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<INetworkEntityManager>::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<NetworkHierarchyRootComponentController>();
|
||||
NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController<NetworkHierarchyChildComponentController>();
|
||||
|
||||
// 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<AzFramework::IEntityBoundsUnion>::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<NetworkHierarchyRootComponentController>();
|
||||
NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController<NetworkHierarchyChildComponentController>();
|
||||
|
||||
// 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();
|
||||
|
||||
@@ -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<NetEntityId> m_removeList;
|
||||
AZStd::unique_ptr<IEntityDomain> m_entityDomain;
|
||||
AZ::ScheduledEvent m_updateEntityDomainEvent;
|
||||
|
||||
OwnedEntitySet m_ownedEntities;
|
||||
|
||||
EntityExitDomainEvent m_entityExitDomainEvent;
|
||||
AZ::Event<> m_onEntityMarkedDirty;
|
||||
|
||||
@@ -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<NetEntityId, AZ::Entity*> m_networkEntityMap;
|
||||
|
||||
@@ -298,18 +249,6 @@ namespace Multiplayer
|
||||
return InvalidNetEntityId;
|
||||
}
|
||||
|
||||
[[nodiscard]] AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
|
||||
[[maybe_unused]] const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable,
|
||||
[[maybe_unused]] const AZ::Transform& transform) override
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void Initialize([[maybe_unused]] const HostId& hostId, [[maybe_unused]] AZStd::unique_ptr<IEntityDomain> 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;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <IMultiplayerConnectionMock.h>
|
||||
#include <MockInterfaces.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/EBus/EventSchedulerSystemComponent.h>
|
||||
#include <AzCore/Console/Console.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
@@ -119,6 +120,8 @@ namespace Multiplayer
|
||||
m_mockTime = AZStd::make_unique<NiceMock<MockTime>>();
|
||||
AZ::Interface<AZ::ITime>::Register(m_mockTime.get());
|
||||
|
||||
m_eventScheduler = AZStd::make_unique<AZ::EventSchedulerSystemComponent>();
|
||||
|
||||
m_mockNetworkTime = AZStd::make_unique<NiceMock<MockNetworkTime>>();
|
||||
AZ::Interface<INetworkTime>::Register(m_mockNetworkTime.get());
|
||||
|
||||
@@ -170,6 +173,7 @@ namespace Multiplayer
|
||||
AZ::Interface<IMultiplayer>::Unregister(m_mockMultiplayer.get());
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(m_mockComponentApplicationRequests.get());
|
||||
|
||||
m_eventScheduler.reset();
|
||||
m_mockTime.reset();
|
||||
|
||||
m_mockNetworkEntityManager.reset();
|
||||
@@ -204,6 +208,7 @@ namespace Multiplayer
|
||||
AZStd::unique_ptr<NiceMock<MockMultiplayer>> m_mockMultiplayer;
|
||||
AZStd::unique_ptr<MockNetworkEntityManager> m_mockNetworkEntityManager;
|
||||
AZStd::unique_ptr<NiceMock<MockTime>> m_mockTime;
|
||||
AZStd::unique_ptr<AZ::EventSchedulerSystemComponent> m_eventScheduler;
|
||||
AZStd::unique_ptr<NiceMock<MockNetworkTime>> m_mockNetworkTime;
|
||||
|
||||
AZStd::unique_ptr<NiceMock<IMultiplayerConnectionMock>> m_mockConnection;
|
||||
|
||||
@@ -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());
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user